docs: 重构文档结构,分离学习笔记和 MVP 架构设计

**变更概述:**
- 将 MVP 架构设计文档独立到项目根目录 `mvp/`
- 整理 `docs/` 为纯学习和分析文档目录
- 按类型分类:learning(学习)、analysis(分析)、reports(报告)、guides(指南)

**目录结构:**
```
mvp/                          # MVP 架构设计(独立)
├── README.md                 # 数据库设计总览
├── architecture/             # 架构文档
│   ├── agent-architecture-mvp.md
│   ├── implementation-plan.md
│   └── ...
└── tables/                   # 数据表设计

docs/                         # 学习和分析文档
├── learning/                 # 学习笔记(00-08 编号)
├── analysis/                 # 分析笔记 + 重构计划
├── reports/                  # 临时报告
└── guides/                   # 指南文档
```

**详细变更:**
- docs/README.md → mvp/README.md(数据库设计入口)
- docs/architecture/ → mvp/architecture/(架构设计)
- docs/tables/ → mvp/tables/(数据表设计)
- docs/学习笔记-*.md → docs/learning/07-*.md, 08-*.md
- docs/项目学习路径.md → docs/learning/00-*.md
- docs/功能分析报告.md → docs/analysis/
- docs/修复报告-*.md → docs/reports/
- docs/日志配置*.md → docs/guides/ 或 docs/reports/
- docs/design/ → docs/analysis/(问题分析和重构计划)
This commit is contained in:
zhuyongxin
2026-06-23 14:14:51 +08:00
parent caef477cec
commit 60be51f4a5
25 changed files with 339 additions and 606 deletions
@@ -0,0 +1,576 @@
# Tool 定义方式对比与优化建议
> **文档日期**: 2026-05-31
> **参考文档**: https://java2ai.com/docs/frameworks/agent-framework/tutorials/tools
> **项目**: SuperBizAgent-java
---
## 📋 Spring AI Agent Framework 的 6 种 Tool 定义方式
| 方式 | 类型 | 难度 | 类型安全 | 动态性 | 最佳场景 |
|------|------|------|---------|--------|---------|
| **1. @Tool 注解** | 声明式 | ⭐ | ✅ | ❌ | 静态工具、类组织 |
| **2. MethodToolCallback** | 编程式 | ⭐⭐⭐ | ✅ | ✅ | 动态构建、反射 |
| **3. FunctionToolCallback** | 函数式 | ⭐⭐ | ✅ | ✅ | 函数式逻辑 |
| **4. @Bean 函数** | Spring式 | ⭐ | ❌ | ✅ | Spring 应用 |
| **5. ToolCallback 接口** | 自定义 | ⭐⭐⭐⭐ | ✅ | ✅ | 高度定制 |
| **6. MCP ToolCallback** | 外部进程 | ⭐⭐ | ✅ | ✅ | 外部服务 |
---
## 🔍 项目当前使用方式
### **方式1:@Tool 注解(主要方式)**
**使用位置**:
- `DateTimeTools.java`
- `InternalDocsTools.java`
- `QueryMetricsTools.java`
- `QueryLogsTools.java`
**代码示例**:
```java
@Component
public class InternalDocsTools {
@Autowired
private VectorSearchService vectorSearchService; // ← 依赖注入
@Value("${rag.top-k:3}")
private int topK; // ← 配置注入
@Tool(description = "Use this tool to search internal documentation...")
public String queryInternalDocs(
@ToolParam(description = "Search query") String query) { // ← 参数注解
List<SearchResult> results = vectorSearchService.searchSimilarDocuments(query, topK);
return objectMapper.writeValueAsString(results);
}
}
```
**注入方式**(`ChatService.java:93-101`):
```java
public Object[] buildMethodToolsArray() {
if (queryLogsTools != null) {
return new Object[]{dateTimeTools, internalDocsTools, queryMetricsTools, queryLogsTools};
} else {
return new Object[]{dateTimeTools, internalDocsTools, queryMetricsTools};
}
}
// 在 ReactAgent 中使用
ReactAgent.builder()
.methodTools(buildMethodToolsArray()) // ← 传入 @Tool 注解的对象
.build();
```
---
### **方式6:MCP ToolCallback(外部工具)**
**使用位置**:
- 腾讯云 CLS 日志查询(真实模式)
- 其他外部 MCP 服务
**代码示例**(`ChatService.java:106-111`):
```java
@Autowired(required = false)
private ToolCallbackProvider tools; // ← MCP 工具提供者
public ToolCallback[] getToolCallbacks() {
if (tools == null) {
return new ToolCallback[0];
}
return tools.getToolCallbacks();
}
// 在 ReactAgent 中使用
ReactAgent.builder()
.methodTools(buildMethodToolsArray()) // Java 工具
.tools(getToolCallbacks()) // MCP 工具
.build();
```
---
## ✅ 当前方式的优缺点分析
### **优点** ✅
| 优点 | 说明 |
|------|------|
| **代码清晰** | `@Tool` 注解一目了然,易于理解 |
| **类型安全** | 编译时检查,减少运行时错误 |
| **依赖注入** | 完美集成 Spring 生态(`@Autowired`, `@Value`) |
| **易于测试** | 工具类可以独立单元测试 |
| **配置灵活** | 通过 `@Value` 读取配置(如 `topK`, `mockEnabled`) |
| **状态管理** | 工具类可以有成员变量(如 `httpClient`, `objectMapper`) |
| **生命周期** | 支持 `@PostConstruct` 初始化(如 `QueryMetricsTools.init()`) |
---
### **缺点** ❌
| 缺点 | 影响 | 是否需要优化 |
|------|------|------------|
| **工具数组需要手动管理** | 每增加一个工具,需要修改 `buildMethodToolsArray()` | ⚠️ 可优化 |
| **工具名称为常量字符串** | `TOOL_QUERY_PROMETHEUS_ALERTS` 容易拼写错误 | ⚠️ 可优化 |
| **无法动态启用/禁用工具** | 必须在编译时确定工具列表 | ⚠️ 可优化(已有 Mock 模式) |
| **工具发现不够智能** | 需要手动添加到数组,无法自动扫描 | ⚠️ 可优化 |
---
## 🚀 优化方案
### **优化1:自动扫描 @Tool 注解** ⭐⭐⭐(推荐)
**问题**:每次新增工具类,都需要在 `ChatService` 中手动添加。
**解决方案**:自动扫描所有带 `@Component` 且包含 `@Tool` 方法的 Bean。
```java
@Service
public class ChatService {
@Autowired
private ApplicationContext applicationContext; // ← Spring 上下文
/**
* 自动扫描所有工具类
* 无需手动维护工具列表
*/
public Object[] buildMethodToolsArray() {
List<Object> tools = new ArrayList<>();
// 1. 获取所有 Spring Bean
Map<String, Object> beans = applicationContext.getBeansWithAnnotation(Component.class);
for (Object bean : beans.values()) {
// 2. 检查是否包含 @Tool 方法
boolean hasTool = Arrays.stream(bean.getClass().getMethods())
.anyMatch(m -> m.isAnnotationPresent(Tool.class));
if (hasTool) {
// 3. 根据配置决定是否添加
if (shouldIncludeTool(bean)) {
tools.add(bean);
logger.info("🔧 自动注册工具: {}", bean.getClass().getSimpleName());
}
}
}
return tools.toArray();
}
/**
* 判断是否应该包含某个工具(基于配置)
*/
private boolean shouldIncludeTool(Object bean) {
// 特殊处理:QueryLogsTools 只在 Mock 模式下启用
if (bean instanceof QueryLogsTools) {
return queryLogsTools != null;
}
return true;
}
}
```
**优点**:
- ✅ 新增工具类无需修改 `ChatService`
- ✅ 自动发现所有工具
- ✅ 保留配置化的启用/禁用逻辑
**缺点**:
- ⚠️ 性能开销(启动时扫描一次,可接受)
- ⚠️ 可能注册不需要的工具(需要过滤逻辑)
---
### **优化2:使用 @Bean 函数定义工具** ⭐⭐
**适用场景**:工具逻辑简单、无状态、偏函数式
**改造示例**:
**改造前**(当前方式):
```java
@Component
public class DateTimeTools {
@Tool(description = "Get the current date and time")
public String getCurrentDateTime() {
return LocalDateTime.now()...toString();
}
}
```
**改造后**(@Bean 函数):
```java
@Configuration
public class ToolsConfiguration {
@Bean("getCurrentDateTime")
@Description("Get the current date and time in the user's timezone. " +
"IMPORTANT: Time changes constantly. Always call this tool...")
public Supplier<String> getCurrentDateTime() {
return () -> LocalDateTime.now()
.atZone(LocaleContextHolder.getTimeZone().toZoneId())
.toString();
}
}
// 使用
ReactAgent.builder()
.toolNames("getCurrentDateTime") // ← 直接使用工具名
.build();
```
**优点**:
- ✅ 更简洁(适合简单工具)
- ✅ 函数式风格
- ✅ Spring 自动发现和注册
**缺点**:
- ❌ 无法使用成员变量(`Supplier` 无状态)
- ❌ 工具名称为字符串,非类型安全
- ❌ 不适合需要依赖注入的复杂工具(如 `InternalDocsTools`)
**结论**:**不推荐全面改造**,因为项目的工具大多需要依赖注入(`VectorSearchService`、`httpClient` 等)。
---
### **优化3:工具元数据统一管理** ⭐⭐⭐
**问题**:工具名称定义为常量,但未被使用,容易不一致。
**当前代码**:
```java
public class QueryMetricsTools {
/** 工具名常量,用于动态构建提示词 */
public static final String TOOL_QUERY_PROMETHEUS_ALERTS = "queryPrometheusAlerts";
@Tool(description = "...")
public String queryPrometheusAlerts() { // ← 方法名就是工具名
// ...
}
}
```
**问题**:`TOOL_QUERY_PROMETHEUS_ALERTS` 从未被使用,可能会过时。
**优化方案**:使用 `@Tool(name = ...)` 明确指定工具名
```java
public class QueryMetricsTools {
public static final String TOOL_NAME = "queryPrometheusAlerts";
@Tool(
name = TOOL_NAME, // ← 明确指定工具名(可选,默认为方法名)
description = "Query active alerts from Prometheus..."
)
public String queryPrometheusAlerts() {
// ...
}
}
```
**或者**:移除无用的常量
```java
public class QueryMetricsTools {
// 删除未使用的常量
// public static final String TOOL_QUERY_PROMETHEUS_ALERTS = "queryPrometheusAlerts";
@Tool(description = "...")
public String queryPrometheusAlerts() { // 方法名即工具名
// ...
}
}
```
---
### **优化4:工具分组与条件注册** ⭐⭐
**问题**:工具启用逻辑分散在多处(`@Autowired(required = false)`, `buildMethodToolsArray()`)
**优化方案**:使用 `@ConditionalOnProperty` 统一管理
```java
// Mock 模式的日志查询工具
@Component
@ConditionalOnProperty(name = "cls.mock-enabled", havingValue = "true")
public class QueryLogsTools {
@Tool(description = "...")
public String queryLogs(...) {
// Mock 实现
}
}
// 真实模式的工具由 MCP 提供,无需 Java 实现
```
**优点**:
- ✅ 配置化启用/禁用
- ✅ 无需 `@Autowired(required = false)`
- ✅ Spring 自动管理生命周期
**修改后的 `ChatService`**:
```java
@Service
public class ChatService {
@Autowired
private List<Object> toolBeans; // ← Spring 自动注入所有工具类
@Autowired(required = false)
private ToolCallbackProvider tools;
public Object[] buildMethodToolsArray() {
return toolBeans.stream()
.filter(bean -> hasToolMethod(bean)) // 过滤出包含 @Tool 方法的 Bean
.toArray();
}
private boolean hasToolMethod(Object bean) {
return Arrays.stream(bean.getClass().getMethods())
.anyMatch(m -> m.isAnnotationPresent(Tool.class));
}
}
```
---
### **优化5:工具返回类型结构化** ⭐⭐
**问题**:工具返回值都是 `String`(JSON),LLM 需要解析
**当前代码**:
```java
@Tool(description = "...")
public String queryInternalDocs(String query) {
List<SearchResult> results = vectorSearchService.searchSimilarDocuments(query, topK);
return objectMapper.writeValueAsString(results); // ← 手动序列化
}
```
**优化方案**:返回结构化对象(Spring AI 自动序列化)
```java
@Tool(description = "...")
public InternalDocsResponse queryInternalDocs(String query) {
List<SearchResult> results = vectorSearchService.searchSimilarDocuments(query, topK);
return new InternalDocsResponse(results); // ← 返回 POJO
}
@Data
public class InternalDocsResponse {
private List<SearchResult> results;
private int totalCount;
private String status;
public InternalDocsResponse(List<SearchResult> results) {
this.results = results;
this.totalCount = results.size();
this.status = "success";
}
}
```
**优点**:
- ✅ 类型安全
- ✅ LLM 自动解析
- ✅ 更清晰的数据结构
**缺点**:
- ⚠️ 需要定义额外的 DTO 类
- ⚠️ Spring AI 需要支持(当前版本可能只支持 `String`)
**验证**:查看 Spring AI 文档确认是否支持非 String 返回值。
---
## 🎯 推荐的优化优先级
### **短期优化(1-2周)**
| 优化项 | 优先级 | 难度 | 收益 |
|--------|--------|------|------|
| **优化3:移除未使用的工具名常量** | 🔴 高 | ⭐ 低 | 代码整洁 |
| **优化4:使用 `@ConditionalOnProperty`** | 🔴 高 | ⭐⭐ 中 | 配置简化 |
| **优化1:自动扫描工具类** | 🟡 中 | ⭐⭐⭐ 中 | 易扩展 |
---
### **中期优化(1个月)**
| 优化项 | 优先级 | 难度 | 收益 |
|--------|--------|------|------|
| **优化5:工具返回类型结构化** | 🟡 中 | ⭐⭐ 中 | 类型安全 |
| **添加工具单元测试** | 🟡 中 | ⭐⭐ 中 | 质量保障 |
| **工具性能监控** | 🟢 低 | ⭐⭐ 中 | 可观测性 |
---
### **长期优化(3个月+)**
| 优化项 | 优先级 | 难度 | 收益 |
|--------|--------|------|------|
| **优化2:部分工具改为 @Bean 函数** | 🟢 低 | ⭐⭐ 中 | 函数式风格 |
| **实现自定义 ToolCallback(高度定制)** | 🟢 低 | ⭐⭐⭐⭐ 高 | 特殊需求 |
---
## 📊 对比表:当前方式 vs 推荐方式
| 维度 | 当前方式 | 推荐方式(优化后) |
|------|---------|------------------|
| **工具发现** | 手动添加到数组 | 自动扫描 `@Tool` 注解 |
| **启用/禁用** | `@Autowired(required = false)` + 条件判断 | `@ConditionalOnProperty` |
| **工具名管理** | 未使用的常量 | 方法名即工具名 |
| **代码行数** | ~100 行 | ~50 行 |
| **易扩展性** | ⭐⭐ | ⭐⭐⭐⭐ |
| **维护成本** | ⭐⭐⭐ | ⭐ |
---
## 💡 最佳实践建议
### 1️⃣ **工具设计原则**
```java
// ✅ 好的工具设计
@Component
public class WeatherTools {
@Tool(description = "Get current weather for a location. Returns temperature, humidity, and conditions.")
public String getCurrentWeather(
@ToolParam(description = "City name, e.g., 'Beijing', 'London'") String city) {
// 清晰的输入验证
if (city == null || city.trim().isEmpty()) {
return "{\"error\": \"City name is required\"}";
}
// 结构化的返回值
WeatherData data = weatherService.getWeather(city);
return objectMapper.writeValueAsString(data);
}
}
// ❌ 不好的工具设计
@Tool(description = "Get weather") // ← 描述不够详细
public String getWeather(String c) { // ← 参数名不明确
return weatherService.get(c); // ← 返回值不规范
}
```
---
### 2️⃣ **工具命名规范**
| 规范 | 示例 | 说明 |
|------|------|------|
| **动词开头** | `getCurrentDateTime`, `queryInternalDocs` | 明确动作 |
| **驼峰命名** | `queryPrometheusAlerts` | Java 规范 |
| **避免缩写** | `queryMetrics` ✅, `queryMtr` ❌ | 可读性 |
| **包含主语** | `queryInternalDocs` ✅, `query` ❌ | 明确查询对象 |
---
### 3️⃣ **工具描述规范**
```java
// ✅ 好的描述
@Tool(description =
"Query active alerts from Prometheus alerting system. " +
"Returns all currently firing alerts with labels, annotations, state, and values. " +
"Use this when you need to check alert status, investigate conditions, or monitor system health.")
public String queryPrometheusAlerts() { }
// ❌ 不好的描述
@Tool(description = "Get alerts") // ← 太简短
public String queryPrometheusAlerts() { }
```
**描述应包含**:
1. **What**:工具的功能
2. **Returns**:返回值类型
3. **When to use**:使用场景
---
### 4️⃣ **工具错误处理**
```java
@Tool(description = "...")
public String queryInternalDocs(String query) {
try {
// 参数验证
if (query == null || query.trim().isEmpty()) {
return buildErrorResponse("Query cannot be empty", "INVALID_INPUT");
}
// 业务逻辑
List<SearchResult> results = vectorSearchService.searchSimilarDocuments(query, topK);
// 成功响应
return buildSuccessResponse(results);
} catch (Exception e) {
logger.error("Tool execution failed", e);
// 返回结构化错误(而不是抛异常)
return buildErrorResponse("Query failed", e.getMessage());
}
}
private String buildErrorResponse(String message, String details) {
return String.format(
"{\"status\": \"error\", \"message\": \"%s\", \"details\": \"%s\"}",
message, details
);
}
```
---
## 📚 参考资料
1. **Spring AI Alibaba Agent Framework 官方文档**
- Tool 定义:https://java2ai.com/docs/frameworks/agent-framework/tutorials/tools
- ReactAgent:https://java2ai.com/docs/frameworks/agent-framework/tutorials/react-agent
2. **Spring AI 官方文档**
- Function Calling:https://docs.spring.io/spring-ai/reference/api/functions.html
3. **项目现有工具类**
- `DateTimeTools.java` - 最简单的工具示例
- `InternalDocsTools.java` - 依赖注入示例
- `QueryMetricsTools.java` - 配置注入 + 状态管理示例
---
## ✅ 总结
### 当前方式:**@Tool 注解 + 手动注册** ✅
**评价**:**已经是很好的选择**,适合当前项目规模和复杂度。
**理由**:
1. ✅ 工具需要依赖注入(`VectorSearchService`, `httpClient` 等)
2. ✅ 工具需要配置注入(`@Value`)
3. ✅ 工具需要生命周期管理(`@PostConstruct`)
4. ✅ 工具逻辑组织在类中,易于维护
---
### 推荐的改进方向:
1. **短期**:移除未使用的常量,使用 `@ConditionalOnProperty`
2. **中期**:自动扫描工具类,减少手动维护
3. **长期**:根据实际需求考虑函数式改造或自定义 ToolCallback
---
**结论**:**保持当前的 @Tool 注解方式**,逐步应用上述优化,而不是全面重构。