Agents¶
Where Venkai sits in an agent architecture, and the two lines of code that put it there.
The pattern¶
sequenceDiagram
autonumber
participant U as Task
participant AG as Agent
participant VK as Venkai
participant LLM as LLM
U->>AG: "add a new billing plan"
AG->>VK: GET .../relevant?query=add a new billing plan&limit=5
VK-->>AG: 5 memories + scores
AG->>LLM: system + selected context + task
LLM-->>AG: plan / action
AG->>AG: execute
AG->>VK: POST /api/memory (what was decided)
VK-->>AG: stored
Read before the prompt. Write after the decision. That is the whole integration.
Minimal implementation¶
import os, requests
BASE = os.environ.get("VENKAI_BASE_URL", "https://api.venkai.fr")
H = {"Authorization": f"Bearer {os.environ['VENKAI_API_KEY']}"}
PROJECT = "billing-service" # a constant: a typo silently creates a new project
def load_context(task: str, limit: int = 5) -> str:
r = requests.get(f"{BASE}/api/context/{PROJECT}/relevant",
headers=H, params={"query": task, "limit": limit}, timeout=10)
r.raise_for_status()
memories = r.json()["memories"]
if not memories:
return ""
lines = [f"- [{m['type']}] {m['content']}" for m in memories]
return "Established context for this project:\n" + "\n".join(lines)
def record(content: str, type: str = "decision", importance: float = 0.8) -> None:
requests.post(f"{BASE}/api/memory", headers=H, timeout=10, json={
"project_id": PROJECT, "agent_id": "planner",
"content": content, "type": type, "importance": importance,
}).raise_for_status()
def run(task: str):
prompt = f"{load_context(task)}\n\nTask: {task}"
result = call_your_llm(prompt) # your framework, your model
if result.decision:
record(result.decision, type="decision", importance=0.85)
return result
Where to put each call¶
Read: once per task, not once per LLM call¶
A multi-step agent that recalls before every tool call pays for retrieval n
times and floods each prompt with the same memories. Load once at the top of
the task and pass the block down.
Recall again only when the subject changes — a sub-agent working on a different component should query for its own component.
Write: on conclusions, not on observations¶
The trap is writing every intermediate thought. That fills the store with low-value rows that then compete for retrieval budget.
| Write | Skip |
|---|---|
| A decision and its reason | "I am now reading the config file" |
| A discovered hard constraint | Tool output you can re-read |
| A correction of a previous belief | Anything already in the repository |
| A user preference | The step-by-step trace |
Rule of thumb: if a competent colleague joining next week would want to know it, write it. Otherwise let it go.
Multi-agent¶
Agents in the same project share one store. Each writes with its own
agent_id, so contributions stay attributable and filterable
(GET /api/context/{project}?agent=planner).
flowchart TB
P["planner"] --> S[("project: billing-service")]
C["coder"] --> S
R["reviewer"] --> S
S -->|"recall(task)"| P
S -->|"recall(task)"| C
S -->|"recall(task)"| R
There is no locking and no conflict detection. Two agents can write
contradictory memories and both will be retrieved, side by side, with no
signal that they disagree. If that matters, have the agent check for a
contradicting memory before writing, and PATCH rather than append when it
finds one.
Handoffs Experimental¶
POST /api/handoffs records a from_agent → to_agent transfer with a list of
context_ids and a note. It is a record of the handoff, not a mechanism —
nothing is enforced or delivered by it.
Framework notes¶
Venkai has no framework-specific packages. It is two HTTP calls, so it drops into anything:
| Framework | Where it goes |
|---|---|
| LangGraph | A node before the model node loads context into state; a node after writes conclusions. |
| CrewAI | In a task callback, or a custom tool the agent can call itself. |
| Claude Code / MCP clients | Via the MCP server — no code at all. |
| Custom loop | load_context() before the prompt, record() after. |
Letting the agent decide¶
Exposing recall as a tool the model can call is tempting and mostly a mistake: the model calls it with vague queries, or forgets. Deterministic loading at the top of the task is more reliable.
Exposing write as a tool is the opposite — the model knows when it has
concluded something, and you do not. Give it record() and describe when to
use it.
Related¶
- Add Venkai to an agent — the fuller walkthrough
- Long-running agents
- Context optimization