CrewAI integration
Add long-term agent memory and knowledge graphs to CrewAI agents
The zep-crewai package gives CrewAI agents persistent memory backed by Zep’s temporal knowledge graph. You persist conversation turns and business data with Zep storage adapters, and give your agents Zep tools so they can retrieve relevant context when they need it. This lets agents carry context across executions, share a common knowledge base, and ground their decisions in what was learned before.
Core benefits
- Persistent memory — Conversations and knowledge persist across sessions and crew runs.
- On-demand retrieval — Agents search Zep through tools and pull in context exactly when a task calls for it.
- Dual storage — User-specific memory for individuals and shared knowledge graphs for organizational data.
- Tool integration — Search and add-data tools let agents read from and write to Zep during execution.
How it works
Memory in this integration is explicit and tool-driven. There are two distinct steps, and you control both.
Persisting context. Create a ZepUserStorage or ZepGraphStorage adapter and call storage.save(value, metadata={"type": ...}). The adapter routes each item by its type:
Retrieving context. Attach create_search_tool (and optionally create_add_data_tool) to an Agent(tools=[...]). The agent searches Zep when it decides the task needs it. You can also call ZepUserStorage.get_context() directly to fetch the prompt-ready context block that Zep auto-assembles for a thread.
Failure isolation. save() on every storage adapter logs a Zep failure and returns normally instead of raising, so a Zep outage never crashes a crew run. When you want misconfiguration to fail loudly, provision with ensure_user and ensure_thread before the crew runs — see provisioning users and threads.
There is no automatic retrieval or storage, and no external_memory= Crew wiring. CrewAI 1.x removed the ExternalMemory(storage=...) wrapper and the storage interface it depended on, so context is never injected behind the scenes. You decide what to save with save(...), and the agent decides what to search through its tools. The package is also sync-only: its adapters are built on the synchronous Zep client.
Installation
Requires Python 3.11+, zep-crewai>=1.2.0, crewai>=1.0.0, zep-cloud>=3.23.0, and pydantic>=2.0.0, plus a Zep Cloud API key. Get your API key from app.getzep.com.
Set your API key in the environment:
Upgrading from zep-crewai 1.1.x
Three changes affect existing code:
- The compound
allsearch scope is removed — useautoto let Zep pick a scope. save()logs Zep failures instead of raising them.search()wraps its context string in a<ZEP_CONTEXT>template; passcontext_template="{context}"for the raw block.
See the package changelog for the full list of changes.
Provisioning users and threads
ensure_user and ensure_thread are idempotent create-then-catch-conflict helpers. Both return True when the resource is newly created and False when it already exists; genuine failures (auth, network, 5xx) raise. Call them before a crew runs so misconfiguration surfaces loudly rather than being swallowed by save().
The optional on_created hook fires exactly once, only when the user is genuinely new — use it for one-time per-user setup such as ontology configuration.
ZepUserStorage and ZepStorage also provision lazily on the first save() or search() call; pass first_name, last_name, email, or on_created to their constructors to feed that path. The lazy path never raises — a provisioning failure is logged and the call becomes a no-op — so prefer the explicit helpers when you want failures to surface. ZepGraphStorage is scoped to a standalone graph rather than a Zep user, so it has no lazy provisioning; passing it on_created raises TypeError.
Storage types
User storage
Use ZepUserStorage for an individual user’s conversation history and personal context. A thread_id is required and ties message storage to a conversation thread. CrewAI has no automatic persistence loop; sharing one thread across multiple agents is safe when your code or tools write each turn once.
To fetch the auto-assembled context block for the thread directly, call get_context():
Graph storage
Use ZepGraphStorage for shared organizational knowledge that multiple agents can read and write.
You can also search a graph directly. search returns a list whose entries include a composed context string wrapped in the storage’s context template — a <ZEP_CONTEXT>...</ZEP_CONTEXT> block by default:
Pass context_template="{context}" to the storage constructor to get the bare context string instead.
Customizing retrieved context
Both storage classes wrap the context string returned from search() in a template. context_template must contain a literal {context} placeholder and is rendered with plain string replacement (never str.format), so context containing {, }, or % is always safe. The default is the DEFAULT_CONTEXT_TEMPLATE export — the <ZEP_CONTEXT>...</ZEP_CONTEXT> block shared across Zep integrations.
ZepUserStorage also accepts a context_builder: a synchronous callable that replaces the default graph composition in search() with your own retrieval logic. The builder receives a frozen ContextInput (zep, user_id, thread_id, user_message) and returns the context string, or None for no results. A builder exception is logged and degrades to empty results.
Tool integration
Tools are the supported extension point for exposing Zep to CrewAI agents. Bind a tool to a single user or a single graph at creation time, then add it to an agent’s tools list.
create_search_tool and create_add_data_tool return ZepSearchTool and ZepAddDataTool instances; both classes are also exported if you prefer to construct them directly. A Zep failure inside either tool returns an error string to the agent — the tool never raises into the crew.
Search tool parameters
The search tool’s args_schema exposes every graph.search parameter to the model by default:
Use pinned_params to fix a parameter to a constant and remove it from the model-facing schema, or hidden_params to remove it from the schema without pinning it (Zep’s server-side default applies). The legacy scope, reranker, and limit keyword arguments each pin and hide their parameter, equivalent to putting them in pinned_params. search_filters and bfs_origin_node_uuids are constructor-only and never exposed to the model.
The tool returns results to the agent as plain - fact lines, one per result.
Add-data tool parameters
data— Content to store; payloads over Zep’sgraph.addceiling are truncated to 9,900 characters instead of failing.data_type— Explicit type:text(default),json, ormessage.
Structured data with ontologies
Define entity models so Zep organizes graph data into typed entities. The SDK requires a docstring on each entity class to describe it.
Configuration options
ZepUserStorage parameters
ZepGraphStorage parameters
ZepGraphStorage has no on_created parameter — it is graph-scoped, with no Zep user to provision. Passing on_created raises TypeError.
ZepStorage parameters
ZepStorage is a standalone user-and-thread adapter that preserves the historical save(value, metadata) / search(query, limit, score_threshold) / reset() contract for existing callers. It takes client, user_id, and thread_id (all required) plus the same lazy-provisioning fields as ZepUserStorage (first_name, last_name, email, on_created). New code should prefer ZepUserStorage.
Size limits
- Zep rejects direct thread-message payloads over 4,096 characters; the CrewAI storage paths truncate message content to 4,000 characters before
thread.add_messages, logging lengths only and never content. - Zep rejects direct
graph.addpayloads over 10,000 characters; CrewAI storage paths andZepAddDataTooltruncate graph payloads to 9,900 characters before callinggraph.add. - Search queries are truncated to 400 characters (Zep’s query limit).
Complete example
This example mirrors the simple_example.py from the integration repository. It persists conversation turns and business data to a user’s memory, then runs an agent that searches that memory through a Zep tool before answering.
Best practices
Storage selection
- Use
ZepUserStoragefor personal preferences, conversation history, and user-specific context. - Use
ZepGraphStoragefor shared knowledge, organizational data, and collaborative information.
Memory management
- Set up ontologies for structured graph data, and give every entity class a docstring.
- Use search filters to target specific node types and improve relevance.
- Combine storage types for comprehensive memory coverage.
Tool usage
- Bind tools to a specific user or graph at creation time.
- Pin or hide search parameters the model should not control with
pinned_paramsandhidden_params. - Save data with the right
type(message,json, ortext) so it routes correctly. - Allow time for indexing — Zep extracts knowledge asynchronously, so facts from a turn are not instantly searchable.
Next steps
- Explore customizing graph structure for advanced knowledge organization
- Learn about searching the graph and how to tune search
- See code examples for additional patterns