Skip to content
Merged
62 changes: 24 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,65 +129,51 @@ results = await v.search(query, user_id, depth="l2", context_window=3)

## Build an Agent with Memory

Three lines to wire memory into any agent loop:
The low-level way is still available, but the new native harness is the right default.

```python
import asyncio
from openai import AsyncOpenAI
from vektori import Vektori

client = AsyncOpenAI()
from vektori import AgentConfig, Vektori, VektoriAgent
from vektori.models.factory import create_chat_model

async def chat(user_id: str):
v = Vektori(
memory = Vektori(
embedding_model="openai:text-embedding-3-small",
extraction_model="openai:gpt-4o-mini",
)
session_id = f"session-{user_id}-001"
history = []
agent = VektoriAgent(
memory=memory,
model=create_chat_model("openai:gpt-4o-mini"),
user_id=user_id,
agent_id="demo-agent",
session_id=f"session-{user_id}-001",
config=AgentConfig(background_add=True),
)

print("Chat with memory (type 'quit' to exit)\n")
while True:
user_input = input("You: ").strip()
if user_input.lower() == "quit":
break
result = await agent.chat(user_input)
print(f"Assistant: {result.content}\n")

# 1. Pull relevant memory
mem = await v.search(query=user_input, user_id=user_id, depth="l1")
facts = "\n".join(f"- {f['text']}" for f in mem.get("facts", []))
episodes = "\n".join(f"- {ep['text']}" for ep in mem.get("episodes", []))

# 2. Inject into system prompt
system = "You are a helpful assistant with memory.\n"
if facts: system += f"\nKnown facts:\n{facts}"
if episodes: system += f"\nBehavioral episodes:\n{episodes}"

# 3. Get response
history.append({"role": "user", "content": user_input})
resp = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "system", "content": system}, *history],
)
reply = resp.choices[0].message.content
history.append({"role": "assistant", "content": reply})
print(f"Assistant: {reply}\n")

# 4. Store exchange
await v.add(
messages=[{"role": "user", "content": user_input},
{"role": "assistant", "content": reply}],
session_id=session_id,
user_id=user_id,
)

await v.close()
await agent.close()
await memory.close()

asyncio.run(chat("demo-user"))
```

You can also run the harness from the CLI:

```bash
vektori agent chat --user-id demo-user --agent-id support-agent
```

More examples in [`/examples`](examples/):
- [`quickstart.py`](examples/quickstart.py) — fully local, zero API keys (Ollama)
- [`openai_agent.py`](examples/openai_agent.py) — OpenAI agent loop
- [`openai_agent.py`](examples/openai_agent.py) — OpenAI native harness loop
- [`vektori_agent_demo.py`](examples/vektori_agent_demo.py) — minimal `VektoriAgent` demo

---

Expand Down
193 changes: 193 additions & 0 deletions examples/agent_type_extraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
"""
Vektori — Agent-type extraction customisation examples
=======================================================

Shows how to tailor what Vektori extracts at L0 (facts) and L1 (episodes)
for different agent personas. The same conversation is run through three
differently-configured Vektori instances so you can see the effect.

Quick-start
-----------
export OPENAI_API_KEY=...
python examples/agent_type_extraction.py

Levels of customisation
-----------------------
Level 1 — agent_type preset (zero effort):
Vektori(agent_type="presales")

Level 2 — domain hints on top of a preset:
Vektori(extraction_config=ExtractionConfig(
agent_type="presales",
focus_on=["ICP fit", "executive sponsor"],
ignore=["pleasantries"],
))

Level 3 — prompt suffix (low effort, precise control):
Vektori(extraction_config=ExtractionConfig(
agent_type="sales",
facts_prompt_suffix="Always extract the exact dollar amount when pricing is mentioned.",
))

Level 4 — full prompt override (escape hatch):
Vektori(extraction_config=ExtractionConfig(
custom_facts_prompt=MY_PROMPT_TEMPLATE,
))
"""

import asyncio
import json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused import.

json is imported but never used in the file.

🧹 Proposed fix
 import asyncio
-import json

 from vektori import ExtractionConfig, Vektori
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/agent_type_extraction.py` at line 39, Remove the unused import
"json" from the top-level imports in examples/agent_type_extraction.py; locate
the import statement that reads "import json" and delete it so there are no
unused imports in the module.


from vektori import ExtractionConfig, Vektori

# ---------------------------------------------------------------------------
# A realistic pre-sales discovery call snippet
# ---------------------------------------------------------------------------
PRESALES_CONVERSATION = [
{
"role": "user",
"content": (
"We're a Series B fintech, around 200 engineers. "
"Right now our AI agents lose context between sessions — support keeps "
"repeating the same questions to customers. It's killing CSAT scores."
),
},
{
"role": "assistant",
"content": (
"That's a common friction point at your scale. What does your current "
"session storage look like — are you on something like Redis, or more ad hoc?"
),
},
{
"role": "user",
"content": (
"Ad hoc, honestly. Each team rolls their own. Our CTO wants a unified "
"memory layer by Q3 — we've got maybe a $60–80K budget for tooling this half. "
"We tried Mem0 briefly but the graph retrieval wasn't granular enough."
),
},
{
"role": "assistant",
"content": (
"Got it. So you need something that preserves the full conversation story, "
"not just entity triples — that's exactly what Vektori's three-layer graph "
"is built for. Who else is involved in the decision besides your CTO?"
),
},
{
"role": "user",
"content": (
"Our VP Eng and the platform team lead, Aisha. She's the one who'd actually "
"integrate it. They're both pretty hands-on technically."
),
},
]

# ---------------------------------------------------------------------------
# A sales closing call
# ---------------------------------------------------------------------------
SALES_CONVERSATION = [
{
"role": "user",
"content": (
"We reviewed the proposal. Legal flagged the data residency clause — "
"they need EU hosting confirmed before we can sign."
),
},
{
"role": "assistant",
"content": (
"Understood. Our EU region is live on AWS eu-west-1 — I'll get that "
"confirmed in writing by tomorrow. Are there any other open items?"
),
},
{
"role": "user",
"content": (
"Just that. We're looking at the $48K/year enterprise tier. "
"If legal clears it this week we can sign by Friday the 18th."
),
},
{
"role": "assistant",
"content": (
"Perfect. I'll loop in our legal team tonight. I'll also prep the "
"countersigned order form so it's ready to go the moment you get approval."
),
},
]


async def run_demo():
print("=" * 70)
print("Vektori ExtractionConfig demo")
print("=" * 70)

# ------------------------------------------------------------------
# Example 1: pre-sales preset — zero effort
# ------------------------------------------------------------------
print("\n[1] agent_type='presales' — built-in preset\n")

presales_v = Vektori(agent_type="presales")
captured: list[dict] = []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused variable.

captured list is declared but never used. The comment mentions _capture_out but it's not passed to add().

🧹 Proposed fix
     print("\n[1] agent_type='presales' — built-in preset\n")

     presales_v = Vektori(agent_type="presales")
-    captured: list[dict] = []
     await presales_v.add(
         messages=PRESALES_CONVERSATION,
         session_id="demo-presales-001",
         user_id="demo-user",
-        # _capture_out is an internal debug hook used in tests
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/agent_type_extraction.py` at line 133, Remove the unused variable
declaration captured: list[dict] = [] from agent_type_extraction.py; either
delete that line entirely or instead wire the intended capture by passing the
_capture_out collector into the add(...) call where the output hook is
registered (refer to the _capture_out and add(...) symbols) so there are no
unused locals left.

await presales_v.add(
messages=PRESALES_CONVERSATION,
session_id="demo-presales-001",
user_id="demo-user",
# _capture_out is an internal debug hook used in tests
)
# Give async extraction a moment to complete for the demo
await asyncio.sleep(3)
memory = await presales_v.search("budget and decision makers", user_id="demo-user")
print("Facts retrieved (presales):")
for f in memory.get("facts", []):
print(f" • {f['text']}")
await presales_v.close()

# ------------------------------------------------------------------
# Example 2: sales preset + domain hints
# ------------------------------------------------------------------
print("\n[2] agent_type='sales' + focus_on=['contract value', 'close date']\n")

sales_v = Vektori(
extraction_config=ExtractionConfig(
agent_type="sales",
focus_on=["contract value", "close date", "legal blockers"],
)
)
await sales_v.add(
messages=SALES_CONVERSATION,
session_id="demo-sales-001",
user_id="demo-sales-user",
)
await asyncio.sleep(3)
memory = await sales_v.search("deal status and blockers", user_id="demo-sales-user")
print("Facts retrieved (sales):")
for f in memory.get("facts", []):
print(f" • {f['text']}")
await sales_v.close()

# ------------------------------------------------------------------
# Example 3: same sales call, general (no preset) — shows the difference
# ------------------------------------------------------------------
print("\n[3] agent_type='general' — no domain bias (baseline)\n")

general_v = Vektori() # default, no agent_type
await general_v.add(
messages=SALES_CONVERSATION,
session_id="demo-general-001",
user_id="demo-general-user",
)
await asyncio.sleep(3)
memory = await general_v.search("deal status and blockers", user_id="demo-general-user")
print("Facts retrieved (general):")
for f in memory.get("facts", []):
print(f" • {f['text']}")
await general_v.close()

print("\nDone.")


if __name__ == "__main__":
asyncio.run(run_demo())
74 changes: 22 additions & 52 deletions examples/openai_agent.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,35 @@
"""
Vektori as memory for a basic OpenAI agent.

Each turn: search memory → inject context → respond → store new messages.
"""
"""Minimal OpenAI-backed native harness example using `VektoriAgent`."""

import asyncio
import os
from openai import AsyncOpenAI
from vektori import Vektori

client = AsyncOpenAI()
from vektori import AgentConfig, Vektori, VektoriAgent
from vektori.models.factory import create_chat_model


async def chat_with_memory(user_id: str):
v = Vektori(
memory = Vektori(
embedding_model="openai:text-embedding-3-small",
extraction_model="openai:gpt-4o-mini",
)

session_id = f"session-{user_id}-001"
conversation_history = []
agent = VektoriAgent(
memory=memory,
model=create_chat_model("openai:gpt-4o-mini"),
user_id=user_id,
agent_id="openai-agent-demo",
session_id=f"session-{user_id}-001",
config=AgentConfig(background_add=True),
)

print("Chat with memory (type 'quit' to exit)\n")
while True:
user_input = input("You: ").strip()
if user_input.lower() == "quit":
break

# 1. Retrieve relevant memories
memory = await v.search(query=user_input, user_id=user_id, depth="l1")
facts_context = "\n".join(f"- {f['text']}" for f in memory.get("facts", []))
episodes_context = "\n".join(f"- {ep['text']}" for ep in memory.get("episodes", []))

# 2. Build system prompt with memory context
system = "You are a helpful assistant with access to user memory.\n"
if facts_context:
system += f"\nKnown facts about this user:\n{facts_context}"
if episodes_context:
system += f"\nMemory episodes:\n{episodes_context}"

# 3. Get response
conversation_history.append({"role": "user", "content": user_input})
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "system", "content": system}, *conversation_history],
)
assistant_reply = response.choices[0].message.content
conversation_history.append({"role": "assistant", "content": assistant_reply})
print(f"Assistant: {assistant_reply}\n")

# 4. Store this exchange in memory
await v.add(
messages=[
{"role": "user", "content": user_input},
{"role": "assistant", "content": assistant_reply},
],
session_id=session_id,
user_id=user_id,
)

await v.close()
try:
while True:
user_input = input("You: ").strip()
if user_input.lower() == "quit":
break
result = await agent.chat(user_input)
print(f"Assistant: {result.content}\n")
finally:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
await agent.close()
await memory.close()


if __name__ == "__main__":
Expand Down
Loading
Loading