feat: 新增评测-优化自动回归与提示词优化闭环示例 (#91)#205
Conversation
|
CLA Assistant Lite bot: I have read the CLA Document and I hereby sign the CLA turrillcerza seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #205 +/- ##
==========================================
Coverage ? 87.90237%
==========================================
Files ? 479
Lines ? 44984
Branches ? 0
==========================================
Hits ? 39542
Misses ? 5442
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
AI Code Review我已经了解足够多的上下文。让我仔细检查一下
让我检查一个微妙的问题:在 让我核实一下 现在让我检查一下 FakeAgentModel 流式传输的问题: 让我检查一个潜在的 bug:在 关键问题:在 让我核实一下用于验证 现在,有一个真正令人担忧的问题: 让我检查一下 FakeJudgeModel 的 现在让我检查一个关于 实际上,在 让我检查一个具体问题: 现在,关于并发计数器重置的一个真正担忧: 但是等等 —— 在测试中, 现在让我找出真正有影响的问题。让我更仔细地检查一下 让我检查 现在让我检查 现在让我检查 让我核实一下 no_effect/overfit 在 all+apply 中是否在成功之后运行。在 让我通过检查 no_effect 场景的需求来确认:no_effect 优化器提出 success=no_effect 候选(基准的文案编辑)。基准仍然是原始的 让我再检查一下:在 现在让我考虑一下 让我看看 让我检查 现在, 等等 —— 让我看看当 实际上在样本输出中,失败案例显示 但为什么 rubric_verdicts 为空?让我检查 让我专注于可验证的问题。让我检查 现在让我检查一个关于 让我检查一个真正的担忧: 让我检查 现在,关于 让我最后核实一下关于 现在让我检查一下当 budget 为 None 时, 让我检查 现在, 让我检查一个关于过拟合防御使用 现在让我检查关于 现在让我检查 现在让我检查当 system_instruction 为 None 时的 让我检查一个关于 现在, 现在我认为我有足够的发现。让我汇总一下。让我检查 让我再核实一个潜在的实际问题:在 让我检查 让我核实一下在 e2e 测试中是否对 现在让我更仔细地检查 让我看一个真正的问题:在 好的,让我再检查一个关于 让我检查 现在让我考虑一下 让我核实一个关于 现在, 让我检查一个真正的问题: 让我检查 现在让我最终确定。最具影响力的可验证问题:
让我再看一个:在 但有一个微妙的问题:当备忘录命中且 现在,关于 好的。让我再核实一下 让我再检查一下:问题是否仅限于 让我检查是否还有其他真正的 Critical 问题。让我重新检查 让我检查一个关于 让我检查当 我现在对我的发现很有信心。我也检查一下 README 中的声明与实际行为是否一致,但这并不是代码问题。 我也核实一下 最后一个真正的担忧:在 让我检查 但有一个微妙的问题: 现在让我写下审查报告。让我核实 我也检查一下:是否有一个问题,即 让我再检查一个潜在的实际问题: 但等等 —— val 包含“介绍一下杭州”。在过拟合场景中,备忘录未命中 → 现在,关于 现在进行汇总。让我最终确定发现的问题。 实际上,让我重新考虑 还有一个: 让我也检查一下 现在让我也核实一个关于测试的真正问题: 让我写下结论。 让我仔细检查一下 让我检查一个关于 让我检查一个关于保护案例 现在最终汇总。我会将 有一个潜在的 Critical: 让我重新考虑 我将最终定稿。让我也检查样本输出报告的时间戳 让我写下最终审查报告。我已经读完了 发现的问题
|
新增 examples/optimization/eval_optimize_loop/:把 AgentEvaluator 与 AgentOptimizer 拼成「baseline 评测 → 失败归因 → GEPA 优化 → 独立验证集 回归 → 可配置 gate 决策 → 审计落盘」六阶段闭环,回答候选 prompt 是否 值得接受。零 SDK 改动、零 API Key(fake agent/judge/reflection 三个确定 性 provider 走 ModelRegistry 正则路由)、trace 模式支持,三场景端到端 远低于 3 分钟。 - 6 条评测 case(3 train + 3 val),内置 success / no_effect / overfit 三类结局场景;overfit 用「泄漏调参集 + 查表记忆候选」机理构造, 过拟合守卫(train↑∧val↓)+ 保护 case + 新增 hard fail 三门齐拒 - 六类失败归因只依赖框架 metric 结果结构,每个失败 case 保证至少 一条带证据的中文理由;合成矩阵 12/12、gate 决策矩阵 12/12 - optimization_report.json/.md 双格式 + 契约校验器(--check),报告含 baseline/candidate 分数、逐 case delta、gate 决策与接受/拒绝理由 - 审计:SDK 原生 rounds/、config.snapshot.json(含 seed)、run.log + pipeline 侧逐 case 记录、归因明细、配置快照;候选换入 try/finally 快照还原,写回源文件需 gate 通过且显式 --apply - 59 个 pytest(fake 模型、归因、闸门、e2e/trace/确定性/时限)+ 中英双 README + 471 字 DESIGN.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b2a6ec9 to
9471072
Compare
PR: feat: 新增评测-优化自动回归闭环示例 eval_optimize_loop (#91)
feat/91-eval-optimize-loop(worktree/home/seat1163/work/Tencent/issue-91)9471072bca6f4c36aac0083650c9b7b25ef2c062(base:099b571= upstream default-branch tip, "feat: 支持动态创建子agent能力 (feat: 支持动态创建子agent能力 #109)")/home/seat1163/work/Tencent/docs/patches/issue-91/0001-feat-91.patch(已在空树上git apply --check验证可干净应用)examples/optimization/eval_optimize_loop/(43 files changed, +7185,全部新增);零 SDK 文件改动中文
摘要
实现 issue #91 要求的「评测 → 失败归因 → prompt 优化 → 回归验证 → 产物审计」六阶段自动闭环,回答一个唯一的问题:优化器改出来的候选 prompt 到底值不值得接受? 全部代码位于新增示例目录
examples/optimization/eval_optimize_loop/,真跑 SDK 的AgentEvaluator(LocalEvalService)与AgentOptimizer(GEPA 反思循环),仅模型层通过ModelRegistry的 provider 正则路由替换为三个确定性 fake provider —— 不改一行 SDK、零 API Key、三场景全流程 < 3 分钟。交付 6 条评测 case(3 train + 3 val)、三个内置场景(优化成功 / 优化无效 / 优化后过拟合退化)、JSON+Markdown 双格式报告与契约校验器、65 个 pytest、中英双语 README 与 471 字方案设计说明。动机
AgentOptimizer能自动改出分数更高的 prompt,但「分数变高」≠「值得上线」:call_agent模式下只见弱指标(SDK 硬性禁配轨迹/召回 metric),信号弱于验收套件;本 PR 把这四个风险各自做成机制:完整验收套件独立复评、过拟合守卫(train↑∧val↓ 必拒)、保护 case + 新增 hard fail 闸门、SDK 原生 rounds/ 审计目录 + pipeline 快照。
设计
关键决策(详见
DESIGN.md与README.md§7):provider_name会走ModelRegistry.create_model("{provider}/{model}")正则路由;注册fake-agent/fake-judge/fake-reflection三个确定性 provider 即可离线全跑(幂等注册,探测用固定串规避resolve的 lru_cache)。<!-- directives: ... -->注释块(output_format / unit_normalization / knowledge / memorize 四个开关),于是「改 prompt」在离线环境下产生真实的行为与分数差异,GEPA 反思循环端到端运转,而非 mock 分数。optimizer.json只配响应精确匹配(黑盒模式 SDK 禁配轨迹/召回);pipeline 复评用 4-metric 验收套件(轨迹 + 精确响应 + rubric + 知识召回)——「优化器说变好了,还要独立复评过才算数」。memorize: train_table查表复读训练/调参集样本;优化器验证集换成泄漏 probe 集(其视角 0/3→3/3 严格提升);独立 val 复评 1/3→0/3。过拟合守卫 + 保护 case + 新增 hard fail 三门齐拒,拒绝理由按严重度点名根因闸门。TargetPrompt.write_all(原子写)+ try/finally 快照还原;写回源文件需要 gate 通过 ∧ 显式--apply双条件。e2e 测试断言跑完后 prompts 与快照 byte 级一致。REQUIRED_TOP_LEVEL_KEYS+validate_report(pytest 与 CLI--check共用同一实现),「失败必有理由」写进契约;配置层extra="forbid",闸门键名拼写错误 fail-fast 而非静默回落默认值。验收标准映射
run_pipeline.py(CLI 六阶段编排);data/train.evalset.json+data/val.evalset.json(3+3);loop_pipeline/report.pytests/test_pipeline_e2e.py::test_success_scenario_end_to_end(6 个 eval_id 全部在 baseline per_case,报告+审计产物逐项断言);test_report_contractloop_pipeline/gates.py六道闸门只依赖 delta/成本/时长的通用结构,阈值全部在pipeline.json可配,与 case 内容零耦合tests/test_gates.py::DECISION_MATRIX—— 12 组合成决策场景(接受 / 各类拒绝 / 开关组合)12/12 判定正确(100%)gates.py::overfit_guard(train↑∧val↓)+protected_cases+no_new_hard_fail;overfit 场景内置该结局test_pipeline_e2e.py::test_overfit_scenario_rejected(train +0.667 / val −0.333 → REJECT);test_gates.py::test_overfit_reason_mentions_overfitting(理由点名「过拟合”)loop_pipeline/attribution.py:6 类规则归因 + 「轨迹在上游」根因优先级 + metric 兜底映射;report.py::validate_report把「失败必有理由」做进契约tests/test_attribution.py::test_six_failure_types_classified—— 6 类 × 2 合成样本 12/12 全对(100%);test_fallback_guarantees_explanation_for_unknown_metric;test_every_failed_case_on_real_baseline_has_reasonloop_agent/fake_models.py)+ trace 数据集(data/trace_baseline.evalset.json,evalMode: "trace");报告内单场景进程内耗时 1.4–2.9stest_pipeline_e2e.py::test_all_scenarios_under_time_budget(三场景 <180s);test_trace_mode_baseline;独立验证时env -u OPENAI_API_KEY ...清空密钥重跑成功loop_pipeline/report.py:REQUIRED_TOP_LEVEL_KEYS契约、build_report/validate_report/render_markdown;CLI--check复用同一校验器test_report_contract(三场景报告全字段断言);破坏性验证:删顶层字段 / 清空 failure_reasons →--checkexit 1 并逐条报错六阶段具体要求逐条覆盖
loop_pipeline/evaluate.py::run_eval(AgentEvaluator.get_executer)→CaseEvalRecord(metric 分/状态/理由、rubric 明细、实际与期望工具轨迹与回答)loop_pipeline/attribution.py:wrong_tool_call / wrong_tool_args / knowledge_recall_miss / format_violation / llm_rubric_fail / final_answer_mismatchloop_pipeline/optimize.py真跑AgentOptimizer.optimize(GEPA),同时优化 system_prompt + skill 两个字段(超出「至少一个」);update_source=Falseloop_pipeline/regression.py:evaluate_candidate(write_all 换入 + finally 还原)+compute_delta(new_pass / new_fail / score_up / score_down / unchanged)pipeline.json六道闸门:提升双阈值 / 禁新增 hard fail / 保护 case / 过拟合守卫 / 成本预算 / 时长预算optimize/rounds/round_*.json、config.snapshot.json(含 seed)、run.log、baseline_prompts/、best_prompts/ + pipeline 侧 baseline/candidate 逐 case 记录、归因明细、配置快照;报告记录 best_prompts sha256data/train.evalset.json、data/val.evalset.json、optimizer.json、loop_agent/prompts/{system,skill}.mdoptimization_report.json(含 baseline / candidate / delta / gate decision / 归因统计)+optimization_report.md(人话版结论)DESIGN.md正文 471 汉字(归因 / 接受策略 / 防过拟合 / 审计四主题齐备)+ English abstract离线运行方式(无任何 API Key / 环境变量)
样例产物路径
examples/optimization/eval_optimize_loop/sample_output/success/optimization_report.{json,md}— ACCEPT(val 1/3→3/3)examples/optimization/eval_optimize_loop/sample_output/no_effect/optimization_report.{json,md}— REJECT(提升不足,全 unchanged)examples/optimization/eval_optimize_loop/sample_output/overfit/optimization_report.{json,md}— REJECT(过拟合守卫,train +0.667 / val −0.333)三份样例均为真实运行产物:独立验证时与全新重跑做结构化 diff,除时间戳/墙钟耗时字段外逐字段一致(确定性由
test_deterministic_reruns固化)。English
Summary
Implements the six-stage "evaluate → attribute failures → optimize prompts → regression-verify → audit" closed loop required by issue #91, answering one question: is the optimizer's candidate prompt actually worth accepting? Everything lives in the new example directory
examples/optimization/eval_optimize_loop/. It genuinely runs the SDK'sAgentEvaluator(LocalEvalService) andAgentOptimizer(GEPA reflection loop); only the model layer is swapped for three deterministic fake providers viaModelRegistry's provider-regex routing — zero SDK changes, zero API keys, all three scenarios end-to-end in well under 3 minutes. Ships 6 eval cases (3 train + 3 val), three built-in scenarios (successful optimization / no-effect / overfitting regression), JSON+Markdown reports with a contract validator, 59 pytest tests, bilingual READMEs, and a 471-character design note.Motivation
A higher optimizer score does not mean a candidate is production-worthy: the optimizer only sees weak metrics in black-box
call_agentmode (the SDK hard-rejects trajectory/recall metrics there); a leaked tuning set makes it overfit without noticing; without per-case deltas you cannot tell whether the gain came at the cost of previously passing critical cases; and without audit artifacts the change cannot pass production review. Each risk is turned into a mechanism: independent re-evaluation with the full acceptance metric suite, an overfit guard (train up AND val down → mandatory reject), protected-case and no-new-hard-fail gates, and SDK-native rounds/ audit output plus pipeline snapshots.Design highlights
provider_name, which routes throughModelRegistry.create_model("{provider}/{model}"); three deterministic fake providers (fake-agent,fake-judge,fake-reflection) are registered idempotently.<!-- directives: ... -->blocks in the prompts, so rewriting a prompt genuinely changes behavior and scores; the GEPA reflection loop runs end-to-end instead of being mocked.TargetPrompt.write_allwith a try/finally snapshot restore; writing back requires gate pass AND an explicit--applyflag.REQUIRED_TOP_LEVEL_KEYS+validate_reportshared by tests and the CLI--checkflag; configs useextra="forbid"so misspelled gate keys fail fast instead of silently falling back to defaults.Acceptance-criteria mapping
run_pipeline.py;data/{train,val}.evalset.json;loop_pipeline/report.pytest_success_scenario_end_to_end,test_report_contractloop_pipeline/gates.py— six gates depend only on generic delta/cost/duration structure; all thresholds configurable inpipeline.jsontest_gates.py::DECISION_MATRIX: 12 synthetic decision scenarios, 12/12 correct (100%)gates.py::overfit_guard+protected_cases+no_new_hard_fail; built-in overfit scenariotest_overfit_scenario_rejected(train +0.667 / val −0.333 → REJECT);test_overfit_reason_mentions_overfittingloop_pipeline/attribution.py(6 rule-based types + root-cause priority + fallback); enforced byvalidate_reporttest_six_failure_types_classified: 12/12 (100%);test_fallback_guarantees_explanation_for_unknown_metric;test_every_failed_case_on_real_baseline_has_reasondata/trace_baseline.evalset.json(evalMode: "trace"); in-process per-scenario time 1.4–2.9stest_all_scenarios_under_time_budget(<180s);test_trace_mode_baseline; verified with API-key env vars explicitly unsetloop_pipeline/report.pycontract + validator shared with CLI--checktest_report_contract; destructive checks (missing fields / cleared failure_reasons → exit 1)Run offline
Sample outputs:
examples/optimization/eval_optimize_loop/sample_output/{success,no_effect,overfit}/optimization_report.{json,md}— real run artifacts, deterministic (verified by structural diff against fresh reruns and bytest_deterministic_reruns).Notes for reviewers
examples/evaluation,examples/optimization).loop_agent/loop_pipelineavoidsys.modulescollisions with other examples (agent,pipeline) when pytest imports multiple examples in one process.🤖 Generated with Claude Code
评审后更新 / Post-review update(2026-07-19)
独立对抗式评审结论:0 个 major;6 个 minor 全部修复(其中第 2 项选择实做而非弱化文档):
--check现在 cwd 无关(相对路径先按 cwd 再按示例目录解析),文件缺失给友好报错而非裸 traceback。--baseline-from-trace实做归因:对 trace 记录跑cluster(),输出trace_attribution.json—— 文档所述「评测与归因」现在名副其实。run_eval异常捕获从宽AssertionError窄化为_EvaluationCasesFailed(与 SDK 自身用法一致),真第三方断言失败不再被吞。_is_json→_is_structured_json:仅 dict/list 计为结构化输出,纯标量期望('42'/'true')不再误判 format_violation。--apply --scenario all改为全场景结束后统一写入,消除场景间污染(测试断言 no_effect/overfit 基线不受影响)。cost_budget门在配置了max_metric_calls但预算不可用(None)时 fail-closed(detail:预算追踪不可用)。测试:65/65 全绿(+6),flake8/yapf 均 clean。
(EN) Post-review update 2026-07-19: an independent adversarial review (6 parallel judges, execution-backed verification) found 0 majors; all 6 minors fixed (incl. implementing trace attribution rather than weakening docs); all items fixed as listed above, full suite green (pytest 65/65, flake8/yapf clean).