Five interrelated cleanups:
1. Lead -> Phenomenon provenance
- Phenomenon.from_lead_id field on the dataclass
- BaseAgent.run(lead_id=...) writes self._current_lead_id
- _add_phenomenon auto-injects from agent state (LLM unaware)
- Orchestrator dispatch passes lead.id; Phase 1/2-auto/4/5 stay None
- Merge path preserves the first non-None lead_id on collision
2. Unified Phenomenon <-> Hypothesis link path
- HypothesisAgent only adds hypotheses, never links
- link_phenomenon_to_hypothesis tool + executor removed
- All links go through Orchestrator._judge_new_phenomena
- Phase 2 unconditionally judges after hypothesis generation
- Gap Analysis judges after each dispatch round
(Three previously-missing judge calls now in place.)
3. SSOT in agent subclasses
- Remove RoleTemplate dataclass, ROLE_TEMPLATES dict,
_instantiate_from_template method
- Each agent subclass owns name, role, and tool list
- agent_factory.py shrinks from 299 to 153 lines
- All 7 agents now route through _AGENT_CLASSES (filesystem,
registry, communication, network, timeline were previously dead
subclasses overridden by templates)
4. Configurable edge weights
- HYPOTHESIS_EDGE_WEIGHTS -> _DEFAULT_EDGE_WEIGHTS (private default)
- EvidenceGraph(edge_weights=...) override via config.yaml
- hypothesis_edge_weights section in config.yaml (commented example)
- main.py and regenerate_report.py read and pass through
5. regenerate_report.py auto-picks the latest run/*/graph_state.json
when no CLI arg is given (was a hardcoded date path)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 lines
2.1 KiB
Python
61 lines
2.1 KiB
Python
"""Hypothesis Agent — generates investigative hypotheses from phenomena.
|
|
|
|
Generates hypotheses only. Phenomenon→Hypothesis linking is handled centrally
|
|
by Orchestrator._judge_new_phenomena, so all link logic lives in one place.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from base_agent import BaseAgent
|
|
from evidence_graph import EvidenceGraph
|
|
from llm_client import LLMClient
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class HypothesisAgent(BaseAgent):
|
|
name = "hypothesis"
|
|
role = (
|
|
"Hypothesis analyst. You review all phenomena discovered so far "
|
|
"and formulate investigative hypotheses about what happened on this system. "
|
|
"Your ultimate goal: build the most complete picture of events that occurred."
|
|
)
|
|
|
|
def __init__(self, llm: LLMClient, graph: EvidenceGraph) -> None:
|
|
super().__init__(llm, graph)
|
|
self._register_hypothesis_tools()
|
|
|
|
def _register_hypothesis_tools(self) -> None:
|
|
self.register_tool(
|
|
name="add_hypothesis",
|
|
description=(
|
|
"Create a new investigative hypothesis about what happened on the system. "
|
|
"Each hypothesis should be a specific, testable claim."
|
|
),
|
|
input_schema={
|
|
"type": "object",
|
|
"properties": {
|
|
"title": {
|
|
"type": "string",
|
|
"description": "Short title for the hypothesis.",
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"description": "Detailed description of what this hypothesis claims.",
|
|
},
|
|
},
|
|
"required": ["title", "description"],
|
|
},
|
|
executor=self._add_hypothesis,
|
|
)
|
|
|
|
async def _add_hypothesis(self, title: str, description: str) -> str:
|
|
hid = await self.graph.add_hypothesis(
|
|
title=title,
|
|
description=description,
|
|
created_by=self.name,
|
|
)
|
|
return f"Hypothesis created: {hid} — {title} (confidence: 0.50)"
|