Delete eval/ablation_results.json, eval/baarbundle.json, eval/benchmark_results.json, eval/benchmark_v9_final.json, eval/bfcl_results.json, eval/v8_*, eval/e2e_standalone_results.json, eval/e2e_v2_fixed_results.json, eval/repro_v2.json, eval/launcher.py, eval/run_bert_eval_launcher.py, docs/model_card.md, docs/deployment_guide.md, docs/ROADMAP.md, docs/technical_blog.md, docs/conformal_report.md, docs/FINAL_COMPREHENSIVE_REPORT.md, docs/final_report_v2.md, eval_runner.py, app.py, dashboard.py, examples/end_to_end_demo.py
Browse files- app.py +0 -183
- dashboard.py +0 -176
- docs/FINAL_COMPREHENSIVE_REPORT.md +0 -277
- docs/ROADMAP.md +0 -89
- docs/conformal_report.md +0 -109
- docs/deployment_guide.md +0 -246
- docs/model_card.md +0 -112
- docs/technical_blog.md +0 -109
- eval/ablation_results.json +0 -32
- eval/benchmark_results.json +0 -54
- eval/bfcl_results.json +0 -31
- eval/e2e_standalone_results.json +0 -101
- eval/e2e_v2_fixed_results.json +0 -76
- eval/launcher.py +0 -33
- eval/repro_v2.json +0 -159
- eval_runner.py +0 -129
- examples/end_to_end_demo.py +0 -255
app.py
DELETED
|
@@ -1,183 +0,0 @@
|
|
| 1 |
-
"""Gradio Space for Agent Cost Optimizer Dashboard.
|
| 2 |
-
|
| 3 |
-
This app visualizes cost-quality frontiers from ACO benchmark runs.
|
| 4 |
-
If no benchmark data exists, it runs the benchmark on first load.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import json
|
| 8 |
-
import subprocess
|
| 9 |
-
import sys
|
| 10 |
-
from pathlib import Path
|
| 11 |
-
from typing import Dict, List, Any
|
| 12 |
-
|
| 13 |
-
import gradio as gr
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def ensure_data_exists():
|
| 17 |
-
"""Run benchmark if data doesn't exist."""
|
| 18 |
-
results_path = Path("eval_results_v2/baseline_results.json")
|
| 19 |
-
report_path = Path("eval_results_v2/report.txt")
|
| 20 |
-
|
| 21 |
-
if not results_path.exists() or not report_path.exists():
|
| 22 |
-
print("Benchmark data not found. Running benchmark...")
|
| 23 |
-
try:
|
| 24 |
-
# Run the benchmark generator
|
| 25 |
-
subprocess.run(
|
| 26 |
-
[sys.executable, "standalone_eval_v2.py", "--tasks", "2000", "--output", "eval_results_v2"],
|
| 27 |
-
capture_output=True, text=True, timeout=120
|
| 28 |
-
)
|
| 29 |
-
print("Benchmark complete.")
|
| 30 |
-
except Exception as e:
|
| 31 |
-
print(f"Benchmark failed: {e}")
|
| 32 |
-
|
| 33 |
-
return results_path, report_path
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
def load_results(path: str) -> Dict[str, Any]:
|
| 37 |
-
with open(path) as f:
|
| 38 |
-
return json.load(f)
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def parse_report(report_path: str) -> str:
|
| 42 |
-
with open(report_path) as f:
|
| 43 |
-
return f.read()
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
def create_frontier_plot(results: Dict[str, Any]):
|
| 47 |
-
points = []
|
| 48 |
-
for name, data in results.items():
|
| 49 |
-
success = (data.get("num_success", 0) + data.get("num_partial", 0)) / max(data.get("num_tasks", 1), 1)
|
| 50 |
-
cost = data.get("avg_cost_success", 0)
|
| 51 |
-
points.append({"baseline": name, "success_rate": success, "cost_per_success": cost})
|
| 52 |
-
points.sort(key=lambda p: (-p["success_rate"], p["cost_per_success"]))
|
| 53 |
-
frontier = []
|
| 54 |
-
min_cost = float("inf")
|
| 55 |
-
for p in points:
|
| 56 |
-
if p["cost_per_success"] <= min_cost:
|
| 57 |
-
frontier.append(p)
|
| 58 |
-
min_cost = p["cost_per_success"]
|
| 59 |
-
return points, frontier
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
def build_dashboard():
|
| 63 |
-
results_path, report_path = ensure_data_exists()
|
| 64 |
-
|
| 65 |
-
if not results_path.exists() or not report_path.exists():
|
| 66 |
-
with gr.Blocks(title="Agent Cost Optimizer Dashboard") as demo:
|
| 67 |
-
gr.Markdown("# Agent Cost Optimizer Dashboard")
|
| 68 |
-
gr.Markdown("## Benchmark data not available")
|
| 69 |
-
gr.Markdown("Run `python standalone_eval_v2.py --tasks 2000 --output eval_results_v2` to generate data.")
|
| 70 |
-
return demo
|
| 71 |
-
|
| 72 |
-
results = load_results(str(results_path))
|
| 73 |
-
report_text = parse_report(str(report_path))
|
| 74 |
-
points, frontier = create_frontier_plot(results)
|
| 75 |
-
|
| 76 |
-
with gr.Blocks(title="Agent Cost Optimizer Dashboard") as demo:
|
| 77 |
-
gr.Markdown("# Agent Cost Optimizer - Cost-Quality Dashboard")
|
| 78 |
-
gr.Markdown("Visualize cost-quality tradeoffs across routing strategies and ablations.")
|
| 79 |
-
|
| 80 |
-
with gr.Row():
|
| 81 |
-
with gr.Column(scale=2):
|
| 82 |
-
gr.Markdown("## Cost-Quality Frontier")
|
| 83 |
-
gr.Markdown("**X-axis**: Average cost per successful task | **Y-axis**: Success rate")
|
| 84 |
-
|
| 85 |
-
scatter_data = [
|
| 86 |
-
[p["baseline"], f"{p['success_rate']:.1%}", f"${p['cost_per_success']:.4f}"]
|
| 87 |
-
for p in points
|
| 88 |
-
]
|
| 89 |
-
gr.Dataframe(
|
| 90 |
-
headers=["Baseline", "Success Rate", "Cost per Success"],
|
| 91 |
-
value=scatter_data,
|
| 92 |
-
label="All Baselines",
|
| 93 |
-
)
|
| 94 |
-
|
| 95 |
-
frontier_data = [
|
| 96 |
-
[p["baseline"], f"{p['success_rate']:.1%}", f"${p['cost_per_success']:.4f}"]
|
| 97 |
-
for p in frontier
|
| 98 |
-
]
|
| 99 |
-
gr.Dataframe(
|
| 100 |
-
headers=["Baseline", "Success Rate", "Cost per Success"],
|
| 101 |
-
value=frontier_data,
|
| 102 |
-
label="Pareto Frontier",
|
| 103 |
-
)
|
| 104 |
-
|
| 105 |
-
with gr.Column(scale=1):
|
| 106 |
-
gr.Markdown("## Pareto Frontier Baselines")
|
| 107 |
-
pareto_names = [p["baseline"] for p in frontier]
|
| 108 |
-
for name in pareto_names:
|
| 109 |
-
gr.Markdown(f"- **{name}**")
|
| 110 |
-
|
| 111 |
-
with gr.Row():
|
| 112 |
-
with gr.Column():
|
| 113 |
-
gr.Markdown("## Baseline Comparison")
|
| 114 |
-
comparison_data = []
|
| 115 |
-
for name, data in results.items():
|
| 116 |
-
comparison_data.append([
|
| 117 |
-
name,
|
| 118 |
-
f"{(data.get('num_success',0)+data.get('num_partial',0))/max(data.get('num_tasks',1),1):.1%}",
|
| 119 |
-
f"${data.get('avg_cost_success',0):.4f}",
|
| 120 |
-
f"${data.get('total_cost',0):.2f}",
|
| 121 |
-
f"{data.get('cost_reduction_vs_frontier',0):.1%}",
|
| 122 |
-
f"{data.get('false_done_rate',0):.1%}",
|
| 123 |
-
f"{data.get('unsafe_cheap_miss_rate',0):.1%}",
|
| 124 |
-
f"{data.get('regression_rate',0):.1%}",
|
| 125 |
-
])
|
| 126 |
-
gr.Dataframe(
|
| 127 |
-
headers=["Baseline", "Success", "Cost/Success", "Total Cost", "Cost Reduction", "False-DONE", "Cheap Miss", "Regression"],
|
| 128 |
-
value=comparison_data,
|
| 129 |
-
)
|
| 130 |
-
|
| 131 |
-
with gr.Row():
|
| 132 |
-
with gr.Column():
|
| 133 |
-
gr.Markdown("## Per-Scenario Breakdown (Full Optimizer)")
|
| 134 |
-
full_data = results.get("full_optimizer", {})
|
| 135 |
-
scenario_stats = full_data.get("per_scenario_stats", {})
|
| 136 |
-
if scenario_stats:
|
| 137 |
-
scenario_data = []
|
| 138 |
-
for scenario, stats in scenario_stats.items():
|
| 139 |
-
count = stats.get("count", 0)
|
| 140 |
-
success = stats.get("success", 0)
|
| 141 |
-
cost = stats.get("cost", 0)
|
| 142 |
-
scenario_data.append([
|
| 143 |
-
scenario,
|
| 144 |
-
str(count),
|
| 145 |
-
f"{success/max(count,1):.1%}",
|
| 146 |
-
f"${cost:.2f}",
|
| 147 |
-
])
|
| 148 |
-
gr.Dataframe(
|
| 149 |
-
headers=["Scenario", "Count", "Success Rate", "Total Cost"],
|
| 150 |
-
value=scenario_data,
|
| 151 |
-
)
|
| 152 |
-
|
| 153 |
-
with gr.Row():
|
| 154 |
-
with gr.Column():
|
| 155 |
-
gr.Markdown("## Ablation Impact")
|
| 156 |
-
gr.Markdown("Cost impact when removing each module (vs full_optimizer)")
|
| 157 |
-
|
| 158 |
-
full_cost = results.get("full_optimizer", {}).get("total_cost", 0)
|
| 159 |
-
ablation_data = []
|
| 160 |
-
for name, data in results.items():
|
| 161 |
-
if name.startswith("no_"):
|
| 162 |
-
delta = data.get("total_cost", 0) - full_cost
|
| 163 |
-
pct = (delta / max(full_cost, 0.001)) * 100
|
| 164 |
-
ablation_data.append([name, f"${delta:.2f}", f"{pct:.1f}%"])
|
| 165 |
-
|
| 166 |
-
if ablation_data:
|
| 167 |
-
ablation_data.sort(key=lambda x: float(x[1].replace("$", "")), reverse=True)
|
| 168 |
-
gr.Dataframe(
|
| 169 |
-
headers=["Module Removed", "Cost Increase", "% Increase"],
|
| 170 |
-
value=ablation_data,
|
| 171 |
-
)
|
| 172 |
-
|
| 173 |
-
with gr.Row():
|
| 174 |
-
with gr.Column():
|
| 175 |
-
gr.Markdown("## Full Report")
|
| 176 |
-
gr.Textbox(report_text, lines=40, label="Benchmark Report", interactive=False)
|
| 177 |
-
|
| 178 |
-
return demo
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
if __name__ == "__main__":
|
| 182 |
-
demo = build_dashboard()
|
| 183 |
-
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dashboard.py
DELETED
|
@@ -1,176 +0,0 @@
|
|
| 1 |
-
"""Gradio Dashboard for Agent Cost Optimizer.
|
| 2 |
-
|
| 3 |
-
Visualizes:
|
| 4 |
-
- Cost-quality frontier (scatter plot: success rate vs avg cost)
|
| 5 |
-
- Baseline comparison bar charts
|
| 6 |
-
- Per-scenario breakdown
|
| 7 |
-
- Module ablation impact
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import json
|
| 11 |
-
import sys
|
| 12 |
-
from pathlib import Path
|
| 13 |
-
from typing import Dict, List, Any
|
| 14 |
-
|
| 15 |
-
import gradio as gr
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def load_results(path: str) -> Dict[str, Any]:
|
| 19 |
-
with open(path) as f:
|
| 20 |
-
return json.load(f)
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def parse_report(report_path: str) -> str:
|
| 24 |
-
with open(report_path) as f:
|
| 25 |
-
return f.read()
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def create_frontier_plot(results: Dict[str, Any]):
|
| 29 |
-
"""Create scatter plot data for cost-quality frontier."""
|
| 30 |
-
points = []
|
| 31 |
-
for name, data in results.items():
|
| 32 |
-
success = (data.get("num_success", 0) + data.get("num_partial", 0)) / max(data.get("num_tasks", 1), 1)
|
| 33 |
-
cost = data.get("avg_cost_success", 0)
|
| 34 |
-
points.append({"baseline": name, "success_rate": success, "cost_per_success": cost})
|
| 35 |
-
|
| 36 |
-
# Sort by success rate desc, cost asc for frontier
|
| 37 |
-
points.sort(key=lambda p: (-p["success_rate"], p["cost_per_success"]))
|
| 38 |
-
|
| 39 |
-
# Build Pareto frontier
|
| 40 |
-
frontier = []
|
| 41 |
-
min_cost = float("inf")
|
| 42 |
-
for p in points:
|
| 43 |
-
if p["cost_per_success"] <= min_cost:
|
| 44 |
-
frontier.append(p)
|
| 45 |
-
min_cost = p["cost_per_success"]
|
| 46 |
-
|
| 47 |
-
return points, frontier
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
def build_dashboard(results_path: str, report_path: str):
|
| 51 |
-
results = load_results(results_path)
|
| 52 |
-
report_text = parse_report(report_path)
|
| 53 |
-
points, frontier = create_frontier_plot(results)
|
| 54 |
-
|
| 55 |
-
with gr.Blocks(title="Agent Cost Optimizer Dashboard") as demo:
|
| 56 |
-
gr.Markdown("# Agent Cost Optimizer - Cost-Quality Dashboard")
|
| 57 |
-
gr.Markdown("Visualize cost-quality tradeoffs across routing strategies and ablations.")
|
| 58 |
-
|
| 59 |
-
with gr.Row():
|
| 60 |
-
with gr.Column(scale=2):
|
| 61 |
-
gr.Markdown("## Cost-Quality Frontier")
|
| 62 |
-
gr.Markdown("**X-axis**: Average cost per successful task | **Y-axis**: Success rate")
|
| 63 |
-
|
| 64 |
-
# Scatter plot using native Gradio components
|
| 65 |
-
scatter_data = [
|
| 66 |
-
[p["baseline"], f"{p['success_rate']:.1%}", f"${p['cost_per_success']:.4f}"]
|
| 67 |
-
for p in points
|
| 68 |
-
]
|
| 69 |
-
gr.Dataframe(
|
| 70 |
-
headers=["Baseline", "Success Rate", "Cost per Success"],
|
| 71 |
-
value=scatter_data,
|
| 72 |
-
label="All Baselines",
|
| 73 |
-
)
|
| 74 |
-
|
| 75 |
-
frontier_data = [
|
| 76 |
-
[p["baseline"], f"{p['success_rate']:.1%}", f"${p['cost_per_success']:.4f}"]
|
| 77 |
-
for p in frontier
|
| 78 |
-
]
|
| 79 |
-
gr.Dataframe(
|
| 80 |
-
headers=["Baseline", "Success Rate", "Cost per Success"],
|
| 81 |
-
value=frontier_data,
|
| 82 |
-
label="Pareto Frontier",
|
| 83 |
-
)
|
| 84 |
-
|
| 85 |
-
with gr.Column(scale=1):
|
| 86 |
-
gr.Markdown("## Pareto Frontier Baselines")
|
| 87 |
-
pareto_names = [p["baseline"] for p in frontier]
|
| 88 |
-
for name in pareto_names:
|
| 89 |
-
gr.Markdown(f"- **{name}**")
|
| 90 |
-
|
| 91 |
-
with gr.Row():
|
| 92 |
-
with gr.Column():
|
| 93 |
-
gr.Markdown("## Baseline Comparison")
|
| 94 |
-
comparison_data = []
|
| 95 |
-
for name, data in results.items():
|
| 96 |
-
comparison_data.append([
|
| 97 |
-
name,
|
| 98 |
-
f"{(data.get('num_success',0)+data.get('num_partial',0))/max(data.get('num_tasks',1),1):.1%}",
|
| 99 |
-
f"${data.get('avg_cost_success',0):.4f}",
|
| 100 |
-
f"${data.get('total_cost',0):.2f}",
|
| 101 |
-
f"{data.get('cost_reduction_vs_frontier',0):.1%}",
|
| 102 |
-
f"{data.get('false_done_rate',0):.1%}",
|
| 103 |
-
f"{data.get('unsafe_cheap_miss_rate',0):.1%}",
|
| 104 |
-
f"{data.get('regression_rate',0):.1%}",
|
| 105 |
-
])
|
| 106 |
-
gr.Dataframe(
|
| 107 |
-
headers=["Baseline", "Success", "Cost/Success", "Total Cost", "Cost Reduction", "False-DONE", "Cheap Miss", "Regression"],
|
| 108 |
-
value=comparison_data,
|
| 109 |
-
)
|
| 110 |
-
|
| 111 |
-
with gr.Row():
|
| 112 |
-
with gr.Column():
|
| 113 |
-
gr.Markdown("## Per-Scenario Breakdown (Full Optimizer)")
|
| 114 |
-
full_data = results.get("full_optimizer", {})
|
| 115 |
-
scenario_stats = full_data.get("per_scenario_stats", {})
|
| 116 |
-
if scenario_stats:
|
| 117 |
-
scenario_data = []
|
| 118 |
-
for scenario, stats in scenario_stats.items():
|
| 119 |
-
count = stats.get("count", 0)
|
| 120 |
-
success = stats.get("success", 0)
|
| 121 |
-
cost = stats.get("cost", 0)
|
| 122 |
-
scenario_data.append([
|
| 123 |
-
scenario,
|
| 124 |
-
str(count),
|
| 125 |
-
f"{success/max(count,1):.1%}",
|
| 126 |
-
f"${cost:.2f}",
|
| 127 |
-
])
|
| 128 |
-
gr.Dataframe(
|
| 129 |
-
headers=["Scenario", "Count", "Success Rate", "Total Cost"],
|
| 130 |
-
value=scenario_data,
|
| 131 |
-
)
|
| 132 |
-
|
| 133 |
-
with gr.Row():
|
| 134 |
-
with gr.Column():
|
| 135 |
-
gr.Markdown("## Ablation Impact")
|
| 136 |
-
gr.Markdown("Cost increase when removing each module (vs full_optimizer)")
|
| 137 |
-
|
| 138 |
-
full_cost = results.get("full_optimizer", {}).get("total_cost", 0)
|
| 139 |
-
ablation_data = []
|
| 140 |
-
for name, data in results.items():
|
| 141 |
-
if name.startswith("no_"):
|
| 142 |
-
delta = data.get("total_cost", 0) - full_cost
|
| 143 |
-
pct = (delta / max(full_cost, 0.001)) * 100
|
| 144 |
-
ablation_data.append([name, f"${delta:.2f}", f"{pct:.1f}%"])
|
| 145 |
-
|
| 146 |
-
if ablation_data:
|
| 147 |
-
ablation_data.sort(key=lambda x: float(x[1].replace("$", "")), reverse=True)
|
| 148 |
-
gr.Dataframe(
|
| 149 |
-
headers=["Module Removed", "Cost Increase", "% Increase"],
|
| 150 |
-
value=ablation_data,
|
| 151 |
-
)
|
| 152 |
-
|
| 153 |
-
with gr.Row():
|
| 154 |
-
with gr.Column():
|
| 155 |
-
gr.Markdown("## Full Report")
|
| 156 |
-
gr.Textbox(report_text, lines=40, label="Benchmark Report")
|
| 157 |
-
|
| 158 |
-
return demo
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
def main():
|
| 162 |
-
import argparse
|
| 163 |
-
parser = argparse.ArgumentParser()
|
| 164 |
-
parser.add_argument("--results", default="./eval_results_v2/baseline_results.json",
|
| 165 |
-
help="Path to baseline results JSON")
|
| 166 |
-
parser.add_argument("--report", default="./eval_results_v2/report.txt",
|
| 167 |
-
help="Path to report text file")
|
| 168 |
-
parser.add_argument("--port", type=int, default=7860)
|
| 169 |
-
args = parser.parse_args()
|
| 170 |
-
|
| 171 |
-
demo = build_dashboard(args.results, args.report)
|
| 172 |
-
demo.launch(server_name="0.0.0.0", server_port=args.port)
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
if __name__ == "__main__":
|
| 176 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/FINAL_COMPREHENSIVE_REPORT.md
DELETED
|
@@ -1,277 +0,0 @@
|
|
| 1 |
-
# Agent Cost Optimizer — Final Comprehensive Report
|
| 2 |
-
|
| 3 |
-
**Date:** 2026-05-08
|
| 4 |
-
**Repository:** [narcolepticchicken/agent-cost-optimizer](https://huggingface.co/narcolepticchicken/agent-cost-optimizer)
|
| 5 |
-
|
| 6 |
-
---
|
| 7 |
-
|
| 8 |
-
## 1. Overview
|
| 9 |
-
|
| 10 |
-
This report presents the complete evaluation of the Agent Cost Optimizer (ACO) model cascading router, tested across multiple approaches on SWE-bench Verified (500 coding tasks × 8 models = 4,000 execution outcomes).
|
| 11 |
-
|
| 12 |
-
### The Core Problem
|
| 13 |
-
|
| 14 |
-
Given a coding task, which model should an agent use? The cheapest model that solves the task (cheapest model that actually produces a working patch). The challenge is predicting which model will succeed without actually running it.
|
| 15 |
-
|
| 16 |
-
### Evaluation Metrics
|
| 17 |
-
|
| 18 |
-
| Metric | Definition |
|
| 19 |
-
|--------|-----------|
|
| 20 |
-
| **Success Rate** | Fraction of tasks where the routed model produces a resolved (passing) patch |
|
| 21 |
-
| **Avg Cost** | Mean cost per task in dollars (from SWE-Router execution data) |
|
| 22 |
-
| **Cost Reduction** | % savings vs always using the frontier model (claude-opus-4.7) |
|
| 23 |
-
| **Pareto Optimal** | No other policy has both higher success AND lower cost |
|
| 24 |
-
|
| 25 |
-
---
|
| 26 |
-
|
| 27 |
-
## 2. All Router Approaches Evaluated
|
| 28 |
-
|
| 29 |
-
### 2.1 Baseline Policies
|
| 30 |
-
|
| 31 |
-
| Policy | Success | AvgCost | CostRed | Description |
|
| 32 |
-
|--------|---------|---------|---------|-------------|
|
| 33 |
-
| **oracle** | 87.0% | $0.059 | 81.5% | Cheapest model that solved each task (upper bound) |
|
| 34 |
-
| **frontier** | 78.2% | $0.317 | 0.0% | Always claude-opus-4.7 (baseline) |
|
| 35 |
-
| **always_cheap** | 63.2% | $0.014 | 95.5% | Always deepseek-v4-flash (lower bound) |
|
| 36 |
-
|
| 37 |
-
### 2.2 XGBoost Keyword Routers
|
| 38 |
-
|
| 39 |
-
| Version | Features | Success | AvgCost | CostRed | Notes |
|
| 40 |
-
|---------|----------|---------|---------|---------|-------|
|
| 41 |
-
| **v10** | 27 keywords + text stats | 79.0% | $0.194 | 38.8% | Best keyword router |
|
| 42 |
-
| **v10+feedback** | v10 route + escalate on failure | 84.8% | $0.202 | **36.4%** | **Best overall: Pareto-optimal** |
|
| 43 |
-
|
| 44 |
-
### 2.3 BERT-Based Routers
|
| 45 |
-
|
| 46 |
-
| Approach | Success | AvgCost | CostRed | Notes |
|
| 47 |
-
|----------|---------|---------|---------|-------|
|
| 48 |
-
| **BERT 5-class (direct)** | 54.8% | $0.042 | 86.8% | Predicts tier 2 for 96% of tasks |
|
| 49 |
-
| **BERT 5-class+feedback** | 84.0% | $0.578 | **-82.5%** | Costs more than frontier! |
|
| 50 |
-
| **BERT binary predictor** | N/A | N/A | N/A | Predicts success 89.5% for ALL tiers |
|
| 51 |
-
| **BAAR (BERT+K+XGB)** | 76.6% | $0.188 | 40.7% | Marginal improvement over keyword-only |
|
| 52 |
-
| **BAAR+feedback** | 85.2% | $0.249 | 21.5% | Slightly better success, worse cost reduction |
|
| 53 |
-
|
| 54 |
-
---
|
| 55 |
-
|
| 56 |
-
## 3. The Pareto Frontier
|
| 57 |
-
|
| 58 |
-
```
|
| 59 |
-
Success Rate
|
| 60 |
-
87% │ ★oracle (87.0%, $0.059, -81.5%)
|
| 61 |
-
│
|
| 62 |
-
85% │ ★baar_fb (85.2%, $0.249, -21.5%)
|
| 63 |
-
│ ★v10_fb (84.8%, $0.202, -36.4%) ← PARETO-OPTIMAL
|
| 64 |
-
│
|
| 65 |
-
80% │ ★v10 (79.0%, $0.194, -38.8%)
|
| 66 |
-
│ ★frontier (78.2%, $0.317, baseline)
|
| 67 |
-
│
|
| 68 |
-
75% │ ★baar (76.6%, $0.188, -40.7%)
|
| 69 |
-
│
|
| 70 |
-
70% │
|
| 71 |
-
│
|
| 72 |
-
65% │
|
| 73 |
-
│ ★always_cheap (63.2%, $0.014, -95.5%)
|
| 74 |
-
60% │
|
| 75 |
-
└──────────────────────────────────────────
|
| 76 |
-
0.05 0.10 0.15 0.20 0.25 0.30
|
| 77 |
-
Avg Cost ($)
|
| 78 |
-
```
|
| 79 |
-
|
| 80 |
-
**Pareto-optimal policies:** oracle, v10+feedback, always_cheap
|
| 81 |
-
|
| 82 |
-
Everything else is dominated — there exists another policy with strictly better success AND lower cost.
|
| 83 |
-
|
| 84 |
-
### Dominated Policies
|
| 85 |
-
|
| 86 |
-
| Policy | Dominated By | Why |
|
| 87 |
-
|--------|-------------|-----|
|
| 88 |
-
| **frontier** | v10 | v10 is cheaper AND more successful |
|
| 89 |
-
| **baar_fb** | v10_fb | v10_fb is cheaper AND nearly as successful |
|
| 90 |
-
| **bert_feedback** | frontier | frontier is cheaper AND more successful |
|
| 91 |
-
| **baar** | v10 | v10 is cheaper AND more successful |
|
| 92 |
-
|
| 93 |
-
---
|
| 94 |
-
|
| 95 |
-
## 4. Why BERT Routing Failed
|
| 96 |
-
|
| 97 |
-
### 4.1 SPROUT → BERT 5-class
|
| 98 |
-
|
| 99 |
-
The BERT 5-class model trained on SPROUT (31K QA tasks) collapses to predicting tier 1 for 99.99% of QA tasks and cannot distinguish coding difficulty on SWE-bench:
|
| 100 |
-
|
| 101 |
-
| Metric | Value |
|
| 102 |
-
|--------|-------|
|
| 103 |
-
| Training accuracy (SPROUT) | 76.9% (epoch 2) |
|
| 104 |
-
| Tier 1 eval accuracy | 99.92% |
|
| 105 |
-
| Tier 2 eval accuracy | 1.26% |
|
| 106 |
-
| Tier 3-5 eval accuracy | 0-1.7% |
|
| 107 |
-
| On SWE-bench (direct) | Predicts tier 2 for 96% of tasks |
|
| 108 |
-
|
| 109 |
-
**Root cause:** 77.4% of SPROUT tasks are solvable by the cheapest model. BERT learns "always predict tier 1" and ignores minority classes. For SWE-bench, the fine-tuned model shifts to "always predict tier 2" — effectively random routing for coding tasks.
|
| 110 |
-
|
| 111 |
-
### 4.2 BERT Binary Success Predictor
|
| 112 |
-
|
| 113 |
-
Predicting P(success) for each tier produced probability 0.89-0.90 for ALL tiers on ALL tasks. The BERT [CLS] embedding doesn't capture task-difficulty signals that differ between coding tasks.
|
| 114 |
-
|
| 115 |
-
### 4.3 BAAR (BERT [CLS] + XGBoost)
|
| 116 |
-
|
| 117 |
-
Adding BERT embeddings as XGBoost features improved CV F1 by 1.8-4.6% across tiers but didn't change routing decisions in any meaningful way. The keyword-only v10 router achieves 42.7% cost reduction vs BAAR's 40.7% — BERT features don't add enough signal to justify the computational cost.
|
| 118 |
-
|
| 119 |
-
The `direct optimal-tier accuracy: 1.000` (perfect on training data) confirms the model is memorizing via the rich 768-dim BERT embeddings, not generalizing.
|
| 120 |
-
|
| 121 |
-
### 4.4 Fundamental Insight
|
| 122 |
-
|
| 123 |
-
**SPROUT (QA benchmark) does not transfer to SWE-bench (coding benchmark).** Task difficulty is domain-specific. A task that's easy for a cheap model on QA (e.g., "what is the capital of France?") has no correlation with coding difficulty. You cannot train a coding-agent router on QA data.
|
| 124 |
-
|
| 125 |
-
---
|
| 126 |
-
|
| 127 |
-
## 5. What Actually Works
|
| 128 |
-
|
| 129 |
-
### 5.1 v10+feedback: The Current Best
|
| 130 |
-
|
| 131 |
-
```
|
| 132 |
-
Route: XGBoost keyword features → predict cheapest viable tier
|
| 133 |
-
Execute: Run the predicted model
|
| 134 |
-
Feedback: If fails, escalate to next tier and retry
|
| 135 |
-
```
|
| 136 |
-
|
| 137 |
-
| Component | Impact |
|
| 138 |
-
|-----------|--------|
|
| 139 |
-
| Keyword features | 23 text-statistics keywords (has_fix, has_error, n_lines, etc.) |
|
| 140 |
-
| XGBoost | 5 binary classifiers, one per tier |
|
| 141 |
-
| Isotonic calibration | Probability calibration for thresholding |
|
| 142 |
-
| Feedback escalation | Try next tier on failure |
|
| 143 |
-
|
| 144 |
-
**84.8% success, 36.4% cost reduction, Pareto-optimal.**
|
| 145 |
-
|
| 146 |
-
### 5.2 Why Keywords Beat BERT
|
| 147 |
-
|
| 148 |
-
Keyword features are interpretable and directly relevant to coding task difficulty:
|
| 149 |
-
- `has_error`, `has_traceback` → likely harder (need debugging)
|
| 150 |
-
- `has_test`, `has_spec` → likely harder (has test requirements)
|
| 151 |
-
- `n_lines`, `has_file_path` → structural complexity signals
|
| 152 |
-
|
| 153 |
-
BERT [CLS] captures holistic semantics optimized for masked language modeling, not task-specific difficulty signals. The 768-dim embeddings are too rich for 500 training examples — they overfit rather than generalize.
|
| 154 |
-
|
| 155 |
-
### 5.3 Why Feedback Escalation Works
|
| 156 |
-
|
| 157 |
-
The feedback mechanism converts cheap-model failures into escalation opportunities. Even if the router only routes correctly 60% of the time, the feedback loop catches the remaining 40% by falling back to stronger models.
|
| 158 |
-
|
| 159 |
-
Cost analysis:
|
| 160 |
-
- 67.4% of tasks solvable by tier 1 ($0.01/task)
|
| 161 |
-
- For the remaining 32.6%, escalate to tier 2 ($0.05) → tier 4 ($0.30)
|
| 162 |
-
- Weighted average: 0.674 × $0.01 + 0.15 × ($0.01+$0.05) + 0.176 × ($0.01+$0.05+$0.30) = $0.202
|
| 163 |
-
- Vs frontier: $0.317 → 36.4% savings
|
| 164 |
-
|
| 165 |
-
---
|
| 166 |
-
|
| 167 |
-
## 6. SWE-Router Dataset Statistics
|
| 168 |
-
|
| 169 |
-
Key statistics from the 500 SWE-bench tasks × 8 models:
|
| 170 |
-
|
| 171 |
-
| Model | Tier | Success | Cost/Task |
|
| 172 |
-
|-------|------|---------|-----------|
|
| 173 |
-
| deepseek-v4-flash | 1 | 63.2% | $0.014 |
|
| 174 |
-
| gpt-5-nano | 1 | — | — |
|
| 175 |
-
| gpt-5-mini | 2 | 51.4% | $0.039 |
|
| 176 |
-
| deepseek-v3.2 | 2 | 75.8% | $0.052 |
|
| 177 |
-
| gemini-2.5-pro | 3 | 51.4% | $0.151 |
|
| 178 |
-
| claude-opus-4.7 | 4 | 78.2% | $0.317 |
|
| 179 |
-
| gpt-5.2 | 4 | 82.6% | $0.274 |
|
| 180 |
-
| gemini-3-pro | 5 | 72.6% | $0.434 |
|
| 181 |
-
|
| 182 |
-
**Interesting:** gpt-5.2 (tier 4) is both cheaper AND more successful than claude-opus-4.7. The frontier should be redefined as gpt-5.2 once confirmed reproducible.
|
| 183 |
-
|
| 184 |
-
**67.4% of tasks are solvable by the cheapest model.** This is the key stat — if you can identify which 67.4% correctly, you save 95% of cost with zero quality loss. If you identify half correctly, you still save ~40%.
|
| 185 |
-
|
| 186 |
-
---
|
| 187 |
-
|
| 188 |
-
## 7. The Upper Bound (Oracle)
|
| 189 |
-
|
| 190 |
-
| Metric | Value |
|
| 191 |
-
|--------|-------|
|
| 192 |
-
| Oracle success | 87.0% |
|
| 193 |
-
| Oracle cost/task | $0.059 |
|
| 194 |
-
| Cost reduction | 81.5% |
|
| 195 |
-
| % tasks solvable by cheapest model | 67.4% |
|
| 196 |
-
| % tasks needing escalation | 32.6% |
|
| 197 |
-
|
| 198 |
-
The oracle reveals that 19.6% of tasks (87.0% - 67.4%) are solvable by a non-frontier model but NOT by the cheapest — these are the "mid-difficulty" tasks where routing really matters.
|
| 199 |
-
|
| 200 |
-
**The gap between v10+feedback (84.8%) and oracle (87.0%) is 2.2 percentage points.** Closing this gap requires better tier-1 vs tier-2 discrimination — the hardest routing decision.
|
| 201 |
-
|
| 202 |
-
---
|
| 203 |
-
|
| 204 |
-
## 8. Key Architectural Decisions
|
| 205 |
-
|
| 206 |
-
### 8.1 Per-Tier Binary Classifiers (Correct)
|
| 207 |
-
Training 5 separate binary classifiers (one per tier) is the right architecture. It handles class imbalance, allows per-tier calibration, and enables threshold-based routing.
|
| 208 |
-
|
| 209 |
-
### 8.2 5-Class Classifier (Wrong)
|
| 210 |
-
A single 5-class classifier (BERT 5-class) collapses to majority class. Not recommended for this problem.
|
| 211 |
-
|
| 212 |
-
### 8.3 Binary Success Predictor (Wrong)
|
| 213 |
-
Predicting "will this model succeed?" for each model produces near-uniform probabilities. The signal is too weak.
|
| 214 |
-
|
| 215 |
-
### 8.4 Execution Feedback (Essential)
|
| 216 |
-
Without feedback escalation, any router mistake becomes a permanent failure. Feedback converts routing errors into slightly longer (but still successful) runs.
|
| 217 |
-
|
| 218 |
-
---
|
| 219 |
-
|
| 220 |
-
## 9. Model Artifacts on Hub
|
| 221 |
-
|
| 222 |
-
| Artifact | Path | Description |
|
| 223 |
-
|----------|------|-------------|
|
| 224 |
-
| BERT 5-class (fine-tuned) | `router_models/bert_5class/` | DistilBERT fine-tuned on SPROUT, 5-class |
|
| 225 |
-
| BAAR bundle | `router_models/baar_bundle.pkl` | BERT+XGBoost bundled router |
|
| 226 |
-
| v10 XGBoost | `router_models/aco_router_v10_xgb.pkl` | Keyword-only XGBoost (best performer) |
|
| 227 |
-
| BAAR eval JSON | `eval/baar_results.json` | BAAR evaluation results |
|
| 228 |
-
| Combined report | `docs/combined_final_report.md` | This file |
|
| 229 |
-
|
| 230 |
-
---
|
| 231 |
-
|
| 232 |
-
## 10. Recommendations
|
| 233 |
-
|
| 234 |
-
### For Production Use
|
| 235 |
-
✅ **v10+feedback** is the recommended approach: 84.8% success at 36.4% cost reduction.
|
| 236 |
-
|
| 237 |
-
### For Further Research
|
| 238 |
-
1. **Train on SWE-bench problem statements directly** — don't use SPROUT
|
| 239 |
-
2. **Per-step routing** — route model choice at each agent step, not once per task
|
| 240 |
-
3. **Confidence-based escalation** — use model logprobs/entropy as additional routing features
|
| 241 |
-
4. **Redefine frontier as gpt-5.2** — it's cheaper AND more successful than claude-opus-4.7 on these tasks
|
| 242 |
-
5. **Explore tier 1 vs tier 2 discrimination** — the 2.2% gap to oracle lives here
|
| 243 |
-
6. **Collect more task features** — repo complexity, file count, test count, line changes needed
|
| 244 |
-
|
| 245 |
-
### What NOT to do
|
| 246 |
-
❌ Use SPROUT to train a coding-agent router
|
| 247 |
-
❌ Use BERT classifiers directly (collapse to majority)
|
| 248 |
-
❌ Route without execution feedback (every mistake is permanent)
|
| 249 |
-
❌ Use a single 5-class classifier (imbalanced classes)
|
| 250 |
-
|
| 251 |
-
---
|
| 252 |
-
|
| 253 |
-
## 11. Complete Evaluation Table
|
| 254 |
-
|
| 255 |
-
| Policy | Success | Cost | CostRed | Pareto |
|
| 256 |
-
|--------|---------|------|---------|--------|
|
| 257 |
-
| oracle | 87.0% | $0.059 | 81.5% | ✅ |
|
| 258 |
-
| always_cheap | 63.2% | $0.014 | 95.5% | ✅ |
|
| 259 |
-
| v10+feedback | 84.8% | $0.202 | 36.4% | ✅ |
|
| 260 |
-
| baar+feedback | 85.2% | $0.249 | 21.5% | ❌ |
|
| 261 |
-
| v10 | 79.0% | $0.194 | 38.8% | ❌ |
|
| 262 |
-
| baar | 76.6% | $0.188 | 40.7% | ❌ |
|
| 263 |
-
| frontier | 78.2% | $0.317 | 0.0% | ❌ |
|
| 264 |
-
| bert+feedback | 84.0% | $0.578 | -82.5% | ❌ |
|
| 265 |
-
| bert_direct | 54.8% | $0.042 | 86.8% | ❌ |
|
| 266 |
-
|
| 267 |
-
---
|
| 268 |
-
|
| 269 |
-
## 12. Conclusion
|
| 270 |
-
|
| 271 |
-
The ACO model cascading router achieves its goal: 36.4% cost reduction at higher task success (84.8% vs 78.2% frontier). The approach is simple (keyword features + XGBoost + thresholded routing + feedback escalation), interpretable, and deployable.
|
| 272 |
-
|
| 273 |
-
BERT-based approaches provide no benefit over keyword features for this task. The domain mismatch between SPROUT (QA) and SWE-bench (coding) is the fundamental blocker. Training on domain-specific (coding) execution data is the path forward.
|
| 274 |
-
|
| 275 |
-
The execution feedback loop is the critical enabler — without it, any router error is permanent. With it, routing mistakes become slightly more expensive but successful runs.
|
| 276 |
-
|
| 277 |
-
**Bottom line: 84.8% success at 63.6% of frontier cost. That's the ACO.**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/ROADMAP.md
DELETED
|
@@ -1,89 +0,0 @@
|
|
| 1 |
-
# ACO Roadmap
|
| 2 |
-
|
| 3 |
-
## Completed (v1-v11)
|
| 4 |
-
|
| 5 |
-
- [x] Normalized trace schema
|
| 6 |
-
- [x] Synthetic trace generator (10K traces)
|
| 7 |
-
- [x] Cost telemetry collector
|
| 8 |
-
- [x] Task cost classifier
|
| 9 |
-
- [x] Model cascade router (XGBoost per-tier)
|
| 10 |
-
- [x] Context budgeter
|
| 11 |
-
- [x] Cache-aware prompt layout
|
| 12 |
-
- [x] Tool-use cost gate
|
| 13 |
-
- [x] Verifier budgeter
|
| 14 |
-
- [x] Retry/recovery optimizer
|
| 15 |
-
- [x] Meta-tool miner
|
| 16 |
-
- [x] Early termination detector
|
| 17 |
-
- [x] Execution-feedback router (entropy cascade)
|
| 18 |
-
- [x] Per-step routing
|
| 19 |
-
- [x] Real benchmark evaluation (SWE-bench, BFCL)
|
| 20 |
-
- [x] Ablation study on real data
|
| 21 |
-
- [x] Literature review
|
| 22 |
-
- [x] Deployment guide
|
| 23 |
-
- [x] Technical blog post
|
| 24 |
-
- [x] Final report
|
| 25 |
-
- [x] Model cards
|
| 26 |
-
|
| 27 |
-
## Completed This Session
|
| 28 |
-
|
| 29 |
-
- [x] Conformal calibration module (`aco/conformal.py`)
|
| 30 |
-
- [x] Cost-quality Pareto frontier report (`docs/pareto_frontier_report.md`)
|
| 31 |
-
- [x] Conformal calibration methodology report (`docs/conformal_report.md`)
|
| 32 |
-
- [x] Integration test (`tests/test_integration.py`)
|
| 33 |
-
- [x] Updated final report v2 (`docs/final_report_v2.md`)
|
| 34 |
-
- [x] BERT router evaluation on SWE-bench (`docs/bert_eval_report.md`)
|
| 35 |
-
- [x] BERT 5-class training script (`training/train_bert_5class.py`)
|
| 36 |
-
|
| 37 |
-
## In Progress
|
| 38 |
-
|
| 39 |
-
- [ ] BERT 5-class retraining (SPROUT data parsing fix needed)
|
| 40 |
-
- [ ] Gradio dashboard with real benchmark numbers
|
| 41 |
-
|
| 42 |
-
## Next Priority (CPU-friendly)
|
| 43 |
-
|
| 44 |
-
- [ ] Conformal calibration deployment (integrate into router)
|
| 45 |
-
- [ ] Cost-quality Pareto frontier visualization (plotting code)
|
| 46 |
-
- [ ] JSON schema validation for traces
|
| 47 |
-
- [ ] Unit tests for all 11 modules
|
| 48 |
-
- [ ] Integration test suite (run test_integration.py)
|
| 49 |
-
- [ ] Example notebooks
|
| 50 |
-
- [ ] Provider adapter examples (OpenAI, Anthropic, local)
|
| 51 |
-
- [ ] Config file validator
|
| 52 |
-
- [ ] CLI improvements (batch routing, cost estimation)
|
| 53 |
-
|
| 54 |
-
## Next Priority (GPU needed)
|
| 55 |
-
|
| 56 |
-
- [ ] Execution-feedback with real model logprobs
|
| 57 |
-
- [ ] Best-of-N cheap sampling with reward model
|
| 58 |
-
- [ ] Fine-tuned BERT per-step router (Option C: [CLS] features for XGBoost)
|
| 59 |
-
- [ ] Process reward model for selective verification
|
| 60 |
-
- [ ] Real agent benchmarks (SWE-bench Live, WebArena)
|
| 61 |
-
|
| 62 |
-
## Long-term
|
| 63 |
-
|
| 64 |
-
- [ ] Learned context selector (vs heuristic budgeter)
|
| 65 |
-
- [ ] Workflow mining from real traces
|
| 66 |
-
- [ ] Online learning from new traces
|
| 67 |
-
- [ ] Multi-agent cost optimization
|
| 68 |
-
- [ ] Provider-aware routing (cost/latency/availability)
|
| 69 |
-
- [ ] Budget-constrained decoding
|
| 70 |
-
- [ ] Cross-task transfer learning
|
| 71 |
-
|
| 72 |
-
## Known Limitations
|
| 73 |
-
|
| 74 |
-
- Router trained on SPROUT + SWE-Router only (need more domains)
|
| 75 |
-
- Execution feedback uses simulated logprobs (need real model outputs)
|
| 76 |
-
- No conformal guarantees on quality (hand-tuned thresholds)
|
| 77 |
-
- Per-step routing not yet integrated with v11 XGBoost
|
| 78 |
-
- Cache-aware layout not benchmarked with real providers
|
| 79 |
-
- No real agent harness integration tested end-to-end
|
| 80 |
-
- BERT router is binary classifier, not suitable for tier routing (needs 5-class retrain)
|
| 81 |
-
|
| 82 |
-
## Headroom
|
| 83 |
-
|
| 84 |
-
Oracle on SWE-bench shows 80.3% cost reduction is achievable. v11 achieves 36.9%. The remaining 43.4% comes from:
|
| 85 |
-
- Better per-step routing (~10%)
|
| 86 |
-
- Real execution feedback (~10%)
|
| 87 |
-
- Best-of-N cheap sampling (~8%)
|
| 88 |
-
- Conformal calibration (~5%)
|
| 89 |
-
- More training data from more domains (~10%)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/conformal_report.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
| 1 |
-
# Conformal Calibration of Escalation Thresholds
|
| 2 |
-
|
| 3 |
-
## Problem
|
| 4 |
-
|
| 5 |
-
The model cascade router uses a heuristic threshold: route to the cheapest tier where P(success) >= 0.65. This threshold was hand-tuned. There is no guarantee that it provides adequate coverage — we might be escalating too often (wasting cost) or not often enough (accepting too many failures).
|
| 6 |
-
|
| 7 |
-
## Solution
|
| 8 |
-
|
| 9 |
-
Conformal risk control (Angelopoulos et al., 2022, arxiv:2208.02814) provides distribution-free coverage guarantees. Applied to our escalation problem following RouteNLP (arxiv:2604.23577):
|
| 10 |
-
|
| 11 |
-
**Guarantee**: P(failure AND no escalation) ≤ α
|
| 12 |
-
|
| 13 |
-
This means: if the router decides NOT to escalate (i.e., it trusts the cheap model), the probability that the cheap model actually fails is at most α.
|
| 14 |
-
|
| 15 |
-
## Method
|
| 16 |
-
|
| 17 |
-
### 1. Nonconformity Score
|
| 18 |
-
|
| 19 |
-
For each calibration example i at tier t:
|
| 20 |
-
- Compute the calibrated P(success) from the XGBoost + isotonic model
|
| 21 |
-
- If the model failed (y_i = 0), the nonconformity score is s_i = P(success)_i
|
| 22 |
-
- If the model succeeded (y_i = 1), the nonconformity score is s_i = -∞ (always safe)
|
| 23 |
-
|
| 24 |
-
### 2. Conformal Threshold
|
| 25 |
-
|
| 26 |
-
Set the escalation threshold as the ⌈(1-α)(n+1)/n⌉-th quantile of nonconformity scores among **failed examples only** (following RouteNLP's correctly-handled subset approach).
|
| 27 |
-
|
| 28 |
-
This gives threshold λ̂_t for each tier t.
|
| 29 |
-
|
| 30 |
-
### 3. Routing Decision
|
| 31 |
-
|
| 32 |
-
```
|
| 33 |
-
if P(success at tier t) >= λ̂_t:
|
| 34 |
-
use tier t (no escalation needed)
|
| 35 |
-
else:
|
| 36 |
-
escalate to tier t+1
|
| 37 |
-
```
|
| 38 |
-
|
| 39 |
-
### 4. Coverage Verification
|
| 40 |
-
|
| 41 |
-
On a held-out test set, verify that:
|
| 42 |
-
- P(y=fail | P(success) >= λ̂_t) ≤ α for each tier t
|
| 43 |
-
- This is the violation rate — should be ≤ α
|
| 44 |
-
|
| 45 |
-
## Expected Impact
|
| 46 |
-
|
| 47 |
-
From RouteNLP's results:
|
| 48 |
-
- With 500 calibration examples per tier, violation rate is 4.2% at α=0.05
|
| 49 |
-
- With 100 examples, violation rate is 7.2%
|
| 50 |
-
- With 1000 examples, violation rate is 3.9%
|
| 51 |
-
|
| 52 |
-
Our SWE-Router dataset has 500 tasks × 8 models = 4000 total outcomes, giving us ~800 per tier. Expected violation rate: ~4%.
|
| 53 |
-
|
| 54 |
-
## Implementation
|
| 55 |
-
|
| 56 |
-
The module is in `aco/conformal.py`:
|
| 57 |
-
|
| 58 |
-
```python
|
| 59 |
-
from aco.conformal import ConformalEscalationCalibrator
|
| 60 |
-
|
| 61 |
-
# Calibrate
|
| 62 |
-
cal = ConformalEscalationCalibrator(alpha=0.05)
|
| 63 |
-
thresholds = cal.calibrate(psuccess, outcomes)
|
| 64 |
-
|
| 65 |
-
# Use in routing
|
| 66 |
-
if cal.should_escalate(tier=2, psuccess=0.62):
|
| 67 |
-
# Escalate to tier 3
|
| 68 |
-
...
|
| 69 |
-
```
|
| 70 |
-
|
| 71 |
-
## Integration with v10 Router
|
| 72 |
-
|
| 73 |
-
The conformal calibrator replaces the hardcoded 0.65 threshold in `route_v10()`:
|
| 74 |
-
|
| 75 |
-
```python
|
| 76 |
-
# Before
|
| 77 |
-
for t in range(1, 6):
|
| 78 |
-
if tier_probs[t] >= 0.65: # heuristic
|
| 79 |
-
return t, tier_probs[t], tier_probs
|
| 80 |
-
|
| 81 |
-
# After
|
| 82 |
-
for t in range(1, 6):
|
| 83 |
-
if not cal.should_escalate(t, tier_probs[t]): # conformal
|
| 84 |
-
return t, tier_probs[t], tier_probs
|
| 85 |
-
```
|
| 86 |
-
|
| 87 |
-
## Sensitivity Analysis
|
| 88 |
-
|
| 89 |
-
| α | Expected Violation Rate | Expected Escalation Rate | Cost Impact |
|
| 90 |
-
|---|------------------------|-------------------------|-------------|
|
| 91 |
-
| 0.01 | ~1% | High (conservative) | +10-15% cost |
|
| 92 |
-
| 0.05 | ~4% | Medium | Baseline |
|
| 93 |
-
| 0.10 | ~8% | Low (aggressive) | -5-10% cost |
|
| 94 |
-
| 0.20 | ~15% | Very low | -15-20% cost |
|
| 95 |
-
|
| 96 |
-
For production use, α=0.05 is recommended. For high-risk domains (legal, medical), use α=0.01.
|
| 97 |
-
|
| 98 |
-
## Caveats
|
| 99 |
-
|
| 100 |
-
1. **Exchangeability assumption**: Conformal guarantees require that calibration and test data are exchangeable. Distribution shift (new task types, new models) invalidates the guarantee.
|
| 101 |
-
2. **Sample size**: With only 500 tasks, per-tier calibration has ~100 examples per tier. More data improves calibration.
|
| 102 |
-
3. **Conditional vs marginal**: The guarantee is marginal (averaged over all inputs), not conditional (for a specific input type). Conditional coverage requires stronger assumptions.
|
| 103 |
-
|
| 104 |
-
## References
|
| 105 |
-
|
| 106 |
-
- Angelopoulos, A.N., et al. "Conformal Risk Control." NeurIPS 2022. arxiv:2208.02814
|
| 107 |
-
- RouteNLP: "Closed-Loop LLM Routing with Conformal Cascading and Distillation Co-Optimization." arxiv:2604.23577
|
| 108 |
-
- CP-Router: "An Uncertainty-Aware Router Between LLM and LRM." arxiv:2505.19970
|
| 109 |
-
- CAP: "Learning Conformal Abstention Policies for Adaptive Risk Management." arxiv:2502.06884
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/deployment_guide.md
DELETED
|
@@ -1,246 +0,0 @@
|
|
| 1 |
-
# ACO Deployment Guide
|
| 2 |
-
|
| 3 |
-
## Quick Install
|
| 4 |
-
|
| 5 |
-
```bash
|
| 6 |
-
pip install -e .
|
| 7 |
-
```
|
| 8 |
-
|
| 9 |
-
Or use directly:
|
| 10 |
-
|
| 11 |
-
```python
|
| 12 |
-
from aco.optimizer import ACOOptimizer
|
| 13 |
-
from aco.config import ACOConfig
|
| 14 |
-
```
|
| 15 |
-
|
| 16 |
-
## CLI
|
| 17 |
-
|
| 18 |
-
```bash
|
| 19 |
-
# Route a request to the optimal model
|
| 20 |
-
aco route "Fix the auth bug in production"
|
| 21 |
-
# → tier=5, model=specialist-expert, cost=$1.50
|
| 22 |
-
|
| 23 |
-
aco route "What is 2+2?"
|
| 24 |
-
# → tier=2, model=cheap-cloud-8b, cost=$0.15
|
| 25 |
-
|
| 26 |
-
# Get context budget
|
| 27 |
-
aco budget "Research transformer advances"
|
| 28 |
-
|
| 29 |
-
# Check if a tool call is worth it
|
| 30 |
-
aco gate web_search --task-type research
|
| 31 |
-
|
| 32 |
-
# Check if verification is needed
|
| 33 |
-
aco verify --risk high --confidence 0.7
|
| 34 |
-
|
| 35 |
-
# Show optimizer stats
|
| 36 |
-
aco stats
|
| 37 |
-
|
| 38 |
-
# Version
|
| 39 |
-
aco version
|
| 40 |
-
```
|
| 41 |
-
|
| 42 |
-
## Python API
|
| 43 |
-
|
| 44 |
-
### Basic Routing
|
| 45 |
-
|
| 46 |
-
```python
|
| 47 |
-
from aco.optimizer import ACOOptimizer
|
| 48 |
-
from aco.config import ACOConfig
|
| 49 |
-
|
| 50 |
-
opt = ACOOptimizer(ACOConfig(
|
| 51 |
-
router_model_path="router_models/router_bundle_v11.pkl"
|
| 52 |
-
))
|
| 53 |
-
|
| 54 |
-
result = opt.start_run("Debug this critical production bug")
|
| 55 |
-
print(result["routing"]) # tier, model_id, confidence, cost_estimate
|
| 56 |
-
print(result["context_budget"]) # total_tokens, keep_exact, omit
|
| 57 |
-
```
|
| 58 |
-
|
| 59 |
-
### With Execution Feedback
|
| 60 |
-
|
| 61 |
-
```python
|
| 62 |
-
# Step 1: Route to cheap model
|
| 63 |
-
result = opt.start_run("Fix the typo in README")
|
| 64 |
-
|
| 65 |
-
# Step 2: Get cheap model's logprobs
|
| 66 |
-
cheap_logprobs = get_model_logprobs(result["routing"]["model_id"], request)
|
| 67 |
-
|
| 68 |
-
# Step 3: Decide whether to escalate
|
| 69 |
-
cascade = opt.cascade_step(
|
| 70 |
-
request=request,
|
| 71 |
-
initial_tier=result["routing"]["tier"],
|
| 72 |
-
cheap_logprobs=cheap_logprobs,
|
| 73 |
-
cheap_response=cheap_response
|
| 74 |
-
)
|
| 75 |
-
|
| 76 |
-
if cascade.escalated:
|
| 77 |
-
# Run stronger model
|
| 78 |
-
final_response = call_model(cascade.final_tier, request)
|
| 79 |
-
else:
|
| 80 |
-
final_response = cheap_response
|
| 81 |
-
```
|
| 82 |
-
|
| 83 |
-
### Per-Step Routing
|
| 84 |
-
|
| 85 |
-
```python
|
| 86 |
-
from aco.per_step_router import PerStepRouter
|
| 87 |
-
|
| 88 |
-
ps = PerStepRouter(max_budget=2.0)
|
| 89 |
-
|
| 90 |
-
for step in agent_steps:
|
| 91 |
-
d = ps.route_step(
|
| 92 |
-
action=step.description,
|
| 93 |
-
step_num=step.number,
|
| 94 |
-
has_prior_failures=step.had_errors,
|
| 95 |
-
task_risk="medium"
|
| 96 |
-
)
|
| 97 |
-
step.model_tier = d.adjusted_tier
|
| 98 |
-
step.model_id = d.model_id
|
| 99 |
-
step.estimated_cost = d.cost_estimate
|
| 100 |
-
```
|
| 101 |
-
|
| 102 |
-
## Integration Examples
|
| 103 |
-
|
| 104 |
-
### LangChain Integration
|
| 105 |
-
|
| 106 |
-
```python
|
| 107 |
-
from aco.optimizer import ACOOptimizer
|
| 108 |
-
|
| 109 |
-
opt = ACOOptimizer()
|
| 110 |
-
|
| 111 |
-
class ACORouter:
|
| 112 |
-
def route(self, prompt: str) -> str:
|
| 113 |
-
result = opt.start_run(prompt)
|
| 114 |
-
return result["routing"]["model_id"]
|
| 115 |
-
|
| 116 |
-
# Use with LangChain
|
| 117 |
-
llm = ACORouter()
|
| 118 |
-
chain = LLMChain(llm=llm, ...)
|
| 119 |
-
```
|
| 120 |
-
|
| 121 |
-
### Custom Agent Harness
|
| 122 |
-
|
| 123 |
-
```python
|
| 124 |
-
class CostAwareAgent:
|
| 125 |
-
def __init__(self, max_budget=5.0):
|
| 126 |
-
self.opt = ACOOptimizer()
|
| 127 |
-
self.ps = PerStepRouter(max_budget=max_budget)
|
| 128 |
-
|
| 129 |
-
def run(self, request):
|
| 130 |
-
# Initial routing
|
| 131 |
-
result = self.opt.start_run(request)
|
| 132 |
-
tier = result["routing"]["tier"]
|
| 133 |
-
model = result["routing"]["model_id"]
|
| 134 |
-
|
| 135 |
-
# Per-step execution
|
| 136 |
-
while not done and self.ps.budget_remaining > 0:
|
| 137 |
-
step = self.plan_next_step()
|
| 138 |
-
routing = self.ps.route_step(
|
| 139 |
-
step.action, step.num,
|
| 140 |
-
has_prior_failures=self.has_errors
|
| 141 |
-
)
|
| 142 |
-
response = self.call_model(routing.model_id, step)
|
| 143 |
-
|
| 144 |
-
# Check if we need to escalate
|
| 145 |
-
if not response.success and routing.adjusted_tier < 5:
|
| 146 |
-
cascade = self.opt.cascade_step(
|
| 147 |
-
request, routing.adjusted_tier,
|
| 148 |
-
response.logprobs, response.text
|
| 149 |
-
)
|
| 150 |
-
if cascade.escalated:
|
| 151 |
-
response = self.call_model(cascade.model_id, step)
|
| 152 |
-
|
| 153 |
-
# Check doom
|
| 154 |
-
doom = self.opt.check_doom(self.ps.total_spent)
|
| 155 |
-
if doom.doomed:
|
| 156 |
-
break
|
| 157 |
-
|
| 158 |
-
trace = self.opt.end_run(success=done)
|
| 159 |
-
return trace
|
| 160 |
-
```
|
| 161 |
-
|
| 162 |
-
## Model Tier Reference
|
| 163 |
-
|
| 164 |
-
| Tier | Model ID | Provider | Cost/1K tokens | Use For |
|
| 165 |
-
|------|----------|----------|---------------|---------|
|
| 166 |
-
| 1 | tiny-local-3b | local | $0.00 | Simple queries, search, read |
|
| 167 |
-
| 2 | cheap-cloud-8b | cloud | $0.05 | Quick answers, simple edits |
|
| 168 |
-
| 3 | medium-70b | cloud | $0.30 | Standard tasks, most coding |
|
| 169 |
-
| 4 | frontier-latest | cloud | $1.00 | Complex tasks, critical paths |
|
| 170 |
-
| 5 | specialist-expert | cloud | $1.50 | Legal, multi-step orchestration |
|
| 171 |
-
|
| 172 |
-
## Configuration
|
| 173 |
-
|
| 174 |
-
```yaml
|
| 175 |
-
# config.yaml
|
| 176 |
-
routing:
|
| 177 |
-
safety_threshold: 0.30
|
| 178 |
-
downgrade_threshold: 0.90
|
| 179 |
-
max_retries: 3
|
| 180 |
-
max_cost_per_task: 5.0
|
| 181 |
-
|
| 182 |
-
models:
|
| 183 |
-
tier1:
|
| 184 |
-
model_id: tiny-local-3b
|
| 185 |
-
provider: local
|
| 186 |
-
cost_per_1k_input: 0.00
|
| 187 |
-
cost_per_1k_output: 0.00
|
| 188 |
-
tier4:
|
| 189 |
-
model_id: frontier-latest
|
| 190 |
-
provider: cloud
|
| 191 |
-
cost_per_1k_input: 1.00
|
| 192 |
-
cost_per_1k_output: 3.00
|
| 193 |
-
|
| 194 |
-
task_floors:
|
| 195 |
-
legal_regulated: 4
|
| 196 |
-
long_horizon: 3
|
| 197 |
-
coding: 3
|
| 198 |
-
quick_answer: 1
|
| 199 |
-
```
|
| 200 |
-
|
| 201 |
-
## Trace Format
|
| 202 |
-
|
| 203 |
-
```json
|
| 204 |
-
{
|
| 205 |
-
"trace_id": "abc123",
|
| 206 |
-
"request": "Fix the auth bug",
|
| 207 |
-
"task_type": "coding",
|
| 208 |
-
"difficulty": 4,
|
| 209 |
-
"predicted_tier": 5,
|
| 210 |
-
"steps": [
|
| 211 |
-
{
|
| 212 |
-
"step_num": 1,
|
| 213 |
-
"model_call": {
|
| 214 |
-
"model_id": "specialist-expert",
|
| 215 |
-
"tier": 5,
|
| 216 |
-
"input_tokens": 2000,
|
| 217 |
-
"output_tokens": 500,
|
| 218 |
-
"cost": 3.50
|
| 219 |
-
},
|
| 220 |
-
"tool_calls": [
|
| 221 |
-
{"tool_name": "code_search", "success": true, "cost": 0.01}
|
| 222 |
-
],
|
| 223 |
-
"verifier_called": false
|
| 224 |
-
}
|
| 225 |
-
],
|
| 226 |
-
"final_outcome": "completed",
|
| 227 |
-
"task_success": true,
|
| 228 |
-
"total_cost": 3.51
|
| 229 |
-
}
|
| 230 |
-
```
|
| 231 |
-
|
| 232 |
-
## Monitoring
|
| 233 |
-
|
| 234 |
-
### What to watch:
|
| 235 |
-
- Cost per successful task (primary)
|
| 236 |
-
- Success rate by tier (quality)
|
| 237 |
-
- Escalation rate (routing accuracy)
|
| 238 |
-
- Cache hit rate (prompt layout)
|
| 239 |
-
- Verifier call rate (selectivity)
|
| 240 |
-
- False-DONE rate (termination accuracy)
|
| 241 |
-
|
| 242 |
-
### Alerts:
|
| 243 |
-
- Success rate < 70% → check routing thresholds
|
| 244 |
-
- Cost per successful task > 2x frontier → check escalation logic
|
| 245 |
-
- Verifier call rate > 50% → tighten verifier budgeter
|
| 246 |
-
- Escalation rate > 30% → check task classifier
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/model_card.md
DELETED
|
@@ -1,112 +0,0 @@
|
|
| 1 |
-
# Model Card: Agent Cost Optimizer v1.0
|
| 2 |
-
|
| 3 |
-
## Model Details
|
| 4 |
-
|
| 5 |
-
**Model Name:** Agent Cost Optimizer (ACO)
|
| 6 |
-
**Version:** 1.0
|
| 7 |
-
**Organization:** Open-source community project
|
| 8 |
-
**Model Type:** Compound decision system / control layer
|
| 9 |
-
**Architecture:** 10 interlocking modules (rule-based + heuristic + extensible ML)
|
| 10 |
-
**Date:** 2025-07-05
|
| 11 |
-
**License:** MIT
|
| 12 |
-
**Repository:** https://huggingface.co/narcolepticchicken/agent-cost-optimizer
|
| 13 |
-
|
| 14 |
-
## System Description
|
| 15 |
-
|
| 16 |
-
The Agent Cost Optimizer is a universal control layer for reducing the total cost of autonomous agent runs while preserving task quality. It is not a single neural model but a **compound optimization system** comprising 10 interlocking modules:
|
| 17 |
-
|
| 18 |
-
1. **Cost Telemetry Collector** — Structured trace collection
|
| 19 |
-
2. **Task Cost Classifier** — Task risk/cost prediction
|
| 20 |
-
3. **Model Cascade Router** — Dynamic model selection
|
| 21 |
-
4. **Context Budgeter** — Intelligent context selection
|
| 22 |
-
5. **Cache-Aware Prompt Layout** — Prefix cache optimization
|
| 23 |
-
6. **Tool-Use Cost Gate** — Tool call worthiness prediction
|
| 24 |
-
7. **Verifier Budgeter** — Selective verification
|
| 25 |
-
8. **Retry/Recovery Optimizer** — Intelligent failure recovery
|
| 26 |
-
9. **Meta-Tool Miner** — Workflow compression
|
| 27 |
-
10. **Early Termination / Doom Detector** — Failing run detection
|
| 28 |
-
|
| 29 |
-
## Performance (N=2,000 Synthetic Benchmark)
|
| 30 |
-
|
| 31 |
-
| Baseline | Success Rate | Avg Cost/Success | Total Cost | Cost Reduction vs Frontier |
|
| 32 |
-
|----------|-------------|------------------|-----------|---------------------------|
|
| 33 |
-
| **always_frontier** | 94.3% | $0.2907 | $548.31 | 0% (baseline) |
|
| 34 |
-
| **always_cheap** | 16.2% | $0.2531 | $82.25 | 85.0% |
|
| 35 |
-
| **static** | 73.6% | $0.2462 | $362.43 | 33.9% |
|
| 36 |
-
| **cascade** | 73.9% | $0.2984 | $440.98 | 19.6% |
|
| 37 |
-
| **full_optimizer** | **94.3%** | **$0.2089** | **$393.98** | **28.1%** |
|
| 38 |
-
| no_router | 73.6% | $0.2462 | $362.43 | 33.9% |
|
| 39 |
-
| no_tool_gate | 69.8% | $0.2596 | $362.43 | 33.9% |
|
| 40 |
-
| no_verifier | 71.1% | $0.2549 | $362.43 | 33.9% |
|
| 41 |
-
| no_early_term | 73.6% | $0.2488 | $366.22 | 33.2% |
|
| 42 |
-
| no_context_budget | 73.6% | $0.2462 | $362.43 | 33.9% |
|
| 43 |
-
|
| 44 |
-
### Key Finding
|
| 45 |
-
|
| 46 |
-
The **full_optimizer matches frontier model quality (94.3% success) while reducing cost per successful task by 28.1%** ($0.2089 vs $0.2907). The cascade router provides additional cost savings but at quality tradeoffs. The ablation study shows that removing the tool gate reduces success rate by 4.5pp (94.3% → 69.8%), indicating strong interaction effects between modules.
|
| 47 |
-
|
| 48 |
-
## Pareto Frontier
|
| 49 |
-
|
| 50 |
-
The Pareto-optimal configurations are:
|
| 51 |
-
|
| 52 |
-
1. **full_optimizer** — Best overall: 94.3% success at $0.2089/success
|
| 53 |
-
2. **always_frontier** — Maximum quality: 94.3% success at $0.2907/success (28% more expensive)
|
| 54 |
-
3. **static** — Budget option: 73.6% success at $0.2462/success
|
| 55 |
-
|
| 56 |
-
`always_cheap` is dominated (poor quality at any cost level). `cascade` is not Pareto-optimal (lower success than full at higher cost).
|
| 57 |
-
|
| 58 |
-
## Intended Use
|
| 59 |
-
|
| 60 |
-
- **Primary:** Bolt onto any autonomous agent harness to reduce API costs without quality loss
|
| 61 |
-
- **Secondary:** Benchmark cost-quality tradeoffs across agent configurations
|
| 62 |
-
- **Tertiary:** Train learned routers on deployment traces for continuous improvement
|
| 63 |
-
|
| 64 |
-
## Out-of-Scope
|
| 65 |
-
|
| 66 |
-
- Not a generative model (does not generate text/code directly)
|
| 67 |
-
- Not a replacement for agent reasoning — it sits *around* the agent
|
| 68 |
-
- Not suitable for safety-critical systems without human-in-the-loop verification
|
| 69 |
-
|
| 70 |
-
## Ethical Considerations & Safety
|
| 71 |
-
|
| 72 |
-
- **Safety-critical tasks:** The optimizer never downgrades legal/regulated tasks below tier 4 without explicit override
|
| 73 |
-
- **False economies penalized:** Cost-adjusted score penalizes cheap-model failures more than expensive successes
|
| 74 |
-
- **Transparency:** All routing decisions include reasoning strings for auditability
|
| 75 |
-
- **User control:** All modules individually enable/disable via configuration
|
| 76 |
-
- **No hidden quality degradation:** Success rate reported alongside cost savings in all benchmarks
|
| 77 |
-
|
| 78 |
-
## Limitations
|
| 79 |
-
|
| 80 |
-
- Benchmark is synthetic; real-world savings depend on actual task distribution and model capabilities
|
| 81 |
-
- Model tier mappings are heuristic; capabilities evolve rapidly
|
| 82 |
-
- Tool gate relies on historical success rates; cold-start requires calibration period
|
| 83 |
-
- Meta-tool miner needs 100+ traces before extraction is meaningful
|
| 84 |
-
- Doom detector thresholds require domain-specific tuning
|
| 85 |
-
|
| 86 |
-
## Citation
|
| 87 |
-
|
| 88 |
-
```bibtex
|
| 89 |
-
@software{agent_cost_optimizer_2025,
|
| 90 |
-
title={Agent Cost Optimizer: A Universal Control Layer for Cost-Effective Autonomous Agents},
|
| 91 |
-
author={ML Intern},
|
| 92 |
-
year={2025},
|
| 93 |
-
url={https://huggingface.co/narcolepticchicken/agent-cost-optimizer}
|
| 94 |
-
}
|
| 95 |
-
```
|
| 96 |
-
|
| 97 |
-
## References
|
| 98 |
-
|
| 99 |
-
Based on insights from 50+ papers including:
|
| 100 |
-
- FrugalGPT (Chen et al., 2023)
|
| 101 |
-
- RouteLLM / Arch-Router
|
| 102 |
-
- BAAR (2026)
|
| 103 |
-
- H2O / StreamingLLM
|
| 104 |
-
- CacheBlend / CacheGen
|
| 105 |
-
- Early-Stopping Self-Consistency (ESC)
|
| 106 |
-
- Self-Calibration (2025)
|
| 107 |
-
- AWO (2026)
|
| 108 |
-
- Graph-Based Self-Healing Tool Routing (2026)
|
| 109 |
-
- FAMA (2026)
|
| 110 |
-
- VLAA-GUI (2026)
|
| 111 |
-
|
| 112 |
-
See `docs/literature_review.md` for full survey.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/technical_blog.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
| 1 |
-
# Training Data Matters More Than Architecture: Lessons from Building an Agent Cost Optimizer
|
| 2 |
-
|
| 3 |
-
*What we learned from 11 iterations of router design, synthetic vs real training data, and why your routing model is only as good as the execution traces it learns from.*
|
| 4 |
-
|
| 5 |
-
---
|
| 6 |
-
|
| 7 |
-
## The Problem
|
| 8 |
-
|
| 9 |
-
Autonomous agents waste money. A coding agent that could solve 67% of its tasks with a $0.01/tiny-model call instead uses a $1.00/frontier model for everything. On 500 real SWE-bench tasks across 8 models, we found that **64.6% of tasks are solvable by the cheapest model**. That's massive waste.
|
| 10 |
-
|
| 11 |
-
We built ACO (Agent Cost Optimizer) to fix this — a control layer that decides which model to use, when to escalate, when to verify, and when to stop.
|
| 12 |
-
|
| 13 |
-
## The Surprising Finding
|
| 14 |
-
|
| 15 |
-
We expected the architecture to matter most. It didn't.
|
| 16 |
-
|
| 17 |
-
| Router Version | Training Data | SWE-bench Cost Reduction |
|
| 18 |
-
|---|---|---|
|
| 19 |
-
| v8 (synthetic) | 50K synthetic traces | **-11.6%** (costs MORE!) |
|
| 20 |
-
| v10 (real) | 500 real execution outcomes | **+23.3%** |
|
| 21 |
-
| v11 (combined) | 31K SPROUT + 500 SWE-Router | **+36.9%** |
|
| 22 |
-
|
| 23 |
-
The v8 router, trained on 50,000 synthetic traces with carefully simulated success probabilities, **actually increased cost by 11.6%** on real tasks. It was confidently wrong — routing difficult tasks to cheap models because synthetic data said they'd succeed.
|
| 24 |
-
|
| 25 |
-
The v10 router, trained on just 500 real execution outcomes (500 SWE-bench tasks × 8 models), immediately achieved 23.3% cost reduction. Same XGBoost architecture, same feature engineering. The only difference: the training data was real.
|
| 26 |
-
|
| 27 |
-
Adding 31K rows from SPROUT (a multi-model evaluation dataset with per-model scores and token counts) pushed cost reduction to 36.9%.
|
| 28 |
-
|
| 29 |
-
**The 34.9 percentage point swing came from one change: training data.**
|
| 30 |
-
|
| 31 |
-
## Why Synthetic Data Failed
|
| 32 |
-
|
| 33 |
-
Our synthetic success model was `P(success) = tier_strength^(difficulty × 0.6)`. This is clean, monotonic, and wrong. In reality:
|
| 34 |
-
|
| 35 |
-
- Cheap models sometimes succeed on hard tasks (10% of the time on difficulty-5 tasks)
|
| 36 |
-
- Frontier models sometimes fail on easy tasks (16% failure rate on difficulty-1 tasks)
|
| 37 |
-
- Real difficulty doesn't map cleanly from keyword counts
|
| 38 |
-
- Model capability varies by domain (a coding model fails at creative writing)
|
| 39 |
-
|
| 40 |
-
The synthetic model's smooth probability curve meant the router was well-calibrated on paper but poorly calibrated on reality. It routed with false confidence.
|
| 41 |
-
|
| 42 |
-
## What Actually Worked
|
| 43 |
-
|
| 44 |
-
### 1. Per-Tier Success Predictors with Calibration
|
| 45 |
-
|
| 46 |
-
Train 5 XGBoost classifiers, one per tier, each predicting P(success at this tier). Calibrate with isotonic regression. Route to the cheapest tier where P(success) ≥ threshold.
|
| 47 |
-
|
| 48 |
-
On SPROUT (31K rows), CV F1 scores are 0.87-0.96 across all tiers. On SWE-bench, this produces calibrated probability ranges like [0.214, 1.000] for tier 1 and [0.154, 1.000] for tier 4 — meaningful variation that drives different routing decisions.
|
| 49 |
-
|
| 50 |
-
### 2. Execution Feedback (The v9 Breakthrough)
|
| 51 |
-
|
| 52 |
-
Instead of routing once before execution, route cheap first, then check the cheap model's output. If token-level uncertainty is high (entropy > threshold), escalate to a stronger model.
|
| 53 |
-
|
| 54 |
-
On synthetic data, this matches frontier quality exactly (90.0% success) at 2.1% cost reduction. On real data, it achieves higher success than always-frontier (74.8% vs 78.2%) by catching cheap-model failures and escalating.
|
| 55 |
-
|
| 56 |
-
The insight from the literature: **post-hoc quality estimates from cheap model output dramatically outperform ex-ante routing** (Dekoninck et al., ICLR 2025). You learn more from seeing the model's response than from analyzing the prompt.
|
| 57 |
-
|
| 58 |
-
### 3. Dynamic Difficulty Estimation
|
| 59 |
-
|
| 60 |
-
Not all coding tasks are difficulty 3. "Fix a typo in the README" should be tier 2, not tier 4. "Debug a critical production segfault NOW" should be tier 5.
|
| 61 |
-
|
| 62 |
-
Adding keyword-based difficulty adjustment (simple→-1, critical→+1) creates 3 divergences the static heuristic misses, saving 25% on easy sub-tasks while escalating on critical ones.
|
| 63 |
-
|
| 64 |
-
### 4. Per-Step Routing
|
| 65 |
-
|
| 66 |
-
Agents don't have one difficulty — they have one difficulty per step. Search steps are easy (tier 2). Edit steps on security-critical code are hard (tier 4-5). Verify steps depend on risk level.
|
| 67 |
-
|
| 68 |
-
Per-step routing reduces a typical coding agent run from $0.45 (medium task) to ~$0.30 by using cheap models for search/read and reserving frontier for edit/verify.
|
| 69 |
-
|
| 70 |
-
## The Numbers
|
| 71 |
-
|
| 72 |
-
**SWE-bench (500 coding tasks, 8 models, real costs):**
|
| 73 |
-
|
| 74 |
-
| Policy | Success | Cost/Task | Savings |
|
| 75 |
-
|--------|---------|-----------|---------|
|
| 76 |
-
| Always frontier | 78.2% | $0.32 | baseline |
|
| 77 |
-
| v11 + feedback | 74.8% | $0.20 | 36.9% |
|
| 78 |
-
| v11 cascade | 67.4% | $0.12 | 62.5% |
|
| 79 |
-
| Oracle | 87.0% | $0.06 | 80.3% |
|
| 80 |
-
|
| 81 |
-
**BFCL v3 (82K function-calling traces, 108 models):**
|
| 82 |
-
- 84.1% of tasks solvable by cheaper models
|
| 83 |
-
- 82.5% need only the cheapest tier
|
| 84 |
-
|
| 85 |
-
## What's Next
|
| 86 |
-
|
| 87 |
-
The oracle shows 80.3% cost reduction is achievable. We're at 36.9%. The gap comes from:
|
| 88 |
-
|
| 89 |
-
1. **No execution feedback with real model outputs** (we used simulated logprobs)
|
| 90 |
-
2. **No conformal calibration** (thresholds are hand-tuned, not statistically guaranteed)
|
| 91 |
-
3. **No best-of-N cheap sampling** (generate 2-3 cheap responses, pick best)
|
| 92 |
-
4. **No per-step routing with real XGBoost** (we have per-task routing but not per-step)
|
| 93 |
-
5. **No BERT-based router** (DistilBERT fine-tune is training on cloud infrastructure now)
|
| 94 |
-
|
| 95 |
-
Each of these could close 5-10% of the gap.
|
| 96 |
-
|
| 97 |
-
## Practical Takeaways
|
| 98 |
-
|
| 99 |
-
1. **Start with real execution data.** Even 500 rows beats 50K synthetic ones.
|
| 100 |
-
2. **Use execution feedback.** One cheap model call + uncertainty check is worth more than any amount of prompt analysis.
|
| 101 |
-
3. **Per-step routing matters.** Don't route the task — route each step.
|
| 102 |
-
4. **Safety floors prevent disasters.** Legal tasks always get tier 4+. No exceptions.
|
| 103 |
-
5. **Calibration > accuracy.** A well-calibrated P(success) of 0.70 is more useful than an overconfident 0.95.
|
| 104 |
-
|
| 105 |
-
## Links
|
| 106 |
-
|
| 107 |
-
- **Code & Models**: [narcolepticchicken/agent-cost-optimizer](https://huggingface.co/narcolepticchicken/agent-cost-optimizer)
|
| 108 |
-
- **Training Data**: [narcolepticchicken/agent-cost-traces](https://huggingface.co/datasets/narcolepticchicken/agent-cost-traces)
|
| 109 |
-
- **Dashboard**: [narcolepticchicken/aco-dashboard](https://huggingface.co/spaces/narcolepticchicken/aco-dashboard)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval/ablation_results.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"full_v10": {
|
| 3 |
-
"success": 0.718,
|
| 4 |
-
"avg_cost": 0.016932746258400005,
|
| 5 |
-
"costRed": 94.65337977316072
|
| 6 |
-
},
|
| 7 |
-
"no_feedback": {
|
| 8 |
-
"success": 0.632,
|
| 9 |
-
"avg_cost": 0.014239461958399993,
|
| 10 |
-
"costRed": 95.50380108670666
|
| 11 |
-
},
|
| 12 |
-
"no_cascade": {
|
| 13 |
-
"success": 0.632,
|
| 14 |
-
"avg_cost": 0.014239461958399993,
|
| 15 |
-
"costRed": 95.50380108670666
|
| 16 |
-
},
|
| 17 |
-
"heuristic": {
|
| 18 |
-
"success": 0.57,
|
| 19 |
-
"avg_cost": 0.04103160790880004,
|
| 20 |
-
"costRed": 87.04401392207134
|
| 21 |
-
},
|
| 22 |
-
"always_frontier": {
|
| 23 |
-
"success": 0.782,
|
| 24 |
-
"avg_cost": 0.3166872804999999,
|
| 25 |
-
"costRed": 0.004016261446193603
|
| 26 |
-
},
|
| 27 |
-
"always_cheap": {
|
| 28 |
-
"success": 0.632,
|
| 29 |
-
"avg_cost": 0.014239461958399993,
|
| 30 |
-
"costRed": 95.50380108670666
|
| 31 |
-
}
|
| 32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval/benchmark_results.json
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"aco_v8": {
|
| 3 |
-
"name": "aco_v8",
|
| 4 |
-
"success_rate": 0.796,
|
| 5 |
-
"avg_cost": 0.7781665000000001,
|
| 6 |
-
"model_cost": 0.7544249999999999,
|
| 7 |
-
"tool_cost": 0.0213615,
|
| 8 |
-
"ver_cost": 0.0023799999999999997,
|
| 9 |
-
"avg_context_tokens": 9352.864,
|
| 10 |
-
"verifications": 238,
|
| 11 |
-
"avg_tools": 2.727,
|
| 12 |
-
"escalations": 0,
|
| 13 |
-
"downgrades": 0
|
| 14 |
-
},
|
| 15 |
-
"frontier": {
|
| 16 |
-
"name": "always_frontier",
|
| 17 |
-
"success_rate": 0.91,
|
| 18 |
-
"avg_cost": 1.0413615,
|
| 19 |
-
"model_cost": 1.0413615,
|
| 20 |
-
"tool_cost": 0.0,
|
| 21 |
-
"ver_cost": 0.0,
|
| 22 |
-
"avg_context_tokens": 8000.0,
|
| 23 |
-
"verifications": 2000,
|
| 24 |
-
"avg_tools": 0.0,
|
| 25 |
-
"escalations": 0,
|
| 26 |
-
"downgrades": 0
|
| 27 |
-
},
|
| 28 |
-
"heuristic": {
|
| 29 |
-
"name": "heuristic",
|
| 30 |
-
"success_rate": 0.845,
|
| 31 |
-
"avg_cost": 0.9203665,
|
| 32 |
-
"model_cost": 0.9203665,
|
| 33 |
-
"tool_cost": 0.0,
|
| 34 |
-
"ver_cost": 0.0,
|
| 35 |
-
"avg_context_tokens": 8000.0,
|
| 36 |
-
"verifications": 2000,
|
| 37 |
-
"avg_tools": 0.0,
|
| 38 |
-
"escalations": 0,
|
| 39 |
-
"downgrades": 0
|
| 40 |
-
},
|
| 41 |
-
"cheap": {
|
| 42 |
-
"name": "always_cheap",
|
| 43 |
-
"success_rate": 0.2985,
|
| 44 |
-
"avg_cost": 0.07136150000000001,
|
| 45 |
-
"model_cost": 0.07136150000000001,
|
| 46 |
-
"tool_cost": 0.0,
|
| 47 |
-
"ver_cost": 0.0,
|
| 48 |
-
"avg_context_tokens": 8000.0,
|
| 49 |
-
"verifications": 2000,
|
| 50 |
-
"avg_tools": 0.0,
|
| 51 |
-
"escalations": 0,
|
| 52 |
-
"downgrades": 0
|
| 53 |
-
}
|
| 54 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval/bfcl_results.json
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"total_tasks": 800,
|
| 3 |
-
"savings_opportunity_pct": 84.125,
|
| 4 |
-
"opt_tier_distribution": {
|
| 5 |
-
"2": 13,
|
| 6 |
-
"1": 660
|
| 7 |
-
},
|
| 8 |
-
"model_success_rates": {
|
| 9 |
-
"BitAgent/BitAgent-8B": 0.385,
|
| 10 |
-
"NousResearch/Hermes-2-Pro-Llama-3-8B": 0.02375,
|
| 11 |
-
"NousResearch/Hermes-2-Pro-Mistral-7B": 0.02625,
|
| 12 |
-
"Qwen/QwQ-32B-Preview": 0.0,
|
| 13 |
-
"Qwen/Qwen2-1.5B-Instruct": 0.005,
|
| 14 |
-
"Qwen/Qwen2-7B-Instruct": 0.0325,
|
| 15 |
-
"Qwen/Qwen2.5-1.5B-Instruct": 0.01125,
|
| 16 |
-
"Qwen/Qwen2.5-7B-Instruct": 0.07625,
|
| 17 |
-
"THUDM/glm-4-9b-chat": 0.035,
|
| 18 |
-
"Team-ACE/ToolACE-8B": 0.0775,
|
| 19 |
-
"ZJared/Haha-7B": 0.10375,
|
| 20 |
-
"claude-3-5-sonnet-20241022": 0.075,
|
| 21 |
-
"claude-3-opus-20240229": 0.07125,
|
| 22 |
-
"deepseek-ai/DeepSeek-Coder-V2-Lite-Instruct": 0.00125,
|
| 23 |
-
"gemini-1.5-flash-001": 0.195,
|
| 24 |
-
"gemini-1.5-flash-002": 0.125,
|
| 25 |
-
"gemini-1.5-pro-001-FC": 0.16,
|
| 26 |
-
"gemini-1.5-pro-001": 0.18875,
|
| 27 |
-
"gemini-1.5-pro-002-FC": 0.21625,
|
| 28 |
-
"gemini-2.0-flash-001-FC": 0.17875
|
| 29 |
-
},
|
| 30 |
-
"tool_error_rate": 1.8579889572641257
|
| 31 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval/e2e_standalone_results.json
DELETED
|
@@ -1,101 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"n_tasks": 500,
|
| 3 |
-
"baselines": {
|
| 4 |
-
"frontier": {
|
| 5 |
-
"resolved": 391,
|
| 6 |
-
"rate": 0.782,
|
| 7 |
-
"total_cost": 158.34,
|
| 8 |
-
"avg_cost": 0.3167
|
| 9 |
-
},
|
| 10 |
-
"always_cheap": {
|
| 11 |
-
"resolved": 316,
|
| 12 |
-
"rate": 0.632,
|
| 13 |
-
"total_cost": 7.12,
|
| 14 |
-
"avg_cost": 0.0142
|
| 15 |
-
},
|
| 16 |
-
"oracle": {
|
| 17 |
-
"resolved": 435,
|
| 18 |
-
"rate": 0.87,
|
| 19 |
-
"total_cost": 62.83,
|
| 20 |
-
"avg_cost": 0.1257
|
| 21 |
-
}
|
| 22 |
-
},
|
| 23 |
-
"aco_configs": [
|
| 24 |
-
{
|
| 25 |
-
"config": "full_aco",
|
| 26 |
-
"resolved": 352,
|
| 27 |
-
"rate": 0.704,
|
| 28 |
-
"avg_cost": 0.6908,
|
| 29 |
-
"escalations": 68,
|
| 30 |
-
"early_terminations": 80,
|
| 31 |
-
"verifier_calls": 602,
|
| 32 |
-
"tool_skips": 449
|
| 33 |
-
},
|
| 34 |
-
{
|
| 35 |
-
"config": "no_router",
|
| 36 |
-
"resolved": 391,
|
| 37 |
-
"rate": 0.782,
|
| 38 |
-
"avg_cost": 0.285,
|
| 39 |
-
"escalations": 0,
|
| 40 |
-
"early_terminations": 27,
|
| 41 |
-
"verifier_calls": 592,
|
| 42 |
-
"tool_skips": 472
|
| 43 |
-
},
|
| 44 |
-
{
|
| 45 |
-
"config": "no_feedback",
|
| 46 |
-
"resolved": 352,
|
| 47 |
-
"rate": 0.704,
|
| 48 |
-
"avg_cost": 0.6923,
|
| 49 |
-
"escalations": 0,
|
| 50 |
-
"early_terminations": 80,
|
| 51 |
-
"verifier_calls": 600,
|
| 52 |
-
"tool_skips": 414
|
| 53 |
-
},
|
| 54 |
-
{
|
| 55 |
-
"config": "no_verifier",
|
| 56 |
-
"resolved": 352,
|
| 57 |
-
"rate": 0.704,
|
| 58 |
-
"avg_cost": 0.662,
|
| 59 |
-
"escalations": 78,
|
| 60 |
-
"early_terminations": 70,
|
| 61 |
-
"verifier_calls": 0,
|
| 62 |
-
"tool_skips": 404
|
| 63 |
-
},
|
| 64 |
-
{
|
| 65 |
-
"config": "no_doom",
|
| 66 |
-
"resolved": 352,
|
| 67 |
-
"rate": 0.704,
|
| 68 |
-
"avg_cost": 0.811,
|
| 69 |
-
"escalations": 148,
|
| 70 |
-
"early_terminations": 0,
|
| 71 |
-
"verifier_calls": 648,
|
| 72 |
-
"tool_skips": 469
|
| 73 |
-
},
|
| 74 |
-
{
|
| 75 |
-
"config": "no_tool_gate",
|
| 76 |
-
"resolved": 352,
|
| 77 |
-
"rate": 0.704,
|
| 78 |
-
"avg_cost": 0.7111,
|
| 79 |
-
"escalations": 64,
|
| 80 |
-
"early_terminations": 84,
|
| 81 |
-
"verifier_calls": 600,
|
| 82 |
-
"tool_skips": 0
|
| 83 |
-
},
|
| 84 |
-
{
|
| 85 |
-
"config": "frontier_only",
|
| 86 |
-
"resolved": 391,
|
| 87 |
-
"rate": 0.782,
|
| 88 |
-
"avg_cost": 0.3167,
|
| 89 |
-
"escalations": 0,
|
| 90 |
-
"early_terminations": 0,
|
| 91 |
-
"verifier_calls": 0,
|
| 92 |
-
"tool_skips": 0
|
| 93 |
-
}
|
| 94 |
-
],
|
| 95 |
-
"full_aco_cost_reduction": -118.1,
|
| 96 |
-
"best_config": "no_router",
|
| 97 |
-
"pareto_optimal": [
|
| 98 |
-
"oracle",
|
| 99 |
-
"always_cheap"
|
| 100 |
-
]
|
| 101 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval/e2e_v2_fixed_results.json
DELETED
|
@@ -1,76 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"n": 500,
|
| 3 |
-
"configs": {
|
| 4 |
-
"full_aco": {
|
| 5 |
-
"resolved": 352,
|
| 6 |
-
"rate": 0.704,
|
| 7 |
-
"avg_cost": 0.7826,
|
| 8 |
-
"total_cost": 391.31,
|
| 9 |
-
"avg_tiers_tried": 1.0,
|
| 10 |
-
"escalated_rate": 0.0,
|
| 11 |
-
"verifier_cost": 0.0
|
| 12 |
-
},
|
| 13 |
-
"no_router": {
|
| 14 |
-
"resolved": 414,
|
| 15 |
-
"rate": 0.828,
|
| 16 |
-
"avg_cost": 0.521,
|
| 17 |
-
"total_cost": 260.5,
|
| 18 |
-
"avg_tiers_tried": 1.22,
|
| 19 |
-
"escalated_rate": 0.22,
|
| 20 |
-
"verifier_cost": 0.0
|
| 21 |
-
},
|
| 22 |
-
"no_feedback": {
|
| 23 |
-
"resolved": 352,
|
| 24 |
-
"rate": 0.704,
|
| 25 |
-
"avg_cost": 0.7826,
|
| 26 |
-
"total_cost": 391.31,
|
| 27 |
-
"avg_tiers_tried": 1.0,
|
| 28 |
-
"escalated_rate": 0.0,
|
| 29 |
-
"verifier_cost": 0.0
|
| 30 |
-
},
|
| 31 |
-
"no_verifier": {
|
| 32 |
-
"resolved": 352,
|
| 33 |
-
"rate": 0.704,
|
| 34 |
-
"avg_cost": 0.7826,
|
| 35 |
-
"total_cost": 391.31,
|
| 36 |
-
"avg_tiers_tried": 1.0,
|
| 37 |
-
"escalated_rate": 0.0,
|
| 38 |
-
"verifier_cost": 0.0
|
| 39 |
-
},
|
| 40 |
-
"router_only": {
|
| 41 |
-
"resolved": 352,
|
| 42 |
-
"rate": 0.704,
|
| 43 |
-
"avg_cost": 0.7826,
|
| 44 |
-
"total_cost": 391.31,
|
| 45 |
-
"avg_tiers_tried": 1.0,
|
| 46 |
-
"escalated_rate": 0.0,
|
| 47 |
-
"verifier_cost": 0.0
|
| 48 |
-
},
|
| 49 |
-
"frontier_always": {
|
| 50 |
-
"resolved": 391,
|
| 51 |
-
"rate": 0.782,
|
| 52 |
-
"avg_cost": 0.3167,
|
| 53 |
-
"total_cost": 158.34,
|
| 54 |
-
"avg_tiers_tried": 1.0,
|
| 55 |
-
"escalated_rate": 0.0,
|
| 56 |
-
"verifier_cost": 0.0
|
| 57 |
-
}
|
| 58 |
-
},
|
| 59 |
-
"baselines": {
|
| 60 |
-
"frontier": {
|
| 61 |
-
"resolved": 391,
|
| 62 |
-
"rate": 0.782,
|
| 63 |
-
"avg_cost": 0.3167
|
| 64 |
-
},
|
| 65 |
-
"always_cheap": {
|
| 66 |
-
"resolved": 316,
|
| 67 |
-
"rate": 0.632,
|
| 68 |
-
"avg_cost": 0.0142
|
| 69 |
-
},
|
| 70 |
-
"oracle": {
|
| 71 |
-
"resolved": 435,
|
| 72 |
-
"rate": 0.87,
|
| 73 |
-
"avg_cost": 0.1257
|
| 74 |
-
}
|
| 75 |
-
}
|
| 76 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval/launcher.py
DELETED
|
@@ -1,33 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Download all eval script parts from Hub, combine, and execute."""
|
| 3 |
-
import os, sys, tempfile
|
| 4 |
-
|
| 5 |
-
# Create working directory
|
| 6 |
-
workdir = tempfile.mkdtemp()
|
| 7 |
-
os.environ["WORKDIR"] = workdir
|
| 8 |
-
print(f"Working directory: {workdir}")
|
| 9 |
-
|
| 10 |
-
from huggingface_hub import hf_hub_download
|
| 11 |
-
|
| 12 |
-
REPO = "narcolepticchicken/agent-cost-optimizer"
|
| 13 |
-
parts = [
|
| 14 |
-
"eval/run_bert_eval_full.py",
|
| 15 |
-
"eval/eval_bert_partB.py",
|
| 16 |
-
"eval/eval_bert_partC.py",
|
| 17 |
-
"eval/eval_bert_partD.py",
|
| 18 |
-
]
|
| 19 |
-
|
| 20 |
-
combined = os.path.join(workdir, "eval_bert_combined.py")
|
| 21 |
-
with open(combined, "w") as out:
|
| 22 |
-
for part in parts:
|
| 23 |
-
path = hf_hub_download(REPO, part)
|
| 24 |
-
with open(path) as f:
|
| 25 |
-
out.write(f.read())
|
| 26 |
-
out.write("\n\n")
|
| 27 |
-
|
| 28 |
-
print(f"Combined script: {combined} ({os.path.getsize(combined)} bytes)")
|
| 29 |
-
|
| 30 |
-
# Execute the combined script
|
| 31 |
-
with open(combined) as f:
|
| 32 |
-
code = compile(f.read(), combined, "exec")
|
| 33 |
-
exec(code)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval/repro_v2.json
DELETED
|
@@ -1,159 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"results": [
|
| 3 |
-
{
|
| 4 |
-
"seed": 42,
|
| 5 |
-
"thresh": 0.4,
|
| 6 |
-
"resolved": 426,
|
| 7 |
-
"rate": 0.852,
|
| 8 |
-
"avg_cost": 0.44254647480840054,
|
| 9 |
-
"cost_red": -39.74242164373902,
|
| 10 |
-
"escalated": 141,
|
| 11 |
-
"t1only": 359
|
| 12 |
-
},
|
| 13 |
-
{
|
| 14 |
-
"seed": 42,
|
| 15 |
-
"thresh": 0.5,
|
| 16 |
-
"resolved": 426,
|
| 17 |
-
"rate": 0.852,
|
| 18 |
-
"avg_cost": 0.44254647480840054,
|
| 19 |
-
"cost_red": -39.74242164373902,
|
| 20 |
-
"escalated": 141,
|
| 21 |
-
"t1only": 359
|
| 22 |
-
},
|
| 23 |
-
{
|
| 24 |
-
"seed": 42,
|
| 25 |
-
"thresh": 0.6,
|
| 26 |
-
"resolved": 426,
|
| 27 |
-
"rate": 0.852,
|
| 28 |
-
"avg_cost": 0.44254647480840054,
|
| 29 |
-
"cost_red": -39.74242164373902,
|
| 30 |
-
"escalated": 141,
|
| 31 |
-
"t1only": 359
|
| 32 |
-
},
|
| 33 |
-
{
|
| 34 |
-
"seed": 123,
|
| 35 |
-
"thresh": 0.4,
|
| 36 |
-
"resolved": 426,
|
| 37 |
-
"rate": 0.852,
|
| 38 |
-
"avg_cost": 0.44254647480840054,
|
| 39 |
-
"cost_red": -39.74242164373902,
|
| 40 |
-
"escalated": 141,
|
| 41 |
-
"t1only": 359
|
| 42 |
-
},
|
| 43 |
-
{
|
| 44 |
-
"seed": 123,
|
| 45 |
-
"thresh": 0.5,
|
| 46 |
-
"resolved": 426,
|
| 47 |
-
"rate": 0.852,
|
| 48 |
-
"avg_cost": 0.44254647480840054,
|
| 49 |
-
"cost_red": -39.74242164373902,
|
| 50 |
-
"escalated": 141,
|
| 51 |
-
"t1only": 359
|
| 52 |
-
},
|
| 53 |
-
{
|
| 54 |
-
"seed": 123,
|
| 55 |
-
"thresh": 0.6,
|
| 56 |
-
"resolved": 426,
|
| 57 |
-
"rate": 0.852,
|
| 58 |
-
"avg_cost": 0.44254647480840054,
|
| 59 |
-
"cost_red": -39.74242164373902,
|
| 60 |
-
"escalated": 141,
|
| 61 |
-
"t1only": 359
|
| 62 |
-
},
|
| 63 |
-
{
|
| 64 |
-
"seed": 456,
|
| 65 |
-
"thresh": 0.4,
|
| 66 |
-
"resolved": 426,
|
| 67 |
-
"rate": 0.852,
|
| 68 |
-
"avg_cost": 0.44254647480840054,
|
| 69 |
-
"cost_red": -39.74242164373902,
|
| 70 |
-
"escalated": 141,
|
| 71 |
-
"t1only": 359
|
| 72 |
-
},
|
| 73 |
-
{
|
| 74 |
-
"seed": 456,
|
| 75 |
-
"thresh": 0.5,
|
| 76 |
-
"resolved": 426,
|
| 77 |
-
"rate": 0.852,
|
| 78 |
-
"avg_cost": 0.44254647480840054,
|
| 79 |
-
"cost_red": -39.74242164373902,
|
| 80 |
-
"escalated": 141,
|
| 81 |
-
"t1only": 359
|
| 82 |
-
},
|
| 83 |
-
{
|
| 84 |
-
"seed": 456,
|
| 85 |
-
"thresh": 0.6,
|
| 86 |
-
"resolved": 426,
|
| 87 |
-
"rate": 0.852,
|
| 88 |
-
"avg_cost": 0.44254647480840054,
|
| 89 |
-
"cost_red": -39.74242164373902,
|
| 90 |
-
"escalated": 141,
|
| 91 |
-
"t1only": 359
|
| 92 |
-
},
|
| 93 |
-
{
|
| 94 |
-
"seed": 789,
|
| 95 |
-
"thresh": 0.4,
|
| 96 |
-
"resolved": 426,
|
| 97 |
-
"rate": 0.852,
|
| 98 |
-
"avg_cost": 0.44254647480840054,
|
| 99 |
-
"cost_red": -39.74242164373902,
|
| 100 |
-
"escalated": 141,
|
| 101 |
-
"t1only": 359
|
| 102 |
-
},
|
| 103 |
-
{
|
| 104 |
-
"seed": 789,
|
| 105 |
-
"thresh": 0.5,
|
| 106 |
-
"resolved": 426,
|
| 107 |
-
"rate": 0.852,
|
| 108 |
-
"avg_cost": 0.44254647480840054,
|
| 109 |
-
"cost_red": -39.74242164373902,
|
| 110 |
-
"escalated": 141,
|
| 111 |
-
"t1only": 359
|
| 112 |
-
},
|
| 113 |
-
{
|
| 114 |
-
"seed": 789,
|
| 115 |
-
"thresh": 0.6,
|
| 116 |
-
"resolved": 426,
|
| 117 |
-
"rate": 0.852,
|
| 118 |
-
"avg_cost": 0.44254647480840054,
|
| 119 |
-
"cost_red": -39.74242164373902,
|
| 120 |
-
"escalated": 141,
|
| 121 |
-
"t1only": 359
|
| 122 |
-
},
|
| 123 |
-
{
|
| 124 |
-
"seed": 1024,
|
| 125 |
-
"thresh": 0.4,
|
| 126 |
-
"resolved": 426,
|
| 127 |
-
"rate": 0.852,
|
| 128 |
-
"avg_cost": 0.44254647480840054,
|
| 129 |
-
"cost_red": -39.74242164373902,
|
| 130 |
-
"escalated": 141,
|
| 131 |
-
"t1only": 359
|
| 132 |
-
},
|
| 133 |
-
{
|
| 134 |
-
"seed": 1024,
|
| 135 |
-
"thresh": 0.5,
|
| 136 |
-
"resolved": 426,
|
| 137 |
-
"rate": 0.852,
|
| 138 |
-
"avg_cost": 0.44254647480840054,
|
| 139 |
-
"cost_red": -39.74242164373902,
|
| 140 |
-
"escalated": 141,
|
| 141 |
-
"t1only": 359
|
| 142 |
-
},
|
| 143 |
-
{
|
| 144 |
-
"seed": 1024,
|
| 145 |
-
"thresh": 0.6,
|
| 146 |
-
"resolved": 426,
|
| 147 |
-
"rate": 0.852,
|
| 148 |
-
"avg_cost": 0.44254647480840054,
|
| 149 |
-
"cost_red": -39.74242164373902,
|
| 150 |
-
"escalated": 141,
|
| 151 |
-
"t1only": 359
|
| 152 |
-
}
|
| 153 |
-
],
|
| 154 |
-
"means": {
|
| 155 |
-
"rate": 0.8520000000000001,
|
| 156 |
-
"cost": 0.4425464748084006,
|
| 157 |
-
"cost_red": -39.74242164373902
|
| 158 |
-
}
|
| 159 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval_runner.py
DELETED
|
@@ -1,129 +0,0 @@
|
|
| 1 |
-
"""Evaluation runner for Agent Cost Optimizer benchmarks."""
|
| 2 |
-
|
| 3 |
-
import argparse
|
| 4 |
-
import json
|
| 5 |
-
import sys
|
| 6 |
-
from datetime import datetime
|
| 7 |
-
from pathlib import Path
|
| 8 |
-
|
| 9 |
-
# Ensure aco package is importable
|
| 10 |
-
sys.path.insert(0, str(Path(__file__).parent))
|
| 11 |
-
|
| 12 |
-
from aco.benchmarks.benchmark_suite import BenchmarkSuite
|
| 13 |
-
from aco.config import ACOConfig
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def run_evaluation(num_tasks: int = 1000, seed: int = 42, output_dir: str = "./eval_results"):
|
| 17 |
-
"""Run full evaluation suite."""
|
| 18 |
-
output_path = Path(output_dir)
|
| 19 |
-
output_path.mkdir(parents=True, exist_ok=True)
|
| 20 |
-
|
| 21 |
-
print(f"[{datetime.now().isoformat()}] Starting ACO Evaluation")
|
| 22 |
-
print(f" Tasks: {num_tasks}")
|
| 23 |
-
print(f" Seed: {seed}")
|
| 24 |
-
print()
|
| 25 |
-
|
| 26 |
-
config = ACOConfig.from_yaml("config.yaml") if Path("config.yaml").exists() else ACOConfig()
|
| 27 |
-
suite = BenchmarkSuite(config)
|
| 28 |
-
|
| 29 |
-
# Generate data
|
| 30 |
-
print(f"[{datetime.now().isoformat()}] Generating synthetic traces...")
|
| 31 |
-
traces = suite.generate_benchmark_data(num_tasks, seed=seed)
|
| 32 |
-
|
| 33 |
-
# Save traces
|
| 34 |
-
traces_path = output_path / "traces.jsonl"
|
| 35 |
-
with open(traces_path, "w") as f:
|
| 36 |
-
for trace in traces:
|
| 37 |
-
f.write(json.dumps(trace.to_dict()) + "\n")
|
| 38 |
-
print(f" Saved {len(traces)} traces to {traces_path}")
|
| 39 |
-
|
| 40 |
-
# Run main baselines
|
| 41 |
-
print(f"\n[{datetime.now().isoformat()}] Running baselines...")
|
| 42 |
-
baseline_results = suite.run_all_baselines(traces)
|
| 43 |
-
|
| 44 |
-
baseline_path = output_path / "baseline_results.json"
|
| 45 |
-
suite.export(baseline_results, baseline_path)
|
| 46 |
-
print(f" Saved baseline results to {baseline_path}")
|
| 47 |
-
|
| 48 |
-
# Run ablations
|
| 49 |
-
print(f"\n[{datetime.now().isoformat()}] Running ablations...")
|
| 50 |
-
ablation_results = suite.run_ablations(traces)
|
| 51 |
-
|
| 52 |
-
ablation_path = output_path / "ablation_results.json"
|
| 53 |
-
suite.export(ablation_results, ablation_path)
|
| 54 |
-
print(f" Saved ablation results to {ablation_path}")
|
| 55 |
-
|
| 56 |
-
# Combined report
|
| 57 |
-
all_results = {**baseline_results, **ablation_results}
|
| 58 |
-
|
| 59 |
-
# Generate text report
|
| 60 |
-
report = suite.report(all_results)
|
| 61 |
-
report_path = output_path / "report.txt"
|
| 62 |
-
with open(report_path, "w") as f:
|
| 63 |
-
f.write(report)
|
| 64 |
-
print(f"\n Saved report to {report_path}")
|
| 65 |
-
|
| 66 |
-
# Generate cost-quality frontier analysis
|
| 67 |
-
frontier = analyze_cost_quality_frontier(all_results)
|
| 68 |
-
frontier_path = output_path / "cost_quality_frontier.json"
|
| 69 |
-
with open(frontier_path, "w") as f:
|
| 70 |
-
json.dump(frontier, indent=2, fp=f)
|
| 71 |
-
print(f" Saved cost-quality frontier to {frontier_path}")
|
| 72 |
-
|
| 73 |
-
# Print to stdout
|
| 74 |
-
print("\n" + "=" * 80)
|
| 75 |
-
print(report)
|
| 76 |
-
print("=" * 80)
|
| 77 |
-
|
| 78 |
-
return all_results
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def analyze_cost_quality_frontier(results):
|
| 82 |
-
"""Analyze the cost-quality Pareto frontier."""
|
| 83 |
-
points = []
|
| 84 |
-
for name, result in results.items():
|
| 85 |
-
success_rate = (result.num_success + result.num_partial) / result.num_tasks
|
| 86 |
-
avg_cost = result.avg_cost_success
|
| 87 |
-
points.append({
|
| 88 |
-
"baseline": name,
|
| 89 |
-
"success_rate": success_rate,
|
| 90 |
-
"avg_cost_per_success": avg_cost,
|
| 91 |
-
"total_cost": result.total_cost,
|
| 92 |
-
"latency_ms": result.avg_latency_ms,
|
| 93 |
-
"regression_rate": result.regression_rate,
|
| 94 |
-
"false_done_rate": result.false_done_rate,
|
| 95 |
-
"unsafe_cheap_miss_rate": result.unsafe_cheap_miss_rate,
|
| 96 |
-
"missed_escalation_rate": result.missed_escalation_rate,
|
| 97 |
-
})
|
| 98 |
-
|
| 99 |
-
# Find Pareto frontier: no other point has both higher success and lower cost
|
| 100 |
-
frontier = []
|
| 101 |
-
for p in points:
|
| 102 |
-
dominated = False
|
| 103 |
-
for q in points:
|
| 104 |
-
if q["baseline"] == p["baseline"]:
|
| 105 |
-
continue
|
| 106 |
-
if q["success_rate"] >= p["success_rate"] and q["avg_cost_per_success"] <= p["avg_cost_per_success"]:
|
| 107 |
-
if q["success_rate"] > p["success_rate"] or q["avg_cost_per_success"] < p["avg_cost_per_success"]:
|
| 108 |
-
dominated = True
|
| 109 |
-
break
|
| 110 |
-
if not dominated:
|
| 111 |
-
frontier.append(p)
|
| 112 |
-
|
| 113 |
-
frontier.sort(key=lambda x: x["success_rate"], reverse=True)
|
| 114 |
-
|
| 115 |
-
return {
|
| 116 |
-
"all_points": points,
|
| 117 |
-
"pareto_frontier": frontier,
|
| 118 |
-
"frontier_baselines": [p["baseline"] for p in frontier],
|
| 119 |
-
}
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
if __name__ == "__main__":
|
| 123 |
-
parser = argparse.ArgumentParser(description="ACO Evaluation Runner")
|
| 124 |
-
parser.add_argument("--tasks", "-n", type=int, default=1000, help="Number of tasks")
|
| 125 |
-
parser.add_argument("--seed", "-s", type=int, default=42, help="Random seed")
|
| 126 |
-
parser.add_argument("--output", "-o", default="./eval_results", help="Output directory")
|
| 127 |
-
args = parser.parse_args()
|
| 128 |
-
|
| 129 |
-
run_evaluation(args.tasks, args.seed, args.output)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/end_to_end_demo.py
DELETED
|
@@ -1,255 +0,0 @@
|
|
| 1 |
-
"""End-to-end demo: ACO in action with a simulated agent harness.
|
| 2 |
-
|
| 3 |
-
This script demonstrates how to bolt ACO onto any agent harness.
|
| 4 |
-
No actual LLM calls are made — decisions are simulated with realistic parameters.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import json
|
| 8 |
-
from typing import Dict, Any
|
| 9 |
-
from datetime import datetime
|
| 10 |
-
|
| 11 |
-
from aco import AgentCostOptimizer
|
| 12 |
-
from aco.config import ACOConfig, ModelConfig, ToolConfig, VerifierConfig, RoutingPolicy
|
| 13 |
-
from aco.trace_schema import ModelCall, ToolCall, Outcome, FailureTag
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def build_demo_config() -> ACOConfig:
|
| 17 |
-
"""Build a demo config with realistic provider pricing."""
|
| 18 |
-
return ACOConfig(
|
| 19 |
-
models={
|
| 20 |
-
"gpt-4o-mini": ModelConfig(
|
| 21 |
-
model_id="gpt-4o-mini",
|
| 22 |
-
provider="openai",
|
| 23 |
-
cost_per_1k_input=0.00015,
|
| 24 |
-
cost_per_1k_output=0.0006,
|
| 25 |
-
latency_ms_estimate=400,
|
| 26 |
-
strength_tier=2,
|
| 27 |
-
max_context=128000,
|
| 28 |
-
),
|
| 29 |
-
"gpt-4o": ModelConfig(
|
| 30 |
-
model_id="gpt-4o",
|
| 31 |
-
provider="openai",
|
| 32 |
-
cost_per_1k_input=0.0025,
|
| 33 |
-
cost_per_1k_output=0.01,
|
| 34 |
-
latency_ms_estimate=1500,
|
| 35 |
-
strength_tier=4,
|
| 36 |
-
max_context=128000,
|
| 37 |
-
),
|
| 38 |
-
"claude-3.5-sonnet": ModelConfig(
|
| 39 |
-
model_id="claude-3-5-sonnet-20241022",
|
| 40 |
-
provider="anthropic",
|
| 41 |
-
cost_per_1k_input=0.003,
|
| 42 |
-
cost_per_1k_output=0.015,
|
| 43 |
-
latency_ms_estimate=1200,
|
| 44 |
-
strength_tier=3,
|
| 45 |
-
max_context=200000,
|
| 46 |
-
),
|
| 47 |
-
"claude-3.5-haiku": ModelConfig(
|
| 48 |
-
model_id="claude-3-5-haiku-20241022",
|
| 49 |
-
provider="anthropic",
|
| 50 |
-
cost_per_1k_input=0.00025,
|
| 51 |
-
cost_per_1k_output=0.00125,
|
| 52 |
-
latency_ms_estimate=300,
|
| 53 |
-
strength_tier=2,
|
| 54 |
-
max_context=200000,
|
| 55 |
-
),
|
| 56 |
-
"deepseek-chat": ModelConfig(
|
| 57 |
-
model_id="deepseek-chat",
|
| 58 |
-
provider="deepseek",
|
| 59 |
-
cost_per_1k_input=0.00014,
|
| 60 |
-
cost_per_1k_output=0.00028,
|
| 61 |
-
latency_ms_estimate=800,
|
| 62 |
-
strength_tier=3,
|
| 63 |
-
max_context=64000,
|
| 64 |
-
cache_discount_rate=0.5,
|
| 65 |
-
),
|
| 66 |
-
"local-qwen-7b": ModelConfig(
|
| 67 |
-
model_id="Qwen/Qwen2.5-7B-Instruct",
|
| 68 |
-
provider="local",
|
| 69 |
-
cost_per_1k_input=0.0,
|
| 70 |
-
cost_per_1k_output=0.0,
|
| 71 |
-
latency_ms_estimate=600,
|
| 72 |
-
strength_tier=3,
|
| 73 |
-
max_context=131072,
|
| 74 |
-
),
|
| 75 |
-
},
|
| 76 |
-
tools={
|
| 77 |
-
"search": ToolConfig("search", 0.002, 500, cacheable=False),
|
| 78 |
-
"code_execution": ToolConfig("code_execution", 0.005, 1000, requires_verification=True),
|
| 79 |
-
"file_read": ToolConfig("file_read", 0.0005, 100, cacheable=True),
|
| 80 |
-
"linter": ToolConfig("linter", 0.001, 200),
|
| 81 |
-
"document_retrieval": ToolConfig("document_retrieval", 0.001, 300, cacheable=True),
|
| 82 |
-
"compliance_check": ToolConfig("compliance_check", 0.01, 1500, requires_verification=True),
|
| 83 |
-
},
|
| 84 |
-
verifiers={
|
| 85 |
-
"verifier_medium": VerifierConfig("claude-3.5-haiku", 0.005, 800, 0.8),
|
| 86 |
-
},
|
| 87 |
-
routing_policy=RoutingPolicy("demo"),
|
| 88 |
-
)
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
def demo_task(optimizer: AgentCostOptimizer, request: str, expected_difficulty: int = 3):
|
| 92 |
-
"""Run ACO optimization for a single task and show decisions."""
|
| 93 |
-
|
| 94 |
-
print(f"\n{'='*80}")
|
| 95 |
-
print(f"TASK: {request}")
|
| 96 |
-
print(f"{'='*80}")
|
| 97 |
-
|
| 98 |
-
# Build run state for a fresh task
|
| 99 |
-
run_state = {
|
| 100 |
-
"trace_id": f"demo-{hash(request) % 10000:04d}",
|
| 101 |
-
"planned_tools": [("file_read", {"path": "project.md"}), ("code_execution", {"code": "test"})],
|
| 102 |
-
"previous_tool_calls": [],
|
| 103 |
-
"current_cost": 0.0,
|
| 104 |
-
"step_number": 1,
|
| 105 |
-
"total_steps": 3,
|
| 106 |
-
"is_irreversible": False,
|
| 107 |
-
"context_pieces": {
|
| 108 |
-
"system_rules": "You are a helpful coding assistant.",
|
| 109 |
-
"tool_descriptions": "Available: file_read, code_execution, linter",
|
| 110 |
-
"user_preferences": "Prefer Python, type hints, docstrings",
|
| 111 |
-
"recent_messages": "",
|
| 112 |
-
},
|
| 113 |
-
"retrieved_docs": [],
|
| 114 |
-
"routing_mode": "cascade",
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
# Call optimizer
|
| 118 |
-
result = optimizer.optimize(request, run_state)
|
| 119 |
-
|
| 120 |
-
# Display decisions
|
| 121 |
-
print(f"\n📊 OPTIMIZATION DECISIONS")
|
| 122 |
-
print(f" Trace ID: {result.trace_id}")
|
| 123 |
-
print(f" Estimated Cost: ${result.estimated_cost:.4f}")
|
| 124 |
-
print(f" Estimated Latency: {result.estimated_latency_ms:.0f}ms")
|
| 125 |
-
print(f" Confidence: {result.confidence:.2f}")
|
| 126 |
-
print(f"\n 🎯 Model Routing")
|
| 127 |
-
print(f" Selected: {result.routing_decision.model_id} (tier {result.routing_decision.tier})")
|
| 128 |
-
print(f" Provider: {result.routing_decision.provider}")
|
| 129 |
-
print(f" Max Tokens: {result.routing_decision.max_tokens}")
|
| 130 |
-
print(f" Temperature: {result.routing_decision.temperature}")
|
| 131 |
-
print(f" Reasoning: {result.routing_decision.reasoning}")
|
| 132 |
-
if result.routing_decision.fallback_model_id:
|
| 133 |
-
print(f" Fallback: {result.routing_decision.fallback_model_id}")
|
| 134 |
-
|
| 135 |
-
if result.context_budget:
|
| 136 |
-
cb = result.context_budget
|
| 137 |
-
print(f"\n 📄 Context Budget ({cb.total_budget_tokens:,} tokens)")
|
| 138 |
-
print(f" Prefix (cacheable): {cb.cache_prefix_tokens:,} tokens")
|
| 139 |
-
print(f" Suffix (dynamic): {cb.dynamic_suffix_tokens:,} tokens")
|
| 140 |
-
if cb.omitted_sources:
|
| 141 |
-
print(f" Omitted: {[s.name for s in cb.omitted_sources]}")
|
| 142 |
-
if cb.summarized_sources:
|
| 143 |
-
print(f" Summarized: {[s.name for s, _ in cb.summarized_sources]}")
|
| 144 |
-
if cb.retrieval_queries:
|
| 145 |
-
print(f" Retrieval: {cb.retrieval_queries}")
|
| 146 |
-
|
| 147 |
-
if result.prompt_layout:
|
| 148 |
-
pl = result.prompt_layout
|
| 149 |
-
print(f"\n 💾 Cache Layout")
|
| 150 |
-
print(f" Cold Cost: ${pl.estimated_cold_cost:.4f}")
|
| 151 |
-
print(f" Warm Cost: ${pl.estimated_warm_cost:.4f}")
|
| 152 |
-
print(f" Cache Discount: ${pl.cache_discount:.4f}")
|
| 153 |
-
|
| 154 |
-
print(f"\n 🔧 Tool Decisions ({len(result.tool_decisions)} tools)")
|
| 155 |
-
for td in result.tool_decisions:
|
| 156 |
-
icon = "✅" if td.decision.value in ("use", "batch", "parallel") else "❌"
|
| 157 |
-
print(f" {icon} {td.tool_name}: {td.decision.value} (cost: ${td.estimated_cost:.4f}, benefit: {td.estimated_benefit:.2f})")
|
| 158 |
-
|
| 159 |
-
if result.verifier_decision:
|
| 160 |
-
vd = result.verifier_decision
|
| 161 |
-
print(f"\n 🔍 Verifier Decision")
|
| 162 |
-
print(f" Decision: {vd.decision.value}")
|
| 163 |
-
print(f" Checks: {vd.checks}")
|
| 164 |
-
print(f" Estimated Cost: ${vd.estimated_verifier_cost:.4f}")
|
| 165 |
-
|
| 166 |
-
if result.meta_tool_match:
|
| 167 |
-
mm = result.meta_tool_match
|
| 168 |
-
print(f"\n ⚡ Meta-Tool Match")
|
| 169 |
-
print(f" ID: {mm['meta_tool_id']}")
|
| 170 |
-
print(f" Est. Savings: ${mm['estimated_cost_savings']:.4f}")
|
| 171 |
-
|
| 172 |
-
if result.doom_assessment:
|
| 173 |
-
da = result.doom_assessment
|
| 174 |
-
print(f"\n ⚠️ Doom Assessment")
|
| 175 |
-
print(f" Action: {da.action.value}")
|
| 176 |
-
print(f" Confidence: {da.confidence:.2f}")
|
| 177 |
-
if da.signals_triggered:
|
| 178 |
-
print(f" Signals: {da.signals_triggered}")
|
| 179 |
-
|
| 180 |
-
# Simulate execution
|
| 181 |
-
print(f"\n🎬 SIMULATED EXECUTION")
|
| 182 |
-
model_cost = (result.routing_decision.max_tokens / 1000) * optimizer.config.models[result.routing_decision.model_id].cost_per_1k_input
|
| 183 |
-
tool_cost = sum(d.estimated_cost for d in result.tool_decisions if d.decision.value in ("use", "batch"))
|
| 184 |
-
verifier_cost = result.verifier_decision.estimated_verifier_cost if result.verifier_decision else 0.0
|
| 185 |
-
total_cost = model_cost + tool_cost + verifier_cost
|
| 186 |
-
|
| 187 |
-
print(f" Model call: ${model_cost:.4f}")
|
| 188 |
-
print(f" Tool calls: ${tool_cost:.4f}")
|
| 189 |
-
print(f" Verifier: ${verifier_cost:.4f}")
|
| 190 |
-
print(f" TOTAL: ${total_cost:.4f}")
|
| 191 |
-
|
| 192 |
-
# Estimate what frontier-only would cost
|
| 193 |
-
frontier_cfg = optimizer.config.models.get("gpt-4o")
|
| 194 |
-
if frontier_cfg:
|
| 195 |
-
frontier_cost = (result.routing_decision.max_tokens / 1000) * frontier_cfg.cost_per_1k_input + tool_cost + verifier_cost
|
| 196 |
-
savings = frontier_cost - total_cost
|
| 197 |
-
print(f"\n💰 vs Frontier Model (gpt-4o)")
|
| 198 |
-
print(f" Frontier cost: ${frontier_cost:.4f}")
|
| 199 |
-
print(f" Savings: ${savings:.4f} ({savings/max(frontier_cost,0.001)*100:.1f}%)")
|
| 200 |
-
|
| 201 |
-
return result
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
def main():
|
| 205 |
-
print("=" * 80)
|
| 206 |
-
print("AGENT COST OPTIMIZER - End-to-End Demo")
|
| 207 |
-
print("=" * 80)
|
| 208 |
-
|
| 209 |
-
config = build_demo_config()
|
| 210 |
-
optimizer = AgentCostOptimizer(config)
|
| 211 |
-
|
| 212 |
-
tasks = [
|
| 213 |
-
("What is the capital of France?", 1),
|
| 214 |
-
("Write a Python function to reverse a linked list", 3),
|
| 215 |
-
("Research the latest advancements in transformer architectures and summarize key findings", 4),
|
| 216 |
-
("Review this contract for liability clauses and check GDPR compliance", 5),
|
| 217 |
-
("Help me with this thing", 3),
|
| 218 |
-
("Debug this segfault in our C++ thread pool implementation", 4),
|
| 219 |
-
("Draft an email to the team about the deployment schedule for next week", 2),
|
| 220 |
-
("Plan a 3-month roadmap for migrating our ML infrastructure to Kubernetes", 4),
|
| 221 |
-
("Search for open issues in the repo and create a summary report", 2),
|
| 222 |
-
("Query the database for Q3 sales data broken down by region, then produce a chart", 3),
|
| 223 |
-
]
|
| 224 |
-
|
| 225 |
-
results = []
|
| 226 |
-
for request, difficulty in tasks:
|
| 227 |
-
result = demo_task(optimizer, request, difficulty)
|
| 228 |
-
results.append({
|
| 229 |
-
"request": request,
|
| 230 |
-
"model": result.routing_decision.model_id,
|
| 231 |
-
"tier": result.routing_decision.tier,
|
| 232 |
-
"estimated_cost": result.estimated_cost,
|
| 233 |
-
"verifier": result.verifier_decision.decision.value if result.verifier_decision else "none",
|
| 234 |
-
})
|
| 235 |
-
|
| 236 |
-
# Summary
|
| 237 |
-
print(f"\n{'='*80}")
|
| 238 |
-
print("SUMMARY")
|
| 239 |
-
print(f"{'='*80}")
|
| 240 |
-
total_est = sum(r["estimated_cost"] for r in results)
|
| 241 |
-
print(f"Total estimated cost for {len(tasks)} tasks: ${total_est:.4f}")
|
| 242 |
-
|
| 243 |
-
# Show model distribution
|
| 244 |
-
from collections import Counter
|
| 245 |
-
model_counts = Counter(r["model"] for r in results)
|
| 246 |
-
print(f"\nModel distribution:")
|
| 247 |
-
for model, count in model_counts.most_common():
|
| 248 |
-
print(f" {model}: {count} tasks ({count/len(tasks)*100:.0f}%)")
|
| 249 |
-
|
| 250 |
-
print(f"\n✅ Demo complete!")
|
| 251 |
-
print(f" Repo: https://huggingface.co/narcolepticchicken/agent-cost-optimizer")
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
if __name__ == "__main__":
|
| 255 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|