工具呼叫 (也稱為函式呼叫) 是一種結構化方式,可讓大型語言模型向呼叫它的應用程式提出要求。您可以定義要提供給模型的工具,模型會視需要向應用程式提出工具要求,以便執行您提供的提示。
工具呼叫的用途通常可歸納為幾個主題:
讓大型語言模型存取未經訓練的資訊
- 經常變更的資訊,例如股票價格或目前天氣。
- 應用程式網域專屬資訊,例如產品資訊或使用者個人資料。
請注意,這與檢索增強生成 (RAG) 重疊,後者也是讓 LLM 將事實資訊整合至生成內容的一種方式。RAG 是較為複雜的解決方案,最適合用於大量資訊,或是與提示最相關的資訊模糊不清的情況。另一方面,如果擷取 LLM 所需資訊的操作是簡單的函式呼叫或資料庫查詢,則呼叫工具會更適合。
在 LLM 工作流程中引入一定程度的確定性
- 執行 LLM 無法可靠完成的計算。
- 在特定情況下強制 LLM 產生逐字文字,例如回覆有關應用程式服務條款的問題。
在 LLM 啟動時執行動作
- 透過 LLM 技術輔助的家用助理開啟和關閉燈具
- 在 LLM 輔助的餐廳服務代理中預訂座位
事前準備
如果您想執行本頁的程式碼範例,請先完成「開始使用」指南中的步驟。所有範例都假設您已設定專案,並安裝 Genkit 依附元件。
本頁面將介紹 Genkit 模型抽象化的其中一個進階功能,因此在深入探討之前,請先熟悉「使用 AI 模型產生內容」頁面上的內容。您也應熟悉 Genkit 的系統,以便定義輸入和輸出結構定義,相關資訊請參閱「流程」頁面。
工具呼叫總覽
大致來說,以下是工具呼叫與大型語言模型的互動方式:
- 呼叫應用程式會向 LLM 提示要求,並在提示中加入 LLM 可用來產生回應的工具清單。
- 大型語言模型會生成完整回覆,或以特定格式生成工具呼叫要求。
- 如果呼叫端收到完整回應,系統就會執行要求,並結束互動;但如果呼叫端收到工具呼叫,則會執行適當的邏輯,並向 LLM 傳送新要求,其中包含原始提示或其變化版本,以及工具呼叫的結果。
- LLM 會處理新的提示,如步驟 2 所述。
如要使用這項功能,您必須符合下列幾項條件:
- 模型必須經過訓練,才能在需要完成提示時提出工具要求。大多數透過網頁 API 提供的大型模型 (例如 Gemini 和 Claude) 都能執行這項操作,但較小且較專門的模型通常無法執行。如果您嘗試為不支援的模型提供工具,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
參數。撰寫工具定義時,請特別注意這些參數的用詞和描述性。這些資訊對於大型語言模型有效運用可用工具至關重要。
使用工具
在提示中加入定義的工具,即可產生內容。
產生
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 Dev UI 與工具互動。這通常是建議做法。不過,在某些情況下,您必須根據使用者要求動態定義工具。
您可以使用 ai.dynamicTool
函式動態定義工具。這個函式與 ai.defineTool
方法非常相似。不過,Genkit 執行階段不會追蹤動態工具,因此您無法使用 Genkit 開發人員 UI 與其互動。您必須透過參照將其傳遞至 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,直到每個工具呼叫都已解析為止。您可以在需要時暫停執行作業,例如:
- 向使用者提出問題或顯示 UI。
- 向使用者確認可能有風險的動作。
- 要求針對某項動作取得額外核准。
中斷是一種特殊工具,可暫停迴圈並將控制權交還給程式碼,以便您處理更進階的情境。請參閱中斷指南,瞭解如何使用中斷。
明確處理工具呼叫
如要完全控管這個工具呼叫迴圈 (例如套用更複雜的邏輯),請將 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;
}