docs(examples): add agent memory cookbook with insurance claims scenario
This commit is contained in:
@@ -0,0 +1,690 @@
|
||||
{
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -q mem0ai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"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_PRIYA = \"adjuster_priya_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. Priya is the adjuster who opened it.\n",
|
||||
"\n",
|
||||
"| Mem0 identifier | Maps to | What lives there |\n",
|
||||
"|---|---|---|\n",
|
||||
"| `user_id` | An individual adjuster, e.g. Priya | 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_PRIYA` 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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"had custom_instructions: True\n",
|
||||
"had agent_custom_instructions: False"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"{'message': 'Updated custom instructions and agent custom instructions'}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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: Priya 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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"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": [
|
||||
"Priya starts the day by pulling a number off the policy."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"[\n",
|
||||
" {\n",
|
||||
" \"event_start\": null,\n",
|
||||
" \"event_end\": null,\n",
|
||||
" \"data\": {\n",
|
||||
" \"memory\": \"The building coverage limit on the Meridian Logistics policy for claim CLM-48211 is $2,400,000 with a $25,000 deductible\"\n",
|
||||
" },\n",
|
||||
" \"user_id\": \"adjuster_priya_cookbook_demo\",\n",
|
||||
" \"attributed_to\": \"assistant\",\n",
|
||||
" \"event_date\": null,\n",
|
||||
" \"memory_type\": null,\n",
|
||||
" \"id\": \"79a37e82-9188-4ff2-b504-d1a8d84abb3b\",\n",
|
||||
" \"event\": \"ADD\",\n",
|
||||
" \"plan_status\": null\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_PRIYA,\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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"status: SUCCEEDED | created: 1\n",
|
||||
" - Reserve on claim CLM-48211 was raised to $1.8 million because the structural engineer's report indicated the warehouse requires a full rebuild rather than a repair"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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_PRIYA,\n",
|
||||
" agent_id=CLAIM_AGENT,\n",
|
||||
")\n",
|
||||
"summarize(event)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Then a personal rule for how Priya wants things written."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"status: SUCCEEDED | created: 1\n",
|
||||
" - User prefers drafts to contain no em dashes and coverage position letters to be limited to one page"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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_PRIYA,\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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"status: SUCCEEDED | created: 0"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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_PRIYA,\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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"status: SUCCEEDED | created: 3\n",
|
||||
" - User prefers claim summaries to be formatted as bullet points rather than paragraphs for all claims\n",
|
||||
" - Assistant recorded that subrogation is being pursued against Voss Electrical for claim CLM-48211 based on the fire marshal's finding of faulty wiring as the ignition source\n",
|
||||
" - Assistant noted that the cause of loss for claim CLM-48211 is faulty wiring identified by the fire marshal's report"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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_PRIYA,\n",
|
||||
" agent_id=CLAIM_AGENT,\n",
|
||||
")\n",
|
||||
"summarize(event)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"One exchange produced three memories: a personal formatting preference, a subrogation decision, and a durable claim fact (the cause of loss) that neither of us mentioned as a separate sentence, Mem0 pulled it out of the subrogation reasoning on its own."
|
||||
]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"2 memories on Priya's shelf\n",
|
||||
"- User prefers claim summaries to be formatted as bullet points rather than paragraphs for all claims\n",
|
||||
"- User prefers drafts to contain no em dashes and coverage position letters to be limited to one page"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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",
|
||||
"priya_all = client.get_all(filters={\"user_id\": ADJUSTER_PRIYA})\n",
|
||||
"print(f\"{priya_all['count']} memories on Priya's shelf\")\n",
|
||||
"dump(priya_all[\"results\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"4 memories on the claim's shelf\n",
|
||||
"- Assistant recorded that subrogation is being pursued against Voss Electrical for claim CLM-48211 based on the fire marshal's finding of faulty wiring as the ignition source\n",
|
||||
"- Assistant noted that the cause of loss for claim CLM-48211 is faulty wiring identified by the fire marshal's report\n",
|
||||
"- Reserve on claim CLM-48211 was raised to $1.8 million because the structural engineer's report indicated the warehouse requires a full rebuild rather than a repair\n",
|
||||
"- The building coverage limit on the Meridian Logistics policy for claim CLM-48211 is $2,400,000 with a $25,000 deductible"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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": [
|
||||
"Priya'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 subrogation call, and the cause of loss, and nothing about how Priya 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",
|
||||
"Priya 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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"- Assistant noted that the cause of loss for claim CLM-48211 is faulty wiring identified by the fire marshal's report\n",
|
||||
"- Reserve on claim CLM-48211 was raised to $1.8 million because the structural engineer's report indicated the warehouse requires a full rebuild rather than a repair\n",
|
||||
"- The building coverage limit on the Meridian Logistics policy for claim CLM-48211 is $2,400,000 with a $25,000 deductible\n",
|
||||
"- Assistant recorded that subrogation is being pursued against Voss Electrical for claim CLM-48211 based on the fire marshal's finding of faulty wiring as the ignition source\n",
|
||||
"- User prefers claim summaries to be formatted as bullet points rather than paragraphs for all claims\n",
|
||||
"- User prefers drafts to contain no em dashes and coverage position letters to be limited to one page"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"status_results = client.search(\n",
|
||||
" \"Draft a status update on claim CLM-48211.\",\n",
|
||||
" filters={\"OR\": [{\"user_id\": ADJUSTER_PRIYA}, {\"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 Priya'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 Priya and has never touched CLM-48211 before. His personal shelf should be empty."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"0 memories on Marcus's personal shelf\n",
|
||||
"(none)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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 Priya, it belongs to the claim. When Marcus opens CLM-48211 cold, he sees exactly what she left behind."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"- Assistant noted that the cause of loss for claim CLM-48211 is faulty wiring identified by the fire marshal's report\n",
|
||||
"- Reserve on claim CLM-48211 was raised to $1.8 million because the structural engineer's report indicated the warehouse requires a full rebuild rather than a repair\n",
|
||||
"- The building coverage limit on the Meridian Logistics policy for claim CLM-48211 is $2,400,000 with a $25,000 deductible\n",
|
||||
"- Assistant recorded that subrogation is being pursued against Voss Electrical for claim CLM-48211 based on the fire marshal's finding of faulty wiring as the ignition source"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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 Priya'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",
|
||||
"Priya can inspect and prune her own shelf directly."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"2e2b6f92-effb-4b23-a444-fda5d570781e - User prefers claim summaries to be formatted as bullet points rather than paragraphs for all claims\n",
|
||||
"993076e6-5c6e-433c-9a11-8238548d4091 - User prefers drafts to contain no em dashes and coverage position letters to be limited to one page"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"priya_review = client.get_all(filters={\"user_id\": ADJUSTER_PRIYA})\n",
|
||||
"for r in priya_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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"- User prefers claim summaries to be formatted as bullet points rather than paragraphs for all claims"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"client.delete(\"993076e6-5c6e-433c-9a11-8238548d4091\")\n",
|
||||
"\n",
|
||||
"priya_after = client.get_all(filters={\"user_id\": ADJUSTER_PRIYA})\n",
|
||||
"dump(priya_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",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"demo memories deleted\n",
|
||||
"project instructions restored"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"client.delete_all(user_id=ADJUSTER_PRIYA)\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": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
Reference in New Issue
Block a user