Microsoft Agent Framework integration

Add long-term agent memory to Microsoft Agent Framework agents

Microsoft Agent Framework agents using Zep gain long-term memory backed by a temporal knowledge graph. The zep-ms-agent-framework package attaches a context provider that persists each conversation turn and injects relevant context into the model on every run.

Core benefits

  • Native context-provider hook: Uses the framework’s own before_run / after_run pipeline — the same surface as its built-in memory providers
  • Single round-trip: Persists the user turn and retrieves the context block in one call
  • Whole-user-graph recall: Context is fused across all of a user’s threads, so a new conversation still recalls earlier facts
  • Lazy resource creation: The Zep user and thread are created on first run and cached
  • Graceful degradation: A Zep failure is logged but never crashes the host agent — the turn proceeds without memory

How it works

The integration ships one class, ZepContextProvider, which subclasses the framework’s ContextProvider and overrides the two lifecycle hooks called around every agent.run(...):

  • before_run — extracts the latest user message, lazily creates the Zep user and thread, persists the message via thread.add_messages(return_context=True), and injects the returned context block (facts, relationships, and prior knowledge from the whole user graph) into the model’s instructions.
  • after_run — reads the assistant’s reply and persists it to the same thread, so both sides of the conversation are captured.

Because context is assembled from the entire user graph, the thread only scopes relevance — an agent on a new thread still recalls facts the same user shared earlier.

This integration is injection-only. It persists turns and injects the context block automatically through the framework’s lifecycle hooks; it exposes no model-callable tools, no separate memory types, and no direct query API. To let the model search the graph on demand, use Zep’s SDK (graph.search) directly, or one of the tool-based integrations.

Installation

$pip install zep-ms-agent-framework

The package depends only on agent-framework-core. The example below also uses a model provider:

$pip install zep-ms-agent-framework agent-framework-openai

Requires Python 3.11+, agent-framework-core>=1.8.1, and a Zep Cloud API key. Get your API key from app.getzep.com.

Set up your environment variables:

$export ZEP_API_KEY="your-zep-api-key"
$export OPENAI_API_KEY="your-openai-api-key"

Usage

Attach a ZepContextProvider to an agent through the context_providers keyword argument:

Python
1import asyncio
2from agent_framework import Agent
3from agent_framework.openai import OpenAIChatClient
4from zep_cloud.client import AsyncZep
5from zep_ms_agent_framework import ZepContextProvider
6
7zep = AsyncZep(api_key="your-zep-api-key")
8
9agent = Agent(
10 OpenAIChatClient(model="gpt-4o-mini"),
11 instructions="You are a helpful assistant with long-term memory.",
12 context_providers=[
13 ZepContextProvider(
14 zep_client=zep,
15 user_id="user-123",
16 thread_id="thread-abc",
17 first_name="Jane",
18 last_name="Smith",
19 email="[email protected]", # optional
20 )
21 ],
22)
23
24async def main() -> None:
25 result = await agent.run("Hi, I'm a data scientist in Portland.")
26 print(result.text)
27
28asyncio.run(main())

Memory is scoped per ZepContextProvider instance to one user_id and thread_id. For a multi-user application, construct one provider per user or conversation, passing real names so Zep can resolve the user’s identity node in the graph.

Configuration options

ZepContextProvider accepts:

FieldRequiredDefaultDescription
zep_clientYesInitialized AsyncZep client (caller owns its lifecycle)
user_idYesZep user ID this provider’s memory is scoped to
thread_idYesZep thread ID the conversation is recorded in
first_nameRecommendedNoneUser first name — helps Zep anchor identity
last_nameOptionalNoneUser last name
emailOptionalNoneUser email
user_message_nameOptionalfull nameDisplay name on persisted user messages
assistant_message_nameOptional"Assistant"Display name on persisted assistant messages
source_idOptional"zep"Attribution ID for injected instructions
ignore_rolesOptionalNoneRoles to exclude from graph ingestion (still stored in thread history)
on_user_createdOptionalNoneAsync hook run once after a new user is created

Per-user setup

Use on_user_created to configure per-user resources — a custom ontology, custom extraction instructions, or user summary instructions — the first time a user is created. See customizing graph structure for the available options.

Best practices

  • Pass real names so Zep can anchor and resolve the user’s identity node in the graph
  • One provider per user/conversation — memory is scoped to a single user_id and thread_id
  • Reuse a single AsyncZep client across requests; the caller owns its lifecycle
  • Allow time for indexing — Zep extracts knowledge asynchronously, so facts from a turn are not instantly retrievable

Next steps