前言
很多同学初学 Spring AI Alibaba 智能体时,都会觉得 Agent 开发非常简单:定义工具、注入模型、写一段提示词、调用agent.call()即可运行。
但这种只能跑通的 Demo Agent,绝对无法上线生产。
日常开发中,90% 的 Agent 线上问题(无限循环调用工具、模型幻觉、参数乱传、任务失控、无法排查),全部源于缺少生产级防护。
本文不讲传统编程、Workflow、Skill 基础概念,只讲Spring AI Alibaba 生产级 Agent 的核心落地标准,带完整可运行代码案例,区分「玩具 Agent」和「线上可用 Agent」的本质区别。
阅读提示:本文基于 Spring AI Alibaba 1.1.x 版本,部分 API 与早期版本存在差异,建议对照实际依赖版本确认方法签名。
一、为什么 Demo Agent 不能上生产?
1.1 Demo 版简易 Agent(反面案例,不要直接上生产)
特点:代码少,跑 Demo 很爽,上线会出各种问题
@Service
@RequiredArgsConstructor
public class SimpleDemoAgentService {
private final ChatModel chatModel;
private final DemoTool demoTool;
public ReactAgent buildDemoAgent() {
ToolCallbackProvider toolProvider = ToolCallbackProvider.of(demoTool);
return ReactAgent.builder()
.name("demo-agent")
.model(chatModel)
.tools(toolProvider.getToolCallbacks())
.instruction("你是订单故障排查专家,使用工具完成故障排查。")
// 没有最大迭代、没有超时、没有持久化、没有监听
.saver(new MemorySaver())
.build();
}
public String execute(String goal) {
ReactAgent agent = buildDemoAgent();
return agent.call(goal).getContent();
}
}
@Service
class DemoTool {
@Tool(description = "根据订单号查询错误日志")
public String queryErrorLog(@ToolParam(description = "订单号") String orderNo) {
throw new RuntimeException("数据库查询异常");
}
}1.2 Demo Agent 线上必现的坑
- 没有最大迭代限制,模型可能无限循环调用同一个工具
- 工具抛出异常,直接打断整个 ReAct 推理循环,任务直接失败
- 使用
MemorySaver,服务重启任务状态全部丢失 - 没有事件监听,工具调用、思考过程无记录,出问题无法排查
- 无超时控制,遇到大模型慢响应会持续占用业务线程
- 提示词只有角色,缺少失败策略、禁止行为约束
二、生产级 Agent 完整代码案例
2.1 生产工具层:Skill 改造
重点:工具只负责原子能力,异常由框架统一处理,无需在每个工具方法中重复 try-catch。
Spring AI Alibaba 内置了ToolExecutionExceptionProcessor机制。该接口是一个函数式接口,用于将工具抛出的ToolExecutionException转换为可发送回 AI 模型的字符串,或者抛出异常供调用者处理。这意味着我们可以在 Agent Builder 层面统一配置异常处理策略,工具类本身保持干净。
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class TroubleShootTool {
@Tool(description = "根据订单号查询订单错误日志,获取故障信息")
public String queryErrorLog(@ToolParam(description = "订单编号") String orderNo) {
// 入参校验,直接抛异常,由 ToolExecutionExceptionProcessor 统一处理
if (orderNo == null || orderNo.isBlank()) {
throw new IllegalArgumentException("orderNo不能为空");
}
// 模拟业务查询逻辑
// return db.selectLog(orderNo);
throw new RuntimeException("数据库连接异常");
}
@Tool(description = "查询订单基础信息")
public String queryOrderInfo(@ToolParam(description = "订单编号") String orderNo) {
if (orderNo == null || orderNo.isBlank()) {
throw new IllegalArgumentException("orderNo不能为空");
}
return "{\"orderNo\":\"" + orderNo + "\",\"userId\":10001,\"amount\":129.00}";
}
}2.2 全链路可观测:基于 Hook 机制采集运行轨迹
重点:框架提供Hook机制,可在模型调用前后、工具执行前后插入自定义逻辑。Hook 逻辑要轻,不能阻塞主链路。
Spring AI Alibaba 内置了多种 Hook,包括ModelCallLimitHook(限制 LLM 调用次数)、ToolCallLimitHook(限制工具执行次数)、HumanInTheLoopHook(人工审批)、PIIDetectionHook(敏感信息检测)等。
以下自定义 Hook 用于采集 Agent 全链路运行轨迹:
import com.alibaba.cloud.ai.graph.agent.hook.Hook;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
@Slf4j
@Component
public class TraceHook implements Hook {
private long taskStartTime;
@Override
public int getOrder() {
return 0;
}
@Override
public Map<String, Object> beforeModelCall(Map<String, Object> context) {
if (taskStartTime == 0) {
taskStartTime = System.currentTimeMillis();
log.info("[AgentTrace]任务开始");
}
log.info("[AgentTrace]模型调用前,上下文键:{}", context.keySet());
return context;
}
@Override
public Map<String, Object> afterModelCall(Map<String, Object> context) {
log.info("[AgentTrace]模型调用完成");
return context;
}
public long getCostMs() {
return System.currentTimeMillis() - taskStartTime;
}
}Hook 接口提供了getTools()默认方法,允许 Hook 向 Agent 动态注册工具。耗时统计需业务代码自行完成并落库,框架不会自动持久化。
2.3 构建生产级 ReactAgent 服务
核心点:工具级超时、模型调用限制、生产提示词、注入 Hook、持久化 Saver、异常兜底。
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.BaseCheckpointSaver;
import com.alibaba.cloud.ai.graph.agent.hook.modelcalllimit.ModelCallLimitHook;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.stereotype.Service;
import java.time.Duration;
@Slf4j
@Service
@RequiredArgsConstructor
public class ProductionAgentService {
private final ChatModel chatModel;
private final TroubleShootTool troubleShootTool;
private final TraceHook traceHook;
// 生产替换 MemorySaver,使用 RedisSaver / JDBCSaver 等持久化实现
private final BaseCheckpointSaver checkpointSaver;
/**
* 构建生产可用 Agent 实例
*/
public ReactAgent buildProductionAgent() {
// 生产提示词:不只是角色,增加强制约束、失败策略
String productionInstruct = """
你是订单故障排查专家。
1. 可以调用工具查询订单信息、查询错误日志。
2. 如果工具返回以【工具调用异常/工具业务错误】开头内容,不要无限重试同一个工具。
3. 证据收集充分后,直接输出排查结论,主动结束任务,禁止无意义调用工具。
4. 禁止编造工具,禁止编造业务数据,全部信息来源于工具返回。
""";
return ReactAgent.builder()
.name("production-trouble-agent")
.model(chatModel)
.methodTools(troubleShootTool) // 直接传入工具对象
.instruction(productionInstruct)
.checkpointSaver(checkpointSaver) // 持久化状态存储
.toolExecutionTimeout(Duration.ofSeconds(30)) // 工具级超时(默认5分钟)
.toolExecutionExceptionProcessor((ex, toolName, toolInput) -> {
// 统一异常处理:将异常转为可发送给模型的错误文本
log.error("工具[{}]执行异常,入参:{}", toolName, toolInput, ex);
return "【工具调用异常】" + toolName + "执行失败:" + ex.getMessage();
})
.hooks(
ModelCallLimitHook.builder().runLimit(5).build(), // 限制模型调用次数
traceHook // 全链路轨迹采集
)
.build();
}
/**
* 对外执行 Agent 任务,统一异常兜底
*/
public String runAgentTask(String userGoal) {
ReactAgent agent = buildProductionAgent();
try {
String result = agent.call(userGoal).getContent();
log.info("[Agent]任务完成,总耗时{}ms", traceHook.getCostMs());
return result;
} catch (Exception e) {
log.error("[Agent]执行发生系统异常,耗时{}ms", traceHook.getCostMs(), e);
if (e.getClass().getSimpleName().contains("MaxIterations")
|| e.getMessage() != null && e.getMessage().contains("limit")) {
return "任务终止:已达到最大工具调用轮次,无法继续执行,请精简问题重试。";
}
return "AI任务执行系统异常,请稍后重试。";
}
}
}2.4 持久化 Saver 的选择
MemorySaver仅适合开发测试,生产环境需选用持久化实现。Spring AI Alibaba 提供了多种开箱即用的 Saver:
| Saver 实现 | 存储后端 | 适用场景 |
|---|---|---|
MemorySaver | JVM 堆内存 | 开发/测试/单机,进程重启丢失 |
RedisSaver | Redis | 生产级分布式部署,支持过期策略 |
JDBCSaver | MySQL/PostgreSQL/H2 | 关系型数据库持久化 |
MongoDBSaver | MongoDB | 文档型数据库持久化 |
FileSaver | 文件系统 | 本地文件持久化 |
@Configuration
public class GraphSaverConfig {
@Bean
@ConditionalOnMissingBean
public BaseCheckpointSaver redisSaver(RedisConnectionFactory connectionFactory) {
return new RedisSaver(connectionFactory);
}
}2.5 简单测试 Controller
@RestController
@RequiredArgsConstructor
public class AgentTestController {
private final ProductionAgentService productionAgentService;
@GetMapping("/agent/prod/execute")
public String execute(@RequestParam String goal) {
return productionAgentService.runAgentTask(goal);
}
}访问示例:
http://localhost:8080/agent/prod/execute?goal=帮我排查订单O20260919001为什么提交失败三、Demo Agent vs 生产 Agent 核心对比
| 维度 | Demo 玩具 Agent | 生产级 Agent |
|---|---|---|
| 执行约束 | 完全依赖模型自觉 | ModelCallLimitHook+toolExecutionTimeout双重硬护栏 |
| 工具异常处理 | 直接抛异常,中断任务 | ToolExecutionExceptionProcessor统一处理,模型自主决策 |
| 提示词 | 仅角色描述 | 包含完整业务约束、禁止行为、容错策略 |
| 可观测性 | 无轨迹、无日志、无法排查 | Hook 机制监听,模型调用、工具执行、耗时全记录 |
| 状态存储 | MemorySaver内存,重启丢失 | RedisSaver/JDBCSaver持久化,可回溯可恢复 |
| 上层编排 | 直接对外暴露调用 | 嵌入 Workflow,作为子任务执行 |
四、代码关键点解读
1. 工具异常统一处理 ——ToolExecutionExceptionProcessor
ToolExecutionExceptionProcessor是一个函数式接口,用于处理ToolExecutionException,将异常转换为可发送回 AI 模型的字符串。这意味着工具类只需抛出异常,无需在每个方法中重复 try-catch。模型收到错误文本后会自主决策是重试还是放弃,而不是被异常中断推理循环。
2. 硬护栏 ——ModelCallLimitHook+toolExecutionTimeout
ModelCallLimitHook是 Spring AI Alibaba 内置的 Hook,用于限制每个线程或每次运行的 LLM 调用次数。toolExecutionTimeout控制单个工具的执行超时,默认值为 5 分钟。两者配合形成双重保护:前者防止模型无限循环,后者防止单个工具挂起。
3. Hook 机制 —— 可观测性的核心
Spring AI Alibaba 的 Hook 机制允许在 Agent 执行流程的特定位置(BEFORE_MODEL、AFTER_MODEL 等)插入自定义逻辑。工具调用名称、参数、模型思考均可通过 Hook 捕获。耗时、迭代计数需要业务代码自行统计落库,框架不会自动持久化。Hook 逻辑要轻,不能阻塞主链路。
4. 状态存储 ——BaseCheckpointSaver
MemorySaver只适合单元测试、Demo;生产要实现BaseCheckpointSaver接口,把消息历史、中间状态存入数据库。Spring AI Alibaba Graph 具有内置的持久化层,通过检查点实现,检查点会在每个超级步骤保存图状态的检查点。
5. 异常统一捕获 —— 对外接口友好降级
对外接口不能把框架原始异常直接抛给前端,要做业务友好降级返回。同时,Agent 级的总超时建议通过 Spring 的@Async+CompletableFuture.get(timeout)在外层做兜底。
五、官方能力补充
5.1 结构化输出
生产级 Agent 往往需要结构化的输出而非自由文本。Spring AI Alibaba 的ReactAgent.Builder通过outputSchema和outputType方法处理结构化输出。推荐使用outputType(Class)—— 传入 Java 类,框架自动通过BeanOutputConverter转换为 JSON Schema,兼顾类型安全与代码可维护性。
public record DiagnosisResult(
String orderNo,
String rootCause,
String suggestion
) {}
ReactAgent agent = ReactAgent.builder()
.name("diagnosis-agent")
.model(chatModel)
.methodTools(troubleShootTool)
.outputType(DiagnosisResult.class) // 结构化输出
.build();这样上层业务代码可以直接获得 Java POJO,无需解析自然语言。
5.2 Workflow 编排
Spring AI Alibaba Agent Framework 预置了多种多智能体工作流模式:SequentialAgent(顺序代理)、ParallelAgent(并行代理)、RoutingAgent(路由代理)和LoopAgent(循环代理)。
生产架构建议:Workflow 控边界,Agent 做动态子任务决策。
var workflow = SequentialAgent.builder()
.name("order-troubleshoot-workflow")
.agents(
routingAgent, // 路由:判断问题类型,分发到不同 Agent
reactAgent // 动态子任务:ReAct Agent 完成具体排查
)
.build();六、核心总结
- 能调用工具、能跑通流程的只是 Demo,不是生产 Agent。生产 Agent 80% 代码都是护栏、容错、观测、兜底,而非核心推理逻辑。
- Hook 机制是生产排查核心,可以捕获每一轮模型调用、工具执行、运行状态,实现全链路可追溯。
ModelCallLimitHook和ToolCallLimitHook是官方提供的开箱即用的调用限制手段。 - 生产 Agent 必须代码硬约束 + 模型软约束结合:提示词约束模型行为;
ModelCallLimitHook+toolExecutionTimeout强制兜底,双向防失控。 - 工具定位是原子能力,只干活不决策。异常通过
ToolExecutionExceptionProcessor在 Agent 层面统一消化,不打断推理循环。 - 线上理想架构:Workflow 控边界,Agent 做动态子任务决策。通过
SequentialAgent、RoutingAgent等编排模式组合多个 Agent,规避 AI 不确定性带来的业务风险。 - 结构化输出通过
outputType(Class)实现,让 Agent 返回可直接被业务代码消费的 Java POJO,是生产级 Agent 的重要能力。