AI Agent开发实战:ReAct模式 + Spring AI工具调用完整指南
2026/4/30大约 6 分钟
AI Agent开发实战:ReAct模式 + Spring AI工具调用完整指南
适读人群:有Spring AI基础,想进入Agent开发领域的Java工程师
阅读时长:约20分钟
文章价值:ReAct完整实现 + 多工具Agent + 生产踩坑
什么是AI Agent?为什么它是下一个风口?
传统的AI应用是"一问一答"的模式:用户提问 → LLM回答。但现实的工作任务远比这复杂——"帮我分析竞品,写一份市场报告",这个任务需要:搜索互联网 → 分析数据 → 生成报告 → 根据反馈修改……
AI Agent = LLM + 工具 + 记忆 + 规划能力
ReAct模式:Agent的思考-行动循环
ReAct(Reasoning + Acting)是目前最主流的Agent范式,核心思路是:先思考(Thought),再行动(Action),观察结果(Observation),循环迭代。
Spring AI工具调用实现
基础工具定义(@Tool注解)
@Component
public class AgentTools {
private final WeatherApiClient weatherClient;
private final SearchApiClient searchClient;
private final DatabaseService dbService;
/**
* 天气查询工具
*/
@Tool(description = "查询指定城市的实时天气信息,包含温度、天气状况、空气质量")
public WeatherInfo getWeather(
@ToolParam(description = "城市名称,如:北京、上海、广州") String city
) {
return weatherClient.getCurrentWeather(city);
}
/**
* 网络搜索工具
*/
@Tool(description = "搜索互联网获取最新信息,适用于需要实时资讯的场景")
public SearchResult searchInternet(
@ToolParam(description = "搜索关键词") String query,
@ToolParam(description = "返回结果数量,默认5") int topK
) {
return searchClient.search(query, topK);
}
/**
* 数据库查询工具
*/
@Tool(description = "查询公司内部数据库,获取产品信息、用户数据、订单状态等")
public String queryDatabase(
@ToolParam(description = "SQL查询语句,只允许SELECT操作") String sql
) {
// 安全校验:只允许SELECT
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
return "错误:只允许执行SELECT查询";
}
return dbService.executeQuery(sql);
}
/**
* 代码执行工具
*/
@Tool(description = "执行Python代码片段,用于数据计算、统计分析")
public String executeCode(
@ToolParam(description = "Python代码") String code
) {
return sandboxExecutor.run(code, Duration.ofSeconds(10));
}
}Agent服务构建
@Service
@RequiredArgsConstructor
public class ReActAgentService {
private final ChatClient.Builder chatClientBuilder;
private final AgentTools agentTools;
private final InMemoryChatMemoryRepository memoryRepository;
private static final String AGENT_SYSTEM_PROMPT = """
你是一位智能助手,能够使用多种工具完成复杂任务。
工作方式:
1. 仔细分析用户的任务需求
2. 制定执行计划,确定需要使用哪些工具
3. 按计划逐步使用工具收集信息
4. 综合所有信息生成最终答案
注意事项:
- 每次工具调用都要有明确的目的
- 遇到错误时要尝试备用方案
- 最终答案要清晰、有条理
- 如果任务无法完成,明确说明原因
""";
public ChatClient buildAgentClient(String sessionId) {
ChatMemory memory = new MessageWindowChatMemory(
memoryRepository, sessionId, 20 // 保留最近20条消息
);
return chatClientBuilder
.defaultSystem(AGENT_SYSTEM_PROMPT)
.defaultAdvisors(
new MessageChatMemoryAdvisor(memory),
new SimpleLoggerAdvisor()
)
.defaultTools(agentTools) // 注册所有工具
.build();
}
/**
* 执行Agent任务(同步)
*/
public AgentResult executeTask(String sessionId, String task) {
ChatClient agent = buildAgentClient(sessionId);
long startTime = System.currentTimeMillis();
String result = agent.prompt()
.user(task)
.call()
.content();
return AgentResult.builder()
.result(result)
.executionTime(System.currentTimeMillis() - startTime)
.sessionId(sessionId)
.build();
}
/**
* 执行Agent任务(流式,更好的UX)
*/
public Flux<String> executeTaskStream(String sessionId, String task) {
ChatClient agent = buildAgentClient(sessionId);
return agent.prompt()
.user(task)
.stream()
.content();
}
}多Agent协作:复杂任务分解
对于复杂任务,单个Agent力不从心。多Agent协作将任务分解给专业的子Agent处理。
@Service
@RequiredArgsConstructor
public class MultiAgentOrchestrator {
private final ResearchAgent researchAgent;
private final AnalysisAgent analysisAgent;
private final WritingAgent writingAgent;
public CompletableFuture<Report> generateCompetitiveReport(String topic) {
// 并行执行调研
CompletableFuture<ResearchResult> researchFuture =
CompletableFuture.supplyAsync(() -> researchAgent.research(topic));
// 等待调研完成后进行分析
CompletableFuture<AnalysisResult> analysisFuture = researchFuture
.thenApplyAsync(research -> analysisAgent.analyze(research));
// 所有数据就绪后生成报告
return analysisFuture.thenApplyAsync(analysis ->
writingAgent.writeReport(topic, researchFuture.join(), analysis)
);
}
}Agent的关键能力:记忆管理
@Service
public class AgentMemoryService {
private final VectorStore longTermMemory;
private final InMemoryChatMemoryRepository shortTermMemory;
/**
* 将重要信息存入长期记忆
*/
public void memorize(String agentId, String content, String type) {
Document memory = new Document(content, Map.of(
"agent_id", agentId,
"memory_type", type,
"timestamp", Instant.now().toString()
));
longTermMemory.add(List.of(memory));
}
/**
* 检索相关历史记忆
*/
public List<String> recall(String agentId, String query) {
return longTermMemory.similaritySearch(
SearchRequest.query(query)
.withTopK(3)
.withFilterExpression("agent_id == '" + agentId + "'")
).stream()
.map(Document::getContent)
.collect(Collectors.toList());
}
}生产踩坑经验
坑1:工具描述不够清晰导致LLM选错工具
问题:LLM经常调用错误的工具
解决:工具描述要包含"何时使用"和"何时不使用"
// ❌ 不够清晰
@Tool(description = "搜索信息")
public String search(String query) { ... }
// ✅ 清晰明确
@Tool(description = "搜索互联网获取最新实时信息。适用:新闻资讯、当前事件、最新数据。不适用:公司内部数据(请用queryDatabase)、历史知识(LLM自身知识更权威)")
public String searchInternet(String query) { ... }坑2:无限循环调用工具
问题:Agent陷入死循环,持续调用工具
解决:设置最大迭代次数和超时
// Spring AI 1.0中的工具调用次数限制
ChatOptions options = OpenAiChatOptions.builder()
.withMaxCompletionTokens(4000)
.build();
// 业务层超时控制
String result = Mono.fromCallable(() -> agent.prompt().user(task).call().content())
.timeout(Duration.ofSeconds(60))
.block();坑3:工具异常导致Agent崩溃
问题:外部工具API不稳定,Agent整体失败
解决:工具层加入完善的异常处理
@Tool(description = "查询天气,如果查询失败会返回错误提示")
public String getWeather(String city) {
try {
return weatherClient.query(city).toString();
} catch (Exception e) {
// 返回可读的错误信息,让Agent决定如何处理
return "天气查询失败:" + e.getMessage() + ",建议让用户自行查询天气。";
}
}