應用程式 AI 功能的核心是生成式模型要求,但您很少會直接擷取使用者輸入內容、將其傳遞給模型,然後將模型輸出內容顯示給使用者。通常,模型呼叫必須搭配預處理和後處理步驟。例如:
- 擷取要透過模型呼叫傳送的背景資訊。
- 擷取使用者目前工作階段的記錄,例如在即時通訊應用程式中。
- 使用一個模型,以適合傳遞至其他模型的方式重新格式化使用者輸入內容。
- 在向使用者顯示模型輸出內容前,先評估內容的「安全性」。
- 結合多個模型的輸出內容。
這個工作流程的每個步驟都必須相互配合,才能順利完成任何與 AI 相關的任務。
在 Genkit 中,您可以使用名為「流程」的結構來表示這種緊密連結的邏輯。流程的編寫方式與函式相同,使用一般 Go 程式碼,但會加入額外功能,方便開發 AI 功能:
- 型別安全性:輸入和輸出結構定義,可提供靜態和執行階段型別檢查。
- 與開發人員 UI 整合:使用開發人員 UI 時,偵錯流程與應用程式程式碼無關。您可以在開發人員 UI 中執行流程,並查看流程的每個步驟追蹤記錄。
- 簡化部署程序:使用任何可代管網頁應用程式的平台,直接將流程部署為 Web API 端點。
Genkit 的流程輕巧且不顯眼,也不會強制要求應用程式符合任何特定抽象化。所有流程邏輯都是以標準 Go 編寫,且流程內的程式碼不需要是流程感知。
定義及呼叫流程
最簡單的說法是,流程只是包裝函式。以下範例會包裝呼叫 GenerateData()
的函式:
menuSuggestionFlow := genkit.DefineFlow(g, "menuSuggestionFlow",
func(ctx context.Context, theme string) (string, error) {
resp, err := genkit.GenerateData(ctx, g,
ai.WithPrompt("Invent a menu item for a %s themed restaurant.", theme),
)
if err != nil {
return "", err
}
return resp.Text(), nil
})
只要將 genkit.Generate()
呼叫包裝成這樣,就能新增一些功能:這樣一來,您就能從 Genkit CLI 和開發人員 UI 執行流程,而且是 Genkit 多項功能的必要條件,包括部署和可觀察性 (後續章節會討論這些主題)。
輸入和輸出結構定義
相較於直接呼叫模型 API,Genkit 資料流程最重要的優點之一,就是輸入和輸出內容的型別安全性。定義資料流時,您可以定義結構定義,方法與定義 genkit.Generate()
呼叫的輸出結構定義大致相同;不過,與 genkit.Generate()
不同的是,您也可以指定輸入結構定義。
以下是上一個範例的改良版,其中定義了一個流程,可將字串做為輸入內容並輸出物件:
type MenuItem struct {
Name string `json:"name"`
Description string `json:"description"`
}
menuSuggestionFlow := genkit.DefineFlow(g, "menuSuggestionFlow",
func(ctx context.Context, theme string) (MenuItem, error) {
return genkit.GenerateData[MenuItem](ctx, g,
ai.WithPrompt("Invent a menu item for a %s themed restaurant.", theme),
)
})
請注意,流程的結構定義不一定會與流程中 genkit.Generate()
呼叫的結構定義一致 (事實上,流程可能根本不含 genkit.Generate()
呼叫)。以下是這個範例的變化版本,它會將結構定義傳遞至 genkit.Generate()
,但會使用結構化輸出格式化流程傳回的簡單字串。
type MenuItem struct {
Name string `json:"name"`
Description string `json:"description"`
}
menuSuggestionMarkdownFlow := genkit.DefineFlow(g, "menuSuggestionMarkdownFlow",
func(ctx context.Context, theme string) (string, error) {
item, _, err := genkit.GenerateData[MenuItem](ctx, g,
ai.WithPrompt("Invent a menu item for a %s themed restaurant.", theme),
)
if err != nil {
return "", err
}
return fmt.Sprintf("**%s**: %s", item.Name, item.Description), nil
})
通話流程
定義流程後,您可以從 Go 程式碼中呼叫該流程:
item, err := menuSuggestionFlow.Run(ctx, "bistro")
流程的引數必須符合輸入結構定義。
如果您定義了輸出結構定義,流程回應就會符合該定義。舉例來說,如果您將輸出結構定義設為 MenuItem
,流程輸出內容就會包含其屬性:
item, err := menuSuggestionFlow.Run(ctx, "bistro")
if err != nil {
log.Fatal(err)
}
log.Println(item.DishName)
log.Println(item.Description)
串流流程
Flows 支援使用類似 genkit.Generate()
串流介面的串流功能。當流程產生大量輸出內容時,串流功能就很實用,因為您可以將輸出內容在產生時呈現給使用者,進而提升應用程式的回應速度。舉例來說,以聊天為基礎的 LLM 介面通常會在產生回覆時,將回覆串流傳送給使用者。
以下是支援串流的流程範例:
type Menu struct {
Theme string `json:"theme"`
Items []MenuItem `json:"items"`
}
type MenuItem struct {
Name string `json:"name"`
Description string `json:"description"`
}
menuSuggestionFlow := genkit.DefineStreamingFlow(g, "menuSuggestionFlow",
func(ctx context.Context, theme string, callback core.StreamCallback[string]) (Menu, error) {
item, _, err := genkit.GenerateData[MenuItem](ctx, g,
ai.WithPrompt("Invent a menu item for a %s themed restaurant.", theme),
ai.WithStreaming(func(ctx context.Context, chunk *ai.ModelResponseChunk) error {
// Here, you could process the chunk in some way before sending it to
// the output stream using StreamCallback. In this example, we output
// the text of the chunk, unmodified.
return callback(ctx, chunk.Text())
}),
)
if err != nil {
return nil, err
}
return Menu{
Theme: theme,
Items: []MenuItem{item},
}, nil
})
StreamCallback[string]
中的 string
類型會指定資料流的值類型。這個值不一定需要與傳回類型相同,傳回類型是流程完整輸出的類型 (本例中的 Menu
)。
在這個範例中,流程傳送的值會直接連結至流程內 genkit.Generate()
呼叫傳送的值。雖然這通常是情況,但不一定如此:您可以使用回呼將值輸出至資料流,頻率可視流程的用途而定。
呼叫串流流程
串流流程可以像使用 menuSuggestionFlow.Run(ctx, "bistro")
的非串流流程一樣執行,也可以串流傳輸:
streamCh, err := menuSuggestionFlow.Stream(ctx, "bistro")
if err != nil {
log.Fatal(err)
}
for result := range streamCh {
if result.Err != nil {
log.Fatal("Stream error: %v", result.Err)
}
if result.Done {
log.Printf("Menu with %s theme:\n", result.Output.Theme)
for item := range result.Output.Items {
log.Println(" - %s: %s", item.Name, item.Description)
}
} else {
log.Println("Stream chunk:", result.Stream)
}
}
透過指令列執行流程
您可以使用 Genkit CLI 工具,透過指令列執行流程:
genkit flow:run menuSuggestionFlow '"French"'
如要針對串流流程輸出串流輸出內容,請新增 -s
旗標:
genkit flow:run menuSuggestionFlow '"French"' -s
透過指令列執行流程,可用於測試流程,或執行流程以執行所需的臨時任務,例如執行將文件取入向量資料庫的流程。
偵錯流程
在流程中封裝 AI 邏輯的好處之一,就是您可以使用 Genkit 開發人員 UI 獨立測試及偵錯流程,不必透過應用程式。
開發人員 UI 會依賴 Go 應用程式持續執行,即使邏輯已完成也一樣。如果您是初次使用 Genkit,且 Genkit 並非較廣泛應用程式的一部分,請將 select {}
新增為 main()
的最後一行,以免應用程式關閉,讓您可以在 UI 中檢查。
如要啟動開發人員 UI,請在專案目錄中執行下列指令:
genkit start -- go run .
您可以透過開發人員 UI 的「Run」分頁,執行專案中定義的任何流程:
執行流程後,您可以按一下「View trace」或查看「Inspect」分頁標籤,檢查流程叫用的追蹤記錄。
部署流程
您可以直接將流程部署為網路 API 端點,以便從應用程式用戶端呼叫。部署作業已在其他幾個頁面中詳細討論,但本節將簡要介紹部署選項。
net/http
伺服器
如要使用任何 Go 代管平台 (例如 Cloud Run) 部署流程,請使用 DefineFlow()
定義流程,並使用提供的流程處理常式啟動 net/http
伺服器:
import (
"context"
"log"
"net/http"
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/googlegenai"
"github.com/firebase/genkit/go/plugins/server"
)
func main() {
ctx := context.Background()
g, err := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.GoogleAI{}))
if err != nil {
log.Fatal(err)
}
menuSuggestionFlow := genkit.DefineFlow(g, "menuSuggestionFlow",
func(ctx context.Context, theme string) (MenuItem, error) {
// Flow implementation...
})
mux := http.NewServeMux()
mux.HandleFunc("POST /menuSuggestionFlow", genkit.Handler(menuSuggestionFlow))
log.Fatal(server.Start(ctx, "127.0.0.1:3400", mux))
}
server.Start()
是可選的輔助函式,可啟動伺服器並管理其生命週期,包括擷取中斷信號,以便進行本機開發作業,但您可以使用自己的方法。
如要提供程式碼集定義的所有流程,您可以使用 ListFlows()
:
mux := http.NewServeMux()
for _, flow := range genkit.ListFlows(g) {
mux.HandleFunc("POST /"+flow.Name(), genkit.Handler(flow))
}
log.Fatal(server.Start(ctx, "127.0.0.1:3400", mux))
您可以使用 POST 要求呼叫流程端點,如下所示:
curl -X POST "http://localhost:3400/menuSuggestionFlow" \
-H "Content-Type: application/json" -d '{"data": "banana"}'
其他伺服器架構
您也可以使用其他伺服器架構來部署流程。舉例來說,您只需幾行程式碼即可使用 Gin:
router := gin.Default()
for _, flow := range genkit.ListFlows(g) {
router.POST("/"+flow.Name(), func(c *gin.Context) {
genkit.Handler(flow)(c.Writer, c.Request)
})
}
log.Fatal(router.Run(":3400"))
如要瞭解如何部署至特定平台,請參閱「搭配 Cloud Run 使用 Genkit」。