Function Calling 与工具调用
Function Calling 是大模型从"文本生成器"进化为"任务执行者"的关键机制。通过在请求中声明可用工具,模型能够自主决定何时调用哪个工具、传递什么参数,从而实现搜索、计算、文件操作等超越纯文本生成的能力。
核心概念
什么是 Function Calling
Function Calling(也称 Tool Use)允许开发者在 API 请求中向模型声明一组可用工具。模型在推理过程中,如果判断需要外部信息或操作来完成用户请求,会返回工具调用指令而非直接文本回复。
图表渲染中…
调用流程
| 步骤 | 说明 | 关键点 |
|---|---|---|
| 1. 声明工具 | 在请求 tools 参数中定义可用函数 | JSON Schema 描述参数 |
| 2. 模型决策 | 模型判断是否需要调用工具 | finish_reason: "tool_calls" |
| 3. 执行工具 | 应用层解析并执行工具调用 | 开发者负责实际执行 |
| 4. 回传结果 | 将工具输出以 tool 角色消息回传 | 必须包含 tool_call_id |
| 5. 生成回复 | 模型基于工具结果生成最终回答 | 可能触发多轮调用 |
工具定义规范
JSON Schema 格式
typescript
const tools = [
{
type: 'function',
function: {
name: 'get_current_weather',
description: '获取指定城市的当前天气信息',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: '城市名称,如 "北京" 或 "上海"',
},
unit: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: '温度单位',
},
},
required: ['location'],
},
},
},
{
type: 'function',
function: {
name: 'search_web',
description: '在互联网上搜索信息',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: '搜索关键词',
},
max_results: {
type: 'number',
description: '最大返回结果数',
},
},
required: ['query'],
},
},
},
];工具定义最佳实践
| 原则 | 说明 |
|---|---|
| 名称语义化 | 使用动词+名词格式:get_weather、create_file |
| 描述精确 | 明确说明工具用途、适用场景、限制条件 |
| 参数约束 | 用 enum、pattern、minimum 等约束参数范围 |
| 必填标注 | required 数组标明必需参数 |
| 单一职责 | 每个工具只做一件事,避免"万能工具" |
完整实现
服务端工具调用循环
typescript
// server.ts
import express from 'express';
import axios from 'axios';
import { exec } from 'child_process';
const app = express();
app.use(express.json());
// 工具注册表
const toolHandlers: Record<string, (args: any) => Promise<string>> = {
get_current_weather: async ({ location }) => {
// 实际调用天气 API
const res = await axios.get(`https://api.weather.com/v1/${location}`);
return JSON.stringify(res.data);
},
run_shell_command: async ({ command }) => {
return new Promise((resolve, reject) => {
exec(command, (error, stdout) => {
if (error) reject(error);
else resolve(stdout);
});
});
},
search_web: async ({ query, max_results = 5 }) => {
const res = await axios.get('https://api.search.com/search', {
params: { q: query, limit: max_results },
});
return JSON.stringify(res.data.results);
},
};
app.post('/chat', async (req, res) => {
const { message } = req.body;
const messages = [{ role: 'user', content: message }];
// 工具调用循环(最多 5 轮)
for (let i = 0; i < 5; i++) {
const response = await axios.post(
`${process.env.LLM_BASE_URL}/chat/completions`,
{
model: process.env.LLM_MODEL,
messages,
tools,
tool_choice: 'auto',
},
{
headers: { Authorization: `Bearer ${process.env.LLM_API_KEY}` },
}
);
const choice = response.data.choices[0];
// 如果模型直接回复文本,结束循环
if (choice.finish_reason === 'stop') {
return res.json({ reply: choice.message.content });
}
// 处理工具调用
if (choice.finish_reason === 'tool_calls') {
messages.push(choice.message); // 记录 assistant 的工具调用消息
for (const toolCall of choice.message.tool_calls) {
const { name, arguments: args } = toolCall.function;
const handler = toolHandlers[name];
let result: string;
try {
result = handler
? await handler(JSON.parse(args))
: `Error: Unknown tool "${name}"`;
} catch (e) {
result = `Error: ${e.message}`;
}
// 回传工具结果
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: result,
});
}
}
}
res.json({ reply: '达到最大工具调用轮次限制' });
});
app.listen(3300);tool_choice 参数
| 值 | 行为 | 适用场景 |
|---|---|---|
"auto" | 模型自主决定是否调用工具 | 通用对话(默认) |
"none" | 禁止调用工具 | 纯文本生成 |
"required" | 必须调用至少一个工具 | 强制工具执行 |
{"type":"function","function":{"name":"xxx"}} | 强制调用指定工具 | 确定性流程 |
本地模型工具调用(Ollama)
通过 Ollama 部署的本地模型同样支持 Function Calling:
typescript
// 使用 Ollama 的 OpenAI 兼容接口
const response = await axios.post(
'http://localhost:11434/v1/chat/completions',
{
model: 'qwen3:1.7b',
messages,
tools,
tool_choice: 'auto',
}
);本地 vs 云端对比
| 维度 | 本地模型(Ollama) | 云端 API |
|---|---|---|
| 隐私性 | 数据不出本机 | 数据传输至云端 |
| 延迟 | 取决于硬件 | 网络延迟 + 推理延迟 |
| 工具调用能力 | 小模型可能不稳定 | 大模型更可靠 |
| 成本 | 硬件投入 | 按 Token 计费 |
| 适用场景 | 敏感数据、离线环境 | 生产环境、复杂推理 |
MCP 协议(Model Context Protocol)
MCP 是 Anthropic 提出的开放协议,标准化了 AI 应用与外部工具/数据源的连接方式:
图表渲染中…
MCP vs 传统 Function Calling
| 维度 | Function Calling | MCP |
|---|---|---|
| 定义位置 | 每次 API 请求中 | 独立 Server 进程 |
| 复用性 | 应用内复用 | 跨应用共享 |
| 发现机制 | 手动配置 | 自动发现可用工具 |
| 生态 | 各平台独立 | 开放标准协议 |
| 适用场景 | 应用内工具 | IDE、Agent 框架 |
安全注意事项
| 风险 | 防护措施 |
|---|---|
| 命令注入 | 工具参数严格校验,禁止直接拼接 shell 命令 |
| 权限越界 | 工具白名单 + 最小权限原则 |
| 无限循环 | 设置最大调用轮次(如 5 轮) |
| 数据泄露 | 工具结果脱敏后再回传模型 |
| 成本控制 | 监控 Token 消耗,设置单次会话上限 |
常见问题与陷阱
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 模型不调用工具 | 工具描述不够清晰 | 优化 description,使用 tool_choice: "required" |
| 参数格式错误 | 小模型 JSON 生成能力弱 | 使用更大模型或增加参数校验 + 重试 |
| 工具调用死循环 | 工具结果不满足模型预期 | 设置最大轮次 + 循环检测 |
tool_call_id 缺失 | 未正确回传 | 必须原样返回模型给出的 id |
| 并行工具调用 | 模型一次返回多个 tool_calls | 遍历处理所有调用,全部回传后再生成 |