工具调用(也称为“函数调用”)是一种结构化方式,可让 LLM 向调用它的应用发出请求。您可以定义要向模型提供的工具,模型会根据需要向您的应用发出工具请求,以执行您给出的提示。
工具调用的用例通常可归纳为以下几个主题:
向 LLM 授予其未经训练的信息访问权限
- 经常变化的信息,例如股票价格或当前天气。
- 与您的应用网域相关的信息,例如商品信息或用户个人资料。
请注意,这与检索增强生成 (RAG) 有一定重叠,RAG 也是让 LLM 将事实信息整合到其生成内容中的一种方式。RAG 是一种更复杂的解决方案,最适合以下情况:您拥有大量信息,或者与提示最相关的信息不明确。另一方面,如果检索 LLM 所需的信息是简单的函数调用或数据库查询,则工具调用更合适。
在 LLM 工作流中引入一定程度的确定性
- 执行 LLM 本身无法可靠完成的计算。
- 在某些情况下强制 LLM 生成逐字文本,例如在回答有关应用服务条款的问题时。
在 LLM 发起时执行操作
- 在依托 LLM 的家庭助理中开关灯
- 在 LLM 支持的餐厅代理中预订餐桌
准备工作
如果您想运行本页面中的代码示例,请先完成使用入门指南中的步骤。所有示例都假定您已设置项目并安装了 Genkit 依赖项。
本页面介绍了 Genkit 模型抽象的其中一项高级功能,因此在深入研究之前,您应该先熟悉使用 AI 模型生成内容页面上的内容。您还应熟悉 Genkit 用于定义输入和输出架构的系统,Flow 页面对此进行了讨论。
工具调用概览
概括来说,与 LLM 的典型工具调用交互如下所示:
- 调用方应用会通过请求向 LLM 发出提示,并在提示中添加 LLM 可用于生成回答的工具列表。
- LLM 会生成完整的回答,或以特定格式生成工具调用请求。
- 如果调用方收到完整的回答,则请求会得到满足,并且互动会结束;但如果调用方收到工具调用,则会执行适当的逻辑,并向 LLM 发送包含原始提示或其变体以及工具调用结果的新请求。
- LLM 会处理第 2 步中的新提示。
为此,必须满足以下几项要求:
- 必须对模型进行训练,以便在需要完成提示时发出工具请求。通过 Gemini 等网络 API 提供的大多数较大的模型都可以做到这一点,但较小且更专业的模型通常无法做到这一点。如果您尝试向不支持工具调用的模型提供工具,Genkit 会抛出错误。
- 调用方应用必须以模型所需的格式向模型提供工具定义。
- 调用应用必须提示模型以应用所需的格式生成工具调用请求。
使用 Genkit 进行工具调用
Genkit 为支持该功能的模型提供了一个工具调用单一接口。每个模型插件都确保满足上述最后两项条件,并且 genkit.Generate()
函数会自动执行前面所述的工具调用循环。
模型支持
工具调用支持取决于模型、模型 API 和 Genkit 插件。请参阅相关文档,确定是否可能支持工具调用。此外:
- 如果您尝试向不支持工具调用的模型提供工具,Genkit 会抛出错误。
- 如果插件导出了模型引用,则
ModelInfo.Supports.Tools
属性将指示该模型是否支持工具调用功能。
定义工具
使用 genkit.DefineTool()
函数编写工具定义:
import (
"context"
"log"
"github.com/firebase/genkit/go/ai"
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/googlegenai"
)
func main() {
ctx := context.Background()
g, err := genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{}),
genkit.WithDefaultModel("googleai/gemini-2.0-flash"),
)
if err != nil {
log.Fatal(err)
}
getWeatherTool := genkit.DefineTool(
g, "getWeather", "Gets the current weather in a given location",
func(ctx *ai.ToolContext, location string) (string, error) {
// Here, we would typically make an API call or database query. For this
// example, we just return a fixed value.
return fmt.Sprintf("The current weather in %s is 63°F and sunny.", location);
})
}
此处的语法看起来与 genkit.DefineFlow()
语法类似;不过,您必须编写说明。请特别注意说明的措辞和表达清晰度,因为 LLM 是否会正确调用该工具在很大程度上取决于说明的质量。
使用工具
在提示中添加已定义的工具以生成内容。
生成
resp, err := genkit.Generate(ctx, g,
ai.WithPrompt("What is the weather in San Francisco?"),
ai.WithTools(getWeatherTool),
)
DefinePrompt
weatherPrompt, err := genkit.DefinePrompt(g, "weatherPrompt",
ai.WithPrompt("What is the weather in {{location}}?"),
ai.WithTools(getWeatherTool),
)
if err != nil {
log.Fatal(err)
}
resp, err := weatherPrompt.Execute(ctx,
with.Input(map[string]any{"location": "San Francisco"}),
)
提示文件
---
system: "Answer questions using the tools you have."
tools: [getWeather]
input:
schema:
location: string
---
What is the weather in {{location}}?
然后,您可以在代码中执行提示,如下所示:
// Assuming prompt file named weatherPrompt.prompt exists in ./prompts dir.
weatherPrompt := genkit.LookupPrompt("weatherPrompt")
if weatherPrompt == nil {
log.Fatal("no prompt named 'weatherPrompt' found")
}
resp, err := weatherPrompt.Execute(ctx,
ai.WithInput(map[string]any{"location": "San Francisco"}),
)
如果 LLM 需要使用 getWeather
工具来回答提示,Genkit 会自动处理该工具调用。
显式处理工具调用
如果您想完全控制此工具调用循环(例如,应用更复杂的逻辑),请将 WithReturnToolRequests()
选项设置为 true
。现在,您有责任确保所有工具请求都得到满足:
getWeatherTool := genkit.DefineTool(
g, "getWeather", "Gets the current weather in a given location",
func(ctx *ai.ToolContext, location string) (string, error) {
// Tool implementation...
})
resp, err := genkit.Generate(ctx, g,
ai.WithPrompt("What is the weather in San Francisco?"),
ai.WithTools(getWeatherTool),
ai.WithReturnToolRequests(true),
)
if err != nil {
log.Fatal(err)
}
parts := []*Part{}
for _, req := range resp.ToolRequests() {
tool := genkit.LookupTool(g, req.Name)
if tool == nil {
log.Fatalf("tool %q not found", req.Name)
}
output, err := tool.RunRaw(ctx, req.Input)
if err != nil {
log.Fatalf("tool %q execution failed: %v", err)
}
parts = append(parts,
ai.NewToolResponsePart(&ai.ToolResponse{
Name: req.Name,
Ref: req.Ref,
Output: output,
}))
}
resp, err = genkit.Generate(ctx, g,
ai.WithMessages(resp.History()..., NewMessage(ai.RoleTool, nil, parts...)),
)
if err != nil {
log.Fatal(err)
}