数据库与 API Tool 实战
约 736 字大约 2 分钟
布欧-Lewyon
2026-05-15
首页 › Spring AI › Tool / Function Calling
数据库查询 Tool
将数据库查询能力暴露给 AI,实现自然语言查数据库。
用户查询 Tool
@Component
public class UserDatabaseTools {
private final JdbcTemplate jdbcTemplate;
public UserDatabaseTools(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Tool(name = "query_user_info", description = "Query user information by name or email")
public String queryUserInfo(
@ToolParam(description = "User name or email to search") String keyword) {
String sql = "SELECT id, name, email, phone, register_date FROM users " +
"WHERE name LIKE ? OR email LIKE ? LIMIT 1";
try {
Map<String, Object> result = jdbcTemplate.queryForMap(sql,
"%" + keyword + "%", "%" + keyword + "%");
return """
用户ID: %s
姓名: %s
邮箱: %s
电话: %s
注册时间: %s
""".formatted(
result.get("id"), result.get("name"),
result.get("email"), result.get("phone"),
result.get("register_date"));
} catch (EmptyResultDataAccessException e) {
return "未找到用户: " + keyword;
}
}
@Tool(name = "get_user_orders", description = "Get order history for a user")
public List<OrderSummary> getUserOrders(
@ToolParam(description = "User ID") Long userId) {
return jdbcTemplate.query(
"SELECT id, total_amount, status, created_at FROM orders WHERE user_id = ? ORDER BY created_at DESC LIMIT 10",
(rs, row) -> new OrderSummary(
rs.getLong("id"),
rs.getBigDecimal("total_amount"),
rs.getString("status"),
rs.getTimestamp("created_at").toLocalDateTime()
),
userId
);
}
}
record OrderSummary(Long orderId, BigDecimal amount, String status, LocalDateTime createdAt) {}外部 API Tool
让 AI 调用第三方 API 获取天气、新闻、股票等信息。
天气查询 Tool
@Component
public class WeatherTool {
private final RestTemplate restTemplate;
public WeatherTool() {
this.restTemplate = new RestTemplate();
}
@Tool(name = "get_weather", description = "Get current weather for a city")
public String getWeather(
@ToolParam(description = "City name, e.g. Beijing, Shanghai") String city) {
try {
// 使用 wttr.in 免费天气 API
String url = "https://wttr.in/%s?format=%s".formatted(
city, "Location: %l\\nTemperature: %t\\nCondition: %C\\nHumidity: %h"
);
String response = restTemplate.getForObject(url, String.class);
return response != null ? response.trim() : "No data";
} catch (Exception e) {
return "Failed to get weather for " + city + ": " + e.getMessage();
}
}
}新闻搜索 Tool
@Component
public class NewsTool {
@Value("${news.api.key}")
private String apiKey;
private final RestTemplate restTemplate;
@Tool(name = "search_news", description = "Search latest news by keyword")
public String searchNews(
@ToolParam(description = "Search keyword") String keyword,
@ToolParam(description = "Number of results (max 10)") int limit) {
limit = Math.min(limit, 10);
try {
String url = "https://newsapi.org/v2/everything?q=%s&pageSize=%d&apiKey=%s"
.formatted(keyword, limit, apiKey);
Map<String, Object> response = restTemplate.getForObject(url, Map.class);
if (response == null || !"ok".equals(response.get("status"))) {
return "No news found";
}
@SuppressWarnings("unchecked")
List<Map<String, Object>> articles =
(List<Map<String, Object>>) response.get("articles");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < articles.size(); i++) {
Map<String, Object> article = articles.get(i);
sb.append("%d. [%s] %s\\n".formatted(
i + 1,
article.get("source") != null ?
((Map<?, ?>) article.get("source")).get("name") : "Unknown",
article.get("title")));
}
return sb.toString();
} catch (Exception e) {
return "Error fetching news: " + e.getMessage();
}
}
}Tool 执行错误处理
@Component
public class SafeDatabaseTools {
@Tool(name = "execute_sql", description = "Execute a READ-ONLY SQL query and return results")
public String executeQuery(
@ToolParam(description = "SQL SELECT query to execute") String sql) {
// 安全检查:只允许 SELECT
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
return "Error: Only SELECT queries are allowed";
}
try {
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql);
if (results.isEmpty()) return "No results";
return results.stream()
.map(Map::toString)
.collect(Collectors.joining("\\n"));
} catch (Exception e) {
return "Query error: " + e.getMessage();
}
}
}多 Tool 综合注册
@Configuration
public class ToolsConfiguration {
@Bean
public ChatClient chatClientWithTools(
ChatClient.Builder builder,
UserDatabaseTools userTools,
WeatherTool weatherTool,
ProductTools productTools) {
return builder
.defaultSystem("""
You are a helpful assistant with access to multiple tools.
Use tools when you need real-time data.
""")
.tools(userTools, weatherTool, productTools)
.build();
}
}小结
- 数据库 Tool:注入 JdbcTemplate,编写 SQL,AI 自动调用。
- API Tool:使用 RestTemplate 调用外部 API,AI 适配实时数据。
- 错误处理:在 Tool 方法内捕获异常,返回友好的错误信息。
- 安全考虑:数据库 Tool 应限制为只读操作。
- 多个 Tool 可同时注册,AI 根据描述自动选择合适的 Tool。
上一节:Tool Calling 基础 下一节:图像理解
