Gepubliceerd: 21 januari 2025
Een gestreamd LLM-antwoord bestaat uit gegevens die stapsgewijs en continu worden uitgezonden. Streaminggegevens zien er anders uit dan de server en de client.
Van de server
Om te begrijpen hoe een gestreamd antwoord eruit ziet, vroeg ik Gemini om me een lange grap te vertellen met behulp van het opdrachtregelprogramma curl
. Overweeg de volgende aanroep van de Gemini API. Als u het probeert, zorg er dan voor dat u {GOOGLE_API_KEY}
in de URL vervangt door uw Gemini API-sleutel.
$ curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:streamGenerateContent?alt=sse&key={GOOGLE_API_KEY}" \
-H 'Content-Type: application/json' \
--no-buffer \
-d '{ "contents":[{"parts":[{"text": "Tell me a long T-rex joke, please."}]}]}'
Met dit verzoek wordt de volgende (ingekorte) uitvoer geregistreerd, in gebeurtenisstroomindeling . Elke regel begint met data:
gevolgd door de payload van het bericht. Het concrete formaat is eigenlijk niet belangrijk, het gaat om de stukjes tekst.
//
data: {"candidates":[{"content": {"parts": [{"text": "A T-Rex"}],"role": "model"},
"finishReason": "STOP","index": 0,"safetyRatings": [{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_HATE_SPEECH","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_HARASSMENT","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_DANGEROUS_CONTENT","probability": "NEGLIGIBLE"}]}],
"usageMetadata": {"promptTokenCount": 11,"candidatesTokenCount": 4,"totalTokenCount": 15}}
data: {"candidates": [{"content": {"parts": [{ "text": " walks into a bar and orders a drink. As he sits there, he notices a" }], "role": "model"},
"finishReason": "STOP","index": 0,"safetyRatings": [{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_HATE_SPEECH","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_HARASSMENT","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_DANGEROUS_CONTENT","probability": "NEGLIGIBLE"}]}],
"usageMetadata": {"promptTokenCount": 11,"candidatesTokenCount": 21,"totalTokenCount": 32}}
De eerste payload is JSON. Bekijk de gemarkeerde candidates[0].content.parts[0].text
eens nader:
{
"candidates": [
{
"content": {
"parts": [
{
"text": "A T-Rex"
}
],
"role": "model"
},
"finishReason": "STOP",
"index": 0,
"safetyRatings": [
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HARASSMENT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"probability": "NEGLIGIBLE"
}
]
}
],
"usageMetadata": {
"promptTokenCount": 11,
"candidatesTokenCount": 4,
"totalTokenCount": 15
}
}
Die eerste text
is het begin van Gemini's antwoord. Wanneer u meer text
extraheert, wordt het antwoord door nieuwe regels gescheiden.
Het volgende fragment toont meerdere text
, waarin het uiteindelijke antwoord van het model wordt weergegeven.
"A T-Rex"
" was walking through the prehistoric jungle when he came across a group of Triceratops. "
"\n\n\"Hey, Triceratops!\" the T-Rex roared. \"What are"
" you guys doing?\"\n\nThe Triceratops, a bit nervous, mumbled,
\"Just... just hanging out, you know? Relaxing.\"\n\n\"Well, you"
" guys look pretty relaxed,\" the T-Rex said, eyeing them with a sly grin.
\"Maybe you could give me a hand with something.\"\n\n\"A hand?\""
...
Maar wat gebeurt er als je in plaats van T-rex-grappen het model om iets complexers vraagt? Vraag Gemini bijvoorbeeld om een JavaScript-functie te bedenken om te bepalen of een getal even of oneven is. De text:
brokken zien er iets anders uit.
De uitvoer bevat nu het Markdown- formaat, te beginnen met het JavaScript-codeblok. Het volgende voorbeeld bevat dezelfde voorverwerkingsstappen als voorheen.
"```javascript\nfunction"
" isEven(number) {\n // Check if the number is an integer.\n"
" if (Number.isInteger(number)) {\n // Use the modulo operator"
" (%) to check if the remainder after dividing by 2 is 0.\n return number % 2 === 0; \n } else {\n "
"// Return false if the number is not an integer.\n return false;\n }\n}\n\n// Example usage:\nconsole.log(isEven("
"4)); // Output: true\nconsole.log(isEven(7)); // Output: false\nconsole.log(isEven(3.5)); // Output: false\n```\n\n**Explanation:**\n\n1. **`isEven("
"number)` function:**\n - Takes a single argument `number` representing the number to be checked.\n - Checks if the `number` is an integer using `Number.isInteger()`.\n - If it's an"
...
Om de zaken nog uitdagender te maken, beginnen sommige gemarkeerde items in het ene deel en eindigen ze in het andere. Een deel van de opmaak is genest. In het volgende voorbeeld is de gemarkeerde functie verdeeld over twee regels: **isEven(
and number) function:**
. Gecombineerd is de uitvoer **isEven("number) function:**
. Dit betekent dat als u geformatteerde Markdown wilt uitvoeren, u niet elk deel afzonderlijk kunt verwerken met een Markdown-parser.
Van de klant
Als u modellen zoals Gemma op de client uitvoert met een raamwerk zoals MediaPipe LLM , komen streaminggegevens via een callback-functie.
Bijvoorbeeld:
llmInference.generateResponse(
inputPrompt,
(chunk, done) => {
console.log(chunk);
});
Met de Prompt API krijgt u streaminggegevens in stukjes door een ReadableStream
te doorlopen.
const languageModel = await self.ai.languageModel.create();
const stream = languageModel.promptStreaming(inputPrompt);
for await (const chunk of stream) {
console.log(chunk);
}
Volgende stappen
Vraagt u zich af hoe u gestreamde gegevens efficiënt en veilig kunt weergeven? Lees onze best practices om LLM-antwoorden weer te geven .