Compare commits

...

5 Commits

Author SHA1 Message Date
kartik-mem0 4fb96e9518 docs(cookbooks): rename the adjuster to Sarah and sync outputs to a fresh live run 2026-08-27 15:20:34 +05:30
kartik-mem0 6aa0c27212 docs(cookbooks): label the code tabs TypeScript instead of JavaScript 2026-08-27 15:07:39 +05:30
kartik-mem0 b311527269 docs(cookbooks): drop the sidebar icon from the agent memory page 2026-08-27 15:07:01 +05:30
kartik-mem0 3df9a491e5 docs(cookbooks): add agent memory cookbook page
Adds a narrative cookbook page teaching the user_id/agent_id split for
adjuster preferences vs shared claim memory, reusing the real scenario
and outputs from the agent-memory-insurance notebook. Registers the
page in docs.json and llms.txt, and cross-links the page and notebook.
2026-08-27 14:55:03 +05:30
kartik-mem0 4cdd90cec1 docs(examples): add agent memory cookbook with insurance claims scenario 2026-08-27 14:22:00 +05:30
4 changed files with 1123 additions and 1 deletions
@@ -0,0 +1,278 @@
---
title: Give Agents Shared Working Memory
description: Keep a claims adjuster's personal preferences separate from a claim's shared decisions using user_id, agent_id, and two sets of custom instructions.
---
Sarah is a claims adjuster. Every claim she opens should remember how she likes things drafted. Every claim itself should remember its own decisions, no matter which adjuster opens it next. A single `user_id` and one project-wide instruction set cannot hold both without one leaking into the other.
<Info icon="cloud">
**Works with:** Mem0 Platform (`MemoryClient`)
</Info>
<Info icon="clock">
**Time to complete:** ~15 minutes · **Languages:** Python, TypeScript
</Info>
## Setup
<CodeGroup>
```python Python
import os
from mem0 import MemoryClient
client = MemoryClient(api_key=os.environ["MEM0_API_KEY"])
```
```typescript TypeScript
import { MemoryClient } from "mem0ai";
const client = new MemoryClient({ apiKey: process.env.MEM0_API_KEY });
```
</CodeGroup>
<Note>
Grab an API key from the <a href="https://app.mem0.ai/?utm_source=oss&utm_medium=cookbook-agent-memory" rel="nofollow">Mem0 dashboard</a>. `agent_custom_instructions` needs Python SDK `v2.0.17` or TypeScript SDK `v3.1.5` or later.
</Note>
## Make It Work Once
Claim CLM-48211 is a warehouse fire loss for Meridian Logistics, and Sarah is the adjuster who opened it. Set two rule sets on the project once: `custom_instructions` for what belongs to the adjuster, `agent_custom_instructions` for what belongs to the claim.
<CodeGroup>
```python Python
ADJUSTER = "adjuster_sarah_cookbook_demo"
CLAIM = "claims_agent_clm48211_cookbook_demo"
user_instructions = """Your Task: Track what a claims adjuster needs remembered about themselves, not about any single claim.
Information to Extract:
1. Drafting and communication style
2. Working preferences, such as coverage lines they focus on
Guidelines:
- Store preferences that should carry over to every claim this adjuster works.
Exclude:
- Decisions made on a specific claim (settle vs litigate, reserve changes, subrogation)
- Policy numbers, coverage limits, or other one-off lookups"""
agent_instructions = """Your Task: Track what matters about this specific claim, regardless of which adjuster is working it.
Information to Extract:
1. Claim decisions and reasoning: settle vs litigate calls, reserve changes, subrogation
2. Durable claim facts, such as cause of loss
Guidelines:
- Store facts that the next adjuster to open this claim needs to see.
Exclude:
- Personal style or communication preferences of whichever adjuster is talking"""
client.project.update(
custom_instructions=user_instructions,
agent_custom_instructions=agent_instructions,
)
```
```typescript TypeScript
const ADJUSTER = "adjuster_sarah_cookbook_demo";
const CLAIM = "claims_agent_clm48211_cookbook_demo";
const userInstructions = `Your Task: Track what a claims adjuster needs remembered about themselves, not about any single claim.
Information to Extract:
1. Drafting and communication style
2. Working preferences, such as coverage lines they focus on
Guidelines:
- Store preferences that should carry over to every claim this adjuster works.
Exclude:
- Decisions made on a specific claim (settle vs litigate, reserve changes, subrogation)
- Policy numbers, coverage limits, or other one-off lookups`;
const agentInstructions = `Your Task: Track what matters about this specific claim, regardless of which adjuster is working it.
Information to Extract:
1. Claim decisions and reasoning: settle vs litigate calls, reserve changes, subrogation
2. Durable claim facts, such as cause of loss
Guidelines:
- Store facts that the next adjuster to open this claim needs to see.
Exclude:
- Personal style or communication preferences of whichever adjuster is talking`;
await client.updateProject({
customInstructions: userInstructions,
agentCustomInstructions: agentInstructions,
});
```
</CodeGroup>
Now a normal exchange with the claim, passing both ids on the same `add()` call:
<CodeGroup>
```python Python
client.add(
[
{"role": "user", "content": "I'm raising the reserve on CLM-48211 to $1.8 million. The structural engineer's report says the warehouse needs a full rebuild, not a repair."},
{"role": "assistant", "content": "Got it. Reserve raised to $1.8 million on CLM-48211 for a full rebuild, per the structural engineer's report."},
],
user_id=ADJUSTER,
agent_id=CLAIM,
)
```
```typescript TypeScript
await client.add(
[
{ role: "user", content: "I'm raising the reserve on CLM-48211 to $1.8 million. The structural engineer's report says the warehouse needs a full rebuild, not a repair." },
{ role: "assistant", content: "Got it. Reserve raised to $1.8 million on CLM-48211 for a full rebuild, per the structural engineer's report." },
],
{ userId: ADJUSTER, agentId: CLAIM },
);
```
</CodeGroup>
<Info icon="check">
Expected output: two memories, both attributed to `CLAIM`: `"Reserve for claim CLM-48211 was increased to $1.8 million to cover a full rebuild, as indicated by the structural engineer's report"`, plus the engineer's rebuild finding stored as its own durable fact. Nothing lands on Sarah's personal shelf, the instructions exclude claim decisions from it.
</Info>
## The Problem
Suppose Marcus, a second adjuster, needs to pick up CLM-48211 while Sarah is out. If claim decisions only ever went on the acting adjuster's `user_id`, with no `agent_id` and no `agent_custom_instructions`, Marcus's own memory would be empty no matter what Sarah decided:
```python
marcus_personal = client.get_all(filters={"user_id": "adjuster_marcus_cookbook_demo"})
print(marcus_personal["count"])
```
**Output:**
```
0
```
Marcus has never talked to Mem0 before, so an empty personal shelf is expected. The real gap is structural: if the reserve decision above had gone on Sarah's `user_id` alone, it would live there permanently, and Marcus would have no identifier that reaches it. The claim's history would be trapped on one person's shelf.
## Fix It: Give the Claim Its Own Shelf
`agent_id` fixes this because it names the claim, not the person handling it. Anyone who searches with that `agent_id` sees the same claim history, regardless of whose `user_id` they search alongside:
<CodeGroup>
```python Python
marcus_view = client.search(
"What's going on with CLM-48211?",
filters={"OR": [{"user_id": "adjuster_marcus_cookbook_demo"}, {"agent_id": CLAIM}]},
)
for r in marcus_view["results"]:
print(f"- [{r.get('user_id') or r.get('agent_id')}] {r['memory']}")
```
```typescript TypeScript
const marcusView = await client.search(
"What's going on with CLM-48211?",
{ filters: { OR: [{ userId: "adjuster_marcus_cookbook_demo" }, { agentId: CLAIM }] } },
);
for (const r of marcusView.results) {
console.log(`- [${r.userId ?? r.agentId}] ${r.memory}`);
}
```
</CodeGroup>
**Output** (from the full notebook run, four claim facts on file):
```
- [claims_agent_clm48211_cookbook_demo] Reserve for claim CLM-48211 was increased to $1.8 million to cover a full rebuild, as indicated by the structural engineer's report
- [claims_agent_clm48211_cookbook_demo] The structural engineer's report for claim CLM-48211 states that the warehouse requires a full rebuild rather than a repair
- [claims_agent_clm48211_cookbook_demo] The building coverage limit for claim CLM-48211 under the Meridian Logistics policy is $2,400,000 with a $25,000 deductible
- [claims_agent_clm48211_cookbook_demo] Assistant noted that subrogation will be pursued against Voss Electrical, with the fire marshal's report identifying faulty wiring as the ignition source
```
Marcus sees the full claim history and none of Sarah's personal preferences, because those never carried `agent_id` attribution in the first place.
## Build On It: One Message, Two Shelves
A single message can touch both shelves in one `add()` call. Sarah makes a claim decision and states a personal preference in the same breath:
<CodeGroup>
```python Python
client.add(
[
{"role": "user", "content": "I've decided to pursue subrogation against Voss Electrical, the contractor who rewired the warehouse six months ago. The fire marshal's report points to faulty wiring as the ignition source. Also, from now on keep my claim summaries to bullet points, not paragraphs."},
{"role": "assistant", "content": "Noted both. Pursuing subrogation against Voss Electrical based on the fire marshal's faulty-wiring finding, and I'll keep your summaries in bullet points going forward."},
],
user_id=ADJUSTER,
agent_id=CLAIM,
)
```
```typescript TypeScript
await client.add(
[
{ role: "user", content: "I've decided to pursue subrogation against Voss Electrical, the contractor who rewired the warehouse six months ago. The fire marshal's report points to faulty wiring as the ignition source. Also, from now on keep my claim summaries to bullet points, not paragraphs." },
{ role: "assistant", content: "Noted both. Pursuing subrogation against Voss Electrical based on the fire marshal's faulty-wiring finding, and I'll keep your summaries in bullet points going forward." },
],
{ userId: ADJUSTER, agentId: CLAIM },
);
```
</CodeGroup>
**Output:** two memories from one call, one per shelf. The subrogation decision lands on the claim shelf with the fire marshal's faulty-wiring finding folded into its reasoning, and the bullet-point formatting rule lands on Sarah's personal shelf.
<Warning>
Extraction makes judgment calls, it does not mirror your instructions line by line. In the full notebook run, a one-off policy-limit lookup (excluded by name in the instructions above) still landed on the claim shelf, because it was judged a durable fact worth keeping for the life of the claim. Read what actually got stored with `get_all()` rather than assuming the instructions were applied literally.
</Warning>
## Production Patterns
- **Reviewing and correcting a personal shelf**: `get_all()` with the adjuster's `user_id`, then act on what is actually stored.
<CodeGroup>
```python Python
sarah_shelf = client.get_all(filters={"user_id": ADJUSTER})
for r in sarah_shelf["results"]:
print(r["id"], "-", r["memory"])
```
```typescript TypeScript
const sarahShelf = await client.getAll({ filters: { userId: ADJUSTER } });
for (const r of sarahShelf.results) {
console.log(r.id, "-", r.memory);
}
```
</CodeGroup>
- **Editing without losing a neighbor**: `delete()` removes a whole memory, not a substring of one. If two preferences were extracted into a single combined memory, deleting it to revise one loses both. Use `update()` to rewrite the text in place instead:
<CodeGroup>
```python Python
client.update(memory_id, text="User prefers drafts to contain no em dashes")
```
```typescript TypeScript
await client.update(memoryId, { text: "User prefers drafts to contain no em dashes" });
```
</CodeGroup>
## What You Built
- **A personal shelf per adjuster**: `user_id` plus `custom_instructions` for preferences that follow one person across every claim they touch.
- **A shared shelf per claim**: `agent_id` plus `agent_custom_instructions` for decisions and facts that outlive any single adjuster's session.
- **One query across both**: an `OR` filter on `user_id` and `agent_id` for status updates that need the person's context and the claim's history together.
## Production Checklist
- Set `agent_custom_instructions` alongside `custom_instructions` in the same `project.update()` call, an unset agent instruction set silently falls back to the user one.
- Pass both `user_id` and `agent_id` on every `add()` call for a claim conversation, not just one or the other.
- Use `update()` to correct a single fact; reach for `delete()` only when you mean to remove the whole memory record.
- Delete demo or test data with `delete_all(user_id=...)` and `delete_all(agent_id=...)`, and restore any project instructions you changed for a test run.
## Next Steps
<CardGroup cols={2}>
<Card
title="Custom Instructions"
description="The full reference for custom_instructions and agent_custom_instructions, including per-call overrides."
icon="shield-check"
href="/platform/features/custom-instructions"
/>
<Card
title="Run the Full Notebook"
description="The same scenario end to end, executed live against the Mem0 Platform API with real outputs."
icon="rocket"
href="https://github.com/mem0ai/mem0/blob/main/examples/notebooks/agent-memory-insurance.ipynb"
/>
</CardGroup>
<Snippet file="star-on-github.mdx" />
+2 -1
View File
@@ -406,7 +406,8 @@
"cookbooks/essentials/building-ai-companion",
"cookbooks/essentials/entity-partitioning-playbook",
"cookbooks/essentials/tagging-and-organizing-memories",
"cookbooks/essentials/exporting-memories"
"cookbooks/essentials/exporting-memories",
"cookbooks/essentials/agent-memory-insurance-claims"
]
},
{
+1
View File
@@ -300,6 +300,7 @@ If the user is on a pre-current major (Python < 2, TS < 3, or a Platform call st
- [Partition Memories by Entity](https://docs.mem0.ai/cookbooks/essentials/entity-partitioning-playbook) [Both]: Use when isolating multi-tenant memories.
- [Tagging and Organizing Memories](https://docs.mem0.ai/cookbooks/essentials/tagging-and-organizing-memories) [Both]: Use when memory taxonomy matters.
- [Exporting Memories](https://docs.mem0.ai/cookbooks/essentials/exporting-memories) [Both]: Use when backing up or migrating memory data.
- [Give Agents Shared Working Memory](https://docs.mem0.ai/cookbooks/essentials/agent-memory-insurance-claims) [Platform]: Use when a shared record must outlive the individual using it, such as a claim, ticket, or account that multiple people work over time.
### AI Companions
- [Quickstart Demo](https://docs.mem0.ai/cookbooks/companions/quickstart-demo) [Both]: Use when showing the smallest end-to-end companion.
@@ -0,0 +1,842 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Agent Memory: An Insurance Claims Cookbook\n",
"\n",
"This notebook builds a claims workflow on top of Mem0 where two kinds of memory live side by side:\n",
"\n",
"- **A claims adjuster's own preferences**: drafting style, communication rules, the lines of coverage they focus on. These follow the adjuster from claim to claim and are private to them.\n",
"- **A claim's working memory**: the decisions made on one specific claim and why, plus durable facts about that claim. This is shared by every adjuster who opens the claim, because a claim outlives any single person handling it.\n",
"\n",
"Mem0 keeps these apart using two identifiers on every `add()` call, `user_id` for the adjuster and `agent_id` for the claim, plus two separate sets of extraction rules, `custom_instructions` for the adjuster shelf and `agent_custom_instructions` for the claim shelf. This mirrors the id convention used in Mem0's CoCounsel example, where a lawyer maps to `user_id` and a case maps to `agent_id`. Here the adjuster is the person, and the claim is the shared case file.\n",
"\n",
"This notebook was executed live against the Mem0 Platform API. Every output below is a real response, not a mock.\n",
"\n",
"For the narrative walkthrough of this same pattern, see the [Give Agents Shared Working Memory](https://docs.mem0.ai/cookbooks/essentials/agent-memory-insurance-claims) cookbook page."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:08.774876Z",
"iopub.status.busy": "2026-08-27T09:46:08.774699Z",
"iopub.status.idle": "2026-08-27T09:46:09.471287Z",
"shell.execute_reply": "2026-08-27T09:46:09.470775Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Note: you may need to restart the kernel to use updated packages.\n"
]
}
],
"source": [
"%pip install -q mem0ai"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:09.472860Z",
"iopub.status.busy": "2026-08-27T09:46:09.472717Z",
"iopub.status.idle": "2026-08-27T09:46:14.361722Z",
"shell.execute_reply": "2026-08-27T09:46:14.361005Z"
}
},
"outputs": [],
"source": [
"import json\n",
"import os\n",
"import time\n",
"\n",
"from mem0 import MemoryClient\n",
"\n",
"os.environ[\"MEM0_API_KEY\"] = \"YOUR_MEM0_API_KEY\"\n",
"client = MemoryClient()\n",
"\n",
"ADJUSTER_SARAH = \"adjuster_sarah_cookbook_demo\"\n",
"ADJUSTER_MARCUS = \"adjuster_marcus_cookbook_demo\"\n",
"CLAIM_AGENT = \"claims_agent_clm48211_cookbook_demo\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. The mapping\n",
"\n",
"Claim CLM-48211 is a commercial property fire loss at a Meridian Logistics warehouse. Sarah is the adjuster who opened it.\n",
"\n",
"| Mem0 identifier | Maps to | What lives there |\n",
"|---|---|---|\n",
"| `user_id` | An individual adjuster, e.g. Sarah | Her drafting style, her communication rules, durable across every claim she works |\n",
"| `agent_id` | One claim's agent instance, e.g. `claims_agent_clm48211` | Decisions made on this claim and why, plus durable facts about it, shared by anyone who opens the claim |\n",
"\n",
"Every `add()` call below passes both `user_id=ADJUSTER_SARAH` and `agent_id=CLAIM_AGENT` at once. Mem0 does not require you to pick a shelf ahead of time, it extracts facts from the conversation and routes each one against the rules for its shelf."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Configure the two rule sets\n",
"\n",
"`custom_instructions` governs memories that land under `user_id`. `agent_custom_instructions` is a second, optional rule set that governs memories that land under `agent_id`. Until you set it, agent-scoped memories fall back to `custom_instructions`, which is why the next cell sets both at once.\n",
"\n",
"Before changing anything, save what the project already has, so it can be restored at the end of this notebook."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:14.364467Z",
"iopub.status.busy": "2026-08-27T09:46:14.364243Z",
"iopub.status.idle": "2026-08-27T09:46:14.692628Z",
"shell.execute_reply": "2026-08-27T09:46:14.692110Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"had custom_instructions: True\n",
"had agent_custom_instructions: False\n"
]
}
],
"source": [
"project_snapshot = client.project.get(fields=[\"custom_instructions\", \"agent_custom_instructions\"])\n",
"print(\"had custom_instructions:\", bool(project_snapshot.get(\"custom_instructions\")))\n",
"print(\"had agent_custom_instructions:\", bool(project_snapshot.get(\"agent_custom_instructions\")))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:14.694276Z",
"iopub.status.busy": "2026-08-27T09:46:14.694156Z",
"iopub.status.idle": "2026-08-27T09:46:15.051548Z",
"shell.execute_reply": "2026-08-27T09:46:15.050970Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'message': 'Updated custom instructions and agent custom instructions'}\n"
]
}
],
"source": [
"USER_INSTRUCTIONS = \"\"\"Your Task: Track what a claims adjuster needs remembered about themselves, not about any single claim.\n",
"\n",
"Information to Extract:\n",
"1. Drafting and communication style:\n",
" - Tone, formatting, and length preferences for letters and summaries\n",
" - Rules for how the assistant should write on their behalf\n",
"\n",
"2. Working preferences:\n",
" - Coverage lines or claim types they focus on\n",
" - How they like updates delivered\n",
"\n",
"Guidelines:\n",
"- Store preferences that should carry over to every claim this adjuster works, not just the current one.\n",
"\n",
"Exclude:\n",
"- Decisions made on a specific claim (settle vs litigate, reserve changes, subrogation, coverage determinations)\n",
"- Policy numbers, coverage limits, or other one-off lookups\n",
"- Greetings, small talk, and weather\"\"\"\n",
"\n",
"AGENT_INSTRUCTIONS = \"\"\"Your Task: Track what matters about this specific claim, regardless of which adjuster is working it.\n",
"\n",
"Information to Extract:\n",
"1. Claim decisions and reasoning:\n",
" - Settle vs litigate calls\n",
" - Reserve changes and why\n",
" - Subrogation decisions\n",
" - Coverage determinations\n",
"\n",
"2. Durable claim facts:\n",
" - Cause of loss and other facts that stay true for the life of the claim\n",
"\n",
"Guidelines:\n",
"- Store facts that the next adjuster to open this claim needs to see.\n",
"\n",
"Exclude:\n",
"- Personal style or communication preferences of whichever adjuster is talking\n",
"- One-off lookups that go stale (current weather, a single quoted estimate)\n",
"- Greetings and small talk\"\"\"\n",
"\n",
"update_response = client.project.update(\n",
" custom_instructions=USER_INSTRUCTIONS,\n",
" agent_custom_instructions=AGENT_INSTRUCTIONS,\n",
")\n",
"print(update_response)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Day 1: Sarah works the claim\n",
"\n",
"`add()` on the Platform is asynchronous, it returns a `PENDING` status and an `event_id`, and extraction finishes in the background. A production agent does not need to wait for it, but this notebook does, so the next cell reads its own writes. The small polling helper below is only useful for a demo like this one."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:15.053514Z",
"iopub.status.busy": "2026-08-27T09:46:15.053386Z",
"iopub.status.idle": "2026-08-27T09:46:15.057906Z",
"shell.execute_reply": "2026-08-27T09:46:15.057402Z"
}
},
"outputs": [],
"source": [
"def wait_for_event(client, event_id, timeout=90, poll_every=2):\n",
" deadline = time.time() + timeout\n",
" while time.time() < deadline:\n",
" event = client.client.get(f\"/v1/event/{event_id}/\").json()\n",
" if event[\"status\"] in (\"SUCCEEDED\", \"FAILED\"):\n",
" return event\n",
" time.sleep(poll_every)\n",
" raise TimeoutError(f\"Event {event_id} did not finish in {timeout}s\")\n",
"\n",
"\n",
"def add_and_wait(client, messages, **kwargs):\n",
" response = client.add(messages, **kwargs)\n",
" event = wait_for_event(client, response[\"event_id\"])\n",
" if event[\"status\"] == \"FAILED\":\n",
" raise RuntimeError(f\"Add failed: {event.get('error')}\")\n",
" return event\n",
"\n",
"\n",
"def summarize(event):\n",
" print(\"status:\", event[\"status\"], \"| created:\", len(event.get(\"results\", [])))\n",
" for r in event.get(\"results\", []):\n",
" print(\" -\", r[\"data\"][\"memory\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sarah starts the day by pulling a number off the policy."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:15.059720Z",
"iopub.status.busy": "2026-08-27T09:46:15.059580Z",
"iopub.status.idle": "2026-08-27T09:46:23.223178Z",
"shell.execute_reply": "2026-08-27T09:46:23.222458Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[\n",
" {\n",
" \"id\": \"c76bf47b-c5bf-4893-95d5-6ae8c5a4177b\",\n",
" \"data\": {\n",
" \"memory\": \"The building coverage limit for claim CLM-48211 under the Meridian Logistics policy is $2,400,000 with a $25,000 deductible\"\n",
" },\n",
" \"event\": \"ADD\",\n",
" \"user_id\": \"adjuster_sarah_cookbook_demo\",\n",
" \"event_end\": null,\n",
" \"event_date\": null,\n",
" \"event_start\": null,\n",
" \"memory_type\": null,\n",
" \"plan_status\": null,\n",
" \"attributed_to\": \"assistant\"\n",
" }\n",
"]\n"
]
}
],
"source": [
"event = add_and_wait(\n",
" client,\n",
" [\n",
" {\"role\": \"user\", \"content\": \"What's the building coverage limit on the Meridian Logistics policy for claim CLM-48211?\"},\n",
" {\"role\": \"assistant\", \"content\": \"Checking the policy now. The building coverage limit is $2,400,000 with a $25,000 deductible.\"},\n",
" ],\n",
" user_id=ADJUSTER_SARAH,\n",
" agent_id=CLAIM_AGENT,\n",
")\n",
"print(json.dumps(event[\"results\"], indent=2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice the created record's own `user_id` field. That field just echoes who the call was made for, it is not proof of which shelf the memory actually landed on. `attributed_to` is closer, but the only reliable check is asking Mem0 directly with `get_all()`, which section 5 does. Keep that in mind through the rest of this notebook, a coverage limit like this one turns out to be exactly the kind of durable claim fact `agent_custom_instructions` asks Mem0 to keep, so it is worth checking where it really ends up.\n",
"\n",
"Next, a real decision with the reasoning behind it."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:23.225503Z",
"iopub.status.busy": "2026-08-27T09:46:23.225354Z",
"iopub.status.idle": "2026-08-27T09:46:50.042538Z",
"shell.execute_reply": "2026-08-27T09:46:50.041648Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"status: SUCCEEDED | created: 2\n",
" - Reserve for claim CLM-48211 was increased to $1.8 million to cover a full rebuild, as indicated by the structural engineer's report\n",
" - The structural engineer's report for claim CLM-48211 states that the warehouse requires a full rebuild rather than a repair\n"
]
}
],
"source": [
"event = add_and_wait(\n",
" client,\n",
" [\n",
" {\"role\": \"user\", \"content\": \"I'm raising the reserve on CLM-48211 to $1.8 million. The structural engineer's report says the warehouse needs a full rebuild, not a repair.\"},\n",
" {\"role\": \"assistant\", \"content\": \"Got it. Reserve raised to $1.8 million on CLM-48211 for a full rebuild, per the structural engineer's report. I'll keep that on file for the claim.\"},\n",
" ],\n",
" user_id=ADJUSTER_SARAH,\n",
" agent_id=CLAIM_AGENT,\n",
")\n",
"summarize(event)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then a personal rule for how Sarah wants things written."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:50.044560Z",
"iopub.status.busy": "2026-08-27T09:46:50.044403Z",
"iopub.status.idle": "2026-08-27T09:46:58.104329Z",
"shell.execute_reply": "2026-08-27T09:46:58.103578Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"status: SUCCEEDED | created: 1\n",
" - User prefers that any drafted documents contain no em dashes and that coverage position letters be limited to one page\n"
]
}
],
"source": [
"event = add_and_wait(\n",
" client,\n",
" [\n",
" {\"role\": \"user\", \"content\": \"Also, for anything you draft for me: no em dashes, and keep coverage position letters to one page.\"},\n",
" {\"role\": \"assistant\", \"content\": \"Understood. No em dashes, and coverage letters kept to one page, for everything I draft for you.\"},\n",
" ],\n",
" user_id=ADJUSTER_SARAH,\n",
" agent_id=CLAIM_AGENT,\n",
")\n",
"summarize(event)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And finally, small talk that neither rule set wants."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:46:58.106438Z",
"iopub.status.busy": "2026-08-27T09:46:58.106284Z",
"iopub.status.idle": "2026-08-27T09:47:04.002795Z",
"shell.execute_reply": "2026-08-27T09:47:04.002018Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"status: SUCCEEDED | created: 0\n"
]
}
],
"source": [
"event = add_and_wait(\n",
" client,\n",
" [\n",
" {\"role\": \"user\", \"content\": \"Thanks. Quick one, what's the weather like in Chicago right now?\"},\n",
" {\"role\": \"assistant\", \"content\": \"It's 72 degrees and clear in Chicago right now.\"},\n",
" ],\n",
" user_id=ADJUSTER_SARAH,\n",
" agent_id=CLAIM_AGENT,\n",
")\n",
"summarize(event)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. One message, two shelves\n",
"\n",
"Mem0 does not split a memory across `user_id` and `agent_id`, each extracted fact is attributed to one shelf or the other. When both identifiers are passed to the same `add()` call, Mem0 decides per fact which rule set it matches, so a single exchange that mixes a claim decision with a personal preference comes back split correctly without you doing the routing by hand."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:04.005189Z",
"iopub.status.busy": "2026-08-27T09:47:04.005036Z",
"iopub.status.idle": "2026-08-27T09:47:28.624576Z",
"shell.execute_reply": "2026-08-27T09:47:28.623569Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"status: SUCCEEDED | created: 2\n",
" - User prefers claim summaries to be written as bullet points instead of paragraphs\n",
" - Assistant noted that subrogation will be pursued against Voss Electrical, with the fire marshal's report identifying faulty wiring as the ignition source\n"
]
}
],
"source": [
"event = add_and_wait(\n",
" client,\n",
" [\n",
" {\"role\": \"user\", \"content\": \"I've decided to pursue subrogation against Voss Electrical, the contractor who rewired the warehouse six months ago. The fire marshal's report points to faulty wiring as the ignition source. Also, from now on keep my claim summaries to bullet points, not paragraphs.\"},\n",
" {\"role\": \"assistant\", \"content\": \"Noted both. Pursuing subrogation against Voss Electrical based on the fire marshal's faulty-wiring finding, and I'll keep your summaries in bullet points going forward.\"},\n",
" ],\n",
" user_id=ADJUSTER_SARAH,\n",
" agent_id=CLAIM_AGENT,\n",
")\n",
"summarize(event)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"One exchange produced two memories on two different shelves: a bullet-point formatting preference for Sarah, and a subrogation decision for the claim. Notice the cause of loss (faulty wiring) never got its own memory this run, Mem0 folded it into the subrogation memory's reasoning. Extraction makes judgment calls like that, so read what was actually stored instead of assuming."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Verify the split\n",
"\n",
"`get_all()` with a `filters` dict is the reliable way to check where memories actually landed, scoped to one shelf at a time."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:28.626951Z",
"iopub.status.busy": "2026-08-27T09:47:28.626768Z",
"iopub.status.idle": "2026-08-27T09:47:29.109030Z",
"shell.execute_reply": "2026-08-27T09:47:29.108411Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2 memories on Sarah's shelf\n",
"- User prefers claim summaries to be written as bullet points instead of paragraphs\n",
"- User prefers that any drafted documents contain no em dashes and that coverage position letters be limited to one page\n"
]
}
],
"source": [
"def dump(results, empty_label=\"(none)\"):\n",
" if not results:\n",
" print(empty_label)\n",
" return\n",
" for r in results:\n",
" print(\"-\", r[\"memory\"])\n",
"\n",
"\n",
"sarah_all = client.get_all(filters={\"user_id\": ADJUSTER_SARAH})\n",
"print(f\"{sarah_all['count']} memories on Sarah's shelf\")\n",
"dump(sarah_all[\"results\"])"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:29.110873Z",
"iopub.status.busy": "2026-08-27T09:47:29.110737Z",
"iopub.status.idle": "2026-08-27T09:47:29.597248Z",
"shell.execute_reply": "2026-08-27T09:47:29.596174Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4 memories on the claim's shelf\n",
"- Assistant noted that subrogation will be pursued against Voss Electrical, with the fire marshal's report identifying faulty wiring as the ignition source\n",
"- The structural engineer's report for claim CLM-48211 states that the warehouse requires a full rebuild rather than a repair\n",
"- Reserve for claim CLM-48211 was increased to $1.8 million to cover a full rebuild, as indicated by the structural engineer's report\n",
"- The building coverage limit for claim CLM-48211 under the Meridian Logistics policy is $2,400,000 with a $25,000 deductible\n"
]
}
],
"source": [
"claim_all = client.get_all(filters={\"agent_id\": CLAIM_AGENT})\n",
"print(f\"{claim_all['count']} memories on the claim's shelf\")\n",
"dump(claim_all[\"results\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sarah's shelf holds two personal style rules and nothing about the claim. The claim's shelf holds four claim facts, the coverage limit, the reserve change, the engineer's rebuild finding, and the subrogation call with the cause of loss folded in, and nothing about how Sarah likes things written. The small talk from section 3 is on neither shelf, it was correctly dropped."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Day 2\n",
"\n",
"Sarah comes back the next day and asks for a status update. A search scoped with `OR` across both identifiers pulls from both shelves at once, ranked by relevance to the query."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:29.599237Z",
"iopub.status.busy": "2026-08-27T09:47:29.599098Z",
"iopub.status.idle": "2026-08-27T09:47:30.099318Z",
"shell.execute_reply": "2026-08-27T09:47:30.098227Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"- Reserve for claim CLM-48211 was increased to $1.8 million to cover a full rebuild, as indicated by the structural engineer's report\n",
"- The structural engineer's report for claim CLM-48211 states that the warehouse requires a full rebuild rather than a repair\n",
"- The building coverage limit for claim CLM-48211 under the Meridian Logistics policy is $2,400,000 with a $25,000 deductible\n",
"- User prefers that any drafted documents contain no em dashes and that coverage position letters be limited to one page\n",
"- User prefers claim summaries to be written as bullet points instead of paragraphs\n",
"- Assistant noted that subrogation will be pursued against Voss Electrical, with the fire marshal's report identifying faulty wiring as the ignition source\n"
]
}
],
"source": [
"status_results = client.search(\n",
" \"Draft a status update on claim CLM-48211.\",\n",
" filters={\"OR\": [{\"user_id\": ADJUSTER_SARAH}, {\"agent_id\": CLAIM_AGENT}]},\n",
")\n",
"dump(status_results[\"results\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"One query, one search call, both shelves, correctly ranked. This is what makes the split useful day to day, an agent drafting on Sarah's behalf does not need to know ahead of time which shelf a given fact lives on."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7. A second adjuster opens the same claim\n",
"\n",
"Marcus has never worked with Sarah and has never touched CLM-48211 before. His personal shelf should be empty."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:30.101305Z",
"iopub.status.busy": "2026-08-27T09:47:30.101161Z",
"iopub.status.idle": "2026-08-27T09:47:30.528115Z",
"shell.execute_reply": "2026-08-27T09:47:30.527276Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 memories on Marcus's personal shelf\n",
"(none)\n"
]
}
],
"source": [
"marcus_personal = client.get_all(filters={\"user_id\": ADJUSTER_MARCUS})\n",
"print(f\"{marcus_personal['count']} memories on Marcus's personal shelf\")\n",
"dump(marcus_personal[\"results\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"But the claim's shelf is not private to Sarah, it belongs to the claim. When Marcus opens CLM-48211 cold, he sees exactly what she left behind."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:30.530364Z",
"iopub.status.busy": "2026-08-27T09:47:30.530205Z",
"iopub.status.idle": "2026-08-27T09:47:31.017665Z",
"shell.execute_reply": "2026-08-27T09:47:31.016750Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"- Reserve for claim CLM-48211 was increased to $1.8 million to cover a full rebuild, as indicated by the structural engineer's report\n",
"- The structural engineer's report for claim CLM-48211 states that the warehouse requires a full rebuild rather than a repair\n",
"- The building coverage limit for claim CLM-48211 under the Meridian Logistics policy is $2,400,000 with a $25,000 deductible\n",
"- Assistant noted that subrogation will be pursued against Voss Electrical, with the fire marshal's report identifying faulty wiring as the ignition source\n"
]
}
],
"source": [
"marcus_view = client.search(\n",
" \"What's going on with CLM-48211?\",\n",
" filters={\"OR\": [{\"user_id\": ADJUSTER_MARCUS}, {\"agent_id\": CLAIM_AGENT}]},\n",
")\n",
"dump(marcus_view[\"results\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Four claim facts, zero of Sarah's personal preferences. Marcus gets full context on the claim without inheriting a single one of her style rules, exactly the separation the two rule sets were set up to produce."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 8. Review, edit, and forget\n",
"\n",
"Sarah can inspect and prune her own shelf directly."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:31.019536Z",
"iopub.status.busy": "2026-08-27T09:47:31.019388Z",
"iopub.status.idle": "2026-08-27T09:47:31.495240Z",
"shell.execute_reply": "2026-08-27T09:47:31.494231Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"fab356e1-1603-407e-9f99-24a56c08ff6c - User prefers claim summaries to be written as bullet points instead of paragraphs\n",
"895f5c47-64bf-44c7-a36f-b37697be5037 - User prefers that any drafted documents contain no em dashes and that coverage position letters be limited to one page\n"
]
}
],
"source": [
"sarah_review = client.get_all(filters={\"user_id\": ADJUSTER_SARAH})\n",
"for r in sarah_review[\"results\"]:\n",
" print(r[\"id\"], \"-\", r[\"memory\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The one-page rule is stale, the carrier switched templates. `delete()` removes a memory by id."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:31.497312Z",
"iopub.status.busy": "2026-08-27T09:47:31.497155Z",
"iopub.status.idle": "2026-08-27T09:47:32.650786Z",
"shell.execute_reply": "2026-08-27T09:47:32.650172Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"- User prefers claim summaries to be written as bullet points instead of paragraphs\n"
]
}
],
"source": [
"stale = next(r for r in sarah_review[\"results\"] if \"one page\" in r[\"memory\"].lower() or \"letter\" in r[\"memory\"].lower())\n",
"client.delete(stale[\"id\"])\n",
"\n",
"sarah_after = client.get_all(filters={\"user_id\": ADJUSTER_SARAH})\n",
"dump(sarah_after[\"results\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Worth noticing: the no-em-dashes rule and the one-page rule were extracted into a single combined memory, so deleting it for the stale part removed the em-dash rule too. `delete()` removes a whole memory, not a substring of one. To edit a fact in place without losing its neighbors, use `client.update(memory_id, text=...)` to rewrite the text instead of deleting it.\n",
"\n",
"This notebook created demo data under three throwaway identifiers and changed the project's shared extraction rules to run its examples. Clean both up before you go, delete the demo memories, then restore whatever `custom_instructions` and `agent_custom_instructions` the project had before this notebook touched them."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"execution": {
"iopub.execute_input": "2026-08-27T09:47:32.652435Z",
"iopub.status.busy": "2026-08-27T09:47:32.652317Z",
"iopub.status.idle": "2026-08-27T09:47:34.183374Z",
"shell.execute_reply": "2026-08-27T09:47:34.182528Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"demo memories deleted\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"project instructions restored\n"
]
}
],
"source": [
"client.delete_all(user_id=ADJUSTER_SARAH)\n",
"client.delete_all(user_id=ADJUSTER_MARCUS)\n",
"client.delete_all(agent_id=CLAIM_AGENT)\n",
"print(\"demo memories deleted\")\n",
"\n",
"client.project.update(\n",
" custom_instructions=project_snapshot.get(\"custom_instructions\") or \"\",\n",
" agent_custom_instructions=project_snapshot.get(\"agent_custom_instructions\") or \"\",\n",
")\n",
"print(\"project instructions restored\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 9. Recap\n",
"\n",
"| Concept | This notebook | CoCounsel example |\n",
"|---|---|---|\n",
"| `user_id` | One claims adjuster, durable across every claim they work | One lawyer |\n",
"| `agent_id` | One claim's agent instance, shared by every adjuster who opens it | One case |\n",
"| `custom_instructions` | Governs the adjuster's personal shelf | Governs the lawyer's personal shelf |\n",
"| `agent_custom_instructions` | Governs the claim's shared shelf | Governs the case's shared shelf |\n",
"\n",
"The pattern is not specific to insurance. Anywhere a person works inside a longer-lived, shared context, a support agent inside a ticket, a sales rep inside an account, a caseworker inside a file, the same two identifiers and two rule sets separate what belongs to the person from what belongs to the thing they are working on."
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}