एलएलएम, जवाबों को कैसे स्ट्रीम करते हैं

पब्लिश किया गया: 21 जनवरी, 2025

स्ट्रीम किए गए एलएलएम के जवाब में, डेटा को लगातार और धीरे-धीरे दिखाया जाता है. स्ट्रीम किया जा रहा डेटा, सर्वर और क्लाइंट पर अलग-अलग दिखता है.

सर्वर से

स्ट्रीम किए गए जवाब को समझने के लिए, मैंने Gemini को कमांड लाइन टूल curl का इस्तेमाल करके, एक लंबा चुटकुला सुनाने के लिए कहा. Gemini API को कॉल करने के लिए, यहां दिया गया उदाहरण देखें. अगर आपको इसे आज़माना है, तो यूआरएल में मौजूद {GOOGLE_API_KEY} को अपनी 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: के चंक थोड़े अलग दिखते हैं.

आउटपुट में अब मार्कडाउन फ़ॉर्मैट है. यह 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"

...

समस्या को और ज़्यादा मुश्किल बनाने के लिए, मार्क किए गए कुछ आइटम एक चंक में शुरू होते हैं और दूसरे में खत्म होते हैं. कुछ मार्कअप नेस्ट किए गए हैं. नीचे दिए गए उदाहरण में, हाइलाइट किए गए फ़ंक्शन को दो लाइनों में बांटा गया है: **isEven( और number) function:**. दोनों को मिलाकर, आउटपुट **isEven("number) function:** है. इसका मतलब है कि अगर आपको फ़ॉर्मैट किए गए Markdown का आउटपुट चाहिए, तो Markdown पार्सर की मदद से हर चंक को अलग-अलग प्रोसेस नहीं किया जा सकता.

क्लाइंट से

अगर आपको क्लाइंट पर MediaPipe LLM जैसे फ़्रेमवर्क के साथ Gemma जैसे मॉडल चलाने हैं, तो स्ट्रीमिंग डेटा, कॉलबैक फ़ंक्शन के ज़रिए मिलता है.

उदाहरण के लिए:

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);
}

अगले चरण

क्या आपको यह जानना है कि स्ट्रीम किए गए डेटा को सुरक्षित तरीके से और बेहतर परफ़ॉर्मेंस के साथ कैसे रेंडर किया जाए? एलएलएम से मिले जवाबों को रेंडर करने के सबसे सही तरीके पढ़ें.