RAGAgent
In shortRAGAgent retrieves documents with vector search, optionally reranks with CohereReranker or CrossEncoderReranker, then synthesizes an answer.
- 5 min read
- 5 sections
- Updated
- v0.9.2
- Markdown
A Retrieval-Augmented Generation agent that retrieves relevant documents from a knowledge base before generating an answer.
Import path: tenxgraph.prebuilt.agent
Concept
A plain LLM only knows what it was trained on. RAG extends this by searching a vector store for documents relevant to the userโs query, injecting them into the conversation as a <context> block, then letting the LLM answer from grounded content.
Without reranker
flowchart LR
START([START]) --> RETRIEVE["RETRIEVE\n(vector search)"]
RETRIEVE --> SYNTHESIZE["SYNTHESIZE\n(inject docs + LLM call)"]
SYNTHESIZE --> END_NODE([END])
With reranker
flowchart LR
START([START]) --> RETRIEVE["RETRIEVE\n(top_k candidates)"]
RETRIEVE --> RERANK["RERANK\n(score and trim to top_n)"]
RERANK --> SYNTHESIZE["SYNTHESIZE\n(inject docs + LLM call)"]
SYNTHESIZE --> END_NODE([END])
What each node does
RETRIEVE โ finds the last user message in state.context, calls store.asearch(query, limit=top_k), and stores the result texts in state.execution_meta.internal_data["rag_docs"].
RERANK (optional) โ takes the rag_docs list, calls reranker.arerank(query, docs, top_n=top_n), and replaces the list with the re-ordered top-n results.
SYNTHESIZE โ wraps the docs and original query into a single augmented user message:
<context>
[1] first retrieved chunk
[2] second retrieved chunk
</context>
original user queryIt replaces the last user message in state.context with this augmented version, then calls agent.execute(state, config). The agentโs own system_prompt is never touched.
Why use a reranker?
Vector similarity (top_k=20) retrieves by embedding distance, which does not always equal relevance. A cross-encoder reranker scores each (query, document) pair directly and surfaces better candidates. The pattern is: retrieve many (top_k=20), rerank and keep few (top_n=5).
Reranker types
| Class | Backend | Install |
|---|---|---|
CohereReranker |
Cohere Rerank API (async) | pip install cohere |
CrossEncoderReranker |
Local sentence-transformers (CPU, runs in executor) | pip install sentence-transformers |
| Custom | Any class with async arerank(query, docs, top_n) -> list[str] |
Implement BaseReranker protocol |
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
store |
BaseStore |
required | Knowledge base vector store |
agent |
BaseAgent |
required | Pre-built agent that generates the final answer |
reranker |
BaseReranker | None |
None |
Optional reranker; enables the RERANK node |
top_k |
int |
5 |
Candidates retrieved from the store |
top_n |
int |
3 |
Candidates forwarded to the LLM after reranking (ignored without reranker) |
retrieval_strategy |
RetrievalStrategy |
SIMILARITY |
Vector search strategy |
score_threshold |
float | None |
None |
Minimum similarity score cutoff |
store_config |
dict | None |
None |
Extra config passed to every store.asearch call (e.g. {"user_id": "u42"}) |
state |
AgentState | None |
None |
Optional custom state subclass |
publisher |
BasePublisher | None |
None |
Event publisher for streaming |
container |
InjectQ | None |
None |
Dependency injection container |
compile() Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
checkpointer |
BaseCheckpointer |
None |
Persist and restore conversation state |
store |
BaseStore |
None |
Long-term agent-memory store (separate from the retrieval store) |
interrupt_before |
list[str] |
None |
Pause before the named nodes ("RETRIEVE", "RERANK", "SYNTHESIZE") |
interrupt_after |
list[str] |
None |
Pause after the named nodes |
callback_manager |
CallbackManager |
default | Lifecycle hooks |
media_store |
BaseMediaStore |
None |
Binary/media file storage |
shutdown_timeout |
float |
30.0 |
Seconds to wait for clean shutdown |
Full Code
Minimal โ no reranker
import asyncio
from dotenv import load_dotenv
from tenxgraph.core.graph import Agent
from tenxgraph.prebuilt.agent import RAGAgent
from tenxgraph.storage import create_local_qdrant_store
from tenxgraph.storage.store.embedding import OpenAIEmbedding
from tenxgraph.core.state import Message
load_dotenv()
store = create_local_qdrant_store(
path="./knowledge_base",
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
)
rag = RAGAgent(
store=store,
agent=Agent(
model="gpt-4o-mini",
provider="openai",
system_prompt=[{
"role": "system",
"content": (
"Answer questions using only the provided context. "
"If the context does not contain the answer, say so."
),
}],
),
top_k=5,
)
app = rag.compile()
async def main():
result = await app.ainvoke(
{"messages": [Message.text_message("What is the refund policy?")]},
config={"thread_id": "rag-1"},
)
print(result["context"][-1].text())
asyncio.run(main())With Cohere reranker
Retrieve 20 candidates, rerank and pass the best 5 to the LLM:
import asyncio
from tenxgraph.core.graph import Agent
from tenxgraph.prebuilt.agent import RAGAgent
from tenxgraph.prebuilt.agent.rag import CohereReranker
from tenxgraph.storage import create_local_qdrant_store
from tenxgraph.storage.store.embedding import OpenAIEmbedding
from tenxgraph.core.state import Message
store = create_local_qdrant_store(
path="./knowledge_base",
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
)
rag = RAGAgent(
store=store,
agent=Agent(
model="gpt-4o",
provider="openai",
system_prompt=[{
"role": "system",
"content": "Answer using only the provided context.",
}],
),
reranker=CohereReranker(api_key="cohere-api-key", model="rerank-v4.0-pro"),
top_k=20,
top_n=5,
)
app = rag.compile()
async def main():
result = await app.ainvoke(
{"messages": [Message.text_message("Summarize the warranty terms.")]},
config={"thread_id": "rag-rerank-1"},
)
print(result["context"][-1].text())
asyncio.run(main())With CrossEncoder (fully local, no API key)
from tenxgraph.core.graph import Agent
from tenxgraph.prebuilt.agent import RAGAgent
from tenxgraph.prebuilt.agent.rag import CrossEncoderReranker
from tenxgraph.storage import create_local_qdrant_store
from tenxgraph.storage.store.embedding import OpenAIEmbedding
store = create_local_qdrant_store(
path="./knowledge_base",
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
)
rag = RAGAgent(
store=store,
agent=Agent(model="gpt-4o-mini", provider="openai"),
reranker=CrossEncoderReranker("cross-encoder/ms-marco-MiniLM-L-6-v2"),
top_k=15,
top_n=4,
)
app = rag.compile()Custom reranker
Any class with an async arerank method satisfies the BaseReranker protocol:
from tenxgraph.core.graph import Agent
from tenxgraph.prebuilt.agent import RAGAgent
class MyReranker:
async def arerank(self, query: str, documents: list[str], top_n: int) -> list[str]:
# your ranking logic here
return sorted(documents, key=lambda d: len(d))[:top_n]
rag = RAGAgent(
store=store,
agent=Agent(model="gpt-4o-mini", provider="openai"),
reranker=MyReranker(),
top_k=10,
top_n=3,
)With a checkpointer (persistent conversations)
import asyncio
from tenxgraph.core.graph import Agent
from tenxgraph.prebuilt.agent import RAGAgent
from tenxgraph.storage import create_local_qdrant_store
from tenxgraph.storage.store.embedding import OpenAIEmbedding
from tenxgraph.storage.checkpointer import PgCheckpointer
from tenxgraph.core.state import Message
store = create_local_qdrant_store(
path="./knowledge_base",
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
)
rag = RAGAgent(
store=store,
agent=Agent(
model="gpt-4o-mini",
provider="openai",
system_prompt=[{
"role": "system",
"content": "Answer questions using only the provided context.",
}],
),
top_k=5,
)
checkpointer = PgCheckpointer(postgres_dsn="postgresql://user:pass@localhost/db")
app = rag.compile(checkpointer=checkpointer)
async def main():
result = await app.ainvoke(
{"messages": [Message.text_message("What is the cancellation policy?")]},
config={"thread_id": "user-55-support"},
)
print(result["context"][-1].text())
asyncio.run(main())Google Gemini
from tenxgraph.core.graph import Agent
from tenxgraph.prebuilt.agent import RAGAgent
from tenxgraph.storage import create_local_qdrant_store
from tenxgraph.storage.store.embedding import OpenAIEmbedding
store = create_local_qdrant_store(
path="./knowledge_base",
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
)
rag = RAGAgent(
store=store,
agent=Agent(
model="google/gemini-2.5-flash",
provider="google",
system_prompt=[{
"role": "system",
"content": "Answer questions using only the provided context.",
}],
trim_context=True,
),
top_k=5,
)
app = rag.compile()Running with agentflow play
graph.py
from tenxgraph.core.graph import Agent
from tenxgraph.prebuilt.agent import RAGAgent
from tenxgraph.storage import create_local_qdrant_store
from tenxgraph.storage.store.embedding import OpenAIEmbedding
store = create_local_qdrant_store(
path="./knowledge_base",
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
)
rag = RAGAgent(
store=store,
agent=Agent(
model="gpt-4o-mini",
provider="openai",
system_prompt=[{
"role": "system",
"content": "Answer questions using the provided context only.",
}],
),
top_k=5,
)
app = rag.compile()10xgraph.json
{
"agent": "graph:app",
"env": ".env",
"auth": null,
"checkpointer": null,
"injectq": null,
"store": null,
"redis": null,
"thread_name_generator": null
}.env
OPENAI_API_KEY=sk-...agentflow play