เผยแพร่: 21 มกราคม 2025
การตอบกลับ LLM แบบสตรีมประกอบด้วยข้อมูลที่ส่งออกมาอย่างต่อเนื่องและเพิ่มขึ้นเรื่อยๆ ข้อมูลสตรีมมิงจะแตกต่างจากเซิร์ฟเวอร์และไคลเอ็นต์
จากเซิร์ฟเวอร์
เพื่อทำความเข้าใจว่าคำตอบแบบสตรีมมีลักษณะเป็นอย่างไร เราจึงบอกให้ Gemini เล่ามุกตลกยาวๆ โดยใช้เครื่องมือบรรทัดคำสั่ง curl
พิจารณาการเรียก Gemini API ต่อไปนี้ หากต้องการลองใช้ โปรดแทนที่ {GOOGLE_API_KEY}
ใน URL ด้วยคีย์ Gemini API
$ 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."}]}]}'
คําขอนี้จะบันทึกเอาต์พุตต่อไปนี้ (ตัดให้สั้นลง) ในรูปแบบสตรีมเหตุการณ์
แต่ละบรรทัดจะขึ้นต้นด้วย data:
ตามด้วยเพย์โหลดข้อความ รูปแบบที่แน่นอนนั้นไม่สำคัญ สิ่งสำคัญคือกลุ่มข้อความ
//
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}}
เพย์โหลดแรกคือ JSON มาดูรายละเอียดของส่วนไฮไลต์กัน candidates[0].content.parts[0].text
{
"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
}
}
รายการ text
แรกนั้นคือจุดเริ่มต้นของคำตอบของ Gemini เมื่อดึงข้อมูลรายการ text
รายการขึ้นไป ระบบจะแบ่งคำตอบด้วยบรรทัดใหม่
ข้อมูลโค้ดต่อไปนี้แสดงรายการ text
หลายรายการ ซึ่งแสดงคําตอบสุดท้ายจากโมเดล
"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?\""
...
แต่จะเกิดอะไรขึ้นหากคุณถามโมเดลให้พูดเรื่องที่ซับซ้อนขึ้นเล็กน้อยแทนที่จะถามเรื่องมุกตะเรดอน เช่น ขอให้ Gemini สร้างฟังก์ชัน JavaScript เพื่อระบุว่าตัวเลขเป็นคู่หรือคี่ ข้อมูล text:
มีลักษณะแตกต่างออกไปเล็กน้อย
ตอนนี้เอาต์พุตจะมีรูปแบบ Markdown โดยเริ่มจากบล็อกโค้ด JavaScript ตัวอย่างต่อไปนี้มีขั้นตอนก่อนการประมวลผลเดียวกันกับก่อนหน้านี้
"```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"
...
ปัญหายิ่งยากขึ้นเมื่อรายการที่ทำเครื่องหมายบางส่วนเริ่มต้นในข้อมูลหนึ่งแต่จบในข้อมูลอื่น มีการจัดวางมาร์กอัปบางส่วนไว้ซ้อนกัน ในตัวอย่างนี้ ฟังก์ชันที่ไฮไลต์จะแบ่งออกเป็น 2 บรรทัด ได้แก่ **isEven(
และ number) function:**
เอาต์พุตที่ได้คือ
**isEven("number) function:**
ซึ่งหมายความว่าหากต้องการแสดงผล Markdown ที่มีการจัดรูปแบบ คุณจะประมวลผลแต่ละกลุ่มแยกกันด้วยโปรแกรมแยกวิเคราะห์ Markdown ไม่ได้
จากลูกค้า
หากคุณเรียกใช้โมเดลอย่าง Gemma ในไคลเอ็นต์ด้วยเฟรมเวิร์กอย่าง MediaPipe LLM ข้อมูลสตรีมมิงจะส่งผ่านฟังก์ชันการเรียกกลับ
เช่น
llmInference.generateResponse(
inputPrompt,
(chunk, done) => {
console.log(chunk);
});
เมื่อใช้ Prompt API คุณจะได้รับข้อมูลสตรีมมิงเป็นกลุ่มๆ โดยการวนซ้ำผ่าน ReadableStream
const languageModel = await self.ai.languageModel.create();
const stream = languageModel.promptStreaming(inputPrompt);
for await (const chunk of stream) {
console.log(chunk);
}
ขั้นตอนถัดไป
คุณสงสัยไหมว่าจะแสดงผลข้อมูลที่สตรีมอย่างมีประสิทธิภาพและปลอดภัยได้อย่างไร อ่านแนวทางปฏิบัติแนะนำในการแสดงผลคำตอบ LLM