news 2026/10/14 3:48:01

Spring AI Alibaba Graph:从ReAct循环到图编排的Java Agent工程化实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AI Alibaba Graph:从ReAct循环到图编排的Java Agent工程化实践

这次我们来看一个 Java 后端与大模型结合的工程化方向:在 Spring Boot 生态里,用 Spring AI Alibaba 的 Graph 能力,把一个传统 ReAct/Loop 的 AI Agent,重构成显式、可编排、可观测的图结构工程。

先直接说结论:这个方向最值得关注的点不是“能不能调大模型”,而是“Agent 的每一步怎么编排、怎么中断、怎么恢复、怎么观测”。如果把 ReAct 写成一次性 while 循环,项目能跑,但换一个场景就要重写。用 Graph Engineering 把节点、边、状态、条件分支拆开,Agent 才能从 Demo 变成可以维护的工程系统。这也是标题里“工程化”三个字的含义:不是把模型响应返回出去就结束,而是把推理过程当作一条有起点、有终点、有分支的执行链路。

本文会按工程化实操来展开。先梳理 ReAct 与 Graph 的关系,再给出一个基于 Java + Spring AI Alibaba 的最小可运行工程,包含环境准备、启动方式、节点设计、接口调用、批量任务、性能观察和排查清单。适合正在用 Java 做 AI Agent、但对图编排还不够熟的读者。如果你团队里已经定了 Java 技术栈,又想接大模型 Agent,这篇可以直接收藏作为落地参考。

1. 核心能力速览

能力项说明
技术定位Java 侧 AI Agent 编排,将 ReAct 循环改造成显式图结构
核心技能组合ReAct / Loop + Tool Calling + Graph Engineering + Spring AI Alibaba
运行环境JDK 17+、Maven、Spring Boot 3.x、一个可访问的大模型服务
模型接入阿里云百炼 DashScope 原生支持;其他兼容 OpenAI 的模型服务也可配置
主要功能多节点 Agent 编排、条件分支、工具调用、状态管理、接口暴露
部署形态Spring Boot 应用,支持 REST API、异步任务、批量任务
是否需要 GPU不需要;推理在云端或外部模型服务完成,本地只做编排
本地显存/内存不跑本地模型时无显存要求;JVM 内存按常规 Spring Boot 分配
启动方式命令行启动,配置文件指定模型 Key;本地模型端点也能接
是否支持 API支持;本身是 Spring Boot 服务,可以暴露 /api/agent 等接口
是否支持批量任务支持;可基于消息队列或线程池做批量提交
适合场景业务系统中需要可控、可审计、可重试的 AI Agent 服务

这里需要解释清楚一件容易混淆的事:这套 Java 工程本身是编排层,不负责跑大模型。Agent 的算力消耗发生在 LLM 调用那一侧,如果接云端模型,本地不需要 GPU;如果接本地部署的大模型,显存压力在模型服务进程上,不在 Spring Boot 进程上。所以文章后面不会再围绕显存展开,重点会放在线程、内存、QPS 和调用延迟这些后端指标上。

2. 为什么 Agent 要用 Graph,而不是 while 循环

2.1 ReAct 的本质

ReAct 是 Reasoning + Acting 的组合,核心循环是“推理 → 行动 → 观察 → 再推理”。LLM 在每一轮里会决定:调用某个工具、直接输出最终答案、或者要求补充信息。写代码时最容易出现的形态是 while 循环:

while (round < maxRound) { String response = chatClient.prompt().user(input).call().content(); if (containsToolCall(response)) { String result = executeTool(response); input = input + result; round++; } else { return response; } }

这种写法不是不能用,只是当你有多个工具、多个分支、需要人工确认、需要把过程记录下来做审计时,循环体里的 if/else 会迅速膨胀。逻辑分散在局部变量里,排查问题时只能靠日志和肉眼看代码。

2.2 Graph 把控制流变成数据结构

Graph Engineering 的思路是:不要用代码写死流程,而是把流程描述成“节点 + 边 + 状态”。节点负责具体动作,比如调用 LLM、调用工具、格式化输出;边负责决定下一步走去哪;状态对象在节点之间传递上下文。

维度普通 while 循环Graph 编排
步骤个数写死在循环体内节点可独立增删
分支条件if/else 堆在代码里条件边显式声明
状态传递局部变量来回传统一 State 对象
可观测性日志需要手动埋点节点前后可统一埋点
复用性低,换场景重写高,节点可复用
测试方式整体跑一遍才知道结果每个节点可单独测试

Spring AI Alibaba 提供的 Graph 模块,本质上就是把这类能力搬到 Java 生态里。你不需要再自己实现一套有向无环图框架,直接用它的节点、边、状态模型来组织 Agent 即可。完整类名和 API 签名随版本迭代可能调整,但核心范式是稳定的:Node 做执行,Edge 做路由,State 做传递。

3. 适用场景与使用边界

3.1 适合什么业务

图编排型 Agent 最适合的是“流程固定、分支多、需要审计”的业务,比如:

  • 企业客服助手:接收用户问题,判断是否需要查订单、查库存,再结合结构化数据回答。
  • 数据查询 Agent:把自然语言问题解析为 SQL 或 API 参数,执行后返回结果。
  • 多工具协同工作流:需要调用内部接口、搜索、计算器等多个工具。
  • 需要人工确认的审批类流程:Agent 在关键节点停下来,等用户确认后再继续。
  • 合规要求较高的系统:每一步执行了什么工具、传了什么参数、模型返回了什么,都能在状态对象里留痕。

这些场景的共同点是:任务不是一次模型调用能完成的,而且中间存在明确的工具调用边界。Graph 能把边界拆出来,每一段都可测试。

3.2 不适合什么场景

不需要把简单问题复杂化。一次问答、单轮摘要、普通文本分类,这些场景直接调模型接口即可,引入 Graph 反而增加维护成本。

另外,如果业务对延迟有硬性要求,比如期望单次请求百毫秒内返回,那么瓶颈在 LLM 推理本身,不在编排层。图编排不能降低模型推理时间,只能帮你把“调用模型”和“调用工具”的流程组织得更稳定。真正降低延迟需要靠模型选型、流式输出、缓存和并发控制。

3.3 安全边界与合规提醒

Agent 一旦带上工具调用能力,就相当于开放了一个“AI 可以触发真实操作”的入口。第一个风险是越权调用,必须有用户身份校验、工具权限校验和操作审计;第二个风险是数据合规,调用企业内部系统、读取个人信息之前,要确认数据权限和隐私合规要求;第三个风险是模型输出的可靠性,无论模型表达得多肯定,涉及支付、删除、修改等关键动作时,必须有人工确认节点。

如果你的 Agent 还会处理音视频、图像、人脸、声音等素材,更要在数据采集前取得明确授权,并且只在不扩散、不滥用的隔离环境中处理。这部分不是配置问题,是上线前必须完成的评审项。

4. 环境准备与前置条件

4.1 开发环境清单

项目要求备注
JDKJDK 17+推荐 JDK 21,Spring Boot 3.x 对该版本支持较好
构建工具Maven 3.6+ 或 Gradle 7+文章示例使用 Maven
Spring Boot3.2 及以上需要与 Spring AI 版本匹配
模型服务任意兼容 Spring AI 的模型服务可以使用 DashScope,也可以使用本地模型服务
网络能访问模型服务地址本文示例以云端 API 为主

Spring AI 的版本和 Spring AI Alibaba 的版本是独立发布的。写依赖时要注意版本号不是越高越好,而是要匹配。建议先去 Maven 中央仓库或 Spring 官方文档确认当前稳定版本,再写进 pom.xml。文章下面的依赖版本号只是结构示例,实际部署以仓库最新稳定版为准。

4.2 Maven 依赖

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.4</version> <relativePath/> </parent> <properties> <java.version>17</java.version> <spring-ai-alibaba.version>0.3.0</spring-ai-alibaba.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter</artifactId> <version>${spring-ai-alibaba.version}</version> </dependency> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-graph</artifactId> <version>${spring-ai-alibaba.version}</version> </dependency> </dependencies>

如果你的团队已经用过 Spring AI 的 OpenAI 兼容适配,也可以保留 spring-ai-starter-model-compatible-openai,让 Agent 服务同时支持多个模型来源。需要注意:同时引入多个 starter 时,Spring 的自动配置会生效其中一套,最好在配置里显式指定 model 类型,避免启动时出现多个 ChatModel Bean 冲突。

4.3 模型服务配置

以 DashScope 为例,创建一个application.yml:

spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} base-url: https://dashscope.aliyuncs.com/api/v1 chat: options: model: qwen-plus server: port: 8080

不推荐把 API Key 直接写在配置文件里。用环境变量DASHSCOPE_API_KEY注入,能避免把密钥提交到 Git 仓库。启动前先确认模型服务地址可达,如果之前没申请过 Key,控制台会直接提示 401。

5. Spring AI Alibaba Graph 工程化落地:最小可运行 Agent

5.1 设计一个最小流程

先做一个小目标:一个 Agent 能根据用户提问,选择直接回答或调用天气查询工具。流程拆成四件事:

  1. 接收用户输入,构造 Agent 状态。
  2. 调用 LLM,判断是否要查天气。
  3. 如果要查天气,执行工具,并把结果放回状态。
  4. 再次调用 LLM,生成最终答案。

5.2 定义状态对象

状态对象是图里传递上下文的核心。把所有节点需要的数据都放进一个 AgentState,不要用全局变量或散落的局部变量:

import java.util.ArrayList; import java.util.List; public class AgentState { private String userInput; private String lastReply; private String toolName; private String toolArguments; private String toolResult; private String finalAnswer; private int round = 0; private List<String> trace = new ArrayList<>(); public boolean needTool() { return toolName != null && !toolName.isBlank(); } public void addTrace(String step) { trace.add("[" + System.currentTimeMillis() + "] " + step); } public int getRound() { return round; } public void setRound(int round) { this.round = round; } public String getUserInput() { return userInput; } public void setUserInput(String userInput) { this.userInput = userInput; } public String getLastReply() { return lastReply; } public void setLastReply(String lastReply) { this.lastReply = lastReply; } public String getToolName() { return toolName; } public void setToolName(String toolName) { this.toolName = toolName; } public String getToolArguments() { return toolArguments; } public void setToolArguments(String toolArguments) { this.toolArguments = toolArguments; } public String getToolResult() { return toolResult; } public void setToolResult(String toolResult) { this.toolResult = toolResult; } public String getFinalAnswer() { return finalAnswer; } public void setFinalAnswer(String finalAnswer) { this.finalAnswer = finalAnswer; } public List<String> getTrace() { return trace; } }

round 字段一定要加。即使 Graph 可以构造循环边,也必须给 Agent 设置最大轮次,防止模型调用工具后一直无法收敛,白白消耗 Token 并拖死服务。

5.3 编写 LLM 节点

一个节点只负责一件事。LLM 节点只负责把当前状态转成提示词,调用模型,并把返回结果写回状态:

import org.springframework.ai.chat.client.ChatClient; public class LLMNode implements Node<AgentState> { private final ChatClient chatClient; public LLMNode(ChatClient chatClient) { this.chatClient = chatClient; } @Override public AgentState execute(AgentState state) { String userText = state.getUserInput(); String toolContext = state.getToolResult() == null ? "" : "工具返回结果:" + state.getToolResult(); String prompt = """ 你是一个 Java 技术生态里的智能助手。 当用户的问题需要实时天气信息时,必须返回工具调用指令: {"tool":"getWeather","args":{"city":"城市名"}} 如果问题不需要工具,直接返回自然语言答案。 目前系统上下文:%s """.formatted(toolContext); String response = chatClient.prompt() .system(prompt) .user(userText) .call() .content(); state.setLastReply(response); state.addTrace("LLM 调用完成,返回长度:" + response.length()); return state; } }

在真实项目中,LLM 返回内容可能需要解析成结构化工具调用。Spring AI 的 Tool Calling 支持直接声明工具方法,框架会帮助把模型输出解析成参数。这里为了展示 Graph 控制流,先采用字符串解析的模式,逻辑直观,不依赖太多框架魔法。

5.4 编写工具节点

工具节点负责解析 LLM 返回内容,并执行真正的工具逻辑:

public class ToolsNode implements Node<AgentState> { @Override public AgentState execute(AgentState state) { String reply = state.getLastReply(); // 简化解析:从 JSON 中取 tool 与 args // 真实项目建议用 Jackson 解析 if (reply.contains("getWeather")) { state.setToolName("getWeather"); // 真实场景需要从 args 中解析城市 state.setToolArguments("city: 杭州"); state.setToolResult(queryWeather("杭州")); } else { state.setToolName(null); } state.setRound(state.getRound() + 1); state.addTrace("工具节点执行完成,round=" + state.getRound()); return state; } private String queryWeather(String city) { // 替换为真实天气服务、企业内接口或数据库查询 return city + ":晴,28 摄氏度,体感温度 30"; } }

工具节点最容易犯的错误是:把 LLM 返回解析和工具执行混在同一个方法里。建议拆成两个步骤,解析失败时能单独定位。工具节点还应该对参数做强化校验,因为模型可能产生幻觉参数,不能直接拿参数去调用真实接口。

5.5 建图并设置条件边

图的核心是画清楚节点之间的路径。这里有三条规则:

  • 入口是 llm 节点。
  • llm 判断是否需要工具,需要则走 tools,不需要则走 finish。
  • tools 执行完回到 llm,继续下一轮推理。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AgentGraphConfig { @Bean public AgentGraph agentGraph(ChatClient chatClient) { Graph<AgentState> graph = new Graph<>(AgentState::new); graph.addNode("llm", new LLMNode(chatClient)); graph.addNode("tools", new ToolsNode()); graph.addNode("finish", new FinishNode()); graph.setEntryPoint("llm"); graph.addEdge("llm", "tools", state -> state.getLastReply() != null && state.getLastReply().contains("getWeather")); graph.addEdge("llm", "finish", state -> !state.needTool()); graph.addEdge("tools", "llm"); return new AgentGraph(graph); } }

代码里的Node<AgentState>、Graph<AgentState>、AgentGraph是按模块通用模型写的模板,实际包名和构造方式以 Spring AI Alibaba 当前版本为准。你不需要一字不差照搬,关键是理解这条链路:入口节点、条件边、回头边、结束节点。把这几样画清楚,Agent 的执行路径就清晰了。

5.6 结束节点

结束节点负责把最终答案从状态对象里捞出来。它不调用模型,也不执行工具,只做收尾:

public class FinishNode implements Node<AgentState> { @Override public AgentState execute(AgentState state) { if (state.getFinalAnswer() == null) { state.setFinalAnswer(state.getLastReply()); } state.addTrace("Agent 结束,总轮次:" + state.getRound()); return state; } }

这里有个细节:最后一步不要把上一轮的历史对话拼进业务返回。很多 Agent 在结束节点才发现问题,其实是因为前面几轮的历史消息混进了最终结果。结束节点里保留最小量的必要结果即可。

5.7 启动服务

启动方式和普通 Spring Boot 应用没有区别:

mvn spring-boot:run

启动后观察日志。只要没有报 Bean 创建失败,说明依赖和配置基本没问题。接下来就可以直接构造一个 AgentState,调用图执行:

AgentState state = new AgentState(); state.setUserInput("杭州现在多少度?"); state.setRound(0); AgentState result = agentGraph.execute(state); System.out.println(result.getFinalAnswer()); System.out.println(result.getTrace());

这段代码可以写在测试类里,也可以写在 CommandLineRunner 里。第一次跑通 Graph 只需要几秒钟,关键在于看清控制台里打印的 trace 顺序:llm 节点先执行,然后 tools,再回到 llm,最后 finish,整个过程不超过两轮。

6. 功能测试与效果验证

6.1 测试一:不需要工具的直接问答

输入“介绍一下你自己”,预期路径是 llm → finish。用最简单的问题先验证链路是通的。

判断标准:

  • trace 里没有 tools 节点的执行记录。
  • 最终答案来自模型的一次直接返回。
  • round 最终为 0 或 1。

如果这一步失败,优先检查 DashScope 的 api-key 和模型名称是否正确。常见的 401 或 404 错误都能在启动日志里直接看到。

6.2 测试二:需要工具调用的多轮流程

输入“杭州现在多少度”,预期路径是 llm → tools → llm → finish。

判断标准:

  • trace 里出现 tools 节点执行记录。
  • 最终答案中包含“工具返回结果”里的天气信息。
  • round 最终不超过 2。

这一步能跑通,说明条件边和回头边都工作正常。如果 tools 节点执行完没有回到 llm,要检查graph.addEdge("tools", "llm")是否配置正确。如果 return 后直接结束了,检查 finish 节点的条件路径是否写错。

6.3 测试三:循环上限保护

构造一个永远触发工具的输入,比如连续提问“查询天气、继续查询、再查询”。在真实配置里,Graph 要加一个最大轮次判断。当 round 超过 3 时强制走 finish 节点,避免无限循环消耗 Token。

graph.addEdge("llm", "finish", state -> state.getRound() >= 3);

判断标准:达到上限后 Agent 能正常返回,而不是卡死。即使返回的答案质量可能一般,服务也不应该崩溃。这一步验证的是系统的鲁棒性,不是回答质量。

6.4 测试四:工具异常模拟

修改 ToolsNode,让 queryWeather 方法抛出一个异常。观察 Graph 执行是否会把异常打印出来,是否影响下一个请求。

判断标准:

  • 当前请求返回失败响应,但服务进程不退出。
  • 下一次请求能正常执行。
  • 如果使用的是事件循环或虚拟线程,线程池不能被异常污染。

工具调用是 Agent 中最容易出问题的环节。外部服务超时、参数格式不对、返回内容过长,都可能触发异常。Graph 只能编排控制流,不能替你做异常兜底,这需要在节点实现里自己处理。

7. 接口 API 与批量任务接入

7.1 暴露 REST 接口

工程化落地必须让 Agent 变成服务,否则没法给前端、微信小程序或内部系统调用。写一个简单的 Controller:

import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/agent") public class AgentController { private final AgentGraph agentGraph; public AgentController(AgentGraph agentGraph) { this.agentGraph = agentGraph; } @PostMapping("/invoke") public AgentResponse invoke(@RequestBody AgentRequest request) { AgentState state = new AgentState(); state.setUserInput(request.query()); state.setRound(0); AgentState result = agentGraph.execute(state); return new AgentResponse(result.getFinalAnswer(), result.getRound(), result.getTrace()); } }

配合一个请求体和响应体:

public record AgentRequest(String query, String sessionId) { } public record AgentResponse(String answer, int round, java.util.List<String> trace) { }

调用示例:

curl -X POST http://127.0.0.1:8080/api/agent/invoke \ -H "Content-Type: application/json" \ -d '{"query":"杭州现在多少度?","sessionId":"test-001"}'

接口能跑通之后,前端只需要关心这个 POST 接口。Agent 内部是走一条链还是两条链,前端完全不感知。这也是把 Graph 封装成服务的价值:内部复杂度不会泄漏到接口层。

7.2 批量任务设计

批量任务是 Agent 工程化的常见需求。直接把一批文本循环执行 Graph,不一定可靠,因为:

  • LLM 服务有 QPS 和 Token 并发限制。
  • 单个请求失败时没有重试机制。
  • 数据量大时线程池容易被打满。

建议做法是:用一个有界线程池,配合 CompletableFuture 做异步批量提交:

import java.util.List; import java.util.concurrent.*; @Service public class BatchAgentService { private final AgentGraph agentGraph; private final ExecutorService executor = new ThreadPoolExecutor( 4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy() ); public BatchAgentService(AgentGraph agentGraph) { this.agentGraph = agentGraph; } public List<CompletableFuture<AgentState>> runBatch(List<String> queries) { return queries.stream() .map(query -> CompletableFuture.supplyAsync(() -> { AgentState state = new AgentState(); state.setUserInput(query); state.setRound(0); return agentGraph.execute(state); }, executor)) .toList(); } }

ThreadPoolExecutor 的 CallerRunsPolicy 很关键。它意味着当队列满了,新任务不会丢弃,而是由提交线程自己执行。对批量任务来说,宁可慢一点,也不能丢请求。

7.3 批量任务的失败重试建议

即使有线程池和队列,模型调用也可能因为限流、网络抖动失败。更稳妥的做法是给任务加状态字段:PENDING、RUNNING、SUCCESS、FAILED。失败任务进入重试队列,最多重试 3 次,每次间隔按指数退避。这里给出一个简化的重试策略:

重试次数等待时间备注
11 秒网络瞬时抖动
25 秒模型服务限流恢复
330 秒长时间超时或服务异常

如果第 3 次仍然失败,不要无限重试,直接把任务标记为 FAILED 并写入日志。批量任务跑完后,统计成功率和平均轮次,这些数据会在后面性能观察里用到。

8. 资源占用与性能观察

8.1 观察哪些指标

这套系统的资源占用要分两层看:

  • JVM 编排层:CPU、内存、线程数、活跃连接数。
  • 模型调用层:单次 LLM 调用延迟、Token 消耗、限流次数。

如果模型跑在云端,JVM 内存可能不是瓶颈。最值得关注的是线程池队列长度和模型接口的 P95 延迟。队列一旦堆满,说明并发配置与模型 QPS 不匹配。

8.2 如何观察

Spring Boot 项目直接引入 Actuator 依赖后,可以查看线程和指标:

management.endpoints.web.exposure.include=health,metrics,threaddump management.metrics.enable.jvm=true

观察命令:

curl http://127.0.0.1:8080/actuator/health curl http://127.0.0.1:8080/actuator/metrics/jvm.memory.used curl http://127.0.0.1:8080/actuator/threaddump

如果不想引入额外依赖,也可以用 JDK 自带工具:

jps -l jstat -gc <pid> 1000 jstack <pid> > dump.log

jstack 可以看到线程是否卡在某个工具调用上。批量任务卡死的常见原因就是某个外部工具接口没有设置超时,导致线程一直阻塞。

8.3 如何降低 Token 消耗

Graph 编排不会直接降低 Token 消耗,但可以帮你减少无效轮次:

  • 在 LLM 节点里写清楚“如果不确定是否要调用工具,就直接回答”。
  • 在条件边里加最大轮次限制。
  • 在工具节点把返回结果裁剪到必要长度。
  • 对长文本做分段处理,不要每次都把完整历史塞进去。

真正消耗 Token 的地方是历史消息越长,每次调用成本越高。Graph 虽然把上下文放进了 State,但不等于要把所有历史都发给模型。可控的做法是:每次只发送“系统提示词 + 当前输入 + 最近一轮工具结果”,而不是整个 State。

9. 常见问题与排查方法

问题现象可能原因排查方式解决方案
启动报 Bean 创建失败依赖版本与 Spring Boot 不匹配查看完整堆栈,确认是哪几个 Bean 冲突调整 spring-ai-alibaba 版本,或移除重复的 ChatModel 配置
请求返回 401API Key 配置错误或没有权限检查环境变量 DASHSCOPE_API_KEY 是否注入重新配置 Key,确认模型权限已开通
Agent 一直循环,不结束条件边判断错误,needTool 一直返回 true打印 trace 中的 round 和 lastReply添加 maxRound 条件边,超限强制 finish
工具节点报参数解析失败模型返回的 JSON 格式与解析代码不匹配打印原始 lastReply用 Jackson 严格解析,并加异常兜底
批量任务线程被占满线程池太小或外部接口无超时jstack 查看线程卡点增大队列容量,给外部调用加 socket 超时
中文显示乱码控制台编码问题检查 JVM 默认 charset启动参数加 -Dfile.encoding=UTF-8
接口响应很慢单次 LLM 调用耗时长,或触发了多次工具调用观察 trace 每轮耗时开启流式输出、缩短提示词、减少最大轮次
结果质量不稳定提示词不稳定,或模型温度设置过高对比不同输入下的输出固定 temperature,使用更明确的 few-shot 示例

排查顺序建议遵循先外后内:先看模型服务通不通,再看配置对不对,最后看图编排逻辑。绝大多数第一次跑不通的问题,集中在 API Key 和依赖版本这两类。

10. 最佳实践与使用建议

10.1 从小图开始

不要一上来就画一个包含十几条边的复杂图。先把 LLM 和工具两个节点跑通,再逐步增加条件分支、人工确认节点、异常节点。每加一条边,都要单独测试一次。图结构虽然比循环清晰,但复杂图同样有维护成本。

10.2 保留最小可运行配置

把 API Key、模型名、超时时间、最大轮次都抽到配置文件里,不要硬编码在代码中。这样换一个环境测试时,只需要修改配置,不需要改代码。建议保留一个单独的 local 配置,用来在本地环境快速验证图是通的。

10.3 使用日志与追踪追踪 Agent 流程

Graph 的 State 对象是非常好的日志载体。把每个节点的入参、出参、耗时、Token 用量写进一个 trace 列表,方便后续排查和审计。特别要注意:日志里不要输出完整的 API Key,也不要输出用户敏感信息。涉及个人信息时,只记录必要字段。

10.4 权限与合规

这是整个工程化过程中最容易被忽略的部分。一个带工具调用的 Agent,等于一个自动化的业务执行器。任何外部输入,都可能影响工具参数;工具返回的数据,也可能被模型错误引用。上生产前必须做到:

  • 对每个工具调用做参数白名单校验。
  • 对用户身份做鉴权,防止越权调用。
  • 涉及人脸、声音、个人信息、企业敏感数据时,确认授权范围。
  • 模型输出在面向用户前做一轮过滤,屏蔽不当内容和错误事实。

10.5 接口访问范围控制

Agent 服务一旦暴露出去,就变成了内网或公网的一个新入口。建议把 Agent 接口放在网关后面,限制来源 IP 和调用频率。批量任务的 API 要加鉴权,防止被恶意刷量消耗模型费用。可以直接通过网关配置 RateLimit 策略,也可以自己在服务里用 Redis 计数。

11. 总结与下一步

这个方向最值得尝试的点很简单:用图的思想替换掉脑子里的 while 循环。ReAct 本身不复杂,复杂的是后续的维护、观测和扩展。Graph Engineering 把 Agent 的每一步变成节点和边,让整个执行过程从“读代码猜逻辑”变成“看状态看路径”。对于 Java 技术栈团队,Spring AI Alibaba 的 Graph 模块提供了一个现成的落地方案,不用自己造有向无环图框架。

第一次接触的话,先验证两个基础功能:一个不带工具的直答流程,一个带工具调用的两轮流程。跑通这两条链路,再考虑批量任务、异步处理、人工确认和接口权限。最容易踩的坑有两个:一个是依赖版本不匹配导致启动失败,一个是 Agent 循环没有设置最大轮次。前者通过查看 Maven 依赖树解决,后者在条件边里加一个 round 判断就能避免。

后续扩展方向可以从三个角度考虑:一是把 ToolsNode 扩展成一个工具注册中心,让不同业务团队注册自己的工具;二是接入消息队列,让 Agent 处理耗时长、可异步的任务;三是引入人工确认节点,在关键操作前暂停流程。每一步都比继续维护 while 循环更有工程价值。如果这篇文章对你有用,建议收藏备用,下次要接 Java Agent 的时候直接照着这张图起项目。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 3:47:56

富文本编辑器截图粘贴实现:从剪贴板到图片上传的全链路解析

直接从截图粘贴这个动作说起。你在内部办公系统里写一篇材料&#xff0c;顺手CtrlV把一张截图糊进去&#xff0c;松手后图片就出现在正文里了。这个操作在很多办公场景下是天经地义的事&#xff0c;可一旦放到项目选型或者自研富文本工具的场景里&#xff0c;它往往变成第一个被…

作者头像 李华
网站建设 2026/10/14 3:46:48

如何定位慢查询

一、慢查询发生场景聚合查询多表查询表数据量过大查询深度分页查询表象&#xff1a; 页面加载过慢、接口压测响应时间过长&#xff08;超过 1s&#xff1b;实际中 500ms 以上就值得关注&#xff09;。二、如何定位1. 开启慢查询日志 —— 找到慢 SQL-- 查看是否开启 SHOW VARIA…

作者头像 李华
网站建设 2026/10/14 3:46:11

小程序开发服务商选型避坑指南:避开榜单陷阱与隐性成本

2026年了&#xff0c;小程序开发服务商还是一个让人头疼的选型问题。不是没得选&#xff0c;而是选择太多、榜单太漂亮、销售话术太顺滑&#xff0c;真正到交付阶段却发现货不对板。这几年我前后跟不同规模的开发团队合作过三轮小程序项目&#xff0c;也见过身边朋友被各种“年…

作者头像 李华
网站建设 2026/10/14 3:45:30

YOLOv8药盒识别实战:从数据采集到一键部署全流程

简介&#xff1a;这是一套面向计算机、人工智能、自动化等专业学生与初学者的YOLOv8药品识别实战资源&#xff0c;可用于毕业设计、课程设计、大作业或教学演示。系统基于YOLOv8目标检测框架&#xff0c;支持图片与视频输入&#xff0c;自动定位并识别药盒内常见药品&#xff0…

作者头像 李华
网站建设 2026/10/14 3:45:30

国产芯片EMI SRAM替代ISSI SRAM

EMI的快速异步SRAM产品&#xff0c;采用CMOS工艺&#xff0c;接口为TTL兼容&#xff0c;三态输出&#xff0c;全静态操作&#xff0c;无需时钟与刷新。容量跨度为1Mb至32Mb&#xff0c;覆盖6个密度档&#xff1b;数据组织分为x8与x16两个方向&#xff1b;EMI SRAM供电电压提供四…

作者头像 李华