เผยแพร่: 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:** ซึ่งหมายความว่าหากต้องการแสดงผลมาร์กดาวน์ที่จัดรูปแบบแล้ว
 คุณจะประมวลผลแต่ละก้อนแยกกันด้วยโปรแกรมแยกวิเคราะห์มาร์กดาวน์ไม่ได้
จากไคลเอ็นต์
หากคุณเรียกใช้โมเดลอย่าง Gemma ในไคลเอ็นต์ด้วยเฟรมเวิร์กอย่าง MediaPipe LLM ข้อมูลการสตรีมจะส่งผ่านฟังก์ชัน Callback
เช่น
llmInference.generateResponse(
  inputPrompt,
  (chunk, done) => {
     console.log(chunk);
});
เมื่อใช้ Prompt API
คุณจะได้รับข้อมูลการสตรีมเป็นกลุ่มโดยการวนซ้ำใน
ReadableStream
const languageModel = await LanguageModel.create();
const stream = languageModel.promptStreaming(inputPrompt);
for await (const chunk of stream) {
  console.log(chunk);
}
ขั้นตอนถัดไป
คุณสงสัยไหมว่าจะแสดงข้อมูลที่สตรีมอย่างมีประสิทธิภาพและปลอดภัยได้อย่างไร อ่านแนวทางปฏิบัติแนะนำในการแสดงผลคำตอบของ LLM