工具调用(也称为“函数调用”)是一种结构化方式,可让 LLM 向调用它的应用发出请求。您可以定义要向模型提供的工具,模型会根据需要向您的应用发出工具请求,以执行您给出的提示。
工具调用的用例通常可归纳为以下几个主题:
向 LLM 授予其未经训练的信息访问权限
- 经常变化的信息,例如股票价格或当前天气。
- 与您的应用网域相关的信息,例如商品信息或用户个人资料。
请注意,这与检索增强生成 (RAG) 有一定重叠,RAG 也是让 LLM 将事实信息整合到其生成内容中的一种方式。RAG 是一种更复杂的解决方案,最适合以下情况:您拥有大量信息,或者与提示最相关的信息不明确。另一方面,如果检索 LLM 所需的信息是简单的函数调用或数据库查询,则工具调用更合适。
在 LLM 工作流中引入一定程度的确定性
- 执行 LLM 本身无法可靠完成的计算。
- 在某些情况下强制 LLM 生成逐字文本,例如在回答有关应用服务条款的问题时。
在 LLM 发起时执行操作
- 在依托 LLM 的家庭助理中开关灯
- 在 LLM 支持的餐厅代理中预订餐桌
准备工作
如果您想运行本页面中的代码示例,请先完成使用入门指南中的步骤。所有示例都假定您已设置项目并安装了 Genkit 依赖项。
本页面介绍了 Genkit 模型抽象的其中一项高级功能,因此在深入研究之前,您应该先熟悉使用 AI 模型生成内容页面上的内容。您还应熟悉 Genkit 用于定义输入和输出架构的系统,Flow 页面对此进行了讨论。
工具调用概览
概括来说,与 LLM 的典型工具调用交互如下所示:
- 调用方应用会通过请求向 LLM 发出提示,并在提示中添加 LLM 可用于生成回答的工具列表。
- LLM 会生成完整的回答,或以特定格式生成工具调用请求。
- 如果调用方收到完整的回答,系统会执行适当的逻辑,并向 LLM 发送包含原始提示或其变体以及工具调用结果的新请求。如果调用方收到工具调用,则会执行适当的逻辑,并向 LLM 发送包含原始提示或其变体以及工具调用结果的新请求。
- LLM 会处理第 2 步中的新提示。
为此,必须满足以下几项要求:
- 必须对模型进行训练,以便在需要完成提示时发出工具请求。通过 Gemini 和 Claude 等网络 API 提供的大多数较大的模型都可以做到这一点,但较小且更专业的模型通常无法做到这一点。如果您尝试向不支持工具调用的模型提供工具,Genkit 会抛出错误。
- 调用方应用必须以模型所需的格式向模型提供工具定义。
- 调用应用必须提示模型以应用所需的格式生成工具调用请求。
使用 Genkit 进行工具调用
Genkit 为支持该功能的模型提供了一个工具调用单一接口。每个模型插件都确保满足上述最后两项条件,并且 Genkit 实例的 generate()
函数会自动执行前面所述的工具调用循环。
模型支持
工具调用支持取决于模型、模型 API 和 Genkit 插件。请参阅相关文档,确定是否可能支持工具调用。此外:
- 如果您尝试向不支持工具调用的模型提供工具,Genkit 会抛出错误。
- 如果插件导出了模型引用,则
info.supports.tools
属性将指示该模型是否支持工具调用功能。
定义工具
使用 Genkit 实例的 defineTool()
函数编写工具定义:
import { genkit, z } from 'genkit';
import { googleAI } from '@genkitai/google-ai';
const ai = genkit({
plugins: [googleAI()],
model: googleAI.model('gemini-2.0-flash'),
});
const getWeather = ai.defineTool(
{
name: 'getWeather',
description: 'Gets the current weather in a given location',
inputSchema: z.object({
location: z.string().describe('The location to get the current weather for')
}),
outputSchema: z.string(),
},
async (input) => {
// Here, we would typically make an API call or database query. For this
// example, we just return a fixed value.
return `The current weather in ${input.location} is 63°F and sunny.`;
}
);
此处的语法看起来与 defineFlow()
语法类似;不过,name
、description
和 inputSchema
参数是必需的。编写工具定义时,请特别注意这些参数的措辞和描述性。它们对于 LLM 有效利用可用工具至关重要。
使用工具
在提示中添加已定义的工具以生成内容。
生成
const response = await ai.generate({
prompt: 'What is the weather in Baltimore?',
tools: [getWeather],
});
definePrompt
const weatherPrompt = ai.definePrompt(
{
name: 'weatherPrompt',
tools: [getWeather],
},
'What is the weather in {{location}}?'
);
const response = await weatherPrompt({ location: 'Baltimore' });
提示文件
---
system: "Answer questions using the tools you have."
tools: [getWeather]
input:
schema:
location: string
---
What is the weather in {{location}}?
然后,您可以在代码中执行提示,如下所示:
// assuming prompt file is named weatherPrompt.prompt
const weatherPrompt = ai.prompt('weatherPrompt');
const response = await weatherPrompt({ location: 'Baltimore' });
聊天
const chat = ai.chat({
system: 'Answer questions using the tools you have.',
tools: [getWeather],
});
const response = await chat.send('What is the weather in Baltimore?');
// Or, specify tools that are message-specific
const response = await chat.send({
prompt: 'What is the weather in Baltimore?',
tools: [getWeather],
});
如果 LLM 需要使用 getWeather
工具来回答提示,Genkit 会自动处理该工具调用。
在运行时动态定义工具
与 Genkit 中的大多数内容一样,您需要在应用初始化期间预定义工具。您必须执行此操作,才能从 Genkit 开发者界面与工具进行交互。这通常是推荐的方法。不过,在某些情况下,必须根据用户请求动态定义该工具。
您可以使用 ai.dynamicTool
函数动态定义工具。此函数与 ai.defineTool
方法非常相似。不过,Genkit 运行时不会跟踪动态工具,因此您无法使用 Genkit 开发者界面与其交互。而是必须通过引用将其传递给 ai.generate
调用(对于常规工具,您还可以使用字符串工具名称)。
import { genkit, z } from "genkit";
import { googleAI } from "@genkit-ai/googleai";
const ai = genkit({
plugins: [googleAI()],
model: googleAI.model("gemini-2.0-flash"),
});
const weatherFlow = ai.defineFlow("weatherFlow", async () => {
const getWeather = ai.dynamicTool(
{
name: "getWeather",
description: "Gets the current weather in a given location",
inputSchema: z.object({
location: z
.string()
.describe("The location to get the current weather for"),
}),
outputSchema: z.string(),
},
async (input) => {
return `The current weather in ${input.location} is 63°F and sunny.`;
}
);
const { text } = await ai.generate({
prompt: "What is the weather in Baltimore?",
tools: [getWeather],
});
return text;
});
在定义动态工具时,如需指定输入和输出架构,您可以使用 Zod(如上例所示),也可以传入手动构建的 JSON 架构。
const getWeather = ai.dynamicTool(
{
name: "getWeather",
description: "Gets the current weather in a given location",
inputJsonSchema: myInputJsonSchema,
outputJsonSchema: myOutputJsonSchema,
},
async (input) => { /* ... */ }
);
动态工具不需要实现函数。如果您不传入函数,该工具将像中断一样运行,您可以手动处理工具调用:
const getWeather = ai.dynamicTool(
{
name: "getWeather",
description: "Gets the current weather in a given location",
inputJsonSchema: myInputJsonSchema,
outputJsonSchema: myOutputJsonSchema,
}
);
使用中断暂停工具循环
默认情况下,Genkit 会反复调用 LLM,直到所有工具调用都得到解析为止。您可以在需要时有条件地暂停执行,例如:
- 向用户提问或显示界面。
- 与用户确认可能存在风险的操作。
- 请求对某项操作进行非正式审批。
中断是一种特殊工具,可暂停循环并将控制权返回给您的代码,以便您处理更高级的场景。请参阅中断指南,了解如何使用中断。
显式处理工具调用
如果您想完全控制此工具调用循环(例如,应用更复杂的逻辑),请将 returnToolRequests
参数设置为 true
。现在,您有责任确保所有工具请求都得到满足:
const getWeather = ai.defineTool(
{
// ... tool definition ...
},
async ({ location }) => {
// ... tool implementation ...
},
);
const generateOptions: GenerateOptions = {
prompt: "What's the weather like in Baltimore?",
tools: [getWeather],
returnToolRequests: true,
};
let llmResponse;
while (true) {
llmResponse = await ai.generate(generateOptions);
const toolRequests = llmResponse.toolRequests;
if (toolRequests.length < 1) {
break;
}
const toolResponses: ToolResponsePart[] = await Promise.all(
toolRequests.map(async (part) => {
switch (part.toolRequest.name) {
case 'specialTool':
return {
toolResponse: {
name: part.toolRequest.name,
ref: part.toolRequest.ref,
output: await getWeather(part.toolRequest.input),
},
};
default:
throw Error('Tool not found');
}
})
);
generateOptions.messages = llmResponse.messages;
generateOptions.prompt = toolResponses;
}