Update docs
This commit is contained in:
parent
557a296341
commit
6ee0d74f92
2 changed files with 80 additions and 190 deletions
267
docs/agents.md
267
docs/agents.md
|
|
@ -1,156 +1,57 @@
|
|||
# Agents
|
||||
|
||||
Three agentic flows are provided by haiku.rag:
|
||||
Two agentic flows are provided by haiku.rag:
|
||||
|
||||
- Simple QA Agent — a focused question answering agent
|
||||
- Deep QA Agent — multi-agent question decomposition for complex questions
|
||||
- Research Multi‑Agent — a multi‑step, analyzable research workflow
|
||||
- **Simple QA Agent** — a focused question answering agent
|
||||
- **Research Graph** — a multi-step research workflow with question decomposition
|
||||
|
||||
For an interactive example using Pydantic AI and AG-UI, see the [Interactive Research Assistant](https://github.com/ggozad/haiku.rag/tree/main/examples/ag-ui-research) example ([demo video](https://vimeo.com/1128874386)). The demo uses a knowledge base containing haiku.rag's code and documentation.
|
||||
For an interactive example using Pydantic AI and AG-UI, see the [Interactive Research Assistant](https://github.com/ggozad/haiku.rag/tree/main/examples/ag-ui-research) example ([demo video](https://vimeo.com/1128874386)).
|
||||
|
||||
See [QA and Research Configuration](configuration/qa-research.md) for configuring model, iterations, concurrency, and other settings.
|
||||
|
||||
|
||||
### Simple QA Agent
|
||||
## Simple QA Agent
|
||||
|
||||
The simple QA agent answers a single question using the knowledge base. It retrieves relevant chunks, optionally expands context around them, and asks the model to answer strictly based on that context.
|
||||
|
||||
Key points:
|
||||
|
||||
- Uses a single `search_documents` tool to fetch relevant chunks
|
||||
- Can be run with or without inline citations in the prompt (citations prefer
|
||||
document titles when present, otherwise URIs)
|
||||
- Can be run with or without inline citations in the prompt
|
||||
- Returns a plain string answer
|
||||
|
||||
Python usage:
|
||||
**CLI usage:**
|
||||
|
||||
```bash
|
||||
haiku-rag ask "What is climate change?"
|
||||
|
||||
# With citations
|
||||
haiku-rag ask "What is climate change?" --cite
|
||||
|
||||
# Deep mode (uses research graph with optimized settings)
|
||||
haiku-rag ask "What are the main features of haiku.rag?" --deep
|
||||
```
|
||||
|
||||
**Python usage:**
|
||||
|
||||
```python
|
||||
from haiku.rag.client import HaikuRAG
|
||||
from haiku.rag.qa.agent import QuestionAnswerAgent
|
||||
|
||||
async with HaikuRAG(path_to_db) as client:
|
||||
# Choose a provider and model (see Configuration for env defaults)
|
||||
agent = QuestionAnswerAgent(
|
||||
client=client,
|
||||
provider="openai", # or "ollama", "vllm", etc.
|
||||
provider="openai",
|
||||
model="gpt-4o-mini",
|
||||
use_citations=False, # set True to bias prompt towards citing sources
|
||||
use_citations=False,
|
||||
)
|
||||
|
||||
answer = await agent.answer("What is climate change?")
|
||||
print(answer)
|
||||
```
|
||||
|
||||
### Deep QA Agent
|
||||
## Research Graph
|
||||
|
||||
Deep QA is a multi-agent system that decomposes complex questions into sub-questions, answers them in batches, evaluates sufficiency, and iterates if needed before synthesizing a final answer. It's lighter than the full research workflow but more powerful than the simple QA agent.
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Deep QA graph
|
||||
---
|
||||
stateDiagram-v2
|
||||
[*] --> plan
|
||||
plan --> get_batch
|
||||
get_batch --> search_one: Has questions (map)
|
||||
get_batch --> synthesize: No questions
|
||||
search_one --> collect_answers
|
||||
collect_answers --> decide
|
||||
decide --> get_batch: Continue QA
|
||||
decide --> synthesize: Done with QA
|
||||
synthesize --> [*]
|
||||
```
|
||||
|
||||
Key nodes:
|
||||
|
||||
- **plan**: Decomposes the question into focused sub-questions using a presearch tool
|
||||
- **get_batch**: Retrieves remaining sub-questions for the current iteration
|
||||
- **search_one**: Answers a single sub-question using the knowledge base (mapped in parallel)
|
||||
- **collect_answers**: Aggregates search results from parallel executions
|
||||
- **decide**: Evaluates if sufficient information has been gathered or if more iterations are needed
|
||||
- **synthesize**: Generates the final comprehensive answer from all gathered information
|
||||
|
||||
Key differences from Research:
|
||||
|
||||
- **Simpler evaluation**: Uses sufficiency check (not confidence + insight analysis)
|
||||
- **Direct answers**: Returns just the answer (not a full research report)
|
||||
- **Question-focused**: Optimized for answering specific questions, not open-ended research
|
||||
- **Supports citations**: Can include inline source citations like `[document.md]`
|
||||
- **Configurable iterations**: Control max_iterations (default: 2) and max_concurrency (default: 1)
|
||||
|
||||
Note on parallel execution:
|
||||
- The `search_one` node is mapped over all questions in a batch
|
||||
- Parallelism is controlled via `max_concurrency`
|
||||
- All questions in an iteration are processed before evaluation
|
||||
|
||||
CLI usage:
|
||||
|
||||
```bash
|
||||
# Deep QA without citations
|
||||
haiku-rag ask "What are the main features of haiku.rag?" --deep
|
||||
|
||||
# Deep QA with citations
|
||||
haiku-rag ask "What are the main features of haiku.rag?" --deep --cite
|
||||
```
|
||||
|
||||
Python usage:
|
||||
|
||||
```python
|
||||
from haiku.rag.client import HaikuRAG
|
||||
from haiku.rag.config import Config
|
||||
from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
|
||||
from haiku.rag.graph.deep_qa.graph import build_deep_qa_graph
|
||||
from haiku.rag.graph.deep_qa.state import DeepQADeps, DeepQAState
|
||||
|
||||
async with HaikuRAG(path_to_db) as client:
|
||||
# Use global config (recommended)
|
||||
graph = build_deep_qa_graph(config=Config)
|
||||
context = DeepQAContext(
|
||||
original_question="What are the main features of haiku.rag?",
|
||||
use_citations=True
|
||||
)
|
||||
state = DeepQAState.from_config(context=context, config=Config)
|
||||
deps = DeepQADeps(client=client)
|
||||
|
||||
result = await graph.run(
|
||||
state=state,
|
||||
deps=deps
|
||||
)
|
||||
|
||||
print(result.answer)
|
||||
print(result.sources)
|
||||
```
|
||||
|
||||
Alternative usage with custom config:
|
||||
|
||||
```python
|
||||
# Create a custom config with different settings
|
||||
from haiku.rag.config.models import AppConfig, QAConfig
|
||||
|
||||
custom_config = AppConfig(
|
||||
qa=QAConfig(
|
||||
provider="openai",
|
||||
model="gpt-4o-mini",
|
||||
max_sub_questions=5,
|
||||
max_iterations=3,
|
||||
max_concurrency=2,
|
||||
)
|
||||
)
|
||||
|
||||
graph = build_deep_qa_graph(config=custom_config)
|
||||
context = DeepQAContext(
|
||||
original_question="What are the main features of haiku.rag?",
|
||||
use_citations=True
|
||||
)
|
||||
state = DeepQAState.from_config(context=context, config=custom_config)
|
||||
deps = DeepQADeps(client=client)
|
||||
|
||||
result = await graph.run(state=state, deps=deps)
|
||||
```
|
||||
|
||||
### Research Graph
|
||||
|
||||
The research workflow is implemented as a typed pydantic‑graph. It plans, searches (in parallel batches), evaluates, and synthesizes into a final report — with clear stop conditions and shared state.
|
||||
The research workflow is implemented as a typed pydantic-graph. It plans, searches (in parallel batches), evaluates, and synthesizes into a final report.
|
||||
|
||||
```mermaid
|
||||
---
|
||||
|
|
@ -162,47 +63,49 @@ stateDiagram-v2
|
|||
get_batch --> search_one: Has questions (map)
|
||||
get_batch --> synthesize: No questions
|
||||
search_one --> collect_answers
|
||||
collect_answers --> analyze_insights
|
||||
analyze_insights --> decide
|
||||
collect_answers --> decide
|
||||
decide --> get_batch: Continue research
|
||||
decide --> synthesize: Done researching
|
||||
synthesize --> [*]
|
||||
```
|
||||
|
||||
Key nodes:
|
||||
**Key nodes:**
|
||||
|
||||
- **plan**: Builds up to 3 standalone sub‑questions (uses an internal presearch tool)
|
||||
- **get_batch**: Retrieves remaining sub‑questions for the current iteration
|
||||
- **search_one**: Answers a single sub‑question using the KB with minimal, verbatim context (mapped in parallel)
|
||||
- **plan**: Builds up to 3 standalone sub-questions (uses an internal presearch tool)
|
||||
- **get_batch**: Retrieves remaining sub-questions for the current iteration
|
||||
- **search_one**: Answers a single sub-question using the KB (mapped in parallel)
|
||||
- **collect_answers**: Aggregates search results from parallel executions
|
||||
- **analyze_insights**: Synthesizes fresh insights, updates gaps, and suggests new sub-questions
|
||||
- **decide**: Checks sufficiency/confidence thresholds and determines whether to continue research
|
||||
- **decide**: Evaluates confidence and determines whether to continue or synthesize
|
||||
- **synthesize**: Generates a final structured research report
|
||||
|
||||
Primary models:
|
||||
**Primary models:**
|
||||
|
||||
- `SearchAnswer` — one per sub‑question (query, answer, context, sources)
|
||||
- `InsightRecord` / `GapRecord` — structured tracking of findings and open issues
|
||||
- `InsightAnalysis` — output of the analysis stage (insights, gaps, commentary)
|
||||
- `EvaluationResult` — insights, new questions, sufficiency, confidence
|
||||
- `SearchAnswer` — one per sub-question (query, answer, confidence, citations)
|
||||
- `EvaluationResult` — confidence score, new questions, sufficiency assessment
|
||||
- `ResearchReport` — final report (title, executive summary, findings, conclusions, …)
|
||||
|
||||
Note on parallel execution:
|
||||
**Parallel execution:**
|
||||
|
||||
- The `search_one` node is mapped over all questions in a batch
|
||||
- Parallelism is controlled via `max_concurrency`
|
||||
- Analysis and decision nodes process results after each batch completes
|
||||
- Decision nodes process results after each batch completes
|
||||
|
||||
CLI usage:
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Basic usage (uses config from file or defaults)
|
||||
# Basic usage
|
||||
haiku-rag research "How does haiku.rag organize and query documents?"
|
||||
|
||||
# With verbose output (shows progress)
|
||||
haiku-rag research "How does haiku.rag organize and query documents?" --verbose
|
||||
|
||||
# With custom config file
|
||||
haiku-rag --config my-research-config.yaml research "How does haiku.rag organize and query documents?" --verbose
|
||||
# With document filter
|
||||
haiku-rag research "What are the key findings?" --filter "uri LIKE '%report%'"
|
||||
```
|
||||
|
||||
Python usage (blocking result):
|
||||
### Python Usage
|
||||
|
||||
**Basic example:**
|
||||
|
||||
```python
|
||||
from haiku.rag.client import HaikuRAG
|
||||
|
|
@ -212,41 +115,25 @@ from haiku.rag.graph.research.graph import build_research_graph
|
|||
from haiku.rag.graph.research.state import ResearchDeps, ResearchState
|
||||
|
||||
async with HaikuRAG(path_to_db) as client:
|
||||
# Use global config (recommended)
|
||||
graph = build_research_graph(config=Config)
|
||||
question = "What are the main drivers and trends of global temperature anomalies since 1990?"
|
||||
context = ResearchContext(original_question=question)
|
||||
context = ResearchContext(original_question="What are the main features?")
|
||||
state = ResearchState.from_config(context=context, config=Config)
|
||||
deps = ResearchDeps(client=client)
|
||||
|
||||
result = await graph.run(
|
||||
state=state,
|
||||
deps=deps,
|
||||
)
|
||||
report = await graph.run(state=state, deps=deps)
|
||||
|
||||
report = result
|
||||
print(report.title)
|
||||
print(report.executive_summary)
|
||||
```
|
||||
|
||||
### Filtering Documents
|
||||
|
||||
Both Research and Deep QA graphs support restricting searches to specific documents via the `search_filter` parameter. Set it to a SQL WHERE clause before running:
|
||||
|
||||
```python
|
||||
state = ResearchState.from_config(context=context, config=Config)
|
||||
# Only search documents with these IDs
|
||||
state.search_filter = "id IN ('doc-123', 'doc-456')"
|
||||
|
||||
result = await graph.run(state=state, deps=deps)
|
||||
```
|
||||
|
||||
The filter applies to all search operations in the graph (context gathering and sub-question searches). See [Filtering Search Results](python.md#filtering-search-results) for available filter columns and syntax.
|
||||
|
||||
Alternative usage with custom config:
|
||||
**With custom config:**
|
||||
|
||||
```python
|
||||
from haiku.rag.client import HaikuRAG
|
||||
from haiku.rag.config.models import AppConfig, ResearchConfig
|
||||
from haiku.rag.graph.research.dependencies import ResearchContext
|
||||
from haiku.rag.graph.research.graph import build_research_graph
|
||||
from haiku.rag.graph.research.state import ResearchDeps, ResearchState
|
||||
|
||||
custom_config = AppConfig(
|
||||
research=ResearchConfig(
|
||||
|
|
@ -258,28 +145,28 @@ custom_config = AppConfig(
|
|||
)
|
||||
)
|
||||
|
||||
graph = build_research_graph(config=custom_config)
|
||||
context = ResearchContext(original_question=question)
|
||||
state = ResearchState.from_config(context=context, config=custom_config)
|
||||
deps = ResearchDeps(client=client)
|
||||
async with HaikuRAG(path_to_db) as client:
|
||||
graph = build_research_graph(config=custom_config)
|
||||
context = ResearchContext(original_question="What are the main features?")
|
||||
state = ResearchState.from_config(context=context, config=custom_config)
|
||||
deps = ResearchDeps(client=client)
|
||||
|
||||
result = await graph.run(state=state, deps=deps)
|
||||
report = await graph.run(state=state, deps=deps)
|
||||
```
|
||||
|
||||
Python usage (streamed AG-UI events):
|
||||
**Streaming AG-UI events:**
|
||||
|
||||
```python
|
||||
from haiku.rag.graph.agui import stream_graph
|
||||
from haiku.rag.client import HaikuRAG
|
||||
from haiku.rag.config import Config
|
||||
from haiku.rag.graph.agui import stream_graph
|
||||
from haiku.rag.graph.research.dependencies import ResearchContext
|
||||
from haiku.rag.graph.research.graph import build_research_graph
|
||||
from haiku.rag.graph.research.state import ResearchDeps, ResearchState
|
||||
|
||||
async with HaikuRAG(path_to_db) as client:
|
||||
graph = build_research_graph(config=Config)
|
||||
question = "What are the main drivers and trends of global temperature anomalies since 1990?"
|
||||
context = ResearchContext(original_question=question)
|
||||
context = ResearchContext(original_question="What are the main features?")
|
||||
state = ResearchState.from_config(context=context, config=Config)
|
||||
deps = ResearchDeps(client=client)
|
||||
|
||||
|
|
@ -287,21 +174,25 @@ async with HaikuRAG(path_to_db) as client:
|
|||
if event["type"] == "STEP_STARTED":
|
||||
print(f"Starting step: {event['stepName']}")
|
||||
elif event["type"] == "ACTIVITY_SNAPSHOT":
|
||||
# Activity events include structured data alongside messages
|
||||
content = event['content']
|
||||
content = event["content"]
|
||||
print(f" {content['message']}")
|
||||
|
||||
# Different activity types have different structured fields
|
||||
if 'confidence' in content:
|
||||
if "confidence" in content:
|
||||
print(f" Confidence: {content['confidence']:.0%}")
|
||||
if 'sub_questions' in content:
|
||||
for q in content['sub_questions']:
|
||||
print(f" - {q}")
|
||||
if 'insights' in content:
|
||||
print(f" New insights: {len(content['insights'])}")
|
||||
elif event["type"] == "RUN_FINISHED":
|
||||
print("\nResearch complete!\n")
|
||||
result = event["result"]
|
||||
print(result["title"])
|
||||
print(result["executive_summary"])
|
||||
report = event["result"]
|
||||
print(report["executive_summary"])
|
||||
```
|
||||
|
||||
### Filtering Documents
|
||||
|
||||
Restrict searches to specific documents via the `search_filter` parameter:
|
||||
|
||||
```python
|
||||
# Set filter before running the graph
|
||||
state = ResearchState.from_config(context=context, config=Config)
|
||||
state.search_filter = "id IN ('doc-123', 'doc-456')"
|
||||
|
||||
report = await graph.run(state=state, deps=deps)
|
||||
```
|
||||
|
||||
The filter applies to all search operations in the graph. See [Filtering Search Results](python.md#filtering-search-results) for available filter columns and syntax.
|
||||
|
|
|
|||
|
|
@ -239,8 +239,7 @@ data: {"type":"RUN_FINISHED","threadId":"abc123","runId":"xyz789","result":{"tit
|
|||
- Additional structured fields depending on the activity type:
|
||||
- **Planning**: `sub_questions` (list of strings)
|
||||
- **Searching**: `query` (string), `confidence` (float, on completion), `error` (string, on failure)
|
||||
- **Analyzing**: `insights` (list of insight objects), `gaps` (list of gap objects), `resolved_gaps` (list of strings)
|
||||
- **Evaluating**: `confidence` (float), `is_sufficient` (boolean) for research; `is_sufficient` (boolean), `iterations` (int) for deep QA
|
||||
- **Evaluating**: `confidence` (float), `is_sufficient` (boolean), `new_questions` (list of strings)
|
||||
|
||||
The `message` field is always present for simple rendering, while structured fields enable richer UI features like displaying lists, charts, and detailed status information.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue