纯对话拿不到实时数据、容易编造;Function Calling 让模型输出工具使用意图,由本地执行后回传结果。一文按「为什么 → 是什么 → Demo」讲清。
Function Calling 解决的是:大模型如何安全、可控地用到你的数据和接口。本文按三个问题展开——为什么需要它、它到底是什么、怎么写出一个能跑的 Demo。
1. 为什么需要 Function Calling
大模型只会根据训练数据和当前对话生成文本。它不知道你数据库里今天的订单数,调不到内网 HTTP 接口,训练数据也有时效 cutoff。
只靠纯对话,常见两条路都不理想:
| 做法 | 问题 |
|---|---|
| 让模型直接答 | 实时数据只能猜,容易编造 |
| 把所有数据塞进 prompt | 贵、易超长、难维护 |
Function Calling 提供第三条路:用户用自然语言提问 → 模型判断要不要用工具、填什么参数 → 你的程序去查库 / 调 API → 结果回传给模型 → 模型组织成最终回答。
典型用途:查天气/股价、查订单、创建工单、多步串联(先查用户 ID,再查订单)。副作用操作(写库、发邮件)仍要在本地做鉴权——模型只负责「建议动作」,不负责「执行动作」。
2. Function Calling 是什么
Function Calling 是 OpenAI 系 API 的惯用叫法;更准确说是 Tool Use(工具使用)。名字容易让人以为「模型在执行你的函数」——实际上模型只生成一段结构化文本(tool_calls),真正 call 的是你的代码。
一次完整的 Tool Use 长这样:
用户提问
→ 请求里带上 tools(能用什么)
→ 模型返回 tool_calls(建议用哪个、参数是什么)
→ 本地执行对应函数
→ 追加 role: tool 消息(执行结果)
→ 再请求,模型输出自然语言回答

需要调多个工具时,中间「执行 → 回传」循环,直到模型不再返回 tool_calls。
几个 API 字段:
- tools — 工具清单,每项含
name、description、parameters(JSON Schema)。这里的 function 不会自动绑定你项目里的 TS 函数,靠同名约定自己对接。 - tool_calls — 模型输出的使用意图,例如:
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"北京\"}"
}
}
arguments 是字符串,需 JSON.parse,且可能非法或语义错误,必须本地校验。
- role: tool — 把执行结果塞回
messages,tool_call_id要对上上面的id:
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "晴,28°C"
}
和普通 API 的区别:调哪个接口、参数从哪来,由模型从用户话里预测;但 HTTP 请求、数据库读写仍发生在你的服务。它是「自然语言 → 结构化动作」的中间层,不是 REST 的替代品。
3. Demo:用 DeepSeek 查天气
下面用 DeepSeek 把第 2 节串起来。DeepSeek 兼容 OpenAI Chat Completions,安装 openai 包、改 baseURL 即可;OpenAI、通义等接口写法相同。
流程对应关系:
| 步骤 | 代码里 |
|---|---|
| 声明 tools | tools 数组 + get_weather schema |
| 第一次请求,拿 tool_calls | client.chat.completions.create → first |
| 本地执行 | getWeather(city) |
| 回传结果 | messages.push({ role: "tool", ... }) |
| 第二次请求,拿人话 | second |
import "dotenv/config";
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: "https://api.deepseek.com",
});
const MODEL = process.env.DEEPSEEK_MODEL ?? "deepseek-chat";
const tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [
{
type: "function",
function: {
name: "get_weather",
description: "查询指定城市的当前天气",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "城市名称,如:北京、上海" },
},
required: ["city"],
},
},
},
];
function getWeather(city: string): string {
const data: Record<string, string> = {
北京: "晴,28°C",
上海: "多云,32°C",
};
return data[city] ?? "暂无该城市数据";
}
async function main() {
const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
{ role: "user", content: "北京今天天气怎么样?" },
];
const first = await client.chat.completions.create({
model: MODEL,
messages,
tools,
});
const assistantMsg = first.choices[0].message;
messages.push(assistantMsg);
const toolCall = assistantMsg.tool_calls?.[0];
if (toolCall?.type !== "function") {
console.log("助手:", assistantMsg.content);
return;
}
const { city } = JSON.parse(toolCall.function.arguments) as { city: string };
const weather = getWeather(city);
console.log(`执行: get_weather("${city}") → ${weather}`);
messages.push({
role: "tool",
tool_call_id: toolCall.id,
content: weather,
});
const second = await client.chat.completions.create({
model: MODEL,
messages,
});
console.log("助手:", second.choices[0].message.content);
}
main().catch(console.error);
环境变量:DEEPSEEK_API_KEY、DEEPSEEK_MODEL(可选,默认 deepseek-chat)。运行:
$ tsx src/index.ts
执行: get_weather("北京") → 晴,28°C
助手: 北京今天天气晴朗,气温为 **28°C**,祝您生活愉快!
写 Demo 时注意: description 写清何时用;参数名语义化;副作用要鉴权;模型不调工具就改描述,JSON 解析失败就 try/catch,仍返回 tool_calls 就继续循环。
4. 小结
Function Calling 解决的是大模型够不着真实世界的问题;本质是 tools 声明 → tool_calls 意图 → 本地执行 → role: tool 回灌 → 再请求。Demo 跑通后,换任何兼容 OpenAI 格式的接口都能复用同一套写法。