Skip to content

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 MemoryLayer or on connect("http://...") (RemoteMemory has the same API).
  • REST. POST /v1/kinds/{kind}/{operation}, with the method's arguments as JSON. GET /v1/kinds returns every kind's operations and counts.
  • MCP. Dedicated tools for goals, decisions, lessons and the graph; save_memory for facts, preferences, instructions, knowledge and episodes.
  • From conversation. With the intelligence on, manage(), record_turn(), POST /v1/manage, POST /v1/turns or MCP learn_from_conversation put 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.