Inti dari fitur AI aplikasi adalah permintaan model generatif, tetapi sangat jarang Anda dapat mengambil input pengguna, meneruskannya ke model, dan menampilkan output model kembali kepada pengguna. Biasanya, ada langkah prapemrosesan dan pascapemrosesan yang harus menyertai panggilan model. Contoh:
- Mengambil informasi kontekstual yang akan dikirim dengan panggilan model.
- Mengambil histori sesi pengguna saat ini, misalnya di aplikasi chat.
- Menggunakan satu model untuk memformat ulang input pengguna dengan cara yang sesuai agar dapat diteruskan ke model lain.
- Mengevaluasi "keamanan" output model sebelum menampilkannya kepada pengguna.
- Menggabungkan output dari beberapa model.
Setiap langkah alur kerja ini harus saling bekerja sama agar tugas terkait AI dapat berhasil.
Di Genkit, Anda merepresentasikan logika yang terikat erat ini menggunakan konstruksi yang disebut flow. Flow ditulis seperti fungsi menggunakan kode Go biasa, tetapi menambahkan kemampuan lain yang dimaksudkan untuk memudahkan pengembangan fitur AI:
- Keamanan jenis: Skema input dan output, yang menyediakan pemeriksaan jenis statis dan runtime.
- Integrasi dengan UI developer: Men-debug flow secara terpisah dari kode aplikasi menggunakan UI developer. Di UI developer, Anda dapat menjalankan flow dan melihat trace untuk setiap langkah flow.
- Deployment yang disederhanakan: Men-deploy flow langsung sebagai endpoint API web, menggunakan platform yang dapat menghosting aplikasi web.
Flow Genkit bersifat ringan dan tidak mengganggu, serta tidak memaksa aplikasi Anda untuk mematuhi abstraksi tertentu. Semua logika flow ditulis dalam Go standar, dan kode di dalam flow tidak perlu mengetahui flow itu sendiri.
Menentukan dan memanggil flow
Dalam bentuk yang paling sederhana, flow hanya menggabungkan fungsi. Contoh berikut menggabungkan
fungsi yang memanggil 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
})
Cukup dengan menggabungkan panggilan genkit.Generate()
seperti ini, Anda akan menambahkan beberapa
fungsi: Dengan melakukannya, Anda dapat menjalankan flow dari Genkit CLI dan dari
UI developer, serta merupakan persyaratan untuk beberapa fitur Genkit,
termasuk deployment dan kemampuan observasi (topik ini akan dibahas di bagian selanjutnya).
Skema input dan output
Salah satu keunggulan terpenting yang dimiliki flow Genkit selain memanggil
API model secara langsung adalah keamanan jenis input dan output. Saat menentukan flow, Anda
dapat menentukan skema, dengan cara yang sama seperti menentukan skema output
panggilan genkit.Generate()
. Namun, tidak seperti genkit.Generate()
, Anda juga dapat
menentukan skema input.
Berikut adalah penyempurnaan dari contoh terakhir, yang menentukan flow menggunakan string sebagai input dan menghasilkan sebuah objek:
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),
)
})
Perlu diperhatikan, skema flow tidak harus selaras dengan
skema panggilan genkit.Generate()
dalam flow (bahkan, flow mungkin
tidak berisi panggilan genkit.Generate()
). Berikut adalah variasi contoh
yang meneruskan skema ke genkit.Generate()
, tetapi menggunakan output
terstruktur untuk memformat string sederhana, yang ditampilkan oleh flow.
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
})
Memanggil flow
Setelah menentukan flow, Anda dapat memanggilnya dari kode Go:
item, err := menuSuggestionFlow.Run(ctx, "bistro")
Argumen ke flow harus sesuai dengan skema input.
Jika Anda menentukan skema output, respons flow akan sesuai dengan skema tersebut. Misalnya,
jika Anda menetapkan skema output ke MenuItem
, output flow akan
berisi properti dari skema tersebut:
item, err := menuSuggestionFlow.Run(ctx, "bistro")
if err != nil {
log.Fatal(err)
}
log.Println(item.DishName)
log.Println(item.Description)
Flow streaming
Flow mendukung streaming menggunakan antarmuka yang mirip dengan antarmuka
streaming genkit.Generate()
. Streaming berguna saat flow menghasilkan output
dalam jumlah besar, karena Anda dapat menampilkan output kepada pengguna saat
output tersebut dihasilkan, dan meningkatkan responsivitas aplikasi Anda. Sebagai contoh
umum, antarmuka LLM berbasis chat sering kali melakukan streaming respons kepada
pengguna saat dihasilkan.
Berikut adalah contoh flow yang mendukung streaming:
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
})
Jenis string
di StreamCallback[string]
menentukan jenis
nilai yang di-streaming oleh flow Anda. Jenis ini tidak harus sama dengan
jenis nilai yang ditampilkan, yaitu jenis output lengkap flow
(Menu
dalam contoh ini).
Dalam contoh ini, nilai yang di-streaming oleh flow secara langsung disambungkan ke
nilai yang di-streaming oleh panggilan genkit.Generate()
di dalam flow.
Meskipun sering terjadi, namun tidak harus demikian: Anda dapat menghasilkan nilai
streaming menggunakan callback sesuai dengan kebutuhan flow Anda.
Memanggil flow streaming
Flow streaming dapat dijalankan seperti flow non-streaming dengan
menuSuggestionFlow.Run(ctx, "bistro")
atau dapat di-streaming:
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)
}
}
Menjalankan flow dari command line
Anda dapat menjalankan flow dari command line menggunakan alat Genkit CLI:
genkit flow:run menuSuggestionFlow '"French"'
Untuk flow streaming, Anda dapat mencetak output streaming ke konsol dengan menambahkan
flag -s
:
genkit flow:run menuSuggestionFlow '"French"' -s
Menjalankan flow dari command line berguna untuk mengujinya, atau untuk menjalankan flow yang melakukan tugas yang diperlukan secara ad hoc—misalnya, untuk menjalankan flow yang menyerap dokumen ke dalam database vektor Anda.
Proses debug flow
Salah satu keuntungan dari mengenkapsulasi logika AI dalam flow adalah Anda dapat menguji dan men-debug flow secara independen dari aplikasi menggunakan UI developer Genkit.
UI developer mengandalkan aplikasi Go yang terus berjalan, meskipun logika telah
selesai. Jika Anda baru memulai dan Genkit bukan bagian dari aplikasi
yang lebih luas, tambahkan select {}
sebagai baris terakhir main()
untuk mencegah aplikasi
dimatikan sehingga Anda dapat memeriksanya di UI.
Untuk memulai UI developer, jalankan perintah berikut dari direktori project Anda:
genkit start -- go run .
Dari tab Run di UI developer, Anda dapat menjalankan flow apa pun yang ditentukan dalam project Anda:
Setelah menjalankan flow, Anda dapat memeriksa trace pemanggilan flow dengan mengklik View trace atau melihat tab Inspect.
Men-deploy flow
Anda dapat men-deploy flow secara langsung sebagai endpoint API web, yang siap Anda panggil dari klien aplikasi. Deployment dibahas secara mendetail di beberapa halaman lain, tetapi bagian ini memberikan ringkasan singkat tentang opsi deployment Anda.
Server net/http
Untuk men-deploy flow menggunakan platform hosting Go, seperti Cloud Run, tentukan
flow Anda menggunakan DefineFlow()
dan mulai server net/http
dengan pengendali
flow yang disediakan:
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()
adalah fungsi bantuan opsional yang memulai server dan
mengelola siklus prosesnya, termasuk menangkap sinyal gangguan untuk memudahkan
pengembangan lokal, tetapi Anda dapat menggunakan metode Anda sendiri.
Untuk menayangkan semua flow yang ditentukan dalam codebase, Anda dapat menggunakan 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))
Anda dapat memanggil endpoint flow dengan permintaan POST sebagai berikut:
curl -X POST "http://localhost:3400/menuSuggestionFlow" \
-H "Content-Type: application/json" -d '{"data": "banana"}'
Framework server lainnya
Anda juga dapat menggunakan framework server lain untuk men-deploy flow. Misalnya, Anda dapat menggunakan Gin hanya dengan beberapa baris:
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"))
Untuk informasi terkait deployment ke platform tertentu, lihat Genkit dengan Cloud Run.