工具调用背后:LLM 如何突破“缸中大脑”,操控真实世界?

工具调用背后:LLM 如何突破"缸中大脑",操控真实世界?

你以为 AI 真的会"用"工具?其实它只是在玩一场精密的词语接龙。

你有没有想过:当你在豆包里问"今天上海天气怎么样",它真的能联网搜索;当你在 Claude 里上传 Excel,它能帮你算出季度销售额;甚至 AI Agent 可以直接操作你的 Mac Mini,帮你发邮件、改文档......这些看起来就像 AI 拥有了"自我意识",能主动操控外部世界。

但作为一名开发者,我们心里清楚:那不过是在显卡里疯狂跑着的 LLM,本质上还是一个 "下一个词预测"(Next Token Prediction) 的概率模型。它看不见屏幕,摸不到键盘,更不懂什么是 API 或数据库------它是一个被囚禁在服务器里的"缸中大脑"。

那么问题来了:一个只会"词语接龙"的数学函数,到底是怎么突破物理限制,去调用外部 API、读取数据库、甚至操作物理世界的工具的?

答案就是 Tool Use(工具调用)。今天,我就带你从原理到代码,彻底拆解这个"精心设计的错觉"。


1. 认知植入:把函数"翻译"成大模型能听懂的语言

大模型很聪明,但它只懂两样东西:Token(词元)概率 。它不知道什么是 get_weather API,也不知道什么是 SQL 查询。它的世界只有文本------输入一段话,输出下一段最可能的话。

那么,我们怎么让它"知道"有工具可以用呢?在 System Prompt(系统提示词)里配置工具 ,我们做了一件极其精妙的事------认知植入

所谓认知植入,就是把一个复杂的软件接口(比如 get_closing_price 函数),降维 成一个纯粹的文本描述 。这个描述就是 JSON Schema。大模型通过阅读这段"说明书",知道在什么情况下该"假装"调用什么函数。

来看我们代码中的工具定义,我画了一张图来展示这个**"降维"过程**:

graph LR subgraph A [传统软件世界] F[函数: get_closing_price] --> P[复杂入参/校验/数据库查询] end subgraph B [大模型认知世界] J[JSON Schema 说明书] --> D[description: 获取股票收盘价] J --> N[name: string] J --> R[required: name] end A -- "认知植入 (降维为语言)" --> B B -- "大模型通过模式匹配理解" --> C[LLM 大脑] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#bbf,stroke:#333,stroke-width:2px

具体到代码,是这样实现的:

javascript 复制代码
const tools = [
  {
    "type": "function",
    "function": {
      "name": "get_closing_price",
      // 【专业解释】description 是决策的关键!大模型不看你函数体,只看这段文本。
      // 它通过语义相似度,将用户问题中的"收盘价"映射到这个工具。
      "description": "获取指定股票的收盘价",
      "parameters": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "股票名称" // 约束:参数必须是字符串
          }
        },
        "required": ["name"] // 约束:这个参数必须填,否则无效
      }
    }
  }
];

为什么要强调 JSON Schema 的约束力? 因为大模型的本质是"概率随机"。如果没有 requiredtype 的约束,它可能把"青岛啤酒的收盘价"解析成 { name: 123 } 或遗漏参数。而 Schema 就像"正则表达式"一样,强行规范了 LLM 的输出格式,确保 Runtime(运行环境)能正确解析。


2. 意图识别:大模型的"自言自语"与"暂停"

当用户问:"青岛啤酒的收盘价是多少?"时,推理引擎开始工作。它不会直接说"我不知道",而是做一次快速的"模式识别":

  1. 原始语料库里有实时股价吗?没有。
  2. 认知植入里有工具吗?有,get_closing_price 匹配"收盘价"。
  3. 好,那我"暂停"回答,转为生成调用指令。

此时,模型返回的并不是普通文本,而是一个特殊的 message 对象。我们来看代码中的捕获与打印:

javascript 复制代码
const response = await sendMessage(messages);
const message = response.choices[0].message;
console.log('模型返回message 对象', JSON.stringify(message))

打印出来的内容如下(我加了详细注释):

json 复制代码
{
  "role": "assistant",
  "content": null,  
  // 【新手必看】content 为 null,说明模型拒答了。它把话语权交给了工具。
  "tool_calls": [
    {
      "id": "call_abc123", 
      // 【深度理解】这个 id 极其重要!它就像快递单号。
      // 如果同时调用了 get_weather 和 get_stock,模型需要靠 id 来区分哪个结果对应哪个请求。
      "type": "function",
      "function": {
        "name": "get_closing_price",
        "arguments": "{\"name\":\"青岛啤酒\"}" 
        // LLM 严格遵循了 JSON Schema,把自然语言"青岛啤酒"提取成了参数。
      }
    }
  ]
}

这里的本质是什么? 大模型根本不懂什么叫 API 调用。它只是在做"词语接龙"时,发现按照我们给的 tools 格式续写下去,概率最高。它赌这段 JSON 发出去后,会有人(开发者)响应它。


3. Runtime 介入:传统软件的"手"接住了"大脑"的指令

大模型不能自己执行函数,它只是个"缸中大脑",没有"手脚"。那谁来执行?开发者搭建的 Runtime(运行环境)------也就是我们的 Node.js 代码。

javascript 复制代码
// 这是传统软件世界里最普通的函数,没有任何 AI 属性
function get_closing_price(name) {
  if (name === '青岛啤酒') {
    return '67.92'; // 实际业务中这里可能是数据库查询或 API 请求
  } else if (name === '贵州茅台') {
    return '1488.21';
  } else {
    return '未找到该股票';
  }
}

我们的 Runtime 负责"接住"大模型抛出的指令:

javascript 复制代码
if(response.choices[0].message.tool_calls) {
  // 取出第一个工具调用(多工具并发时需遍历)
  const toolCall = response.choices[0].message.tool_calls[0];
  if (toolCall.function.name === 'get_closing_price') {
    // 【核心步骤】解析参数,LLM 给的 arguments 是字符串,必须 parse 成对象
    const args = JSON.parse(toolCall.function.arguments);
    // 执行真正的业务逻辑
    const price = get_closing_price(args.name);
    console.log('股票收盘价', price); 
    // ...
  }
}

Runtime 只管一件事:执行,拿到结果,然后"还"给大模型。 这里有个非常关键的直觉:结果不是直接 console.log 给用户看的,而是要喂回给大模型


4. 上下文缝合:Tool 消息的注入与模型的"最终回答"

为了让大模型基于工具结果输出最终答案,我们必须把工具的执行结果 作为上下文的一部分,再次发送给大模型。这就是 Tool Use 的闭环

流程如下(序列图最能体现这种交互):

sequenceDiagram participant U as 用户 participant R as Runtime (你的代码) participant L as 大模型 (缸中大脑) U->>R: 1. 提问: 青岛啤酒收盘价? R->>L: 2. 发送消息 + 工具说明书 L->>R: 3. 返回 tool_calls (intent) R->>R: 4. 本地执行 get_closing_price("青岛啤酒") -> 返回 67.92 R->>L: 5. 发送第二轮请求 (追加 tool 消息, 携带 67.92) L->>R: 6. 基于结果生成最终回复 R->>U: 7. 输出: 青岛啤酒收盘价是 67.92 元

对应到代码,就是关键的**"消息追加"**环节:

第一步:将大模型的"调用意图"加入历史

javascript 复制代码
messages.push({
  role: message.role,         // 'assistant'
  content: message.content,   // 此时还是 null
  tool_calls: message.tool_calls // 保存调用记录,让模型记得"我刚才要求调工具了"
})

第二步:将"工具执行结果"加入历史(最难理解的点)

javascript 复制代码
messages.push({
  role: 'tool', // 【专业解释】这是一个特殊角色,代表"外部工具的声音"
  content: price, // 把计算出来的 67.92 塞进去
  tool_call_id: toolCall.id // 【深度思考】如果上下文里挂起了 3 个工具请求,这个 id 就是"凭证",告诉模型这 67.92 是给 call_abc123 这个请求的回复。
})
console.log('更新后完整对话上下文:', messages);
// 此时 messages 里有 3 条记录:User, Assistant(tool_calls), Tool(result)

第三步:第二次调用大模型,生成最终口语化回复

javascript 复制代码
const finalRes = await sendMessage(messages);
console.log('最终模型返回message 对象', 
  finalRes.choices[0].message.content // 终于输出:'青岛啤酒的收盘价是 67.92 元'
)

为什么不能跳过第二步直接返回给用户? 因为大模型需要"润色"。如果直接显示 67.92,用户会觉得很生硬。而让大模型看一眼结果,它就能生成"根据最新数据,青岛啤酒今日收盘价为 67.92 元,较昨日..."这样富有逻辑的句子。


5. 深度思考:这是"智能"还是"精心设计的错觉"?

回到 readme.md 的核心论点:

"那个在显卡里疯狂跑的 LLM,本质上还是个词语接龙的游戏。它是被困在服务器里的缸中大脑。"

整个过程,大模型从未"知道"什么叫 API,什么叫数据库,它只是在做三件事,而这三件事全是数学概率,没有半点"意识":

  1. 模式匹配:根据你的问题向量和工具描述向量,计算出余弦相似度,选最匹配的那个。
  2. 参数填充:把问题里的实体(如"青岛啤酒")按照 Schema 的格式填入 JSON 槽位。
  3. 上下文续写 :看到 role: 'tool' 里的 67.92,预测出下一个最可能的回复词元是"青岛啤酒的收盘价是..."。

我们开发者通过 认知植入 + 意图识别 + Runtime 执行 + 上下文缝合,硬生生造出了一个"AI 会调用工具"的幻觉。这就像魔术师的手法------你以为 AI 有手,其实那双手是我们(Runtime)。


6. 从"单一调用"到"通用 Agent"的延伸

你可能会问:这只是查个股价,如果我要让 AI 操作电脑、读写文件、发邮件呢?

原理完全相同,甚至你可以让模型在一次响应中返回多个 tool_calls

javascript 复制代码
// 伪代码:如果用户问"整理文件夹并发送给老板"
tool_calls: [
  { function: { name: 'list_files', arguments: {...} } },
  { function: { name: 'compress_folder', arguments: {...} } },
  { function: { name: 'send_email', arguments: {...} } }
]

Runtime 顺序或并发执行完这些函数,把一堆结果塞回给大模型,大模型就能总结出:"已为您整理 10 个文件,压缩包已发送至 boss@email.com"。这就是 AutoGPT 和各类 AI Agent 的底层雏形。


结语:每一个"智能"背后,都是代码和数据的精心编排

下次你看到 AI 帮你查天气、读 Excel、操作电脑时,不妨会心一笑------它不过是个被精心引导的"词语接龙大师"。而让它看起来像拥有超能力的,是我们这些开发者设计的一套精密的"提词术"(Prompt Engineering)和"执行引擎"(Runtime)。

工具调用,不仅仅是调用 API,它是认知植入、意图识别、Runtime 编排三位一体的系统工程。

希望这篇文章能帮你拨开迷雾,看清 Agent 的本质。当你再写 tools 配置时,记得你不仅在写 JSON,你在重塑大模型的认知边界

Happy coding,我们下篇见! 🚀

相关推荐
姗姗来迟了1 小时前
用React Hook封装AI对话状态
人工智能
Goodbye1 小时前
从 Token 到 Embedding:LLM 核心基础深度解析
javascript·人工智能
阿瑞IT1 小时前
AI Agent 在甘特计划变更场景中的动态响应工程实践
人工智能
Goodbye1 小时前
从函数到智能:LLM Tool Use 深度解析
javascript·人工智能
半个落月1 小时前
大模型到底是怎么“调用工具”的?从一个 Node.js Demo 看懂 Tool Use
javascript·人工智能
烬羽2 小时前
中英文 token 数量差一倍?两段 JS 代码搞懂 LLM 底层是怎么"读"文字的
javascript·程序员·架构
MingXin2 小时前
Claude Code 对接 DeepSeek 完整使用教程(2026 最新版)
人工智能
山河木马2 小时前
矩阵专题1-怎么创建模型矩阵(uModelMatrix)
javascript·webgl·计算机图形学
Oo9202 小时前
LLM 分词与嵌入:从文本到向量,模型如何"读懂"你的输入
人工智能