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.
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.
Vous pouvez cloner le dépôt GitHub contenant le résultat final ici.
Pour configurer à partir de zéro :
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 :
Pour l'instant, vous pouvez coder en dur ces valeurs ; ils seront tous retirés de nos streams.
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, }; }
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. ); }
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!