|
| 1 | +# Tool Integration & Harness Evolution Guide |
| 2 | + |
| 3 | +This guide covers the tool-calling architecture integrated into the agent graph and how to enable harness evolution features. |
| 4 | + |
| 5 | +## Architecture Overview |
| 6 | + |
| 7 | +The agent graph now includes **tool orchestration** for: |
| 8 | +1. **Market context gathering** — regime signals, web search |
| 9 | +2. **Quality assessment** — eval benchmark scoring |
| 10 | +3. **Falsifiable claim tracking** — proposal prediction accuracy |
| 11 | + |
| 12 | +``` |
| 13 | +analyze ──► hypothesize (with tools) ──► backtest ──► reflect (with claim scoring) ──► store |
| 14 | + ▲ │ |
| 15 | + └──────────────── (retry if needed) ◄──────────┘ |
| 16 | +``` |
| 17 | + |
| 18 | +## Setup |
| 19 | + |
| 20 | +### 1. Install Dependencies |
| 21 | + |
| 22 | +The tool system requires Claude SDK and Tavily for web search (optional): |
| 23 | + |
| 24 | +```bash |
| 25 | +pip install "anthropic>=0.30" "tavily-python>=0.3" |
| 26 | +``` |
| 27 | + |
| 28 | +Or install the project with LLM extras: |
| 29 | +```bash |
| 30 | +pip install -e ".[llm]" |
| 31 | +``` |
| 32 | + |
| 33 | +### 2. Configure API Keys (`.env`) |
| 34 | + |
| 35 | +Copy `.env.example` to `.env` and set your API keys: |
| 36 | + |
| 37 | +```bash |
| 38 | +# Required for tool orchestrator (Claude tool-use) |
| 39 | +ANTHROPIC_API_KEY=your_key_here |
| 40 | + |
| 41 | +# Optional for web search in tools |
| 42 | +TAVILY_API_KEY=your_key_here |
| 43 | + |
| 44 | +# Existing keys (Gemini, FRED, etc.) |
| 45 | +GOOGLE_API_KEY=your_key_here |
| 46 | +FRED_API_KEY=your_key_here |
| 47 | +``` |
| 48 | + |
| 49 | +**⚠️ Important:** `.env` is in `.gitignore`. Never commit API keys. |
| 50 | + |
| 51 | +### 3. Enable Tools in Agent |
| 52 | + |
| 53 | +The `hypothesize_node` in `src/agent/agent_graph.py` now: |
| 54 | +1. **Tries tool orchestration first** — if Claude/Tavily available |
| 55 | +2. **Falls back to ProposalGenerator** — if tools fail or APIs missing |
| 56 | +3. **Scores claims in reflect** — tracks proposal accuracy |
| 57 | + |
| 58 | +No code changes needed to activate — tools are tried automatically. |
| 59 | + |
| 60 | +## How It Works |
| 61 | + |
| 62 | +### Tool Orchestrator Loop |
| 63 | + |
| 64 | +When `hypothesize_node` is called: |
| 65 | + |
| 66 | +```python |
| 67 | +# 1. Build tool registry |
| 68 | +registry = get_default_registry() |
| 69 | + |
| 70 | +# 2. Create orchestrator |
| 71 | +orchestrator = ToolOrchestrator(registry) |
| 72 | + |
| 73 | +# 3. Run Claude with tools |
| 74 | +result = orchestrator.run_tool_loop( |
| 75 | + user_prompt="Generate 5 momentum proposals...", |
| 76 | + regime_context=context, |
| 77 | + strategy_type="momentum", |
| 78 | + max_turns=2, |
| 79 | +) |
| 80 | + |
| 81 | +# 4. Claude can call tools like: |
| 82 | +# - get_regime_context() → retrieve stored regime signals |
| 83 | +# - search_market_sentiment() → web search via Tavily |
| 84 | +# - extract_parameter_recommendations() → access parameter grid |
| 85 | +# - run_benchmark_to_assess_quality() → eval harness quality |
| 86 | + |
| 87 | +# 5. Tool results fed back to Claude |
| 88 | +# 6. Claude reasons and generates proposals (as JSON) |
| 89 | +# 7. Proposals parsed into Proposal objects (TODO) |
| 90 | +``` |
| 91 | + |
| 92 | +### Available Tools |
| 93 | + |
| 94 | +| Tool | Purpose | Requires | |
| 95 | +|------|---------|----------| |
| 96 | +| `get_regime_context` | Retrieve stored market context | StrategyMemory | |
| 97 | +| `search_market_sentiment` | Web search market news, VIX | Tavily API | |
| 98 | +| `search_strategy_research` | Web search strategy ideas | Tavily API | |
| 99 | +| `extract_parameter_recommendations` | Access parameter grid | Config grid | |
| 100 | +| `run_benchmark_to_assess_quality` | Quality assessment eval | Recent backtest results | |
| 101 | + |
| 102 | +### Falsifiable Claims |
| 103 | + |
| 104 | +When a proposal is generated with a claim (e.g., "This window length will improve Sharpe by 15%"): |
| 105 | + |
| 106 | +1. **Proposal generation** — claim is recorded in `Proposal.reasoning` |
| 107 | +2. **Reflect node** — `_score_falsifiable_claims()` compares predicted vs. actual |
| 108 | +3. **Memory storage** — claim accuracy tracked in `StrategyMemory` |
| 109 | +4. **Harness eval** — eval suite scores "% of claims materialized" |
| 110 | + |
| 111 | +This creates a feedback loop where the harness learns which proposal strategies work. |
| 112 | + |
| 113 | +## Extending the Tool System |
| 114 | + |
| 115 | +### Add a New Tool |
| 116 | + |
| 117 | +1. Implement the callable function: |
| 118 | + |
| 119 | +```python |
| 120 | +# src/agent/tools/registry.py |
| 121 | + |
| 122 | +def _my_new_tool(param1: str) -> Dict[str, Any]: |
| 123 | + """Your tool implementation.""" |
| 124 | + return {"result": "..."} |
| 125 | +``` |
| 126 | + |
| 127 | +2. Register it in `get_default_registry()`: |
| 128 | + |
| 129 | +```python |
| 130 | +registry.register( |
| 131 | + Tool( |
| 132 | + name="my_new_tool", |
| 133 | + description="What it does", |
| 134 | + input_schema={ |
| 135 | + "properties": { |
| 136 | + "param1": {"type": "string"}, |
| 137 | + }, |
| 138 | + "required": ["param1"], |
| 139 | + }, |
| 140 | + callable_fn=_my_new_tool, |
| 141 | + category="custom", # For organization |
| 142 | + ) |
| 143 | +) |
| 144 | +``` |
| 145 | + |
| 146 | +3. The tool is now available in Claude tool-use loops and can be called by agents. |
| 147 | + |
| 148 | +### Modify Tool Behavior |
| 149 | + |
| 150 | +Tools and their schemas live in `src/agent/tools/`: |
| 151 | + |
| 152 | +- **Tool definitions** — `registry.py` (callable + schema) |
| 153 | +- **Web search** — `_search_market_sentiment`, `_search_strategy_research` |
| 154 | +- **Evaluation** — `evals.py` (quality assessment) |
| 155 | +- **Orchestration** — `orchestrator.py` (Claude loop) |
| 156 | + |
| 157 | +Change any of these and the agent's next run uses the new version. |
| 158 | + |
| 159 | +## Evaluation & Harness Quality |
| 160 | + |
| 161 | +### Run Quality Assessment |
| 162 | + |
| 163 | +Agents can call `run_benchmark_to_assess_quality` to self-evaluate: |
| 164 | + |
| 165 | +```python |
| 166 | +result = registry.execute_tool( |
| 167 | + "run_benchmark_to_assess_quality", |
| 168 | + {"strategy_type": "momentum", "regime": "Bull"}, |
| 169 | +) |
| 170 | + |
| 171 | +# Returns: |
| 172 | +{ |
| 173 | + "overall_score": 0.75, |
| 174 | + "passed": True, |
| 175 | + "metrics": { |
| 176 | + "oos_sharpe": 0.52, |
| 177 | + "stability": 0.68, |
| 178 | + "generalization_gap": 0.12, |
| 179 | + "tool_accuracy": 0.80, |
| 180 | + }, |
| 181 | + "thresholds": {...}, |
| 182 | + "recommendation": "Harness performing well. Consider scaling." |
| 183 | +} |
| 184 | +``` |
| 185 | + |
| 186 | +### Metrics Explained |
| 187 | + |
| 188 | +- **OOS Sharpe** — Out-of-sample Sharpe ratio (primary metric) |
| 189 | +- **Stability** — Consistency across regimes (1 - normalized std dev) |
| 190 | +- **Generalization Gap** — Train Sharpe − held-out Sharpe (lower is better) |
| 191 | +- **Tool Accuracy** — % of falsifiable claims that materialized |
| 192 | + |
| 193 | +## Walk-Forward Validation |
| 194 | + |
| 195 | +To prevent overfitting, partition price history into three windows: |
| 196 | + |
| 197 | +```python |
| 198 | +# Train window: [t0, t1) |
| 199 | +# - Agent evolves harness |
| 200 | +# - Tests proposals on held-in backtest data |
| 201 | + |
| 202 | +# Validation window: [t1, t2) |
| 203 | +# - Accept/reject decisions based on OOS Sharpe |
| 204 | +# - Check falsifiable claims |
| 205 | + |
| 206 | +# Held-out test window: [t2, t3) |
| 207 | +# - Touched once, at epoch end |
| 208 | +# - True generalization measurement |
| 209 | +# - Computes generalization gap (train improvement − test improvement) |
| 210 | +``` |
| 211 | + |
| 212 | +Implementation in `experiments/walk_forward_context.py` and `walk_forward_context_with_costs.py`. |
| 213 | + |
| 214 | +## Logging & Debugging |
| 215 | + |
| 216 | +Enable DEBUG logging to see tool calls and claims scoring: |
| 217 | + |
| 218 | +```python |
| 219 | +import logging |
| 220 | +logging.basicConfig(level=logging.DEBUG) |
| 221 | +``` |
| 222 | + |
| 223 | +Output includes: |
| 224 | +- Tool invocations and results |
| 225 | +- Proposal generation methods |
| 226 | +- Falsifiable claim scoring |
| 227 | +- Harness evolution decisions |
| 228 | + |
| 229 | +## Troubleshooting |
| 230 | + |
| 231 | +### Tools Not Found |
| 232 | + |
| 233 | +If you see `Tool <name> not found`, check: |
| 234 | +- Tool is registered in `get_default_registry()` |
| 235 | +- No typos in tool name |
| 236 | + |
| 237 | +### Missing API Keys |
| 238 | + |
| 239 | +- **Tavily** — Web search tools return empty results if `TAVILY_API_KEY` not set |
| 240 | +- **Anthropic** — Tool orchestrator falls back to ProposalGenerator if `ANTHROPIC_API_KEY` not set |
| 241 | + |
| 242 | +Both degrade gracefully; harness works without them. |
| 243 | + |
| 244 | +### Falsifiable Claims Not Scoring |
| 245 | + |
| 246 | +Check: |
| 247 | +- `Proposal.reasoning` is populated (has the claim text) |
| 248 | +- `reflect_node` is called after backtest |
| 249 | +- `_score_falsifiable_claims()` is logging debug output |
| 250 | + |
| 251 | +## Next Steps |
| 252 | + |
| 253 | +1. **Parse Claude proposals** — Complete `_hypothesize_with_tools()` to extract proposals from Claude JSON |
| 254 | +2. **Evaluate on held-out data** — Run walk-forward harness with three-way split |
| 255 | +3. **Track generalization gap** — Measure train vs. held-out improvement |
| 256 | +4. **Evolve the grid** — Learn parameter space from results |
| 257 | +5. **Multi-agent swarm** — Add specialist agents (critic, regime analyst, etc.) |
| 258 | + |
| 259 | +## References |
| 260 | + |
| 261 | +- `src/agent/tools/README.md` — Tool system architecture |
| 262 | +- `src/agent/tools/example_integration.py` — Integration examples |
| 263 | +- `DESIGN.md` — Agent graph and harness architecture |
| 264 | +- `docs/PAPER_DRAFT.md` — Research methodology |
| 265 | +- Research: [Harness Engineering for Self-Improvement](https://lilianweng.github.io/posts/2026-07-04-harness/) |
0 commit comments