摘要:为什么现在的 AI Agent(如 Manus)能自主写代码、修 Bug?秘密在于“循环”。传统的 Chain 架构是一条直线,而 Agent 架构是一个圆。
本文将深入剖析Spring AI Alibaba Graph模块,揭示它是如何通过“图论”思想实现复杂任务编排的,并手把手带你构建一个**“会自我反思的代码审计 Agent”**。
第一部分:什么是 Spring AI Alibaba Graph?
在 AI 开发中,我们经常遇到这样的场景:
线性逻辑(Chain):用户提问 -> 检索 RAG -> 生成答案。这是 ChatClient 擅长的。
循环逻辑(Loop):写代码 -> 运行报错 -> 读取错误 -> 修改代码 -> 再运行。这是 Graph 擅长的。
Spring AI Alibaba Graph是一个基于状态机(State Machine)和图论(Graph Theory)的低代码编排框架。它对标的是 Python 界的LangGraph。
核心概念模型
State (状态):一个共享的内存对象(通常是一个 Map 或 Java Record)。所有节点都从这里读数据,往这里写数据。这是 Agent 的“短期记忆”。
Node (节点):执行具体任务的单元(如:调用大模型、执行 SQL、搜索网页)。
Edge (边):连接节点的线。
Normal Edge:做完 A,就去 B。
Conditional Edge:做完 A,根据结果判断,是去 B 还是回 C(实现循环的关键)。
Graph (图):将上述元素编译成一个可执行的 Runnable。
第二部分:源码视角的架构解析
打开 spring-ai-alibaba-graph 的源码(概念模型),你会发现它极其精简。
1. StateGraph (画布)
这是核心入口。
public class StateGraph<T> { // 注册节点 public void addNode(String name, Function<T, T> action) { ... } // 注册边 public void addEdge(String from, String to) { ... } // 注册条件边 (路由逻辑) public void addConditionalEdges(String source, Function<T, String> router, Map<String, String> pathMap) { ... } // 编译成可运行的 APP public CompiledGraph<T> compile() { ... } }2. Checkpoint (存档点) - 高级特性
源码中通常包含持久化接口。这意味着 Agent 运行到一半(比如等待人类审批),可以将当前 State 序列化存入数据库。下次加载时,恢复内存,继续运行。这对于长程任务(Long-running task)至关重要。
3. Saver & Loader
为了支持像JManus那样运行 24 小时,Graph 模块内部实现了状态的快照保存机制。
第三部分:实战——构建一个“自我修正的代码审计 Agent”
我们将构建一个 Agent,它不会写完代码就跑,而是会自己检查。如果发现有 Bug,它会重写,直到通过审查为止。
1. 定义状态 (The Memory)
首先,我们需要定义 Agent 的大脑里存什么东西。
// Agent 的共享内存状态 public class AgentState { private String userRequirement; // 用户需求 private String generatedCode; // 生成的代码 private String reviewComment; // 审查意见 private int retryCount; // 重试次数(防止死循环) // Getters, Setters, Constructor... }2. 定义节点 (The Nodes)
我们需要两个核心节点:Coder(写手)和Reviewer(审核员)。
@Configuration public class GraphConfig { @Autowired private ChatClient chatClient; // 节点 1: 程序员 @Bean public Function<AgentState, AgentState> coderNode() { return state -> { String prompt = "请根据需求写 Java 代码。需求:" + state.getUserRequirement(); if (state.getReviewComment() != null) { prompt += "\n 上次审核没通过,意见是:" + state.getReviewComment() + "。请修复。"; } String code = chatClient.prompt().user(prompt).call().content(); state.setGeneratedCode(code); state.setRetryCount(state.getRetryCount() + 1); System.out.println("👨💻 Coder 已生成代码 (第 " + state.getRetryCount() + " 次)"); return state; }; } // 节点 2: 审核员 @Bean public Function<AgentState, AgentState> reviewerNode() { return state -> { String prompt = "请审核以下代码,如果通过回复 'PASS',否则回复具体修改建议。\n代码:\n" + state.getGeneratedCode(); String comment = chatClient.prompt().user(prompt).call().content(); state.setReviewComment(comment); System.out.println("🧐 Reviewer 意见: " + comment); return state; }; } }3. 定义路由逻辑 (The Router)
这是 Agent 的“判断力”。
@Bean public Function<AgentState, String> checkResultRouter() { return state -> { // 1. 如果审核通过,结束 if (state.getReviewComment().contains("PASS")) { return "END"; } // 2. 如果重试超过 3 次,强制结束(避免死循环耗干 Token) if (state.getRetryCount() >= 3) { return "END"; } // 3. 否则,打回重写 return "RETRY"; }; }4. 组装图 (Assemble the Graph)
现在把积木搭起来。
@Service public class CodeAgentService { @Autowired private StateGraph<AgentState> stateGraph; // 假设这是库提供的 builder public String generateHighQualityCode(String requirement) { // 1. 初始化图构建器 var workflow = new StateGraph<>(AgentState.class); // 2. 添加节点 workflow.addNode("coder", coderNode()); workflow.addNode("reviewer", reviewerNode()); // 3. 定义边 (Edge) workflow.setEntryPoint("coder"); // 起点 workflow.addEdge("coder", "reviewer"); // 写完 -> 审核 // 4. 定义条件边 (Conditional Edge) // 从 reviewer 出来,根据 router 的结果,决定去哪 workflow.addConditionalEdges( "reviewer", checkResultRouter(), Map.of( "RETRY", "coder", // 审核不通过 -> 回去重写 (形成闭环!) "END", "end" // 审核通过 -> 结束 ) ); // 5. 编译并运行 var app = workflow.compile(); // 6. 初始状态 AgentState input = new AgentState(); input.setUserRequirement(requirement); input.setRetryCount(0); // 7. 执行 AgentState result = app.invoke(input); return result.getGeneratedCode(); } }运行效果模拟
当你调用 generateHighQualityCode("写一个冒泡排序") 时,控制台可能会打印:
--- 🚀 任务开始 --- 👨💻 Coder 已生成代码 (第 1 次) 🧐 Reviewer 意见: 逻辑大致正确,但缺少边界条件检查,数组为空会报错。 --- 🔄 触发 RETRY 路由,回滚至 Coder --- 👨💻 Coder 已生成代码 (第 2 次) -> AI 读取了意见,添加了 if(arr == null) 判断 🧐 Reviewer 意见: PASS --- ✅ 触发 END 路由,任务完成 ---这就是JManus等项目的核心原理:通过图结构,让 AI 拥有了“自我反思”和“迭代优化”的时间窗口。
总结
Spring AI Alibaba Graph的出现,填补了 Java 生态在 Agent 编排领域的空白。
对比 ChatClient:ChatClient 是“射箭”(开弓没有回头箭),Graph 是“自动驾驶”(实时感知路况并调整方向)。
适用场景:代码生成与修复(如本例)、长篇报告写作(大纲 -> 写草稿 -> 润色 -> 总结)、复杂多步任务(如:制定旅游计划 -> 订票 -> 失败则修改计划 -> 再订票)。
目前Spring AI Alibaba Graph的定位似乎有了新的变化,目前官方也做了重新规划,新的消息待确认后在Spring AI Alibaba系列和大家同步。
欢迎关注、一起学习、一起进步~