Tool 定义与 AgentService 递归循环
约 565 字大约 2 分钟
布欧-Lewyon
2026-06-08
首页 › Agent Spring Boot › Function Calling
本篇讲手写 Tool 注册,非 Spring AI
@Tool。若要对照框架版,见 Spring AI Tool Calling(在新窗口打开)。
ToolDefinition 静态注册
AgentService 静态块构造 OpenAI tools[] 数组:
tools.add(new ToolDefinition(
"get_weather",
"查询指定城市的实时天气信息",
weatherParams // JSON Schema: { type, properties, required }
));
tools.add(new ToolDefinition(
"get_current_time",
"获取当前日期和时间,支持指定时区",
timeParams
));序列化形状:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "...",
"parameters": { "type": "object", "properties": { ... }, "required": ["city"] }
}
}ToolService 执行
@Service
public class ToolService {
public Map<String, Object> getWeather(String city) { /* 模拟六城天气 */ }
public String getCurrentTime(String timezone) { /* ZonedDateTime */ }
}递归 callLLM
public Mono<String> callLLM(List<ChatMessage> messages, int remainingRounds) {
if (remainingRounds <= 0) {
return Mono.just("工具调用轮次超限");
}
// POST /chat/completions ...
if ("stop".equals(finishReason)) {
return Mono.just(assistantContent);
}
if ("tool_calls".equals(finishReason)) {
messages.add(assistantMsg);
List<Mono<ChatMessage>> toolMonos = assistantMsg.getToolCalls().stream()
.map(this::executeToolAndBuildMessage)
.collect(Collectors.toList());
return Mono.zip(toolMonos, arr -> { /* append tool messages */ })
.flatMap(ignored -> callLLM(messages, remainingRounds - 1));
}
return Mono.just("模型返回异常");
}remainingRounds 初始 3,防止死循环。
executeToolAndBuildMessage
String functionName = toolCall.getFunction().getName();
Map<String, Object> args = objectMapper.readValue(
toolCall.getFunction().getArguments(),
new TypeReference<>() {});
switch (functionName) {
case "get_weather":
result = toolService.getWeather((String) args.getOrDefault("city", "北京"));
break;
case "get_current_time":
result = toolService.getCurrentTime((String) args.getOrDefault("timezone", "Asia/Shanghai"));
break;
}
return Mono.just(ChatMessage.toolResult(toolCall.getId(), functionName, resultText));对外 API
@GetMapping("/agent/chat")
public Mono<Map<String, String>> agentChat(@RequestParam String message) {
return agentService.agentChat(message)
.map(answer -> Map.of("answer", answer));
}curl "http://localhost:8080/api/agent/chat?message=上海现在几点,天气如何"模型可能连续调 get_current_time 和 get_weather,Mono.zip 并行执行。
System Prompt 路由
private static final String SYSTEM_PROMPT =
"你是一个乐于助人的 AI 助手。你可以使用提供的工具来获取信息。\n" +
"- 当用户询问天气时,使用 get_weather 工具。\n" +
"- 当用户询问当前时间时,使用 get_current_time 工具。\n" +
"- 对于其他问题,直接回答即可。";Schema alone 不够,Prompt 要写清何时必须调 Tool。
学习点
| 知识点 | 说明 |
|---|---|
静态 TOOLS 列表 | 启动时构建,全程复用 |
Mono.zip | 多 tool 并行 |
递归 callLLM | 替代 while,配合 Reactor |
stream:false | Agent 路径固定非流式 |
小结
- Tool 用
ToolDefinition+ JSON Schema 注册;执行在ToolService+ switch 分发。 callLLM根据finish_reason递归,最多 3 轮。- 扩展新 Tool 需改三处:静态注册、ToolService 方法、switch 分支(后续可重构为 Registry)。
上一节:协议与流程
下一节:ToolService 实战
