Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 7 — Streaming: phản hồi thời gian thực và cập nhật tiến trình

Đã đọc 0/8 mục · còn khoảng 4 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Bài 7 — Streaming: phản hồi thời gian thực

Agent gọi nhiều tool có thể mất nhiều giây tới vài chục giây. Nếu người dùng nhìn màn hình trắng, họ nghĩ hỏng. Streaming cho phép hiển thị text ngay khi model sinh ra và báo tiến trình "đang gọi tool...", giảm cảm giác chờ và cho phép hủy sớm.

Streaming với Anthropic

with client.messages.stream(
    model="claude-sonnet-4-5", max_tokens=1024,
    messages=[{"role": "user", "content": "Tóm tắt đơn ORD-1234"}],
    tools=tools,
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)   # in dần từng mẩu
    final = stream.get_final_message()     # lấy message hoàn chỉnh

text_stream cho bạn từng đoạn text; get_final_message() trả message đầy đủ (gồm cả tool_use) để bạn tiếp tục vòng lặp. Trong loop agent, bạn stream phần text và khi stop_reason == tool_use thì chạy tool như bình thường.

Streaming với OpenAI Agents SDK

import asyncio
from agents import Agent, Runner

async def main():
    result = Runner.run_streamed(agent, "Thời tiết Hà Nội?")
    async for event in result.stream_events():
        if event.type == "raw_response_event":
            # từng delta text từ model
            ...
        elif event.type == "run_item_stream_event":
            # sự kiện cấp cao: tool được gọi, output tool, handoff...
            print("[sự kiện]", event.name)

asyncio.run(main())

SDK phát hai tầng sự kiện: raw_response_event (delta token thô) và run_item_stream_event (sự kiện có ý nghĩa: gọi tool, nhận kết quả, handoff). Dùng tầng cao để cập nhật UI "Đang tra cứu đơn hàng...", tầng thấp để in text dần.

Vì sao sự kiện tool quan trọng hơn bạn nghĩ

Nếu chỉ stream token text, người dùng thấy chữ chạy mượt rồi... khựng vài giây khi agent gọi tool, rồi lại chạy tiếp. Khoảng khựng đó không có lời giải thích khiến họ nghĩ hệ thống treo. Đây chính là lúc run_item_stream_event cứu bạn: khi nhận sự kiện tool được gọi, hiển thị ngay "Đang tra cứu đơn hàng..."; khi nhận kết quả tool, đổi thành "Đã có dữ liệu, đang soạn câu trả lời...". Người dùng luôn biết agent đang làm gì. Với agent gọi nhiều tool tuần tự (tra đơn → tính hoàn tiền → tạo phiếu), chuỗi trạng thái này biến một khoảng chờ mờ mịt thành một tiến trình minh bạch — khác biệt lớn về trải nghiệm.

Các bước tích hợp streaming vào UI

  • Chọn tầng sự kiện phù hợp: text delta để in chữ; sự kiện cấp cao để hiện trạng thái.
  • Hiển thị chỉ báo tool: khi có tool call, hiện "Đang xử lý: <tên tool>" thay vì im lặng.
  • Ghép text delta an toàn: nối vào buffer, đừng giả định mỗi delta là một câu trọn.
  • Xử lý hủy: cho người dùng nút Dừng; đóng stream để không tốn token thừa.
  • Luôn lấy message cuối (get_final_message / kết quả run) để có bản đầy đủ cho lịch sử và log.
  • Bọc try/finally để đảm bảo stream được đóng kể cả khi lỗi.

Checklist trải nghiệm streaming tốt

  • [ ] Chữ hiện dần trong <1s sau khi gửi.
  • [ ] Có trạng thái rõ khi agent đang gọi tool (không để màn hình đứng im).
  • [ ] Cho phép hủy giữa chừng và giải phóng tài nguyên.
  • [ ] Vẫn lưu được message cuối đầy đủ để audit/log.
  • [ ] Guardrail output vẫn chạy trên bản cuối, không phải trên từng delta.

Streaming không hợp mọi ngữ cảnh

Streaming đáng giá khi có con người đang nhìn màn hình chờ. Với tác vụ nền (một cron job phân loại email, một agent chạy hàng loạt), streaming chỉ thêm phức tạp mà không ai hưởng lợi — hãy dùng chế độ chờ kết quả cuối cho gọn. Ngoài ra, guardrail output (Bài 6) phải chạy trên bản trả lời cuối hoàn chỉnh, không phải trên từng delta: một mẩu delta có thể vô hại nhưng ghép lại mới lộ thông tin nhạy cảm. Vì vậy dù stream để hiển thị, bạn vẫn giữ lại message cuối và cho guardrail duyệt nó trước khi coi là an toàn.

Sai lầm thường gặp

  • Chỉ stream token mà bỏ sự kiện tool → người dùng thấy khựng lúc gọi tool mà không hiểu vì sao.
  • Chạy output guardrail trên từng delta → sai; guardrail phải chạy trên bản cuối hoàn chỉnh.
  • Quên get_final_message → mất tool_use, loop tiếp theo hỏng.
  • Không đóng stream khi lỗi/hủy → rò kết nối, tốn token.
  • Giả định delta = câu hoàn chỉnh → hiển thị bị vỡ; luôn nối buffer.
  • Stream trong tác vụ nền không có UI → vô ích, chỉ thêm phức tạp; chỉ stream khi có người xem.
Bài 8: đưa agent lên production — quan sát, chi phí, độ tin cậy.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi