docs(knowledge): 添加测试知识库文档

新增 6 个知识库文档,用于测试 L0+L1 混合检索功能:

API 类:
- payment-errors.md - 支付网关错误码定义

领域知识类:
- spring-ai-tool-best-practices.md - Spring AI 工具定义最佳实践

基础设施类:
- redis-config.md - Redis 缓存配置指南
- mysql-connection-pool.md - MySQL 连接池配置
- flyway-best-practices.md - Flyway 数据库迁移最佳实践

故障排查类:
- fault-diagnosis-process.md - 故障诊断流程规范

所有文档均包含:
- 标准 frontmatter 元数据 (title, keywords, summary, category)
- 实用配置示例和代码片段
- 支持 L0 精确匹配的关键词
This commit is contained in:
zhuyongxin
2026-06-24 16:19:10 +08:00
parent d6229f3385
commit 3956426c97
5 changed files with 889 additions and 0 deletions
@@ -0,0 +1,259 @@
---
title: Spring AI 工具定义最佳实践
keywords: [Spring AI, @Tool, 工具定义, Agent, 函数调用]
summary: 如何为 Spring AI Agent 定义高质量的工具(Tool),包括命名、描述、参数设计和错误处理
category: domain
---
# Spring AI 工具定义最佳实践
## 工具定义基础
### 基本注解
```java
@Component
public class MyTools {
@Tool(description = "查询用户信息。参数 userId: 用户ID(必填)")
public UserInfo getUserInfo(String userId) {
// 实现
}
}
```
### 关键要素
1. **@Component** - 让 Spring 扫描到
2. **@Tool** - 标记为 Agent 可调用的工具
3. **description** - 告诉 Agent 这个工具做什么
## 描述(Description)编写规范
### 好的描述
```java
@Tool(description = "查询知识库文档。优先精确匹配关键词,未命中或多个匹配时自动补充语义相关片段。" +
"参数 query: 查询关键词,例如 'ERR_TIMEOUT'、'支付网关超时'")
public LookupResult lookupKnowledge(String query) { ... }
```
**要点**:
- ✅ 说明工具用途(查询知识库)
- ✅ 说明工作机制(精确匹配 → 语义补充)
- ✅ 说明参数含义和示例
### 差的描述
```java
@Tool(description = "查询文档") // ❌ 太简略
public LookupResult lookup(String q) { ... }
```
## 参数设计
### 参数命名
```java
// ✅ 好的命名 - 语义清晰
public Result search(String query, int maxResults, String category)
// ❌ 差的命名 - 缩写难懂
public Result search(String q, int max, String cat)
```
### 参数类型
```java
// ✅ 使用明确的类型
public UserInfo getUser(String userId)
public List<Order> getOrders(LocalDate startDate, LocalDate endDate)
// ❌ 使用 Object 或 Map
public Object getUser(Map<String, Object> params) // Agent 不知道传什么
```
### 可选参数处理
```java
@Tool(description = "查询订单。参数 status: 订单状态(可选,不传则查所有)")
public List<Order> getOrders(
@Nullable String status // 使用 @Nullable 标注
) {
if (status == null) {
return orderRepository.findAll();
}
return orderRepository.findByStatus(status);
}
```
## 返回值设计
### 使用明确的返回类型
```java
// ✅ 好的返回类型
public class LookupResult {
private boolean found;
private PrimaryResult primary;
private SupplementResult supplement;
}
// ❌ 返回 String - Agent 难以解析
public String lookup(String query) {
return "找到文档: xxx"; // 非结构化
}
```
### 返回错误信息
```java
public LookupResult lookup(String query) {
if (query == null || query.isEmpty()) {
return LookupResult.builder()
.found(false)
.error("查询关键词不能为空")
.build();
}
// 正常逻辑
}
```
## 错误处理
### 优雅降级
```java
@Tool(description = "查询用户信息")
public UserInfo getUser(String userId) {
try {
return userService.findById(userId);
} catch (UserNotFoundException e) {
log.warn("用户不存在: userId={}", userId);
return UserInfo.notFound(userId); // 返回特殊对象,不抛异常
} catch (Exception e) {
log.error("查询用户失败: userId={}", userId, e);
return UserInfo.error("系统错误,请稍后重试");
}
}
```
### 不要抛出未捕获的异常
```java
// ❌ 不要这样做
@Tool(description = "查询用户")
public UserInfo getUser(String userId) {
return userService.findById(userId); // 可能抛出异常,Agent 无法处理
}
```
## 可观测性
### 日志规范
```java
@Tool(description = "查询订单")
public List<Order> getOrders(String userId) {
String requestId = UUID.randomUUID().toString().substring(0, 8);
long startTime = System.currentTimeMillis();
log.info("[{}] 收到订单查询请求: userId={}", requestId, userId);
try {
List<Order> orders = orderService.findByUserId(userId);
long elapsed = System.currentTimeMillis() - startTime;
log.info("[{}] 查询完成: count={}, time={}ms", requestId, orders.size(), elapsed);
return orders;
} catch (Exception e) {
log.error("[{}] 查询失败: userId={}", requestId, userId, e);
throw e;
}
}
```
## 性能优化
### 设置合理的超时
```java
@Tool(description = "查询大数据集")
public DataResult queryBigData(String query) {
// 设置超时保护
return CompletableFuture
.supplyAsync(() -> heavyQuery(query))
.orTimeout(5, TimeUnit.SECONDS)
.exceptionally(ex -> DataResult.timeout())
.join();
}
```
### 避免返回超大数据
```java
// ✅ 分页或限制数量
@Tool(description = "查询用户列表(最多返回 100 条)")
public List<User> listUsers(int page, int size) {
size = Math.min(size, 100); // 强制上限
return userService.findAll(PageRequest.of(page, size));
}
// ❌ 返回全量数据
public List<User> listAllUsers() {
return userService.findAll(); // 可能几万条
}
```
## 工具组合示例
### 查询 + 操作的组合
```java
@Component
public class OrderTools {
@Tool(description = "查询订单详情")
public OrderDetail getOrder(String orderId) { ... }
@Tool(description = "取消订单")
public CancelResult cancelOrder(String orderId, String reason) { ... }
@Tool(description = "申请退款")
public RefundResult refund(String orderId, Double amount) { ... }
}
```
**Agent 使用场景**:
1. 用户:"帮我查一下订单 12345"
2. Agent 调用 `getOrder("12345")`
3. 用户:"帮我取消这个订单"
4. Agent 调用 `cancelOrder("12345", "用户主动取消")`
## 常见陷阱
### ❌ 工具做太多事
```java
// 不要把整个业务流程塞进一个工具
@Tool(description = "处理订单")
public void processOrder(String orderId) {
// 查询订单
// 验证库存
// 扣减库存
// 创建物流单
// 发送通知
// ... 太多步骤,Agent 无法介入
}
```
### ✅ 拆分成多个工具
```java
@Tool(description = "查询订单")
public Order getOrder(String orderId) { ... }
@Tool(description = "验证库存")
public StockResult checkStock(String productId, int quantity) { ... }
@Tool(description = "创建物流单")
public ShipmentResult createShipment(String orderId) { ... }
```
### ❌ 描述不准确
```java
@Tool(description = "查询用户")
public UserInfo getUser(String query) {
// 实际上支持按 userId、email、手机号查询
// 但描述没说清楚,Agent 不知道
}
```
### ✅ 描述完整
```java
@Tool(description = "查询用户信息。支持按 userId、email 或手机号查询。" +
"参数 query: 用户ID、邮箱或手机号")
public UserInfo getUser(String query) { ... }
```