Dipublikasikan: 21 Januari 2025
Respons LLM yang di-streaming terdiri dari data yang dikeluarkan secara bertahap dan terus-menerus. Data streaming terlihat berbeda dari server dan klien.
Dari server
Untuk memahami tampilan respons yang di-streaming, saya meminta Gemini untuk menceritakan
lelucon panjang menggunakan alat command line curl
. Pertimbangkan
panggilan berikut ke Gemini API. Jika Anda mencobanya, pastikan untuk mengganti
{GOOGLE_API_KEY}
di URL dengan kunci Gemini API Anda.
$ 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."}]}]}'
Permintaan ini mencatat output (terpotong) berikut ke dalam log, dalam
format aliran peristiwa.
Setiap baris dimulai dengan data:
, diikuti dengan payload pesan. Format konkret
sebenarnya tidak penting, yang penting adalah bagian teks.
//
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}}
Payload pertama adalah JSON. Pelajari lebih lanjut candidates[0].content.parts[0].text
yang ditandai:
{
"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
}
}
Entri text
pertama tersebut adalah awal respons Gemini. Saat Anda mengekstrak
lebih banyak entri text
, respons akan dibatasi baris baru.
Cuplikan berikut menunjukkan beberapa entri text
, yang menunjukkan respons
akhir dari model.
"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?\""
...
Namun, apa yang terjadi jika Anda meminta model untuk membuat sesuatu
yang sedikit lebih kompleks, bukan lelucon T-rex? Misalnya, minta Gemini untuk membuat fungsi JavaScript
untuk menentukan apakah angka tersebut genap atau ganjil. Potongan text:
terlihat
sedikit berbeda.
Output kini berisi format Markdown, yang dimulai dengan blok kode JavaScript. Contoh berikut menyertakan langkah prapemrosesan yang sama seperti sebelumnya.
"```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"
...
Untuk mempersulit masalah, beberapa item yang diberi markup dimulai di satu bagian
dan diakhiri di bagian lain. Beberapa markup bertingkat. Dalam contoh berikut, fungsi yang ditandai dibagi menjadi dua baris: **isEven(
dan number) function:**
. Jika digabungkan, outputnya adalah
**isEven("number) function:**
. Artinya, jika ingin menghasilkan output Markdown
yang diformat, Anda tidak dapat memproses setiap bagian satu per satu dengan parser Markdown.
Dari klien
Jika Anda menjalankan model seperti Gemma di klien dengan framework seperti MediaPipe LLM, data streaming akan berasal dari fungsi callback.
Contoh:
llmInference.generateResponse(
inputPrompt,
(chunk, done) => {
console.log(chunk);
});
Dengan Prompt API,
Anda mendapatkan data streaming sebagai bagian dengan melakukan iterasi pada
ReadableStream
.
const languageModel = await self.ai.languageModel.create();
const stream = languageModel.promptStreaming(inputPrompt);
for await (const chunk of stream) {
console.log(chunk);
}
Langkah berikutnya
Ingin tahu cara merender data streaming dengan performa tinggi dan aman? Baca praktik terbaik untuk merender respons LLM.