LLM Tool Use 架构解密:从 Function Calling 到 Agent 工具链
大型语言模型能对话、能写作,但这只是表层能力。真正让它变成"数字员工"的,是**工具调用(Tool Use / Function Calling)**——让 LLM 能够搜索网页、执行代码、读写文件、操作数据库。本质上,工具调用是给 LLM 安装"手脚",让它从被动回答者变为主动执行者。
大型语言模型能对话、能写作,但这只是表层能力。真正让它变成"数字员工"的,是工具调用(Tool Use / Function Calling)——让 LLM 能够搜索网页、执行代码、读写文件、操作数据库。本质上,工具调用是给 LLM 安装"手脚",让它从被动回答者变为主动执行者。
2025-2026年,Tool Use 已经成为所有主流 LLM API 的标配能力。GPT-4o、Claude 4、Gemini 2.5、DeepSeek-V3 都有成熟的函数调用实现。但背后的架构设计,门道很多。
为什么需要 Tool Use:超越纯文本生成
纯文本生成的 LLM 有三个根本局限:
1. 知识有截止日期。模型的权重是冻结的,无法获取实时信息。
2. 没有执行能力。它能描述"如何安装 Node.js",但无法真的执行安装。
3. 缺乏状态修改。它可以写 SQL,但无法真的操作数据库。
Tool Use 本质上是把 LLM 的"推理能力"和外部系统的"执行能力"解耦再缝合:你用 LLM 做大脑,用工具做手脚,各司其职。
Function Calling 的三种实现模式
模式一:JSON Schema 强制约束(ChatGPT Style)
这是 OpenAI 在 2023 年首创的方案,也是目前最广泛采用的模式:
`json
// 用户请求
{
"messages": [
{"role": "user", "content": "北京现在多少度?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"}
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
`
模型输出会停止生成文本,改为输出一个特殊的 tool_calls 块:
`json
{
"finish_reason": "tool_calls",
"model": "gpt-4o",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"北京\"}"
}
}
]
}
`
前端拿到这个调用,执行工具,把结果塞回 messages 继续对话:
`json
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temperature\": \"28°C\", \"condition\": \"晴\"}"
}
`
这是同步两步式:生成 → 停止 → 执行 → 继续。优点是实现简单、可靠;缺点是交互延迟高,多工具场景下要串行等待。
模式二:Stream with Tool Delta(Anthropic Style)
Claude 3.5 和 GPT-4o 的 streaming 模式做了优化:在生成文本 token 的同时,实时流式返回 tool_use 事件,不需要等文本生成完才开始执行。
`typescript
// 伪代码:流式处理 tool_call
for await (const event of model.stream()) {
if (event.type === 'content_block_start') {
// 开始输出文本
}
if (event.type === 'content_block_delta') {
process.stdout.write(event.delta.text);
}
if (event.type === 'tool_use') {
// 工具调用请求,可以提前执行
const result = await executeTool(event.tool);
messages.push({ role: 'tool', tool_call_id: event.id, content: result });
}
}
`
这种模式的优势是工具调用可以被提前发现和处理,适合需要并行执行多个工具的场景。
模式三:Think Clause + Action Separation(DeepSeek Style)
DeepSeek-V3 采用了另一种思路:让模型在生成 tool_call 之前,先输出一段推理过程(Think),再决定调用什么工具。
`json
{
"arguments": {
"thought": "用户想知道北京天气。我需要先调用天气API...",
"city": "北京"
}
}
`
这个设计的好处是:模型在调用工具前会先自我反思,减少"工具调用幻觉"(明明不需要调用工具却调用了,或者调用了错误工具)。
多工具编排:从单步到 Agent 循环
单次工具调用场景有限,真正体现 LLM Agent 能力的是多步工具链编排。
典型场景:用户说"帮我查一下特斯拉股价,然后告诉我如果我买100股,现在要花多少钱"
这需要三步:
1. 查股价(web search / financial API)
2. 计算 100 * 股价
3. 用计算结果回复用户
实现这个逻辑,有三种编排架构:
架构 A:ReAct(Reasoning + Acting)
`
Thought: 用户想知道买100股特斯拉花多少钱。我需要先查当前股价。
Action: get_stock_price
Observation: 特斯拉股价 $248.50
Thought: 现在计算 100 * 248.50
Action: calculate
Observation: 结果是 $24,850
Thought: 计算完成,可以回复用户了
Final Answer: 购买100股特斯拉需要约 $24,850
`
ReAct 的优点是推理过程透明、可审计;缺点是 token 开销大(每步都要输出 Thought/Observation)。
架构 B:Plan-and-Execute
不再一步一步来,而是先整体规划,再批量执行:
`
Plan:
1. get_stock_price("TSLA")
2. calculate.multiply(result_1, 100)
3. format_currency(result_2)
Execute Phase (parallel):
→ get_stock_price → $248.50
→ calculate.multiply($248.50, 100) → $24,850
→ format_currency → "$24,850.00"
`
Plan-and-Execute 的优势是可以并行执行独立工具,大幅降低延迟;挑战在于规划阶段就要把整个流程想清楚,一旦中间步骤失败,恢复成本高。
架构 C:Hugging Agent(Tool Loop)
这是开源社区常用的方案:一个 while 循环,持续调用工具直到 LLM 决定"任务完成"。
`python
def agent_loop(messages, max_iterations=10):
for i in range(max_iterations):
response = llm.chat(messages)
if not response.tool_calls:
# 没有更多工具调用,任务完成
return response.content
for tool_call in response.tool_calls:
result = execute_tool(tool_call)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
return "达到最大迭代次数"
`
这个模式简单直接,但容易陷入循环调用(LLM 反复调用同一工具而不收敛)。
工具调用的核心挑战:幻觉与可靠性
工具调用看似美好,工程落地却有三大深坑:
坑 1:参数幻觉(Parameter Hallucination)
LLM 有时会输出符合 JSON Schema 但值完全错误的参数:
`json
// 模型生成了一个"合理但错误的"参数
{ "city": "北京", "date": "2024-15-99" } // 根本不存在的日期
`
解决思路:
- **输出 Schema 验证层**:用 JSON Schema 库做运行时校验
- **Few-shot Examples**:在 system prompt 里给模型看正确和错误的参数示例
- **结构化输出(Structured Output)**:用严格的 BNF 语法替代 JSON Schema
坑 2:工具描述依赖(Tool Description Dependency)
模型调用什么工具、传什么参数,完全依赖工具描述(description)的质量。一个模糊的描述会导致模型选错工具。
`python
# 模糊描述 → 模型可能选错
{
"name": "search",
"description": "搜索信息"
}
# 精准描述 → 模型准确调用
{
"name": "search",
"description": "在 Google 上搜索实时网页信息,返回标题+摘要+链接。不适合精确数值查询。"
}
`
坑 3:状态一致性与工具幂等性
多步骤 Agent 中,前面工具的执行结果会影响后续决策。如果某个工具的状态在执行过程中发生变化(比如余额减少了),模型需要正确感知这个变化。
2026 年的 Tool Use 新趋势
MCP(Model Context Protocol):统一工具接口
Anthropic 在 2024 年末提出的 MCP,正在成为行业事实标准。它的核心思路是:把工具的发现、调用、结果返回都标准化,让同一个 Agent 可以无缝切换不同的工具后端。
`
Host (LLM) ←→ MCP Client ←→ MCP Server (工具实现)
`
不再需要为每个 LLM Provider 写不同的工具适配器,MCP Server 是一次编写、各处运行。
工具调用的安全边界
当 LLM 可以执行任意工具时,安全问题就变得尖锐:
- 工具是否有权限检查?
- 工具执行失败时如何优雅降级?
- 恶意 prompt 能否诱导 LLM 执行危险操作?
行业正在形成工具权限分级模型:读取类工具(GET 请求、只读 API)开放,写入类工具(POST、DELETE、文件写入)需要二次确认。
写在最后
Function Calling 不是新概念,但 2025-2026 年的工程实践让它真正成熟。从 JSON Schema 约束到流式工具 delta,从单步调用到多工具编排,每一步都是对 LLM"推理"与"执行"边界的新探索。
工具调用的终极目标,是让 LLM 成为真正的 Agent——不只是给建议,而是真的能把事情做成。随着 MCP 等标准的普及,这个目标正在加速实现。
但别忘了:工具是放大器,用得好威力无边,用得差则是给自己埋雷。