สตรีมข้อความโดยใช้ Google Chat API

คู่มือนี้จะอธิบายวิธีสตรีมข้อความแบบเพิ่มขึ้นทีละรายการใน Google Chat โดยใช้ทรัพยากร StreamMessage ใน Chat API การสตรีมช่วยให้แอป Google Chat แสดงตัวบ่งชี้ความคืบหน้าแบบเรียลไทม์ ขั้นตอนการให้เหตุผลหรือการเรียกใช้เครื่องมือระดับกลาง และการตอบกลับข้อความแบบเพิ่มทีละส่วนก่อนที่จะสรุปข้อความ

การสตรีมออกแบบมาสำหรับแอป Chat ที่ทำงานด้วย Generative AI หรือเวิร์กโฟลว์แบ็กเอนด์ที่ใช้เวลานาน ซึ่งการสร้างคำตอบที่สมบูรณ์จะใช้เวลาหลายวินาทีหรือหลายนาที

วิธีการทำงานของสตรีมข้อความ

ข้อความสตรีมมิงจะดำเนินไปตามสถานะสตรีมมิงที่ใช้งานอยู่ก่อนที่จะกลายเป็นทรัพยากร Message มาตรฐานในพื้นที่ทำงาน ดังนี้

  1. เริ่มสตรีม (spaces.streamMessages.create): แอปใน Chat จะสร้าง StreamMessage ในพื้นที่ทำงานแบบแยกชุดข้อความ โดยอ้างอิงข้อความการโต้ตอบของผู้ใช้ที่แอปตอบกลับ API จะแสดงสัญญาณบอกสถานะความคืบหน้าแบบเรียลไทม์ในชุดข้อความ และแสดงผลชื่อทรัพยากร StreamMessage และ nextChunkToken เริ่มต้น
  2. อัปเดตสตรีม (appendStreamMessageContent หรือ patch): เมื่อแอปใน Chat สร้างเอาต์พุต แอปจะส่งการอัปเดตตามลำดับโดยใช้ nextChunkToken ล่าสุด ดังนี้
    • เรียกใช้ spaces.streamMessages.appendStreamMessageContent เพื่อเชื่อมโยงกลุ่มข้อความใหม่ เพิ่มขั้นตอนกลาง เพิ่มการอ้างอิงในบรรทัด หรืออัปเดต progressText
    • เรียก spaces.streamMessages.patch เพื่อแทนที่กลุ่มเนื้อหาที่สตรีมหรืออัปเดต progressText ในตำแหน่งเดิม
  3. สรุปหรือทิ้งไลฟ์สด (commit หรือ delete)
    • เรียกใช้ spaces.streamMessages.commit เพื่อสรุปสตรีมและแปลงเป็นทรัพยากร Message ถาวร นอกจากนี้ คุณยังแนบองค์ประกอบแบบคอมมิตเท่านั้น เช่น การ์ด วิดเจ็ตเสริม และแหล่งที่มาของส่วนท้ายเมื่อคอมมิตได้ด้วย
    • เรียกใช้ spaces.streamMessages.delete เพื่อทิ้งสตรีมที่ใช้งานอยู่โดยไม่บันทึกข้อความในพื้นที่ทำงาน

ขณะที่สตรีมทำงานอยู่ เฉพาะผู้ส่งข้อความที่อ้างอิง (referencedMessage) ที่กำลังสตรีมอยู่เท่านั้นที่จะคลิกหยุดเพื่อยกเลิกการสร้างได้ Chat จะส่งเหตุการณ์คำสั่งระบบการยกเลิกไปยังแอป Chat เพื่อให้คุณหยุดงานแบ็กเอนด์และคอมมิตเอาต์พุตบางส่วนหรือลบสตรีมได้

ข้อกำหนดเบื้องต้น

หากต้องการสตรีมข้อความด้วย Chat API ให้ทำตามข้อกำหนดเบื้องต้นต่อไปนี้ให้เสร็จสมบูรณ์

  • บัญชี Google Workspace แบบ Business หรือ Enterprise ที่มีสิทธิ์เข้าถึง Google Chat
  • ตั้งค่าสภาพแวดล้อมดังนี้
  • เพิ่มแอปใน Chat ลงในพื้นที่ใน Chat แบบแยกชุดข้อความ (หรือข้อความส่วนตัวแบบ 1:1 ที่มีข้อความอย่างน้อย 1 ข้อความอยู่แล้ว) และรับชื่อทรัพยากรของข้อความที่เข้าถึงได้เพื่อตอบกลับ (spaces/SPACE_ID/messages/MESSAGE_ID)
  • สำหรับตัวอย่าง Python ให้ติดตั้งไลบรารีของไคลเอ็นต์ Google API สำหรับ Python และ google-auth แล้วเริ่มต้นไคลเอ็นต์บริการ Chat API ดังนี้

    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)
    

เริ่มไลฟ์สด

หากต้องการเริ่มสตรีมการตอบกลับ ให้เรียกใช้ spaces.streamMessages.create และส่ง StreamMessage ในเนื้อหาคำขอ ดังนี้

  • referencedMessage (ต้องระบุ): ชื่อทรัพยากรของข้อความที่ทริกเกอร์การตอบกลับ โดยจัดรูปแบบเป็น spaces/{space}/messages/{message} ข้อความที่อ้างอิงต้องอยู่ในพื้นที่ทำงานเดียวกันและแอปใน Chat ของคุณต้องเข้าถึงได้ Chat จะโพสต์การตอบกลับที่สตรีมในชุดข้อความเดียวกับ referencedMessage
  • progressText (ต้องระบุ): ป้ายกำกับสถานะข้อความธรรมดาที่ไม่ใช่ค่าว่าง (สูงสุด 250 อักขระ) ที่แสดงข้างตัวบ่งชี้ความคืบหน้า เช่น "Thinking..."
  • markupSyntax (ไม่บังคับ): ไวยากรณ์การจัดรูปแบบข้อความสำหรับข้อความ เช่น MARKUP_SYNTAX_MARKDOWN หรือ MARKUP_SYNTAX_CHAT (ค่าเริ่มต้นเป็นไวยากรณ์ที่กำหนดค่าไว้ของแอปใน Chat หาก MARKUP_SYNTAX_UNSPECIFIED) ตั้งค่า MARKUP_SYNTAX_MARKDOWN หากคุณวางแผนที่จะรวมการอ้างอิงในบรรทัด
  • streamContentChunks (ไม่บังคับ): ส่วน text, step หรือ citation เริ่มต้นที่จะแสดงทันที

บันทึกทั้ง name (spaces/{space}/streamMessages/{stream_message}) และ nextChunkToken จากการตอบกลับสำหรับการเรียกครั้งต่อๆ ไป

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

เมธอดจะแสดงผลทรัพยากร StreamMessage ดังนี้

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

เพิ่มกลุ่มเนื้อหาต่อท้าย

เมื่อแอปใน Chat สร้างขั้นตอนหรือโทเค็นข้อความระดับกลาง ให้เรียกใช้ spaces.streamMessages.appendStreamMessageContent ดังนี้

  • ผ่าน nextChunkToken ล่าสุดใน chunkToken การเรียกใช้ที่สำเร็จแต่ละครั้งจะแสดงผล nextChunkToken ใหม่ที่คุณต้องส่งต่อในคำขออัปเดตหรือคอมมิตครั้งถัดไป
  • text ส่วนจะเชื่อมโยงกับข้อความที่สตรีมก่อนหน้านี้โดยตรง ใส่ช่องว่างหรือขึ้นบรรทัดใหม่ตามความจำเป็นระหว่างกลุ่มข้อความ
  • step ส่วนจะต่อท้ายรายการขั้นตอนกลางที่แสดงในข้อความ
  • progressText ไม่บังคับ หากคุณละเว้น progressText หรือตั้งค่าเป็น "", Chat จะแสดง progressText ที่ตั้งค่าไว้ก่อนหน้านี้ต่อไป

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

แทนที่เนื้อหาที่สตรีมหรือข้อความแสดงความคืบหน้า

หากต้องการเขียนเนื้อหาที่สตรีมก่อนหน้านี้ใหม่ เช่น หลังจากเรียกใช้ตัวกรองหลังการประมวลผลหรือตัวกรองความปลอดภัย หรืออัปเดตเฉพาะ progressText โดยไม่เพิ่มกลุ่ม ให้เรียกใช้ spaces.streamMessages.patch ดังนี้

  • ตั้งค่า updateMask เป็นรายการช่องที่คั่นด้วยคอมมาเพื่ออัปเดต ได้แก่ progressText, streamContentChunks หรือ progressText,streamContentChunks
  • ใส่ nextChunkToken ล่าสุดไว้ในเนื้อหาคำขอ StreamMessage และบันทึก nextChunkToken ใหม่ที่แสดงในการตอบกลับ
  • เมื่อ streamContentChunks อยู่ใน updateMask ส่วนที่ระบุจะแทนที่เนื้อหาที่สตรีมก่อนหน้านี้แทนที่จะต่อท้าย
  • เมื่อ progressText อยู่ใน updateMask progressText ต้องเป็นข้อความธรรมดาที่ไม่ใช่ค่าว่างและมีความยาวไม่เกิน 250 อักขระ

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

สรุปไลฟ์สด

เมื่อสร้างเสร็จแล้ว ให้เรียกใช้ spaces.streamMessages.commit ด้วย chunkToken ล่าสุดเพื่อสรุปสตรีมเป็นทรัพยากร Message ถาวร ดังนี้

  • คุณสามารถเลือกที่จะรวม streamContentChunks สุดท้ายในคำขอคอมมิตได้ ซึ่งรวมถึงประเภทกลุ่มข้อมูลแบบคอมมิตเท่านั้น (card, accessoryWidget และ citedSource)
  • หากคุณสตรีมเนื้อหาที่ไม่ว่างเปล่าในการเรียกก่อนหน้านี้แล้ว คุณสามารถละเว้น streamContentChunks เมื่อคอมมิตได้ ข้อความที่คอมมิตขั้นสุดท้ายต้องไม่ว่างเปล่า

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

ทิ้งสตรีมที่ใช้งานอยู่

หากเกิดข้อผิดพลาดที่กู้คืนไม่ได้หรือผู้ใช้ยกเลิกการสร้าง และคุณไม่ต้องการทิ้งการตอบกลับบางส่วนไว้ในชุดข้อความ ให้เรียกใช้ 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()

เมื่อคอมมิตสตรีมลงใน Message แล้ว ให้เรียกใช้ spaces.messages.delete แทนหากต้องการลบข้อความที่สรุปแล้ว

ประเภทกลุ่มเนื้อหาที่รองรับ

แต่ละรายการใน streamContentChunks จะระบุช่อง 1 ช่องในยูเนียน StreamContentChunk ข้อมูลบางประเภทสามารถส่งได้ตลอดทั้งสตรีม ส่วนข้อมูลประเภทอื่นๆ จะยอมรับเฉพาะใน spaces.streamMessages.commit เท่านั้น

ฟิลด์กลุ่ม วิธีการที่รองรับ ช่อง Message สุดท้าย หมายเหตุ
text create, appendStreamMessageContent, patch, commit text ข้อความส่วนเพิ่ม เชื่อมโยงเมื่อวันที่ appendStreamMessageContent แทนที่เมื่อวันที่ patch ข้อความที่คอมมิตขั้นสุดท้ายต้องมีขนาดไม่เกิน 32,000 ไบต์
step create, appendStreamMessageContent, patch, commit elements.steps.steps[] ขั้นตอนการประมวลผลระดับกลางที่มี title ข้อความธรรมดา (ต้องระบุ) และ description (ไม่บังคับ)
citation create, appendStreamMessageContent, patch, commit elements.citations[] Citation ในบรรทัดที่มี id ที่เป็นตัวอักษรและตัวเลขคละกัน (ไม่เกิน 63 อักขระ อนุญาตให้ใช้ขีดล่าง) และ citedSources ต้องมีแท็ก markupSyntax: "MARKUP_SYNTAX_MARKDOWN" และแท็ก <chat-citation data-id="ID">...</chat-citation> ที่ตรงกันใน text การส่ง citation ที่มี id อยู่แล้วจะเขียนทับการอ้างอิงก่อนหน้า
card commit เท่านั้น cardsV2[] การ์ด CardWithId แบบอินเทอร์แอกทีฟหรือให้ข้อมูล
accessoryWidget commit เท่านั้น accessoryWidgets[] ปุ่ม AccessoryWidget ที่แสดงด้านล่างข้อความ เช่น ปุ่มความคิดเห็น
citedSource commit เท่านั้น elements.citedSources[] ลิงก์ส่วนท้าย CitedSource ที่แสดงใต้ข้อความโดยไม่มีแท็กการอ้างอิงในบรรทัด

จัดการการยกเลิกสตรีมที่ผู้ใช้เริ่ม

ขณะที่สตรีมทำงานอยู่ เฉพาะผู้ส่งข้อความที่อ้างอิง (referencedMessage) ที่กำลังสตรีมอยู่เท่านั้นที่จะคลิกหยุดในข้อความสตรีมมิงได้ เมื่อผู้ใช้หยุดสตรีม Chat จะส่งเหตุการณ์การโต้ตอบคำสั่งของระบบที่มี cancelStream.streamMessage (ชื่อทรัพยากร spaces/{space}/streamMessages/{stream_message} ของสตรีมที่หยุด) ไปยังปลายทางของแอปใน Chat

โครงสร้างของเพย์โหลด JSON ขาเข้าจะขึ้นอยู่กับว่าคุณสร้างแอปใน Chat เป็นส่วนเสริมของ Google Workspace หรือใช้เหตุการณ์การโต้ตอบของ Chat API ดังนี้

ส่วนเสริมของ 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"
    }
  }
}

เมื่อแอปใน Chat ได้รับเหตุการณ์ cancelStream ระบบจะดำเนินการดังนี้

  1. ส่งสัญญาณให้ผู้ปฏิบัติงานในเบื้องหลังหยุดสร้างโทเค็นเพิ่มเติมหรือเรียกใช้เครื่องมือดาวน์สตรีมสำหรับ streamMessage
  2. แก้ไขสตรีมที่ใช้งานอยู่โดยใช้วิธีใดวิธีหนึ่งต่อไปนี้
    • เก็บเอาต์พุตบางส่วน: เรียกใช้ spaces.streamMessages.commit ด้วย chunkToken ล่าสุด (เลือกเพิ่มหมายเหตุ เช่น "\n\n_Generation stopped._" ได้)
    • ทิ้งการตอบกลับ: เรียกใช้ spaces.streamMessages.delete เพื่อไม่ให้มีการบันทึกข้อมูลใดๆ ลงในชุดข้อความ
  3. แสดงผลการตอบกลับ JSON ของ HTTP 200 OK ที่ว่างเปล่า ({}) จากตัวแฮนเดิลเหตุการณ์การโต้ตอบ

ข้อจำกัด

การสตรีมข้อความมีข้อจำกัดดังต่อไปนี้

  • ความพร้อมให้บริการของแพลตฟอร์ม: การสตรีมข้อความจะปรากฏใน Chat บนเว็บเท่านั้น และจะรองรับ Android และ iOS ในเร็วๆ นี้
  • หมดเวลาสตรีม: StreamMessage ที่ใช้งานอยู่จะหมดเวลาหลังจากสร้าง 10 นาที แอป Chat ของคุณต้องเรียกใช้ spaces.streamMessages.commit หรือ spaces.streamMessages.delete ภายใน 10 นาทีหลังจากเริ่มสตรีม