AI Tool Use 深度解析:大模型是如何"突破物理限制"调用外部工具的?
你有没有想过,豆包能自动搜索网页、Claude 能分析 Excel 表格、AI Agent 能操作电脑......这些"超能力"到底是怎么实现的?那个被困在显卡里的"缸中大脑",是怎么突破物理限制去调用 API、读数据库、操作物理世界的?
前言
很多人对 AI 的工具调用能力感到神奇,甚至以为大模型真的"会用工具"。
但作为开发者,我们需要清醒地认识到:这是一个精心设计的错觉。
用户以为是 LLM 完成了所有事情,其实不是。那个在显卡里疯狂跑的大模型,本质上还是一个词语接龙游戏 ------Next Token Prediction。它看不见屏幕,摸不到键盘,是一个被困在服务器里的缸中大脑。
那么问题来了:一个只能预测下一个 token 的概率模型,到底是怎么调用 API、读取数据库、操作物理世界的?
答案是:AI + Tools = Agent ,而连接它们的桥梁,就是 Tool Use。
今天我们就来深度拆解 Tool Use 背后的三个核心阶段。
一、认知植入:把工具"降维"成语言
大模型不懂 API,但它听得懂语言
大模型不懂什么是天气 API,也不懂数据库查询。但它有一个超强的能力:理解自然语言。
所以我们要做的第一件事,就是把复杂的软件工具降维成语言。
具体怎么做?用 JSON Schema。
JSON Schema = 工具的"使用说明书"
在 System Prompt 里配置工具的时候,我们其实是在做一件非常精妙的事------认知植入。
我们把一个复杂的软件接口函数(比如 get_close_price),翻译成大模型能理解的"使用说明书":
json
{
"type": "function",
"function": {
"name": "get_closing_price",
"description": "获取指定股票的收盘价",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "股票名称"
}
},
"required": ["name"]
}
}
}
这里有几个关键点:
name:函数名,精确的标识符description:自然语言描述,告诉 LLM 这个工具是干什么的parameters:参数的类型约束和描述
⚠️ 重要提示 :LLM 的输出本质上是概率随机的,所以工具描述必须具体、清晰、无歧义。描述越模糊,LLM 调错工具的概率越高。
在这个阶段,一个复杂的软件工具被降维 成了纯粹的文本描述。大模型调用的不是真正的软件接口,而是一段文本描述。
二、意图识别:LLM 的"决策时刻"
当用户提问,推理引擎开始工作
用户问:"上海的天气怎么样?"
LLM 的推理引擎开始了一系列快速评估:
- 首先,在原始训练语料中搜索------问天气?回答不了,没有实时数据。
- 然后 ,回头看认知植入的工具列表------哎?有个
get_weather工具! - 决策:我需要调用这个工具。
接下来,AI 会停止和你的对话,转而开始"自言自语"。它严格按照我们定义的那套"说明书",生成一段结构化的调用代码:
json
{
"tool_calls": [
{
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"上海\"}"
}
}
]
}
关键认知:LLM 不能执行,开发者可以
注意,LLM 不能执行 任何东西。它生成的 tool_calls 只是一段自然语言描述的调用意图。
它依赖的是强大的模式识别和逻辑推理能力------它"赌"这段代码发出后,会有人(或程序)响应。
这就像一个被关在房间里的翻译官,它能写出完美的调用指令,但自己没法按下"执行"按钮。
三、Runtime 介入:真正的执行者
传统软件世界登场
LLM 生成了 tool_calls 之后,真正的执行者登场了------Runtime(Node.js / Python / Java 等传统运行时)。
Runtime 的工作流程:
用户提问 → LLM 决策(生成 tool_calls)→ Runtime 执行工具 → 拿到结果 → 返回给 LLM → LLM 生成最终回答
注意这里有个关键细节:工具的执行结果不是直接返回给用户,而是返回给大模型。
大模型拿到工具返回的结果后,结合最初的用户问题和完整的对话上下文,生成最终的自然语言回答。
完整流程图
csharp
用户:"青岛啤酒的收盘价是多少?"
↓
[LLM 第一次调用]
模型返回 tool_calls: get_closing_price("青岛啤酒")
↓
[Runtime 执行]
调用 get_closing_price() → 返回 "67.92"
↓
[结果回传给 LLM]
将工具结果加入对话历史
↓
[LLM 第二次调用]
模型返回:"青岛啤酒的收盘价是 67.92 元。"
四、代码实战:手写一个 Tool Use Demo
理论讲完了,来看代码。以下是一个完整的 Tool Use 实现:
1. 项目初始化
bash
mkdir tool-use-demo && cd tool-use-demo
pnpm init
pnpm add openai dotenv
2. 配置环境变量 .env
env
MIMO_API_KEY=your_api_key
MIMO_API_BASE_URL=https://api.xiaomimimo.com/v1
3. 核心代码 index.mjs
javascript
import OpenAI from 'openai';
import dotenv from 'dotenv';
dotenv.config();
// ========== 1. 初始化客户端(缸中大脑)==========
const client = new OpenAI({
apiKey: process.env.MIMO_API_KEY,
baseURL: process.env.MIMO_API_BASE_URL,
});
// ========== 2. 认知植入:工具配置(JSON Schema)==========
// 将函数降维为语言,新旧范式的融合
const tools = [
{
type: 'function',
function: {
name: 'get_closing_price',
description: '获取指定股票的收盘价', // LLM 决策的依据
parameters: {
type: 'object',
properties: {
name: {
type: 'string',
description: '股票名称',
},
},
required: ['name'],
},
},
},
{
type: 'function',
function: {
name: 'get_weather',
description: '获取指定城市的天气',
parameters: {
type: 'object',
properties: {
city: {
type: 'string',
description: '城市名称',
},
},
required: ['city'],
},
},
},
];
// ========== 3. 传统软件世界:真正的工具函数 ==========
function get_closing_price(name) {
if (name === '青岛啤酒') {
return '67.92';
} else if (name === '北京啤酒') {
return '1488.21';
} else {
return '未找到该股票';
}
}
// ========== 4. 发送消息 ==========
async function sendMessage(messages) {
const res = await client.chat.chat.completions.create({
model: 'mimo-v2.5-pro',
messages: messages,
tools: tools,
tool_choice: 'auto', // 让模型自动决定是否调用工具
});
return res;
}
// ========== 5. 主流程:完整的 Tool Use 循环 ==========
async function main() {
// 用户提问
let messages = [
{ role: 'user', content: '青岛啤酒的收盘价是多少?' }
];
// --- 第一次调用:LLM 决策 ---
const response = await sendMessage(messages);
const message = response.choices[0].message;
console.log('模型返回:', JSON.stringify(message));
// --- 检查是否有 tool_calls ---
if (message.tool_calls) {
messages.push(message); // 将 LLM 的决策加入对话历史
const toolCall = message.tool_calls[0];
// --- Runtime 介入:执行工具 ---
if (toolCall.function.name === 'get_closing_price') {
const args = JSON.parse(toolCall.function.arguments);
const price = get_closing_price(args.name);
console.log('收盘价结果:', price);
// 将工具结果以 tool 角色加入对话历史
messages.push({
role: 'tool',
content: price,
tool_call_id: toolCall.id,
});
// --- 第二次调用:LLM 根据结果生成最终回答 ---
const finalRes = await sendMessage(messages);
console.log('最终回答:', finalRes.choices[0].message.content);
}
}
}
main();
运行结果
swift
模型返回: {"role":"assistant","content":"","tool_calls":[{"id":"call_xxx","type":"function","function":{"name":"get_closing_price","arguments":"{\"name\":\"青岛啤酒\"}"}}]}
收盘价结果: 67.92
更新完整对话上下文: [...]
最终回答: 青岛啤酒的收盘价是 67.92 元。
五、核心要点总结
| 阶段 | 谁在干活 | 做什么 |
|---|---|---|
| 认知植入 | 开发者 | 用 JSON Schema 把工具描述成 LLM 能理解的语言 |
| 意图识别 | LLM | 分析用户意图,决定调用哪个工具,生成 tool_calls |
| Runtime 介入 | 传统运行时 | 执行真正的函数调用,拿到结果回传给 LLM |
三个容易踩的坑
- 工具描述不够清晰 → LLM 可能调错工具或生成错误参数
- 没有维护对话历史 → 第二次调用时 LLM 丢失上下文
- 直接把结果返回给用户 → 应该先返回给 LLM 让它组织语言
结语
Tool Use 的本质,是把大模型的"语言智能"和传统软件的"执行能力"连接起来。
LLM 负责"想"(决策),Runtime 负责"做"(执行)。两者配合,就从一个"缸中大脑"变成了能操作真实世界的 Agent。
这不是魔法,是工程。
而理解了这个原理,你就能构建更复杂的 Agent 系统------比如让 AI 自动搜索网页、操作数据库、控制智能家居......可能性是无限的。
🔗 相关阅读
如果这篇文章对你有帮助,欢迎点赞 👍 收藏 ⭐ 关注,后续会持续更新 AI Agent 系列内容!