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

6.6 KiB
Raw Blame History

🐛 Bug 修复:工具调用链递归处理

问题描述

AI 第二次调用了 publish_content 工具,但工具没有被实际执行。

问题现象

Client 日志

// 第一次 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 是否再次调用了工具

问题代码

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() 结束,没有继续处理!
  ↓
工具调用链断裂

修复方案

添加递归处理逻辑

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 (完成)

测试验证

预期日志

// 第一次 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 源码,它也使用递归或循环处理工具调用链:

// 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