Truyền trực tuyến tin nhắn bằng API Google Chat

Hướng dẫn này giải thích cách truyền trực tuyến tin nhắn theo từng phần trong Google Chat bằng cách sử dụng tài nguyên StreamMessage trong API Chat. Tính năng truyền trực tuyến cho phép ứng dụng Google Chat hiển thị các chỉ báo tiến trình trực tiếp, các bước suy luận trung gian hoặc thực thi công cụ và các phản hồi văn bản gia tăng trước khi hoàn tất tin nhắn.

Tính năng truyền trực tuyến được thiết kế cho các ứng dụng Chat dựa trên AI tạo sinh hoặc các quy trình làm việc phụ trợ chạy trong thời gian dài, trong đó việc tạo phản hồi hoàn chỉnh mất vài giây hoặc vài phút.

Cách hoạt động của tính năng truyền tin nhắn trực tuyến

Tin nhắn truyền trực tuyến sẽ trải qua trạng thái truyền trực tuyến đang hoạt động trước khi trở thành tài nguyên Message chuẩn trong một không gian:

  1. Bắt đầu một chuỗi tin nhắn (spaces.streamMessages.create): Ứng dụng Chat của bạn tạo một StreamMessage trong một không gian theo chuỗi, tham chiếu đến tin nhắn tương tác của người dùng mà ứng dụng đang trả lời. API này hiển thị chỉ báo tiến trình trực tiếp trong chuỗi tin nhắn và trả về tên tài nguyên StreamMessage và nextChunkToken ban đầu.
  2. Cập nhật luồng (appendStreamMessageContent hoặc patch): Khi tạo kết quả, ứng dụng Chat sẽ gửi các bản cập nhật tuần tự bằng nextChunkToken mới nhất:
    • Gọi spaces.streamMessages.appendStreamMessageContent để nối các đoạn văn bản mới, thêm các bước trung gian, thêm trích dẫn cùng dòng hoặc cập nhật progressText.
    • Gọi spaces.streamMessages.patch để thay thế các đoạn nội dung được truyền trực tuyến hoặc cập nhật progressText tại chỗ.
  3. Hoàn tất hoặc loại bỏ luồng (commit hoặc delete):
    • Gọi spaces.streamMessages.commit để hoàn tất luồng và chuyển đổi luồng đó thành tài nguyên Message vĩnh viễn. Bạn cũng có thể đính kèm các phần tử chỉ cam kết như thẻ, tiện ích phụ kiện và nguồn chân trang khi cam kết.
    • Gọi spaces.streamMessages.delete để loại bỏ một sự kiện phát trực tiếp đang hoạt động mà không lưu tin nhắn trong không gian.

Khi một luồng đang hoạt động, chỉ người gửi tin nhắn được tham chiếu (referencedMessage) đang được truyền trực tuyến mới có thể nhấp vào Dừng để huỷ quá trình tạo. Chat sẽ gửi một sự kiện lệnh hệ thống huỷ đến ứng dụng Chat của bạn để bạn có thể dừng công việc phụ trợ và cam kết kết quả một phần hoặc xoá luồng.

Điều kiện tiên quyết

Để truyền trực tuyến tin nhắn bằng API Chat, hãy hoàn tất các điều kiện tiên quyết sau:

  • Tài khoản Google Workspace dành cho Doanh nghiệp hoặc Doanh nghiệp lớn có quyền truy cập vào Google Chat.
  • Thiết lập môi trường:
  • Thêm ứng dụng Chat vào một không gian Chat theo chuỗi (hoặc tin nhắn trực tiếp 1:1 đã chứa ít nhất một tin nhắn) và lấy tên tài nguyên của một tin nhắn có thể truy cập để trả lời (spaces/SPACE_ID/messages/MESSAGE_ID).
  • Đối với các mẫu Python, hãy cài đặt Thư viện ứng dụng API của Google cho Python và google-auth, sau đó khởi động ứng dụng dịch vụ API Chat:

    pip install google-api-python-client google-auth
    from google.oauth2 import service_account
    from googleapiclient.discovery import build
    
    SCOPES = ["https://www.googleapis.com/auth/chat.bot"]
    creds = service_account.Credentials.from_service_account_file(
        "credentials.json", scopes=SCOPES
    )
    chat = build("chat", "v1", credentials=creds)
    

Bắt đầu phát trực tiếp

Để bắt đầu truyền trực tuyến câu trả lời, hãy gọi spaces.streamMessages.create và truyền StreamMessage trong nội dung yêu cầu:

  • referencedMessage (Bắt buộc): Tên tài nguyên của tin nhắn đã kích hoạt phản hồi, được định dạng là spaces/{space}/messages/{message}. Tin nhắn được tham chiếu phải thuộc cùng một không gian và ứng dụng Chat của bạn phải truy cập được. Chat sẽ đăng câu trả lời được truyền trực tuyến trong cùng một chuỗi với referencedMessage.
  • progressText (Bắt buộc): Nhãn trạng thái văn bản thuần túy không trống (tối đa 250 ký tự) xuất hiện bên cạnh chỉ báo tiến trình, chẳng hạn như "Thinking...".
  • markupSyntax (Không bắt buộc): Cú pháp định dạng văn bản cho tin nhắn, chẳng hạn như MARKUP_SYNTAX_MARKDOWN hoặc MARKUP_SYNTAX_CHAT (mặc định là cú pháp đã định cấu hình của ứng dụng Chat nếu MARKUP_SYNTAX_UNSPECIFIED). Đặt MARKUP_SYNTAX_MARKDOWN nếu bạn dự định thêm trích dẫn cùng dòng.
  • streamContentChunks (Không bắt buộc): Các đoạn text, step hoặc citation ban đầu để hiển thị ngay lập tức.

Lưu cả name (spaces/{space}/streamMessages/{stream_message}) và nextChunkToken từ phản hồi cho các lệnh gọi tiếp theo.

HTTP

POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "referencedMessage": "spaces/SPACE_ID/messages/MESSAGE_ID",
  "progressText": "Thinking...",
  "markupSyntax": "MARKUP_SYNTAX_MARKDOWN"
}

Python

stream = (
    chat.spaces()
    .streamMessages()
    .create(
        parent="spaces/SPACE_ID",
        body={
            "referencedMessage": "spaces/SPACE_ID/messages/MESSAGE_ID",
            "progressText": "Thinking...",
            "markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
        },
    )
    .execute()
)

stream_name = stream["name"]
chunk_token = stream["nextChunkToken"]

Phương thức này trả về một tài nguyên StreamMessage:

{
  "name": "spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID",
  "progressText": "Thinking...",
  "markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
  "nextChunkToken": "INITIAL_CHUNK_TOKEN"
}

Thêm các đoạn nội dung

Khi ứng dụng Chat của bạn tạo ra các bước trung gian hoặc mã thông báo văn bản, hãy gọi spaces.streamMessages.appendStreamMessageContent:

  • Truyền nextChunkToken mới nhất vào chunkToken. Mỗi lệnh gọi thành công sẽ trả về một nextChunkToken mới mà bạn phải chuyển vào yêu cầu cập nhật hoặc cam kết tiếp theo.
  • Các đoạn text nối trực tiếp vào văn bản đã truyền trực tuyến trước đó. Thêm dấu cách hoặc dòng mới nếu cần giữa các đoạn.
  • step đoạn được thêm vào danh sách các bước trung gian xuất hiện trên thông báo.
  • progressText là không bắt buộc. Nếu bạn bỏ qua progressText hoặc đặt thành "", Chat sẽ tiếp tục hiển thị progressText đã đặt trước đó.

HTTP

POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID:appendStreamMessageContent HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "chunkToken": "INITIAL_CHUNK_TOKEN",
  "progressText": "Searching documentation...",
  "streamContentChunks": [
    {
      "step": {
        "title": "Search knowledge base",
        "description": "Found 3 matching articles."
      }
    },
    {
      "text": "See <chat-citation data-id=\"c1\">guide</chat-citation>:\n"
    },
    {
      "citation": {
        "id": "c1",
        "citedSources": [
          {
            "title": "System Architecture Guide",
            "uri": "https://example.com/docs/arch"
          }
        ]
      }
    }
  ]
}

Python

stream = (
    chat.spaces()
    .streamMessages()
    .appendStreamMessageContent(
        name=stream_name,
        body={
            "chunkToken": chunk_token,
            "progressText": "Searching documentation...",
            "streamContentChunks": [
                {
                    "step": {
                        "title": "Search knowledge base",
                        "description": "Found 3 matching articles.",
                    }
                },
                {
                    "text": (
                        'See <chat-citation data-id="c1">'
                        "guide</chat-citation>:\n"
                    )
                },
                {
                    "citation": {
                        "id": "c1",
                        "citedSources": [
                            {
                                "title": "System Architecture Guide",
                                "uri": "https://example.com/docs/arch",
                            }
                        ],
                    }
                },
            ],
        },
    )
    .execute()
)

chunk_token = stream["nextChunkToken"]

Thay thế nội dung được truyền trực tuyến hoặc văn bản tiến trình

Nếu bạn cần viết lại nội dung đã truyền trực tuyến trước đó (ví dụ: sau khi chạy bộ lọc an toàn hoặc hậu xử lý) hoặc chỉ cập nhật progressText mà không thêm các đoạn, hãy gọi spaces.streamMessages.patch:

  • Đặt updateMask thành danh sách các trường được phân tách bằng dấu phẩy để cập nhật: progressText, streamContentChunks hoặc progressText,streamContentChunks.
  • Đưa nextChunkToken mới nhất vào nội dung yêu cầu StreamMessage và ghi lại nextChunkToken mới được trả về trong phản hồi.
  • Khi streamContentChunks ở chế độ updateMask, các đoạn được cung cấp sẽ thay thế nội dung đã truyền trực tuyến trước đó thay vì thêm vào nội dung đó.
  • Khi progressText ở updateMask, progressText phải là văn bản thuần túy không trống, tối đa 250 ký tự.

HTTP

PATCH https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID?updateMask=progressText,streamContentChunks HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "nextChunkToken": "LATEST_CHUNK_TOKEN",
  "progressText": "Formatting response...",
  "streamContentChunks": [
    {
      "text": "Updated response text."
    }
  ]
}

Python

stream = (
    chat.spaces()
    .streamMessages()
    .patch(
        name=stream_name,
        updateMask="progressText,streamContentChunks",
        body={
            "nextChunkToken": chunk_token,
            "progressText": "Formatting response...",
            "streamContentChunks": [
                {"text": "Updated response text."}
            ],
        },
    )
    .execute()
)

chunk_token = stream["nextChunkToken"]

Hoàn tất một sự kiện phát trực tiếp

Khi quá trình tạo hoàn tất, hãy gọi spaces.streamMessages.commit bằng chunkToken mới nhất để hoàn tất luồng thành tài nguyên Message vĩnh viễn:

  • Bạn có thể tuỳ ý thêm streamContentChunks cuối cùng vào yêu cầu cam kết, bao gồm cả các loại đoạn chỉ cam kết (card, accessoryWidget và citedSource).
  • Nếu đã truyền trực tuyến nội dung không trống trong các lệnh gọi trước đó, bạn có thể bỏ qua streamContentChunks khi cam kết. Thông báo cam kết cuối cùng không được để trống.

HTTP

POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID:commit HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "chunkToken": "LATEST_CHUNK_TOKEN",
  "streamContentChunks": [
    {
      "text": "All checks passed."
    },
    {
      "citedSource": {
        "title": "Deployment Runbook",
        "uri": "https://example.com/docs/runbook"
      }
    },
    {
      "card": {
        "cardId": "details-card",
        "card": {
          "header": {
            "title": "Build Status"
          },
          "sections": [
            {
              "widgets": [
                {
                  "textParagraph": {
                    "text": "All 42 checks succeeded."
                  }
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

Python

status_card = {
    "cardId": "details-card",
    "card": {
        "header": {"title": "Build Status"},
        "sections": [
            {
                "widgets": [
                    {"textParagraph": {"text": "All 42 checks succeeded."}}
                ]
            }
        ],
    },
}

commit_response = (
    chat.spaces()
    .streamMessages()
    .commit(
        name=stream_name,
        body={
            "chunkToken": chunk_token,
            "streamContentChunks": [
                {"text": "All checks passed."},
                {
                    "citedSource": {
                        "title": "Deployment Runbook",
                        "uri": "https://example.com/docs/runbook",
                    }
                },
                {"card": status_card},
            ],
        },
    )
    .execute()
)

final_message = commit_response["message"]
print(f"Committed message: {final_message['name']}")

Huỷ một luồng đang hoạt động

Nếu xảy ra lỗi không thể khôi phục hoặc người dùng huỷ quá trình tạo và bạn không muốn để lại một phần câu trả lời trong chuỗi, hãy gọi spaces.streamMessages.delete:

HTTP

DELETE https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID HTTP/1.1
Authorization: Bearer ACCESS_TOKEN

Python

chat.spaces().streamMessages().delete(name=stream_name).execute()

Sau khi một luồng được cam kết vào Message, hãy gọi spaces.messages.delete nếu bạn cần xoá thông báo đã hoàn tất.

Các loại đoạn nội dung được hỗ trợ

Mỗi mục nhập trong streamContentChunks chỉ định một trường trong hợp nhất StreamContentChunk. Một số loại đoạn có thể được gửi trong suốt luồng, trong khi một số loại khác chỉ được chấp nhận trong spaces.streamMessages.commit:

Trường đoạn Các phương thức được hỗ trợ Trường Message cuối cùng Ghi chú
text create, appendStreamMessageContent, patch, commit text Văn bản gia tăng. Được nối vào ngày appendStreamMessageContent; được thay thế vào ngày patch. Văn bản cuối cùng được xác nhận không được vượt quá 32.000 byte.
step create, appendStreamMessageContent, patch, commit elements.steps.steps[] Bước xử lý trung gian với title văn bản thuần tuý (bắt buộc) và description không bắt buộc.
citation create, appendStreamMessageContent, patch, commit elements.citations[] Citation cùng dòng với id gồm chữ và số (dưới 63 ký tự, cho phép dấu gạch dưới) và citedSources. Yêu cầu thẻ markupSyntax: "MARKUP_SYNTAX_MARKDOWN" và thẻ <chat-citation data-id="ID">...</chat-citation> tương ứng trong text. Việc gửi citation có id hiện có sẽ ghi đè trích dẫn trước đó.
card Chỉ commit cardsV2[] Thẻ CardWithId tương tác hoặc cung cấp thông tin.
accessoryWidget Chỉ commit accessoryWidgets[] AccessoryWidget nút hiển thị bên dưới thông báo, chẳng hạn như nút phản hồi.
citedSource Chỉ commit elements.citedSources[] CitedSource đường liên kết ở chân trang xuất hiện bên dưới thông báo mà không có thẻ trích dẫn cùng dòng.

Xử lý việc huỷ luồng do người dùng bắt đầu

Khi một luồng đang hoạt động, chỉ người gửi tin nhắn được tham chiếu (referencedMessage) đang được truyền trực tuyến mới có thể nhấp vào Dừng trên tin nhắn truyền trực tuyến. Khi người dùng dừng một sự kiện phát trực tiếp, Chat sẽ gửi một sự kiện tương tác lệnh hệ thống chứa cancelStream.streamMessage (tên tài nguyên spaces/{space}/streamMessages/{stream_message} của sự kiện phát trực tiếp đã dừng) đến điểm cuối của ứng dụng Chat.

Cấu trúc của tải trọng JSON đến phụ thuộc vào việc bạn xây dựng ứng dụng Chat dưới dạng tiện ích bổ sung của Google Workspace hay sử dụng các sự kiện tương tác của API Chat:

Tiện ích bổ sung cho Google Workspace

{
  "commonEventObject": {
    "hostApp": "CHAT"
  },
  "chat": {
    "user": {
      "name": "users/USER_ID"
    },
    "space": {
      "name": "spaces/SPACE_ID"
    },
    "systemCommandPayload": {
      "systemCommandMetadata": {
        "cancelStream": {
          "streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
        }
      }
    }
  }
}

Google Chat API

{
  "type": "SYSTEM_COMMAND",
  "space": {
    "name": "spaces/SPACE_ID"
  },
  "user": {
    "name": "users/USER_ID"
  },
  "systemCommandMetadata": {
    "cancelStream": {
      "streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
    }
  }
}

Khi ứng dụng Chat của bạn nhận được một sự kiện cancelStream:

  1. Báo hiệu cho trình thực thi trong nền ngừng tạo thêm mã thông báo hoặc chạy các công cụ hạ nguồn cho streamMessage.
  2. Giải quyết luồng đang hoạt động bằng một trong các phương pháp sau:
    • Giữ lại kết quả một phần: Gọi spaces.streamMessages.commit bằng chunkToken mới nhất (tuỳ ý thêm ghi chú như "\n\n_Generation stopped._").
    • Huỷ thư trả lời: Gọi spaces.streamMessages.delete để không lưu nội dung nào vào chuỗi tin nhắn.
  3. Trả về phản hồi JSON 200 OK HTTP trống ({}) từ trình xử lý sự kiện tương tác.

Các điểm hạn chế

Tính năng truyền trực tuyến tin nhắn có các hạn chế sau:

  • Phạm vi cung cấp trên nền tảng: Tính năng truyền trực tuyến tin nhắn chỉ có trong Chat trên web. Chúng tôi sẽ sớm hỗ trợ Android và iOS.
  • Hết thời gian chờ của luồng: Một StreamMessage đang hoạt động sẽ hết thời gian chờ sau 10 phút kể từ khi tạo. Ứng dụng Chat của bạn phải gọi spaces.streamMessages.commit hoặc spaces.streamMessages.delete trong vòng 10 phút kể từ khi bắt đầu luồng.