What's inside¶
Fifteen kinds of memory¶
| Kind | Accessor | Remembers |
|---|---|---|
| Profile | memory.profile |
who the user is: one value per slot, with history |
| Factual | memory.facts |
facts and preferences, with confidence and verification |
| Semantic | memory.semantic |
domain knowledge, shared by every user or personal; ingest() documents |
| Episodic | memory.episodes |
events with when, where, who and outcome; consolidated as they age |
| Working | memory.working(session) |
the current task's scratchpad: small, expiring, never embedded |
| Conversational | memory.conversation(session) |
rolling summary + recent turns, constant size |
| Goal | memory.goals |
objectives with priority, progress, due dates, sub-goals |
| Procedural | memory.procedures |
how-tos with versioned steps and a success record; standing rules |
| Skill | memory.skills |
reusable prompts, tools and code, ranked by relevance × reliability |
| Strategy | memory.strategies |
what worked for which kind of task, and what to avoid |
| Decision | memory.decisions |
decisions with options, rationale, outcome and supersession |
| Graph | memory.graph |
a temporal knowledge graph of entities and relations with history, and links between the memories themselves (serves, resolves, refines ...) |
| KV cache | memory.kv |
a byte-stable prompt prefix for provider caches; embedding and context caches |
| Parametric | memory.parametric |
the bridge to fine-tuned weights: export datasets, skip what a model has learned |
| Hybrid | memory.recall() |
dense + BM25 + graph + recency, fused with RRF, packed into a token budget |
Details and APIs: memory types. Runnable recipes: cookbook.
Every kind, on every surface¶
Each kind is reached the same way wherever your agent runs:
- Python, in-process or remote. The accessors above, on a
MemoryLayeror onconnect("http://...")(RemoteMemoryhas the same API). - REST.
POST /v1/kinds/{kind}/{operation}, with the method's arguments as JSON.GET /v1/kindsreturns every kind's operations and counts. - MCP. Dedicated tools for goals, decisions, lessons and the graph;
save_memoryfor facts, preferences, instructions, knowledge and episodes. - From conversation. With the intelligence on,
manage(),record_turn(),POST /v1/manage,POST /v1/turnsor MCPlearn_from_conversationput each sentence into the right kind.
Whichever way a memory was written, it reaches the agent through recall() (REST POST /v1/recall, MCP
get_memory_context).
All fifteen in one script (offline, no setup):
import kioko
memory = kioko.connect() # in-process; connect("http://memory-service:8765") gives the same API remotely
user = "kumar"
memory.profile.set("location", "Pune", user_id=user) # 1 profile
memory.facts.add("User's manager is Krutika", key="work.manager", user_id=user) # 2 factual
memory.semantic.add("OPC UA servers listen on TCP port 4840 by default", topic="iiot") # 3 semantic (shared)
memory.episodes.record("Presented the maintenance demo", when="2026-10-20", user_id=user, # 4 episodic
outcome="approved for a pilot")
memory.working("shift-1", user_id=user).set("ticket", "INC-4521") # 5 working
memory.conversation("shift-1", user_id=user).add_turn("The relay tripped", "Reset it at the panel") # 6 conversational
goal = memory.goals.add("Ship Kioko 1.0", user_id=user, priority="high", due="2026-12-15") # 7 goal
memory.procedures.add("Reset the safety relay", user_id=user, # 8 procedural
steps=["Switch off the breaker", "Press reset for 5 seconds", "Switch it back on"])
memory.skills.add("summarize alarm log", description="Top recurring faults from a PLC alarm log", # 9 skill
body="Summarize these alarms by fault code: {alarms}", user_id=user)
memory.strategies.reflect("debugging drives", "check the encoder cable first", "success", user_id=user) # 10 strategy
memory.decisions.record("Vector store", chosen="pgvector", rationale="we already run Postgres", # 11 decision
user_id=user)
memory.graph.add("Kumar", "reports_to", "Krutika", user_id=user) # 12 graph
prefix = memory.kv.prefix(user_id=user) # 13 KV cache
rows = memory.parametric.export_dataset(user_id=user) # 14 parametric
ctx = memory.recall("The relay tripped again, what do I do?", user_id=user, session_id="shift-1") # 15 hybrid
print(ctx.text)
The same over REST (any language). Session-scoped kinds take a session_id:
H="Authorization: Bearer $KIOKO_API_KEY"
curl -s localhost:8765/v1/kinds/goals/add -H "$H" -H 'Content-Type: application/json' \
-d '{"user_id": "kumar", "title": "Ship Kioko 1.0", "priority": "high", "due": "2026-12-15"}'
curl -s localhost:8765/v1/kinds/working/set -H "$H" -H 'Content-Type: application/json' \
-d '{"user_id": "kumar", "session_id": "shift-1", "slot": "ticket", "value": "INC-4521"}'
curl -s localhost:8765/v1/kinds/decisions/record -H "$H" -H 'Content-Type: application/json' \
-d '{"user_id": "kumar", "title": "Vector store", "chosen": "Qdrant", "rationale": "fast to set up"}'
curl -s localhost:8765/v1/kinds/decisions/supersede -H "$H" -H 'Content-Type: application/json' \
-d '{"user_id": "kumar", "ref": "Vector store", "chosen": "pgvector", "rationale": "we already run Postgres"}'
curl -s "localhost:8765/v1/kinds?user_id=kumar" -H "$H" # every kind's operations and counts
Writing each kind
| Kind | Python | REST POST /v1/kinds/... |
MCP tool |
|---|---|---|---|
| Profile | profile.set() · get() · history() · remove() |
profile/set · get · history · remove |
(from conversation) |
| Factual | facts.add() · verify() · search() |
facts/add · verify · search |
save_memory (fact, preference, instruction) |
| Semantic | semantic.add() · ingest() · search() · forget_source() |
semantic/add · ingest · search · forget_source |
save_memory (semantic) |
| Episodic | episodes.record() · timeline() · recent() · consolidate() |
episodes/record · timeline · recent · consolidate |
save_memory (episodic) |
| Working | working(session).set() · snapshot() · promote() |
working/set · snapshot · promote (+ session_id) |
(from conversation) |
| Conversational | conversation(session).add_turn() · as_messages() · context() |
conversation/add_turn · context (+ session_id); POST /v1/turns |
(from conversation) |
| Goal | goals.add() · update() · complete() · drop() · tree() |
goals/add · update · complete · drop · tree |
manage_goal |
| Procedural | procedures.add() · find() · record_outcome() · add_rule() |
procedures/add · find · record_outcome · add_rule |
(from conversation) |
| Skill | skills.add() · find() · render_prompt() · record_use() |
skills/add · find · render_prompt · record_use |
|
| Strategy | strategies.reflect() · recommend() · avoid() |
strategies/reflect · recommend · avoid |
record_lesson |
| Decision | decisions.record() · supersede() · set_outcome() · chain() |
decisions/record · supersede · set_outcome · chain |
record_decision (supersedes: true) |
| Graph | graph.add() · about() · path(); memory links: graph.links() · related() · link() |
graph/add · about · path · neighbors; /v1/memories/{id}/links |
relate_entities · explore_entity · explore_memories |
| KV cache | kv.prefix() · anthropic_system() · openai_messages() · stats() |
kv/prefix · stats |
|
| Parametric | parametric.export_dataset() · register_model() · mark_consolidated() |
parametric/export_dataset · register_model · models |
|
| Hybrid | recall() · hybrid.search() |
POST /v1/recall |
get_memory_context · search_memory |
Learning and recalling each kind
| Kind | The intelligence fills it from (manage, checked offline) |
In the recall block as |
|---|---|---|
| Profile | "I'm Kumar and I live in Pune." → profile.set |
About the user (also in the cacheable prefix) |
| Factual | "My team has 6 engineers." → fact.add; "I prefer short answers." → instruction.add |
Relevant memories; instructions in Instructions from the user |
| Semantic | "FYI, OPC UA servers listen on TCP port 4840 by default." → knowledge.add |
Background knowledge |
| Episodic | "Yesterday I presented the maintenance demo to the plant team." → episode.add |
Relevant memories (more of them for "when did ..." questions) |
| Working | "I'm debugging ticket INC-4521, error E-217 on line 3." → working.set |
Current task notes |
| Conversational | every turn passed to record_turn() / POST /v1/turns |
Earlier in this session |
| Goal | "My goal is to ship Kioko 1.0 by December 15." → goal.add; "I'm about 60% done ..." → goal.progress |
The user's open goals (also in the cacheable prefix) |
| Procedural | "To reset the safety relay, switch off the breaker, then ..." → procedure.add |
Procedures that may apply; rules in Instructions from the user |
| Skill | outcomes only (skill.outcome); skills are added in code |
Skills you can use |
| Strategy | "Lesson learned: when debugging OPC UA, check the certificate trust list first." → strategy.add |
Lessons from earlier attempts |
| Decision | "We decided to use Qdrant because ..." → decision.add; "We switched from Qdrant to pgvector because ..." → decision.supersede |
Earlier decisions (superseded ones left out) |
| Graph | "My manager is Krutika." → relation.add; memory links are made as memories are written |
Known relationships and How these memories connect |
| KV cache | built from the stable kinds | the byte-stable prefix for provider prompt caches (kv.anthropic_system(), kv.openai_messages()) |
| Parametric | stable memories exported for fine-tuning | memories a fine-tuned model already knows are left out (recall(..., model=...)) |
| Hybrid | (retrieval over all of the above) | the whole block, ranked and packed into budget_tokens |
The intelligence¶
memory = kioko.intelligent() # or MemoryLayer(..., intelligence=True)
memory.manage("My goal is to pass the Azure AI Engineer exam by March 31.", user_id="kumar")
memory.manage("We decided to use Qdrant because it is fast.", user_id="kumar")
report = memory.manage("I'm about 60% done with the Azure exam prep, and we switched from Qdrant to pgvector "
"because we already run Postgres.", user_id="kumar", dry_run=True)
print(report.summary())
# [dry run] goal.progress "Pass the Azure AI Engineer exam by March 31" -> 60%
# [dry run] decision.supersede "use Qdrant" -> pgvector
Read intelligence for the operations, the LLM mode with validation and fallback, retrieval plans, maintenance (summaries, reflection, pinning hot memories, session wrap-up, consolidation) and the research behind it: Mem0, MIRIX, Generative Agents, MemGPT, MemOS, MemoryOS, LightMem and others.
The memory graph¶
memory.graph.add("Kioko", "uses", "pgvector", user_id="kumar") # the entity graph
memory.manage("We decided to use pgvector because we already run Postgres.", user_id="kumar")
# linked: decision "use pgvector because we already run Postgres" serves goal "Ship the Kioko memory layer ..."
# (why: via Kioko uses pgvector)
memory.graph.links(memory_id) # typed links with their evidence, weight and author
memory.graph.related(memory_id) # spreading activation: what is connected, with the chain of links
memory.graph.explain(a, b) # why two memories are, or are not, linked
ctx = memory.recall("line 3 is faulting with E-217 again", user_id="kumar")
# ... brings the fix that worked last time, and a "How these memories connect:" section
Each new memory is anchored (entities, rare words), compared with candidates from an inverted index, nearest
neighbours and its session, scored with a noisy-OR of shared terms, meaning, entity-graph bridges and time, typed
from the pair of kinds and cue words, and stored on both memories. Feedback ("that worked") adds resolves
links and strengthens what was used; unused links fade. Graph memory has the algorithm,
with formulas, worked examples and measured cost.
Stores¶
| Store | Target | Notes |
|---|---|---|
| In-process | memory:// |
tests, notebooks |
| SQLite | sqlite:///memory.db |
standard library; memories, history and events in one file |
| Postgres + pgvector | postgresql://user:password@host:5432/db |
HNSW cosine index per embedding size; halfvec above 2,000 dimensions; iterative filtered scans |
| Qdrant | qdrant://host:6333/collection |
Qdrant's REST API with the standard library; payload indexes; vector-less memories never surface in vector search |
| Your own | MemoryLayer(MyStore()) |
five methods, plus reusable contract tests |
docker compose -f docker-compose.stores.yml up -d starts Postgres with pgvector, Qdrant and a Kioko service.
Read stores for the details.