Cách LLM truyền trực tuyến phản hồi

Ngày phát hành: 21 tháng 1 năm 2025

Phản hồi LLM theo luồng bao gồm dữ liệu được phát ra liên tục và tăng dần. Dữ liệu truyền trực tuyến khác với máy chủ và ứng dụng.

Từ máy chủ

Để hiểu rõ hơn về câu trả lời theo luồng, tôi đã yêu cầu Gemini kể cho tôi một câu chuyện cười dài bằng công cụ dòng lệnh curl. Hãy xem xét lệnh gọi sau đây đến API Gemini. Nếu bạn thử, hãy nhớ thay thế {GOOGLE_API_KEY} trong URL bằng khoá API Gemini.

$ 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."}]}]}'

Yêu cầu này ghi lại kết quả (bị cắt bớt) sau đây, ở định dạng luồng sự kiện. Mỗi dòng bắt đầu bằng data:, theo sau là tải trọng thông báo. Định dạng cụ thể không thực sự quan trọng, điều quan trọng là các đoạn văn bản.

//
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}}
Sau khi thực thi lệnh, các phần kết quả sẽ được truyền vào.

Gói dữ liệu đầu tiên là JSON. Hãy xem kỹ hơn về candidates[0].content.parts[0].text được làm nổi bật:

{
  "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
  }
}

Mục nhập text đầu tiên đó là phần đầu của câu trả lời của Gemini. Khi bạn trích xuất thêm các mục text, phản hồi sẽ được phân tách bằng dòng mới.

Đoạn mã sau đây cho thấy nhiều mục nhập text, cho thấy phản hồi cuối cùng từ mô hình.

"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?\""

...

Nhưng điều gì sẽ xảy ra nếu thay vì yêu cầu mô hình tạo ra các câu chuyện cười về T-rex, bạn yêu cầu mô hình tạo ra một nội dung phức tạp hơn một chút. Ví dụ: yêu cầu Gemini tạo một hàm JavaScript để xác định xem một số là số chẵn hay số lẻ. Các đoạn text: trông có chút khác biệt.

Kết quả hiện chứa định dạng Markdown, bắt đầu bằng khối mã JavaScript. Mẫu sau đây bao gồm các bước xử lý trước giống như trước đây.

"```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"

...

Để làm cho vấn đề trở nên khó khăn hơn, một số mục được đánh dấu bắt đầu trong một đoạn và kết thúc trong một đoạn khác. Một số mã đánh dấu được lồng. Trong ví dụ sau, hàm được làm nổi bật được chia thành hai dòng: **isEven(number) function:**. Kết hợp, kết quả là **isEven("number) function:**. Điều này có nghĩa là nếu muốn xuất Markdown được định dạng, bạn không thể chỉ xử lý từng phần riêng lẻ bằng trình phân tích cú pháp Markdown.

Từ ứng dụng

Nếu bạn chạy các mô hình như Gemma trên ứng dụng bằng một khung như MediaPipe LLM, dữ liệu truyền trực tuyến sẽ đi qua một hàm gọi lại.

Ví dụ:

llmInference.generateResponse(
  inputPrompt,
  (chunk, done) => {
     console.log(chunk);
});

Với Prompt API, bạn sẽ nhận được dữ liệu truyền trực tuyến dưới dạng các đoạn bằng cách lặp lại trên ReadableStream.

const languageModel = await self.ai.languageModel.create();
const stream = languageModel.promptStreaming(inputPrompt);
for await (const chunk of stream) {
  console.log(chunk);
}

Các bước tiếp theo

Bạn có thắc mắc về cách hiển thị dữ liệu phát trực tuyến một cách hiệu quả và an toàn không? Hãy đọc các phương pháp hay nhất để hiển thị phản hồi LLM.