Get started
Quickstart
Give a chat loop long-term memory in a few minutes.
This walks through a small assistant that remembers what each user tells it, across sessions.
1. Install and set a key
pip install outhad-contextkitai openai
export OPENAI_API_KEY="sk-..."
ContextKit uses OpenAI by default: gpt-4o-mini to extract facts and text-embedding-3-small to embed them. To use Anthropic, Gemini, Ollama, or another provider instead, see Providers.
2. Create a memory
from outhad_contextkit import Memory
memory = Memory.from_config({
"vector_store": {
"provider": "qdrant",
"config": {"path": "./memory", "on_disk": True},
},
})
This keeps memories in a local Qdrant database under ./memory. No server is needed.
Set a path with on_disk: True
With no configuration, Qdrant runs from /tmp/qdrant with on_disk off, and that directory is cleared every time a Memory starts. The config above keeps memories across restarts. Create memories with Memory.from_config(...) rather than Memory(), so each instance gets its own config.
3. Add what the user says
Pass a string, or a list of chat messages:
memory.add(
[
{"role": "user", "content": "I'm vegetarian, and I'm allergic to peanuts."},
{"role": "assistant", "content": "Got it, I'll keep that in mind."},
],
user_id="alice",
)
The LLM reads the exchange and stores the facts it finds, such as "Is vegetarian" and "Allergic to peanuts". The return value lists what changed:
{"results": [
{"id": "…", "memory": "Is vegetarian", "event": "ADD"},
{"id": "…", "memory": "Allergic to peanuts", "event": "ADD"},
]}
Later, when Alice says "I've started eating fish again", the LLM updates the existing memory instead of adding a contradictory one, and the result has "event": "UPDATE" with the old text in previous_memory.
4. Search before you answer
hits = memory.search("What should I cook tonight?", user_id="alice", limit=3)
for hit in hits["results"]:
print(f'{hit["score"]:.2f} {hit["memory"]}')
Each result has the memory text, its id, a similarity score, its metadata, and created_at / updated_at timestamps.
5. Put it together
search before it, add after it. What this turn adds is what the next turn's search can find. orange: an LLM call · blue: a scope or policy checkfrom openai import OpenAI
from outhad_contextkit import Memory
llm = OpenAI()
memory = Memory.from_config({
"vector_store": {"provider": "qdrant", "config": {"path": "./memory", "on_disk": True}},
})
def reply(message: str, user_id: str) -> str:
hits = memory.search(message, user_id=user_id, limit=5)
known = "\n".join(f"- {hit['memory']}" for hit in hits["results"])
answer = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"You are a helpful assistant. What you know about the user:\n{known}"},
{"role": "user", "content": message},
],
).choices[0].message.content
memory.add(
[{"role": "user", "content": message}, {"role": "assistant", "content": answer}],
user_id=user_id,
)
return answer
print(reply("I'm vegetarian and allergic to peanuts.", "alice"))
print(reply("What should I make for dinner?", "alice"))
Each turn sends the model a few relevant facts instead of the whole conversation, and stores whatever the turn taught it.
What is stored where
| Data | Where |
|---|---|
| Memories and embeddings | The vector store: ./memory with the config above |
| Change history | ~/.outhad_contextkit/history.db (SQLite) |
| An anonymous install id | ~/.outhad_contextkit/config.json |
Set OUTHAD_CONTEXTKIT_DIR to move the ~/.outhad_contextkit directory, or history_db_path in the config to move only the history. Details are on the Installation page.
Next steps
- Memory operations: update, delete, history, and the full
add/searchoptions. - Scoping and filters: agents, runs, and metadata filters.
- Providers: other LLMs, embedders, and vector stores.