Trong các ứng dụng AI Agent chuyên nghiệp, độ trễ (latency) là một vấn đề không thể tránh khỏi khi LLM cần thời gian để "suy nghĩ" và tạo ra các nội dung dài. Nếu bắt người dùng đợi 30 giây trước khi hiện ra câu trả lời, họ sẽ cảm thấy ứng dụng bị treo. Bài học này sẽ hướng dẫn bạn kỹ thuật Streaming để truyền dữ liệu về theo kiểu "vừa nghĩ vừa nói", tích hợp hoàn hảo với hệ thống tiện ích đã thiết lập từ Bài 1.
I. Tại sao cần Streaming?
Streaming giúp giải quyết bài toán trải nghiệm người dùng (UX) bằng cách:
- Phản hồi tức thì: Người dùng thấy những chữ đầu tiên xuất hiện chỉ sau 1-2 giây.
- Giảm sự sốt ruột: Người dùng có thể đọc dần nội dung AI đang tạo ra, tạo cảm giác hệ thống đang làm việc liên tục.
II. Cơ chế agent.stream và Async Iterator
Thay vì dùng hàm .invoke() trả về kết quả một lần duy nhất, LangChain cung cấp hàm .stream(). Hàm này trả về một Async Iterator (trình lặp bất đồng bộ), cho phép bạn lặp qua từng "mảnh" dữ liệu (chunk) ngay khi chúng được máy chủ AI gửi về.
Chúng ta sử dụng chế độ streamMode: "messages" để nhận được từng token (từng chữ) của câu trả lời.
III. Mã nguồn: 09-real-time-message-streaming.ts
Chúng ta sẽ sử dụng Gemini 2.5 Flash để viết một nội dung dài và quan sát hiệu ứng "chữ chạy" ngay trên terminal.
// apps/langchain/scripts/09-real-time-message-streaming.ts
// pnpm --filter=ai-notes-langchain run tsx scripts/09-real-time-message-streaming.ts
import './env'; // nạp API Key và môi trường
import { createGeminiModel, generateImage } from '@workspace/util-langchain';
import { createAgent } from 'langchain';
async function main() {
const model = createGeminiModel();
const agent = createAgent({
model: model,
tools: [], // Simple test without tools for clarity
});
// 1. Generate visual diagram using our utility
await generateImage(agent.graph, 'graph-ignore/scripts-09-streaming.jpg');
console.log('--- Starting Streaming (AI is writing...) ---\n');
// 2. Use agent.stream instead of invoke
// streamMode: "messages" allows us to capture every single token/chunk
const stream = await agent.stream(
{
messages: [
{
role: 'user',
content:
'Please write a 200-word essay about the beauty of Ha Long Bay in Vietnam.',
},
],
},
{ streamMode: 'messages' },
);
// 3. Iterate over the incoming stream of data
for await (const [chunk, metadata] of stream) {
// chunk: the message object containing new content
// metadata: info about which node produced the chunk (e.g., 'agent')
if (chunk.content) {
// process.stdout.write prints text without a newline,
// creating the "typewriter" effect on the terminal.
process.stdout.write(chunk.content as string);
}
}
console.log('\n\n--- Finished receiving all tokens ---');
}
main().catch(console.error);IV. Phân tích kỹ thuật
for await...of: Vì dữ liệu được gửi về theo thời gian thực (giống như dòng nước), chúng ta không thể dùng vòng lặpforthông thường mà phải dùng trình lặp bất đồng bộ để "hứng" dữ liệu.chunk.content: Mỗi mảnh dữ liệu trả về thường chỉ là một từ, một dấu phẩy hoặc thậm chí là một dấu cách. Bằng cách nối chúng lại liên tục thông quaprocess.stdout.write, bạn sẽ tạo ra hiệu ứng chữ chạy mượt mà.metadata: Trường này giúp bạn phân biệt mảnh dữ liệu đến từ đâu. Trong các Agent phức tạp, bạn có thể dùng nó để hiển thị các log như: "Agent đang gọi công cụ tìm kiếm..." hoặc "Đang tổng hợp câu trả lời...".
Sơ đồ hoạt động:

V. Cách chạy code
Sử dụng lệnh sau để thấy hiệu ứng chữ chạy thời gian thực:
pnpm --filter=ai-notes-langchain run tsx scripts/09-real-time-message-streaming.tsVI. Tổng kết
.stream()là phương thức bắt buộc cho mọi giao diện Chatbot thực tế để đảm bảo trải nghiệm người dùng tốt nhất.- Luôn sử dụng
streamMode: "messages"khi bạn muốn hiển thị token LLM theo thời gian thực. - Hệ thống
util-langchaingiúp bạn dễ dàng theo dõi kiến trúc của các streaming agent thông qua sơ đồ logic.
Trong bài học cuối cùng của phần Cơ bản, chúng ta sẽ học cách gửi các Cập nhật tùy chỉnh (Custom Updates) từ bên trong Tool để báo cáo tiến độ cho người dùng trong khi chờ đợi các tác vụ nặng!
👉 Bài tiếp theo: 10. Gửi cập nhật trạng thái từ Tool (Custom Updates)