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:
@@ -0,0 +1,568 @@
|
||||
# MethodToolCallback vs ToolCallingManager 深度分析
|
||||
|
||||
> **问题来源**: Debugger 发现 tool 调用没有经过 `ToolCallingManager`,而是直接经过 `MethodToolCallback`
|
||||
> **分析日期**: 2026-05-31
|
||||
> **项目**: SuperBizAgent-java
|
||||
|
||||
---
|
||||
|
||||
## 🔍 核心问题
|
||||
|
||||
用户在 debugger 中发现:
|
||||
```
|
||||
预期调用链路:
|
||||
ReactAgent.call() → ToolCallingManager → MethodToolCallback → 实际工具方法
|
||||
|
||||
实际调用链路:
|
||||
ReactAgent.call() → MethodToolCallback → 实际工具方法 ❌ 跳过了 ToolCallingManager
|
||||
```
|
||||
|
||||
**疑问**:
|
||||
1. `MethodToolCallback` 和 `ToolCallingManager` 有什么区别?
|
||||
2. 为什么会跳过 `ToolCallingManager`?
|
||||
3. 正常的调用链路应该是怎样的?
|
||||
|
||||
---
|
||||
|
||||
## 📚 组件职责分析
|
||||
|
||||
### 1️⃣ **MethodToolCallback** - 工具调用执行器
|
||||
|
||||
**类型**:`ToolCallback` 接口的具体实现
|
||||
|
||||
**职责**:
|
||||
- **执行层**:通过反射调用带 `@Tool` 注解的 Java 方法
|
||||
- **参数转换**:将 JSON 字符串参数转换为方法参数
|
||||
- **结果封装**:将方法返回值转换为 LLM 可读的格式
|
||||
|
||||
**核心方法**:
|
||||
```java
|
||||
public class MethodToolCallback implements ToolCallback {
|
||||
|
||||
private final Method toolMethod; // 工具方法(反射)
|
||||
private final Object toolObject; // 工具对象实例
|
||||
private final ToolDefinition definition; // 工具定义
|
||||
|
||||
@Override
|
||||
public String call(String toolInput) {
|
||||
// 1. 解析 JSON 参数
|
||||
Object[] args = parseArguments(toolInput, toolMethod);
|
||||
|
||||
// 2. 反射调用方法
|
||||
Object result = toolMethod.invoke(toolObject, args);
|
||||
|
||||
// 3. 转换为 JSON 返回
|
||||
return convertToJson(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**创建时机**:
|
||||
```java
|
||||
// Spring AI 框架内部自动创建
|
||||
ReactAgent.builder()
|
||||
.methodTools(new DateTimeTools()) // ← 传入带 @Tool 的对象
|
||||
.build();
|
||||
|
||||
// 内部逻辑(简化):
|
||||
for (Object toolObject : methodTools) {
|
||||
for (Method method : toolObject.getClass().getMethods()) {
|
||||
if (method.isAnnotationPresent(Tool.class)) {
|
||||
ToolCallback callback = new MethodToolCallback(
|
||||
method, // getCurrentDateTime()
|
||||
toolObject, // dateTimeTools 实例
|
||||
extractDefinition(method)
|
||||
);
|
||||
toolCallbacks.add(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ **ToolCallingManager** - 工具调用管理器
|
||||
|
||||
**类型**:更高层次的协调器(可能存在于某些框架版本)
|
||||
|
||||
**职责**(推测):
|
||||
- **协调层**:管理多个工具调用的生命周期
|
||||
- **权限控制**:检查工具调用权限
|
||||
- **日志记录**:统一记录所有工具调用
|
||||
- **异常处理**:统一捕获和处理工具调用异常
|
||||
- **性能监控**:统计工具调用次数、耗时等
|
||||
|
||||
**可能的实现**(伪代码):
|
||||
```java
|
||||
public class ToolCallingManager {
|
||||
|
||||
private final List<ToolCallback> toolCallbacks;
|
||||
private final ToolCallLogger logger;
|
||||
private final ToolCallPermissionChecker permissionChecker;
|
||||
|
||||
public String executeToolCall(String toolName, String arguments) {
|
||||
// 1. 权限检查
|
||||
if (!permissionChecker.canCall(toolName)) {
|
||||
throw new PermissionDeniedException("Tool not allowed: " + toolName);
|
||||
}
|
||||
|
||||
// 2. 查找对应的 ToolCallback
|
||||
ToolCallback callback = findToolCallback(toolName);
|
||||
|
||||
// 3. 日志记录(调用前)
|
||||
logger.logBefore(toolName, arguments);
|
||||
|
||||
try {
|
||||
// 4. 执行实际调用
|
||||
String result = callback.call(arguments); // ← 调用 MethodToolCallback
|
||||
|
||||
// 5. 日志记录(调用后)
|
||||
logger.logAfter(toolName, result);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
logger.logError(toolName, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔗 调用链路分析
|
||||
|
||||
### **情况1:Spring AI 标准架构(无 ToolCallingManager)** ⭐
|
||||
|
||||
```
|
||||
用户: "现在几点了?"
|
||||
↓
|
||||
ReactAgent.call(question)
|
||||
↓
|
||||
ChatModel.call(prompt, tools) // DeepSeek V4
|
||||
↓
|
||||
LLM 返回工具调用请求:
|
||||
{
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_abc123",
|
||||
"name": "getCurrentDateTime",
|
||||
"arguments": "{}"
|
||||
}
|
||||
]
|
||||
}
|
||||
↓
|
||||
ReactAgent 内部循环处理工具调用
|
||||
↓
|
||||
找到对应的 ToolCallback(MethodToolCallback 实例)
|
||||
↓
|
||||
MethodToolCallback.call("{}") // ← 直接调用
|
||||
↓
|
||||
反射调用 DateTimeTools.getCurrentDateTime()
|
||||
↓
|
||||
返回: "2026-05-31T16:30:00+08:00[Asia/Shanghai]"
|
||||
↓
|
||||
将结果作为新消息发送给 LLM
|
||||
↓
|
||||
LLM 生成最终回答
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- ✅ **简单直接**:没有中间层,性能更好
|
||||
- ✅ **职责清晰**:MethodToolCallback 只负责执行
|
||||
- ❌ **缺少统一管理**:日志、权限、监控需要在各处实现
|
||||
|
||||
---
|
||||
|
||||
### **情况2:带 ToolCallingManager 的架构(某些企业版本)** ⭐⭐
|
||||
|
||||
```
|
||||
用户: "现在几点了?"
|
||||
↓
|
||||
ReactAgent.call(question)
|
||||
↓
|
||||
ChatModel.call(prompt, tools)
|
||||
↓
|
||||
LLM 返回工具调用请求
|
||||
↓
|
||||
ReactAgent 内部循环
|
||||
↓
|
||||
ToolCallingManager.executeToolCall("getCurrentDateTime", "{}") // ← 经过管理器
|
||||
↓
|
||||
│
|
||||
├─ 权限检查 ✅
|
||||
├─ 日志记录: "🔧 调用工具: getCurrentDateTime"
|
||||
├─ 性能计时开始 ⏱️
|
||||
│
|
||||
↓
|
||||
查找 MethodToolCallback(根据工具名)
|
||||
↓
|
||||
MethodToolCallback.call("{}")
|
||||
↓
|
||||
反射调用 DateTimeTools.getCurrentDateTime()
|
||||
↓
|
||||
返回结果
|
||||
↓
|
||||
│
|
||||
├─ 性能计时结束: 15ms ⏱️
|
||||
├─ 日志记录: "✅ 工具返回: 2026-05-31..."
|
||||
├─ 监控埋点: toolCallCount++
|
||||
│
|
||||
↓
|
||||
返回给 ReactAgent
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- ✅ **统一管理**:权限、日志、监控集中处理
|
||||
- ✅ **易扩展**:可以添加拦截器、缓存等
|
||||
- ❌ **额外开销**:多一层调用,性能略降
|
||||
- ❌ **复杂度高**:架构更复杂
|
||||
|
||||
---
|
||||
|
||||
## 🤔 为什么你的项目没有经过 ToolCallingManager?
|
||||
|
||||
### **原因分析** ⭐⭐⭐
|
||||
|
||||
#### **1️⃣ 框架版本差异**
|
||||
|
||||
**Spring AI Alibaba Agent Framework** 的不同版本可能有不同的架构:
|
||||
|
||||
| 版本 | 架构 | 说明 |
|
||||
|------|------|------|
|
||||
| **早期版本** | `ReactAgent` → `MethodToolCallback` | 简单直接 |
|
||||
| **企业版/高级版** | `ReactAgent` → `ToolCallingManager` → `MethodToolCallback` | 统一管理 |
|
||||
|
||||
**项目依赖**(`pom.xml:86-88`):
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud.ai</groupId>
|
||||
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**可能性**:项目使用的是**标准版本**,不包含 `ToolCallingManager`。
|
||||
|
||||
---
|
||||
|
||||
#### **2️⃣ 配置未启用**
|
||||
|
||||
某些框架会提供 `ToolCallingManager` 作为**可选组件**:
|
||||
|
||||
```java
|
||||
// 默认配置(直接调用)
|
||||
ReactAgent.builder()
|
||||
.methodTools(tools)
|
||||
.build();
|
||||
|
||||
// 启用 ToolCallingManager(可能需要手动配置)
|
||||
ReactAgent.builder()
|
||||
.methodTools(tools)
|
||||
.toolCallingManager(customManager) // ← 需要手动设置
|
||||
.build();
|
||||
```
|
||||
|
||||
**验证方法**:
|
||||
```java
|
||||
// ChatService.java:134-142
|
||||
ReactAgent agent = ReactAgent.builder()
|
||||
.name("intelligent_assistant")
|
||||
.model(chatModel)
|
||||
.systemPrompt(systemPrompt)
|
||||
.methodTools(buildMethodToolsArray())
|
||||
.tools(getToolCallbacks())
|
||||
.build();
|
||||
|
||||
// 检查是否有 .toolCallingManager() 方法可用
|
||||
// 如果没有,说明框架不支持
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **3️⃣ 设计哲学不同**
|
||||
|
||||
**Spring AI 的设计理念**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Spring AI 核心理念:简单 > 复杂 │
|
||||
│ │
|
||||
│ - ToolCallback 接口已经足够抽象 │
|
||||
│ - 开发者可以自己实现 ToolCallback │
|
||||
│ - 不强制使用统一的管理器 │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**类比**:
|
||||
```
|
||||
Spring AI ToolCallback ≈ Java Interface(接口)
|
||||
- 简单、灵活、可扩展
|
||||
- 开发者可以自由实现
|
||||
|
||||
ToolCallingManager ≈ 中央调度器(可选)
|
||||
- 统一管理、但增加复杂度
|
||||
- 不是所有项目都需要
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 实际调用链路验证
|
||||
|
||||
### **添加调试日志**
|
||||
|
||||
在项目中添加日志验证调用链路:
|
||||
|
||||
```java
|
||||
// 方式1:在工具方法中添加日志
|
||||
@Tool(description = "...")
|
||||
public String getCurrentDateTime() {
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
logger.debug("📍 getCurrentDateTime 调用栈:");
|
||||
for (int i = 0; i < Math.min(10, stackTrace.length); i++) {
|
||||
logger.debug(" {} - {}.{}()", i,
|
||||
stackTrace[i].getClassName(),
|
||||
stackTrace[i].getMethodName());
|
||||
}
|
||||
|
||||
String result = LocalDateTime.now()...toString();
|
||||
logger.debug("🕐 getCurrentDateTime 返回: {}", result);
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
**预期输出**:
|
||||
```log
|
||||
📍 getCurrentDateTime 调用栈:
|
||||
0 - java.lang.Thread.getStackTrace()
|
||||
1 - org.example.agent.tool.DateTimeTools.getCurrentDateTime()
|
||||
2 - jdk.internal.reflect.NativeMethodAccessorImpl.invoke0()
|
||||
3 - jdk.internal.reflect.NativeMethodAccessorImpl.invoke()
|
||||
4 - jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke()
|
||||
5 - java.lang.reflect.Method.invoke()
|
||||
6 - org.springframework.ai.tool.method.MethodToolCallback.call() ← 确认!
|
||||
7 - com.alibaba.cloud.ai.graph.agent.ReactAgent.executeToolCall()
|
||||
8 - com.alibaba.cloud.ai.graph.agent.ReactAgent.call()
|
||||
```
|
||||
|
||||
**结论**:调用链中**没有 ToolCallingManager**,直接是 `MethodToolCallback`。
|
||||
|
||||
---
|
||||
|
||||
### **方式2:使用 Aspect 拦截**
|
||||
|
||||
```java
|
||||
@Aspect
|
||||
@Component
|
||||
public class ToolCallAspect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ToolCallAspect.class);
|
||||
|
||||
@Around("@annotation(org.springframework.ai.tool.annotation.Tool)")
|
||||
public Object logToolCall(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
String toolName = joinPoint.getSignature().getName();
|
||||
Object[] args = joinPoint.getArgs();
|
||||
|
||||
logger.info("🔧 [ToolCall] 开始调用: {}, 参数: {}", toolName, Arrays.toString(args));
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
Object result = joinPoint.proceed();
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
|
||||
logger.info("✅ [ToolCall] 完成调用: {}, 耗时: {}ms", toolName, duration);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ [ToolCall] 调用失败: {}, 错误: {}", toolName, e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 自己实现了 "ToolCallingManager" 的日志记录功能
|
||||
- ✅ 不依赖框架版本
|
||||
- ✅ 可以轻松扩展(权限检查、性能监控)
|
||||
|
||||
---
|
||||
|
||||
## 📊 两种架构的对比
|
||||
|
||||
| 维度 | 直接调用 MethodToolCallback | 通过 ToolCallingManager |
|
||||
|------|---------------------------|------------------------|
|
||||
| **调用链路** | `ReactAgent` → `MethodToolCallback` | `ReactAgent` → `ToolCallingManager` → `MethodToolCallback` |
|
||||
| **性能** | ⭐⭐⭐ 快 | ⭐⭐ 略慢(多一层) |
|
||||
| **复杂度** | ⭐ 简单 | ⭐⭐⭐ 复杂 |
|
||||
| **统一日志** | ❌ 需要在每个工具中实现 | ✅ 集中在 Manager |
|
||||
| **权限控制** | ❌ 需要在每个工具中实现 | ✅ 集中在 Manager |
|
||||
| **性能监控** | ❌ 需要自己实现 | ✅ 集中在 Manager |
|
||||
| **扩展性** | ⭐⭐ 需要修改每个工具 | ⭐⭐⭐ 在 Manager 扩展 |
|
||||
| **适用场景** | 小型项目、简单工具 | 大型项目、企业级应用 |
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践建议
|
||||
|
||||
### **1️⃣ 如果没有 ToolCallingManager,自己实现类似功能** ⭐⭐⭐
|
||||
|
||||
使用 **Spring AOP** 模拟 ToolCallingManager 的功能:
|
||||
|
||||
```java
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ToolCallMonitor {
|
||||
|
||||
private final AtomicLong callCount = new AtomicLong(0);
|
||||
private final Map<String, AtomicLong> toolCallCounts = new ConcurrentHashMap<>();
|
||||
|
||||
@Around("@annotation(tool)")
|
||||
public Object monitorToolCall(ProceedingJoinPoint joinPoint, Tool tool) throws Throwable {
|
||||
String toolName = joinPoint.getSignature().getName();
|
||||
long callId = callCount.incrementAndGet();
|
||||
toolCallCounts.computeIfAbsent(toolName, k -> new AtomicLong(0)).incrementAndGet();
|
||||
|
||||
log.info("🔧 [ToolCall#{}] 开始: {}, 描述: {}", callId, toolName, tool.description());
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
Object result = joinPoint.proceed();
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
|
||||
log.info("✅ [ToolCall#{}] 完成: {}, 耗时: {}ms, 结果长度: {}",
|
||||
callId, toolName, duration,
|
||||
result instanceof String ? ((String) result).length() : "N/A");
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ [ToolCall#{}] 失败: {}, 错误: {}", callId, toolName, e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 60000) // 每分钟输出统计
|
||||
public void printStatistics() {
|
||||
log.info("📊 [ToolCall Statistics] 总调用次数: {}, 各工具调用次数: {}",
|
||||
callCount.get(), toolCallCounts);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**依赖**:
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **2️⃣ 使用装饰器模式包装 ToolCallback** ⭐⭐
|
||||
|
||||
如果想在调用层面控制:
|
||||
|
||||
```java
|
||||
public class ManagedToolCallback implements ToolCallback {
|
||||
|
||||
private final ToolCallback delegate; // 原始的 MethodToolCallback
|
||||
private final ToolCallLogger logger;
|
||||
|
||||
public ManagedToolCallback(ToolCallback delegate) {
|
||||
this.delegate = delegate;
|
||||
this.logger = new ToolCallLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call(String toolInput, ToolContext context) {
|
||||
String toolName = getToolDefinition().name();
|
||||
|
||||
logger.logBefore(toolName, toolInput);
|
||||
|
||||
try {
|
||||
String result = delegate.call(toolInput, context); // ← 调用原始 MethodToolCallback
|
||||
logger.logAfter(toolName, result);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.logError(toolName, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToolDefinition getToolDefinition() {
|
||||
return delegate.getToolDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
ReactAgent.builder()
|
||||
.tools(wrapWithManagement(buildMethodToolsArray())) // ← 包装所有工具
|
||||
.build();
|
||||
|
||||
private ToolCallback[] wrapWithManagement(Object[] methodTools) {
|
||||
// 1. 让 Spring AI 创建 MethodToolCallback
|
||||
// 2. 包装成 ManagedToolCallback
|
||||
// 3. 返回包装后的数组
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **3️⃣ 保持现状,添加必要的日志** ⭐(推荐)
|
||||
|
||||
如果项目规模不大,**保持简单架构**:
|
||||
|
||||
```java
|
||||
// DateTimeTools.java
|
||||
@Tool(description = "...")
|
||||
public String getCurrentDateTime() {
|
||||
logger.debug("🕐 getCurrentDateTime 被调用"); // ← 简单日志
|
||||
String result = LocalDateTime.now()...toString();
|
||||
logger.debug("🕐 getCurrentDateTime 返回: {}", result);
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 简单直接
|
||||
- ✅ 无额外依赖
|
||||
- ✅ 性能最好
|
||||
|
||||
---
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
### **核心答案**
|
||||
|
||||
| 问题 | 答案 |
|
||||
|------|------|
|
||||
| **为什么没有经过 ToolCallingManager?** | 项目使用的 Spring AI 版本采用**简单架构**,直接调用 `MethodToolCallback` |
|
||||
| **MethodToolCallback 是什么?** | 工具调用的**执行器**,通过反射调用 @Tool 方法 |
|
||||
| **ToolCallingManager 是什么?** | 工具调用的**管理器**(某些版本),统一处理日志、权限、监控 |
|
||||
| **两者有什么区别?** | `MethodToolCallback` 是**执行层**,`ToolCallingManager` 是**管理层** |
|
||||
| **是否需要 ToolCallingManager?** | **不一定**,小型项目用 AOP 或简单日志即可 |
|
||||
|
||||
---
|
||||
|
||||
### **推荐方案**
|
||||
|
||||
**短期**(立即实施):
|
||||
1. ✅ 保持现状(`MethodToolCallback` 直接调用)
|
||||
2. ✅ 在工具方法中添加必要的日志(已完成)
|
||||
3. ✅ 使用 debugger 日志记录调用栈(验证架构)
|
||||
|
||||
**中期**(1-2周):
|
||||
1. ⚠️ 添加 Spring AOP 拦截器(模拟 ToolCallingManager)
|
||||
2. ⚠️ 统一日志格式和性能监控
|
||||
|
||||
**长期**(按需):
|
||||
1. 🟢 如果项目规模增大,考虑升级框架版本(如果新版本包含 ToolCallingManager)
|
||||
2. 🟢 或者自己实现装饰器模式的统一管理
|
||||
|
||||
---
|
||||
|
||||
**最终建议**:**不需要担心没有 ToolCallingManager**,这是**正常的架构**,项目当前规模下**直接调用 MethodToolCallback 已经足够**。
|
||||
Reference in New Issue
Block a user