RAG、检索与 Agent Eval
我如何为 AIOps Agent 构建 10 个 Snapshot 与 5 个 Live Benchmark
说明 Snapshot 与 Live 场景的边界、证据来源、评分和正式基线管理。
本文介绍当前项目中的 10 个 Snapshot 场景和 5 个 Live 场景,包括每个场景模拟的故障、Agent 可使用的工具,以及必须获得的关键证据。
一、Snapshot 和 Live 有什么区别?
两者使用相同的 Agent 诊断链路,但数据来源不同。
| 维度 | Snapshot Benchmark | Live Benchmark |
|---|---|---|
| 故障来源 | 预先冻结的工具响应 | Docker 隔离环境中真实注入 |
| 工具调用 | 从 tool_responses.yaml 回放 | 查询真实 PostgreSQL、Redis、Nginx、Order API |
| 稳定性 | 高,适合 CI 回归 | 受服务状态和网络影响 |
| 执行速度 | 较快 | 较慢 |
| 恢复验证 | 主要评估诊断与方案 | 可真实恢复并验证 |
| CLS | 不需要 | 可通过 SearchLog 查询真实日志 |
| 主要用途 | 测试根因辨别和证据推理 | 测试完整生产链路和安全闭环 |
整体链路如下:
故障告警
↓
Planner 生成调查计划
↓
调用诊断工具
↓
获得 Observation
↓
Evidence Evaluation
↓
假设排除与根因决策
↓
Validator 核验
↓
恢复执行或生成恢复提案
↓
独立 Verify + Cleanup
↓
Benchmark 评分
这里有一个非常重要的区别:
工具输出只是 Observation,并不会自动成为有效 Evidence。
工具结果还必须经过证据评估,判断它:
- 是否与当前假设相关;
- 支持还是反驳某个假设;
- 是否来自独立的数据源;
- 是否能够支撑 trigger、mechanism 和 impact 因果链;
- 是否存在重复证据或来源污染。
二、10 个 Snapshot 场景
APY-002:PostgreSQL 慢事务导致连接池耗尽
告警现象
应用大量出现数据库连接获取超时,连接池已经没有可用连接。
诊断工具
InspectPostgresInspectDatabasePoolGetServiceMetricsGetDeploymentChanges
关键证据
- 存在持续约 428 秒的长事务;
- 11 个数据库会话被阻塞,等待类型为
transactionid; - 连接池 20 个连接全部借出,37 个请求等待连接;
- 连接的主要持有者正在等待数据库锁;
- 流量只小幅增加,不足以解释连接池突然耗尽。
正确根因
component: postgresql
mechanism: slow_transaction_pool_exhaustion
这里的关键不是“池满了”,而是继续向下追问:连接为什么一直没有归还?答案是连接被数据库慢事务和锁等待占用。
APY-003:应用进程退出导致 Nginx 502
告警现象
Nginx 返回 502,无法连接 checkout 服务。
诊断工具
InspectContainerInspectNginx
关键证据
- checkout 容器状态为
exited,退出码为 137; - 应用端口 8080 没有监听;
- Nginx upstream 正确指向 8080;
- Nginx 请求 upstream 时收到
connection refused。
正确根因
component: checkout-service
mechanism: process_unavailable
APY-006:Nginx upstream 端口配置错误导致 502
这个场景和 APY-003 的外部现象几乎相同,但根因完全不同。
诊断工具
InspectContainerInspectNginx
关键证据
- checkout 容器健康;
- 应用正常监听 8080;
- Nginx upstream 却配置成 8081;
- 访问 8081 返回
connection refused。
正确根因
component: nginx-gateway
mechanism: upstream_port_mismatch
APY-003 和 APY-006 构成一组差分场景:
| 场景 | 表面现象 | 应用状态 | Nginx 配置 | 根因 |
|---|---|---|---|---|
| APY-003 | 502 | 进程退出 | 正确 | 应用不可用 |
| APY-006 | 502 | 健康 | 端口错误 | 网关配置错误 |
它可以判断 Agent 是真正综合证据,还是看到 502 就机械地回答“应用挂了”。
APY-007:Redis 服务进程停止
告警现象
应用无法连接 Redis,请求出现 connection refused。
诊断工具
InspectRedisInspectRedisClientPoolGetServiceMetricsGetDeploymentChanges
关键证据
- Redis 进程已经停止,端口没有监听;
PING不可用;- 客户端最近错误为
connection refused; - 连接池中没有 stale connection;
- 没有明显的连接池 waiter。
正确根因
component: redis
mechanism: service_process_stopped
APY-011:异常路径没有归还 PostgreSQL 连接
告警现象
连接池耗尽,新的 checkout 请求无法获得数据库连接。
诊断工具
InspectPostgresInspectDatabasePoolGetServiceMetricsGetDeploymentChanges
关键证据
- PostgreSQL 没有长事务,也没有数据库锁等待;
- 连接 checkout 次数为 9130,checkin 次数为 9047;
- 20 个连接被应用持续保留;
- 保留连接的路径为
checkout-discount-exception。
正确根因
component: checkout-service
mechanism: borrowed_connection_not_returned
APY-002 和 APY-011 同样构成差分场景:
- APY-002:连接因为数据库慢事务无法释放;
- APY-011:连接因为应用异常路径遗漏 checkin 无法释放。
因此,“连接池满”只是结果,不是根因。
APY-012:Redis 恢复后,应用仍保留旧连接
告警现象
Redis 已经恢复,但应用请求仍然失败。
诊断工具
InspectRedisInspectRedisClientPoolGetServiceMetricsGetDeploymentChanges
关键证据
- Redis 进程已经运行,
PING=PONG,网络连接正常; - 新建直连可以成功;
- 应用连接池仍保留 24 个 stale connections;
- 有 26 个请求等待连接;
- Redis 恢复后,连接池 generation 没有变化。
正确根因
component: checkout-service
mechanism: stale_connections_retained_after_recovery
APY-007 和 APY-012 用于区分 Redis 服务真的停止,与 Redis 已恢复但应用客户端没有完成连接恢复。
APY-013:相反资源顺序导致 PostgreSQL 死锁
告警现象
并发订单事务发生回滚,但数据库整体仍然可达。
诊断工具
InspectPostgresErrorsInspectTransactionResourceOrderInspectPostgresWaitGraphGetDatabaseMetrics
关键证据
- PostgreSQL 返回 SQLSTATE
40P01和deadlock detected; - 事务 A 先更新 order row,再更新 inventory row;
- 事务 B 以相反顺序更新资源;
- wait graph 中出现长度为 2 的环;
- 数据库 CPU、连接数和整体延迟正常。
正确根因
component: order-service
mechanism: opposite_order_transaction_deadlock
完整因果链应当是:
两个并发事务以相反顺序获取资源
→ 形成循环等待
→ PostgreSQL 选择一个事务作为 victim
→ 返回 40P01 并回滚事务
APY-014:Redis maxclients 被耗尽
告警现象
已经建立的 Redis 连接还能使用,但新连接被拒绝。
诊断工具
InspectRedisServerGetRedisConnectionMetricsListRedisClientsInspectHostLimits
关键证据
- Redis 进程仍在运行,已有连接
PING=PONG; connectedClients == maxclients == 16;rejectedConnections增加 83;- 其中 14 个连接属于当前 Benchmark run;
- 宿主机文件描述符仍然充足。
正确根因
component: live-eval-redis
mechanism: benchmark_clients_exhausted_maxclients
这组证据排除了 Redis 进程停止和宿主机文件描述符耗尽两种常见误判。
APY-015:上游响应超过 Nginx read timeout
告警现象
Nginx 返回 504 Gateway Timeout。
诊断工具
InspectGatewayRequestTimelineInspectGatewayErrorsProbeUpstreamHealthGetGatewayMetrics
关键证据
- Nginx 连接 upstream 只用了约 4ms;
- 请求约 752ms 后返回 504;
- 错误发生在
reading response header阶段; - upstream health endpoint 返回 200;
- 慢接口 first byte 约为 1500ms;
- Nginx read deadline 为 750ms;
- Nginx 本身没有明显资源压力。
正确根因
component: live-eval-upstream
mechanism: upstream_response_exceeded_proxy_read_timeout
这说明连接已经成功,问题发生在“等待响应”阶段,而不是 upstream 端口不可达。
APY-016:客户端忽略 Retry-After 导致重试风暴
告警现象
接口收到大量 429,整体流量突然放大。
诊断工具
InspectHttpAttemptsInspectRateLimitTimelineInspectClientRetryPolicyInspectTrafficAndDependencyHealth
关键证据
- 每个逻辑请求的尝试次数从 1.08 上升到 4.92;
- 429 比例达到 61%;
- 总请求量增长约 310%;
- 服务返回
Retry-After: 2,但客户端没有遵守; - 没有 exponential backoff,也没有 jitter;
- 实际重试间隔接近 0;
- 恶意流量比例较低,下游依赖整体健康。
正确根因
component: checkout-client
mechanism: retry_after_ignored_without_backoff
它测试的是 Agent 能否识别“客户端重试放大”,而不是把 429 简单归因于恶意流量或服务容量不足。
三、5 个 Live 场景
与 Snapshot 不同,Live 场景会在隔离 Docker 服务中真实构造故障。Agent 获得的是经过脱敏的实时证据,而不是 ground_truth.yaml 中的答案。
APY-LIVE-PG-LOCK-001:PostgreSQL 行锁阻塞
故障注入
一个合成事务锁住当前测试订单行,另一个订单状态更新被阻塞,最终业务探针超时。
运行时工具
InspectPostgresSessionsInspectPostgresLockGraphVerifyServiceHealthSearchLog:启用 CLS 模式时使用
关键证据
- waiter 的
waitEventType=Lock; - 阻塞图存在
blocker → waiter边; - 被锁资源是当前测试订单行;
- PostgreSQL 本身仍然可达;
- 业务更新探针超时;
- CLS 日志记录对应 run 的 request timeout。
需要排除
- 数据库不可达;
- SQL 自身计算过慢;
- 不存在 blocker 的普通慢查询。
恢复闭环
终止当前 run 的合成 blocker
→ waiter 解除阻塞
→ 业务探针重新成功
→ PostgreSQL 健康
→ 锁图清空
→ 确认未影响无关会话
恢复动作只能作用于当前 Benchmark 创建并确认所有权的 blocker。
APY-LIVE-PG-DEADLOCK-001:真实 PostgreSQL 死锁
故障注入
两个并发事务以相反顺序更新两条业务记录,形成真实等待环。
运行时工具
InspectPostgresDeadlockAuditInspectPostgresTransactionResultVerifyPostgresHealthSearchLog
关键证据
- SQLSTATE 为
40P01; - PostgreSQL 确认发生死锁;
- 两个事务的资源获取顺序相反;
- 审计中存在 deadlock cycle;
- 其中一个事务被回滚;
- PostgreSQL 整体仍然健康。
需要排除
- 普通的长时间锁等待;
- 没有锁环的慢 SQL;
- 数据库整体不可用。
恢复闭环
只重试被 PostgreSQL 回滚的 Benchmark 事务,而不是终止未知数据库会话。验证条件包括:
- victim 事务重试成功;
- 两条业务记录均更新成功;
- 当前 run 没有遗留事务;
- PostgreSQL 保持健康。
APY-LIVE-REDIS-MAXCLIENTS-001:Redis 连接槽耗尽
故障注入
使用带有当前 run 标识的 Benchmark 客户端,占满隔离 Redis 的连接上限。
运行时工具
InspectRedisServerInfoListBenchmarkRedisClientsVerifyRedisPingSearchLog
关键证据
connectedClients == maxclients;rejectedConnectionsDelta增加;- 新连接被拒绝;
- 已有控制连接仍能 PING;
- 占用连接的是当前 run 的命名 Benchmark clients;
- CLS 中存在对应的新连接拒绝事件。
需要排除
- Redis 进程停止;
- 宿主机文件描述符耗尽;
- 应用只保留了少量 stale connections。
恢复闭环
只关闭当前 run 的命名客户端
→ connectedClients 降到限制以下
→ 新连接恢复成功
→ 原有控制连接仍然健康
→ 无关客户端不受影响
这里不能使用“关闭所有 Redis 客户端”这样的高风险动作。
APY-LIVE-NGINX-TIMEOUT-001:上游响应超时
故障注入
隔离 upstream 可以正常建立连接,但业务响应时间超过 Nginx 的 proxy_read_timeout,网关返回真实 504。
运行时工具
InspectNginxRequestTimelineProbeLiveEvalUpstreamReadNginxTimeoutSummaryProposeNginxTimeoutMitigationSearchLog
关键证据
- 网关返回 504;
- upstream connection 已成功建立;
- 请求耗时达到 read deadline;
- upstream 独立健康检查返回 200;
- 网关自身健康检查返回 200;
- CLS 中存在对应的 upstream timeout 日志。
需要排除
- upstream 端口关闭;
- 路由到错误地址;
- Nginx 自身资源耗尽。
恢复合同
这是 proposal_only 场景。Agent 不能直接修改或 reload Nginx,只能生成结构化方案:
- 修改目标;
- 风险说明;
- 回滚方法;
- 至少两项可执行验证步骤;
- 明确要求人工审批。
最终还要验证 Nginx 配置哈希没有变化、Agent 没有执行写操作,以及网关和 upstream 仍保持健康。
APY-LIVE-ORDER-POOL-LEAK-001:Order API 异常路径连接泄漏
故障注入
Order API 使用固定容量的 asyncpg pool。特定订单更新路径在 checkout 数据库连接后抛出异常,但没有执行对应 release。
运行时工具
InspectOrderPoolStateInspectOrderDatabaseSessionsVerifyOrderDatabaseReachabilitySearchLog
在 Multi-Agent 模式下,这些工具可以分为两个调查域:
Runtime Specialist
├── InspectOrderPoolState
├── InspectOrderDatabaseSessions
└── VerifyOrderDatabaseReachability
Log Specialist
└── SearchLog
关键证据
运行时证据:
- 连接池达到容量上限;
freeConnections=0;- 出现等待连接的请求;
- 当前 run 的数据库会话仍然存在;
- PostgreSQL 可以正常连接;
- 没有数据库 lock wait;
- 业务探针因为无法获取连接而超时。
CLS 生命周期证据:
connection_checkout
→ order_update_exception
→ request_timeout
并且缺少与 checkout 匹配的 connection_checkin。
需要排除
- 正常流量超过容量;
- PostgreSQL 不可达;
- 慢 SQL 长时间持有连接;
- 数据库锁等待。
正确根因
component: order-api
mechanism: exception_path_connection_not_released
恢复闭环
隔离 Live Harness 可以重启当前测试的 Order API 实例,使旧 pool generation 释放并创建新连接池,然后验证:
- 旧 generation 会话已经清除;
- 新 generation 已就绪;
- 业务探针恢复;
- PostgreSQL 健康;
- 无关数据库会话仍然存在;
- 测试订单和残留会话完成清理。