Recipe: Add Conversation History
The Problem
"Our users expect the agent to remember what was said earlier in the conversation, and to be able to resume a conversation later (or across page reloads) without losing context. We also need to list a user's past conversations so they can pick one up again."
Ingredients
- A Thread — the conversation container (like a chat window or support ticket)
- Messages — the individual user/assistant/system/tool turns stored inside a thread
agent.thread_id,client.create_thread(),agent.list_messages()- Your application-side mapping of
user_id -> thread_id(database/cache)
The Recipe
1. Create a thread
thread = await client.create_thread(
agent_id="your-agent-id",
thread_data={
"title": "Order #12345 Support", # descriptive titles help identify conversations later
"metadata": {"user_id": "u-123"},
},
)
2. Assign the thread to the agent
agent.thread_id = thread.threadId
# Messages from this point on are stored in this thread
response = await agent.run("Hello!")
Every agent.run() / agent.stream() call automatically stores both the user message and the assistant's response in the thread — you don't create messages manually.
3. Retrieve history and pass it back explicitly
The SDK stores messages for persistence but does not automatically load previous messages as context for the next call. You must explicitly fetch and pass message_history.
# First message - no history needed yet
response1 = await agent.run("What's my order status?")
# Fetch prior messages for context
message_history = await agent.list_messages(
thread_id=thread.threadId,
limit=50,
)
# Second message - now the agent has context
response2 = await agent.run(
"And when will it ship?",
message_history=message_history,
)
4. Resume a thread later, or list all threads for a user
# Resume an existing thread
thread = await client.get_thread(agent_id="your-agent-id", thread_id="thread-id-here")
agent.thread_id = thread.threadId
# List threads (e.g. to build a "past conversations" picker)
threads = await client.list_threads(agent_id="your-agent-id", limit=10, offset=0)
for t in threads:
print(f"- {t.title} (created: {t.createdAt})")
In production, persist thread_id in your app backend (for example against your own user_id and workspace_id). This is what lets you resume conversations after browser refreshes, service restarts, and handoffs.
5. Handle a missing thread gracefully
from caip_agents_sdk import NotFoundError
try:
thread = await client.get_thread(agent_id, thread_id)
except NotFoundError:
thread = await client.create_thread(agent_id=agent_id, thread_data={"title": "New Session"})
Message structure, for reference
Each message has a role (user, assistant, system, tool), structured content ([{"type": "text", "text": "..."}]), optional metadata, and — for assistant messages — token usage. See the Messages concept guide for the full schema.
Enterprise Hardening Checklist
- Define thread ownership and access control (who can read/write which thread).
- Do not store sensitive raw payloads in app logs; use metadata pointers instead.
- Pass a bounded
message_historywindow (for example last 30-100 messages) to control latency and token cost. - Add a thread archival policy (
open,completed,archived) for lifecycle hygiene. - Test recovery behavior for deleted or inaccessible thread IDs.
Common Pitfall
The SDK persists messages automatically, but context replay is explicit. If you skip fetching and passing message_history, your agent will answer as if the conversation started fresh.
Related Recipes
- Create an Agent — the thread is assigned to an initialized agent
- Ground responses in your own documents on top of conversation history: Add a Vector Store
- Full reference: Threads concept guide