|
| 1 | +--- |
| 2 | +title: "Pydantic AI Persistent Memory: Add It in 5 Lines of Code" |
| 3 | +authors: [hindsight] |
| 4 | +date: 2026-03-09 |
| 5 | +tags: [memory, openai, anthropic, gemini, python, rust, agents, rag, vector, pydantic-ai, knowledge-graph] |
| 6 | +image: /img/blog/pydantic-ai-persistent-memory.png |
| 7 | +hide_table_of_contents: true |
| 8 | +--- |
| 9 | + |
| 10 | +If you have built an AI agent with [Pydantic AI](https://ai.pydantic.dev/), you already know it handles typed outputs, dependency injection, and async workflows well. But there is one thing it does not do: remember anything between runs. Every call to `agent.run()` starts with a blank slate. Your agent has no idea what the user said yesterday, what preferences they shared, or what it already researched. |
| 11 | + |
| 12 | +Adding **Pydantic AI persistent memory** does not require building a custom RAG pipeline or managing your own vector database. With the `hindsight-pydantic-ai` integration, you can wire long-term memory into any Pydantic AI agent in five lines of Python. This guide walks through the full setup, from installation to production-ready patterns. |
| 13 | + |
| 14 | +<!-- truncate --> |
| 15 | + |
| 16 | +## TL;DR |
| 17 | + |
| 18 | +- Pydantic AI has no built-in persistent memory. Your agent starts from scratch every run. |
| 19 | +- `hindsight-pydantic-ai` adds retain, recall, and reflect tools plus auto-injected memory instructions. |
| 20 | +- Five lines of setup: create a client, call `create_hindsight_tools()`, and pass them to your Agent. |
| 21 | +- `memory_instructions()` silently pre-loads relevant memories into the system prompt on every run, so the agent starts each conversation with context. |
| 22 | +- Works with any model provider, including OpenAI, Anthropic, and Gemini. |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## The Problem: Pydantic AI Has No Persistent Memory |
| 27 | + |
| 28 | +[Pydantic AI](https://ai.pydantic.dev/) is a solid framework. Typed outputs, dependency injection, async-native design, and a clean tool API make it a popular choice for building Python-based agents. However, it ships with no memory layer at all. |
| 29 | + |
| 30 | +Every `agent.run()` starts from zero. The agent does not know what the user said yesterday. It does not know their preferences. It does not know what it already researched. As a result, agents that interact with users over multiple sessions lose all accumulated context between runs. |
| 31 | + |
| 32 | +You can pass `message_history` to continue a conversation within a single session. But that is chat history, not memory. Chat history does not generalize facts. It does not consolidate repeated information. And it grows linearly until it exceeds your context window and token limit. |
| 33 | + |
| 34 | +Real agent memory means something different: |
| 35 | + |
| 36 | +- Extracting structured facts from conversations |
| 37 | +- Building a knowledge graph of entities and relationships |
| 38 | +- Retrieving relevant context across days, weeks, and months |
| 39 | +- Synthesizing coherent answers from scattered memories |
| 40 | + |
| 41 | +That is what [Hindsight](https://hindsight.vectorize.io/) provides. If you have used Hindsight with other frameworks like [CrewAI](/blog/2026/03/02/crewai) or [OpenAI](/blog/2026/03/05/add-memory-to-openai-application), the concept is the same. And `hindsight-pydantic-ai` wires it directly into Pydantic AI's tool and instruction system, so you do not need to build any of this yourself. |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +## How Pydantic AI Persistent Memory Works with Hindsight |
| 46 | + |
| 47 | +Before diving into code, it helps to understand what happens under the hood when you add Pydantic AI persistent memory through the Hindsight integration. |
| 48 | + |
| 49 | +Hindsight is a memory engine that runs locally or in the cloud. When your agent stores a fact, Hindsight does not just save raw text. Instead, it extracts structured entities and relationships, builds a knowledge graph, and indexes everything for multi-strategy retrieval. That means when your agent searches memory later, it can find relevant information through semantic search, BM25 keyword matching, graph traversal, and temporal ranking, all combined. |
| 50 | + |
| 51 | +The `hindsight-pydantic-ai` package connects this engine to Pydantic AI through two integration points: |
| 52 | + |
| 53 | +``` |
| 54 | +Pydantic AI Agent |
| 55 | + |-- tools=[create_hindsight_tools(...)] |
| 56 | + | |-- hindsight_retain -> store facts to memory |
| 57 | + | |-- hindsight_recall -> search memory for relevant info |
| 58 | + | |-- hindsight_reflect -> synthesize an answer from all memories |
| 59 | + | |
| 60 | + |-- instructions=[memory_instructions(...)] |
| 61 | + |-- auto-recalls relevant memories into the system prompt |
| 62 | +``` |
| 63 | + |
| 64 | +**Tools** let the agent explicitly store and retrieve memories during a conversation. **Instructions** silently inject relevant memories before the agent even starts thinking. Both are optional. You can use one, the other, or both together depending on your use case. |
| 65 | + |
| 66 | +The tools are async functions that call Hindsight's API directly. Since Pydantic AI is async-native, the closures use `aretain()`, `arecall()`, and `areflect()`. There are no thread-pool hacks or compatibility layers needed. If you prefer a protocol-based approach, you can also use [Hindsight's MCP memory server](/blog/2026/03/04/mcp-agent-memory) instead of direct tool integration. |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +## Setting Up Pydantic AI Persistent Memory in 5 Steps |
| 71 | + |
| 72 | +The setup process takes about five minutes. You will install Hindsight, add the Pydantic AI memory integration package, wire the tools into your agent, and test that memories persist across sessions. |
| 73 | + |
| 74 | +### Step 1: Install and Start Hindsight |
| 75 | + |
| 76 | +First, install the Hindsight server and start it locally: |
| 77 | + |
| 78 | +```bash |
| 79 | +pip install hindsight-all |
| 80 | +``` |
| 81 | + |
| 82 | +```bash |
| 83 | +export HINDSIGHT_API_LLM_API_KEY=YOUR_OPENAI_KEY |
| 84 | +hindsight-api |
| 85 | +``` |
| 86 | + |
| 87 | +This runs locally at `http://localhost:8888`. It includes embedded Postgres, local embeddings, and local reranking. No external services are required beyond an LLM API key for entity extraction. |
| 88 | + |
| 89 | +> **Note:** You can also use [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup) and skip the self-hosted setup entirely. The cloud version provides the same API with managed infrastructure. |
| 90 | +
|
| 91 | +### Step 2: Install the Pydantic AI Memory Integration |
| 92 | + |
| 93 | +Next, install the integration package: |
| 94 | + |
| 95 | +```bash |
| 96 | +pip install hindsight-pydantic-ai |
| 97 | +``` |
| 98 | + |
| 99 | +You also need a model provider. For OpenAI: |
| 100 | + |
| 101 | +```bash |
| 102 | +pip install "pydantic-ai-slim[openai]" |
| 103 | +``` |
| 104 | + |
| 105 | +Other providers work the same way. Swap in `[anthropic]` or `[google]` as needed. The Pydantic AI persistent memory integration is model-agnostic, so the memory layer works identically regardless of which LLM you choose. Hindsight supports [100+ LLM providers through LiteLLM](/blog/2026/03/03/litellm). |
| 106 | + |
| 107 | +### Step 3: Add Persistent Memory Tools to Your Pydantic AI Agent |
| 108 | + |
| 109 | +Here is where the five lines come in. Create a Hindsight client, generate the Pydantic AI persistent memory tools, and pass them to your agent: |
| 110 | + |
| 111 | +```python |
| 112 | +from hindsight_client import Hindsight |
| 113 | +from hindsight_pydantic_ai import create_hindsight_tools |
| 114 | +from pydantic_ai import Agent |
| 115 | + |
| 116 | +client = Hindsight(base_url="http://localhost:8888") |
| 117 | + |
| 118 | +agent = Agent( |
| 119 | + "openai:gpt-4o-mini", |
| 120 | + tools=create_hindsight_tools(client=client, bank_id="user-123"), |
| 121 | +) |
| 122 | +``` |
| 123 | + |
| 124 | +That is the full Pydantic AI persistent memory setup. The agent now has three tools: |
| 125 | + |
| 126 | +- `hindsight_retain(content)` stores information to long-term memory |
| 127 | +- `hindsight_recall(query)` searches memory and returns matching facts |
| 128 | +- `hindsight_reflect(query)` synthesizes a reasoned answer from all relevant memories |
| 129 | + |
| 130 | +The agent decides when to use each tool based on the conversation context. You do not need to call them manually. |
| 131 | + |
| 132 | +### Step 4: Test Cross-Session Memory |
| 133 | + |
| 134 | +Run two separate conversations to verify that Pydantic AI agent memory persists across sessions: |
| 135 | + |
| 136 | +```python |
| 137 | +import asyncio |
| 138 | + |
| 139 | +async def main(): |
| 140 | + # First conversation |
| 141 | + r1 = await agent.run( |
| 142 | + "Remember that I prefer functional programming patterns " |
| 143 | + "and I'm building a data pipeline in Python." |
| 144 | + ) |
| 145 | + print(r1.output) |
| 146 | + |
| 147 | + # Later conversation -- agent recalls context |
| 148 | + r2 = await agent.run("What approach should I take for error handling?") |
| 149 | + print(r2.output) |
| 150 | + |
| 151 | +asyncio.run(main()) |
| 152 | +``` |
| 153 | + |
| 154 | +In the first run, the agent stores the preferences via `hindsight_retain`. In the second run, the agent calls `hindsight_recall` to find relevant context. It then gives advice grounded in what it knows: functional patterns, Python, data pipelines. |
| 155 | + |
| 156 | +This is the core benefit of Pydantic AI persistent memory: it works across runs, across days, and across process restarts. The memories live in Hindsight's knowledge graph, not in the agent's context window. Even if you restart your Python process completely, the agent picks up right where it left off. |
| 157 | + |
| 158 | +### Step 5: Auto-Inject Memories with Pydantic AI Instructions |
| 159 | + |
| 160 | +The tools above require the agent to decide to search memory on its own. Sometimes you want Pydantic AI persistent memory injected automatically, before the agent starts responding. |
| 161 | + |
| 162 | +Pydantic AI's `instructions` parameter supports async callables that run on every `agent.run()`. This is a natural fit for memory injection: |
| 163 | + |
| 164 | +```python |
| 165 | +from hindsight_pydantic_ai import create_hindsight_tools, memory_instructions |
| 166 | + |
| 167 | +agent = Agent( |
| 168 | + "openai:gpt-4o-mini", |
| 169 | + tools=create_hindsight_tools(client=client, bank_id="user-123"), |
| 170 | + instructions=[memory_instructions(client=client, bank_id="user-123")], |
| 171 | +) |
| 172 | +``` |
| 173 | + |
| 174 | +Now on every run, `memory_instructions` calls Hindsight's recall API and injects relevant memories into the system prompt. The agent starts every conversation with context about the user, without needing a tool call. |
| 175 | + |
| 176 | +You can customize the query, result count, and prefix to control what gets injected: |
| 177 | + |
| 178 | +```python |
| 179 | +memory_instructions( |
| 180 | + client=client, |
| 181 | + bank_id="user-123", |
| 182 | + query="user preferences, history, and context", |
| 183 | + max_results=10, |
| 184 | + prefix="Here is what you know about this user:\n", |
| 185 | +) |
| 186 | +``` |
| 187 | + |
| 188 | +If recall fails or returns nothing, the instructions function returns an empty string. It never blocks the agent from responding. |
| 189 | + |
| 190 | +--- |
| 191 | + |
| 192 | +## Advanced Pydantic AI Memory Configuration |
| 193 | + |
| 194 | +### Selecting Which Memory Tools to Include |
| 195 | + |
| 196 | +You do not always need all three persistent memory tools. `create_hindsight_tools` lets you pick which ones to include: |
| 197 | + |
| 198 | +```python |
| 199 | +# Read-only agent: can search memory but not write to it |
| 200 | +tools = create_hindsight_tools( |
| 201 | + client=client, |
| 202 | + bank_id="user-123", |
| 203 | + include_retain=False, |
| 204 | + include_recall=True, |
| 205 | + include_reflect=True, |
| 206 | +) |
| 207 | + |
| 208 | +# Write-only agent: stores data but does not query |
| 209 | +tools = create_hindsight_tools( |
| 210 | + client=client, |
| 211 | + bank_id="user-123", |
| 212 | + include_retain=True, |
| 213 | + include_recall=False, |
| 214 | + include_reflect=False, |
| 215 | +) |
| 216 | +``` |
| 217 | + |
| 218 | +This flexibility is useful in multi-agent architectures. For instance, you might have one Pydantic AI agent that gathers information and writes to persistent memory, while a separate agent reads memory to answer questions. Splitting read and write access keeps each agent focused on its role and prevents unintended memory writes from agents that should only consume context. |
| 219 | + |
| 220 | +### Global Configuration for Multiple Agents |
| 221 | + |
| 222 | +If you have multiple Pydantic AI agents sharing the same Hindsight persistent memory instance, use the global config instead of passing `client` everywhere: |
| 223 | + |
| 224 | +```python |
| 225 | +from hindsight_pydantic_ai import configure, create_hindsight_tools |
| 226 | + |
| 227 | +configure(hindsight_api_url="http://localhost:8888", api_key="YOUR_KEY") |
| 228 | + |
| 229 | +# No client needed: tools use the global config |
| 230 | +agent1_tools = create_hindsight_tools(bank_id="agent-1") |
| 231 | +agent2_tools = create_hindsight_tools(bank_id="agent-2") |
| 232 | +``` |
| 233 | + |
| 234 | +An explicit `client=` parameter always takes priority over the global config. This lets you override on a per-agent basis when needed. |
| 235 | + |
| 236 | +--- |
| 237 | + |
| 238 | +## Full Working Example of Pydantic AI Persistent Memory |
| 239 | + |
| 240 | +Save this as `memory_agent.py` and run it to see Pydantic AI persistent memory in action: |
| 241 | + |
| 242 | +```python |
| 243 | +import asyncio |
| 244 | + |
| 245 | +from hindsight_client import Hindsight |
| 246 | +from hindsight_pydantic_ai import create_hindsight_tools, memory_instructions |
| 247 | +from pydantic_ai import Agent |
| 248 | + |
| 249 | +BANK_ID = "demo-user" |
| 250 | + |
| 251 | + |
| 252 | +async def main(): |
| 253 | + client = Hindsight(base_url="http://localhost:8888") |
| 254 | + await client.acreate_bank(bank_id=BANK_ID, name="Demo User Memory") |
| 255 | + |
| 256 | + agent = Agent( |
| 257 | + "openai:gpt-4o-mini", |
| 258 | + tools=create_hindsight_tools(client=client, bank_id=BANK_ID), |
| 259 | + instructions=[memory_instructions(client=client, bank_id=BANK_ID)], |
| 260 | + ) |
| 261 | + |
| 262 | + print("--- Run 1: Teaching the agent ---") |
| 263 | + r1 = await agent.run( |
| 264 | + "Remember: I'm a backend engineer. I use Python and Rust. " |
| 265 | + "I prefer small, composable libraries over large frameworks." |
| 266 | + ) |
| 267 | + print(f"Agent: {r1.output}\n") |
| 268 | + |
| 269 | + print("--- Run 2: Agent recalls context ---") |
| 270 | + r2 = await agent.run("Recommend a web framework for my next project.") |
| 271 | + print(f"Agent: {r2.output}\n") |
| 272 | + |
| 273 | + print("--- Run 3: Agent synthesizes ---") |
| 274 | + r3 = await agent.run("What do you know about my engineering philosophy?") |
| 275 | + print(f"Agent: {r3.output}") |
| 276 | + |
| 277 | + |
| 278 | +asyncio.run(main()) |
| 279 | +``` |
| 280 | + |
| 281 | +Run it: |
| 282 | + |
| 283 | +```bash |
| 284 | +export OPENAI_API_KEY=YOUR_KEY |
| 285 | +python memory_agent.py |
| 286 | +``` |
| 287 | + |
| 288 | +Run it again after the first execution finishes. The agent remembers everything from the first session because Pydantic AI persistent memory stores facts in Hindsight, not in the process. |
| 289 | + |
| 290 | +--- |
| 291 | + |
| 292 | +## Pydantic AI Persistent Memory: Pitfalls and Edge Cases |
| 293 | + |
| 294 | +**Bank ID collisions.** Each `bank_id` is a separate persistent memory store. If two unrelated agents share a bank, their memories merge in unexpected ways. Use unique bank IDs per user, per agent, or per project. |
| 295 | + |
| 296 | +**Instruction latency.** `memory_instructions` makes a recall API call on every `agent.run()`. For latency-sensitive applications, use `budget="low"` and a small `max_results`. Alternatively, skip automatic memory injection entirely and rely on the agent to call the recall tool only when needed. In most cases, the added latency from persistent memory lookups is 50-200ms depending on memory size and network conditions. |
| 297 | + |
| 298 | +**Duplicate memories.** If the agent stores the same information multiple times, Hindsight deduplicates at the fact level. However, it is still better to give the agent clear guidance in the system prompt about when to store new facts versus when to skip. |
| 299 | + |
| 300 | +**Async event loop conflicts.** The sync `create_bank()` method does not work inside `asyncio.run()` because it tries to create a nested event loop. Always use `await client.acreate_bank()` in async code. This is a common Python async pitfall, not specific to Hindsight or Pydantic AI persistent memory. |
| 301 | + |
| 302 | +--- |
| 303 | + |
| 304 | +## Pydantic AI Memory: Tradeoffs and Alternatives |
| 305 | + |
| 306 | +### When Pydantic AI Persistent Memory Makes Sense |
| 307 | + |
| 308 | +Pydantic AI persistent memory with Hindsight works best for agents that interact with the same user or context across multiple sessions. Good use cases include personal assistants, support bots, research agents that accumulate knowledge, and any Python agent where remembering past interactions improves quality over time. |
| 309 | + |
| 310 | +### When Not to Use It |
| 311 | + |
| 312 | +Skip agent persistent memory for one-shot agents that never run again, stateless API handlers where each request is independent, or agents where you want full control over what goes into the prompt. In the last case, use the Hindsight Python client directly instead of the Pydantic AI integration. |
| 313 | + |
| 314 | +### How It Compares to Alternatives |
| 315 | + |
| 316 | +| Approach | Strengths | Weaknesses | Best For | |
| 317 | +|---|---|---|---| |
| 318 | +| **Hindsight + Pydantic AI** | Multi-strategy retrieval (semantic + BM25 + graph + temporal), structured fact extraction, synthesis engine | Requires running Hindsight server or using cloud | Multi-session agents needing deep memory | |
| 319 | +| **Manual message_history** | Built into Pydantic AI, no extra dependencies | Does not generalize facts, grows until context window limit | Short single-session conversations | |
| 320 | +| **Custom vector store + RAG** | Full control over embeddings and retrieval | You manage chunking, indexing, and retrieval yourself | Teams with existing vector infrastructure | |
| 321 | +| **Mem0** | Another external memory option, easy API | Fewer retrieval strategies, no graph-based recall | Simpler memory needs without entity relationships | |
| 322 | + |
| 323 | +The right choice depends on your requirements. For most Pydantic AI agents that need persistent memory across sessions with minimal setup, the Hindsight integration offers the fastest path to production. |
| 324 | + |
| 325 | +--- |
| 326 | + |
| 327 | +## Recap: Pydantic AI Persistent Memory Integration |
| 328 | + |
| 329 | +Adding Pydantic AI persistent memory does not require complex infrastructure or custom retrieval pipelines. The `hindsight-pydantic-ai` integration provides two functions that cover the full agent memory lifecycle: |
| 330 | + |
| 331 | +- **`create_hindsight_tools()`** returns async tools that the agent calls to store and retrieve knowledge across sessions |
| 332 | +- **`memory_instructions()`** auto-injects relevant memories on every run, so the agent starts with context and no tool call is needed |
| 333 | + |
| 334 | +The integration is minimal by design. Two functions, no subclassing, no changes to your deps type. Just Pydantic AI tools and instructions that connect to a real memory engine. |
| 335 | + |
| 336 | +Memories survive process restarts, build a knowledge graph over time, and compound in value as your agent learns more about each user. For Python developers building Pydantic AI agents that need persistent memory, this is the simplest path from stateless to stateful. |
| 337 | + |
| 338 | +--- |
| 339 | + |
| 340 | +## Next Steps |
| 341 | + |
| 342 | +- **Try it locally**: `pip install hindsight-all hindsight-pydantic-ai "pydantic-ai-slim[openai]"` and run the example above |
| 343 | +- **Use Hindsight Cloud**: Skip self-hosting with a [free account](https://ui.hindsight.vectorize.io/signup) |
| 344 | +- **Tag memories for scoping**: Use `tags` on retain and `recall_tags` on search to partition memories by project, environment, or topic |
| 345 | +- **Combine tools and instructions**: Use `memory_instructions` for automatic context and tools for explicit store and retrieve during conversations |
| 346 | +- **Read the Pydantic AI docs**: Learn more about [Pydantic AI tools and instructions](https://ai.pydantic.dev/tools/) to extend your agent further |
| 347 | +- **Explore other integrations**: Add memory to [CrewAI agents](/blog/2026/03/02/crewai), [OpenAI apps](/blog/2026/03/05/add-memory-to-openai-application), or any framework via [MCP](/blog/2026/03/04/mcp-agent-memory) |
| 348 | +- **Inspect the knowledge graph**: Run the Hindsight control plane or use the [cloud dashboard](https://ui.hindsight.vectorize.io/signup) to browse extracted facts, entities, and relationships |
0 commit comments