ホームページ  >  記事  >  ウェブフロントエンド  >  Vercel AI SDK を使用した複数の並列 AI ストリーム

Vercel AI SDK を使用した複数の並列 AI ストリーム

WBOY
WBOYオリジナル
2024-07-17 17:43:02422ブラウズ

Multiple Parallel AI Streams with the Vercel AI SDK

Vercel AI SDK を使用すると、OpenAI、Anthropic などの LLM API とのやり取りが簡単になり、データをストリーミングして、Web アプリの読み込み時に迅速に表示できるようになります。この記事では、複数のプロンプトを同時に実行し、その結果を並行して確認する方法を学びます。

TL;DR: GitHub リポジトリはここにあります。

なぜこれをやりたいのでしょうか?

Web アプリでは、複数のデータ取得リクエストを同時に実行することは珍しくありません。たとえば、架空のブログ システムでは、ダッシュボード インターフェイスが読み込まれるときに、ユーザーのプロフィール データ、ユーザーが作成した投稿、およびお気に入りに登録した他のユーザーの投稿をすべて同時に取得したい場合があります。

同じダッシュボードが同時に OpenAI にリクエストを行っていた場合、ユーザーのプロフィールを改善するためのヒントと最新の投稿の分析を同時に OpenAI に尋ねることができます。理論的には、必要に応じて数十の AI リクエストを並行して使用し (まったく異なるプラットフォームやモデルからであっても)、情報の分析、コンテンツの生成、その他あらゆる種類のタスクを同時に実行できます。

インストールとセットアップ

ここで、最終結果を含む GitHub リポジトリのクローンを作成できます。

最初からセットアップするには:

  1. Next.js App Router クイックスタートに従ってください。 基本のみです。アプリを生成し、依存関係をインストールし、OpenAI API キーを追加します。
  2. shadcn/ui をインストールして設定します。

基本的なUIの設定

すべての作業を行うメインコンポーネントには、フォームと出力用のいくつかのコンテナが含まれます。いくつかの基本的な shadcn-ui コンポーネントを使用すると、フォームは次のようになります:

export function GenerationForm() {
    // State and other info will be defined here...

        return (
        <form onSubmit={onSubmit} className="flex flex-col gap-3 w-full">
          <div className="inline-block mb-4 w-full flex flex-row gap-1">
            <Button type="submit">Generate News & Weather</Button>
          </div>

          {isGenerating ? (
            <div className="flex flex-row w-full justify-center items-center p-4 transition-all">
              <Spinner className="h-6 w-6 text-slate-900" />
            </div>
          ) : null}

          <h3 className="font-bold">Historical Weather</h3>
          <div className="mt-4 mb-8 p-4 rounded-md shadow-md bg-blue-100">
            {weather ? weather : null}
          </div>

          <h4 className="font-bold">Historical News</h4>
          <div className="mt-4 p-4 rounded-md shadow-md bg-green-100">{news ? news : null}</div>
        </form>
      )
}

ここにはいくつかの内容があることがわかります:

  • フォーム
  • 読み込みアニメーション (およびそれを表示/非表示にするための isGenerating フラグ)
  • 天気コンテンツをレンダリングするためのコンテナ
  • ニュース コンテンツをレンダリングするためのコンテナ

現時点では、これらの値をハードコーディングできます。それらはすべてストリームから取得されます。

React サーバー コンポーネント (RSC) のセットアップ

streamAnswer サーバー アクションは、ストリームの作成と更新の作業を実行します。

アクションの構造は次のとおりです:

export async function streamAnswer(question: string) {
    // Booleans for indicating whether each stream is currently streaming
  const isGeneratingStream1 = createStreamableValue(true);
  const isGeneratingStream2 = createStreamableValue(true);

  // The current stream values
  const weatherStream = createStreamableValue("");
  const newsStream = createStreamableValue("");

    // Create the first stream. Notice that we don't use await here, so that we
    //  don't block the rest of this function from running.
    streamText({
        // ... params, including the LLM prompt
  }).then(async (result) => {
          // Read from the async iterator. Set the stream value to each new word
          //  received.
      for await (const value of result.textStream) {
        weatherStream.update(value || "");
      }
    } finally {
        // Set isGenerating to false, and close that stream.
      isGeneratingStream1.update(false);
      isGeneratingStream1.done();

      // Close the given stream so the request doesn't hang.
      weatherStream.done();
    }
  });

  // Same thing for the second stream.
    streamText({
        // ... params
  }).then(async (result) => {
      // ...
  })

  // Return any streams we want to read on the client.
  return {
    isGeneratingStream1: isGeneratingStream1.value,
    isGeneratingStream2: isGeneratingStream2.value,
    weatherStream: weatherStream.value,
    newsStream: newsStream.value,
  };
}

クライアントコードの記述

フォームの onSubmit ハンドラーがここでのすべての作業を行います。その仕組みの内訳は次のとおりです:

"use client";

import { SyntheticEvent, useState } from "react";
import { Button } from "./ui/button";
import { readStreamableValue, useUIState } from "ai/rsc";
import { streamAnswer } from "@/app/actions";
import { Spinner } from "./svgs/Spinner";

export function GenerationForm() {
    // State for loading flags
  const [isGeneratingStream1, setIsGeneratingStream1] = useState<boolean>(false);
  const [isGeneratingStream2, setIsGeneratingStream2] = useState<boolean>(false);

  // State for the LLM output streams
  const [weather, setWeather] = useState<string>("");
  const [news, setNews] = useState<string>("");

  // We'll hide the loader when both streams are done.
  const isGenerating = isGeneratingStream1 || isGeneratingStream2;

  async function onSubmit(e: SyntheticEvent) {
    e.preventDefault();

    // Clear previous results.
    setNews("");
    setWeather("");

        // Call the server action. The returned object will have all the streams in it.
    const result = await streamAnswer(question);

    // Translate each stream into an async iterator so we can loop through
    //  the values as they are generated.
    const isGeneratingStream1 = readStreamableValue(result.isGeneratingStream1);
    const isGeneratingStream2 = readStreamableValue(result.isGeneratingStream2);
    const weatherStream = readStreamableValue(result.weatherStream);
    const newsStream = readStreamableValue(result.newsStream);

        // Iterate through each stream, putting its values into state one by one.
        //  Notice the IIFEs again! As on the server, these allow us to prevent blocking
        //   the function, so that we can run these iterators in parallel.
    (async () => {
      for await (const value of isGeneratingStream1) {
        if (value != null) {
          setIsGeneratingStream1(value);
        }
      }
    })();

    (async () => {
      for await (const value of isGeneratingStream2) {
        if (value != null) {
          setIsGeneratingStream2(value);
        }
      }
    })();

    (async () => {
      for await (const value of weatherStream) {
        setWeather((existing) => (existing + value) as string);
      }
    })();

    (async () => {
      for await (const value of newsStream) {
        setNews((existing) => (existing + value) as string);
      }
    })();
  }

  return (
    // ... The form code from before.
  );
}

その他の楽しいこと

  • streamObject() を使用したテキストの代わりに構造化された JSON データをストリーミングする
  • さらに多くのことを並行してストリーミングします
  • さまざまな API から同時にストリーミング
  • 比較のために同じプロンプトを使用して異なるモデルをストリーミングする (例: Cohere、Anthropic、Gemini など)
  • サーバーから UI をストリーミングする ( createStreamableUI() を使用)

詳細な資料とリンク

  • サーバーのアクションとミューテーション
  • Vercel AI SDK
  • streamText() API ドキュメント
  • Next.js App Router クイックスタート

以上がVercel AI SDK を使用した複数の並列 AI ストリームの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。