大模型的手与脚: LLM工具调用(Function Calling)原理与项目实战

在与大语言模型(LLM)交互时,我们常常会遇到它的能力边界:它不知道实时信息,也无法直接操作外部系统。为了解决这个问题,“工具调用”(Tool Calling / Function Calling)应运而生。

本文将剥离复杂的框架(暂不涉及 MCP 协议),通过一个实际的 AI Copilot 项目代码,带你搞懂工具调用背后的核心逻辑,并一步步实现让大模型具备查天气、搜文档的能力。


一、 工具调用的核心原理

很多人对 Function Calling 有一个误解,认为“大模型在运行我的代码”。实际上,大模型只负责动脑,不负责动手

一个完整的工具调用流程包含以下四个步骤:

  1. 定义工具 (Define):开发者向大模型描述有哪些工具可用,以及这些工具需要什么参数。

  2. 模型决策 (Decide):大模型分析用户的提问。如果发现需要使用工具,它会暂停生成普通文本,而是输出一个要求调用某工具的 JSON 数据

  3. 本地执行 (Execute):我们的服务端代码拦截到这个请求,在本地执行相应的函数(比如调 API、查数据库),拿到结果。

  4. 回传总结 (Synthesize):我们将执行结果以特定格式再次发给大模型,大模型基于这些真实数据,最终生成给用户的自然语言回答。


二、 实践步骤:从零手搓工具链

在我们的项目中,为了让 AI Copilot 更加智能,我们设计了一个工具注册中心。下面我们将结合代码,看看如何具体落地。

步骤 1:精确描述工具的“说明书” (Schema Definition)

大模型看不懂底层代码,它只能通过你提供的“说明书”来判断何时使用工具。在项目中,我们使用 Zod 来严格定义输入参数的类型和描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/app/api/scrape/tools/lookup-weather.ts
import { z } from "zod";

// 1. 定义 Schema:这是给大模型看的说明书
export const lookupWeatherSchema = z.object({
location: z
.string()
.min(1)
.max(80)
.describe("要查询天气的地点名称,例如 Shanghai、Tokyo、New York"), // 这里的 describe 至关重要,它是大模型的决策依据
});

export type LookupWeatherInput = z.infer<typeof lookupWeatherSchema>;

实践心得describe 中的描述越清晰,大模型的触发就越精准。

步骤 2:编写本地执行逻辑 (Execution Logic)

当大模型说:“我要查 Shanghai 的天气”,我们的服务器就要真正去拉取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// src/app/api/scrape/tools/lookup-weather.ts

export async function lookupWeather({ location }: LookupWeatherInput): Promise<string> {
// 安全过滤:防止注入攻击
const safeLocation = sanitizeLocation(location);
console.log(`[Function Calling] Weather lookup: ${safeLocation}`);

// 1. 获取地理编码
const geoRes = await fetchWithRetry(
`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(safeLocation)}&count=1&language=zh&format=json`,
{ method: "GET" }
);

if (!geoRes.ok) return `查询 ${safeLocation} 天气失败`;
const geoData = await geoRes.json();
const place = geoData.results?.[0];

if (!place) return `未找到地点:${safeLocation}`;

// 2. 获取实时天气
const weatherRes = await fetchWithRetry(
`https://api.open-meteo.com/v1/forecast?latitude=${place.latitude}&longitude=${place.longitude}&current=temperature_2m,weather_code&timezone=auto`,
{ method: "GET" }
);

const weatherData = await weatherRes.json();

// 3. 将复杂 JSON 转化为大模型易读的纯文本描述
return [
`地点:${place.name}`,
`时间:${weatherData.current.time}`,
`气温:${weatherData.current.temperature_2m}°C`,
].join("\n");
}

步骤 3:高阶玩法——将 RAG 封装为工具

工具调用不仅能查天气,还能结合向量数据库(如 Pinecone)用来检索私有知识库。在项目中,我们将对 React 源码的搜索也封装成了一个 Tool:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// src/app/api/scrape/tools/search-react-docs.ts

export const searchReactDocsSchema = z.object({
searchQuery: z.string().describe("用来去向量数据库检索的精准搜索关键词"),
});

export async function searchReactDocs({ searchQuery }: SearchReactDocsInput): Promise<string> {
// 1. 调用 Embedding 模型,将大模型生成的关键词转化为向量
const embedRes = await fetchWithRetry("https://api.gptsapi.net/v1/embeddings", { /*...*/ });
const queryVector = embedRes.data[0].embedding;

// 2. 在 Pinecone 数据库中进行相似度检索
const pinecone = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
const index = pinecone.index(process.env.PINECONE_INDEX_NAME);

const queryResponse = await index.query({
vector: queryVector,
topK: 3,
includeMetadata: true,
});

// 3. 拼接检索到的背景知识返回给大模型
const contextDocs = queryResponse.matches
.map((match) => match.metadata?.text || "")
.join("\n\n---\n\n");

return contextDocs || "未检索到相关内容";
}

这种模式非常强大:大模型不再是被动接受我们塞给它的文档,而是主动决定“我现在需要搜什么关键词”,极大提升了检索的灵活性。

步骤 4:统一管理与注册 (Tool Registry)

为了让系统易于扩展,我们在 index.ts 中将所有的工具集中注册。这种配置化的写法也是未来对接 MCP (Model Context Protocol) 协议的基础:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/app/api/scrape/tools/index.ts
import { lookupWeather, lookupWeatherSchema } from "./lookup-weather";
import { searchReactDocs, searchReactDocsSchema } from "./search-react-docs";

export const SCRAPE_TOOLS = {
search_react_docs: {
description: "当用户提问关于 React 源码、API 用法或具体技术细节时,必须调用此工具去知识库中检索背景信息。",
schema: searchReactDocsSchema,
execute: searchReactDocs,
},
lookup_weather: {
description: "当用户提问某个地点的实时天气时,调用此工具查询该地点当前天气。",
schema: lookupWeatherSchema,
execute: lookupWeather,
},
} as const;

结语

通过 Function Calling,大模型完成了从“懂很多道理的旁观者”到“能帮你干活的数字员工”的蜕变。理解了 定义 -> 决策 -> 本地执行 -> 总结 这个闭环,你就可以将企业内部的任何 API(如 ERP 系统、工单系统)接入到 LLM 中。

在后续的文章中,我们将进一步探讨如何引入 MCP(Model Context Protocol)协议,让这套工具调用的流程更加标准化、跨平台化。敬请期待!

源码已托管至🫱 GitHub,期待你的 Star🌟。


大模型的手与脚: LLM工具调用(Function Calling)原理与项目实战
http://example.com/2026/05/02/大模型的手与脚-LLM工具调用-Function-Calling-原理与项目实战/
作者
Lingkai Shi
发布于
2026年5月2日
许可协议