SpringAI系列|第6篇:Tool-Calling工具调用
Spring AI 实战系列 | 第 6 篇:Tool-Calling 工具调用
📄 约 7500 字 | ⏱️ 阅读约 18 分钟
系列说明:本文是《Spring AI 实战系列》第 6 篇,系统讲解 Tool Calling 的核心原理、声明式与编程式工具定义、多场景实战案例(天气/股票/数据库),以及生产环境的最佳实践。
前置知识:已完成第 2 篇,掌握 ChatClient 和 Prompt 工程化基础。
文章目录
前言
笔者曾参与一个智能客服项目,用户询问"我的订单到哪了?"
若 AI 仅基于训练数据回答,其无法获知用户的订单状态。但若使 AI 能够调用订单查询接口,即可给出准确的实时信息。
此即 Tool Calling 的价值——赋予 AI “手”,使其能够操作业务系统。它是 AI 从"聊天机器人"进化为"智能助手"的关键一步。
本文将从基础概念到生产实践,将 Tool Calling 论述透彻。
一、什么是 Tool Calling
1.1 直观理解
无 Tool Calling 时:
用户:北京今天天气怎么样?
AI:抱歉,我无法获取实时天气信息。
有 Tool Calling 时:
用户:北京今天天气怎么样?
AI:我查一下...(调用天气 API)
AI:北京今天晴,25°C,空气质量优。
核心区别:无 Tool Calling 的 AI 仅能"说",有 Tool Calling 的 AI 能够"做"。
1.2 工作原理

用户提问 → AI 判断是否需要调用工具
→ 如果需要,生成工具调用参数
→ 系统执行工具,获取结果
→ 把工具结果返回给 AI
→ AI 基于结果生成最终回答
整个过程对用户透明,用户仅看到最终的回答。
1.3 与 RAG 的区别
Tool Calling 和 RAG 都是增强 AI 能力的手段,但侧重点不同:
| 维度 | RAG | Tool Calling |
|---|---|---|
| 数据来源 | 静态文档 / 知识库 | 实时接口 / 数据库 |
| 核心能力 | 读取知识 | 执行操作 |
| 典型场景 | 企业问答、文档检索 | 查询订单、下单、预约 |
| 数据时效性 | 取决于索引更新频率 | 实时 |
| 是否修改数据 | 只读 | 可读写 |
简单记:RAG 使 AI “懂”,Tool Calling 使 AI “能”。 实际项目中两者经常配合使用。
二、声明式工具定义:@Tool 注解
Spring AI 1.0+ 提供了 @Tool 和 @ToolParam 注解,使开发者能够像编写普通 Controller 一样将 Java 方法暴露给 AI。
2.1 定义工具方法
@Service
public class WeatherService {
@Tool(name = "getWeather", description = "获取指定城市的当前天气信息,包括温度、天气状况、湿度和风速")
public WeatherInfo getWeather(
@ToolParam(description = "城市名称,如北京、上海、广州") String city) {
// 调用天气 API
return callWeatherApi(city);
}
@Tool(name = "getForecast", description = "获取指定城市未来几天的天气预报")
public List<WeatherInfo> getForecast(
@ToolParam(description = "城市名称") String city,
@ToolParam(description = "预报天数,范围 1-7") int days) {
return callForecastApi(city, days);
}
public record WeatherInfo(String city, String condition,
int temperature, String airQuality,
int humidity, int windSpeed) {}
}
关键点:
@Tool的description决定 AI 何时调用该工具@ToolParam的description决定 AI 如何填写参数- 返回值可以是任意对象,Spring AI 会自动序列化为 JSON
2.2 注册工具到 ChatClient
@Service
public class AiAssistant {
private final ChatClient chatClient;
public AiAssistant(ChatClient.Builder builder, WeatherService weatherService) {
this.chatClient = builder
.defaultTools(weatherService) // 注册工具
.build();
}
public String chat(String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}
defaultTools() 可以接收多个工具服务,Spring AI 会自动扫描所有带 @Tool 注解的方法。
2.3 运行效果
// 场景 1:单工具调用
String r1 = aiAssistant.chat("北京今天天气怎么样?");
// AI 自动调用 getWeather("北京")
// 输出:北京今天晴,25°C,空气质量优,湿度 45%,风速 3 级
// 场景 2:多参数工具
String r2 = aiAssistant.chat("上海未来三天天气如何?");
// AI 自动调用 getForecast("上海", 3)
// 场景 3:不需要工具
String r3 = aiAssistant.chat("你好");
// AI 直接回答,不调用任何工具
AI 会根据问题自主判断是否需要调用工具,开发者无需手动控制。
三、编程式工具定义
除了注解方式,Spring AI 还支持用代码手动创建工具,适合动态生成或需要复杂逻辑的场景。
3.1 使用 FunctionToolCallback
@Service
public class OrderToolService {
private final OrderRepository orderRepository;
public ToolCallback getOrderTool() {
return FunctionToolCallback.builder("getOrderStatus", (String orderId) -> {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new RuntimeException("订单不存在:" + orderId));
return new OrderStatus(order.getId(), order.getStatus(), order.getTrackingNumber());
})
.description("查询订单状态和物流信息,需要提供订单ID")
.inputType(String.class)
.build();
}
public ToolCallback cancelOrderTool() {
return FunctionToolCallback.builder("cancelOrder", (String orderId) -> {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new RuntimeException("订单不存在"));
if (!order.canCancel()) {
return "该订单当前状态为 " + order.getStatus() + ",无法取消";
}
order.cancel();
orderRepository.save(order);
return "订单 " + orderId + " 已成功取消";
})
.description("取消指定订单。注意:只有待发货状态的订单可以取消")
.inputType(String.class)
.build();
}
public record OrderStatus(String orderId, String status, String trackingNumber) {}
}
3.2 注册编程式工具
@Service
public class OrderAssistant {
private final ChatClient chatClient;
public OrderAssistant(ChatClient.Builder builder, OrderToolService orderToolService) {
this.chatClient = builder
.defaultTools(
orderToolService.getOrderTool(),
orderToolService.cancelOrderTool()
)
.build();
}
public String queryOrder(String question) {
return chatClient.prompt()
.system("你是一位订单查询助手。请基于查询结果友好地回答用户。")
.user(question)
.call()
.content();
}
}
3.3 声明式 vs 编程式对比
| 维度 | @Tool 注解 | FunctionToolCallback |
|---|---|---|
| 适用场景 | 静态工具、方法明确的业务逻辑 | 动态工具、运行时生成 |
| 代码量 | 少,只需加注解 | 多,需手动构建 |
| 参数描述 | 通过 @ToolParam 描述 | 通过 inputType 和 description |
| 灵活性 | 一般 | 高,可动态决定暴露哪些工具 |
| 推荐度 | ⭐⭐⭐⭐⭐ 大多数场景 | ⭐⭐⭐ 特殊场景 |
建议优先使用 @Tool 注解,代码更简洁、可读性更好。仅在需要动态控制工具时采用编程式方式。
四、实战案例
4.1 天气查询助手
完整实现一个天气查询工具,支持实时天气和预报查询:
@Service
public class WeatherToolService {
private final RestTemplate restTemplate = new RestTemplate();
@Tool(name = "getCurrentWeather",
description = "获取指定城市的当前实时天气,包括温度、天气状况、湿度、风速")
public WeatherResult getCurrentWeather(
@ToolParam(description = "城市名称,如北京、上海、广州、深圳") String city) {
// 调用第三方天气 API(示例)
String url = "https://api.weather.com/v1/current?city=" + URLEncoder.encode(city, StandardCharsets.UTF_8);
try {
WeatherApiResponse response = restTemplate.getForObject(url, WeatherApiResponse.class);
return new WeatherResult(
city, response.getTemperature(), response.getCondition(),
response.getHumidity(), response.getWindSpeed()
);
} catch (Exception e) {
return new WeatherResult(city, 0, "天气服务暂时不可用", 0, 0);
}
}
@Tool(name = "getWeatherForecast",
description = "获取指定城市未来几天的天气预报,用于出行规划")
public List<WeatherResult> getWeatherForecast(
@ToolParam(description = "城市名称") String city,
@ToolParam(description = "预报天数,1-7 天") int days) {
String url = String.format("https://api.weather.com/v1/forecast?city=%s&days=%d",
URLEncoder.encode(city, StandardCharsets.UTF_8), days);
ForecastApiResponse response = restTemplate.getForObject(url, ForecastApiResponse.class);
return response.getDaily().stream()
.map(d -> new WeatherResult(city, d.getTemp(), d.getCondition(),
d.getHumidity(), d.getWind()))
.collect(Collectors.toList());
}
public record WeatherResult(String city, int temperature, String condition,
int humidity, int windSpeed) {}
}
使用方式:
@Service
public class WeatherAssistant {
private final ChatClient chatClient;
public WeatherAssistant(ChatClient.Builder builder, WeatherToolService weatherTool) {
this.chatClient = builder.defaultTools(weatherTool).build();
}
public String ask(String question) {
return chatClient.prompt()
.system("你是一位天气助手。请根据天气数据给出友好的建议,比如是否适合出门、是否需要带伞等。")
.user(question)
.call()
.content();
}
}
// 测试
weatherAssistant.ask("北京今天天气怎么样?");
// → AI 调用 getCurrentWeather("北京"),输出:北京今天晴,25°C...
weatherAssistant.ask("上海未来三天天气如何,适合出门旅游吗?");
// → AI 调用 getWeatherForecast("上海", 3),结合数据给出建议
4.2 股票分析助手
@Service
public class StockToolService {
private final RestTemplate restTemplate = new RestTemplate();
@Tool(name = "getStockPrice",
description = "获取股票的实时价格信息,包括涨跌幅、成交量等")
public StockInfo getStockPrice(
@ToolParam(description = "股票代码,A 股如 600519,美股如 AAPL") String symbol) {
String url = "https://api.stock.com/price?symbol=" + symbol;
StockApiResponse response = restTemplate.getForObject(url, StockApiResponse.class);
return new StockInfo(symbol, response.getName(), response.getCurrentPrice(),
response.getChangePercent(), response.getVolume());
}
@Tool(name = "getStockHistory",
description = "获取股票历史价格走势,用于技术分析")
public List<StockHistory> getStockHistory(
@ToolParam(description = "股票代码") String symbol,
@ToolParam(description = "时间范围:1d(天), 1w(周), 1m(月), 3m(季), 1y(年)") String period) {
String url = String.format("https://api.stock.com/history?symbol=%s&period=%s", symbol, period);
return restTemplate.getForObject(url, new ParameterizedTypeReference<List<StockHistory>>() {});
}
public record StockInfo(String symbol, String name, double price,
double changePercent, long volume) {}
public record StockHistory(String date, double open, double close,
double high, double low, long volume) {}
}
配合 System Prompt 做分析:
@Service
public class StockAssistant {
private final ChatClient chatClient;
public StockAssistant(ChatClient.Builder builder, StockToolService stockTool) {
this.chatClient = builder.defaultTools(stockTool).build();
}
public String analyze(String question) {
return chatClient.prompt()
.system("""
你是一位专业的股票分析师。当用户询问股票时:
1. 先调用工具获取实时价格和历史数据
2. 分析趋势和关键指标
3. 给出客观分析(不构成投资建议)
4. 用通俗易懂的语言解释专业术语
""")
.user(question)
.call()
.content();
}
}
// 测试
stockAssistant.analyze("茅台最近走势怎么样?");
// → AI 调用 getStockPrice("600519") + getStockHistory("600519", "1m")
// → 输出综合分析报告
4.3 数据库查询助手(客服场景)
此场景最为实用——使 AI 直接查询数据库以回答用户问题:
@Service
public class DatabaseToolService {
private final JdbcTemplate jdbcTemplate;
public DatabaseToolService(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Tool(name = "queryUserInfo", description = "查询用户基本信息。当用户询问自己的账户信息时使用")
public UserInfo queryUserInfo(
@ToolParam(description = "用户ID,格式如 U12345") String userId) {
return jdbcTemplate.queryForObject(
"SELECT id, name, email, phone, create_time FROM users WHERE id = ?",
new BeanPropertyRowMapper<>(UserInfo.class), userId);
}
@Tool(name = "queryUserOrders",
description = "查询用户的订单列表。当用户询问订单、购买记录、物流时使用")
public List<OrderInfo> queryUserOrders(
@ToolParam(description = "用户ID") String userId,
@ToolParam(description = "订单状态筛选,可选值:PENDING, PAID, SHIPPED, COMPLETED。不传则查询全部") String status) {
String sql = "SELECT id, user_id, total_amount, status, create_time FROM orders " +
"WHERE user_id = ?" + (status != null ? " AND status = ?" : "") +
" ORDER BY create_time DESC LIMIT 20";
Object[] params = status != null ? new Object[]{userId, status} : new Object[]{userId};
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(OrderInfo.class), params);
}
@Tool(name = "queryOrderDetail", description = "查询订单详情,包括商品列表")
public OrderDetail queryOrderDetail(
@ToolParam(description = "订单ID,格式如 O98765") String orderId) {
OrderInfo order = jdbcTemplate.queryForObject(
"SELECT * FROM orders WHERE id = ?",
new BeanPropertyRowMapper<>(OrderInfo.class), orderId);
List<OrderItem> items = jdbcTemplate.query(
"SELECT * FROM order_items WHERE order_id = ?",
new BeanPropertyRowMapper<>(OrderItem.class), orderId);
return new OrderDetail(order, items);
}
// Record 定义
public record UserInfo(String id, String name, String email,
String phone, LocalDateTime createTime) {}
public record OrderInfo(String id, String userId, BigDecimal totalAmount,
String status, LocalDateTime createTime) {}
public record OrderItem(String id, String orderId, String productName,
int quantity, BigDecimal price) {}
public record OrderDetail(OrderInfo order, List<OrderItem> items) {}
}
客服助手整合:
@Service
public class CustomerServiceAssistant {
private final ChatClient chatClient;
public CustomerServiceAssistant(ChatClient.Builder builder, DatabaseToolService dbTool) {
this.chatClient = builder
.defaultTools(dbTool)
.build();
}
public String handleQuery(String question) {
return chatClient.prompt()
.system("""
你是某电商平台的智能客服助手。规则:
1. 先通过工具查询数据库获取准确信息
2. 基于真实数据回答,不要编造
3. 语气友好、专业
4. 如果查不到数据,引导用户提供正确的 ID
5. 涉及退款、投诉等问题,建议转人工客服
""")
.user(question)
.call()
.content();
}
}
// 测试效果
customerServiceAssistant.handleQuery("用户 U12345 最近有什么订单?");
// → AI 调用 queryUserOrders("U12345", null),返回订单列表
customerServiceAssistant.handleQuery("订单 O98765 买了什么?什么时候能到?");
// → AI 调用 queryOrderDetail("O98765"), 返回订单详情和商品信息
4.4 多工具组合
实际项目中往往需要同时挂载多种工具:
@Service
public class ComprehensiveAssistant {
private final ChatClient chatClient;
public ComprehensiveAssistant(
ChatClient.Builder builder,
WeatherToolService weatherTool,
StockToolService stockTool,
DatabaseToolService dbTool) {
this.chatClient = builder
.defaultTools(weatherTool, stockTool, dbTool) // 同时注册多个工具
.build();
}
public String ask(String question) {
return chatClient.prompt()
.system("你是一位全能助手,可以查询天气、股票和订单信息。根据用户问题智能选择合适的工具。")
.user(question)
.call()
.content();
}
}
// AI 会根据问题自动选择合适的工具
comprehensiveAssistant.ask("北京今天天气怎么样?顺便帮我查下茅台股价");
// → AI 同时调用 getCurrentWeather("北京") + getStockPrice("600519")
五、生产环境最佳实践
Demo 能跑通只是第一步,上生产环境还需要处理诸多细节。
5.1 工具描述优化
工具描述是 AI 判断"调不调"、"调哪个"的唯一依据,描述的质量直接决定调用的准确率。
❌ 反面教材:
@Tool(name = "getData", description = "获取数据")
@Tool(name = "tool1", description = "查询工具")
✅ 正确做法:
// 描述要具体,说明何时使用
@Tool(name = "getUserOrders",
description = "查询指定用户的订单列表。当用户询问自己的订单、购买记录、物流状态时使用此工具")
// 参数描述要给示例
@ToolParam(description = "用户ID,格式为 U 开头加 5 位数字,如 U12345")
// 命名要有语义
@Tool(name = "queryUserOrders", description = "...")
@Tool(name = "createRefundRequest", description = "...")
@Tool(name = "checkRefundStatus", description = "...")
核心原则:
- 描述要说明"什么时候用",而非"做什么"
- 参数描述要给格式示例和取值范围
- 命名采用动词开头,见名知意
5.2 错误处理
工具执行可能出错,关键是让 AI 能理解错误并妥善回应。
工具内捕获异常:
@Tool(name = "getWeather", description = "获取指定城市的当前天气")
public WeatherResult getWeather(String city) {
try {
return callWeatherApi(city);
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
// 返回特殊结果而非抛异常,让 AI 能理解并友好回复
return new WeatherResult(city, 0, "未找到该城市的天气信息,请确认城市名称是否正确", 0, 0);
}
throw new RuntimeException("天气服务暂时不可用,请稍后重试");
} catch (Exception e) {
throw new RuntimeException("查询天气失败:" + e.getMessage());
}
}
错误信息要让人能看懂:
// ❌ 差的错误信息 —— AI 无法据此生成有用的回复
throw new RuntimeException("NullPointerException at line 45");
// ✅ 好的错误信息 —— AI 据此向用户说明情况
throw new RuntimeException("查询失败:用户不存在。请确认用户ID格式是否正确(应为 U 开头加数字,如 U12345)");
外部 API 加重试:
@Tool(name = "getStockPrice", description = "获取股票实时价格")
public StockInfo getStockPrice(String symbol) {
int maxRetries = 3;
for (int i = 0; i < maxRetries; i++) {
try {
return callStockApi(symbol);
} catch (Exception e) {
if (i == maxRetries - 1) {
throw new RuntimeException("股票服务暂时不可用,请稍后重试");
}
try { Thread.sleep(1000 * (i + 1)); } catch (InterruptedException ignored) {}
}
}
throw new RuntimeException("查询失败");
}
5.3 权限控制
生产环境必须限制 AI 能操作什么、不能操作什么。
身份校验——只能查自己的数据:
@Tool(name = "queryUserOrders", description = "查询用户订单列表")
public List<OrderInfo> queryUserOrders(
@ToolParam(description = "用户ID") String userId) {
// 获取当前登录用户
String currentUserId = SecurityContextHolder.getContext()
.getAuthentication().getName();
// 强制校验:只能查自己的订单
if (!currentUserId.equals(userId)) {
throw new SecurityException("无权查询其他用户的订单");
}
return orderRepository.findByUserId(userId);
}
按角色分配工具:
@Configuration
public class ToolConfig {
@Bean
public ChatClient adminChatClient(ChatClient.Builder builder,
AdminToolService adminTool,
UserToolService userTool) {
// 管理员拥有全部工具
return builder.defaultTools(adminTool, userTool).build();
}
@Bean
public ChatClient userChatClient(ChatClient.Builder builder,
UserToolService userTool) {
// 普通用户只有基础工具
return builder.defaultTools(userTool).build();
}
}
运行时动态控制:
@Service
public class DynamicToolService {
public ChatClient createClientForUser(String userId, ChatClient.Builder builder) {
List<Object> tools = new ArrayList<>();
// 所有用户都有基础工具
tools.add(new WeatherToolService());
tools.add(new StockToolService());
// 管理员额外有管理工具
if (isAdmin(userId)) {
tools.add(new AdminToolService());
}
// VIP 用户有高级工具
if (isVip(userId)) {
tools.add(new PremiumToolService());
}
return builder.defaultTools(tools.toArray()).build();
}
}
5.4 操作审计
所有工具调用都应该记录审计日志,便于排查问题和合规检查:
@Aspect
@Component
public class ToolAuditAspect {
@Autowired
private AuditLogRepository auditLogRepository;
@Around("@annotation(org.springframework.ai.tool.annotation.Tool)")
public Object audit(ProceedingJoinPoint joinPoint) throws Throwable {
String toolName = joinPoint.getSignature().getName();
String userId = SecurityContextHolder.getContext()
.getAuthentication().getName();
String params = Arrays.toString(joinPoint.getArgs());
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
auditLogRepository.save(new AuditLog(
userId, toolName, params, "SUCCESS",
System.currentTimeMillis() - startTime,
LocalDateTime.now()
));
return result;
} catch (Exception e) {
auditLogRepository.save(new AuditLog(
userId, toolName, params, "FAILED: " + e.getMessage(),
System.currentTimeMillis() - startTime,
LocalDateTime.now()
));
throw e;
}
}
}
5.5 性能优化
工具结果缓存:
@Service
public class CachedToolService {
private final Cache<String, Object> cache = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES) // 5 分钟过期
.maximumSize(1000) // 最多缓存 1000 条
.build();
@Tool(name = "getWeather", description = "获取指定城市的当前天气")
public WeatherResult getWeather(String city) {
String cacheKey = "weather:" + city;
// 先查缓存
WeatherResult cached = (WeatherResult) cache.getIfPresent(cacheKey);
if (cached != null) {
return cached;
}
// 缓存未命中,调用 API
WeatherResult result = callWeatherApi(city);
cache.put(cacheKey, result);
return result;
}
}
异步执行非关键操作:
@Tool(name = "sendNotification", description = "发送通知消息")
public String sendNotification(
@ToolParam(description = "用户ID") String userId,
@ToolParam(description = "通知内容") String message) {
// 异步发送,不阻塞 AI 响应
CompletableFuture.runAsync(() -> {
notificationService.send(userId, message);
});
return "通知已发送";
}
控制返回数据量:
@Tool(name = "getStockPrice", description = "获取股票实时价格")
public StockInfo getStockPrice(String symbol) {
StockFullData fullData = callApi(symbol); // API 返回大量字段
// 只返回关键字段,减少 Token 消耗
return new StockInfo(
fullData.getSymbol(),
fullData.getName(),
fullData.getCurrentPrice(),
fullData.getChangePercent(),
fullData.getVolume()
// 不要把 K线数据、买卖盘等全部返回
);
}
六、常见问题汇总
Q1:AI 何时会调用工具?
AI 根据用户问题和工具描述自动判断。判断依据包括:
- 用户问题是否匹配工具的功能描述
- 用户是否提供了必要的参数信息
- 问题是否需要实时数据或执行操作
若用户问"你好",AI 不会调用任何工具;若问"北京天气",AI 则会调用天气工具。
Q2:AI 总是调错工具怎么办?
- 优化工具描述:让每个工具的边界更清晰
- 减少工具数量:工具越多,AI 越容易混淆
- 合并相似工具:比如"按日期查订单"和"按状态查订单"可以合并为一个
- 在 System Prompt 中明确指令:告诉 AI 什么情况用什么工具
Q3:工具执行太慢怎么办?
- 加缓存:天气、股价等变化不频繁的数据适合缓存
- 异步执行:通知、日志等非关键操作异步处理
- 设置超时:避免某个工具拖慢整个响应
- 优化内部逻辑:检查 SQL 是否有索引、API 调用是否必要
Q4:工具返回数据太多怎么办?
- 在工具方法里做数据裁剪,只返回关键字段
- 分页返回:列表类数据限制条数
- 多层查询:先返回摘要,AI 判断后再查详情
Q5:如何调试工具调用?
Spring AI 提供了详细的日志,开启 DEBUG 级别可以看到:
logging:
level:
org.springframework.ai.chat.client: DEBUG
日志会显示 AI 选择了哪个工具、传了什么参数、返回了什么结果。
写在总结
Tool Calling 是 Spring AI 中最实用的特性之一。它使 AI 从"能聊天"进化到"能干活",是构建企业级 AI 应用的核心技术栈。
回顾核心要点:
| 要点 | 关键做法 |
|---|---|
| 工具定义 | 优先用 @Tool 注解,动态场景用 FunctionToolCallback |
| 描述质量 | 决定 AI 调用准确率,要具体、给示例 |
| 错误处理 | 异常要转化为 AI 能理解的友好信息 |
| 权限控制 | 身份校验 + 角色分级 + 动态分配 |
| 性能优化 | 缓存 + 异步 + 数据裁剪 |
| 可观测性 | 审计日志 + DEBUG 日志 |
下一篇进入 Advisor 机制与对话管理,讲解如何让 AI 记住对话历史、管理上下文窗口、过滤敏感内容。
系列目录:
- 第一篇-SpringAI概述与快速上手
- 第二篇-模型集成与对话基础
- 第三篇-结构化输出与多模态
- 第四篇-Embedding与向量数据库
- 第五篇-RAG检索增强生成
- 第六篇-Tool-Calling工具调用 ✅(本文)
- 第七篇-Advisor机制与对话管理
- 第八篇-MCP模型上下文协议
- 第九篇-AI-Agent开发
- 第十篇-企业级应用与最佳实践
若本文对您有所帮助,欢迎点赞、收藏与关注,系列持续更新中!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)