Prompt API ile Vercel AI SDK kullanıcı arayüzünü ve yapay zeka öğelerini kullanma

Yayınlanma tarihi: 16 Temmuz 2026

Use the built-in Prompt API with the Vercel AI SDK (Yerleşik istem API'sini Vercel AI SDK ile kullanma) başlıklı makalede, generateText(), streamText(), hibrit kod ve Output.object() kullanılarak yapılandırılmış çıkış olmak üzere dört temel oluşturma öğesini görmüştünüz. Bu öğelerin tümü @browser-ai/core tarafından desteklenir. Bu kez daha etkileşimli bir şey oluşturacaksınız: Prompt API kullanılamadığında otomatik olarak bir bulut modeline geri dönen, tamamen tarayıcıda çalışan tam bir akış sohbeti kullanıcı arayüzü.

Ne oluşturuyorsunuz?

Aşağıdakileri yapan bir React sohbet arayüzü:

  • Çok turlu sohbeti yayınlamak için Vercel AI SDK'sının useChat kancasını kullanır.
  • Arka uç sunucusu gerektirmeden model döngüsünü tarayıcıda çalıştırır.
  • Prompt API kullanılamadığında otomatik olarak Gemini 2.5 Flash'a geri döner.
  • Asistan yanıtlarını GitHub'a özgü Markdown olarak oluşturur ve yayın sırasında eksik jetonları işler.
  • İlk jeton beklenirken "Düşünüyor…" parıltısı gösterilir.
  • Yeni mesajlara otomatik olarak kaydırılır. Yukarı kaydırdığınızda en alta kaydırma düğmesi gösterilir.

Ek bağımlılıklar

ai, @browser-ai/core ve @ai-sdk/google ile birlikte sohbet kullanıcı arayüzü için React, AI SDK'nın React bağlamaları ve birkaç Markdown paketi gerekir:

npm install react react-dom @ai-sdk/react
npm install react-markdown remark-gfm harden-react-markdown
npm install -D @types/react @types/react-dom

Kullanıcı arayüzü için Tailwind CSS, bazı bileşen yardımcı programları ve simgeler için Lucide'i de ekleyin:

npm install -D tailwindcss postcss autoprefixer
npm install clsx tailwind-merge lucide-react

Oylama yapılandırması: çok girişli derleme

Projede zaten index.html var. chat.html öğesini ikinci giriş noktası olarak ekleyin ve Vite'ı her ikisini de oluşturacak şekilde yapılandırın:

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: { alias: { '@': resolve(__dirname, './src') } },
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        chat: resolve(__dirname, 'chat.html'),
      },
    },
  },
});

chat.html, yalnızca <div id="root"> ve bir komut dosyası etiketinden oluşur:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Built-in AI Chatbot</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/chat.tsx"></script>
  </body>
</html>

Otomatik model seçimi

Uygulama, chatbot'ta bu seçimi otomatik olarak yapar: Önce yerleşik modeli dener ve Prompt API kullanılamıyorsa buluta geri döner.

Bu işlemi, React monte edilmeden önce, modül yükleme sırasında yapar. Böylece kullanıcı ilk mesajını yazdığında aracı hazır olur:

const agentPromise: Promise<ToolLoopAgent> = (async () => {
  const builtIn = browserAI();
  let model: any = builtIn;

  if (typeof builtIn.availability === 'function') {
    const availability = await builtIn.availability();
    if (availability === 'unavailable') {
      const { createGoogleGenerativeAI } = await import('@ai-sdk/google');
      model = createGoogleGenerativeAI({ apiKey })('gemini-2.5-flash');
    } else if (availability === 'downloadable') {
      await builtIn.createSessionWithProgress(() => {});
    }
  }

  return new ToolLoopAgent({ model, instructions: 'You are a helpful assistant.' });
})();

Yeni bileşen ToolLoopAgent; herhangi bir modelin üzerinde çok turlu bir sohbet döngüsünü yöneten yapay zeka SDK'sı soyutlamasıdır. Modeli ve sistem istemini alır ve ileri geri iletişimi dahili olarak yönetir.

UseChat'i kullanmak için ajanı bağlayın

@ai-sdk/react paketinin useChat kancası normalde bir HTTP uç noktasıyla iletişim kurar. Tarayıcı tarafında çıkarım için bunun yerine DirectChatTransport kullanın. Bu işlev, ToolLoopAgent döngüsünü tamamen tarayıcıda çalıştırır ve sunucu kullanılmaz:

const transport = useMemo(() => new DirectChatTransport({ agent }), [agent]);
const { messages, sendMessage, status, stop } = useChat({ transport });

useMemo, görüşme durumunu tuttuğu için önemlidir ve kararlı bir referans olmalıdır.DirectChatTransport Her oluşturma işleminde yeniden oluşturmak, sohbeti sıfırlar.

useChat size şunları sunar:

  • Messages: UIMessage[] olarak tam görüşme, her biri role ve parts dizisiyle
  • sendMessage({ text }): Yeni bir kullanıcı dönüşü gönderir ve yanıtı yayınlamaya başlar.
  • Status: 'idle' | 'submitted' | 'streaming' | 'error'
  • Stop: devam eden bir oluşturma işlemini iptal eder

İletileri oluşturma

Her mesajda bir parts dizisi bulunur. Bu chatbot için yalnızca type: 'text' bölümleriyle ilgileniyoruz. Kullanıcı mesajları sağa hizalı bir balon olarak görünür. Asistan mesajları ise sola hizalı olup bir simge içerir:

const ChatMessage = ({ message, isStreaming }: { message: UIMessage; isStreaming: boolean }) => {
  const isUser = message.role === 'user';

  const textParts = message.parts.map((part, i) => {
    if (part.type !== 'text') return null;
    if (isUser) return <span key={i}>{part.text}</span>;
    return <Response key={i} parseIncompleteMarkdown={isStreaming}>{part.text}</Response>;
  });

  if (isUser) {
    return (
      <div className="flex flex-col items-end gap-2 animate-fade-up">
        <MessageContent className="w-fit max-w-[min(80%,56ch)] ...">
          {textParts}
        </MessageContent>
      </div>
    );
  }

  return (
    <div className="flex items-start gap-3">
      <AIIcon />
      <MessageContent className="text-[13px] leading-[1.65]">{textParts}</MessageContent>
    </div>
  );
};

MessageContent ve Response, Yapay Zeka Öğeleri'dir. Bunlar, npm'den yüklemek yerine projenize kopyaladığınız shadcn tarzı kaynak bileşenlerdir. Response, GitHub'a özgü Markdown (tablolar, görev listeleri, üstü çizili) için react-markdown ile remark-gfm, yapay zeka çıkışındaki bağlantı ve resimleri temizlemek için harden-react-markdown ile sarmalar.

İleti hâlâ yayınlanırken parseIncompleteMarkdown özelliği true olur. Yayın sırasında model, **bold yazabilir ancak henüz ** kapatmayabilir. Bu durumda, yıldız işareti olarak oluşturulacak bir sarkan jeton kalır. parseIncompleteMarkdown, açık olan **, __, `, ~~ öğelerini kapatır ve oluşturulan çıktının her artımlı parçada temiz kalması için sarkan [ bağlantı başlangıçlarını keser.

"Düşünüyor…" durumu

Bir mesaj gönderme ile ilk jetonu alma arasındaki süre status'dır. 'submitted'. Bu süre zarfında uygulamada animasyonlu bir parıltı gösterilir:

{status === 'submitted' && messages.at(-1)?.role !== 'assistant' && (
  <ThinkingMessage />
)}

messages.at(-1)?.role !== 'assistant' koşulu, asistan mesajı akışı başladıktan sonra ışıltının yeniden görünmesini engeller.

ThinkingMessage, Shimmer bileşenini kullanır: background-clip: text kullanılarak hareketli gradyan içeren bir <span>, "Düşünülüyor…" metnine kapsamlı bir vurgu efekti verir.

Otomatik kaydırma

Yeni içerik geldiğinde uygulama en alta kaydırılır ancak yalnızca kullanıcı zaten en alttaysa. Kullanıcıları, okudukları bir şeyden veya sohbetin ortasından uzaklaştırmak rahatsız edici olabilir.

const [isAtBottom, setIsAtBottom] = useState(true);

useEffect(() => {
  if (isAtBottom) endRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages, status, isAtBottom]);

const handleScroll = () => {
  const el = containerRef.current;
  if (!el) return;
  setIsAtBottom(el.scrollHeight - el.scrollTop - el.clientHeight < 50);
};

isAtBottom yanlış olduğunda kaydırma çubuğunu en alta götürme düğmesi görünür ve kullanıcı en alta döndüğünde kaybolur.

Giriş alanı

Metin alanı, her giriş etkinliğinde yüksekliği auto olarak sıfırlanıp ardından scrollHeight olarak ayarlanarak yazarken otomatik olarak yeniden boyutlandırılır. Enter tuşuyla (Üst Karakter+Enter değil) gönderilir ve yanıt yayınlanırken Gönder düğmesinin yerini stop() işlevini çağıran bir Durdur düğmesi alır:

<textarea
  onInput={(e) => {
    const el = e.currentTarget;
    el.style.height = 'auto';
    el.style.height = `${el.scrollHeight}px`;
  }}
  onKeyDown={(e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      if (input.trim() && !isStreaming) {
        sendMessage({ text: input });
        setInput('');
      }
    }
  }}
/>;
{
  isStreaming ? (
    <Button variant="outline" onClick={stop}>
      Stop
    </Button>
  ) : (
    <Button type="submit" disabled={!input.trim()}>
      Send
    </Button>
  );
}

Yükleme durumu olan bir öğe bağlama

agentPromise asenkron olduğundan Chat öğesini oluşturmadan önce bunun tamamlanmasını bekleyin. Bir App sarmalayıcı, sözü yerine getirir ve bu sırada bir döndürücü gösterir:

function App() {
  const [agent, setAgent] = (useState < ToolLoopAgent) | (null > null);

  useEffect(() => {
    agentPromise.then(setAgent);
  }, []);

  if (!agent) {
    return (
      <div className="flex h-dvh items-center justify-center">
        <Loader size={20} />
      </div>
    );
  }

  return <Chat agent={agent} />;
}

Ajan çözüldüğünde (yerleşik modelin anında başlatılması veya modelin indirilmesinin beklenmesi) döndürücü kaybolur ve sohbet kullanıcı arayüzü yüklenir.

Demo

Canlı demo, tamamen tarayıcınızda çalışan tam işlevli bir chatbot'tur. Bir mesaj yazıp Enter tuşuna basın. İstem API'si kullanılabiliyorsa yanıt, ağ isteği olmadan doğrudan cihazdaki modelden yayınlanır. Tarayıcınız Prompt API'yi desteklemiyorsa otomatik olarak Gemini 2.5 Flash'a geri döner. Listede yer alan bir şeyi açıklamasını, kod snippet'i yazmasını veya Markdown biçimlendirmesi kullanmasını isteyin. Yanıtlar, biçimlendirilmiş kod blokları, tablolar ve satır içi kodlarla birlikte kullanıma hazır olarak oluşturulur.

Metin giriş alanı ve akış yanıtı alanı içeren, yapay zeka asistanıyla yapılan bir görüşmeyi gösteren sohbet arayüzü.

Sonuç

Bu iki makale boyunca, Vercel AI SDK'nın tarayıcının yerleşik Prompt API'si ile neler yapabileceğini, ham oluşturma temellerinden cilalı bir akış sohbeti arayüzüne kadar tüm yönleriyle gördünüz.

Use the built-in Prompt API with the Vercel AI SDK (Vercel AI SDK ile yerleşik istem API'sini kullanma) başlıklı makalede, akış olmayan ve akışlı metin oluşturma için generateText() ve streamText()'yi kullanmayı, Output.object() ile yapılandırılmış JSON çıkışı istemeyi ve oluşturma mantığında değişiklik yapmadan çalışma zamanında yerleşik model ile bulut sağlayıcı arasında seçim yapan karma kod yazmayı öğrenmiştiniz.

Bu dokümanda, aynı yapı taşlarını alıp tam bir React kullanıcı arayüzüyle sarmaladınız: görüşme döngüsünü yönetmek için ToolLoopAgent, yanıtları doğrudan tarayıcıda yayınlamak için DirectChatTransport içeren useChat ve Markdown yanıtlarını geldikçe temiz bir şekilde oluşturmak için AI Elements bileşenleri. Tüm bunlar, Prompt API kullanılamadığında otomatik bulut yedeklemesiyle birlikte.

Sonuç olarak, tamamen tarayıcıda çalışan ve arka uç gerektirmeyen iki demo elde edilir: