SpringAI Alibaba
一、Agents
Agents 将大语言模型与工具结合,创建具备任务推理、工具使用决策、工具调用的自动化系统,系统具备持续推理、工具调用的循环迭代能力,直至问题解决。
Spring AI Alibaba 提供了基于 ReactAgent 的生产级 Agent 实现。
一个 LLM Agent 在循环中通过运行工具来实现目标。Agent 会一直运行直到满足停止条件 ------ 即当模型输出最终答案或达到迭代限制时。
1.1 ReactAgent 理论基础
把 ReAct(Reasoning + Acting)想象成一个做事非常有条理的"聪明人"。过去的 AI 往往是一次性直接猜一个答案,而 ReAct 则是让 AI 学会了**"一边想,一边做,摸着石头过河"**。
简单来说,它的工作过程就像我们人类解决复杂问题时的本能反应:
🔄 ReAct 的"做事四步曲"
- 想一想(思考 / Reasoning): 遇到问题先别急着动手。先分析一下现在是个什么情况?为了解决这个问题,我第一步应该干嘛?
- 做一做(行动 / Acting): 根据刚才的想法去实际操作。比如去网上搜个资料、调个计算器算个数,或者如果已经得出结论了,就直接把答案写出来。
- 看一看(观察 / Observation): 看看刚才的操作得到了什么反馈?搜到了什么网页?计算器给出的数字是多少?
- 接着来(迭代 / Iteration): 拿着刚刚看到的新线索,再回到第一步继续"想一想"。就这样一圈一圈地循环,直到把任务彻底搞定。
1.2 ReactAgent 的原理
在 Spring AI Alibaba 里,ReactAgent 底层是跑在一个叫作 Graph(图) 的引擎上的。
- 节点(Nodes): 就像是一个个**"办事窗口" ** 或 "加工站",每个站点负责做特定的事情。
- 边(edgs): 就像是连接这些站点的**"马路"或"传送带"**,规定了 Agent 干完这一步后,下一步该往哪走。
简单来说:
agent是一个无限循环,在这个循环中,agent不断和llm对话,并引导llm执行任务,此外还能响应llm的工具调用等功能,最后由llm主动结束循环(agent会限制llm的调用次数、护轨、拦截等等)
所以这就是为什么发起一次问题,底层会调用很多次ai的原因

1.3 核心组件
添加依赖
xml
<dependencies>
<!-- Spring AI Alibaba Agent Framework -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>1.1.2.0</version>
</dependency>
<!-- DashScope ChatModel 支持(如果使用其他模型,请跳转 Spring AI 文档选择对应的 starter) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>1.1.2.0</version>
</dependency>
</dependencies>
模型(Model)
配置方式:
yaml
# application.yml
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
然后直接使用spring自动注入的chatmodel
2.
java
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
// 创建 DashScope API 实例
DashScopeApi dashScopeApi = DashScopeApi.builder()
.apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
.build();
// 创建 ChatModel
ChatModel chatModel = DashScopeChatModel.builder()
.dashScopeApi(dashScopeApi)
.build();
// 创建 Agent
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.build();
-
通过chatoptions配置
javaimport com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; ChatModel chatModel = DashScopeChatModel.builder() .dashScopeApi(dashScopeApi) .defaultOptions(DashScopeChatOptions.builder() .withModel(DashScopeChatModel.DEFAULT_MODEL_NAME) .withTemperature(0.7) // 控制随机性 .withMaxToken(2000) // 最大输出长度 .withTopP(0.9) // 核采样参数 .build()) .build();
工具(Tools)
- 使用方式一:使用FunctionToolCallback制作工具,并在agent中使用
java
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.ai.chat.model.ToolContext;
import java.util.function.BiFunction;
// 定义工具(示例:仅一个搜索工具)
public class SearchTool implements BiFunction<String, ToolContext, String> {
@Override
public String apply(String query, ToolContext context) {
// 实现搜索逻辑
return "搜索结果: " + query;
}
}
// 创建工具回调
ToolCallback searchTool = FunctionToolCallback.builder("search", new SearchTool())
.description("搜索工具")
.build();
// 在Agent中使用
ReactAgent agent = ReactAgent.builder()
.name("search_agent")
.model(chatModel)
.tools(searchTool)
.build();
-
使用方式二:使用tool注解,在agent中使用
定义多个tool
javapackage com.dyl.springaialibabastudy.tools; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Component; /** * @author dyl * @version 1.0 * @description: 天气工具类 - 使用 @Tool 注解 * @date 2026/3/21 */ @Component public class WeatherTools { /** * 获取指定城市的天气信息 */ @Tool(description = "获取指定城市的天气信息") public String getWeatherForLocation( @ToolParam(description = "城市名称") String city) { return "It's always sunny in " + city + "!"; } /** * 获取用户所在位置 */ @Tool(description = "根据用户 ID 获取用户所在位置") public String getUserLocation( @ToolParam(description = "用户查询") String query) { // 简化版本:默认返回 Florida // 如需从上下文获取 userId,可通过 Agent 配置传递 // return "Florida"; throw new RuntimeException("Tool not implemented; please try it later!"); } /** * 获取未来天气预报 */ @Tool(description = "获取指定城市未来5天的天气预报") public String getWeatherForecast( @ToolParam(description = "城市名称") String city, @ToolParam(description = "预报天数") Integer days) { StringBuilder forecast = new StringBuilder(); forecast.append(city).append(" 未来 ").append(days).append(" 天预报:\n"); for (int i = 1; i <= days; i++) { forecast.append("第").append(i).append("天: 晴朗, 22°C\n"); } return forecast.toString(); } }使用 FunctionToolCallback.from 批量获取tool
java@Autowired private WeatherTools weatherTools; ReactAgent.builder() .name("weather_agent") .model(model) .tools(ToolCallbacks.from(weatherTools)) -
工具调用错误的处理
javaimport org.springframework.ai.tool.execution.ToolExecutionException; import org.springframework.ai.tool.execution.ToolExecutionExceptionProcessor; import org.springframework.stereotype.Component; @Component public class CustomToolExecutionExceptionProcessor implements ToolExecutionExceptionProcessor { @Override public String process(ToolExecutionException exception) { // 自定义错误处理逻辑 return "Tool failed: error code: 1928391," + exception.getMessage(); } } ReactAgent.builder() .name("weather_agent") .model(model) .tools(ToolCallbacks.from(weatherTools)) .toolExecutionExceptionProcessor(customToolExecutionExceptionProcessor)
系统提示词(System Prompt)
方式一:使用System Prompt
java
private static final String SYSTEM_PROMPT = """
You are an expert weather forecaster, who speaks in puns.
You have access to the following tools:
- getWeatherForLocation: Get weather for a specific city
- getUserLocation: Get the user's location based on user ID
- getWeatherForecast: Get weather forecast for multiple days
If a user asks you for the weather, make sure you know the location.
If you can tell from the question that they mean wherever they are,
use the getUserLocation tool to find their location.
""";
ReactAgent.builder()
.name("weather_agent")
.model(model)
.tools(ToolCallbacks.from(weatherTools))
.systemPrompt(SYSTEM_PROMPT)
方式二:使用instruction
java
String instruction = """
你是一个经验丰富的软件架构师。
在回答问题时,请:
1. 首先理解用户的核心需求
2. 分析可能的技术方案
3. 提供清晰的建议和理由
4. 如果需要更多信息,主动询问
保持专业、友好的语气。
""";
ReactAgent agent = ReactAgent.builder()
.name("architect_agent")
.model(chatModel)
.instruction(instruction)
.build();
方式三:动态提示词
java
public class DynamicPromptInterceptor extends ModelInterceptor {
@Override
public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
// 基于上下文构建动态 system prompt
String userRole = (String) request.getContext().getOrDefault("user_role", "default");
String dynamicPrompt = switch (userRole) {
case "expert" -> "你正在与技术专家对话。使用专业术语深入技术细节";
case "beginner" -> "你正在与初学者对话。使用简单语言解释基础概念";
default -> "你是一个专业的助手,保持友好和专业。";
};
SystemMessage enhancedSystemMessage;
if (request.getSystemMessage() == null) {
enhancedSystemMessage = new SystemMessage(dynamicPrompt);
} else {
enhancedSystemMessage = new SystemMessage(request.getSystemMessage().getText() + " " + dynamicPrompt);
}
ModelRequest modified = ModelRequest.builder(request)
.systemMessage(enhancedSystemMessage)
.build();
return handler.call(modified);
}
@Override
public String getName() {
return "DynamicPromptInterceptor";
}
}
ReactAgent agent = ReactAgent.builder()
.name("adaptive_agent")
.model(chatModel)
.interceptors(new DynamicPromptInterceptor())
.build();
1.4 Agent 调用
-
call 方法调用
java// 字符串输入 AssistantMessage response = agent.call("你好,你是谁?"); System.out.println(response.getText()); -
invoke调用
javaOptional<OverAllState> result = agent.invoke("帮我写一首诗"); if (result.isPresent()) { OverAllState state = result.get(); // 访问消息历史 Optional<Object> messages = state.value("messages"); List<Message> messageList = (List<Message>) messages.get(); // 访问自定义状态 Optional<Object> customData = state.value("custom_key"); System.out.println("完整状态:" + state); }
1.5 高级特性
结构化输出
-
使用outputType
-
定义结构化输出类
java@Data @JsonClassDescription("响应格式") public class StructOutput { @Description("标题") private String title; @Description("内容") private String content; @Description("风格") private String style; } -
agent定义时指定outputType
javaReactAgent agent = ReactAgent.builder() .name("my_agent") .model(chatModel) .outputType(StructOutput.class) .saver(new MemorySaver()) .build();
-
-
使用outputSchema
java// 使用 BeanOutputConverter 生成 outputSchema BeanOutputConverter<StructOutput> outputConverter = new BeanOutputConverter<>(StructOutput.class); String format = outputConverter.getFormat(); ReactAgent agent = ReactAgent.builder() .name("analysis_agent") .model(chatModel) .outputSchema(format) .saver(new MemorySaver()) .build();
outputType:类型安全,适合结构固定的场景(推荐)outputSchema:使用BeanOutputConverter生成时提供类型安全,手动提供字符串时灵活性高,适合动态或复杂的输出格式
记忆(Memory)
内存记忆:
java
// 配置内存存储
ReactAgent agent = ReactAgent.builder()
.name("chat_agent")
.model(chatModel)
.saver(new MemorySaver())
.build();
// 使用 thread_id 维护对话上下文
RunnableConfig config = RunnableConfig.builder()
.threadId("user_123")
.build();
agent.call("我叫张三", config);
agent.call("我叫什么名字?", config); // 输出: "你叫张三"
钩子(Hooks)
Hooks 允许在 Agent 执行的关键点插入自定义逻辑。(类似AOP)
java
// 1. AgentHook - 在 Agent 开始/结束时执行,每次Agent调用只会运行一次
@HookPositions({HookPosition.BEFORE_AGENT, HookPosition.AFTER_AGENT})
public class LoggingHook extends AgentHook {
@Override
public String getName() {
return "logging";
}
@Override
public CompletableFuture<Map<String, Object>> beforeAgent(OverAllState state, RunnableConfig config) {
System.out.println("Agent 开始执行");
return CompletableFuture.completedFuture(Map.of());
}
@Override
public CompletableFuture<Map<String, Object>> afterAgent(OverAllState state, RunnableConfig config) {
System.out.println("Agent 执行完成");
return CompletableFuture.completedFuture(Map.of());
}
}
// 2. MessagesModelHook - 在模型调用前后执行(例如:消息修剪),专门用于操作消息列表,使用更简单,更推荐。区别于AgentHook,MessagesModelHook在一次agent调用中可能会调用多次,也就是每次 reasoning-acting 迭代都会执行
@HookPositions({HookPosition.BEFORE_MODEL})
public class MessageTrimmingHook extends MessagesModelHook {
private static final int MAX_MESSAGES = 10;
@Override
public String getName() {
return "message_trimming";
}
@Override
public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {
if (previousMessages.size() > MAX_MESSAGES) {
// 只保留最后 MAX_MESSAGES 条消息
List<Message> trimmedMessages = previousMessages.subList(previousMessages.size() - MAX_MESSAGES, previousMessages.size());
return new AgentCommand(trimmedMessages, UpdatePolicy.REPLACE);
}
// 消息数量未超过限制,直接返回原消息列表
return new AgentCommand(previousMessages);
}
}
// 使用
ReactAgent.builder()
.name("weather_agent")
.model(model)
.tools(ToolCallbacks.from(weatherTools))
.systemPrompt(SYSTEM_PROMPT)
.hooks(loggingHook, messageTrimmingHook)
.toolExecutionExceptionProcessor(customToolExecutionExceptionProcessor)
.saver(new MemorySaver())
.build();
拦截器(interceptor)
java
import com.alibaba.cloud.ai.graph.agent.interceptor.*;
// ModelInterceptor - 内容安全检查
public class GuardrailInterceptor extends ModelInterceptor {
@Override
public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
// 前置:检查输入
if (containsSensitiveContent(request.getMessages())) {
return ModelResponse.of(AssistantMessage.builder().content("检测到不适当的内容").build());
}
// 执行调用
ModelResponse response = handler.call(request);
// 后置:检查输出
return sanitizeIfNeeded(response);
}
}
// ToolInterceptor - 监控和错误处理
public class ToolMonitoringInterceptor extends ToolInterceptor {
@Override
public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {
long startTime = System.currentTimeMillis();
try {
ToolCallResponse response = handler.call(request);
logSuccess(request, System.currentTimeMillis() - startTime);
return response;
} catch (Exception e) {
logError(request, e, System.currentTimeMillis() - startTime);
return ToolCallResponse.error(request.getToolCall(),
"工具执行遇到问题,请稍后重试");
}
}
}
// 组合使用
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.interceptors(new GuardrailInterceptor(), new LoggingInterceptor(), new ToolMonitoringInterceptor())
.saver(new MemorySaver())
.build();
迭代控制
java
// 最大调用次数
ModelCallLimitHook modelCallLimitHook = ModelCallLimitHook.builder()
// 每次最多调用 10 次
.runLimit(10)
// 超出限制时抛出异常
.exitBehavior(ModelCallLimitHook.ExitBehavior.ERROR)
.build();
// 使用内置的 ModelCallLimitHook 限制模型调用次数
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.hooks(loggingHook, messageTrimmingHook, modelCallLimitHook)
.saver(new MemorySaver())
.build();
流式输出
java
Flux<NodeOutput> stream = agent.stream("复杂任务");
stream.subscribe(
output -> {
// 检查是否为 StreamingOutput 类型
if (output instanceof StreamingOutput streamingOutput) {
OutputType type = streamingOutput.getOutputType();
// 处理模型推理的流式输出
if (type == OutputType.AGENT_MODEL_STREAMING) {
// 流式增量内容,逐步显示
System.out.print(streamingOutput.message().getText());
} else if (type == OutputType.AGENT_MODEL_FINISHED) {
// 模型推理完成,可获取完整响应
System.out.println("\n模型输出完成");
}
// 处理工具调用完成(目前不支持 STREAMING)
if (type == OutputType.AGENT_TOOL_FINISHED) {
System.out.println("工具调用完成: " + output.node());
}
// 对于 Hook 节点,通常只关注完成事件(如果Hook没有有效输出可以忽略)
if (type == OutputType.AGENT_HOOK_FINISHED) {
System.out.println("Hook 执行完成: " + output.node());
}
}
},
error -> System.err.println("错误: " + error),
() -> System.out.println("Agent 执行完成")
);