# 🐛 Bug 修复:工具调用链递归处理 ## 问题描述 AI 第二次调用了 `publish_content` 工具,但工具没有被实际执行。 ## 问题现象 ### Client 日志 ```javascript // 第一次 AI 调用 🔧 AI 调用: mcp__check_login_status ✅ 工具执行成功 // 第二次 AI 调用 🔧 AI 调用: mcp__publish_content arguments: {"title":"家庭版酸菜鱼...", "content":"...", ...} // 日志在这里停止,没有执行 publish_content ⏱️ [callModelStream] 真流式总耗时: 40972.00 ms ``` ### Server 日志 ``` [TOOL] check_login_status: Execution completed ← 只有第一个工具 [CONNECTION] CONNECTION CLOSED // publish_content 根本没有被调用! ``` --- ## 根本原因 在 `executeToolCalls` 方法中,代码调用 `sendChatRequestStream` 后就直接结束了,**没有检查 AI 是否再次调用了工具**! ### 问题代码 ```typescript async executeToolCalls(...) { // 1. 执行工具 const toolResults = [...] // 2. 将结果发送给 AI await modelServiceManager.sendChatRequestStream( service.id, messages, selectedModel, onChunk, tools ) // ❌ 直接结束!没有检查 AI 是否再次调用工具 } ``` ### 调用流程 ``` 用户: "发布文章" ↓ AI 第一次调用: check_login_status ↓ executeToolCalls() 执行 check_login_status ↓ 发送结果给 AI ↓ AI 第二次调用: publish_content ← 这里返回了 tool_calls ↓ ❌ executeToolCalls() 结束,没有继续处理! ↓ 工具调用链断裂 ``` --- ## 修复方案 ### 添加递归处理逻辑 ```typescript async executeToolCalls(...) { // 1. 执行工具 const toolResults = [...] // 2. 将结果发送给 AI const result = await modelServiceManager.sendChatRequestStream( service.id, messages, selectedModel, onChunk, tools ) // 3. ✅ 递归处理:如果 AI 再次调用工具,继续执行 if (result.data?.toolCalls && result.data.toolCalls.length > 0) { console.log('🔁 AI 再次调用工具,递归执行') await this.executeToolCalls( conversation, result.data.toolCalls, mcpServerId, model, onChunk, tools ) } else { console.log('✅ 工具调用链完成') } } ``` --- ## 修复后的完整流程 ``` 用户: "发布文章,主题:酸菜鱼" ↓ AI 第一次调用: check_login_status ↓ executeToolCalls() 第一次调用 → 执行 check_login_status → 发送结果给 AI → 检查 AI 响应 → 发现 AI 再次调用了 publish_content ✅ ↓ executeToolCalls() 第二次调用(递归) → 执行 publish_content → 发送结果给 AI → 检查 AI 响应 → 没有更多工具调用 ✅ ↓ 工具调用链完成 ↓ AI 生成最终友好回复 ``` --- ## 支持的调用模式 ### 1. 单次工具调用 ``` AI → Tool → AI (完成) ``` ### 2. 两次工具调用 ``` AI → Tool A → AI → Tool B → AI (完成) ``` ### 3. 多次工具调用链 ``` AI → Tool A → AI → Tool B → AI → Tool C → AI (完成) ``` ### 4. 并行工具调用(待实现) ``` AI → [Tool A, Tool B, Tool C] → AI (完成) ``` --- ## 测试验证 ### 预期日志 ```javascript // 第一次 AI 调用 🔧 [makeChatRequestStream] 最终收集到工具调用: 1 个 工具 [0]: {name: "mcp__check_login_status"} 🔧 [executeToolCalls] 执行 1 个工具调用 ✅ [MCPClientService.callTool] 工具调用成功 🤖 [executeToolCalls] 将工具结果发送给 AI 🔧 [executeToolCalls] 继续传递工具列表: 3 个 // 第二次 AI 调用 🔧 [makeChatRequestStream] 最终收集到工具调用: 1 个 工具 [0]: {name: "mcp__publish_content", arguments: "{...}"} 🔁 [executeToolCalls] AI 再次调用工具,递归执行: 1 个 ← 新增! // executeToolCalls 第二次调用(递归) 🔧 [executeToolCalls] 执行 1 个工具调用 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 🔧 [executeToolCalls] 工具调用详情: - 完整工具名: mcp__publish_content - 提取工具名: publish_content - 参数: {"title":"...", "content":"...", ...} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 🔧 [MCPClientService.callTool] 准备调用工具 - 工具名称: publish_content - 参数: {...} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ✅ [MCPClientService.callTool] 工具调用成功 ← 这次真的执行了! ✅ [executeToolCalls] 工具调用链完成 ``` ### Server 端日志 ``` [TOOL] check_login_status: Execution completed [TOOL] publish_content: Starting execution ← 现在能看到了! [TOOL] publish_content: Content published successfully [TOOL] publish_content: Execution completed ``` --- ## 技术要点 ### 为什么需要递归? 1. **工具调用链是动态的** - AI 可能需要多步完成任务 - 第一步:检查状态 - 第二步:执行操作 - 第三步:验证结果 2. **支持复杂业务流程** ``` 用户: "查询账户余额,如果大于100,就发布一篇文章" AI → check_balance (余额: 150) → AI 判断: 余额够了 → publish_content (发布文章) → AI 返回: "已发布" ``` 3. **符合 Function Calling 规范** - OpenAI API 支持多轮工具调用 - 每次都需要检查是否有新的 tool_calls ### Cherry Studio 的实现 查看 Cherry Studio 源码,它也使用递归或循环处理工具调用链: ```typescript // Cherry Studio 的递归实现 async function handleToolCalls(toolCalls) { const results = await executeTools(toolCalls) const response = await sendMessage({ messages: [...history, ...results], tools }) // 递归处理 if (response.toolCalls) { return await handleToolCalls(response.toolCalls) } return response } ``` --- ## 相关文件 - `/web/src/services/chatService.ts` - Line 1036-1050 - 添加递归处理逻辑 --- ## 总结 | 项目 | 修复前 | 修复后 | |------|--------|--------| | 第一次工具调用 | ✅ 执行 | ✅ 执行 | | 第二次工具调用 | ❌ 不执行 | ✅ 执行 | | 第三次及更多 | ❌ 不执行 | ✅ 递归执行 | | 工具调用链 | ❌ 断裂 | ✅ 完整 | | Server 收到请求 | ❌ 第二次无 | ✅ 全部收到 | **修复状态**: ✅ 已修复 **测试状态**: ⏳ 待测试 **版本**: v1.0.2+ Recursive Fix --- **更新时间**: 2024-01-15