LangGraph 学习笔记:为什么 Agent 需要状态图
这篇不是照着文档翻译,而是从一个学生做自动化运维 Agent 的角度,记录我为什么觉得 LangGraph 的状态、节点、边和检查点很重要。
我一开始接触 Agent 的时候,脑子里想的其实很简单:模型先分析问题,然后调用工具,拿到结果后继续分析,最后输出答案。也就是大家经常说的 ReAct。这个思路很直观,而且写一个 Demo 也不难。
但后面我在想自动化运维 Agent 的时候,发现一个问题:真实任务不是三五步就结束的。一次排障可能要查日志、查指标、查最近发布、查知识库、比较多个根因,还可能要等人确认。这个时候如果只是写一个 while 循环,让模型自己决定下一步,就会有点不可控。
LangGraph 吸引我的地方就在这里。它不是单纯让 Agent “更聪明”,而是让 Agent 的执行过程更像一个可管理的工作流。
ReAct 的问题不是不能用,而是不够稳
ReAct 很适合短任务,比如:
用户问:Redis big key 怎么排查?
模型:调用知识库搜索。
工具:返回文档。
模型:总结回答。
这种场景下没什么问题。但如果任务变成:
订单服务超时率升高,请定位可能原因并给出处理建议。
Agent 可能需要做很多事情:
- 先判断告警等级。
- 查询最近 10 分钟错误日志。
- 查询接口耗时指标。
- 查询数据库连接池指标。
- 查询最近发布记录。
- 检索历史故障文档。
- 根据证据排序根因。
- 生成报告。
- 如果建议重启服务,还要人工确认。
如果所有步骤都放在一个循环里,出了问题很难判断:到底是模型规划错了,还是工具返回错了,还是上下文太长导致模型忘了前面的证据?
State 不只是聊天记录
LangGraph 里最核心的概念之一是 State。刚开始我以为 State 就是 messages,也就是对话历史。后来发现如果这么理解就太窄了。
对一个运维 Agent 来说,State 应该保存的是“任务继续执行所需的信息”,比如:
{
"alert": {
"service": "order-service",
"level": "P1",
"message": "接口超时率升高"
},
"plan": [
"查询错误日志",
"查询数据库连接池指标",
"查询最近发布"
],
"evidence": [],
"currentStep": 0,
"riskLevel": "LOW",
"finalReport": null
}
这样每个节点都不是从一大段聊天记录里猜当前状态,而是直接读结构化字段。这个设计对后端开发很友好,因为它像是在维护一个工作流实例。
Node 要小一点
我以前写代码也容易犯一个问题:一个方法里塞太多逻辑。Agent 节点也是一样。如果写一个万能节点,让它既规划、又查日志、又总结、又决定是否人工确认,那后面一定不好调。
我会把节点拆得细一点:
planner:只负责生成排查计划。query_logs:只负责查日志。query_metrics:只负责查指标。query_deployments:只负责查发布记录。knowledge_search:只负责查知识库。rank_hypotheses:根据证据排序可能根因。human_review:等待人工确认。reporter:生成最终报告。
这样做的好处是,每个节点的输入输出都比较清楚。比如 query_logs 节点输入服务名、时间范围、关键词,输出日志摘要和原始证据 ID。它不需要知道最后报告怎么写。
Edge 让流程可控
边决定下一步执行哪个节点。这个东西看起来像流程图,但我觉得它的意义比流程图大,因为它把“控制逻辑”从 prompt 里拿出来了。
比如可以写一些条件:
- 如果日志里出现大量数据库连接超时,就进入
query_metrics。 - 如果最近 30 分钟有发布,就进入
query_deployments。 - 如果证据不足,就回到
planner补充排查步骤。 - 如果下一步动作是重启、切流、修改配置,就进入
human_review。 - 如果根因置信度够高,就进入
reporter。
这比完全相信模型自由决策更稳。模型还是可以参与判断,但它的判断被状态图限制在合理范围内。
Checkpoint 是我觉得最工程化的点
如果只是课堂作业或者 Demo,失败了重新跑一遍也没关系。但真实系统不行。假设 Agent 已经查了 5 个工具,结果生成报告时服务重启,如果没有 checkpoint,就只能重新查一遍。
这会带来几个问题:
- 重复消耗 token。
- 重复调用日志和指标平台。
- 某些工具调用可能不是幂等的。
- 用户不知道任务执行到哪一步。
所以 checkpoint 很重要。每执行完一个节点,就把 State 保存下来。失败后可以从最近的节点继续执行。
如果我用 Java 后端实现类似能力,可能会设计几张表:
agent_workflow_instance
- id
- conversation_id
- current_node
- status
- created_at
- updated_at
agent_workflow_checkpoint
- id
- workflow_id
- node_name
- state_json
- created_at
agent_tool_invocation
- id
- workflow_id
- tool_name
- request_summary
- response_summary
- cost_ms
- status
这套表不一定完美,但至少能支撑恢复、审计和排查。
Human-in-the-loop 不是为了显得高级
很多 Agent 文章都会提 Human-in-the-loop,但有时候写得像概念。我觉得它在运维场景里是刚需。
比如 Agent 分析后建议:
可以尝试重启 order-service 的两个异常实例。
这个动作不能让模型直接执行。更合理的方式是进入人工确认节点:
- Agent 汇总当前证据。
- 说明为什么建议重启。
- 列出可能影响。
- 等待用户批准、拒绝或修改方案。
- 根据用户选择继续执行。
这样 Agent 的定位更清楚:它可以帮忙分析、整理证据、提出建议,但高风险操作还是由人负责确认。
和 Spring AI 怎么结合
我现在的理解是,Spring AI 更偏“模型能力接入”,LangGraph 更偏“Agent 流程组织”。如果放在 Java 项目里,可以用 Spring AI 做:
ChatClient调模型。- Tool Calling 调内部服务。
- RAG 查知识库。
- Advisor 做记忆、日志、权限。
然后用 LangGraph 的思想组织流程:
- State 保存任务上下文。
- Node 封装每一步动作。
- Edge 控制下一步。
- Checkpoint 保存状态。
- Human node 等待人工确认。
这两个东西结合起来,才比较像一个能落地的 Agent 系统。
小结
LangGraph 对我最大的启发是:复杂 Agent 不能只靠一个大 prompt 和一个循环。它需要状态、流程、恢复和边界。
如果说 ReAct 适合快速做出一个能用的 Demo,那么状态图更适合把 Demo 往工程系统推进。作为学生项目,我觉得不一定要一上来实现得特别完整,但至少要能讲清楚为什么需要 State、为什么要拆 Node、为什么高风险动作要人工确认。这样项目听起来会比“我接了一个大模型接口”扎实很多。