一个只能预测下一个词的模型,是怎么突破物理限制,去调用API、操作电脑的?
前言:一场精心设计的"错觉"
你有没有想过一个问题:大模型本质上是个"词语接龙"游戏。
它被困在服务器里,看不见屏幕,摸不着键盘,唯一的技能就是根据上文预测下一个词的概率。那它怎么就能突然会调用天气API、查询股票数据、甚至操作你的电脑了呢?
这不是魔法,这是一场由开发者精心设计的"认知植入"。
今天我们就来拆解一下,LLM + Tools = Agent 背后的技术逻辑。读完你会明白,那些看似"有自我意识"的AI,本质上还是在做概率计算,只是我们巧妙地给它配了一副"眼镜",让它看到了更广阔的世界。
一、核心痛点:LLM的"信息茧房"
大模型的训练数据是有截止日期的。你问它:
"青岛啤酒今天的收盘价是多少?"
它大概率会懵掉------训练语料里没有今天的实时数据。它甚至不知道"今天"是哪一天。
同样的问题还有:
"上海的天气怎么样?"
"帮我查一下我的邮箱最新邮件"
"把桌面上的这个文件重命名"
这些问题的共同点是:需要访问外部世界的实时信息或执行具体操作。而纯语言模型,做不到。
二、解决方案:工具降维成语言
2.1 认知植入------JSON Schema说明书
解决思路其实很巧妙:我们不需要让LLM学会调用API,我们只需要让它"描述"出想调用什么。
怎么做到?答案就在 System Prompt 里的 tools 配置。
看一段实际代码:
javascript
matlab
const tools = [
{
type: 'function',
function: {
name: 'get_closing_price',
description: '获取股票的收盘价',
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']
}
}
}
];
这就是在做一件事:把复杂的软件接口函数,翻译成LLM能理解的"使用说明书" 。
LLM不懂什么是 API,不懂什么是数据库查询,但它听得懂自然语言描述------"获取股票的收盘价",它明白这句话的意思。
2.2 范式的融合
这个过程可以理解为:将函数降维为语言。
传统软件世界里,函数是代码,是机器执行的指令。但在AI Agent的世界里,工具先被描述成一段文本(JSON Schema),植入到LLM的认知中。
新旧范式在这里融合了:
- 旧范式:开发者写代码,调用函数,执行任务
- 新范式:开发者配置工具描述 → LLM理解描述 → 决策"要用哪个工具" → 生成调用指令 → Runtime执行
三、运行时:LLM的"自我对话"与人的介入
3.1 LLM的推理过程
当用户问:
"青岛啤酒的收盘价是多少?"
LLM的推理引擎会做一系列快速评估:
- 检索训练语料:原始数据里没有实时的股票价格
- 检查"认知植入" :等等,我有工具吗?
- 确认工具存在 :真有,一个叫
get_closing_price的工具 - 生成调用指令:按照 JSON Schema 的格式,生成一段调用代码
然后,关键来了------AI会停止和你的对话,转而开始"自言自语" 。
它不会真的去执行代码,而是严格按说明书格式,生成类似这样的内容:
text
css
tool_calls: {
id: "call_abc123",
function: {
name: "get_closing_price",
arguments: '{"name": "青岛啤酒"}'
}
}
3.2 Runtime的介入------真正的执行者
LLM生成调用指令后,它赌这段代码发出去,会有人响应。
谁响应?开发者编写的Runtime程序。
javascript
php
// 传统软件世界的真实函数
function get_closing_price(name) {
if (name === '青岛啤酒') {
return '67.92';
} else if (name === '贵州茅台') {
return '1488.21';
}
}
// Runtime检测到 tool_calls,执行对应函数
if (toolCall.function.name === 'get_closing_price') {
const args = JSON.parse(toolCall.function.arguments);
const price = get_closing_price(args.name);
// 把结果返回给大模型
messages.push({
role: 'tool',
content: price,
tool_call_id: toolCall.id
});
}
这时候,人/AI都可以调用这个函数,Runtime只负责一件事:执行,拿到结果。
3.3 结果回传与二次推理
拿到结果后,不是直接返回给用户,而是先返回给大模型。
javascript
php
// 把工具执行结果追加到对话上下文
messages.push({
role: 'tool',
content: '67.92',
tool_call_id: 'call_abc123'
});
// 再次调用大模型
const finalRes = await sendMessage(messages);
console.log(finalRes.choices[0].message.content);
// 输出:青岛啤酒的收盘价是67.92元。
大模型拿到工具返回的结果后,再结合用户最初的提问和整个上下文,生成最终的自然语言回复。
这就是完整的 "思考 → 调用 → 执行 → 再思考 → 回答" 闭环。
四、完整调用链路图
text
arduino
用户提问
↓
LLM推理引擎工作
↓
检查"认知植入"中是否有可用工具
↓
生成 tool_calls(格式化的调用指令)
↓
Runtime 接收指令
↓
执行对应的本地/远程函数
↓
将执行结果作为 tool message 返回给 LLM
↓
LLM 根据上下文生成最终回复
↓
返回给用户
这个过程中,LLM 始终在做同一件事:Next Token Prediction。只是我们通过巧妙的 Prompt 设计和 Runtime 编排,让它"看起来"像是在自主决策和调用工具。
五、关键设计细节
5.1 为什么用 JSON Schema?
因为 JSON 是一种结构化的、LLM容易理解和生成的格式。通过 description 字段,我们把函数语义传递给模型;通过 properties 和 required,我们约束了参数的格式和必填项。
5.2 多工具如何关联?
当多个工具调用时,通过 tool_call_id 来关联请求和响应:
javascript
php
messages.push({
role: 'tool',
content: price,
tool_call_id: toolCall.id // 关键:一一对应
});
5.3 LLM的概率性与描述的精确性
LLM的生成是概率性的,所以工具描述必须足够具体和清晰。模糊的描述会导致模型选择错误的工具或生成错误的参数。
比如 description: '获取股票的收盘价' 就比 description: '获取股票信息' 更精准。
六、总结:Agent的本质
回顾整个过程,所谓的 AI Agent,本质上是:
- 认知植入:通过 System Prompt 把工具描述"植入"LLM的认知中
- 意图识别:LLM识别用户意图,决策是否调用工具、调用哪个工具
- 调用生成:LLM生成格式化的 tool_calls 指令
- Runtime执行:外部程序执行实际函数调用
- 结果回传:执行结果返回给LLM
- 最终回复:LLM整合信息,生成自然语言回复
LLM始终只是一个"大脑",而工具是它的"手脚" 。
它依然在玩"词语接龙"的游戏,只是我们给了它一副"工具说明书"的牌,让它能"描述"出调用工具的意图,再靠外部的Runtime去真正执行。
这不是魔法,这是一场精妙的工程编排。
写在最后
下次当你看到AI Agent在自动搜索网页、分析Excel表格、操作电脑时,你可以会心一笑:
"它只是在做词语接龙,只不过接出的词恰好是一段函数调用指令。"
而把这个"恰好"变成"必然"的,正是背后无数开发者精心设计的 Prompt、Schema 和 Runtime 编排。
这大概就是 LLM + Tools = Agent 最迷人的地方------用概率的引擎,驱动确定的世界。