Next.js ve Vercel AI SDK ile Streaming Mimarisi ve LLM Entegrasyonu
Büyük Dil Modelleri (LLM - OpenAI, Anthropic, Gemini vb.) hayatımıza girdiğinden beri web uygulamalarının çehresi değişti. Geleneksel HTTP istek-cevap (request-response) döngüsü, AI tabanlı uygulamalar için yetersiz kalmaktadır. Çünkü bir LLM'in tam cevabı üretmesi bazen 10-15 saniye sürebilir. Kullanıcıyı 15 saniye boş bir yükleniyor (loading) ekranına mahkum etmek kabul edilemez bir kullanıcı deneyimidir (UX).
Çözüm: Streaming (Veri Akışı) mimarisidir. Bu makalede, Next.js App Router ve Vercel AI SDK kullanarak modern ve ölçeklenebilir bir yapay zeka entegrasyonunun mimarisini inceleyeceğiz.
Neden Geleneksel REST API Yerine Streaming?
Geleneksel mimaride sunucu, AI modeline istek atar, cevabın tamamlanmasını bekler ve tüm cevabı tek bir JSON bloğu halinde istemciye gönderir.
Streaming mimarisinde ise Next.js Node.js (veya Edge) çalışma zamanında LLM'den gelen her bir kelimeyi (token) anında tarayıcıya (istemciye) aktarır. Böylece kullanıcılar sanki biri karşılarında daktiloyla yazı yazıyormuş gibi sonucu anında görmeye başlarlar.
Vercel AI SDK ve Server Actions Kullanımı
Next.js Server Actions, API route (/api/...) oluşturma zorunluluğunu ortadan kaldırır. Frontend kodumuzdan doğrudan sunucu tarafı bir fonksiyonu çağırabiliriz.
Aşağıdaki örnekte, Vercel AI SDK'nın streamText metodunu kullanarak nasıl optimize bir akış kurduğumuzu görebilirsiniz:
// app/actions.ts (Sunucu Tarafı - Server Action)
"use server";
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { createStreamableValue } from "ai/rsc";
export async function generateProductDescription(prompt: string) {
// 1. İstemci ile sunucu arasında aktarılabilir (streamable) bir state oluşturuyoruz
const stream = createStreamableValue("");
// 2. AI modeline asenkron olarak bağlanıp stream başlatıyoruz
(async () => {
const { textStream } = await streamText({
model: openai("gpt-4-turbo"),
prompt: `Sen bir SEO uzmanısın. Şu ürün için dikkat çekici bir açıklama yaz: ${prompt}`,
});
// 3. Gelen her token'ı streamable state'e ekliyoruz
for await (const delta of textStream) {
stream.update(delta);
}
// 4. İşlem bitince stream'i kapatıyoruz
stream.done();
})();
// İstemciye akış nesnesini dönüyoruz
return { output: stream.value };
}