手写一个 Function Call——从 API 调用来

上一篇讲了 Function Call 的概念,这一篇直接上手。我们用 DeepSeek 的 API 完整走一遍:定义函数 → 让 AI 决定调用 → 执行函数 → 把结果喂回给 AI

目标:写一段代码,让 AI 能通过调用天气函数来回答"北京今天热吗?"这类问题。

2.1 准备工作

装好 Node.js(v18+),建个目录初始化项目:

mkdir function-call-demo
cd function-call-demo

npm init -y
npm install openai

DeepSeek 的 API 兼容 OpenAI 的格式,所以我们直接安装 openai 这个 npm 包,改一下 baseURLapiKey 就能用openai

2.2 第一步:定义函数

我们先写一个假的天气函数,模拟返回数据:

// index.js
export function getWeather(city) {
  const data = {
    "北京": { temperature: 28, condition: "晴", humidity: 45 },
    "上海": { temperature: 25, condition: "阴", humidity: 70 },
    "深圳": { temperature: 30, condition: "多云", humidity: 80 },
    "广州": { temperature: 32, condition: "阵雨", humidity: 85 },
  };
  return data[city] || { temperature: 20, condition: "未知", humidity: 50 };
}

真实场景下这里调的是天气 API,现在先用假数据跑通流程。

2.3 第二步:告诉 AI 有哪些函数可用

AI 不知道你有 getWeather 这个函数。你需要用 tools 参数告诉它——函数叫什么、干什么用、参数长什么样。

// index.js
import OpenAI from "openai"; 

export function getWeather(city) {
  const data = {
    "北京": { temperature: 28, condition: "晴", humidity: 45 },
    "上海": { temperature: 25, condition: "阴", humidity: 70 },
    "深圳": { temperature: 30, condition: "多云", humidity: 80 },
    "广州": { temperature: 32, condition: "阵雨", humidity: 85 },
  };
  return data[city] || { temperature: 20, condition: "未知", humidity: 50 };
}

const openai = new OpenAI({ // DeepSeek 的 API 兼容 OpenAI 的格式
  baseURL: "https://api.deepseek.com",
  apiKey: "sk-your-deepseek-key", // need to update your api-key
});

// 描述函数,告诉 AI "你有这些工具可以用"
const tools = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "查询指定城市的实时天气",
      parameters: {
        type: "object",
        properties: {
          city: {
            type: "string",
            description: "城市名称,如 北京、上海",
          },
        },
        required: ["city"],
      },
    },
  },
];

这个结构就是 AI 的"菜单"——它知道有个 get_weather 函数,需要传入 city 参数。

2.4 第三步:发消息,拿指令

把用户问题 + 函数定义一起发给 AI:

const messages = [
  { role: "user", content: "北京今天热吗?" },
];

const response = await openai.chat.completions.create({
  model: "deepseek-v4-flash",
  messages, // 用户问题
  tools, // 函数定义
});

const choice = response.choices[0];

如果 AI 认为需要调用函数,choice.finish_reason 会是 "tool_calls",然后 choice.message.tool_calls 里就是调用指令:

const toolCall = choice.message.tool_calls?.[0];

if (toolCall) {
  console.log("AI 想调用:", toolCall.function.name);
  console.log("参数:", toolCall.function.arguments);
  // 输出: AI 想调用: get_weather
  // 输出: 参数: {"city":"北京"}
}

tool_calls 是一个数组,因为 AI 可能一次想调多个函数(比如同时查北京和上海的天气)。

2.5 第四步:执行函数

拿到函数名和参数后,你自己去执行:

if (toolCall) {
  const { name, arguments: argsStr } = toolCall.function;
  const args = JSON.parse(argsStr);

  let result;
  if (name === "get_weather") {
    result = getWeather(args.city);
  }

  console.log("执行结果:", result);
}

注意:这里是你自己的代码在执行函数。AI 只告诉你要调什么,调不调、怎么调还是你说了算。

提示:这些步骤其实已经有对应的 sdk 实现了,如可以用 Vercel 的 ai sdk。

2.6 第五步:把结果送回 AI

函数执行完了,把结果作为一条新消息发给 AI,让它组织成自然语言回答:

// 把 AI 之前的回复加入对话
messages.push(choice.message);

// 把函数执行结果加入对话
messages.push({
  role: "tool",
  tool_call_id: toolCall.id,
  content: JSON.stringify(result),
});

// 再问一次 AI
const secondResponse = await openai.chat.completions.create({
  model: "deepseek-v4-flash",
  messages,
});

console.log("AI:", secondResponse.choices[0].message.content);
// 输出: 北京今天 28°C,天气晴朗,有点热,建议穿短袖或薄衬衫。

2.7 完整代码

把上面几段拼起来,index.js 完整内容:

import OpenAI from "openai";

function getWeather(city) {
  const data = {
    北京: { temperature: 28, condition: "晴", humidity: 45 },
    上海: { temperature: 25, condition: "阴", humidity: 70 },
    深圳: { temperature: 30, condition: "多云", humidity: 80 },
  };
  return data[city] || { temperature: 20, condition: "未知", humidity: 50 };
}

const openai = new OpenAI({
  baseURL: "https://api.deepseek.com",
  apiKey: "sk-your-deepseek-key",
});

const tools = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "查询指定城市的实时天气",
      parameters: {
        type: "object",
        properties: {
          city: { type: "string", description: "城市名称,如 北京" },
        },
        required: ["city"],
      },
    },
  },
];


async function main() {
  const messages = [{ role: "user", content: "北京今天热吗?" }];

  // 第一轮:AI 决定调用函数
  const response = await openai.chat.completions.create({
    model: "deepseek-v4-flash",
    messages,
    tools,
  });

  const choice = response.choices[0];
  const toolCall = choice.message.tool_calls?.[0];

  if (!toolCall) {
    console.log("AI 不需要调用函数:", choice.message.content);
    return;
  }

  console.log("→ 调用函数:", toolCall.function.name);
  const args = JSON.parse(toolCall.function.arguments);

  // 执行函数
  const result = getWeather(args.city);
  console.log("→ 执行结果:", JSON.stringify(result));

  // 第二轮:把结果送回 AI
  messages.push(choice.message);
  messages.push({
    role: "tool",
    tool_call_id: toolCall.id,
    content: JSON.stringify(result),
  });

  const secondResponse = await openai.chat.completions.create({
    model: "deepseek-v4-flash",
    messages,
  });
  
  // 打印 AI 最终的回答
  console.log("\n🤖 AI:", secondResponse.choices[0].message.content);
}

main().catch(console.error);

2.8 跑起来

node index.js

输出:

→ 调用函数: get_weather
→ 执行结果: {"temperature":28,"condition":"晴","humidity":45}

🤖 AI: 北京今天 28°C,天气晴朗。整体来说不算特别热,但中午会有点晒,建议穿短袖或薄衬衫,注意防晒。

整个过程你写的代码只做了一件事:当中间人。用户 → AI → 你的代码 → 函数 → 你的代码 → AI → 用户。

提示:如果 整个过程 用到 loop 的话,那么这个就是一个会思考和自主干活的 Agent 了。

2.9 如果 AI 想同时调多个函数?

有时 AI 会一次请求调用多个函数。比如用户问"北京和上海的天气怎么样?"。

tool_calls 会是一个数组:

const toolCalls = choice.message.tool_calls;

// 遍历执行每个函数
const results = toolCalls.map((call) => {
  const args = JSON.parse(call.function.arguments);
  if (call.function.name === "get_weather") {
    return getWeather(args.city);
  }
  // TODO ...
});

// 把每个结果都加进对话
for (let i = 0; i < toolCalls.length; i++) {
  messages.push({
    role: "tool",
    tool_call_id: toolCalls[i].id,
    content: JSON.stringify(results[i]),
  });
}

核心逻辑不变:拿到指令 → 自己执行 → 送回结果,只是多了个循环。

2.10 小结

这一篇你做了这些事:

  • tools 参数 告诉 AI 有哪些函数可用
  • AI 返回 tool_calls 告诉你"调这个函数,传这些参数"
  • 自己执行函数,控制权在你手里
  • 把结果作为 role: “tool” 的消息送回 AI
  • AI 结合结果生成自然语言回答

上一篇:[Function Call 是什么?——让 AI 学会"干活"

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐