工具呼叫

工具呼叫 (也稱為函式呼叫) 是一種結構化方式,可讓大型語言模型向呼叫它的應用程式提出要求。您可以定義要提供給模型的工具,模型會視需要向應用程式提出工具要求,以便執行您提供的提示。

工具呼叫的用途通常可歸納為幾個主題:

讓大型語言模型存取未經訓練的資訊

  • 經常變更的資訊,例如股票價格或目前天氣。
  • 應用程式網域專屬資訊,例如產品資訊或使用者個人資料。

請注意,這與檢索增強生成 (RAG) 重疊,後者也是讓 LLM 將事實資訊整合至生成內容的一種方式。RAG 是較為複雜的解決方案,最適合用於大量資訊,或是與提示最相關的資訊模糊不清的情況。另一方面,如果函式呼叫或資料庫查詢是擷取 LLM 所需資訊的必要條件,那麼呼叫工具會更適合。

在 LLM 工作流程中引入一定程度的確定性

  • 執行 LLM 無法可靠完成的計算。
  • 在特定情況下,強制 LLM 產生逐字文字,例如回覆有關應用程式服務條款的問題。

在 LLM 啟動時執行動作

  • 透過 LLM 技術輔助的家用助理開啟和關閉燈具
  • 在 LLM 輔助的餐廳服務代理中預訂座位

事前準備

如果您想執行本頁的程式碼範例,請先完成「開始使用」指南中的步驟。所有範例都假設您已設定專案,並安裝 Genkit 依附元件。

本頁面將介紹 Genkit 模型抽象化的其中一個進階功能,因此在深入探討之前,請先熟悉「使用 AI 模型產生內容」頁面上的內容。您也應熟悉 Genkit 的系統,以便定義輸入和輸出結構定義,相關資訊請參閱「流程」頁面。

工具呼叫總覽

大致來說,與大型語言模型互動的典型工具呼叫如下:

  1. 呼叫應用程式會向 LLM 提示要求,並在提示中加入 LLM 可用來產生回應的工具清單。
  2. 大型語言模型會產生完整回覆,或以特定格式產生工具呼叫要求。
  3. 如果呼叫端收到完整回應,系統就會執行要求,並結束互動;但如果呼叫端收到工具呼叫,則會執行適當的邏輯,並向 LLM 傳送新要求,其中包含原始提示或某些變化版本,以及工具呼叫的結果。
  4. 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)
}