Uygulamanızın yapay zeka özelliklerinin temelinde üretken model istekleri vardır ancak kullanıcı girişini alıp modele iletmeniz ve model çıktısını kullanıcıya göstermeniz pek mümkün değildir. Genellikle model çağrısına eşlik etmesi gereken ön ve son işleme adımları vardır. Örneğin:
- Model çağrısıyla gönderilecek bağlamsal bilgileri alma
- Örneğin, bir sohbet uygulamasında kullanıcının mevcut oturumunun geçmişini alma
- Kullanıcı girişini başka bir modele aktarmaya uygun olacak şekilde yeniden biçimlendirmek için bir model kullanma
- Kullanıcıya sunmadan önce bir modelin çıktısının "güvenliğini" değerlendirme
- Birkaç modelin çıktısını birleştirme
Yapay zeka ile ilgili görevlerin başarılı olması için bu iş akışının her adımı birlikte çalışmalıdır.
Genkit'te bu sıkı bağlantılı mantığı akış adı verilen bir yapı kullanarak temsil edersiniz. Akışlar, normal TypeScript kodu kullanılarak işlevler gibi yazılır ancak yapay zeka özelliklerinin geliştirilmesini kolaylaştırmak için ek özellikler ekler:
- Tür güvenliği: Hem statik hem de çalışma zamanında tür kontrolü sağlayan Zod kullanılarak tanımlanan giriş ve çıkış şemaları
- Geliştirici kullanıcı arayüzü ile entegrasyon: Geliştirici kullanıcı arayüzünü kullanarak akışları uygulama kodunuzdan bağımsız olarak hata ayıklayın. Geliştirici kullanıcı arayüzünde akışları çalıştırabilir ve akışın her adımı için izlemeleri görüntüleyebilirsiniz.
- Basitleştirilmiş dağıtım: Firebase için Cloud Functions'i veya web uygulaması barındırabilen herhangi bir platformu kullanarak akışları doğrudan web API uç noktaları olarak dağıtın.
Diğer çerçevelerdeki benzer özelliklerin aksine, Genkit'in akışları hafif ve göze çarpmayan bir yapıdadır ve uygulamanızı belirli bir soyutlamaya uymaya zorlamaz. Akıştaki tüm mantık standart TypeScript ile yazılmıştır ve akıştaki kodun akış bilincine sahip olması gerekmez.
Akışları tanımlama ve çağırma
En basit haliyle akış, bir işlevi sarmalayan bir yapıdır. Aşağıdaki örnekte, generate()
işlevini çağıran bir işlev sarmalanmıştır:
export const menuSuggestionFlow = ai.defineFlow(
{
name: 'menuSuggestionFlow',
},
async (restaurantTheme) => {
const { text } = await ai.generate({
model: gemini15Flash,
prompt: `Invent a menu item for a ${restaurantTheme} themed restaurant.`,
});
return text;
}
);
generate()
çağrılarınızı bu şekilde sarmalayarak bazı işlevler eklersiniz: Bu, akışı Genkit CLI'den ve geliştirici kullanıcı arayüzünden çalıştırmanıza olanak tanır ve dağıtım ve gözlemlenebilirlik dahil olmak üzere Genkit'in birçok özelliği için bir şarttır (bu konular sonraki bölümlerde ele alınmıştır).
Giriş ve çıkış şemaları
Genkit akışlarının, model API'sini doğrudan çağırmaya kıyasla en önemli avantajlarından biri hem girişlerin hem de çıkışların tür güvenliğidir. Akışları tanımlarken, Zod'u kullanarak akışlar için şema tanımlayabilirsiniz. Bu, generate()
çağrısının çıkış şemasını tanımlamanıza benzer. Ancak generate()
'ten farklı olarak bir giriş şeması da belirtebilirsiniz.
Son örneğin daha ayrıntılı bir versiyonunu aşağıda bulabilirsiniz. Bu örnekte, giriş olarak bir dize alan ve çıkış olarak bir nesne döndüren bir akış tanımlanmaktadır:
const MenuItemSchema = z.object({
dishname: z.string(),
description: z.string(),
});
export const menuSuggestionFlowWithSchema = ai.defineFlow(
{
name: 'menuSuggestionFlow',
inputSchema: z.string(),
outputSchema: MenuItemSchema,
},
async (restaurantTheme) => {
const { output } = await ai.generate({
model: gemini15Flash,
prompt: `Invent a menu item for a ${restaurantTheme} themed restaurant.`,
output: { schema: MenuItemSchema },
});
if (output == null) {
throw new Error("Response doesn't satisfy schema.");
}
return output;
}
);
Bir akış şemasının, akış içindeki generate()
çağrılarının şemasıyla eşleşmesi gerekmediğini unutmayın (aslında bir akışta generate()
çağrısı bile olmayabilir). Aşağıda, generate()
'ye bir şema aktaran ancak akışın döndürdüğü basit bir dizeyi biçimlendirmek için yapılandırılmış çıkışı kullanan örneğin bir varyasyonu verilmiştir.
export const menuSuggestionFlowMarkdown = ai.defineFlow(
{
name: 'menuSuggestionFlow',
inputSchema: z.string(),
outputSchema: z.string(),
},
async (restaurantTheme) => {
const { output } = await ai.generate({
model: gemini15Flash,
prompt: `Invent a menu item for a ${restaurantTheme} themed restaurant.`,
output: { schema: MenuItemSchema },
});
if (output == null) {
throw new Error("Response doesn't satisfy schema.");
}
return `**${output.dishname}**: ${output.description}`;
}
);
Arama akışları
Tanımladığınız bir akışı Node.js kodunuzdan çağırabilirsiniz:
const { text } = await menuSuggestionFlow('bistro');
Akışa ait bağımsız değişken, tanımladıysanız giriş şemasına uygun olmalıdır.
Bir çıkış şeması tanımladıysanız akış yanıtı buna uygun olur. Örneğin, çıkış şemasını MenuItemSchema
olarak ayarlarsanız akış çıkışı, özelliklerini içerir:
const { dishname, description } =
await menuSuggestionFlowWithSchema('bistro');
Akış akışları
Akışlar, generate()
'ın yayın arayüzüne benzer bir arayüz kullanarak yayını destekler. Akışınız çok miktarda çıkış oluşturduğunda akış, çıkışı oluşturulurken kullanıcıya sunabileceğiniz için yayın özelliği kullanışlıdır. Bu da uygulamanızın algılanan yanıt verebilirliğini artırır. Sohbet tabanlı LLM arayüzleri genellikle yanıtlarını oluşturulurken kullanıcıya aktarır.
Akışları destekleyen bir akış örneğini aşağıda görebilirsiniz:
export const menuSuggestionStreamingFlow = ai.defineStreamingFlow(
{
name: 'menuSuggestionFlow',
inputSchema: z.string(),
streamSchema: z.string(),
outputSchema: z.object({ theme: z.string(), menuItem: z.string() }),
},
async (restaurantTheme, streamingCallback) => {
const response = await ai.generateStream({
model: gemini15Flash,
prompt: `Invent a menu item for a ${restaurantTheme} themed restaurant.`,
});
if (streamingCallback) {
for await (const chunk of response.stream) {
// Here, you could process the chunk in some way before sending it to
// the output stream via streamingCallback(). In this example, we output
// the text of the chunk, unmodified.
streamingCallback(chunk.text);
}
}
return {
theme: restaurantTheme,
menuItem: (await response.response).text,
};
}
);
streamSchema
seçeneği, akışınızın yayınladığı değerlerin türünü belirtir. Bu, akıştaki tüm çıkışın türü olanoutputSchema
ile aynı türde olmak zorunda değildir.streamingCallback
,streamSchema
tarafından belirtilen türde tek bir parametre alan bir geri çağırma işlevidir. Akışınızda veri bulunduğunda bu işlevi çağırarak verileri çıkış akışına gönderin.streamingCallback
parametresinin yalnızca akışınızın arayanı akış çıkışı isteğinde bulunduğunda tanımlandığını unutmayın. Bu nedenle, bu parametreyi çağırmadan önce tanımlanmış olup olmadığını kontrol etmeniz gerekir.
Yukarıdaki örnekte, akış tarafından aktarılan değerler doğrudan akış içindeki generate()
çağrısı tarafından aktarılan değerlerle birleştirilir. Bu genellikle geçerli olsa da gerekli değildir: Geri çağırma işlevini, akışınız için yararlı olduğu sıklıkta kullanarak akışa değer gönderebilirsiniz.
Görüşme aktarma akışları
Akış akışları da çağrılabilir ancak hemen bir promise yerine bir yanıt nesnesi döndürürler:
const response = menuSuggestionStreamingFlow('Danube');
Yanıt nesnesinde, oluşturulduğu sırada akış çıktısını iterasyonla işlemek için kullanabileceğiniz bir akış mülkü vardır:
for await (const chunk of response.stream) {
console.log('chunk', chunk);
}
Akış olmayan bir akışta olduğu gibi, akışın tamamını da alabilirsiniz:
const output = await response.output;
Bir akıştaki akış çıkışının, tam çıkışla aynı türde olmayabileceğini unutmayın. Akış çıkışı streamSchema
, tam çıkış ise outputSchema
ile uyumludur.
Akışları komut satırından çalıştırma
Genkit CLI aracını kullanarak akışları komut satırından çalıştırabilirsiniz:
genkit flow:run menuSuggestionFlow '"French"'
Akış akışları için -s
işaretini ekleyerek akış çıkışını konsola yazdırabilirsiniz:
genkit flow:run menuSuggestionFlow '"French"' -s
Bir akışı komut satırından çalıştırmak, akışı test etmek veya gerektiğinde anlık olarak görevleri gerçekleştiren akışları çalıştırmak için (ör. bir dokümanı vektör veritabanınıza aktaran bir akışı çalıştırmak) kullanışlıdır.
Akışlarda hata ayıklama
Yapay zeka mantığını bir akış içinde kapsamanın avantajlarından biri, Genkit geliştirici kullanıcı arayüzünü kullanarak akışı uygulamanızdan bağımsız olarak test edip hata ayıklayabilmenizdir.
Geliştirici kullanıcı arayüzünü başlatmak için proje dizininizde aşağıdaki komutları çalıştırın:
genkit start -- tsx --watch src/your-code.ts
Geliştirici kullanıcı arayüzünün Çalıştır sekmesinden projenizde tanımlanan akışlardan herhangi birini çalıştırabilirsiniz:
Bir akışı çalıştırdıktan sonra İzleyiciyi görüntüle'yi tıklayarak veya İncele sekmesine giderek akışı çağırma işleminin izini inceleyebilirsiniz.
İzleme görüntüleyicisinde, akıştaki tüm adımların yanı sıra akıştaki her bir adımla ilgili ayrıntıları görebilirsiniz. Örneğin, aşağıdaki akışı düşünün. Bu akış, birkaç oluşturma isteği içerir:
const PrixFixeMenuSchema = z.object({
starter: z.string(),
soup: z.string(),
main: z.string(),
dessert: z.string(),
});
export const complexMenuSuggestionFlow = ai.defineFlow(
{
name: 'complexMenuSuggestionFlow',
inputSchema: z.string(),
outputSchema: PrixFixeMenuSchema,
},
async (theme: string): Promise<z.infer<typeof PrixFixeMenuSchema>> => {
const chat = ai.chat({ model: gemini15Flash });
await chat.send('What makes a good prix fixe menu?');
await chat.send(
'What are some ingredients, seasonings, and cooking techniques that ' +
`would work for a ${theme} themed menu?`
);
const { output } = await chat.send({
prompt:
`Based on our discussion, invent a prix fixe menu for a ${theme} ` +
'themed restaurant.',
output: {
schema: PrixFixeMenuSchema,
},
});
if (!output) {
throw new Error('No data generated.');
}
return output;
}
);
Bu akışı çalıştırdığınızda izleme görüntüleyici, her bir oluşturma isteğiyle ilgili çıkışı da dahil olmak üzere ayrıntıları gösterir:
Akış adımları
Son örnekte, her generate()
çağrısının izleme görüntüleyicisinde ayrı bir adım olarak gösterildiğini görmüştünüz. Genkit'in temel işlemlerinin her biri, bir akışın ayrı adımları olarak gösterilir:
generate()
Chat.send()
embed()
index()
retrieve()
İzlemelerinize yukarıdakilerden farklı bir kod eklemek istiyorsanız kodu bir run()
çağrısına sarmalayarak bunu yapabilirsiniz. Bunu, Genkit uyumlu olmayan üçüncü taraf kitaplıklarına yapılan çağrılar veya kodun kritik bölümleri için yapabilirsiniz.
Örneğin, iki adımlı bir akış aşağıda verilmiştir: İlk adım, belirtilmemiş bir yöntem kullanarak bir menü alır ve ikinci adım, generate()
çağrısı için menüye bağlam olarak yer verir.
export const menuQuestionFlow = ai.defineFlow(
{
name: 'menuQuestionFlow',
inputSchema: z.string(),
outputSchema: z.string(),
},
async (input: string): Promise<string> => {
const menu = await run('retrieve-daily-menu', async (): Promise<string> => {
// Retrieve today's menu. (This could be a database access or simply
// fetching the menu from your website.)
// ...
return menu;
});
const { text } = await ai.generate({
model: gemini15Flash,
system: "Help the user answer questions about today's menu.",
prompt: input,
docs: [{ content: [{ text: menu }] }],
});
return text;
}
);
Alma adımı bir run()
çağrısına sarmalandığı için izleme görüntüleyiciye bir adım olarak eklenir:
Akışları dağıtma
Akışlarınızı doğrudan web API uç noktaları olarak dağıtabilir ve uygulama istemcilerinizden çağırmaya hazır hale getirebilirsiniz. Dağıtım, diğer çeşitli sayfalarda ayrıntılı olarak ele alınmıştır ancak bu bölümde dağıtım seçeneklerinize kısaca göz atabilirsiniz.
Cloud Functions for Firebase
Cloud Functions for Firebase ile akışları dağıtmak için firebase
eklentisini kullanın. Akış tanımlarınızdaki defineFlow
değerini onFlow
ile değiştirin ve bir authPolicy
ekleyin.
import { firebaseAuth } from '@genkit-ai/firebase/auth';
import { onFlow } from '@genkit-ai/firebase/functions';
export const menuSuggestion = onFlow(
ai,
{
name: 'menuSuggestionFlow',
authPolicy: firebaseAuth((user) => {
if (!user.email_verified) {
throw new Error('Verified email required to run flow');
}
}),
},
async (restaurantTheme) => {
// ...
}
);
Daha fazla bilgi için aşağıdaki sayfalara bakın:
Express.js
Cloud Run gibi herhangi bir Node.js barındırma platformunu kullanarak akışları dağıtmak için defineFlow()
kullanarak akışlarınızı tanımlayın ve ardından startFlowServer()
işlevini çağırın:
export const menuSuggestionFlow = ai.defineFlow(
{
name: 'menuSuggestionFlow',
},
async (restaurantTheme) => {
// ...
}
);
ai.startFlowServer({
flows: [menuSuggestionFlow],
});
Varsayılan olarak startFlowServer
, kod tabanınızda HTTP uç noktası olarak tanımlanan tüm akışları (ör. http://localhost:3400/menuSuggestionFlow
) sunar. Bir akışı POST isteğiyle şu şekilde çağırabilirsiniz:
curl -X POST "http://localhost:3400/menuSuggestionFlow" \
-H "Content-Type: application/json" -d '{"data": "banana"}'
Gerekirse akış sunucusunu, aşağıda gösterildiği gibi belirli bir akış listesi yayınlayacak şekilde özelleştirebilirsiniz. Özel bir bağlantı noktası da belirtebilirsiniz (ayarlanmışsa PORT ortam değişkeni kullanılır) veya CORS ayarlarını belirtebilirsiniz.
export const flowA = ai.defineFlow({ name: 'flowA' }, async (subject) => {
// ...
});
export const flowB = ai.defineFlow({ name: 'flowB' }, async (subject) => {
// ...
});
ai.startFlowServer({
flows: [flowB],
port: 4567,
cors: {
origin: '*',
},
});
Belirli platformlara dağıtma hakkında bilgi edinmek için Cloud Run ile dağıtma ve Akışları herhangi bir Node.js platformuna dağıtma başlıklı makaleleri inceleyin.