Overview
AgentKit enables persistent conversations that maintain context across multiple runs. By implementing a History Adapter, you can connect your agents and networks to any database or storage solution, allowing conversations to resume exactly where they left off. A History Adapter is a configuration object that bridges AgentKit’s execution lifecycle with your database. It tells AgentKit how to:- Create new conversation threads
- Load existing conversation history
- Save new messages and results
HistoryConfig interface.
The adapter is passed to createAgent() or createNetwork() and AgentKit automatically calls your adapter’s methods at the appropriate times during execution.
HistoryConfig Interface
TheHistoryConfig interface has four optional methods. Below is an expanded view of the interface showing the context and parameters passed to each method.
createThread
- Creates a new conversation thread in your database or ensures an existing thread is present
- Invoked at the start of a run to initialize the thread
- Important: If a
threadIdalready exists in the state, your adapter should upsert (insert or update) to ensure the thread exists in storage - Returns an object with the
threadId
get
- Retrieves conversation history from your database
- Invoked after thread initialization, but only if:
- A
threadIdis present in the state - The client didn’t provide
resultsormessages - The thread was not just created in this run (client provided the threadId)
- A
- Returns an array of
AgentResult[]representing the conversation history - Recommended: Include both user messages and agent results by converting user messages to
AgentResultobjects withagentName: "user"to preserve conversation order
appendUserMessage
- Saves the user’s message immediately at the beginning of a run
- Invoked after thread initialization but before any agents execute
- Ensures user intent is captured even if the agent run fails (enables “regenerate” workflows)
- Receives the user’s message with a canonical, client-generated ID for idempotency
appendResults
- Saves new agent results to your database after a network or agent run
- Invoked at the end of a successful agent or network run
- Receives only the new results generated during this run (AgentKit automatically filters out historical results to prevent duplicates)
Usage
Here’s a complete example of creating a network with history persistence:Once you’ve created your adapter, pass it to the
history property when creating an agent or network:
Persistence Patterns
AgentKit supports two distint patterns for managing conversation history.Server-Authoritative
The client sends a message with athreadId. AgentKit automatically loads the full conversation context from your database before the network runs.
Client-Authoritative (Performance Optimized)
The client maintains conversation state locally and sends the complete history with each request. AgentKit detects this and skips the database read for better performance.results or messages to createState will disable the history.get() call, enabling this client-authoritative pattern.
Server/Client Hybrid Pattern
You can combine the Server-Authoritative and Client-Authoritative patterns for an optimal user experience. This hybrid approach allows for fast initial conversation loading and high-performance interactive chat.- Initial Load (Server-Authoritative): When a user opens a conversation thread, the client sends only the
threadId. AgentKit fetches the history from your database usinghistory.get(). The application then hydrates the client-side state with this history. - Interactive Session (Client-Authoritative): For all subsequent requests within the session, the client sends the full, up-to-date history (
resultsormessages) along with thethreadId. AgentKit detects the client-provided history and skips the database read, resulting in a faster response.
How Thread IDs Are Managed
AgentKit offers a flexible system for managing conversation thread IDs, ensuring that history is handled correctly whether you’re starting a new conversation or continuing an existing one. Here’s how AgentKit determines whichthreadId to use:
Thread Initialization Flow
| Scenario | threadId provided? | createThread exists? | Behavior |
|---|---|---|---|
| Resume existing thread | ✅ Yes | ✅ Yes | Calls createThread to upsert/ensure thread exists in DB |
| Resume existing thread | ✅ Yes | ❌ No | Uses provided threadId directly |
| New conversation | ❌ No | ✅ Yes | Calls createThread to create new thread and get threadId |
| New conversation (fallback) | ❌ No | ❌ No (but get exists) | Auto-generates UUID as threadId |
-
Explicit
threadIdwithcreateThread: When you provide athreadIdand your adapter has acreateThreadmethod, AgentKit callscreateThreadto ensure the thread exists in your database. Your adapter should implement an upsert pattern (insert if new, update if exists) to handle both new and existing threads gracefully. -
Automatic Creation via
createThread: If you don’t provide athreadId, AgentKit checks if your history adapter has acreateThreadmethod. If so, AgentKit calls it to create a new conversation thread in your database. YourcreateThreadfunction is responsible for generating and returning the new uniquethreadId. This is the recommended approach for starting new conversations, as it ensures a record is created in your backend from the very beginning. -
Automatic Generation (Fallback): In cases where you don’t provide a
threadIdand your history adapter does not have acreateThreadmethod but does have agetmethod, AgentKit provides a fallback. It will automatically generate a standard UUID and assign it as thethreadIdfor the current run. This convenience ensures the conversation can proceed with a unique identifier for saving and loading history, even without an explicit creation step.
Best Practices
Implement Idempotency with Message IDs and Checksums
Implement Idempotency with Message IDs and Checksums
Use unique constraints on
message_id and checksum to prevent duplicate messages during retries or streaming scenarios.Leverage Inngest's Durable Steps
Leverage Inngest's Durable Steps
Wrap database operations in
step.run() for automatic retries and durability.Handle Missing Threads Gracefully
Handle Missing Threads Gracefully
If a thread doesn’t exist, return an empty array rather than throwing an error.
Index Your Database Properly
Index Your Database Properly
Ensure you have indexes on key columns for fast queries.
Return Complete Conversation History
Return Complete Conversation History
Include both user messages and agent results in your
get() method to preserve conversation order.Implement Upsert in createThread
Implement Upsert in createThread
Handle both new and existing threads gracefully by implementing an upsert pattern.
Future Enhancements
The history system provides a foundation for advanced features to be released in the coming future including:- Database Adapters: Pre-built adapters for popular databases (coming soon)
- Progressive Summarization: Automatic conversation compression for long threads
- Search & Retrieval: Semantic search across conversation history
Complete Example
Check out the AgentKit Starter for a complete implementation featuring:- PostgreSQL history adapter
- ChatGPT-style UI with thread management
- Real-time streaming responses
- Both server and client-authoritative patterns