Register the optional capabilities where agents are composed

The README feature list and the overview stopped at the analysis capability.
Both examples and the app backend composed agents without the capabilities the
documentation recommends alongside an evidence capability.

custom_agent.py ran each input as an independent agent run, so it needed a state
dict and a carried history before compaction could mean anything there: without
state the evidence record is empty, and earlier evidence would reduce to
receipts retaining nothing.
This commit is contained in:
Yiorgis Gozadinos 2026-08-13 15:04:05 +03:00
parent 21e261f608
commit 771ac9c96c
No known key found for this signature in database
5 changed files with 41 additions and 10 deletions

View file

@ -15,6 +15,8 @@ Agentic RAG built on [LanceDB](https://lancedb.com/), [Pydantic AI](https://ai.p
- **Vision QA** — Vision-capable models receive figure bytes alongside chunk text; attach your own images to questions in `ask`, `analyze`, MCP, and the chat TUI
- **Reranking** — local cross-encoders, Cohere, Zero Entropy, or vLLM
- **Analysis capability** — Complex analytical tasks via sandboxed Python code execution (aggregation, computation, multi-document analysis)
- **Evidence compaction** — Optional capability that replaces earlier questions' search results on the request with the evidence they cited, so long conversations stop resending everything they retrieved
- **Citation policy** — Optional capability that requires every answer to declare what grounds it, including declaring that nothing does
- **Conversational RAG** — Chat TUI and web application for multi-turn conversations with session memory
- **Document structure** — Stores full [DoclingDocument](https://docling-project.github.io/docling/concepts/docling_document/), enabling structure-aware context expansion
- **Multiple providers** — Embeddings: Ollama, OpenAI, VoyageAI, Cohere, LM Studio, vLLM (multimodal via `multimodal: true` on vLLM/VoyageAI/Cohere). QA: any model supported by Pydantic AI

View file

@ -21,6 +21,9 @@ from starlette.routing import Route
from haiku.rag.capabilities.compaction import (
create_capability as create_compaction,
)
from haiku.rag.capabilities.policy import (
create_capability as create_citation_policy,
)
from haiku.rag.capabilities.rag import AGENT_PREAMBLE, RAGState, create_capability
from haiku.rag.client import HaikuRAG
from haiku.rag.config import load_yaml_config
@ -85,8 +88,9 @@ agent = Agent(
get_model(Config.qa.model, Config),
instructions=AGENT_PREAMBLE,
# Conversations here are multi-turn, so earlier questions are reduced to the
# evidence they cited rather than carried whole.
capabilities=[capability, create_compaction()],
# evidence they cited rather than carried whole, and every answer declares
# what grounds it so the UI can show citations for all of them.
capabilities=[capability, create_compaction(), create_citation_policy()],
deps_type=AppDeps,
)

View file

@ -27,7 +27,7 @@ The chat TUI is one way to interact with the database. `haiku-rag ask` and `haik
**Search.** Hybrid retrieval (vector + full-text with reciprocal rank fusion), optional cross-encoder reranking, structure-aware context expansion. Image-as-query and cross-modal retrieval when configured with a multimodal embedder.
**Answer.** RAG capability with citations including page numbers, section headings, and visual grounding. Vision-capable models receive figure bytes alongside chunk text. Analysis capability with a sandboxed Python interpreter for aggregation and computation across documents.
**Answer.** RAG capability with citations including page numbers, section headings, and visual grounding. Vision-capable models receive figure bytes alongside chunk text. Analysis capability with a sandboxed Python interpreter for aggregation and computation across documents. Optional capabilities compact a long conversation down to the evidence it cited, and require every answer to declare its grounding.
**Integrate.** Use it from Python, the CLI, the [MCP server](mcp.md), or through composable native Pydantic AI [capabilities](capabilities/index.md).

View file

@ -1,6 +1,7 @@
"""Custom agent using the native haiku.rag RAG capability.
Demonstrates composing a native Pydantic AI capability into an agent.
Demonstrates composing native Pydantic AI capabilities into an agent, and what a
multi-turn conversation needs to carry between runs.
Requirements:
- An Ollama instance running locally (default embedder)
@ -13,21 +14,40 @@ Usage:
import asyncio
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
from pydantic_ai import Agent
from pydantic_ai.messages import ModelMessage
from haiku.rag.capabilities.rag import create_capability
from haiku.rag.capabilities.compaction import create_capability as compaction
from haiku.rag.capabilities.policy import create_capability as citation_policy
from haiku.rag.capabilities.rag import create_capability as rag
@dataclass
class Deps:
state: dict[str, Any] = field(default_factory=dict)
async def main(db_path: str) -> None:
capability = create_capability(db_path=Path(db_path), defer_loading=False)
agent = Agent(
"anthropic:claude-haiku-4-5-20251001",
capabilities=[capability],
capabilities=[
rag(db_path=Path(db_path), defer_loading=False),
compaction(),
citation_policy(),
],
deps_type=Deps,
)
# One state dict and one history for the whole session. The capabilities read
# both: the state holds what was retrieved and cited, and the message counts
# are how they tell one question from the next.
deps = Deps()
messages: list[ModelMessage] = []
print("Custom agent ready. Ctrl+C to exit.\n")
while True:
try:
@ -38,7 +58,8 @@ async def main(db_path: str) -> None:
if not user_input:
continue
result = await agent.run(user_input)
result = await agent.run(user_input, deps=deps, message_history=messages)
messages = list(result.all_messages())
print(f"\nAgent: {result.output}\n")

View file

@ -26,6 +26,8 @@ from starlette.requests import Request
from starlette.responses import JSONResponse, Response, StreamingResponse
from starlette.routing import Route
from haiku.rag.capabilities.compaction import create_capability as compaction
from haiku.rag.capabilities.policy import create_capability as citation_policy
from haiku.rag.capabilities.rag import RAGState, create_capability
db_path = os.environ.get("DB_PATH")
@ -45,7 +47,9 @@ class AppDeps:
agent = Agent(
"anthropic:claude-haiku-4-5-20251001",
capabilities=[capability],
# The client returns the state snapshot with every run, so earlier questions are
# reduced to the evidence they cited and every answer declares its grounding.
capabilities=[capability, compaction(), citation_policy()],
deps_type=AppDeps,
)