Skip to content

Feature/eval optimize loop#213

Open
guocfu wants to merge 18 commits into
trpc-group:mainfrom
guocfu:feature/eval-optimize-loop
Open

Feature/eval optimize loop#213
guocfu wants to merge 18 commits into
trpc-group:mainfrom
guocfu:feature/eval-optimize-loop

Conversation

@guocfu

@guocfu guocfu commented Jul 21, 2026

Copy link
Copy Markdown

变更说明

实现 Evaluation + Optimization 自动回归与提示词优化闭环,覆盖输入准备、完整评测、失败分析、候选优化、Gate 决策、安全写回和审计报告。

#91

主要功能

输入准备与 Prompt 工作区

  • 校验 Pipeline、Optimizer 和 EvalSet 配置
  • 对输入文件和源 Prompt 生成 SHA-256 快照
  • 创建隔离的 Prompt 工作副本
  • 检测评测期间的输入和源 Prompt 漂移

四次完整评测

分别执行:

  1. Baseline Train
  2. Baseline Validation
  3. Candidate Train
  4. Candidate Validation

真实优化器内部的 minibatch 评测不会替代四次完整回归。

评测分析

  • 将 SDK 原始评测结果转换为稳定的 Case 级结构
  • 对失败 Case 进行确定性归因
  • 生成 Baseline 与 Candidate 的 Case Diff
  • 识别 newly passed、newly failed、regression 和 overfitting

Prompt 候选生成

支持两种候选来源:

  • 确定性 Candidate Provider,用于无 API Key 的离线测试
  • AgentOptimizer,用于真实反思模型驱动的 Prompt 优化

真实优化器始终使用 update_source=False,候选先进入隔离工作区接受完整回归。

Gate 与安全写回

  • 根据 Validation 改善、通过率、关键 Case、严重回归和资源预算进行决策
  • 输出 ACCEPT 或 REJECT 及对应证据
  • 仅在 Gate ACCEPT、显式启用写回且源 Prompt 哈希未变化时更新源文件
  • 写回后执行回读校验
  • Trace 回放模式始终禁止源 Prompt 写回

运行模式

Offline

  • 使用 SDK LlmAgent + Runner + Session
  • Agent 内部注入 DeterministicFakeModel
  • 使用确定性 Candidate Provider
  • 不需要真实 API Key
  • Prompt 改变会通过 system instruction 影响模型输出

Trace

  • 使用 SDK 原生 eval_mode="trace"
  • 回放预录制的 Baseline/Candidate 轨迹
  • 不运行 Agent、Model 或 Candidate Provider
  • 复用评测标准化、归因、Case Diff、Gate 和报告链路

Real

  • 业务 Agent 使用真实模型
  • AgentOptimizer 使用真实反思优化模型
  • 模型连接凭据从环境变量读取
  • 优化器参数通过命令行显式传入
  • 真实集成入口默认关闭,必须显式指定 --run-real

报告与审计产物

每次运行可生成:

  • 完整 JSON 报告
  • Markdown 摘要
  • Artifact Index
  • 四次 SDK 原始评测结果
  • Baseline/Candidate Prompt 快照
  • 输入配置和 SHA-256
  • Gate 决策及拒绝原因
  • 写回结果
  • 优化器原生产物和资源观测

同时提供可校验的 sample_output 示例。

场景结果

模式 场景 Baseline Train/Val Candidate Train/Val Gate
Offline improve 1/3 + 1/3 3/3 + 3/3 ACCEPT
Offline no_improvement 1/3 + 1/3 1/3 + 1/3 REJECT
Offline overfit 1/3 + 1/3 3/3 + 0/3 REJECT
Trace improve 1/3 + 1/3 3/3 + 3/3 ACCEPT
Trace no_improvement 1/3 + 1/3 1/3 + 1/3 REJECT
Trace overfit 1/3 + 1/3 3/3 + 0/3 REJECT

测试

已覆盖:

  • 输入校验和 Prompt 工作区隔离
  • 四次完整评测
  • 评测标准化、失败归因和 Case Diff
  • Gate 与资源预算
  • 真实 AgentOptimizer 适配
  • 安全写回和源 Prompt 漂移保护
  • Offline 和 Trace 三场景矩阵
  • 报告、Artifact Index 和示例产物校验
  • 真实业务 Agent 及真实优化器集成入口
  • 无 API Key 的 Offline/Trace CLI

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

预算使用了 on_unavailable: "warning" 且仅有持续时间限制,因此离线模式运行正常。不错。

现在我来整理一下审查意见。我确认了一个具体问题(CLI 失败时打印未脱敏的异常),让我再重新评估一下其严重程度——这是一个记录到 stderr 的示例 CLI。base_url 并不是真正意义上的秘密(它是端点),但 api_key 可能会出现在异常中。OpenAI 客户端异常通常不包含 api_key,但可能会包含 base_url。由于 _redact_error_message 明确对失败报告进行了脱敏,因此在 CLI 的 stderr 中不对相同数据进行脱敏是一个不一致的安全缺口。⚠️ 警告。

让我考虑一下是否还有其他具体问题。_rollback_written_source 的 TOCTOU 注释承认了回调存在竞争条件,但记录了路径原子性——这是可以接受的。

再看一点:在 discover_run_artifacts 中,使用 path.relative_to(root)os.walk 在每次迭代时都会解析 root;如果 root 包含符号链接组件,但 _resolved_run_dir 已经拒绝了对 run_dir 本身的符号链接(而不是父目录)。问题较小。

让我最终定稿。

发现的问题

🚨 Critical

(无)

⚠️ Warning

  • examples/optimization/eval_optimize_loop/run_real_integration.py:5345:失败时直接打印原始异常到 stderr,未脱敏

    • main()except Exception as exc: print(f"Real integration failed: {exc}", file=sys.stderr) 会原样输出异常字符串;真实模式下异常来自 OpenAI/AgentOptimizer 客户端,可能包含 base_url 或连接串等敏感信息。同一 PR 中 report_builder._redact_error_message 已对落盘的 failure report 做了脱敏,但这条 CLI 顶层 stderr 路径未对齐,存在凭据进入终端/日志的风险。建议复用 _redact_error_message(或等价逻辑)对 exc 脱敏后再输出,原始异常仍可通过 failure_report.json 审计。
  • examples/optimization/eval_optimize_loop/pipeline.py:4476-4493:trace 阶段的分析与 Gate 异常未包装,错误定位信息丢失

    • _execute_trace_stage 直接调用 build_evaluation_analysisevaluate_gate,未像 offline/real 阶段那样捕获 EvaluationAnalysisError/GateEvaluationError 并包装为 PipelineStageExecutionError("stage 3a analysis failed: ..."/"stage 3b gate failed: ...")。结果是 trace 模式下分析/Gate 失败时 failure_report.failed_phase 与错误消息缺少阶段上下文,排障与审计信息不一致。建议与 offline/real 阶段保持相同的异常包装。

💡 Suggestion

  • examples/optimization/eval_optimize_loop/pipeline.py:3998-4010_restore_working_baseline 在 read 失败后未尝试恢复
    • working_target.read_all() 抛错时直接 raise,工作 Prompt 可能停留在 optimizer 写入的中间态而未被还原。当前 offline/real 路径对该错误的处理依赖上层失败报告,影响有限;若后续工作区被复用,建议在读失败时仍尝试一次 write_all(baseline) 兜底。

总结

整体实现严谨:路径穿越/符号链接防护、原子目录发布、凭据占位符校验、失败报告脱敏和回写回滚都做了显式处理。存在一处安全对齐缺口(真实集成 CLI 顶层 stderr 未脱敏)和一处阶段错误包装不一致,均不阻塞核心功能但建议修复。

测试建议

  • 补充一个用例:在 run_real_integration.main() 的失败路径中注入包含 TRPC_AGENT_BASE_URL/TRPC_AGENT_API_KEY 值的异常,断言 stderr 输出中不出现这些明文(与 test_failure_report_redacts_environment_values_and_common_secret_forms 对齐)。
  • 暂无其他额外测试建议。

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

我已经获取了足够的信息来定稿。让我来编写审查报告。

发现的问题

🚨 Critical

(未发现必须修复的 Critical 问题。)

⚠️ Warning

  • examples/optimization/eval_optimize_loop/candidate_provider.py:96-118:运行时配置脱敏键集与产物校验键集不一致

    • _replace_persisted_connection_values 只替换 api_key/apiKey/base_url/baseUrl 四个键,而 artifact_writer._validate_sensitive_config_values(line 39-72、用于 input.optimizer_config 拷贝校验)覆盖 token/authorization/secret/password/client_secret 等十余类敏感键。optimizer.runtime.json 写到 run 根目录后会被 publish_report_bundle 当作 native artifact 直接索引发布(artifact_writer.py:441-446),但不经过任何敏感值校验。若真实 reflection_lm 模板带 authorization/token 等字段,明文会被写入并发布到 artifact index,而同样配置作为 input.optimizer_config 拷贝时却会被拒绝,存在脱敏与审计口径不一致导致的凭据泄露风险。建议统一两处的敏感键集合,并对发布前的 optimizer.runtime.json 复用 _validate_sensitive_config_values 校验。
  • examples/optimization/eval_optimize_loop/pipeline.py:476-489evaluation_adapter.py:205-227:报告通过计数与 Gate 通过计数口径不一致

    • _summarize_results 仅在 final_eval_status == PASSED 时计为通过,而 standardize_snapshotpassed_case_count 还要求所有 metric 可用且通过,否则记为 not_evaluated 不计入。因此 EvaluationSnapshot.passed_case_count(用于报告/markdown)可能大于 StandardizedEvaluation.passed_case_count(用于 Gate 的 pass-rate 规则),导致报告展示的通过数与驱动决策的 Gate 通过数不同,审计对照时易误判。建议让报告也使用 analysis.* 的标准化通过计数,保持口径一致。

💡 Suggestion

  • examples/optimization/eval_optimize_loop/pipeline.py:595:632:759:790progress.enter("baseline_train") 在同一阶段被重复调用两次。_MutableReportProgress.enter 能幂等处理,但语义冗余,建议删除进入函数体后的首次冗余调用,避免后续维护时混淆阶段起点。

总结

整体实现质量较高:写回有 hash 校验+回滚+读回验证、源 Prompt 始终隔离、产物发布用原子 no-replace、敏感配置在拷贝路径有较完整校验。未发现必须修复的阻塞问题;两处 Warning 属于脱敏口径与通过计数口径不一致,可能造成凭据泄露或审计误判,建议修复。

测试建议

  • 补充一个 real 模式端到端测试:生成 optimizer.runtime.json 并经 publish_report_bundle 发布后,断言其中不包含 authorization/token/secret 等非标准敏感键的明文(覆盖上述脱敏口径缺口)。
  • 补充一个用例:某 case 的 final_eval_status == PASSED 但存在 not_evaluated metric,断言报告 EvaluationSnapshot.passed_case_count 与 Gate 使用的标准化 passed_case_count 行为符合预期统一口径。

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

基于我对 PR diff 及其相关上下文的审查,以下是我的代码审查结果。

发现的问题

⚠️ Warning

  • examples/optimization/eval_optimize_loop/config.py:194examples/optimization/eval_optimize_loop/config.py:208:配置项 ReportingConfig.include_case_evidenceArtifactConfig.audit_all_candidates 已声明并被全部 pipeline.*.json 配置消费,但全仓搜索后二者在 diff 内的任何构建/发布逻辑中均未被读取(publish_report_bundle 始终写全部评测证据,audit_all_candidates 从未参与判定)。
    • 这是“无效配置”:用户修改这两个开关不会产生任何行为变化,容易误以为已生效。建议要么在 publish_report_bundle / 报告构建处真正消费(例如 include_case_evidence=False 时裁剪 EvaluationSnapshot.eval_results_by_eval_id 等证据字段),要么从 schema 中移除并在文档说明为预留字段,避免配置与行为不一致。

💡 Suggestion

  • examples/optimization/eval_optimize_loop/report_builder.py:53redact_error_message_HTTP_URL 会把异常文本里所有 http(s):// 串替换为 [REDACTED,包括非敏感的文档链接或帮助链接。对失败报告可读性有轻微影响;如需更精确,可仅对与已识别敏感值匹配的 URL 做替换,或保留对已知 base_url 的精确匹配。当前实现安全侧偏向保守,影响较低。

总结

整体风险较低,未发现安全漏洞或会导致核心功能失败的明确逻辑错误;写回、漂移检测、回滚、原子发布与敏感配置脱敏等关键路径均有对应实现与测试覆盖。唯一值得在合入前处理的是 include_case_evidence / audit_all_candidates 两个未接线的配置项,建议让其生效或移除以避免配置语义与实际行为脱节。

测试建议

  • 建议补一条测试:在 include_case_evidence=False(或 audit_all_candidates 切换)时验证发布产物确实发生变化,用以锁住配置项的预期语义;若决定移除该字段,则补一条 schema 拒绝未知字段的回归测试即可。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants