AIOps Agent 与 LangGraph
为什么运维 Agent 不能只靠 ReAct?用 LangGraph 构建证据驱动的 Plan-Execute-Replan 诊断链路
解释 Plan-Execute-Replan 如何组织竞争假设、工具证据、裁决与安全恢复。
一次接口超时,可能来自数据库死锁、连接池耗尽、上游响应过慢、网络抖动,也可能只是应用自身处理变慢。运维 Agent 真正困难的地方,不是给出一个“听起来合理”的答案,而是知道还缺什么证据、为什么排除其他原因,以及在结论不可靠时拒绝执行恢复动作。
假设告警平台只告诉我们:order-service 的请求超时率突然升高。
如果直接把这句话交给大模型,它很容易生成一份完整的排查清单:检查 CPU、连接池、慢查询、锁等待、网络和下游服务。内容看起来没错,却没有回答最重要的问题:这一次超时究竟由什么造成?
进一步让模型调用工具,也不代表问题自然消失。Agent 可能查到一条 deadlock detected,立刻把死锁当成根因;也可能连续调用多个工具,却没有记录每条结果支持或反驳了哪个假设;还可能在网络重试后重复执行同一个动作。对于聊天机器人,这些问题可能只影响回答质量;对于能够操作生产系统的运维 Agent,它们会直接变成安全风险。
本文以项目中的 APY-013 PostgreSQL 死锁场景为例,解释我们为什么没有把诊断流程写成一段无限 ReAct 循环,而是采用 Plan-Execute-Replan + LangGraph + 证据状态机。APY-013 是一次受控的 Snapshot Benchmark:运行会调用真实模型,但诊断工具返回来自固定快照。它既不是 Docker Live 故障注入,也不是真实生产事故。
先给出结论:
- Plan-Execute-Replan 负责组织调查方法,让每次工具调用都有明确的待验证假设和因果目的;
- LangGraph 把调查方法变成可持久化、可恢复、有界并可审计的状态图;
- 确定性规则负责证据边界和安全底线,LLM 负责规划、语义归纳和必要的语义核验;
- 运维 Agent 首先是一套调查系统,其次才是一套回答系统。
同一个现象,为什么不能直接映射到一个根因
“接口超时”只是影响,不是根因。至少可以构造出下面几条完全不同的因果链:
事务按相反顺序加锁 → PostgreSQL 检测到等待环并终止事务 → 接口失败或超时
连接未归还 → 连接池可用连接降为 0 → 新请求等待连接并超时
上游响应变慢 → Nginx 等待超过 proxy_read_timeout → 返回 504
慢 SQL 扫描大量数据 → 数据库响应时间上升 → 应用请求堆积
这些链条可能产生相似的外部症状,却需要不同证据和不同恢复动作。死锁应该关注 SQLSTATE、等待环和事务资源顺序;连接池耗尽要看 checkout/checkin 生命周期、池容量和 waiter;Nginx 504 则要区分 upstream timeout、连接失败和网关自身限流。
因此,Agent 不能只维护一个“当前答案”,还要维护一组可竞争的假设。例如:
H1: opposite_order_transaction_deadlock
H2: postgres_lock_wait
H3: postgres_slow_query
H4: connection_pool_exhaustion
每条新 Observation 只能在自己的证据范围内改变假设状态。当前实现使用四种 disposition:
supported:证据足以支持该假设;refuted:证据直接反驳该假设;causally_inactive:现象存在,但不处于本次故障的有效因果链中;unresolved:证据仍不足或存在冲突。
只有形成唯一受支持根因、关闭竞争假设,并补齐 trigger → mechanism → impact 因果角色之后,系统才具备进入 Decision 的基础。
这里的关键不是让模型多想几步,而是让“为什么相信这个结论”成为系统中的显式状态。
单次 LLM、ReAct 和固定 Workflow 分别缺什么
ReAct 并不是一种错误方法。对于低风险、步骤较短、工具结果立即可判断的任务,它通常足够直接。问题在于,高风险运维诊断还要求中断恢复、证据审计、调用预算和授权隔离,这些不能只依赖模型在上下文中自觉遵守。
| 方式 | 优点 | 在运维诊断中的主要缺口 | 更适合的场景 |
|---|---|---|---|
| 单次 LLM | 实现简单、响应快 | 没有真实取证;容易把常识写成当前事实 | 告警解释、文档问答 |
| 纯 ReAct 循环 | 能边观察边调用工具 | 计划和停止条件隐含在上下文里;恢复后难以判断已经完成了什么;副作用治理弱 | 低风险、短链路的探索任务 |
| 固定 Workflow | 路径确定、容易测试 | 对未知根因和证据缺口适应性差;容易机械地查完所有工具 | SOP 高度稳定的自动化 |
| Plan-Execute-Replan | 计划显式、可根据证据调整 | 状态和路由设计更复杂,需要定义严格合同 | 多假设、跨数据源、有安全边界的诊断 |
纯 ReAct 常见的抽象是:
Thought → Action → Observation → Thought → ... → Answer
它描述了模型如何交替推理和行动,却没有天然规定:
- 一条 Observation 必须支持或反驳哪个公开假设;
- 什么时候证据已经充分,什么时候只是模型“感觉够了”;
- Worker 在第三步后崩溃,重启时怎样避免重复调用工具;
- LLM Validator 超时时,是否还能授权恢复;
- 一次知识检索是否应该被当成诊断证据;
- 相同恢复动作如何保证最多执行一次。
这些都可以在 ReAct 外围补上,但补到最后,实际上已经在构建一个状态机。与其让关键约束散落在 Prompt、循环和异常处理里,不如一开始就把节点、状态、边和失败路径公开表达出来。
为什么选择 Plan-Execute-Replan
Plan-Execute-Replan 可以先用一个“值班工程师调查事故”的心智模型理解:
- Planner 写调查计划,不直接宣布根因;
- Executor 按计划调用只读工具;
- Fact Adapter 把不同工具结果转成统一证据语言;
- Sufficiency Gate 判断证据是否足够;
- Replanner 只补缺口,不把已经做过的调查全部重来;
- Decision 在证据闭环后生成根因候选;
- Validator 和 Policy Gate 分别核验结论、控制权限。
这个类比的边界也很明确:系统不会真的模拟多个工程师的自由讨论。每个节点必须读写结构化状态,路由由公开条件决定,高风险动作还要经过独立策略门。
Plan 不只是工具列表
一个弱计划可能只有:
{"tool": "InspectPostgresWaitGraph", "arguments": {"service": "order-service"}}
工具执行成功后,系统仍然不知道为什么查它。当前计划合同还会保存:
testsHypotheses:这一步允许检验哪些假设;causalIntent:期待它补充 trigger、mechanism、impact 还是 context;sourceDomain:证据来自 knowledge、runtime、log 或 change;targetComponent:调查哪个组件;- 工具参数和执行顺序。
例如等待图用于验证死锁机制,事务资源顺序用于确定触发条件,错误日志用于确认影响。模型可以生成 Observation 的摘要和语义判断,但不能越权用“查指标”的步骤去反驳所有未被该步骤声明的假设。
Replan 不等于重新生成一切
Sufficiency Gate 会先检查公开状态:是否仍有竞争假设、是否存在至少两条独立正证据、是否补齐因果角色、是否还有未执行且相关的计划步骤。只有确实存在缺口时,才继续执行或进入一次有界 Replan。
当前 evidence-driven-v4 默认最多 Replan 一次,并受总执行步数、模型调用预算、soft deadline 和 hard deadline 约束。这使得 Replan 是“针对证据缺口的有限修正”,而不是模型无限自问自答。
为什么用 LangGraph,而不是手写一个大循环
我们选择 LangGraph,不是因为节点图看起来更像 Agent,而是它恰好提供了运维工作流最需要的几个抽象。
1. 状态
计划、计划索引、Observation、Evidence ID、假设状态、模型调用次数、Replan 次数、恢复提案和验证结果都进入共享状态。节点只处理自己负责的输入和输出,不需要从一整段聊天记录猜测当前进度。
2. 条件边可以公开表达安全决策
Sufficiency Gate → Executor/Replanner/Adjudicator/Decision、Deterministic Validator → Replanner/Manual Review/Recovery Planner 都是代码中的条件边。失败路径不再只是某个 Prompt 里的自然语言要求。
3. Checkpoint 支持恢复和审计
当前图版本为 aiops-diagnostic-v3,运行线程使用类似下面的稳定标识:
aiops:{task_id}:{graph_version}
节点执行还会生成稳定 execution_key,将 task、graph version、node、逻辑迭代和输入指纹绑定起来。同一输入在网络中断后重放时,可以读取已完成结果,避免重复调用 LLM、重复追加假设或再次执行同一个工具。Checkpoint 用于恢复当前状态,追加式 audit event 用于保留完整历史,两者不能混为一张“万能日志表”。
4. 图结构允许受控演进
Single 和 Multi 只在取证阶段分叉,后面仍汇入同一套 Evidence、Decision、Validator 和 Policy 合同。这样可以单独评估 Multi 是否真的带来跨数据源证据增益,而不需要复制整套决策链。
LangGraph 也不是免费的午餐。状态 Schema、节点幂等、Checkpoint 版本兼容和条件边测试都会增加实现成本。如果任务只是查一次服务状态并返回文本,手写函数可能更合适。
当前 evidence-driven-v4 链路是什么样的
下面不是概念图,而是依据当前 main 中图构建代码整理的已实现链路。为了便于阅读,图中省略了部分回环标签,但保留主要节点和失败路径。
flowchart TD
A[Alert / Diagnostic Task] --> K[Knowledge Investigator]
K --> P[Main Planner]
P --> SR{Strategy Router}
SR -->|Single| E[Executor]
SR -->|Deterministic fast path| SG[Sufficiency Gate]
SR -->|Multi| ID[Investigator Dispatch]
SR -->|Multi unavailable| MR[Manual Review]
ID --> RI[Runtime Specialist]
ID --> LI[Log Specialist]
RI --> EA[Evidence Aggregator]
LI --> EA
EA -->|usable packets| FA[Fact Adapter]
EA -->|fallback| E
EA -->|unsafe / unavailable| MR
E --> FA
FA --> SG
SG -->|next useful step| SR
SG -->|plan exhausted, gap remains| RP[Replanner]
SG -->|conflicting unresolved evidence| HA[Hypothesis Adjudicator]
SG -->|decision ready| D[Decision]
HA --> SG
RP -->|new step| E
RP -->|no useful step| D
D --> DV{Deterministic Validator}
DV -->|repairable gap| RP
DV -->|invalid / unsafe| MR
DV -->|valid| RCP[Recovery Planner]
RCP --> VR{Validator Router}
VR -->|semantic risk| LV[Optional LLM Validator]
VR -->|no semantic risk| PG[Policy Gate]
LV --> PG
MR --> PG
PG --> R[Report]
用简化伪代码表示,它更接近一个有状态协议,而不是聊天循环:
# 简化示意,不是可直接运行的项目源码
graph = StateGraph(DiagnosticState)
graph.add_edge(START, "knowledge_investigator")
graph.add_edge("knowledge_investigator", "planner")
graph.add_edge("planner", "strategy_router")
graph.add_conditional_edges(
"sufficiency_gate",
route_after_sufficiency,
{
"executor": "strategy_router",
"replanner": "replanner",
"hypothesis_adjudicator": "hypothesis_adjudicator",
"decision": "decision",
},
)
graph.add_conditional_edges(
"deterministic_validator",
route_after_validation,
{
"replanner": "replanner",
"manual_review": "manual_review",
"recovery_planner": "recovery_planner",
},
)
graph.add_conditional_edges(
"validator_router",
route_validator,
{"llm_validator": "llm_validator", "policy_gate": "policy_gate"},
)
每个节点到底在做什么
与其把节点理解成一群会聊天的角色,不如统一问五个问题:输入是什么、职责是什么、输出什么、下一步如何路由、失败时怎么办。
Knowledge Investigator:提供候选知识,不提供答案
它根据告警和查询检索知识卡、SOP 或历史排查资料,输出带来源的候选知识。RAG 的作用是缩小搜索空间、补充工具选择和排查提示,不是把 Benchmark 的 ground_truth.yaml 注入 Agent。
知识命中本身也不是运行证据。一篇博客说“死锁可能导致超时”,只能帮助 Planner 决定检查等待图,不能证明这次事故就是死锁。因此 knowledge_retrieval 不应占用“诊断工具必须产生 Observation”的评分分母。
Main Planner:把问题拆成可证伪步骤
Planner 读取告警、可用工具、候选知识和公开假设,输出有界计划。每步不仅绑定工具,还绑定 hypothesis、causal intent、数据源和目标组件。
Planner 失败时不能偷偷执行一个猜测工具。结构不合法、参数超出工具合同或角色越权时,系统进行有限格式修复;仍失败则安全终止或转人工,而不是把自由文本当作计划继续执行。
Strategy Router:决定用 Single、Multi 还是快速路径
路由输入包括所需数据域、未解决假设数量、缺失因果角色、是否存在高质量冲突、剩余时间和模型预算等公开特征。
Single 模式由共享 Executor 顺序执行计划。Multi 模式只在需要跨 runtime/log 数据源、且预算和能力满足条件时,分派 Runtime 与 Log Specialist。当前自动发布策略保持保守:Multi 可以在 Benchmark forced mode 中验收,但不能因为“多 Agent 听起来更强”就默认开启。
如果证据已经足够,路由可以走 deterministic fast path;如果 Multi 被强制请求但模型预算不足,则进入人工复核,而不是静默退回一个看似成功的路径。
Specialist 与 Evidence Aggregator:并行取证,统一回到同一事实层
Runtime Specialist 负责数据库、进程、连接池、指标等运行态工具;Log Specialist 负责 CLS 等日志证据。每个 Specialist 可以生成局部计划、调用分配给自己的工具并分析 Evidence,但不能直接产生最终恢复授权。
Evidence Aggregator 对结果去重、校验来源和作用域,生成共享证据包。即使 Specialist 的附加 LLM 分析超时,只要工具 Evidence 已经完整持久化,聚合层也可以保留这些确定性事实;反过来,只有一段漂亮分析却没有 Evidence ID,不能推进根因决策。
Executor:执行计划,不决定真相
Executor 负责选择当前步骤、规范化参数、调用白名单工具、记录工具审计和执行状态。只读诊断工具可以按稳定 tool_call_key 复用;恢复类工具必须额外绑定 recovery_intent_id,对状态未知且不可安全重放的动作只能转人工。
Executor 的输出仍是原始 Observation。它不会因为工具返回 deadlock detected 就直接写入最终根因。
Fact Adapter:把异构 Observation 投影成可比较证据
不同工具的返回格式差异很大:SQLSTATE、等待图、连接池指标、CLS 日志和 Nginx 状态不能直接比较。Fact Adapter 将它们转换为受限的 Diagnostic Fact,并按照 Plan 中的 testsHypotheses 和 causalIntent 更新假设。
这是整条链路中非常容易被忽略的一层。工具调用成功不等于证据解释正确;如果所有 Observation 都被错误标记为 mechanism,系统就无法构建唯一 trigger,更不能生成完整因果链。
Sufficiency Gate:决定“继续查”还是“可以下结论”
当前 Sufficiency 使用确定性状态计算,而不是询问模型“你觉得证据够了吗”。它检查:
- 是否只有一个 supported hypothesis;
- 其他假设是否被 refuted、causally inactive 或仍 unresolved;
- 是否存在独立正证据;
- trigger、mechanism、impact 是否完整且不冲突;
- 是否还能执行与缺口相关的计划步骤;
- 时间、步骤和 Replan 预算是否允许继续。
根据结果,它可以继续执行、进入 Replanner、调用 Hypothesis Adjudicator,或进入 Decision。
Hypothesis Adjudicator:只处理真正的语义冲突
当多条高质量证据对同一假设产生冲突,简单计数无法决定哪条更可信。Adjudicator 可以对限定范围内的公开 Evidence 做语义裁决,但不能读取 Ground Truth,也不能修改未提供证据的假设。
它不是每次都调用的“第二个大模型”。纯确定性证据已经闭环时,跳过它可以降低时延和供应商失败面。
Decision:把证据链收敛为一个候选根因
Decision 输出 component、mechanism、trigger、causal chain、confidence 和 Evidence IDs。这里强调“候选”,因为生成结构正确不代表内容已被证据支持。
系统还允许非常受限的规范化:只有候选本身通过标签、任务 Evidence 归属、唯一 supported hypothesis、无开放竞争项等检查,且缺陷仅是 trigger/causal chain 的表达时,才可以从候选已经引用的 Observation summary 重建结构。它不能从假设描述、评分词表或 Ground Truth 猜答案。
Deterministic Validator:先检查可证明的事实
确定性 Validator 检查 Evidence ID 是否属于当前任务、根因是否对应唯一受支持假设、因果角色是否完整、是否存在未关闭竞争项,以及字段是否符合公开合同。
通过后才进入 Recovery Planner;如果缺口仍可修复并有预算,则回到 Replanner;否则进入 Manual Review。这层是安全底线,不依赖供应商是否在线。
Recovery Planner、Validator Router 与 LLM Validator:结论核验不等于授权
Recovery Planner 生成 no_action、proposal_only、manual_review 或受策略约束的恢复意图。Validator Router 再根据是否请求自动恢复、是否存在高风险结论、是否需要 LLM 语义裁决等条件,决定是否调用独立 LLM Validator。
因此 LLM Validator 是一个条件门,不是每次诊断的必经节点。纯确定性证据闭环、且只生成低风险提案时可以跳过;涉及自动执行或语义风险时才增加一次独立核验。
即使 LLM Validator 返回 valid,它也没有授权能力。最终是否执行由 Policy Gate 决定。模型调用超时、结构化输出解析失败或纠正重试耗尽时,系统必须 fail closed:保留可审计错误分类,进入人工复核,并保持 executionPermitted=false。
Policy Gate 与 Report:最后分离“可以怎么做”和“允许做什么”
Policy Gate 综合工具策略、恢复类型、验证来源和外部授权,决定动作能否执行。Report 只负责把计划、证据、假设变化、根因、验证和策略结果组织成人可读输出。
这意味着报告生成失败不应抹掉已持久化诊断事实;同样,一份语言流畅的报告也不能反向提升恢复权限。
三类路由解决的是三种不同问题
把所有条件边都叫“路由”容易混淆。当前系统至少有三类路由,它们的决策对象完全不同。
| 路由 | 回答的问题 | 主要输入 | 可能输出 |
|---|---|---|---|
| Sufficiency/Replan 路由 | 证据够不够,下一步查什么 | 假设状态、因果覆盖、计划进度、时间预算 | 继续取证、Replan、Adjudicate、Decision |
| Single/Multi Strategy Router | 由谁、从哪些数据源取证 | 数据域、复杂度、冲突、预算、能力可用性 | Single、Multi、fast path、manual review |
| Validator Router | 是否需要额外语义核验 | 恢复意图、语义风险、确定性验证结果 | LLM Validator 或直接 Policy Gate |
第一类优化调查路径,第二类分配调查资源,第三类控制安全成本。把三者合成一个“大模型 Router”,会让失败原因难以定位:究竟是没查到证据、分派错 Agent,还是不该跳过核验?
APY-013:证据都查到了,为什么仍然只有 89 分
APY-013 模拟两个事务以相反顺序获取订单行和库存行,最终触发 PostgreSQL 死锁。一次早期真实运行收集到了三条关键证据:
- PostgreSQL 返回
SQLSTATE 40P01和deadlock detected; - Wait Graph 出现长度为 2 的等待环,且不是普通长事务阻塞;
- 两个事务以相反顺序获取
order-row与inventory-row。
Agent 也正确识别了:
component = order-service
mechanism = opposite_order_transaction_deadlock
但该次真实模型 Snapshot Run 最终为 89 分。重要的是,不能把“分数低”笼统归结为 Agent 不够聪明。审计链将问题拆成了三个不同层次。
第一层:Agent 的根因方向是对的,但因果表达不完整
当时 trigger 和 causal chain 没有完全贴合公开证据。修复不是把正确答案写进 Prompt,而是给 Plan Step 增加 causalIntent,让错误日志、等待图和资源顺序分别承担 impact、mechanism 和 trigger 角色,再由 Fact Adapter 校验 Observation 是否越权。
第二层:Validator 调用失败,不代表证据不足
LLM Validator 返回 model_call_failed。此前系统无法区分供应商超时、structured-output 方法不兼容、JSON 解析失败或 Schema 字段错误。
修复后,Validator 会记录允许列表中的模型名、阶段和错误子分类,不保存 API Key、完整 Prompt、原始响应、异常正文或原始 CLS 日志。如果候选已经通过全部公开确定性检查,但 LLM Validator 不可用,系统可以保留结论,不过只能降级为:
validationOrigin = deterministic_grounded_fallback
recoveryMode = manual_review
executionPermitted = false
这条路径的含义不是“确定性规则替 LLM 猜了答案”,而是“已证明的证据结论可以保留,但外部语义门不可用时绝不扩大执行权限”。
第三层:Scorer 的统计口径也可能错
当时一次 knowledge_retrieval 被计入 completed tool calls,而评分器要求每次 completed tool call 都对应一次 Evidence Evaluation。实际上知识检索提供的是调查上下文,不是本次故障 Observation,于是系统被错误扣除了 observations_evaluated 分数。
修复方式是区分知识工具和诊断工具,而不是降低阈值。后续验收确认诊断 Observation 分母恢复正确。
这次排查建立了一个很有用的分层:
sequenceDiagram
participant A as Agent Workflow
participant T as Diagnostic Tools
participant E as Evidence State
participant V as Validators
participant S as Scorer
A->>T: 按 causalIntent 调用工具
T-->>A: SQLSTATE / Wait Graph / Resource Order
A->>E: 绑定 hypothesis 与 trigger/mechanism/impact
E-->>A: 唯一 supported root cause
A->>V: 提交 Evidence IDs 与候选结论
V-->>A: 确定性通过,LLM 调用失败
A->>A: fail closed,转 manual review
A->>S: 提交不可变运行 Artifact
S-->>A: 分别评价证据、决策、验证与安全闭环
如果没有这条审计链,我们可能会错误地“优化 Agent Prompt”,实际上真正需要修的是 Validator 兼容性和 Scorer 分母;也可能为了提高分数放宽安全门,掩盖真实问题。
LangGraph 带来的复杂度值得吗
值得,但有前提。
当系统同时满足以下多项条件时,显式图通常能带来收益:
- 同一现象存在多个竞争根因;
- 需要跨日志、指标、数据库或变更记录取证;
- 工具调用昂贵、可能超时,或存在副作用;
- 任务需要断点恢复、重试幂等和完整审计;
- 不同结论需要不同级别的核验与授权;
- 希望通过 Benchmark 分辨 Agent、工具、Validator 和 Scorer 的问题。
如果只是查询一个只读 API、按固定模板总结结果,Plan、Replan、Adjudicator 和多层 Validator 可能都是过度设计。一个有超时和类型校验的普通函数,反而更清晰。
即使需要 LangGraph,也要警惕几个反模式:
- 每个节点都调用一次 LLM。 节点是责任边界,不是模型调用配额。确定性逻辑能完成的路由不应交给模型。
- 用更多 Agent 代替证据设计。 多 Agent 只能扩大取证面,不能自动解决 Evidence 合同和聚合问题。
- 把 RAG 命中当作现场证据。 知识告诉我们“应该查什么”,工具 Evidence 才说明“这次发生了什么”。
- Checkpoint 只保存聊天文本。 恢复需要计划索引、执行键、证据和预算等结构化状态。
- Validator 通过就自动执行。 结论正确性与动作授权属于不同控制面。
- 为了 Benchmark 对场景写特判。 正确修复应改善公开合同,并能覆盖同义正确、缺环、冲突和错误机制样例,不能读取 Ground Truth。
总结:把运维 Agent 当成调查系统
“为什么不用一个 ReAct Agent 把所有工具暴露出去?”答案不是 ReAct 能力不够,而是高风险运维诊断还需要一套独立于模型自觉性的工程约束。
Plan-Execute-Replan 让调查目标、工具步骤和证据缺口显式化;LangGraph 让这些状态能够持久化、回环、恢复和审计;Fact Adapter 与 Sufficiency Gate 将“模型觉得合理”转成“证据是否闭环”;Deterministic Validator、可选 LLM Validator 和 Policy Gate 则把事实核验、语义核验与执行授权分开。
最终形成的不是一条追求尽快回答的链,而是一条在不确定性中逐步收敛、在失败时安全退出的链:
先提出可证伪假设
→ 再按因果目的收集证据
→ 关闭竞争根因
→ 生成有 Evidence ID 支撑的结论
→ 独立核验
→ 最后才讨论是否允许恢复
这也是本项目最重要的架构判断:运维 Agent 首先要能证明自己为什么这样判断,其次才是告诉用户应该怎么做。