Skip to main content

Zep Memory Agent

A multi-agent conversational memory pipeline that exercises all 5 Zep instrumentor methods -- memory.add, memory.get, memory.search, memory.delete, and graph.search -- through a zep-storer child agent (adds messages to session memory) and a zep-retriever child agent (searches memory, queries the knowledge graph, synthesizes context, and generates a personalized answer with OpenAI). Includes session cleanup via memory.delete.

Environment variables

This example runs in dry-run mode by default (no API key needed). For live mode, set OPENAI_API_KEY, WAXELL_API_KEY, and WAXELL_API_URL.

Architecture

Key Code

Zep memory and graph tool operations

All 5 Zep instrumentor methods are wrapped with @waxell.tool(tool_type="memory").

@waxell.tool(tool_type="memory")
def zep_memory_add(zep, session_id: str, messages: list) -> dict:
return zep.memory.add(session_id=session_id, messages=messages)

@waxell.tool(tool_type="memory")
def zep_memory_search(zep, session_id: str, query: str) -> list:
return zep.memory.search(session_id=session_id, query=query)

@waxell.tool(tool_type="memory")
def zep_memory_get(zep, session_id: str):
return zep.memory.get(session_id=session_id)

@waxell.tool(tool_type="memory")
def zep_graph_search(zep, user_id: str, query: str) -> list:
return zep.graph.search(user_id=user_id, query=query)

@waxell.tool(tool_type="memory")
def zep_memory_delete(zep, session_id: str) -> dict:
return zep.memory.delete(session_id=session_id)

Dual retrieval and context synthesis

Memory search results and knowledge graph results are retrieved separately, then synthesized into a unified context.

@waxell.retrieval(source="zep")
def retrieve_from_memory(query: str, search_results: list) -> list[dict]:
return [
{"text": r.message.content, "score": r.score}
for r in search_results[:3]
]

@waxell.retrieval(source="zep_graph")
def retrieve_from_graph(query: str, graph_results: list) -> list[dict]:
return [
{"text": r.message.content, "score": r.score}
for r in graph_results[:3]
]

@waxell.step_dec(name="synthesize_memory_context")
async def synthesize_memory_context(memory, search_results, graph_results) -> dict:
# Combines session summary, facts, search results, and graph entries
return {"context": context, "context_length": len(context), "sources": {...}}

What this demonstrates

  • @waxell.tool(tool_type="memory") -- 6 Zep operations covering all 5 instrumentor methods (2 adds, 1 search, 1 get, 1 graph search, 1 delete).
  • @waxell.retrieval(source="zep") -- memory search retrieval with relevance scores.
  • @waxell.retrieval(source="zep_graph") -- knowledge graph retrieval.
  • @waxell.step_dec -- memory context synthesis from multiple sources.
  • waxell.score() -- memory_coverage and context_quality scores.
  • waxell.tag() / waxell.metadata() -- agent role and memory system tags.
  • Auto-instrumented LLM calls -- OpenAI synthesis with memory context.
  • Nested @waxell.observe -- orchestrator + 2 child agents (zep-storer, zep-retriever).
  • Full Zep lifecycle -- session creation, message storage, search, graph query, LLM synthesis, and cleanup.

Run it

# Dry-run (no API key needed)
python -m app.demos.zep_memory_agent --dry-run

# Live mode with OpenAI
OPENAI_API_KEY=sk-... python -m app.demos.zep_memory_agent

Source

dev/waxell-dev/app/demos/zep_memory_agent.py