Skip to content

Commit eb89054

Browse files
committed
feat(eval_optimize_loop): add CLI entry point and README
1 parent f8bdacb commit eb89054

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Eval + Optimize Closed Loop
2+
3+
A reproducible Evaluation + Optimization pipeline that builds a closed loop of "baseline evaluation → failure attribution → prompt optimization → candidate validation → acceptance gating → audit reporting".
4+
5+
## Quickstart (trace mode, no API keys)
6+
7+
```bash
8+
source .venv/bin/activate
9+
python run_pipeline.py
10+
```
11+
12+
This runs against the 6 sample cases (3 train, 3 val) using pre-recorded traces. No API keys required.
13+
14+
## Pipeline stages
15+
16+
1. **Baseline Evaluation** — Runs AgentEvaluator on train and val evalsets against the baseline prompt, recording per-case metrics, pass/fail status, and key trajectories.
17+
2. **Failure Attribution** — Clusters failed cases by type: final_response_mismatch, format_violation, etc. Each failed case gets at least one attributed category.
18+
3. **Optimization** — In live mode, delegates to AgentOptimizer (GEPA reflective optimization). In trace mode, uses a pre-cooked optimized prompt.
19+
4. **Candidate Validation** — Re-evaluates the candidate prompt on both train and val sets, producing per-case results for delta comparison.
20+
5. **Delta Analysis** — Compares baseline vs candidate per case: newly passing, newly failing, per-metric score deltas.
21+
6. **Acceptance Gate** — Configurable rules: min_improvement, allow_new_fails, protected_case_ids, max_cost_usd, max_duration_seconds. Detects overfitting (train improves but val degrades).
22+
7. **Audit Reports** — Generates optimization_report.json (machine-readable) and optimization_report.md (human-readable).
23+
24+
## Configuration
25+
26+
See `pipeline.json` for trace mode or create your own. Key sections:
27+
28+
- `mode`: `"trace"` (no API keys) or `"live"` (requires call_agent)
29+
- `evaluate`: Metric definitions and thresholds
30+
- `gate`: Acceptance rules
31+
32+
## Sample data
33+
34+
The 6 sample cases are designed to demonstrate three scenarios:
35+
- **Optimizable**: Case fails with baseline, passes with optimized prompt
36+
- **No improvement**: Case fails with both prompts
37+
- **Regression**: Case passes with baseline, fails with optimized prompt
38+
39+
With `allow_new_fails: false`, the gate correctly REJECTS when the candidate introduces new failures (anti-overfitting protection).
40+
41+
## Live mode
42+
43+
To use live mode with your own agent:
44+
45+
```python
46+
from pipeline import EvalOptimizePipeline
47+
from trpc_agent_sdk.evaluation import TargetPrompt
48+
49+
target = TargetPrompt().add_path("system_prompt", "path/to/prompt.md")
50+
pipeline = EvalOptimizePipeline.from_config(
51+
"pipeline.json",
52+
call_agent=your_call_agent,
53+
target_prompt=target,
54+
)
55+
result = await pipeline.run()
56+
```
57+
58+
## Output
59+
60+
- `outputs/optimization_report.json` — Full structured result
61+
- `outputs/optimization_report.md` — Human-readable summary with verdict, pass rates, per-case delta table, failure attribution, gate results, overfitting check, and audit trail
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
from __future__ import annotations
3+
4+
import argparse
5+
import asyncio
6+
import sys
7+
from pathlib import Path
8+
9+
_HERE = Path(__file__).resolve().parent
10+
_REPO_ROOT = _HERE.parents[2]
11+
if str(_REPO_ROOT) not in sys.path:
12+
sys.path.insert(0, str(_REPO_ROOT))
13+
14+
from pipeline import EvalOptimizePipeline # noqa: E402
15+
16+
17+
async def main() -> None:
18+
parser = argparse.ArgumentParser(
19+
description="Eval + Optimize closed-loop pipeline",
20+
)
21+
parser.add_argument(
22+
"--pipeline-config",
23+
type=str,
24+
default=str(_HERE / "pipeline.json"),
25+
help="Path to pipeline.json (default: pipeline.json in same directory)",
26+
)
27+
args = parser.parse_args()
28+
29+
pipeline = EvalOptimizePipeline.from_config(args.pipeline_config)
30+
result = await pipeline.run()
31+
32+
output_dir = Path(pipeline._config.output_dir).resolve()
33+
print(f"\nPipeline complete: {result.gate_decision}")
34+
print(f" Duration: {result.duration_seconds:.2f}s")
35+
print(f" Reports: {output_dir}/")
36+
print(f" optimization_report.json")
37+
print(f" optimization_report.md")
38+
39+
if result.gate_decision == "REJECT":
40+
sys.exit(1)
41+
42+
43+
if __name__ == "__main__":
44+
asyncio.run(main())

0 commit comments

Comments
 (0)