RAG、检索与 Agent Eval
Agent 答对了,为什么 Benchmark 仍然失败?——一次 AIOps Agent 的评测调优实战
区分诊断能力、评分口径与 Validator 失败,展示一次有证据的 Benchmark 调优。
Agent 的根因判断正确,不代表 Validator 可用、Scorer 正确,也不代表恢复动作可以自动执行。
本文数字均来自项目中已持久化或已文档化的 Snapshot/Live 评测 Run,只用于解释调优过程,不代表生产 环境 SLA 或业务收益。
一个反直觉的 89 分
一次 PostgreSQL 死锁诊断中,Agent 已经给出了正确方向:故障组件是 order-service,机制是
opposite_order_transaction_deadlock。它收集的三条必要证据也全部通过,component、mechanism
和安全边界都没有问题。
结果却只有 89 分。
更反直觉的是,运行记录里还出现了 model_call_failed。这是否意味着 Agent 根本没有答对?并不是。
失败的是 LLM Validator 的调用;候选结论本身已经通过十项公开的确定性检查。此外,评分器把一次
knowledge_retrieval 也算进了“必须产生诊断 Observation 的工具调用”,分母比真正的诊断工具多了
一个。与此同时,Agent 对 trigger 和 causal chain 的表达又确实不够完整。
同一张 89 分答卷里,其实混合了三类问题:
- Agent 生成的因果表达不完整;
- Validator 的模型调用不可用;
- Scorer 的统计口径有偏差。
所以,看到低分后立刻改 Prompt,往往改错了地方。问题不一定出在“Agent 有没有答对”,而可能出在 Observation 如何变成 Evidence、Decision 是否满足结构约束、Validator 能否完成调用,以及 Scorer 是否按正确口径读取 Artifact。调优对象因此不是一句答案,而是整条可审计证据链。
先把 Agent Benchmark 看成一条证据链
先约定几个容易混淆的概念。假设 Agent 调用数据库等待图工具:
- Observation 是工具返回的原始观测,例如“事务 A 等待事务 B 持有的行锁”;
- Evidence 是经过相关性、归属和可信度评估后,能够支持或反驳某个 hypothesis 的证据;
- Decision 是 Agent 基于 Evidence 形成的候选根因与因果链;
- Validator 检查候选结论有没有被引用的 Evidence 支撑,并满足结构与安全约束;
- Scorer 在 Agent 运行结束后,读取与 Agent 隔离的 Ground Truth 并计算分数;
- Policy Gate 不负责判断答案像不像标准答案,它只决定某项恢复动作是否可以执行。
原始日志不是自动成为证据,正确结论也不是自动获得执行权限。完整链路更接近下面这样:
flowchart LR
S[Scenario] --> A[Agent]
A --> T[Tools]
T --> O[Observation]
O --> E[Evidence]
E --> D[Decision]
D --> V[Validator]
V --> SC[Scorer]
GT[Evaluator-only Ground Truth] --> SC
SC --> R[Scorecard]
V --> P[Policy Gate]
这里最重要的安全边界是:Ground Truth 只在 Agent 返回结果后进入评测侧。Agent、工具、RAG 和诊断 报告都不能读取它。否则测到的就不是诊断能力,而是答案泄漏后的复述能力。
| 参与者 | 主要输入 | 它回答的问题 | 失败时的安全行为 |
|---|---|---|---|
| Agent | 场景、工具、RAG、已收集 Evidence | “目前最可能的根因是什么?” | 证据不足时继续调查或停止,不猜答案 |
| Validator | 候选 Decision 与其引用的 Evidence | “这个结论是否有依据且结构完整?” | 不可用时只能安全降级,不能扩大恢复权限 |
| Scorer | 运行 Artifact 与 evaluator-only Ground Truth | “这次运行满足多少评测要求?” | 记录失败项,不反向影响 Agent 决策 |
| Policy Gate | 已核验结论、风险和恢复策略 | “这项动作现在允许执行吗?” | 默认拒绝或转人工复核 |
这张表带来一个很实用的判断:诊断正确、核验可用、评分准确和恢复获批,是四件不同的事。
案例一:根因正确,Validator 与 Scorer 却各自失败
现象:结论正确,仍然丢了 11 分
这次真实 APY-013 运行开启了 RAG,知识库中有 30 个 ready/indexed 文档。Agent 用时
336,212 ms,得到 89 分;三个必要证据里程碑、独立正证据、根因 component/mechanism 和安全边界
全部通过。
如果只看最终分数,很容易得出一个错误假设:根因判断还不够准,应该继续调 Planner 或 Prompt。
但 Artifact 把扣分拆得很清楚:
trigger_wrong和causal_chain_incomplete合计损失 6 分,确实属于 Decision 的语义表达问题;observations_evaluated损失 5 分,却不是少做了 Evidence Evaluation;- LLM Validator 记录
model_call_failed,但这也不等于候选根因错误。
根因一:Scorer 的分母混入了非诊断工具
本次执行了四个需要形成 Observation 的诊断工具,也完成了四次 Evidence Evaluation。此外还有一次
knowledge_retrieval。旧 Scorer 直接统计所有 completed tool calls,于是分母变成五;再拿四条
Observation 去比较,便判定“并非所有工具结果都被评估”。
问题出在指标定义:知识检索返回的是辅助诊断的参考内容,不是本场景中必须生成 Observation 的运行时 诊断工具。修复不是补造第五条 Observation,而是让 Scorer 只统计会产生诊断 Observation 的工具类别。
根因二:Validator 不可用和结论不可信被混成一件事
候选 Decision 已经通过十项公开的确定性检查,但 LLM Validator 调用失败。如果 Workflow 把“模型不可用” 解释成“证据不足”,它可能进入无关 Replan,重复调用工具,甚至把已经有依据的候选丢掉。
修复后的策略将两类状态分开:
- 确定性 Validator 检查 Evidence 归属、唯一 trigger、因果角色和引用完整性;
- LLM Validator 失败只记录允许列表内的阶段与错误类别,不保存原始异常或模型响应;
- 候选通过全部确定性检查时,保存为
deterministic_grounded_fallback; - fallback 强制进入
manual_review,Policy Gate 保持executionPermitted=false。
这不是用确定性规则“假装 LLM 通过”,而是在供应商不可用时保留一个已被公开证据支持的结论,同时明确 禁止自动恢复。最终审计能够同时回答:“我们认为根因成立吗?”以及“语义核验这次真的成功了吗?”
验证与边界
这次运行证明了安全降级闭环有效,但 89 分不应被包装成满分:trigger 和 causal chain 的语义缺口仍然存在。 同样,修正 Observation 分母也只是在修复评分准确性,不代表 Agent 能力突然提升。
这个案例的要点是:正确诊断、Validator 可用性和 Scorer 正确性必须分别验收。
案例二:修正评分口径后,为什么反而只剩 50 分
评分口径修正后,新 Run 的 observations_evaluated 果然变成了 5/5。但总分没有上升,反而降到了
50 分,失败项是 missing_root_cause_decision。
这里还有一个很容易犯的复盘错误:仓库中有两次 50 分运行,它们暴露的是不同问题。把两次记录揉成一条 时间线,会得到一个从未真实发生过的失败故事。
第一次 50 分:有 Observation,却没有唯一 trigger
Run eval-528fbe19193743b18cb90fb6f1eaf0c7 中,三个必要证据、安全边界和工具预算都已通过,
knowledge_retrieval 也不再占用诊断 Observation 配额。
真正的问题发生在 Observation → Evidence:模型把三条 Observation 全部标成了 mechanism,没有任何
一条承担 trigger 角色。没有唯一 trigger,就无法构造完整 Root Cause Decision。Decision 解析最终记录
invalid_model_output,Validator 因为根本没有候选而记录 candidate_missing——这次甚至没有调用 LLM
Validator,因此不能拿它评价 Validator 供应商是否稳定。
系统也没有从 hypothesis 描述里猜一个 trigger。这个 fail-closed 行为很重要:50 分虽然难看,却比“补出” 一个没有证据绑定的满分答案可信。
第二次 50 分:角色正确了,状态却被越权改写
接下来的 Run eval-247b1751764e40c08fd9a7f0b4cde4f0 已经不是同一个问题。Planner 为步骤形成了
完整角色覆盖:PostgreSQL Error 对应 impact,Wait Graph 对应 mechanism,Resource Order 对应 trigger,
Metrics 对应 context;前三条 Observation 也正确形成了 impact/mechanism/trigger。
但 Sufficiency 仍继续执行 Metrics。该步骤的模型输出越权 refute 了当前步骤没有测试的三个 hypothesis,
把之前的唯一支持假设一起抹掉。与此同时,Decision 的 structured call 又出现
model_call_failed/provider_4xx。所以这次 50 分包含两个独立故障:
- Evidence Evaluation 修改了自己无权修改的 hypothesis 状态;
- structured-output 方法与当前模型 capability 不兼容。
修复:让计划声明意图,让持久化状态拥有最终解释权
Planner 现在为每个步骤保存 causalIntent 和它实际测试的 testsHypotheses。Evidence Evaluation 只能在
这个集合内更新状态,不能因为一句模型总结就 refute 其他竞争假设:
{
"causalIntent": "trigger",
"testsHypotheses": ["hypothesis-from-current-plan-step"],
"evidenceRole": "trigger"
}
Sufficiency 的 supported、refuted 和 unresolved 不再由模型重新宣布,而是从公开 hypothesis 全集与
持久化 Hypothesis State 确定性派生。模型仍可以建议缺失证据和下一步工具,但不能成为状态真相的唯一来源。
structured-output 也改为读取模型 capability profile:当前模型选择 json_mode,旧配置缺失该字段时才保留
function_calling 兼容。修复后的最小真实 readiness 使用合成公开事实,在 24.1 秒内完成;这只证明调用
合同可用,不等于那两次失败 Run 被“改成了成功”。历史 Artifact 保持不可变,后续真实验收必须产生新的
Run。
这个案例说明,分数下降有时不是新版本变差,而是评分器终于不再掩盖更深的能力缺口。先问哪一层开始 偏离证据,再决定是否修改 LangGraph、Tool Calling 或评分器。
案例三:Single 与 Multi 都是满分,应该选哪个?
如果 Single-Agent 和 Multi-Agent 都拿到 100 分,是否应该直接选择结构更“先进”的 Multi-Agent?
当前数据不支持这个结论。我们先在 Nginx timeout 场景执行一组配对 A/B,发现两种模式的结果完全相同; 随后又构造了同时需要 Runtime 状态与 CLS 生命周期日志的 Order Pool 场景。第二组正式 A/B 依然没有 拉开准确率,却明确暴露了时延和模型调用成本的差异。
实验一:Nginx 3×3 没有拉开差距
真实 campaign route-ab-20260820 固定了 Git 工作树、30 卡知识库、模型、CLS、工具白名单和评分器:
| Nginx 真实 3×3 | Single | Multi |
|---|---|---|
| 有效 Run | 3/3 | 3/3 |
| 平均总分 | 100 | 100 |
| Root Cause Top-1 | 100% | 100% |
| Evidence Recall | 100% | 100% |
| 每次模型调用 | 2 | 2 |
| 安全硬门禁 | 3/3 通过 | 3/3 通过 |
这组结果证明,在这个 Nginx timeout 场景和当前配置下,Multi 没有造成准确性、安全或模型调用量退化; 但 Root Cause Top-1 与 Evidence Recall 的增益都是 0 个百分点,因此也没有测出 Multi 的额外能力。
一种合理解释是:场景的主要因果链比较明确,两个模式都碰到了指标上限,产生了 Benchmark 天花板效应。 但这只是需要用更有区分度场景验证的假设,不能据此宣称“Single 足够处理所有 Nginx 故障”。
为什么还要构造第二个实验?
Nginx 场景的因果链较集中,没有充分检验多条独立调查路径能否带来新信息。为了提高区分度,第二个实验 改用 Order Pool:Runtime 侧要确认连接池容量、waiter、数据库可达性和锁等待;CLS 侧要还原 checkout、 更新失败、未归还连接与 acquire timeout 的事件窗口。两类证据共同支持同一个连接生命周期根因。
这比 Nginx 多出一个独立证据域,但仍不能预设 Multi 一定更好。“场景更复杂”在这里是实验设计假设, 不是已经被某个复杂度指标证明的事实。判断仍必须来自同配置、重复执行的配对结果。
实验二:Order Pool 正式 3×3 A/B
修复后的正式 campaign final-order-pool-ab-20260821-223939-f72cdb9 按 Single 01~03、Multi
01~03 顺序执行。六个 Run 均存在于 PostgreSQL 与外部 Evaluation Archive,规范化 Archive checksum
与 PostgreSQL artifact_checksum 逐项一致。
| Order Pool 正式 3×3 | Single | Multi |
|---|---|---|
| 有效 Run | 3/3 | 3/3 |
| 平均总分 | 100 | 100 |
| Root Cause Top-1 | 100% | 100% |
| Evidence Recall | 100% | 100% |
| 平均时延 | 211,298 ms | 360,215 ms |
| nearest-rank P95 | 217,033 ms | 360,333 ms |
| 每次模型调用 | 3 | 7 |
| 每次工具调用 | 5 | 5 |
| 每次持久化 Evidence | 8 | 8 |
| 验证与 cleanup | 3/3 通过 | 3/3 通过 |
两种模式都正确完成根因诊断、Evidence Recall、验证与 cleanup;工具调用和持久化 Evidence 数量也相同。 当前实验没有观察到 Multi 的能力收益,却观察到明确成本:Single 平均时延低约 41%,P95 更低,每次模型 调用为 3 次而不是 7 次。
选择关键不是“难不难”,而是证据能否有效拆分
“简单问题用 Single,困难问题用 Multi”方向上有一定道理,却仍然太粗。一个问题即使很难,如果所有步骤 都依赖同一个数据库状态并且必须串行完成,拆成多个 Agent 也只会增加协调成本。反过来,一个看似不复杂的 问题,如果需要同时核对 Runtime、日志和变更记录,Multi 反而可能获得真正互补的 Evidence。
更稳妥的路由标准是证据复杂度和可分解性:
| 条件 | 推荐模式 | 原因 |
|---|---|---|
| 单一证据域、主要假设明确、步骤强依赖 | Single | Multi 无法形成有效并行,反而增加协调成本 |
| 多个数据源,但 Single 能在预算和 deadline 内完整闭环 | 优先 Single | 当前没有证据证明拆分能提升能力 |
| Runtime、日志、变更记录等证据域可以独立调查 | Multi 候选 | 可以并行获得互补 Evidence |
| 多个竞争根因需要不同数据域分别证伪 | Multi 候选 | Specialist 独立调查后由中央链路聚合 |
| 场景很难,但步骤必须串行或共享同一状态 | Single | 难度不等于可并行性 |
| Multi 只会重复相同工具和 Evidence | 不升级 | 没有新增信息,只增加调用和时延 |
难度可以作为路由信号,但不是充分条件。升级 Multi 前,至少应该确认存在两个可独立调查的证据域,并且 并行结果能增加新 Evidence、关闭竞争假设,或在 deadline 内完成 Single 无法完成的调查。
当前为什么选择 Single?
在两种模式准确率、Evidence Recall、工具调用、Evidence 数量和安全闭环相同的前提下,平均/P95 时延与
模型调用量成为决定因素。因此当前生产 auto 选择 Single。这是对现有 Benchmark 的工程决策,不是
“Single 普遍优于 Multi”的结论。
下一轮怎样真正验证 Multi?
下一轮更有区分度的测试,应构造同时涉及 Runtime、CLS 和变更记录的竞争根因,并加入部分缺失、相互冲突 或时间窗口错位的 Evidence。然后在同一 Git SHA、模型、RAG、CLS、工具和 Scorer 下,顺序执行 Single ×3 与 Multi ×3。
比较指标也不能只有总分,还应包含 Root Cause Top-1、Evidence Recall、独立来源组、重复与冲突 Evidence、 模型调用量、平均/P95 时延、安全闭环和 cleanup。只有 Multi 在不降低准确性、安全和稳定性的前提下产生 可重复的能力增益,才有理由扩大生产路由。
因此,当前综合准确率、时延与模型调用成本,生产继续使用 Single。现有满分数据证明 Multi-Agent 可以 完成闭环,但还没有证明它值得成为默认路径。
案例四:证据齐全,确定性规则为什么仍会误判
最后一个案例不涉及 LLM 解析失败,而是一个很典型的事件语义错误。
Order Pool 泄漏场景已经收集到四组独立工具 Evidence:连接池满、空闲连接为 0、存在 waiter、业务探针 超时;PostgreSQL 仍可达且没有锁等待。CLS 生命周期里也能看到未归还的 checkout、订单更新失败和后续 acquire timeout。
结果仍然是 rootCauseDecision=null,终态为 VALID_FAIL/recovery_denied。
错误假设:只要出现过 checkin,就不存在连接泄漏
完整事件流并不只包含故障请求。在真正的泄漏之前,有一个正常请求已经完成:
正常请求:checkout → checkin
故障请求:checkout → order_update_failed → pool_acquire_timeout
旧 trusted lifecycle matcher 使用的是全局存在性规则:只要整段日志任意位置出现
connection_checkin,就把它当作泄漏假设的反证。于是,故障前已经闭合的正常请求误伤了后面的泄漏链。
这里不能靠删除正常日志、扩充同义词或要求模型忽略 checkin 来“过测”。真正需要修复的是事件相关性: 全局出现过某事件,不等于它发生在当前候选因果窗口内。
修复:先定位候选链,再判断窗口内是否归还
新的 matcher 先定位 checkout → order_update_failed → pool_acquire_timeout 候选窗口,只检查该候选
checkout 到 timeout 之间是否存在 checkin:
for checkout in checkouts:
failure = first_event_after(checkout, "order_update_failed")
timeout = first_event_after(failure, "pool_acquire_timeout")
if failure and timeout and not exists_between("checkin", checkout, timeout):
candidates.append((checkout, failure, timeout))
这段伪代码只解释窗口语义。真实实现还必须绑定请求与连接标识,处理乱序、重复事件和窗口上界;否则仍可能 把不同请求的事件错误拼接成一条链。窗口内出现 checkin 时,规则继续 fail closed,不形成泄漏结论。
规则判断正确后,语义投影还可能继续丢分
matcher 修复后的下一次真实 Single 已形成 deterministic_grounded Decision,component、mechanism、
Evidence、恢复和安全策略都正确,Evidence Recall 也是 100%,但原始总分仍只有 90。原因是确定性投影只
罗列“看到哪些事件、池处于什么状态”,没有明确表达:异常路径没有释放连接,连接池因此耗尽,新订单等待
连接并最终超时。
这一次修复没有增加场景关键词,而是把已有 Evidence 按通用因果角色投影为 trigger、mechanism 和 impact, 并要求 mechanism、impact 都引用实际 Evidence。Ground Truth、同义词表、评分阈值和恢复权限都没有改动。
最终真实 Single order-pool-specialist-single-gate-semantic-fixed-20260821 达到
100/100 VALID_PASS:Root Cause Top-1、Evidence Recall、恢复验证、cleanup 和安全硬门禁全部通过;
诊断任务持久化了八条 Evidence、四个独立 sourceFingerprint,没有重复 Evidence。
这里仍有一条安全边界:生产 Recovery Policy 保持 manual_review_required 和
executionPermitted=false。运行中的恢复由隔离 Live Benchmark harness 按场景合同执行,不代表生产 Agent
获得了自动执行权限。
这个案例的结论是:确定性规则也需要像模型一样接受反例和时序边界测试;“不用 LLM”不等于“不会误判”。
一套可以复用的六步排查法
遇到“看起来答对却没通过”的 Agent Benchmark,可以按下面的顺序排查。顺序很重要:越靠前的问题,越可能 让后面的分数和耗时失去解释意义。
第一步:冻结失败 Run
保存 terminal Artifact、Checkpoint、工具审计、模型调用状态和 checksum,给失败运行一个不可变 identity。 不要覆盖结果,也不要只保留最好的一次重跑。没有失败历史,就无法区分修复、波动和选择性汇报。
第二步:先检查运行是否有效
确认故障是否真的注入、依赖是否 ready、工具是否完成、参数是否绑定到当前 owner/Run,以及 cleanup 是否 执行。基础设施失败应标记为 infra invalid,而不是算成 Agent 诊断失败;同样,提前退出的低时延不能进入 性能收益比较。
第三步:逐层检查 Observation、Evidence 与 Decision
对每次工具调用追问三个问题:工具返回了什么 Observation?它是否经过 Evidence Evaluation?它支持或反驳 的是哪个当前步骤允许测试的 hypothesis?然后检查 Decision 引用的 Evidence 是否真的覆盖 trigger、 mechanism 和 impact。不要从最终答案反推中间链路“应该正确”。
第四步:单独检查 Validator
把“语义拒绝候选”与“调用失败”分开。记录安全的阶段、错误类别、Schema 解析结果、尝试次数和 deadline; 不要持久化原始模型响应或异常正文。检查 structured-output 方法是否符合模型 capability,以及剩余 deadline 是否足以支持格式纠正重试。
第五步:逐项核对 Scorer
检查每项分数的分母、过滤条件、语义匹配和 hard gate。尤其注意:知识检索是否被误算作诊断 Observation、 无序事件是否被当作有序因果链、全局事件是否误伤候选窗口。不要先降阈值,要先用正例、同义正确例、缺环例、 乱序例和错误机制例复现规则边界。
第六步:离线回归后只跑新的真实 canary
先用合成公开事实验证 Parser、状态转换、matcher 和 Scorer,再使用相同模型、RAG、工具和评分器执行一个新的 真实 canary。新结果必须使用新 Run ID,失败就保存并停止。一次 canary 只证明该配置下的可用性,扩大到 3×3 A/B、默认路由或生产恢复权限需要独立门禁。
总结:让整条链路对同一事实达成一致
“Agent 答对了,Benchmark 为什么仍失败”没有唯一答案。失败可能发生在 Planner、Tool Calling、 Observation → Evidence、Decision、Validator、Scorer,也可能只是供应商或运行时没有完成有效调用。
这次调优最重要的收获不是把某个场景从 89、50 或 90 提到 100,而是建立了一条可以逐层审计的解释链:
工具调用可追踪
→ Observation 有来源
→ Evidence 有角色和 hypothesis 归属
→ Decision 有完整因果链
→ Validator 区分语义拒绝与调用失败
→ Scorer 使用正确口径
→ Policy Gate 独立控制恢复权限
可信的 Benchmark 调优,不是让 Agent 更像标准答案,而是让工具观测、Evidence、Decision、Validator 和 Scorer 对同一事实形成一致、可审计的解释,同时保留模型失败、证据不足和高风险恢复时的安全边界。
当下一次分数异常时,先别急着改 Prompt。先找到证据链从哪一层开始断裂——那里才是真正值得修复的地方。