Skip to content

Commit ee2bebd

Browse files
DK09876claude
andcommitted
feat: add Haystack agent memory cookbook notebook
Demonstrates persistent memory for Haystack agents using hindsight-haystack: retain, recall, and reflect tools with cross-session memory persistence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d3a4f3f commit ee2bebd

1 file changed

Lines changed: 359 additions & 0 deletions

File tree

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "0",
6+
"metadata": {},
7+
"source": [
8+
"# Haystack Agents with Persistent Memory\n",
9+
"\n",
10+
"Build Haystack agents that remember user preferences and past interactions across conversations using Hindsight memory.\n",
11+
"\n",
12+
"This notebook demonstrates:\n",
13+
"- Creating Hindsight memory tools for a Haystack `Agent`\n",
14+
"- Storing information with `retain_memory`\n",
15+
"- Retrieving memories across sessions with `recall_memory`\n",
16+
"- Synthesizing knowledge with `reflect_on_memory`\n",
17+
"\n",
18+
"## Prerequisites\n",
19+
"\n",
20+
"- Python 3.10+\n",
21+
"- A [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup) account **or** a self-hosted Hindsight instance\n",
22+
"- An OpenAI API key (or any Haystack-supported chat model)\n",
23+
"\n",
24+
"### Option A: Hindsight Cloud\n",
25+
"\n",
26+
"Sign up at [ui.hindsight.vectorize.io](https://ui.hindsight.vectorize.io/signup) and grab your API key from the dashboard.\n",
27+
"\n",
28+
"### Option B: Self-Hosted (Docker)\n",
29+
"\n",
30+
"```bash\n",
31+
"export OPENAI_API_KEY=your-key\n",
32+
"\n",
33+
"docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \\\n",
34+
" -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \\\n",
35+
" -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \\\n",
36+
" -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \\\n",
37+
" ghcr.io/vectorize-io/hindsight:latest\n",
38+
"```"
39+
]
40+
},
41+
{
42+
"cell_type": "markdown",
43+
"id": "1",
44+
"metadata": {},
45+
"source": [
46+
"## Installation"
47+
]
48+
},
49+
{
50+
"cell_type": "code",
51+
"execution_count": null,
52+
"id": "2",
53+
"metadata": {},
54+
"outputs": [],
55+
"source": [
56+
"!pip install hindsight-haystack haystack-ai openai python-dotenv -U"
57+
]
58+
},
59+
{
60+
"cell_type": "markdown",
61+
"id": "3",
62+
"metadata": {},
63+
"source": [
64+
"## Setup\n",
65+
"\n",
66+
"Configure the Hindsight client. Set `HINDSIGHT_API_URL` and `HINDSIGHT_API_KEY` in your `.env` file or environment:\n",
67+
"\n",
68+
"| | Hindsight Cloud | Self-Hosted |\n",
69+
"|---|---|---|\n",
70+
"| `HINDSIGHT_API_URL` | `https://api.hindsight.vectorize.io` | `http://localhost:8888` |\n",
71+
"| `HINDSIGHT_API_KEY` | Your cloud API key | *(not required for local)* |\n",
72+
"\n",
73+
"> **Note:** You also need `OPENAI_API_KEY` set in your environment or `.env` file for the Haystack chat model."
74+
]
75+
},
76+
{
77+
"cell_type": "code",
78+
"execution_count": null,
79+
"id": "4",
80+
"metadata": {},
81+
"outputs": [],
82+
"source": [
83+
"import os\n",
84+
"from dotenv import load_dotenv\n",
85+
"\n",
86+
"load_dotenv()\n",
87+
"\n",
88+
"HINDSIGHT_API_URL = os.getenv(\"HINDSIGHT_API_URL\", \"http://localhost:8888\")\n",
89+
"HINDSIGHT_API_KEY = os.getenv(\"HINDSIGHT_API_KEY\") # Required for Hindsight Cloud\n",
90+
"\n",
91+
"from hindsight_client import Hindsight\n",
92+
"\n",
93+
"client_kwargs = {\"base_url\": HINDSIGHT_API_URL}\n",
94+
"if HINDSIGHT_API_KEY:\n",
95+
" client_kwargs[\"api_key\"] = HINDSIGHT_API_KEY\n",
96+
"\n",
97+
"client = Hindsight(**client_kwargs)"
98+
]
99+
},
100+
{
101+
"cell_type": "markdown",
102+
"id": "5",
103+
"metadata": {},
104+
"source": [
105+
"## Create Memory Tools\n",
106+
"\n",
107+
"`create_hindsight_tools()` returns Haystack `Tool` objects that the agent can call.\n",
108+
"The `mission` parameter auto-creates the memory bank on first use."
109+
]
110+
},
111+
{
112+
"cell_type": "code",
113+
"execution_count": null,
114+
"id": "6",
115+
"metadata": {},
116+
"outputs": [],
117+
"source": [
118+
"# Clean up any leftover bank from a previous run\n",
119+
"try:\n",
120+
" client.delete_bank(\"demo-haystack\")\n",
121+
"except Exception:\n",
122+
" pass"
123+
]
124+
},
125+
{
126+
"cell_type": "code",
127+
"execution_count": null,
128+
"id": "7",
129+
"metadata": {},
130+
"outputs": [],
131+
"source": [
132+
"from hindsight_haystack import create_hindsight_tools\n",
133+
"\n",
134+
"tools = create_hindsight_tools(\n",
135+
" client=client,\n",
136+
" bank_id=\"demo-haystack\",\n",
137+
" mission=\"Track user preferences, background, and project context\",\n",
138+
" tags=[\"source:chat\"],\n",
139+
" retain_context=\"haystack-cookbook\",\n",
140+
")\n",
141+
"\n",
142+
"print(f\"Tools created: {[t.name for t in tools]}\")"
143+
]
144+
},
145+
{
146+
"cell_type": "markdown",
147+
"id": "8",
148+
"metadata": {},
149+
"source": [
150+
"## Build the Agent\n",
151+
"\n",
152+
"Create a Haystack `Agent` with the memory tools and an OpenAI chat generator."
153+
]
154+
},
155+
{
156+
"cell_type": "code",
157+
"execution_count": null,
158+
"id": "9",
159+
"metadata": {},
160+
"outputs": [],
161+
"source": [
162+
"from haystack.components.agents import Agent\n",
163+
"from haystack.components.generators.chat import OpenAIChatGenerator\n",
164+
"from haystack.dataclasses import ChatMessage\n",
165+
"\n",
166+
"agent = Agent(\n",
167+
" chat_generator=OpenAIChatGenerator(model=\"gpt-4o-mini\"),\n",
168+
" tools=tools,\n",
169+
" system_prompt=(\n",
170+
" \"You are a helpful assistant with long-term memory. \"\n",
171+
" \"Use retain_memory to store important facts about the user. \"\n",
172+
" \"Use recall_memory to search your memory before answering. \"\n",
173+
" \"Use reflect_on_memory for thoughtful summaries of what you know.\"\n",
174+
" ),\n",
175+
")"
176+
]
177+
},
178+
{
179+
"cell_type": "markdown",
180+
"id": "10",
181+
"metadata": {},
182+
"source": [
183+
"## Conversation 1: Store Preferences\n",
184+
"\n",
185+
"Tell the agent about yourself. It will use `retain_memory` to store the information in Hindsight."
186+
]
187+
},
188+
{
189+
"cell_type": "code",
190+
"execution_count": null,
191+
"id": "11",
192+
"metadata": {},
193+
"outputs": [],
194+
"source": [
195+
"result = agent.run(\n",
196+
" messages=[\n",
197+
" ChatMessage.from_user(\n",
198+
" \"Hi! I'm Alice. I'm a data scientist who works with Python and SQL. \"\n",
199+
" \"I prefer dark mode and use VS Code. I'm currently working on a \"\n",
200+
" \"recommendation engine for an e-commerce platform.\"\n",
201+
" )\n",
202+
" ]\n",
203+
")\n",
204+
"print(f\"\\nAgent: {result['messages'][-1].text}\")"
205+
]
206+
},
207+
{
208+
"cell_type": "code",
209+
"execution_count": null,
210+
"id": "12",
211+
"metadata": {},
212+
"outputs": [],
213+
"source": [
214+
"import time\n",
215+
"\n",
216+
"# Hindsight processes retained content asynchronously (extracting facts, entities, embeddings).\n",
217+
"# The sleep gives the server time to finish before we recall.\n",
218+
"time.sleep(3)"
219+
]
220+
},
221+
{
222+
"cell_type": "markdown",
223+
"id": "13",
224+
"metadata": {},
225+
"source": [
226+
"## Conversation 2: Recall Across Sessions\n",
227+
"\n",
228+
"Create a new agent instance — simulating a new session. Memory persists because it's\n",
229+
"stored in Hindsight, not in the agent. The agent uses `recall_memory` to find relevant facts."
230+
]
231+
},
232+
{
233+
"cell_type": "code",
234+
"execution_count": null,
235+
"id": "14",
236+
"metadata": {},
237+
"outputs": [],
238+
"source": [
239+
"# New agent instance — fresh session, same memory bank\n",
240+
"agent2 = Agent(\n",
241+
" chat_generator=OpenAIChatGenerator(model=\"gpt-4o-mini\"),\n",
242+
" tools=tools,\n",
243+
" system_prompt=(\n",
244+
" \"You are a helpful assistant with long-term memory. \"\n",
245+
" \"Use recall_memory to search your memory before answering questions.\"\n",
246+
" ),\n",
247+
")\n",
248+
"\n",
249+
"result = agent2.run(\n",
250+
" messages=[ChatMessage.from_user(\"What IDE do I use? And what's my current project?\")]\n",
251+
")\n",
252+
"print(f\"\\nAgent: {result['messages'][-1].text}\")"
253+
]
254+
},
255+
{
256+
"cell_type": "markdown",
257+
"id": "15",
258+
"metadata": {},
259+
"source": [
260+
"## Reflect: Synthesize Knowledge\n",
261+
"\n",
262+
"Use `reflect_on_memory` to get a synthesized, reasoned answer that draws on the full knowledge graph — not just raw facts."
263+
]
264+
},
265+
{
266+
"cell_type": "code",
267+
"execution_count": null,
268+
"id": "16",
269+
"metadata": {},
270+
"outputs": [],
271+
"source": [
272+
"result = agent2.run(\n",
273+
" messages=[\n",
274+
" ChatMessage.from_user(\n",
275+
" \"Based on everything you know about me, what tools and libraries \"\n",
276+
" \"would you recommend for my current project?\"\n",
277+
" )\n",
278+
" ]\n",
279+
")\n",
280+
"print(f\"\\nAgent: {result['messages'][-1].text}\")"
281+
]
282+
},
283+
{
284+
"cell_type": "markdown",
285+
"id": "17",
286+
"metadata": {},
287+
"source": [
288+
"## Selective Tools\n",
289+
"\n",
290+
"Use `include_retain`, `include_recall`, and `include_reflect` to control which tools are exposed."
291+
]
292+
},
293+
{
294+
"cell_type": "code",
295+
"execution_count": null,
296+
"id": "18",
297+
"metadata": {},
298+
"outputs": [],
299+
"source": [
300+
"# Create only retain + recall tools (no reflect)\n",
301+
"selective_tools = create_hindsight_tools(\n",
302+
" client=client,\n",
303+
" bank_id=\"demo-haystack\",\n",
304+
" tags=[\"source:chat\"],\n",
305+
" budget=\"mid\",\n",
306+
" include_reflect=False,\n",
307+
")\n",
308+
"\n",
309+
"print(f\"Selective tools: {[t.name for t in selective_tools]}\")"
310+
]
311+
},
312+
{
313+
"cell_type": "markdown",
314+
"id": "19",
315+
"metadata": {},
316+
"source": [
317+
"## Cleanup"
318+
]
319+
},
320+
{
321+
"cell_type": "code",
322+
"execution_count": null,
323+
"id": "20",
324+
"metadata": {},
325+
"outputs": [],
326+
"source": [
327+
"client.delete_bank(\"demo-haystack\")\n",
328+
"print(\"Bank deleted.\")"
329+
]
330+
},
331+
{
332+
"cell_type": "markdown",
333+
"id": "21",
334+
"metadata": {},
335+
"source": [
336+
"## Key Takeaways\n",
337+
"\n",
338+
"- **Tool Factory** (`create_hindsight_tools`): Returns Haystack `Tool` objects — pass directly to `Agent(tools=...)`.\n",
339+
"- **Three tools**: `retain_memory` (store), `recall_memory` (search), `reflect_on_memory` (synthesize from knowledge graph).\n",
340+
"- **Bank missions**: Use `mission=` to auto-create banks with context for fact extraction — no manual `create_bank` step needed.\n",
341+
"- **Per-user banks**: Use `bank_id=f\"user-{user_id}\"` for per-user memory isolation.\n",
342+
"- **Tags & context**: Scope memories by source, conversation, or topic for precise recall."
343+
]
344+
}
345+
],
346+
"metadata": {
347+
"kernelspec": {
348+
"display_name": "Python 3",
349+
"language": "python",
350+
"name": "python3"
351+
},
352+
"language_info": {
353+
"name": "python",
354+
"version": "3.10.0"
355+
}
356+
},
357+
"nbformat": 4,
358+
"nbformat_minor": 5
359+
}

0 commit comments

Comments
 (0)