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:
parent
21e261f608
commit
771ac9c96c
5 changed files with 41 additions and 10 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue