RAG capability
RAGCapability adds grounded document search, sandboxed Python over the documents, and citations to a Pydantic AI agent.
from pydantic_ai import Agent
from haiku.rag.capabilities.rag import create_capability as rag
agent = Agent(
"openai:gpt-5",
capabilities=[rag(db_path="my.lancedb")],
)
result = await agent.run("What safety equipment does the manual require?")
print(result.output)
HaikuRAG.ask runs the same capability behind a single call. To add evidence compaction and the citation policy, see Compose an agent.
Tools
| Tool | Purpose |
|---|---|
search(query, limit?, sources?) |
Hybrid vector and full-text search with context expansion. sources narrows one search to named collections within the run. It is offered only when the run spans more than one. |
execute_code(code) |
Run Python against the virtual document filesystem. |
cite(chunk_ids) |
Register retrieved or filesystem-derived chunk IDs as answer citations. |
Parameters
create_capability takes:
| Parameter | Default | Meaning |
|---|---|---|
db_path |
None |
A database path, where the configuration places none. See Database selection |
config |
the loaded configuration | An AppConfig |
defer_loading |
False |
Keep the instructions and tools out of the model context until the model loads the capability |
rag |
None |
An open HaikuRAG client to use. The capability never closes it |
request_limit |
30 |
Model requests per agent run. None disables it |
sources |
all configured | The databases the capability covers, the sandbox filesystem included |
vision |
qa.model.vision |
Whether picture results are attached to search returns as images. Set it for the model the agent runs |
Budgets
When the request limit is reached, search and execute_code are removed and cite remains for two further requests that call one of the capability's tools, so the model can register citations before answering. Requests spent on other tools do not count against that window, and other tools stay available. Each agent run starts a fresh limit.
When qa.max_searches or qa.max_executions runs out, the exhausted tool keeps failing rather than disappearing, and the instructions name it on every following request. Searching from inside execute_code does not count against qa.max_searches. The budgets are described under Search and question answering.
Sandbox
execute_code runs Python in a Monty interpreter over a virtual filesystem. Each document the question covers is mounted at /documents/{document_id}/:
| File | Contents |
|---|---|
metadata.json |
Document id, title, URI, creation time and metadata |
content.txt |
The full text |
items.jsonl |
Document items in reading order |
chunks.jsonl |
Chunk ids with their metadata |
toc.json |
The section tree |
Code can also await search(query), whose rows carry chunk_meta and picture_refs, and await list_documents(), whose rows carry metadata.
Monty is a Python subset. Useful modules include json, re, math, pathlib, datetime, collections, itertools, functools and dataclasses. decimal and statistics are absent. Class inheritance and generators are not supported. Files are read-only, and there is no network and no filesystem beyond /documents, which is the working directory. time.sleep() returns immediately.
One sandbox serves every execute_code call of a run, so variables persist between calls, except across a call stopped by sandbox.code_timeout. It opens when the model first executes code. sandbox.code_timeout and sandbox.max_output_chars bound each call, see Sandbox configuration. The MCP server's execute_code tool runs the same sandbox, one program per call.
State
When agent dependencies expose a state dictionary, the capability keeps a RAGState under "rag":
class RAGState(BaseModel):
citation_index: dict[str, Citation]
citations: list[str]
document_filter: str | None
sources: list[str] | None
evidence: CapabilityEvidenceRecord
searches: dict[str, list[SearchResult]]
executions: list[CodeExecutionEntry]
document_filter, sources, citation_index and evidence persist across runs. Citations, searches and executions are cleared when a new question starts. A run that resumes a question keeps the evidence it is still answering from.
evidence records which chunks this capability cited, in which question, and whether the citing question also retrieved them. haiku.rag.capabilities.ledger.citation_status(records, question=...) derives missing, grounded or ungrounded from it.
The capability does not alter the message history. To stop long conversations resending old retrieved content, register the compaction capability.
Domain context and vision
prompts.domain_preamble is prepended to the packaged capability instructions, see Prompts. With vision on, picture results are attached to search returns as BinaryContent. See picture handling.