Agent Memory
The identity graph is agent memory. When an AI agent resolves an entity, it can store memory entries - decisions made, investigations completed, patterns observed - linked to that entity. The next agent that resolves the same entity gets the full context from every previous agent, automatically.
No shared database to configure. No message passing between agents. Resolve the entity, get the memory.
Local memory for quick start
Want memory without cloud setup? The Python SDK includes local memory with LLM-powered fact extraction - works immediately with zero config. See Local Memory (SDK).
import kanoniv
mem = kanoniv.get_memory(agent_name="my-agent", api_key="sk-...")
mem.add([
{"role": "user", "content": "I'm Bill from Acme, switch to annual billing"},
{"role": "assistant", "content": "Done!"},
], user_id="[email protected]")Why This Matters
AI agents are stateless by default. Agent A qualifies a lead, Agent B enriches the same contact, Agent C writes the outreach - but none of them know what the others did. Teams solve this with shared databases, vector stores, or message queues. All of these require agents to know about each other.
Kanoniv takes a different approach: memory is a property of identity. If two agents resolve the same real-world person, they share memory - even if they have never communicated.
How It Works
Memory entries are linked to entities in the identity graph. When you call POST /v1/resolve/realtime with include_memory: true, the response includes the standard resolution result plus all memory entries linked to that entity.
Because memory is attached to canonical entities (not source records), it survives merges. If two records are later resolved to the same person, their memory entries combine automatically.
Agent A resolves [email protected] -> Entity ENT_7f82
Agent A stores: "Qualified as enterprise lead"
Agent B resolves (555) 010-1234 -> Entity ENT_7f82 (same person)
Agent B receives: memory from Agent A
Agent B stores: "Enriched via LinkedIn - VP Engineering at Acme"
Agent C resolves [email protected] -> Entity ENT_7f82
Agent C receives: memory from Agent A + Agent BEach agent adds to the cumulative memory. No agent needs to know the others exist.
Example: Four-Agent Pipeline
Consider a pipeline where four agents process the same prospect independently.
Agent 1 - Lead Qualification
POST /v1/resolve/realtime
{
"source_name": "inbound",
"external_id": "lead_442",
"data": { "email": "[email protected]", "company": "Acme Corp" },
"include_memory": true
}Response includes entity_id: "ENT_7f82" and an empty memory: [] array. Agent 1 then stores a decision:
POST /v1/memory
{
"entry_type": "decision",
"slug": "lead-qualified",
"title": "Qualified as enterprise lead",
"content": "Company has 500+ employees, ARR > $10M. Matches ICP.",
"linked_entities": ["ENT_7f82"],
"author": "lead-qualifier-agent"
}Agent 2 - Enrichment
Resolves the same person by phone number. Gets Agent 1's memory automatically:
POST /v1/resolve/realtime
{
"source_name": "enrichment",
"external_id": "enrich_991",
"data": { "phone": "5550101234", "name": "John Doe" },
"include_memory": true
}Response includes memory: [{ "title": "Qualified as enterprise lead", ... }]. Agent 2 adds its own findings:
POST /v1/memory
{
"entry_type": "investigation",
"slug": "linkedin-enrichment",
"title": "Enriched via LinkedIn",
"content": "VP Engineering at Acme Corp. 12 years experience. Active on GitHub.",
"linked_entities": ["ENT_7f82"],
"author": "enrichment-agent"
}Agent 3 - Pattern Detection
Resolves the same entity and receives all prior memory. Notices a recurring pattern:
POST /v1/memory
{
"entry_type": "pattern",
"slug": "personal-email-pattern",
"title": "Always uses personal email for signups",
"content": "This contact has signed up 3 times with personal email, never corporate.",
"linked_entities": ["ENT_7f82"],
"author": "pattern-agent"
}Agent 4 - Outreach
Resolves [email protected] and receives three memory entries from three different agents. It knows the lead is qualified, the contact is a VP, and they prefer personal email - without ever communicating with the other agents.
Memory Entry Types
| Type | Purpose | Example |
|---|---|---|
decision | An action taken or conclusion reached | "Qualified as sales lead", "Marked as do-not-contact" |
investigation | Research findings or enrichment data | "Enriched via LinkedIn", "Verified address via USPS" |
pattern | A recurring observation about the entity | "Always uses personal email", "Purchases quarterly" |
knowledge | Project docs shared across agents | "CLAUDE.md", "Architecture notes" |
expertise | Agent's domain expertise declaration | "Salesforce sync specialist" |
intent | Short-lived action declaration (auto-expires) | "Syncing to Salesforce" (TTL: 5 min) |
task | Work assigned to another agent | "Sync lead to CRM" (assigned_to: crm-agent) |
New entry types
knowledge, expertise, intent, and task enable cross-agent coordination. See Intent & Expertise and Cross-Agent Tasks for details.
Memory Statuses
| Status | Meaning |
|---|---|
active | Current and relevant |
superseded | Replaced by a newer entry |
resolved | The issue or investigation is complete |
archived | No longer relevant, retained for audit |
API Reference
Resolve with Memory
Include include_memory: true in any resolve call to receive linked memory entries in the response.
POST /v1/resolve/realtime
Content-Type: application/json
Authorization: Bearer <api_key>
{
"source_name": "crm",
"external_id": "contact_123",
"data": { "email": "[email protected]" },
"include_memory": true
}The response includes the standard resolve fields (entity_id, decision, confidence, is_new) plus a memory array.
Create Memory Entry
POST /v1/memory
Content-Type: application/json
Authorization: Bearer <api_key>
{
"entry_type": "decision",
"slug": "lead-qualified",
"title": "Qualified as enterprise lead",
"content": "Matches ICP. 500+ employees, ARR > $10M.",
"linked_entities": ["ENT_7f82"],
"linked_sources": ["crm"],
"author": "qualifier-agent"
}| Field | Type | Required | Description |
|---|---|---|---|
entry_type | string | Yes | One of: decision, investigation, pattern |
slug | string | Yes | URL-safe identifier for the entry |
title | string | Yes | Short summary |
content | string | Yes | Full details |
linked_entities | string[] | Yes | Entity IDs this memory is linked to |
linked_sources | string[] | No | Source names for context |
author | string | No | Agent or user that created the entry |
Get Memory by Entity
GET /v1/memory/by-entity/:entity_id
Authorization: Bearer <api_key>Returns all memory entries linked to the given entity, ordered by creation time.
Append to Memory
Add a timestamped note to an existing memory entry without replacing it.
POST /v1/memory/:id/append
Content-Type: application/json
Authorization: Bearer <api_key>
{
"note": "Follow-up call scheduled for March 10."
}Search Memory
Full-text search across all memory entries in the tenant.
GET /v1/memory/search?q=enterprise+lead
Authorization: Bearer <api_key>Update Memory
PUT /v1/memory/:id
Content-Type: application/json
Authorization: Bearer <api_key>
{
"title": "Updated title",
"content": "Updated content",
"status": "superseded"
}Archive Memory
DELETE /v1/memory/:id
Authorization: Bearer <api_key>Sets the entry status to archived. Memory entries are never hard-deleted.
MCP Tools
Agents using Kanoniv via MCP (npx @kanoniv/mcp) have eight memory tools available:
| Tool | Description |
|---|---|
resolve_with_memory | Resolve an entity and return linked memory entries |
memorize | Store a decision, investigation, pattern, knowledge, or expertise entry |
recall | Get all memory linked to an entity |
search_memory | Full-text search across all memory (filter by entry_type) |
declare_intent | Tell other agents what you're about to do (auto-expires via TTL) |
query_expertise | Find which agents have experience with an entity or domain |
sync_knowledge | Bulk-import project docs so all agents can access them |
See Sync & Pull for knowledge sharing, Intent & Expertise for coordination, and Cross-Agent Tasks for task assignment.
MCP Example
{
"tool": "resolve_with_memory",
"arguments": {
"source_name": "crm",
"external_id": "contact_123",
"data": { "email": "[email protected]" }
}
}Returns the resolved entity plus all linked memory. The agent can then call memorize to store new findings:
{
"tool": "memorize",
"arguments": {
"entity_id": "ENT_7f82",
"entry_type": "decision",
"slug": "outreach-approved",
"title": "Approved for outreach sequence",
"content": "Enterprise ICP match. VP-level contact. Green light for personalized sequence."
}
}Memory and Merges
When two entities merge, their memory entries combine. If Entity A has 3 memory entries and Entity B has 2, the merged entity has all 5. No memory is lost during merges.
This means memory accumulates naturally as the identity graph evolves. Early-stage agents that resolve partial data contribute context that later agents benefit from - even after the graph restructures through merges.
Memory is tenant-scoped
All memory entries are isolated by tenant, enforced by row-level security. Agents using different API keys within the same tenant share memory. Agents in different tenants never see each other's memory.
