Files
map-client-vue/docs/BUG_FIX_TOOL_CHAIN.md
2025-10-15 15:07:45 +08:00

233 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🐛 Bug 修复:工具调用链断裂问题
## 问题描述
从日志分析发现AI 第一次成功调用了 `check_login_status` 工具,但第二次调用 AI 时没有传递工具列表,导致 AI 无法继续调用 `publish_content` 工具。
## 问题现象
### ✅ 第一次 AI 调用(成功)
```javascript
🎯 [makeChatRequestStream] 准备请求参数:
工具数量: 3 // ← 有工具
🔧 [makeChatRequestStream] 最终收集到工具调用: 1
工具 [0]: {
name: "mcp__check_login_status", // ← AI 调用了检查登录
arguments: "{}"
}
```
### ❌ 第二次 AI 调用(问题)
```javascript
🎯 [makeChatRequestStream] 准备请求参数:
消息数量: 3
工具数量: 0 // ← 没有工具AI 无法继续调用 publish_content
```
## 根本原因
`executeToolCalls` 方法中,执行完工具后,将结果发送给 AI 时**没有传递 `tools` 参数**
```typescript
// ❌ 错误的代码
await modelServiceManager.sendChatRequestStream(
service.id,
messages,
selectedModel,
onChunk
// 缺少 tools 参数!
)
```
这导致 AI 在第二次调用时不知道有哪些工具可用,所以无法调用 `publish_content`
---
## 修复方案
### 1. 添加 tools 参数
修改 `executeToolCalls` 方法签名,接收 tools 参数:
```typescript
private async executeToolCalls(
conversation: Conversation,
toolCalls: any[],
mcpServerId: string,
model: string | undefined,
onChunk: (chunk: string) => void,
tools?: any[] // ← 新增 tools 参数
): Promise<void>
```
### 2. 传递 tools 给第二次 AI 调用
```typescript
// ✅ 修复后的代码
await modelServiceManager.sendChatRequestStream(
service.id,
messages,
selectedModel,
onChunk,
tools // ← 传递工具列表
)
```
### 3. 在调用处传递 tools
```typescript
if (result.data?.toolCalls && result.data.toolCalls.length > 0 && mcpServerId) {
await this.executeToolCalls(
conversation,
result.data.toolCalls,
mcpServerId,
model,
onChunk,
tools // ← 传递 tools
)
}
```
---
## 完整的工具调用链
修复后的完整流程:
```
用户输入: "发布文章,主题:酸菜鱼"
第一次 AI 调用(带 tools
messages: [
{ role: 'system', content: '你是一个智能助手...' },
{ role: 'user', content: '发布文章,主题:酸菜鱼' }
]
tools: [mcp__check_login_status, mcp__publish_content, ...] ← 有工具
AI 决策: "先检查登录状态"
tool_calls: [{ name: 'mcp__check_login_status', arguments: '{}' }]
执行工具: check_login_status
result: "✅ 登录状态正常"
第二次 AI 调用(带 tools✅ 修复后
messages: [
{ role: 'system', content: '...' },
{ role: 'user', content: '...' },
{ role: 'assistant', tool_calls: [...] },
{ role: 'tool', content: '✅ 登录状态正常' }
]
tools: [mcp__check_login_status, mcp__publish_content, ...] ← 有工具✅
AI 决策: "登录正常,现在发布内容"
tool_calls: [{ name: 'mcp__publish_content', arguments: '{...}' }]
执行工具: publish_content
result: "✅ 发布成功"
第三次 AI 调用(带 tools
AI 生成友好回复:
"✅ 文章已成功发布!链接:..."
```
---
## 测试验证
修复后重新测试:
```
用户: 主题是:如何制作酸菜鱼,帮我生成内容。发布文章。
```
**预期日志**
```javascript
// 第一次 AI 调用
🎯 [makeChatRequestStream] 工具数量: 3
🔧 AI 调用: mcp__check_login_status
// 第二次 AI 调用(修复后)
🔧 [executeToolCalls] 继续传递工具列表: 3 新增日志
🎯 [makeChatRequestStream] 工具数量: 3 修复后有工具了
🔧 AI 调用: mcp__publish_content
// 第三次 AI 调用
🎯 [makeChatRequestStream] 工具数量: 3
AI 返回: "文章已成功发布..."
```
---
## 技术要点
### 为什么需要每次都传递 tools
在 OpenAI Function Calling 机制中:
1. **AI 需要知道有哪些工具可用**
- 每次调用 AI 时都需要传递完整的工具列表
- AI 根据上下文决定是否需要调用工具
2. **支持多轮工具调用**
```
AI → Tool A → AI → Tool B → AI → Tool C → AI
```
每次 AI 调用都需要工具列表,才能决定下一步操作
3. **工具链的完整性**
- 第一步:检查登录状态
- 第二步:发布内容
- 第三步:查询发布结果
- ...
### Cherry Studio 的实现
查看 Cherry Studio 源码可以确认,它也是每次都传递 tools
```typescript
// Cherry Studio 的实现
export async function executeToolCalls(toolCalls: any[], tools: any[]) {
const toolResults = await Promise.all(
toolCalls.map(call => executeTool(call))
)
// 继续调用 AI 时传递 tools
return await sendMessage({
messages: [...history, ...toolResults],
tools // ← 传递 tools
})
}
```
---
## 相关文件
- `/web/src/services/chatService.ts` - 核心修复位置
- Line 945: `executeToolCalls` 方法签名
- Line 1040: 传递 tools 给第二次 AI 调用
- Line 563: 调用 `executeToolCalls` 时传递 tools
---
## 总结
| 项目 | 修复前 | 修复后 |
|------|--------|--------|
| 第一次 AI 调用 | ✅ 有工具3个 | ✅ 有工具3个 |
| 执行工具 | ✅ 成功执行 | ✅ 成功执行 |
| 第二次 AI 调用 | ❌ 无工具0个 | ✅ 有工具3个|
| AI 能否继续调用 | ❌ 不能 | ✅ 能 |
| 工具调用链 | ❌ 断裂 | ✅ 完整 |
**修复状态**: ✅ 已修复
**测试状态**: ⏳ 待测试
**版本**: v1.0.2+ Bug Fix
---
**更新时间**: 2024-01-15