大家好,我是Java1234_小锋老师。
最近用 Spring AI 2.0 做了几个 Agent 项目,踩了不少坑,也摸出一点门道。这篇文章不打算写成官方文档,就是把我觉得最实用的十条经验整理出来,配上关键代码,方便你直接上手。
写在前面:Agent 到底在干什么
很多人第一次接触 Agent,会被各种名词绕晕。其实可以把它想成一件事:
用户提一个问题 → 大模型决定要不要调工具 → 工具跑完把结果还给模型 → 模型给出最终回答。
Spring AI 2.0 最大的变化,是把「工具调用循环」从各个 ChatModel 内部抽出来,统一交给ChatClient+ToolCallingAdvisor管理。架构上更清晰,调试也更容易。
下面这张图用 Mermaid 描述了 Agent 一次完整对话的大致流程:
经验一:ChatClient 是入口,别绕远路
Spring AI 2.0 里,推荐所有 Agent 场景都从ChatClient出发,而不是直接调ChatModel。
原因很简单:ChatClient自带 Advisor 链、工具注册、流式封装,你少写很多胶水代码。直接调ChatModel的话,工具循环、记忆、RAG 都得自己拼。
@RestController@RequestMapping("/agent")publicclassAgentController{privatefinalChatClientchatClient;publicAgentController(ChatClient.Builderbuilder){this.chatClient=builder.defaultSystem("你是一个 helpful 的助手,回答要简洁。").build();}@PostMapping("/chat")publicStringchat(@RequestBodyChatRequestrequest){returnchatClient.prompt().user(request.message()).call().content();}}application.yml里配好模型就行,Spring Boot 会自动注入ChatClient.Builder:
spring:ai:openai:api-key:${OPENAI_API_KEY}base-url:https://dashscope.aliyuncs.com/compatible-mode/v1chat:options:model:qwen3.6-plustemperature:0.3经验二:工具调用交给 ToolCallingAdvisor
2.0 之前,工具调用逻辑散落在各个 Model 实现里,换模型容易出兼容问题。2.0 起,ToolCallingAdvisor统一负责工具循环,ChatClient会自动注册它。
你只需要把工具传进去,框架帮你跑完「模型 → 调工具 → 再喂给模型」这一圈:
@ServicepublicclassWeatherAgent{privatefinalChatClientchatClient;privatefinalWeatherToolsweatherTools;publicWeatherAgent(ChatClient.Builderbuilder,WeatherToolsweatherTools){this.weatherTools=weatherTools;this.chatClient=builder.build();}publicStringask(Stringquestion){returnchatClient.prompt().user(question).tools(weatherTools)// 注册工具,循环由 ToolCallingAdvisor 处理.call().content();}}对应 Mermaid 流程:
别自己写 while 循环去解析 tool_calls,除非你有非常特殊的业务需求(比如要和 Prompt Cache 深度结合)。大多数场景,框架托管就够了。
经验三:用 @Tool 定义工具,少写样板代码
定义工具最省心的方式:写一个普通 Java 类,方法上加@Tool注解。
@ComponentpublicclassWeatherTools{@Tool(description="根据城市名查询当前天气,例如:北京、上海")publicStringgetWeather(@ToolParam(description="城市名称,不含'市'字")Stringcity){// 实际项目里这里调第三方 APIreturnswitch(city){case"北京"->"晴,28°C,东北风 2 级";case"上海"->"多云,26°C,东南风 3 级";default->"暂无 "+city+" 的天气数据";};}@Tool(description="查询指定城市未来三天的天气预报")publicList<String>getForecast(Stringcity){returnList.of(city+" 明天:多云,25~31°C",city+" 后天:小雨,22~27°C",city+" 大后天:晴,24~30°C");}}几点小建议:
description一定要写清楚,模型靠它决定什么时候调、传什么参数- 参数用
@ToolParam补充说明,减少模型传错值的概率 - 返回值尽量是 String 或简单 POJO,复杂对象模型不好消化
经验四:System Prompt 比换模型更管用
做 Agent 时,遇到问题第一反应往往是「换个更强的模型」。我的经验是:先把 System Prompt 写好,效果往往比换模型明显,还省钱。
this.chatClient=builder.defaultSystem(""" 你是「小助手」,一个面向企业内部员工的问答 Agent。 规则: 1. 涉及公司制度、流程的问题,必须先调用 searchKnowledge 工具检索,不能凭记忆回答。 2. 不确定的信息,明确说「我不确定」,不要编造。 3. 回答用中文,条理清晰,必要时用编号列表。 4. 单次回答控制在 300 字以内,除非用户要求详细说明。 """).build();Prompt 里写清楚「角色、边界、工具使用规则、输出格式」,Agent 的稳定性会好很多。可以把 Prompt 抽到配置文件或数据库,方便运营同学迭代,不用每次改代码发版。
经验五:用 Advisor 链拆分职责
Agent 很容易写成一个大 Service:记忆在这里、RAG 在那里、日志又塞进来……代码很快变成一团。Spring AI 2.0 的Advisor 机制就是用来解耦的。
常见组合:
| Advisor | 作用 |
|---|---|
MessageChatMemoryAdvisor | 自动读写会话历史 |
QuestionAnswerAdvisor | RAG 检索增强 |
ToolCallingAdvisor | 工具调用循环(框架自动注册) |
| 自定义 Advisor | 审计日志、权限校验、敏感词过滤 |
@ConfigurationpublicclassAgentConfig{@BeanChatClientagentChatClient(ChatClient.Builderbuilder,ChatMemorychatMemory,VectorStorevectorStore){varmemoryAdvisor=MessageChatMemoryAdvisor.builder(chatMemory).build();varragAdvisor=QuestionAnswerAdvisor.builder(vectorStore).build();returnbuilder.defaultAdvisors(memoryAdvisor,ragAdvisor).defaultSystem("你是企业知识库助手,优先依据检索结果回答。").build();}}Advisor 的执行顺序很重要。想观察每一次工具调用的中间过程,就把自定义 Advisor 的 order 设得比ToolCallingAdvisor更高(数值更大),这样它会在工具循环「内部」被触发。
经验六:流式输出要尽早接上
Agent 调工具时,用户可能要等好几秒。如果一直白屏,体验很差。流式输出(SSE)应该尽早接上,至少让用户看到「正在思考」或逐字输出的效果。
@GetMapping(value="/chat/stream",produces=MediaType.TEXT_EVENT_STREAM_VALUE)publicFlux<String>chatStream(@RequestParamStringmessage){returnchatClient.prompt().user(message).tools(weatherTools).stream().content();}前端用 EventSource 或 fetch 读 SSE 就行。注意:流式场景下如果有多轮工具调用,中间会有几次「停顿」,可以在 UI 上加一个 loading 状态,别让用户以为卡死了。
经验七:会话记忆别自己拼 List
多轮对话是 Agent 的基本能力。2.0 里直接用ChatMemory+MessageChatMemoryAdvisor,别自己维护List<Message>再手动塞给模型。
@BeanChatMemorychatMemory(){returnMessageWindowChatMemory.builder().maxMessages(20)// 保留最近 20 条,防止 token 爆炸.build();}// 调用时带上 conversationIdpublicStringchat(StringconversationId,Stringmessage){returnchatClient.prompt().user(message).advisors(a->a.param(ChatMemory.CONVERSATION_ID,conversationId)).call().content();}记忆窗口别设太大。对话一长,token 费用和响应延迟都会上去。重要信息可以落库,需要时再检索,而不是全部堆在上下文里。
经验八:工具宁少勿滥
新手容易犯一个错:给 Agent 塞几十个工具,觉得功能越多越好。实际效果是模型经常选错工具,或者犹豫半天。
我的做法是:
- 单 Agent 控制在5~8 个工具以内
- 功能相近的工具合并,比如
searchOrderById和searchOrderByPhone合成一个带参数的searchOrder - 复杂流程拆成多个专用 Agent,而不是一个「万能 Agent」
// 不推荐:工具太多,模型容易懵.tools(tool1,tool2,tool3,tool4,tool5,tool6,tool7,tool8,tool9,tool10)// 推荐:按场景分组,每次只注册相关工具.tools(orderTools)// 订单 Agent.tools(inventoryTools)// 库存 Agent经验九:可观测性要前置
Agent 出问题时,最难排查的是「模型为什么没调工具」或「为什么调错了工具」。所以日志和监控要在一开始就接上,别等线上出事故再补。
@Slf4j@ComponentpublicclassAgentAuditAdvisorimplementsCallAdvisor{@OverridepublicChatClientResponseadviseCall(ChatClientRequestrequest,CallAdvisorChainchain){log.info("Agent 请求 | user={}",request.prompt().getUserMessage());longstart=System.currentTimeMillis();ChatClientResponseresponse=chain.nextCall(request);log.info("Agent 响应 | 耗时={}ms | tokens={}",System.currentTimeMillis()-start,response.chatResponse().getMetadata().getUsage());returnresponse;}@OverridepublicStringgetName(){return"AgentAuditAdvisor";}@OverridepublicintgetOrder(){returnOrdered.HIGHEST_PRECEDENCE+100;}}Spring AI 也集成了 Micrometer,配合 Prometheus + Grafana 可以看 token 用量、调用延迟。Agent 是按 token 计费的,这个监控直接关系到成本。
经验十:配置分层,开发生产分开
最后一个经验偏工程化:开发和生产环境用不同的模型和参数,别本地用 GPT-4o,上线才发现成本和延迟扛不住。
# application-dev.ymlspring:ai:openai:chat:options:model:gpt-4o-minitemperature:0.7# application-prod.ymlspring:ai:openai:chat:options:model:gpt-4otemperature:0.2# 生产环境温度低一点,输出更稳定工具调用次数也可以加限制,防止模型陷入无限循环:
spring:ai:chat:client:tool-calling:max-iterations:5# 最多 5 轮工具调用最后小结下哈
Spring AI 2.0 把 Agent 开发里最难啃的「工具调用循环」收进了框架,开发者可以更专注在业务本身。回顾这十条:
- 从
ChatClient出发 - 工具循环交给
ToolCallingAdvisor - 用
@Tool快速定义工具 - 先把 Prompt 写好
- Advisor 链拆分职责
- 尽早接入流式输出
- 用
ChatMemory管理多轮对话 - 工具少而精
- 可观测性前置
- 配置分层
Agent 没有银弹,但把这些基础打牢,后面加 RAG、多 Agent 协作、人工审核环节都会轻松很多。如果你也在用 Spring AI 2.0 做 Agent,欢迎交流踩坑经历。