Entity Filtering When Querying the Zep Graph Memory: A Complete Technical Guide
Entity filtering in the Zep graph memory extracts only domain-specific nodes by stripping generic labels and optionally applying type whitelists to produce a clean entity collection for downstream processing.
The mirofish repository implements a sophisticated filtering pipeline within its Zep integration layer to prepare simulation data. This process ensures that only meaningful, user-defined entities—rather than structural placeholders—are passed to agent workflows. Understanding this entity filtering when querying the Zep graph memory is essential for developers customizing knowledge graph simulations or extending the backend's memory management capabilities.
The Filtering Architecture in ZepEntityReader
The core logic resides in backend/app/services/zep_entity_reader.py, specifically within the ZepEntityReader class. This service acts as the boundary between the mirofish simulation engine and the Zep knowledge graph API, handling connection management, retry logic, and entity sanitation.
Connecting to Zep and Retrieving Raw Graph Data
The process begins with client initialization and bulk data retrieval. The reader instantiates a Zep client using the ZEP_API_KEY from the global configuration, then fetches the complete graph topology.
# From backend/app/services/zep_entity_reader.py
reader = ZepEntityReader() # Lines 80-86: Configures client with API key
nodes = reader.get_all_nodes(graph_id) # Lines 26-36: Retry-enabled node fetch
edges = reader.get_all_edges(graph_id) # Lines 57-68: Optional edge retrieval
The system stores retrieved nodes in a dictionary keyed by UUID to enable O(1) lookups during edge traversal (lines 52-54). This lookup table becomes critical when enriching entities with relationship context later in the pipeline.
The Core Filtering Logic
The filter_defined_entities method applies a three-stage validation to determine which nodes represent "real" entities versus structural artifacts:
1. Default-Label Removal
Every node in Zep carries generic labels "Entity" or "Node". The filter strips these universal markers to reveal domain-specific classifications underneath.
2. Custom-Label Detection
A node is retained only if it possesses at least one label that is not "Entity" or "Node". This signals that the node represents a domain-specific concept (such as "Person" or "Organization") rather than a graph placeholder (lines 61-66).
3. Optional Whitelist Intersection
If the caller provides the defined_entity_types parameter, the method performs a set intersection between the node's custom labels and the whitelist. Nodes failing this check are discarded (lines 69-74).
The first surviving custom label becomes the node's resolved entity_type, used for statistics aggregation and prompt generation downstream (lines 75-78).
Edge Enrichment and Relationship Mapping
When enrich_with_edges=True, the system augments filtered entities with their graph context. For each retained node, the method:
- Traverses all edges to find connections originating from or terminating at the current node, capturing directionality, edge names, facts, and opposite node UUIDs (lines 89-107)
- Resolves neighbor node details using the UUID lookup table, attaching minimal snapshots (UUID, name, labels, summary) to the
EntityNodeinstance (lines 108-124)
This enrichment transforms flat entity lists into contextual subgraphs, enabling agents to understand relationship topology during simulation steps.
Integration with the Simulation Workflow
The filtered entities feed directly into the simulation preparation stage. In backend/app/services/simulation_manager.py, the prepare_simulation method invokes filter_defined_entities during Stage 1 (lines 71-85), obtaining the canonical entity list that drives:
- Profile generation for simulation agents
- Configuration creation for scenario parameters
- Subsequent graph-memory updates via
ZepGraphMemoryUpdater
This integration ensures that only validated, domain-relevant entities influence the simulation state, preventing placeholder nodes from corrupting agent reasoning.
Practical Implementation Example
The following pattern demonstrates how to query and filter entities with optional type constraints and relationship enrichment:
from backend.app.services.zep_entity_reader import ZepEntityReader
# Initialize reader (automatically loads ZEP_API_KEY from config)
reader = ZepEntityReader()
# Execute filtering with whitelist and edge enrichment
filtered = reader.filter_defined_entities(
graph_id="simulation-graph-uuid",
defined_entity_types=["Person", "Organization", "Location"], # Optional whitelist
enrich_with_edges=True # Include relationship context
)
# Process results
print(f"Retrieved {filtered.filtered_count} valid entities of types {filtered.entity_types}")
for entity in filtered.entities:
print(f"\n{entity.name} ({entity.get_entity_type()})")
for edge in entity.related_edges:
print(f" → {edge['direction']} via {edge['edge_name']} to {edge.get('target_node_uuid')}")
This code returns a FilteredEntities dataclass (lines 130-138) containing the sanitized entity list, discovered type taxonomy, and cardinality counts.
Summary
- Entity filtering in mirofish removes generic Zep labels (
"Entity","Node") to isolate domain-specific nodes. - The
filter_defined_entitiesmethod inzep_entity_reader.pyimplements a whitelist-aware pipeline that validates nodes based on custom label presence. - Edge enrichment optionally attaches relationship context by traversing the graph and resolving neighbor nodes via UUID lookup tables.
- The
SimulationManagerconsumes filtered results during preparation stage 1 to ensure only meaningful entities drive agent simulations. - All configuration relies on
ZEP_API_KEYfrom the global config module, with retry logic wrapping all Zep API calls.
Frequently Asked Questions
What distinguishes a "custom label" from a default label in Zep entity filtering?
Default labels are the universal markers "Entity" and "Node" that Zep assigns to every node automatically. Custom labels are user-defined classifications (such as "Customer" or "Product") assigned during graph construction. The filtering pipeline explicitly strips default labels and checks for the existence of at least one custom label to determine if a node represents a domain-specific entity worth retaining.
How does the defined_entity_types whitelist parameter function?
When provided, defined_entity_types acts as an allowlist that intersects with a node's custom labels. If the node possesses any label present in the whitelist, it passes validation; otherwise, it is discarded. If the parameter is omitted (None), the system retains all nodes possessing at least one custom label regardless of its specific value.
When should developers enable enrich_with_edges during filtering?
Enable edge enrichment when the simulation logic requires relationship awareness—such as when agents need to understand connections between entities or when the scenario involves network traversal. This adds computational overhead due to edge walking and neighbor lookups (lines 89-124), so it should be disabled for simple entity counts or type validation tasks where topology is irrelevant.
What data structure does filter_defined_entities return?
The method returns a FilteredEntities dataclass containing three fields: entities (a list of EntityNode objects with optional edge attachments), entity_types (a set of discovered custom label strings), and filtered_count (an integer tally of valid entities). This structure provides both the processed data and metadata necessary for downstream simulation configuration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →