Joseph Pollack
implements documentation improvements
d45d242
# Agents Architecture
DeepCritical uses Pydantic AI agents for all AI-powered operations. All agents follow a consistent pattern and use structured output types.
## Agent Pattern
### Pydantic AI Agents
Pydantic AI agents use the `Agent` class with the following structure:
- **System Prompt**: Module-level constant with date injection
- **Agent Class**: `__init__(model: Any | None = None)`
- **Main Method**: Async method (e.g., `async def evaluate()`, `async def write_report()`)
- **Factory Function**: `def create_agent_name(model: Any | None = None, oauth_token: str | None = None) -> AgentName`
**Note**: Factory functions accept an optional `oauth_token` parameter for HuggingFace authentication, which takes priority over environment variables.
## Model Initialization
Agents use `get_model()` from `src/agent_factory/judges.py` if no model is provided. This supports:
- OpenAI models
- Anthropic models
- HuggingFace Inference API models
The model selection is based on the configured `LLM_PROVIDER` in settings.
## Error Handling
Agents return fallback values on failure rather than raising exceptions:
- `KnowledgeGapOutput(research_complete=False, outstanding_gaps=[...])`
- Empty strings for text outputs
- Default structured outputs
All errors are logged with context using structlog.
## Input Validation
All agents validate inputs:
- Check that queries/inputs are not empty
- Truncate very long inputs with warnings
- Handle None values gracefully
## Output Types
Agents use structured output types from `src/utils/models.py`:
- `KnowledgeGapOutput`: Research completeness evaluation
- `AgentSelectionPlan`: Tool selection plan
- `ReportDraft`: Long-form report structure
- `ParsedQuery`: Query parsing and mode detection
For text output (writer agents), agents return `str` directly.
## Agent Types
### Knowledge Gap Agent
**File**: `src/agents/knowledge_gap.py`
**Purpose**: Evaluates research state and identifies knowledge gaps.
**Output**: `KnowledgeGapOutput` with:
- `research_complete`: Boolean indicating if research is complete
- `outstanding_gaps`: List of remaining knowledge gaps
**Methods**:
- `async def evaluate(query, background_context, conversation_history, iteration, time_elapsed_minutes, max_time_minutes) -> KnowledgeGapOutput`
### Tool Selector Agent
**File**: `src/agents/tool_selector.py`
**Purpose**: Selects appropriate tools for addressing knowledge gaps.
**Output**: `AgentSelectionPlan` with list of `AgentTask` objects.
**Available Agents**:
- `WebSearchAgent`: General web search for fresh information
- `SiteCrawlerAgent`: Research specific entities/companies
- `RAGAgent`: Semantic search within collected evidence
### Writer Agent
**File**: `src/agents/writer.py`
**Purpose**: Generates final reports from research findings.
**Output**: Markdown string with numbered citations.
**Methods**:
- `async def write_report(query, findings, output_length, output_instructions) -> str`
**Features**:
- Validates inputs
- Truncates very long findings (max 50000 chars) with warning
- Retry logic for transient failures (3 retries)
- Citation validation before returning
### Long Writer Agent
**File**: `src/agents/long_writer.py`
**Purpose**: Long-form report generation with section-by-section writing.
**Input/Output**: Uses `ReportDraft` models.
**Methods**:
- `async def write_next_section(query, draft, section_title, section_content) -> LongWriterOutput`
- `async def write_report(query, report_title, report_draft) -> str`
**Features**:
- Writes sections iteratively
- Aggregates references across sections
- Reformats section headings and references
- Deduplicates and renumbers references
### Proofreader Agent
**File**: `src/agents/proofreader.py`
**Purpose**: Proofreads and polishes report drafts.
**Input**: `ReportDraft`
**Output**: Polished markdown string
**Methods**:
- `async def proofread(query, report_title, report_draft) -> str`
**Features**:
- Removes duplicate content across sections
- Adds executive summary if multiple sections
- Preserves all references and citations
- Improves flow and readability
### Thinking Agent
**File**: `src/agents/thinking.py`
**Purpose**: Generates observations from conversation history.
**Output**: Observation string
**Methods**:
- `async def generate_observations(query, background_context, conversation_history) -> str`
### Input Parser Agent
**File**: `src/agents/input_parser.py`
**Purpose**: Parses and improves user queries, detects research mode.
**Output**: `ParsedQuery` with:
- `original_query`: Original query string
- `improved_query`: Refined query string
- `research_mode`: "iterative" or "deep"
- `key_entities`: List of key entities
- `research_questions`: List of research questions
## Magentic Agents
The following agents use the `BaseAgent` pattern from `agent-framework` and are used exclusively with `MagenticOrchestrator`:
### Hypothesis Agent
**File**: `src/agents/hypothesis_agent.py`
**Purpose**: Generates mechanistic hypotheses based on evidence.
**Pattern**: `BaseAgent` from `agent-framework`
**Methods**:
- `async def run(messages, thread, **kwargs) -> AgentRunResponse`
**Features**:
- Uses internal Pydantic AI `Agent` with `HypothesisAssessment` output type
- Accesses shared `evidence_store` for evidence
- Uses embedding service for diverse evidence selection (MMR algorithm)
- Stores hypotheses in shared context
### Search Agent
**File**: `src/agents/search_agent.py`
**Purpose**: Wraps `SearchHandler` as an agent for Magentic orchestrator.
**Pattern**: `BaseAgent` from `agent-framework`
**Methods**:
- `async def run(messages, thread, **kwargs) -> AgentRunResponse`
**Features**:
- Executes searches via `SearchHandlerProtocol`
- Deduplicates evidence using embedding service
- Searches for semantically related evidence
- Updates shared evidence store
### Analysis Agent
**File**: `src/agents/analysis_agent.py`
**Purpose**: Performs statistical analysis using Modal sandbox.
**Pattern**: `BaseAgent` from `agent-framework`
**Methods**:
- `async def run(messages, thread, **kwargs) -> AgentRunResponse`
**Features**:
- Wraps `StatisticalAnalyzer` service
- Analyzes evidence and hypotheses
- Returns verdict (SUPPORTED/REFUTED/INCONCLUSIVE)
- Stores analysis results in shared context
### Report Agent (Magentic)
**File**: `src/agents/report_agent.py`
**Purpose**: Generates structured scientific reports from evidence and hypotheses.
**Pattern**: `BaseAgent` from `agent-framework`
**Methods**:
- `async def run(messages, thread, **kwargs) -> AgentRunResponse`
**Features**:
- Uses internal Pydantic AI `Agent` with `ResearchReport` output type
- Accesses shared evidence store and hypotheses
- Validates citations before returning
- Formats report as markdown
### Judge Agent
**File**: `src/agents/judge_agent.py`
**Purpose**: Evaluates evidence quality and determines if sufficient for synthesis.
**Pattern**: `BaseAgent` from `agent-framework`
**Methods**:
- `async def run(messages, thread, **kwargs) -> AgentRunResponse`
- `async def run_stream(messages, thread, **kwargs) -> AsyncIterable[AgentRunResponseUpdate]`
**Features**:
- Wraps `JudgeHandlerProtocol`
- Accesses shared evidence store
- Returns `JudgeAssessment` with sufficient flag, confidence, and recommendation
## Agent Patterns
DeepCritical uses two distinct agent patterns:
### 1. Pydantic AI Agents (Traditional Pattern)
These agents use the Pydantic AI `Agent` class directly and are used in iterative and deep research flows:
- **Pattern**: `Agent(model, output_type, system_prompt)`
- **Initialization**: `__init__(model: Any | None = None)`
- **Methods**: Agent-specific async methods (e.g., `async def evaluate()`, `async def write_report()`)
- **Examples**: `KnowledgeGapAgent`, `ToolSelectorAgent`, `WriterAgent`, `LongWriterAgent`, `ProofreaderAgent`, `ThinkingAgent`, `InputParserAgent`
### 2. Magentic Agents (Agent-Framework Pattern)
These agents use the `BaseAgent` class from `agent-framework` and are used in Magentic orchestrator:
- **Pattern**: `BaseAgent` from `agent-framework` with `async def run()` method
- **Initialization**: `__init__(evidence_store, embedding_service, ...)`
- **Methods**: `async def run(messages, thread, **kwargs) -> AgentRunResponse`
- **Examples**: `HypothesisAgent`, `SearchAgent`, `AnalysisAgent`, `ReportAgent`, `JudgeAgent`
**Note**: Magentic agents are used exclusively with the `MagenticOrchestrator` and follow the agent-framework protocol for multi-agent coordination.
## Factory Functions
All agents have factory functions in `src/agent_factory/agents.py`:
<!--codeinclude-->
[Factory Functions](../src/agent_factory/agents.py) start_line:79 end_line:100
<!--/codeinclude-->
Factory functions:
- Use `get_model()` if no model provided
- Accept `oauth_token` parameter for HuggingFace authentication
- Raise `ConfigurationError` if creation fails
- Log agent creation
## See Also
- [Orchestrators](orchestrators.md) - How agents are orchestrated
- [API Reference - Agents](../api/agents.md) - API documentation
- [Contributing - Code Style](../contributing/code-style.md) - Development guidelines