`_compact_old_tool_returns`, `PRIOR_TURN_NOTICE` and `turn_start` leave `RAGCapabilityBase`, along with its `wrap_model_request` hook. The evidence capabilities now retrieve and validate, and nothing else. Registering the compaction capability is what rewrites a request; leaving it out sends the transcript untouched, which was never a choice a host could make before. The boundary is the recorded question identity rather than message shape, so a resumption compacts what lies below the question in progress instead of switching compaction off for the whole run. The newest earlier evidence return carries the capsule and every other becomes a receipt, so one capsule exists by construction and every return stays paired with its call. Pictures of cited evidence are fetched through the capability that retrieved them and re-attached beside the capsule with fresh labels. Ownership of a picture on the wire requires the machine tag we write and an image directly after it, since neither position nor prose is proof: several tools' results can arrive in one request, and a user quoting our wording above their own picture had it removed. A picture that cannot be fetched or decoded is emitted with neither its image nor its label. The chat TUI and the example backend register the compactor, being multi-turn. `client.ask`, `client.analyze` and the MCP tools do not: a single-shot question has nothing earlier to compact.
82 lines
3.4 KiB
Markdown
82 lines
3.4 KiB
Markdown
# Capabilities
|
|
|
|
haiku.rag provides native [Pydantic AI capabilities](https://ai.pydantic.dev/capabilities/):
|
|
|
|
| Capability | Use it for |
|
|
|---|---|
|
|
| [`RAGCapability`](rag.md) | Grounded document search and citations. |
|
|
| [`AnalysisCapability`](analysis.md) | Corpus computation and structural analysis with sandboxed Python. |
|
|
| `EvidenceCompactionCapability` | Optional. Shrinking a conversation's history to the evidence that was cited. |
|
|
|
|
The two evidence capabilities are deferred by default. An agent initially sees only their descriptions and the standard `load_capability` tool. Instructions and tools enter the model context only when the model loads a capability.
|
|
|
|
## Compose an agent
|
|
|
|
```python
|
|
from pydantic_ai import Agent
|
|
from haiku.rag.capabilities.rag import create_capability
|
|
|
|
rag = create_capability(db_path="my.lancedb")
|
|
agent = Agent("openai:gpt-5", capabilities=[rag])
|
|
|
|
result = await agent.run("What does the knowledge base say about X?")
|
|
print(result.output)
|
|
```
|
|
|
|
Attach both capabilities when an agent should choose between retrieval and computation:
|
|
|
|
```python
|
|
from haiku.rag.capabilities.analysis import create_capability as analysis
|
|
from haiku.rag.capabilities.rag import create_capability as rag
|
|
|
|
agent = Agent(
|
|
"openai:gpt-5",
|
|
capabilities=[rag(db_path="my.lancedb"), analysis(db_path="my.lancedb")],
|
|
)
|
|
```
|
|
|
|
## Multi-turn conversations
|
|
|
|
Every question adds its search results to the history, so requests grow turn after
|
|
turn, and can degrade answers or exceed a provider's limits as they do. Register the
|
|
compaction capability to replace earlier questions' evidence with the evidence that
|
|
was actually cited:
|
|
|
|
```python
|
|
from haiku.rag.capabilities.compaction import create_capability as compaction
|
|
from haiku.rag.capabilities.rag import create_capability as rag
|
|
|
|
agent = Agent(
|
|
"openai:gpt-5",
|
|
capabilities=[rag(db_path="my.lancedb"), compaction()],
|
|
)
|
|
```
|
|
|
|
Cited text and cited page images are kept in full, grouped by the question that
|
|
cited them, and stay citable by the same chunk ids. Everything else earlier becomes a
|
|
short receipt. Registering the capability is the only switch: leave it out and the
|
|
transcript reaches the model untouched. There is nothing to configure.
|
|
|
|
Compaction rewrites the request, never the stored history, so `all_messages()` still
|
|
holds everything the run gathered. Retained evidence still grows with the
|
|
conversation — this reduces what a request carries, it does not bound it. A host that
|
|
needs more aggressive pruning can compact its own requests further, on the wire only.
|
|
|
|
Resuming a question (deferred tool results, an interruption, a suspension) requires
|
|
the host to carry the capability state from the run being resumed, alongside the
|
|
message history. Without it the identity of the question in progress is unknowable
|
|
and the run fails rather than silently treating it as a new question.
|
|
|
|
## State
|
|
|
|
Capabilities use a plain `state: dict[str, Any]` attribute on agent dependencies when one is available. RAG state lives under `"rag"`; analysis state lives under `"analysis"`. This keeps state independent of any transport or UI protocol.
|
|
|
|
Applications serving AG-UI should adapt the agent with Pydantic AI's `AGUIAdapter`. Native model and tool events require no haiku.rag-specific bridge.
|
|
|
|
## Database path
|
|
|
|
Both factories resolve their database in this order:
|
|
|
|
1. The `db_path` argument.
|
|
2. `HAIKU_RAG_DB`.
|
|
3. `config.storage.data_dir / "haiku.rag.lancedb"`.
|