1. Spring AI智能体开发实战指南
在当今AI技术快速发展的背景下,将大模型能力整合到企业应用中已成为趋势。Spring AI作为Spring生态中的AI集成框架,为Java开发者提供了便捷的AI能力接入方案。本文将详细介绍如何使用Spring AI框架构建一个具备工具调用能力的智能体系统。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与项目搭建
2.1 基础环境配置
开发Spring AI智能体需要以下环境配置:
- JDK 17或更高版本
- Spring Boot 3.4.5
- Spring AI 1.0.0
- 可选模型服务:
- Ollama本地模型服务
- 阿里云百炼大模型平台
建议使用IntelliJ IDEA或VS Code作为开发IDE,它们对Spring Boot项目有良好的支持。项目构建工具推荐使用Maven或Gradle,本文以Maven为例。
2.2 项目依赖配置
在pom.xml中配置Spring AI和Spring Boot的依赖管理:
xml复制<properties>
<spring-ai.version>1.0.0</spring-ai.version>
<spring-boot.version>3.4.5</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
然后添加具体依赖:
xml复制<dependencies>
<!-- Spring AI Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2.3 模型服务配置
在application.yml中配置模型服务:
yaml复制server:
port: 8080
spring:
application:
name: Agent-Demo
ai:
ollama:
base-url: http://localhost:11434
chat:
model: qwen2.5:3b
openai:
base-url: https://dashscope.aliyuncs.com/compatible-mode
api-key: your-api-key
chat:
options:
model: qwen-plus
如果使用Ollama本地模型,需要先安装并启动Ollama服务,然后通过以下命令查看可用模型:
bash复制ollama list
对于阿里云百炼平台,需要先注册账号并获取API Key,平台地址为:https://bailian.console.aliyun.com
3. 智能体核心架构设计
3.1 基础抽象类设计
智能体系统的核心是一个抽象基类BaseAgent,它定义了智能体的基本结构和执行流程:
java复制@Component
@Data
@Slf4j
public abstract class BaseAgent {
// 智能体基本信息
protected String name;
protected String description;
protected String systemPrompt;
protected String nextStepPrompt;
// LLM客户端
@Resource(name = "agentAssist")
protected ChatClient chatClient;
// 智能体状态
protected AgentState state = AgentState.IDLE;
// 消息历史
protected List<Message> msg = new ArrayList<>();
// 执行控制
protected int maxSteps = 10;
protected int currentStep = 0;
/**
* 执行智能体任务
*/
public void run(String message, SseEmitter emitter) {
log.info("智能体状态: {}", state.getState());
addMessage("user", message);
setState(AgentState.RUNNING);
try {
while (currentStep < maxSteps && state != AgentState.FINISHED) {
currentStep++;
String stepResult = step();
String resMessage = String.format("{\"step\": %d, \"result\": %s}",
currentStep, stepResult);
try {
emitter.send(SseEmitter.event()
.name("step" + currentStep)
.data(resMessage)
.id(String.valueOf(currentStep))
);
emitter.send("\n\n");
} catch (IOException e) {
setState(AgentState.ERROR);
emitter.send(SseEmitter.event()
.name("error")
.data("{\"error\": \"发送数据失败: " + e.getMessage() + "\"}")
.id("error")
);
emitter.completeWithError(e);
return;
}
}
emitter.complete();
} catch (Exception e) {
setState(AgentState.ERROR);
try {
emitter.send(SseEmitter.event()
.name("error")
.data("{\"error\": \"发送数据失败: " + e.getMessage() + "\"}")
.id("error")
);
emitter.completeWithError(e);
} catch (IOException ex) {
log.info("执行异常: {}", ex.getMessage());
}
} finally {
cleanup();
}
}
protected abstract String step();
protected abstract void cleanup();
}
BaseAgent采用了模板方法模式,定义了智能体的执行流程(run方法),而将具体步骤(step)和清理工作(cleanup)留给子类实现。
3.2 ReAct模式实现
ReAct(Reasoning and Acting)是一种结合推理和行动的智能体模式,我们通过ReActAgent类实现:
java复制@Data
public abstract class ReActAgent extends BaseAgent {
@Override
protected String step() {
boolean shouldAct = think();
System.out.println("shouldAct --> " + shouldAct);
if (!shouldAct) {
return "执行完成,无需再调用方法";
}
return act();
}
protected abstract boolean think();
protected abstract String act();
}
ReActAgent继承自BaseAgent,将step方法分解为think(思考)和act(行动)两个阶段,实现了ReAct模式的核心思想。
3.3 工具调用实现
ToolCallAgent进一步扩展了ReActAgent,增加了工具调用能力:
java复制@Component
@Slf4j
public class ToolCallAgent extends ReActAgent {
@Autowired
protected ToolCallbackProvider toolCallbackProvider;
protected List<AssistantMessage.ToolCall> toolCalls = new ArrayList<>();
@Override
protected boolean think() {
ChatOptions options = ToolCallingChatOptions.builder()
.toolCallbacks(toolCallbackProvider.getToolCallbacks())
.internalToolExecutionEnabled(false)
.build();
ChatResponse chatResponse = chatClient.prompt(systemPrompt)
.system(nextStepPrompt)
.messages(msg)
.options(options)
.call()
.chatResponse();
AssistantMessage output = chatResponse.getResult().getOutput();
toolCalls.addAll(output.getToolCalls());
log.info("---> 本轮挑选的工具:{}", toolCalls);
String content = output.getText();
addMessage("assistant", content);
// 检查终止条件
if (toolCalls.stream().anyMatch(toolCall -> "terminate".equals(toolCall.name()))) {
System.out.println("检测到终止请求,结束交互");
setState(AgentState.FINISHED);
toolCalls.clear();
return false;
}
return true;
}
@Override
protected String act() {
// 实际工具调用逻辑
return "工具调用结果";
}
@Override
protected void cleanup() {
// 清理资源
}
}
4. 业务智能体实现
4.1 ManusAgent实现
ManusAgent是一个具体的业务智能体实现,它继承自ToolCallAgent:
java复制@Component
public class ManusAgent extends ToolCallAgent {
@Resource(name = "agentAssist")
private ChatClient chatClient;
public ManusAgent() {
this.name = "Java Manus";
this.description = "解决多个任务的多功能智能体";
this.setSystemPrompt("""
您必须对所有用户交互以中文进行响应.你是Java Manus,一个先进的人工智能助手,旨在通过利用各种工具有效地解决任何用户任务.您的功能包括运行计算、处理文本、与远程服务交互以及在必要时终止任务.遵循这些步骤来处理任务:
1.任务分析与分解
2.工具选择
3.参数格式化
4.异常处理
5.任务完成与终止
例子:
- 用户任务:"查找最近的新闻并进行简单的计算。"
1. 任务1:检索最近的新闻
- 思考:这需要从网上获取信息
- 执行:使用网页浏览工具,输入"最近的新闻"之类的查询关键字
- 输出:总结出一些文章摘要
2. 任务2:执行一个简单的计算
- 思考:这需要执行一个数学表达式
- 执行:使用代码执行工具,如使用"2+2"
- 输出:结果为4
3. 合并结果:用中文回复新闻摘要和计算结果
4. 终止:如果所有子任务都已完成,则调用终止工具
即使内部推理是用英语,也要用中文回答.如果对任务不确定,请与用户澄清或使用终止工具以避免无限循环.
""");
this.setNextStepPrompt("""
根据用户的请求和当前上下文,确定下一个操作.遵循以下步骤:
1. 分析上下文
- 查看用户的原始请求和最近的对话历史.
- 标识当前子任务或任务计划中的下一步.
- 检查之前的工具输出以获取相关信息.
2. 计划并行动
- 确定当前子任务是否需要工具、进一步思考或终止.
- 如果需要工具,请根据其功能选择最合适的工具.
- 指定准确的参数,确保它们符合工具的需求(例如,字符串,JSON).
3. 执行或终止
- 如果调用工具,请提供清晰的参数并预测输出.
- 如果所有子任务都完成了(例如:检索到的信息、保存的文件),调用终止工具来完成任务.
- 如果卡住了(例如:重复的错误,重复的操作,比如保存相同的文件),调用终止工具并用中文解释,例如:"任务已完成,无需重复操作".
4. 日志推理
- 简要解释你的思考过程(内部可以用英语,但用中文回答).
- 注意在计划期间所做的任何挑战或假设.
例子:
- 上下文:用户询问最近的新闻和计算;检索到的新闻,正在进行计算.
1.分析:下一个子任务是执行计算.
2.计划:用一个数学表达式作为字符串来使用代码执行工具.
3.执行:使用"2 + 2"之类的参数调用工具.
4.推理:计算简单明了,期望得到数值结果.
5.请用中文回答:"正在执行计算...".
6.终止检查:如果所有子任务都已完成,则调用终止工具.
如果调用工具失败,请分析错误,调整参数或工具后重试.如果多次尝试仍无进展或任务已完成,则调用终止工具并用中文进行解释.
""");
this.setMaxSteps(10);
this.setChatClient(chatClient);
}
}
4.2 数学工具实现
实现一个简单的数学计算工具:
java复制@Service
public class MathTool {
@Tool(name = "add", description = "加法运算")
public String add(@ToolParam(description = "第一个数字") int a,
@ToolParam(description = "第二个数字") int b) {
System.out.println("加法运算");
return String.valueOf((a + b));
}
@Tool(name = "substract", description = "减法运算")
public String substract(@ToolParam(description = "第一个数字") int a,
@ToolParam(description = "第二个数字") int b) {
System.out.println("减法运算");
return String.valueOf((a - b));
}
@Tool(name = "multiply", description = "乘法运算")
public String multiply(@ToolParam(description = "第一个数字") int a,
@ToolParam(description = "第二个数字") int b) {
System.out.println("乘法运算");
return String.valueOf((a * b));
}
@Tool(name = "divide", description = "除法运算")
public String divide(@ToolParam(description = "第一个数字") int a,
@ToolParam(description = "第二个数字") int b) {
System.out.println("除法运算");
return String.valueOf((a / b));
}
}
4.3 工具配置
将工具注册到Spring AI框架:
java复制@Configuration
public class AgentConfig {
@Bean("toolCallbackProvider")
public ToolCallbackProvider toolCallbackProvider(MathTool mathTool) {
return MethodToolCallbackProvider
.builder()
.toolObjects(mathTool)
.build();
}
}
5. 控制器与API接口
5.1 智能体控制器
实现一个REST控制器来暴露智能体服务:
java复制@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai/agent/manus")
public class ManusController {
@Autowired
private ManusAgent manusAgent;
@GetMapping(value = "/execute", produces = "text/html;charset=UTF-8")
public SseEmitter execute(@RequestParam(name = "message") String message) {
SseEmitter sseEmitter = new SseEmitter(-1L);
manusAgent.run(message, sseEmitter);
return sseEmitter;
}
}
5.2 测试智能体
启动项目后,可以通过以下方式测试智能体:
-
直接访问API接口:
code复制GET /ai/agent/manus/execute?message=请计算3加5的结果 -
使用Postman或curl工具测试:
bash复制curl "http://localhost:8080/ai/agent/manus/execute?message=请计算3加5的结果" -
查看控制台日志,观察智能体的执行流程和工具调用情况
6. 架构设计与设计模式应用
6.1 设计模式应用
-
模板方法模式(Template Method Pattern)
- BaseAgent定义了run方法的算法骨架
- 将step和cleanup等步骤延迟到子类实现
- 优点:代码复用,扩展性强
-
责任链模式(Chain of Responsibility Pattern)
- 通过继承链形成处理责任传递:BaseAgent → ReActAgent → ToolCallAgent → ManusAgent
- 每个层级增强智能体能力,实现功能解耦
-
策略模式(Strategy Pattern)
- 不同层级的智能体可以提供不同的think()和act()实现
- 通过抽象方法定义算法族,使它们可以互相替换
-
工厂方法模式(Factory Method Pattern)
- 通过Spring的@Component注解管理智能体实例
- Spring容器作为工厂创建和管理智能体bean
6.2 设计原则应用
-
开闭原则(Open Close Principle)
- 对扩展开放:新增智能体类型只需扩展基类
- 对修改关闭:无需修改现有代码即可添加新功能
-
单一职责原则(Single Responsibility Principle)
- BaseAgent:基础流程控制
- ReActAgent:ReAct模式实现
- ToolCallAgent:工具调用功能
- ManusAgent:具体业务逻辑
-
依赖倒置原则(Dependence Inversion Principle)
- 高层模块不依赖低层模块,都依赖抽象
- 通过抽象方法(step(),think(),act())定义契约
7. 开发经验与注意事项
7.1 开发经验分享
-
提示词工程
- 系统提示词(systemPrompt)要明确智能体的角色和能力范围
- 下一步提示词(nextStepPrompt)要详细指导决策过程
- 使用具体例子帮助模型理解预期行为
-
工具设计
- 工具功能要单一明确
- 参数描述要详细,帮助模型正确调用
- 工具命名要有意义且一致
-
执行控制
- 设置最大步数(maxSteps)防止无限循环
- 实现终止条件检查
- 做好错误处理和状态管理
7.2 常见问题排查
-
工具未被调用
- 检查工具是否正确注册到Spring容器
- 验证ToolCallbackProvider配置是否正确
- 确保工具方法的@Tool和@ToolParam注解使用正确
-
模型响应不符合预期
- 优化提示词,增加更多示例
- 调整temperature等参数控制创造性
- 检查模型是否支持工具调用功能
-
性能问题
- 限制最大步数和单步超时时间
- 考虑缓存常用工具调用结果
- 异步处理耗时操作
-
状态管理问题
- 确保正确维护和重置智能体状态
- 在多请求场景下考虑实例隔离
- 实现合理的清理机制
8. 扩展与优化方向
8.1 功能扩展
-
多工具集成
- 添加网络搜索工具
- 集成数据库查询工具
- 增加文件处理工具
-
记忆与上下文
- 实现对话历史持久化
- 添加上下文记忆机制
- 支持长期记忆存储
-
多模态能力
- 集成图像处理工具
- 添加语音交互支持
- 支持多模态输入输出
8.2 性能优化
-
异步处理
- 使用CompletableFuture实现异步工具调用
- 采用反应式编程模型
- 实现批量处理能力
-
缓存策略
- 缓存常见工具调用结果
- 实现对话历史缓存
- 考虑模型响应缓存
-
负载均衡
- 多模型实例负载均衡
- 智能体实例池化管理
- 弹性伸缩策略
在实际项目中,可以根据具体需求选择合适的扩展和优化方向。Spring AI的模块化设计使得这些扩展可以逐步实现,而不会影响现有功能的稳定性。
