Maison >interface Web >js tutoriel >Plusieurs flux d'IA parallèles avec le SDK Vercel AI

Plusieurs flux d'IA parallèles avec le SDK Vercel AI

WBOY
WBOYoriginal
2024-07-17 17:43:02458parcourir

Multiple Parallel AI Streams with the Vercel AI SDK

Le SDK Vercel AI facilite l'interaction avec les API LLM comme OpenAI, Anthropic, etc., et diffuse les données afin qu'elles apparaissent rapidement dans votre application Web lors de leur chargement. Dans cet article, nous apprendrons comment exécuter plusieurs invites en même temps et voir leurs résultats en parallèle.

TL;DR : GitHub Repo est ici.

Pourquoi voudrais-je faire ça ?

Il n'est pas rare dans une application Web de vouloir exécuter plusieurs requêtes de récupération de données en même temps. Par exemple, dans un système de blog hypothétique, lorsque l’interface du tableau de bord se charge, nous pourrions vouloir récupérer les données de profil de l’utilisateur, les publications qu’il a créées et les publications d’autres utilisateurs qu’il a mises en favoris en même temps.

Si le même tableau de bord faisait des requêtes à OpenAI en même temps, nous pourrions vouloir simultanément demander à OpenAI des conseils pour améliorer le profil de l'utilisateur et une analyse de son dernier message en même temps. En théorie, nous pourrions utiliser des dizaines de requêtes d'IA en parallèle si nous le voulions (même à partir de plates-formes et de modèles complètement différents), analyser des informations, générer du contenu et effectuer tous types d'autres tâches en même temps.

Installation et configuration

Vous pouvez cloner le dépôt GitHub contenant le résultat final ici.

Pour configurer à partir de zéro :

  1. Suivez le Démarrage rapide du routeur d'application Next.js. Juste les bases ; générez l'application, installez les dépendances et ajoutez votre clé API OpenAI.
  2. Installez et configurez shadcn/ui.

Configuration de l'interface utilisateur de base

Le composant principal qui fait tout le travail contiendra un formulaire et quelques conteneurs pour la sortie. En utilisant certains composants de base shadcn-ui, le formulaire ressemblera à ceci :

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>
      )
}

Vous pouvez voir que nous avons quelques éléments ici :

  • Un formulaire
  • Une animation de chargement (et un indicateur isGenerating pour l'afficher/masquer)
  • Un conteneur pour le rendu du contenu météo
  • Un conteneur pour le rendu du contenu d'actualité

Pour l'instant, vous pouvez coder en dur ces valeurs ; ils seront tous retirés de nos streams.

Configuration des composants du serveur React (RSC)

L'action du serveur streamAnswer est ce qui fera le travail de création et de mise à jour de nos flux.

La structure de l'action est la suivante :

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,
  };
}

Ecrire le code client

Le gestionnaire onSubmit du formulaire fera tout le travail ici. Voici le détail de son fonctionnement :

"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.
  );
}

Autres choses amusantes à essayer

  • Diffusion de données JSON structurées au lieu de texte à l'aide de streamObject()
  • Diffuser beaucoup plus de choses en parallèle
  • Diffusion simultanée à partir de différentes API
  • Diffusion en continu de différents modèles avec les mêmes invites de comparaison (par exemple, Cohere, Anthropic, Gemini, etc.)
  • Diffusion de l'interface utilisateur depuis le serveur (à l'aide de createStreamableUI() )

Lectures complémentaires et liens

  • Actions et mutations du serveur
  • SDK Vercel IA
  • Documentation de l'API streamText()
  • Démarrage rapide du routeur d'application Next.js

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn