This commit is contained in:
Yiorgis Gozadinos 2026-01-30 22:43:56 +02:00
parent 57d5b1bde5
commit 42f2d0fd0e
No known key found for this signature in database
3 changed files with 24 additions and 26 deletions

View file

@ -1,6 +1,14 @@
# Changelog # Changelog
## [Unreleased] ## [Unreleased]
### Changed
- **Iterative Research Planning**: Research graph now uses an iterative feedback loop instead of batch question processing
- Planner proposes ONE question at a time, sees the answer, then decides whether to continue
- Removes `gather_context` tool — planner proposes questions directly
- Simpler flow: `plan_next``search_one` → loop back until complete → `synthesize`
- Consolidated `build_conversational_graph()` into `build_research_graph(output_mode="conversational")`
## [0.27.2] - 2026-01-29 ## [0.27.2] - 2026-01-29
### Added ### Added

View file

@ -163,44 +163,39 @@ Frontend clients should extract state from under this key. See the [Web Applicat
## Research Graph ## 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. The research workflow is implemented as a typed pydantic-graph. It uses an iterative feedback loop where the planner proposes one question at a time, sees the answer, then decides whether to continue or synthesize.
```mermaid ```mermaid
--- ---
title: Research graph title: Research graph
--- ---
stateDiagram-v2 stateDiagram-v2
[*] --> plan [*] --> plan_next
plan --> get_batch plan_next --> search_one: Has next question
get_batch --> search_one: Has questions (map) plan_next --> synthesize: Complete or max iterations
get_batch --> synthesize: No questions search_one --> plan_next
search_one --> collect_answers
collect_answers --> decide
decide --> get_batch: Continue research
decide --> synthesize: Done researching
synthesize --> [*] synthesize --> [*]
``` ```
**Key nodes:** **Key nodes:**
- **plan**: Builds up to 3 standalone sub-questions (uses an internal presearch tool) - **plan_next**: Evaluates gathered evidence and either proposes the next question to investigate or marks research as complete
- **get_batch**: Retrieves remaining sub-questions for the current iteration - **search_one**: Answers a single question using the knowledge base
- **search_one**: Answers a single sub-question using the KB (mapped in parallel)
- **collect_answers**: Aggregates search results from parallel executions
- **decide**: Evaluates confidence and determines whether to continue or synthesize
- **synthesize**: Generates a final structured research report - **synthesize**: Generates a final structured research report
**Primary models:** **Primary models:**
- `SearchAnswer` — one per sub-question (query, answer, confidence, citations) - `IterativePlanResult` — planning decision (is_complete, next_question, reasoning)
- `EvaluationResult` — confidence score, new questions, sufficiency assessment - `SearchAnswer` — answer to a single question (query, answer, confidence, citations)
- `ResearchReport` — final report (title, executive summary, findings, conclusions, …) - `ResearchReport` — final report (title, executive summary, findings, conclusions, …)
- `ConversationalAnswer` — alternative output for chat integration (answer, citations, confidence)
**Parallel execution:** **Iterative flow:**
- The `search_one` node is mapped over all questions in a batch - Each iteration: planner evaluates context → proposes one question → search answers it → loop back
- Parallelism is controlled via `max_concurrency` - Planner can decompose complex questions (e.g., "benefits and drawbacks" → start with "benefits")
- Decision nodes process results after each batch completes - Session context is used to resolve ambiguous references and inform planning
- Loop terminates when planner marks `is_complete=True` or `max_iterations` is reached
### CLI Usage ### CLI Usage

View file

@ -308,12 +308,7 @@ async def run_qa_benchmark(
async def answer_question(question: str) -> str: async def answer_question(question: str) -> str:
context = ResearchContext(original_question=question) context = ResearchContext(original_question=question)
state = ResearchState.from_config( state = ResearchState.from_config(context=context, config=config)
context=context,
config=config,
max_iterations=2,
confidence_threshold=0.0,
)
deps = ResearchDeps(client=rag) deps = ResearchDeps(client=rag)
report = await graph.run(state=state, deps=deps) report = await graph.run(state=state, deps=deps)
return report.executive_summary if report else "" return report.executive_summary if report else ""