feat(examples): add evaluation + optimization closed-loop pipeline - #284
feat(examples): add evaluation + optimization closed-loop pipeline#284coder-mtj wants to merge 89 commits into
Conversation
Implements trpc-group#91 — reproducible Evaluation + Optimization pipeline: - Config loading (optimizer.json + evalsets validated) - Baseline evaluation (fake mode with trace evalsets + SDK path) - Failure attribution (10 categories: tool errors, rubric, format, etc.) - Multi-dimensional gate (improvement threshold, critical cases, cost budget) - Validation set comparison (new passes/failures, overfitting detection) - JSON + Markdown report with full audit trail - 6 train+val evalset cases (3 optimizable, 1 degrading, 1 format, 1 edge) - 35 tests covering config, baseline, attribution, gate, validation, report, integration Signed-off-by: coder-mtj <coder-mtj@users.noreply.github.com>
…overage - Add pipeline/optimize.py: GEPA optimization wrapper (fake + live modes) - Add pipeline/tracing.py: audit trail with seed/timing/cost/reproduce - Add agent/ package: calculator agent for optimization testing - Update run_pipeline.py: integrate new modules, AuditTracer, enhanced CLI - Split monolithic test file into 14 focused test files - Expand from 35 to 189 tests (5.4x increase) - Add 6-dimensional test coverage: unit, integration, mock data, edge/boundary, regression, performance - Enhance evalsets: 34 train + 16 val + 12 holdout cases (multi-domain: math, reasoning, tool calls, Chinese, CJK, format) - Add DESIGN.md and README.md with architecture documentation - All 189 tests passing, pipeline verified end-to-end in fake mode
…de no-op - 新增 pipeline/comparator.py:分层评测规则(纯数字/contains/带单位/格式/工具) - 修复 run_baseline_fake 空转:比较 conversation 期望 vs actual_conversation 实际 - 归因增强:直接读取 comparator 的 category/evidence - 新增 23 个 comparator 单元测试,全量 212 tests 通过 Signed-off-by: popo <18682875253@163.com>
…valset data - 新增 tests/test_gold_verdicts.py:84 条黄金判定表锁定归因精度(≥90%) - 修复 train 数据标注错误:train_reasoning_002_fail / train_tool_002_fail 改为真正失败 - 新增 large_train.evalset.json(50 cases,17 个 _fail) - comparator 增强:货币千分位、数字子集匹配 - 全量 299 tests 通过 Signed-off-by: popo <18682875253@163.com>
…te rejection - 新增 --scenario CLI(fix_attributed/noop/overfit)演示三类验收场景 - validate.py: run_validation_trace 用 TraceMatcher 重评候选 actuals,带 per_case_results - gate.py: 候选在验证集新增失败 → REJECT(过拟合检测真实生效) - optimize.py: SCENARIOS 注册表 + candidate_strategy/fixed_categories - 修复 Windows GBK 控制台 emoji print 崩溃 - 三类场景验证:fix_attributed=ACCEPT, noop=NEEDS_REVIEW, overfit=REJECT(CI 退出码 1) Signed-off-by: popo <18682875253@163.com>
- JSON 报告新增 candidate 块(train/validation 评分 + 逐 case delta) - MD 报告新增 Candidate vs Baseline 逐 case 对比表 - 归因条目补充 evidence 字段(可解释性) - 修复 FailureCategory 枚举序列化 Signed-off-by: popo <18682875253@163.com>
- agent.py: 新增 build_call_agent()(确定性离线 CallAgent) - baseline.py: run_baseline_sdk 变 async,用 AgentEvaluator.evaluate_eval_set; SDK 失败降级到 trace comparator - optimize.py: run_optimize_live 正确 await AgentOptimizer.optimize(call_agent=...) - optimizer.json: 补充 reflection_lm 配置 - run_pipeline.py: live 模式用 asyncio.run 隔离,项目根加入 sys.path - 修复 SDK schema 不兼容时 live 模式崩溃问题 Signed-off-by: popo <18682875253@163.com>
… tests - test_scenarios.py: 三场景端到端(fix_attributed=ACCEPT, noop=NEEDS_REVIEW, overfit=REJECT) - test_attribution_accuracy.py: 归因准确率 ≥90%(验收标准 trpc-group#4) - test_live_mode_import.py: live 模式健壮性 + fake 性能 <3s - 全量 317 tests 通过 Signed-off-by: popo <18682875253@163.com>
…kage entry - pipeline/__init__.py: 统一 re-export 全部核心符号 - 支持 from pipeline import PipelineConfig, run_baseline_fake, ... - 清理 SDK live 运行产生的垃圾文件(baseline_prompts/ 等) - 317 tests 保持全绿,零回归 Signed-off-by: popo <18682875253@163.com>
…gful sample report - README: 三场景演示、工作原理、模块地图、CLI 参数、验收标准对照 - DESIGN: comparator/三场景/6 维度 gate/live 降级说明 - ai-prompts: 补充第 5 轮(trace 回放评测、三场景、过拟合拒绝) - attribution: 修复 by_category 序列化(枚举 .value) - sample_output: 有意义的默认报告(失败+归因+候选+gate ACCEPT) - .gitignore: 忽略 SDK live 运行产物 Signed-off-by: popo <18682875253@163.com>
Critical fixes: - baseline.py: pass required eval_config to evaluate_eval_set (was always falling back) - baseline.py: use EvalCaseResult.final_eval_status instead of nonexistent 'passed' - optimize.py: map SDK OptimizeResult fields correctly (total_llm_cost/total_rounds/best_prompts/rounds) Other: - config.py: add load_optimize_config() to build EvalConfig from optimizer.json - baseline/optimize/config: extract sys.path setup into named helpers (no silent except) - validate.py: move copy import to module top - run_pipeline.py: load EvalConfig in live mode - tests: add live-mode contract tests (mock SDK, verify field mapping) 319 tests pass Signed-off-by: popo <18682875253@163.com>
…erfit default (AI review round 2) - optimize.py: converged now checks SDK status == 'SUCCEEDED' (not 'accepted') - comparator.py: _compare_tools reads tool_responses (real evalset structure); numeric comparison with rounding tolerance - validate.py: overfit scenario auto-perturbs 2 val cases when --val-regression-cases empty (was mis-ACCEPT) - test_gold_verdicts: train_tool_002_fail now correctly attributed to tool_call_error - tests: mock SDK status updated to 'SUCCEEDED'; tool test cases use real data structure 319 tests pass; three scenarios give ACCEPT/NEEDS_REVIEW/REJECT Signed-off-by: popo <18682875253@163.com>
… param (AI review round 3) - run_pipeline.py: load and score --holdout-evalset via comparator; write to report audit - validate.py: remove unused fixed_categories param from _apply_scenario - three scenarios still give ACCEPT/NEEDS_REVIEW/REJECT; 319 tests pass Signed-off-by: popo <18682875253@163.com>
- tracing: keep injected reproduce_command covering all non-default CLI args instead of overwriting with the minimal mode/seed fallback - run_pipeline: CI mode exits 2 on NEEDS_REVIEW (REJECT stays 1) - gate: extend critical-case protection to validation-set regressions and wire --critical-cases CLI arg so the protection is reachable
…roup#139 - gate: compute all 6 checks before branching so audit detail survives early REJECT paths (no_degradation/critical_cases/new_failures/ overfitting/cost_budget always recorded) - optimize: converged by attribution coverage, not iteration-cap proxy - tracing: make finalize() idempotent so report and terminal duration match - comparator: use _CATEGORY_PRIORITY for MISSING_EXPECTED_OUTPUT instead of hardcoded 99 priority - run_pipeline: explicitly warn when live-mode validation/gate runs on scenario-simulated candidates (honest labeling per review Critical)
…coring - test_performance: loosen wall-clock budgets (6/50/30-case 30s, report 10s, 100-case 60s) to avoid flaky failures on busy CI runners - run_pipeline: in live mode, explicitly mark holdout as trace-comparator scored since train/val use the SDK — keeps the audit report honest
Live mode is currently SDK wiring + offline deterministic agent (fake reflection_lm), with scenario-simulated validation/gate — README now says so instead of claiming real end-to-end LLM optimization.
…n cleanups - agent: correct build_call_agent docstring — live mode uses the offline deterministic agent, real LLM not yet wired (no false 'real LLM' claim) - run_pipeline: explicitly warn when live baseline falls back to trace comparator, so fallback pass rates aren't mistaken for real SDK scoring - config: drop dead allow_no_degradation field - conftest: temp_evalset/temp_json_file now auto-clean at teardown
…eanups - baseline: skip EvalStatus.NOT_EVALUATED cases in live baseline — they are not failures, so pass_rate/gate judgment are no longer polluted - optimize: when SDK status != SUCCEEDED (FAILED/CANCELED), clear best_prompt/optimized_fields and record an error instead of reporting a failed run's 'best' as a usable artifact - baseline: preserve exception type in fallback error message - agent/config: move import re to module top; drop unused Optional import - tests: add NOT_EVALUATED skip + non-SUCCEEDED cleanup coverage
…ests - run_pipeline: reject '..' traversal in --output-dir (CI arbitrary-write risk) - run_pipeline: shell-quote string args in reproduce_command (shlex.quote) - test_pipeline_overfit: make early-stop test actually assert REJECT on validation_new_failures instead of ending at a comment - test_live_mode_import: replace tautological 'never raises' tests with deterministic SDK-missing fallback assertions - test_run_pipeline_helpers: cover space-in-path quoting
…trap - baseline.run_baseline_sdk: narrow fallback catch to (ValueError/KeyError/ TypeError) so pipeline bugs (AttributeError etc.) propagate instead of being masked as 'SDK failure' and silently scored by trace comparator - baseline.run_baseline_fake: parse broken JSON gracefully into errors, consistent with missing-file handling (was raising JSONDecodeError) - extract duplicated _ensure_repo_root_in_path/_ensure_import_paths into pipeline/_paths.py (single source, no behavior change); drop now-unused sys imports - tests: lock run_baseline_fake invalid-JSON contract; assert non-SDK exceptions re-raise
… scope - run_pipeline: resolve --output-dir to absolute and require it under the repo root (rejects '..' traversal AND external absolute paths like /tmp); extracted is_output_dir_allowed() for testability - comparator: format layer now only checks 'ONLY-number' (json/markdown detection was dead code never enforced); docs updated to match so the documented promise and behavior agree - tests: cover output-dir containment (accept in-repo, reject /tmp and traversal) and update format tests
…e check - baseline/config: import EvalStatus / load_optimize_config from the public trpc_agent_sdk.evaluation namespace instead of private _eval_metrics / _optimize_config, so an SDK internal refactor won't ImportError and silently degrade live mode to fake scoring - attribution: _attribute_from_cases now flags pass=False cases missing from failed_case_ids too, matching _extract_failures - tests: _FakeEvalModule mocks now expose EvalStatus for the public import
- run_pipeline: wrap live run_baseline_sdk/run_optimize_live asyncio.run calls in try/except so uncaught SDK exceptions (RuntimeError etc.) degrade to errors/fake like fake mode instead of crashing the pipeline - run_pipeline: in live mode, downgrade overfitting REJECT to NEEDS_REVIEW since baseline=SDK and candidate=trace-comparator scoring are not directly comparable (avoids incomparable scores blocking CI) - gate: document baseline_metrics/candidate_metrics as audit-only (they do not participate in decisions)
…y threshold docs - validate: overfit scenario now raises a clear error when val set is empty or the selected regression cases produce no new_fail (would otherwise be silently ACCEPTed, violating acceptance criterion trpc-group#3) - test_gold_verdicts: clarify docstrings that the test locks >=90%, stricter than the >=75% acceptance criterion (was internally inconsistent) - tests: cover empty-val and no-regression overfit edge cases
…warning - optimize: clear best_prompt/optimized_fields and record an error whenever SDK status != SUCCEEDED, including when status is empty/missing (previously the empty case silently kept the failed run's 'best') - run_pipeline: baseline fallback/degradation messages now go to audit warnings only, not audit.errors (they are expected behavior, not fatal) - comparator: remove dead _STRIP_CHARS constant - tests: cover empty-status artifact cleanup
…und cap, strict subdir - run_pipeline: live mode now downgrades ACCEPT to NEEDS_REVIEW too (not just overfitting REJECT) since baseline=SDK vs candidate=trace-comparator scores are not comparable - run_pipeline: catch ValidationError from overfit/empty-val guard and degrade to a non-ACCEPT result instead of crashing; report still generated - optimize: cap overfit max_rounds at len(categories_to_fix) so the modulo indexing can't 'fix' the same category repeatedly and skew convergence - is_output_dir_allowed: require strict subdirectory of repo root (reject writing reports directly to the root) - tests: cover repo-root rejection
The live graceful-degradation blocks did 'from pipeline.baseline import run_baseline_fake' / 'from pipeline.optimize import OptimizeResult' inside main(). Python treats a from-import as a local binding for the whole function, so the fake-mode path referenced run_baseline_fake before it was assigned → UnboundLocalError crash. Both names are already imported at module top; removed the inner imports. Verified fake (ACCEPT) and live (degrade) both exit 0.
…ent loop - baseline.run_baseline_sdk: aggregate results per case_id (dedupe) instead of per-run, so total_cases/passed/failed are consistent and failed_case_ids has no duplicates when the SDK returns multiple runs per case (NUM_RUNS>1) - run_pipeline: run train/val live baselines in one asyncio.run(asyncio.gather) instead of two separate event loops (avoids loop-reuse/resource warnings)
AI Code Review我已经有了足够的覆盖。测试很完善,并通过 在最终确定前,让我再核实一个关于 再核实一下: 现在开始撰写审查报告。 发现的问题🚨 Critical未发现必须修复的阻塞性问题。代码整体防御性强:空值、除零、负成本、场景边界、SDK 不可用降级、空候选回归防护(
|
…surfacing - comparator: TraceMatcher.numeric_tolerance / contains_len_threshold 真正透传 到 compare_invocations(此前硬编码 1e-6/20,自定义参数被静默忽略) - run_pipeline: Stage 7 写入前 realpath 再校验 output_dir(TOCTOU 窗口防护) - agent: _call 检查 _live_run 的 error 键,存在则抛 RuntimeError 而非静默 返回空串当合法结果 - 补测试:TraceMatcher(numeric_tolerance=0.01) 自定义容差生效
AI Code Review基于我对 PR diff 和仓库上下文的全面审查,我的结论如下。 发现的问题🚨 Critical
|
| class TestPipelineStress: | ||
| """Stress tests for full pipeline under load.""" | ||
|
|
||
| def test_full_pipeline_50_cases(self, temp_json_file): |
There was a problem hiding this comment.
test_full_pipeline_50_cases 跳过真实验证阶段,断言恒真
Stage 5-6 未调用 run_validation_trace/run_validation_fake,而是用 candidate_pass_rate=min(1.0, bl_train.pass_rate+0.2) 与 candidate_failed=[] 合成必然通过的候选喂给 evaluate_gate,绕开过拟合检测。comparator/validate/gate 的回归均无法被捕获,应改用真实 candidate_train 过 gate。
… guard - test_full_pipeline_50_cases 走真实 run_validation_fake + gate,不再合成必然通过的候选(reviewer Critical) - optimize.py: timeout<=0/非数值回退默认;SDK AgentOptimizer 抛 AttributeError 降级记 error 而非上抛(reviewer Warning) - run_pipeline: except (AttributeError, TypeError) 注释澄清仅 pipeline 自身 bug 逃逸 - live 测试显式传仓库内 --output-dir,消除 CWD 依赖;TestBuildCallAgent 断言收紧 + error 路径契约(reviewer Warning/Suggestion) - 归因测试精确锁定类别数,删过时注释(reviewer Warning)
AI Code Review我已经完成了全面审查,以下是我的最终评估。 发现的问题🚨 Critical(无)
|
… config + symlink-safe report write - build_reproduce_command now compares against parser.get_default() instead of hardcoded literals, so argparse/PipelineConfig default drift can't silently corrupt the audit reproduce_command (reviewer Warning) - load_pipeline_config raises TypeError on unknown override keys instead of silently dropping them (reviewer Warning) - report file paths realpath-checked before write to avoid following a pre-placed symlink out of the repo (reviewer Suggestion)
✅ Ready for review — PR #284 交付说明@helloopenworld 请 review 确认(head Head: 交付内容:可复现的 Evaluation + Optimization 闭环 pipeline( CI:8/8 checks green on 本地验证:399 tests 全绿( 本轮保守稳健性修复:
请 review,谢谢。 |
AI Code Review我已经有了完整的认识,让我来撰写最终的审查报告。 发现的问题🚨 Critical未发现 Critical 级阻塞问题。代码逻辑自洽,关键边界(空候选、不可扰动 case、overfit 空 val、SDK 不可用降级、输出路径越界/符号链接)均有显式处理与测试覆盖;与 SDK 公开导出(
|
…load errors + score metric label + negation window + single FailureCategory - run_pipeline live baseline now re-raises AttributeError/TypeError like the optimize stage, instead of silently degrading pipeline bugs to trace comparator (reviewer Warning) - evalset load failures raise EvalsetLoadError (ValueError subclass) and are reported as hard errors instead of masquerading as scenario config errors - best_score metric annotated (live=validation_pass_rate vs fake=train) in report/audit and console - comparator negation scan extends to the next '=' candidate (was fixed 80-char window) so far negations are caught; regression tests added - FailureCategory unified to comparator as single source (was duplicated as str subclass vs str,Enum)
AI Code Review审查已完成。正在撰写审查报告。 发现的问题🚨 Critical
|
| print(f" Scenario: {cfg.scenario}") | ||
| print(f" Iterations: {optimize_result.total_iterations}") | ||
| print(f" Best score: {optimize_result.best_score:.3f} " | ||
| f"({_best_score_metric})") |
There was a problem hiding this comment.
Stage 4 引用未定义变量 _best_score_metric 导致 pipeline 必崩
Stage 4 打印处使用了 _best_score_metric,但该变量直到 Stage 7(约 651 行)才赋值。main() 为局部作用域,执行到 510 行必然抛 UnboundLocalError,使默认 --mode fake 入口运行到 Stage 4 即崩溃。建议将 _best_score_metric 的计算上移到 Stage 4 打印之前。
…() e2e test - _best_score_metric was referenced in the Stage 4 console print but only assigned at Stage 7, causing UnboundLocalError on every fake/live run (reviewer Critical); compute it before the print - add end-to-end test that runs rp.main() in fake mode through all 7 stages and asserts exit 0 + report artifacts, locking the main entrypoint (reviewer Warning: no fake main() coverage existed)
AI Code Review这里有一个微妙的问题: 然而,确实存在一个具体的漏洞: 现在,有一个具体的问题: 好了,我已经进行了彻底的审查。代码防御性很强,测试充分,且 SDK 集成已验证。让我整理一下我的发现。真正可报告的问题非常有限。让我找出仅有的几个合理的问题: 让我重新检查一个真正的问题:在 让我再检查一下 我现在对整体情况已经完全了解了。让我写下审查结果,重点关注真正可验证的、实质性的问题。老实说,这段代码质量很高。让我重新考虑一下什么才是真正可报告的: 再检查一次 —— 现在让我再检查一个真正的问题 —— 在 让我最终完成。我将报告有限且真正可验证的发现。鉴于代码的高质量,我将非常具体地说明少数真正的问题。 让我重新考虑 现在,一个真正的具体问题:在 现在让我完成审查并撰写审查结果。我将报告我能够验证的具体问题,即使它们很轻微,并将那些不构成问题的事项标记为已验证。我已完整审查 发现的问题🚨 Critical
|
…+ structured best_score - audit.holdout now carries scored_via=trace_comparator (+ not_comparable_note in live) so report consumers can't treat it as comparable to SDK baseline (reviewer W) - AgentOptimizer writes to output_dir/sdk_artifacts/ instead of mixing with the pipeline's own reports (reviewer W) - optimizer.best_score_info exposes value+metric together so fake(train) vs live(validation) scores can't be compared bare (reviewer W) - baseline SDK return-shape check split type/length for clarity (reviewer S) - fake main() e2e test now asserts holdout.scored_via and best_score_info.metric
AI Code Review我现在已经完全理解了。让我来写最终的审查意见。基于我深入的分析,代码非常成熟,在之前的审查周期中已经解决了许多问题(从注释中可见)。剩余可验证的问题如下: 发现的问题
|
… narrow SDK except + score caliber note - ci.yml test job now runs pytest on examples/optimization/eval_optimize_loop/tests/ so the 400+ acceptance tests actually gate regressions (reviewer W1) - scenario-config-error path no longer prints/records 'Overfitting detected' (was a synthesized new_fail delta, not real overfit) (reviewer W2) - AgentOptimizer args are assembled outside the SDK except AttributeError block so pipeline-side AttributeErrors propagate instead of masquerading as SDK issues (reviewer W3) - SDK-path metric_breakdown notes that final_response_avg_score falls back to pass_rate, distinct from fake per-case mean (reviewer S) - new tests: overfit scenario-error no-misreport e2e + optimize config-attr propagation
AI Code Review
我已经有了足够的验证。让我来撰写最终的审查意见。我会精简内容,只保留真正有价值且已验证的问题。 发现的问题
|
…l CWD dep
test_fake_pipeline_main_end_to_end asserted audit.holdout.scored_via but argv
did not pass --holdout-evalset, so cfg.holdout_evalset stayed relative
('data/holdout.evalset.json') and resolved under CWD: local run from
eval_optimize_loop found the file, but CI runs pytest from the repo root where
it doesn't exist -> holdout never scored -> scored_via=None -> test failed.
Now all main() argv blocks pass the absolute holdout path, removing the CWD
dependency. Caught by the CI wiring added in 3492385 (reviewer W1 payoff).
AI Code Review我现在已经有了完整的了解。让我来撰写审查报告。 发现的问题🚨 Critical未发现 Critical 级别问题。核心数据流(baseline → attribution → optimize → validate → gate → report)的状态流转、异常分流(ImportError 降级 vs ValueError/AttributeError 上抛)、SDK 返回结构解包(4-tuple 校验、
|
✅ CI 全绿 — head
|
- optimize.py: anchor live train/val relative paths to example dir (与 prompt_dir 同口径),避免从仓库根跑 live 时 SDK FileNotFoundError 被 except 静默降级为失败 (reviewer W1) - OptimizeResult.best_score_metric 随结果携带口径,run_pipeline 单一来源 引用,不再按 cfg.mode 二次推导 (reviewer W2) - baseline.py: SDK 路径兜底字段改名 final_response_avg_score_approximated, 与 fake 路径 per-case 均值同名字段隔离 (reviewer W3) - tests: +5 覆盖路径锚定/口径标注/字段隔离 (412 total)
AI Code Review发现的问题🚨 Critical
|
…epo-root/fallback paths Reviewer Critical + 4 Warnings (helloopenworld @ 542d3f5): - Critical: getattr default only fires when attr missing; SDK returning total_llm_cost/total_rounds/validation_pass_rate=None leaked None into total_cost/score -> add_cost 'if usd < 0' and best_score max() threw TypeError, masked by broad except as empty result. Add _sdk_num_or_zero. - W1: sdk_output_dir now anchored to example dir like prompt/train/val, keeps SDK artifacts out of repo root (gitignore scope). - W2: find_repo_root gains require_pkg to prefer the marker that actually contains trpc_agent_sdk, avoids wrong repo root in nested pyproject layout. - W3: trace-comparator fallback for live baseline wrapped in its own try/except -> empty BaselineResult instead of crashing Stage 2. - W4: candidate-segment boundary uses (?<![<>!=])=(?!=) so ==/>=/<=/!= '=' are not treated as candidate starts (negation context no longer truncated by comparison operators). +5 tests (417 total green).
AI Code Review发现的问题
|
…swer candidates, smoke-test default entrypoint Reviewer Warnings (helloopenworld @ 9017b5b): - W1: holdout evalset was resolved by CWD (os.path.exists/run_baseline_fake), silently skipped from repo root; now anchored to example dir like prompt/train/val. - W2: eq_candidates regex treated '=' inside >=/<=/!=/== as an answer equals, so 'x == 42, answer = 10' could match expected 42; now requires independent assignment '=' via (?<![<>!=])=(?!=), consistent with segment boundary. - W3: CI never exercised the documented default entrypoint from the example dir (relative data/... paths); add smoke-test step with working-directory. +2 tests (419 total green).
AI Code Review
让我确认一下是否值得查看示例文件的 gitignore 以及 现在我来撰写最终的审查报告。我还需要复核一下子智能体提到的 发现的问题
|
✅ CI 全绿 — head
|
Summary | 概述
This PR adds a reproducible Evaluation + Optimization closed-loop pipeline under
examples/optimization/eval_optimize_loop/(Tencent RhinocerosBird issue #91).
The pipeline automates the full loop for prompt evaluation and optimization:
baseline.py,comparator.py) — evaluate an eval-set with aTraceMatcher, produce per-case pass/fail, scores, andfailure reasons. Supports
fake(offline, no LLM),trace(SDK trace replay), andlive(real LLM) modes.attribution.py) — classify each failure into one of 10 categories (e.g.missing_final_answer,wrong_answer,tool_call_error,format_error, ...) with confidence, detail and evidence. Accuracy ≥ 90% verified against a gold table(
tests/test_gold_verdicts.py).optimize.py) — runAgentOptimizerwith timeout guard and strategy bookkeeping (candidate prompt + fixed categories + cost).validate.py) — re-evaluate the candidate on a held-out validation set, compute per-case deltas (new_pass/new_fail/unchanged), and detect overfitting (train improves while val regresses).gate.py) — quality / cost / budget / time / scenario checks that decideaccept/reject/needs review.report.py) — JSON and Markdown reports with seeds, duration, cost, reproduce command, gate checks and attributionbreakdown.
All run in three modes (
fake/trace/live), configurable via CLI (run_pipeline.py) orpipeline/config.py.Related Issue | 关联 Issue
Fixes #91
Change Type | 修改类型
How to Use | 使用方法
Test Plan | 测试计划
eb2cb75(build / test / lint / review / codecov / scan / external / CLA)Status | 交付状态
✅ Ready for review — all CI checks green, full local suite passing (419 tests).
Conservative robustness fixes landed in this branch:
run_validation_fakepath (no tautological gate assertions)AttributeError/ValueError/ timeouts) degrade to recordederrors instead of crashing the pipeline, with contract tests pinning the behavior
argparse(parser.get_default), so auditreproduce_commandcan't silently drift from the CLI defaultsPipelineConfigoverride keys raiseTypeErrorinstead of being silently ignoredLatest round (head eb2cb75):
run_optimize_live: normalize None SDK fields (total_llm_cost/total_rounds/validation_pass_rate)so add_cost's
if usd < 0and best_score's max() never hit None -> real live artifacts no longer maskedsdk_output_diranchored to example dir (like prompt/train/val), keeps artifacts out of repo rootfind_repo_root(require_pkg="trpc_agent_sdk")prefers marker containing the SDK in nested-pyproject layouts(?<![<>!=])=(?!=)so ==/>=/<=/!= are not candidate startsLatest round (head eb2cb75):
'=' in >=/<=/!=/== no longer misread as answer candidates