Skip to main content
Version: 0.0.4

Retrieve and Answer

This guide shows how to use SearchEngine to retrieve entities and chunks from a knowledge graph, and how to use build_agent to get cited answers.

Prerequisites​

Install the retrieval and agent extras:

pip install 'agentic-graphrag[neo4j,llm]'
# For the agent layer:
pip install 'agentic-graphrag[agents]'

Direct SearchEngine Usage​

import asyncio
from agrag.common.data_models.graph_schema import GENERIC
from agrag.graphdb.neo4j import Neo4jGraphStore, Neo4jSettings
from agrag.embedding.sentence_transformers import SentenceTransformerEmbedder
from agrag.retrieval.search_engine import SearchEngine
from agrag.retrieval.recipes import HYBRID

async def main():
graph_store = Neo4jGraphStore(settings=Neo4jSettings())
embedder = SentenceTransformerEmbedder()

engine = SearchEngine(
graph_store=graph_store,
embedder=embedder,
entity_labels=[entity.label for entity in GENERIC.entities],
)

results = await engine.search(
"What treats headaches?",
HYBRID,
)

for result in results:
print(f"{result.item} (score={result.score:.3f})")

asyncio.run(main())

Child chunks and their parents​

When you chunk with ParentChildChunker, a chunk search returns child chunks. Each SearchResult has the parent chunk in SearchResult.parent; it is None for every other result. The agent sees the parent text under the citation key of the first child hit, for example [C1]. A later child of the same parent shows its own text and the note (part of [C1]). A citation always names the child. See Configure chunking for the setup.

Using build_agent​

import asyncio
from agrag.agents.build import build_agent
from agrag.agents.settings import AgentLLMSettings
from agrag.common.data_models.graph_schema import GENERIC
from agrag.embedding.sentence_transformers import SentenceTransformerEmbedder
from agrag.graphdb.neo4j import Neo4jGraphStore, Neo4jSettings
from agrag.retrieval.search_engine import SearchEngine

async def main():
graph_store = Neo4jGraphStore(settings=Neo4jSettings())
embedder = SentenceTransformerEmbedder()
engine = SearchEngine(
graph_store=graph_store,
embedder=embedder,
)

agent = build_agent(
engine=engine,
llm_settings=AgentLLMSettings.from_openai_compatible_env(),
graph_schema=GENERIC,
)

result = await agent.ainvoke({
"messages": [
{"role": "user", "content": "What treats headaches?"}
]
})

last_message = result["messages"][-1]
print(last_message["content"] if isinstance(last_message, dict) else last_message.content)

asyncio.run(main())

result["ledger"] holds the citation keys the agent assigned during the run. Call result["ledger"].resolve("C1") to get the SearchResult behind a key in the answer. It returns None for a key the run never assigned. Each call to ainvoke returns a new ledger.

The deep-agent path returns LangChain message objects, so the last message's text is .content; the optional-dependency fallback returns dictionaries, as shown above. graph_schema tells the researcher's prompt and the generated-query tool which labels and relationship types the graph uses. Omit it to use the engine's own schema, which falls back to GENERIC when the engine was built without one. Pass a schema that differs from the engine's schema, and build_agent raises ValueError, so the prompt can never describe a graph the engine does not search.

Scoping agent retrieval​

By default, the agent's tools search the whole graph. Pass SearchFilters to limit every tool search by document set, tenant, or label:

from agrag.retrieval.filters import SearchFilters

agent = build_agent(
engine=engine,
llm_settings=AgentLLMSettings.from_openai_compatible_env(),
filters=SearchFilters(document_ids=["doc-1", "doc-2"]),
)

Set the filters when your application builds the agent. The tools enforce the filters. The model cannot see or change them.

Build-time filters and per-call tool parameters have different roles. The caller sets filters, and the model cannot see them. The model chooses the tool parameters for each call, such as labels, document_ids, limit, min_score, relation_type, and direction. These parameters can only narrow the build-time scope. They cannot widen it. The tools refuse requests outside the scope.

The document_ids filter applies to the five discovery tools and entity lookup. It also limits neighbors from find_related_entities and traverse_from_entity. The labels and properties filters apply to entity lookup and traversal neighbors. relation_types is a traversal allowlist. list_relationship_types uses only this allowlist. The eleventh tool, query_graph_directly, is outside both groups. Unscoped agents receive it. Caller-scoped agents do not.

Researcher tools​

The researcher subagent receives eleven tools when the agent is unscoped, ten when caller-set base filters are non-empty. The tools fall into three groups plus a calculator.

Discovery tools find entry points into the graph:

ToolPurposeKey parameters
search_source_textFind passages of source text that mention the query termsquery, limit, document_ids
look_up_entityFind entities that match a name or descriptionquery, limit, labels
explore_relatedSearch entities and source text together for a topicquery, limit, labels, document_ids
answer_from_graph_structureAnswer from reranked entity and passage evidencequery, limit, min_score
answer_thematic_questionAnswer a broad question from community summariesquery, limit

Structural navigation tools read what the graph stores about one entity:

ToolPurposeKey parameters
describe_entityShow an entity's own recorded properties, not its neighboursentity
list_relationship_typesList the relationship types attached to an entityentity, relation_type_filter

Relational tools follow relationships between entities:

ToolPurposeKey parameters
find_related_entitiesFind the entities one relationship type away from an entityentity, relation_type, direction, community_expand
traverse_from_entityWalk outward from an entity, with a fallback to relationship types when the neighbourhood is too wideentity, relation_type, direction, depth
query_graph_directlyAsk a structural question in one generated read-only Cypher query; present only for unscoped agentsquery

compute_over_evidence stands outside the three groups. It computes a count, a sum, or a comparison over numbers the researcher already read from earlier tool results. It never queries the graph. The design rationale for this tool set is recorded in agrag_agents.md in the test-agentic-graphrag companion repository's thoughts notes.

Available Recipes​

RecipeMethodsBFSReranker
ENTITYentityNoNone
CHUNKchunkNoNone
HYBRIDentity, chunkNoNone
HYBRID_RERANKEDentity, chunkNocross_encoder
GRAPH_EXPANDentityYesNone
THEMATICcommunityNoNone
TEXT2CYPHERtext2cypherNoNone

Configuration​

Set environment variables with the RETRIEVAL_ prefix:

RETRIEVAL_ENTITY_LABELS='["Drug","Condition"]'
RETRIEVAL_ENTITY_TOP_K=10
RETRIEVAL_CHUNK_TOP_K=10
RETRIEVAL_HYBRID_ALPHA=0.5
RETRIEVAL_TRAVERSAL_DEPTH=2

RETRIEVAL_ENTITY_LABELS (or the entity_labels argument) names the schema entity labels that entity search runs against. Ingestion creates one native vector index per label, so search needs the labels themselves. The RETRIEVAL_ENTITY_COLLECTION name does not apply here; it applies only when you configure a separate VectorStore.