生产环境 Agent 实战写法(Spring‑AI,Java 后端)
发布时间:2026/9/1 10:54:35 作者:尧图编辑部 阅读量:1,286
)
目录整体生产流程图生产代码骨架可直接改造进项目1. Agent 执行服务类生产环境重点细节面试高频1、工具ToolCallback不能裸写2、ChatMemory 不能直接用 InMemoryChatMemory3、Token 监控保护4、链路追踪 SkyWalking5、禁止信任大模型输出6、区分两种 Agent 模式面试常问为什么生产不用 SpringAI 内置 ReActAgent扩展生产还会叠加什么组件生产绝对不直接用框架自带ReActAgent。 原因黑盒封装不好埋监控、不好自定义重试、异常不好接管、链路追踪难做、迭代控制、工具权限、熔断降级都不好插手。线上主流方案自己实现 ReAct 循环基于 Spring‑AI 的底层原语ChatClient ToolCallback ChatMemory自己控制 while 循环。核心生产诉求可控迭代次数防止死循环每一步日志、链路追踪SkyWalking工具调用熔断、超时、异常捕获异常回传给大模型Token 统计超限保护会话记忆策略不能无脑全量塞历史工具鉴权、参数校验防止大模型注入越权任务超时整体控制输出可溯源每一步思考、调用了什么工具、入参出参全部落库架构Agent 只是业务调度层大模型本身才是推理底层Spring‑AI 只是 SDK 封装。整体生产流程图用户输入 → 组装系统Prompt(Agent角色指令) → 带上会话记忆 → 请求LLM → 判断返回是否存在ToolCall ├─无ToolCall输出最终结果结束Agent └─有ToolCall 1. 参数校验、工具权限校验 2. 逐个执行工具捕获所有异常 3. 工具执行结果含异常栈写回上下文记忆 4. 迭代计数1判断是否超过最大迭代 5. 回到LLM请求下一轮思考 迭代超限 → 返回任务执行失败生产代码骨架可直接改造进项目Maven 依赖spring‑ai 1.0dependency groupIdorg.springframework.ai/groupId artifactIdspring‑ai‑openai‑spring‑boot‑starter/artifactId /dependency1. Agent 执行服务类javaimport org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.memory.InMemoryChatMemory; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.ToolCall; import org.springframework.ai.chat.model.ToolCallback; import io.micrometer.tracing.Tracer; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; import java.util.UUID; Slf4j Service public class ProductionAgentService { // 大模型对话客户端已经注册全部业务tools private final ChatClient chatClient; // 链路追踪 private final Tracer tracer; // Agent生产配置抽成配置文件 private static final int MAX_ITERATION 8; // 最大思考迭代防止死循环 private static final long AGENT_TOTAL_TIMEOUT_MS 30_000; // Agent整体总超时30s public ProductionAgentService(ChatClient.Builder chatClientBuilder, ListToolCallback businessToolCallbacks, Tracer tracer) { this.tracer tracer; this.chatClient chatClientBuilder .defaultTools(businessToolCallbacks) // 注册所有业务工具 .build(); } /** * param sessionId 会话ID用于记忆隔离 * param userQuery 用户原始问题 * return agent最终回答 */ public AgentResult runAgent(String sessionId, String userQuery) { String agentTraceId UUID.randomUUID().toString(); ChatMemory chatMemory new InMemoryChatMemory(); long startTs System.currentTimeMillis(); int iteration 0; try { while (iteration MAX_ITERATION) { // 全局总超时保护 if(System.currentTimeMillis() - startTs AGENT_TOTAL_TIMEOUT_MS){ log.warn([Agent][{}]整体执行超时, agentTraceId); return AgentResult.fail(任务执行超时请简化问题重试, iteration); } iteration; log.info([Agent][{}]第{}轮思考用户query:{}, agentTraceId, iteration, userQuery); // 调用大模型带上记忆系统prompt定义Agent行为规范 ChatResponse chatResponse chatClient.prompt() .system( 你是业务智能Agent。 规则 1.需要外部数据必须调用工具禁止编造数据 2.拿到工具返回结果后再综合回答 3.如果工具报错把报错信息纳入思考 4.确认信息全部齐全后输出最终答案不要再调用工具。 ) .advisors(advisorSpec - advisorSpec.chatMemory(chatMemory, sessionId)) .user(userQuery) .call() .chatResponse(); ListToolCall toolCalls chatResponse.getToolCalls(); if (toolCalls null || toolCalls.isEmpty()) { // ✅没有工具调用任务结束返回最终结果 String finalAnswer chatResponse.getResult().getOutput().getContent(); log.info([Agent][{}]任务完成迭代次数:{}, agentTraceId, iteration); return AgentResult.success(finalAnswer, iteration); } log.info([Agent][{}]本轮需要调用工具:{}, agentTraceId, toolCalls); // ⚠️SpringAI ToolCallbackAdvisor 会自动执行工具异常结果自动写入chatMemory // 不需要自己手动反射调用工具但是工具内部必须自己做参数校验、鉴权、超时捕获 // userQuery置空下一轮由memory提供上下文 userQuery ; } // 迭代次数耗尽 log.warn([Agent][{}]达到最大迭代次数{}终止任务, agentTraceId, MAX_ITERATION); return AgentResult.fail(任务迭代次数到达上限无法完成全部需求, iteration); }catch (Exception e){ log.error([Agent][{}]Agent执行异常, agentTraceId, e); return AgentResult.fail(Agent内部执行异常e.getMessage(), iteration); } } // 返回体 public static record AgentResult(boolean success, String answer, int iteration){ public static AgentResult success(String answer, int iter){ return new AgentResult(true, answer, iter); } public static AgentResult fail(String answer, int iter){ return new AgentResult(false, answer, iter); } } }生产环境重点细节面试高频1、工具ToolCallback不能裸写业务工具不能直接写死逻辑生产工具必须做参数校验大模型可能输出非法参数提前拦截用户身份鉴权Agent 调用业务接口必须携带登录身份不能随便查所有人订单工具独立超时每个工具设置超时时间不能一个慢工具卡死整个 Agent 循环捕获业务异常把异常信息返回给大模型Agent 可以基于错误继续决策javaComponent public class QueryOrderTool implements ToolCallback { Override public ToolResponse call(ToolCallRequest toolCallRequest) { try{ // 参数校验 鉴权 // 业务查询逻辑 return ToolResponse.success(data); }catch (Exception ex){ // 异常原样返回给大模型不要吞异常 return ToolResponse.error(查询订单失败ex.getMessage()); } } }2、ChatMemory 不能直接用 InMemoryChatMemoryInMemory 只适合测试。生产会话记忆要持久化RedisChatMemory。会话持久化服务重启记忆不丢失支持记忆裁剪不能无限制保存全部历史防止 Token 爆炸策略超过阈值做摘要压缩丢弃早期原始对话。3、Token 监控保护每轮调用拿到ChatResponse.getMetadata()获取输入输出 token 设置阈值token 超过上限直接终止 Agent返回提示。4、链路追踪 SkyWalking每一轮思考、每一次工具调用都要打 tagagentTraceId、迭代轮次、工具名称、入参、token 消耗、耗时。 排查线上 Agent 诡异行为看日志链路看大模型每一轮思考和工具返回。5、禁止信任大模型输出Agent 安全红线大模型输出的工具参数永远不可信。 所有入参必须二次校验防止 prompt 注入越权查询数据、删除数据。6、区分两种 Agent 模式ReAct Agent上面代码思考→工具→思考绝大多数业务 Agent 用这个。Plan‑and‑Execute规划执行先一次性规划多个子任务再逐个执行。适合复杂长任务开发成本更高。面试常问为什么生产不用 SpringAI 内置 ReActAgent内置 ReActAgent 把 while 循环封装在框架内部。无法细粒度控制每一步日志、埋点、metrics 指标迭代过程中的异常、工具失败策略被封装无法自定义降级不方便接入链路追踪每一轮思考看不到记忆裁剪、token 超限保护很难介入线上出问题想看中间每一轮思考和工具返回黑盒很难排查。所以生产项目复用 SpringAI 底层 ChatClient、ToolCallback自己手写外层循环调度。扩展生产还会叠加什么组件RAG 知识库作为长期记忆Agent 执行前先检索知识库把参考文档塞进 system promptMCP外部工具协议对接第三方能力指标埋点Agent 执行成功率、平均迭代次数、token 消耗、失败原因分类人工兜底Agent 执行失败可以转人工介入会话输出校验Agent 返回结果后增加校验层校验输出格式、业务正确性