AI & Tools
2026-09-115 phút
Xử lý sự cố Vertex AI Stream bị Empty Reply trên các hệ thống AI Gateway
Phân tích lỗi Gemini 2.5/3.x trên Google Vertex AI rơi vào trạng thái hidden-thinking làm kết quả trả về bị rỗng (Empty Completion), và giải pháp cấu hình provider layer.
Vertex AIGeminiOpenClawStreamingTroubleshootingAI Engineering
1. Triệu chứng sự cố (The Silent Assistant)
Trong quá trình vận hành các bot trợ lý lập trình hoặc cổng AI Gateway (như OpenClaw kết nối trực tiếp với Google Vertex AI qua provider @mariozechner/pi-ai):
- Trên giao diện chat (Telegram / Web UI), bot hiển thị trạng thái "đang gõ..." (typing).
- Quá trình stream kết thúc nhưng bot hoàn toàn im lặng, không trả về bất kỳ tin nhắn nào!
- Người dùng tưởng bot bị đơ hoặc mất mạng, nhưng kiểm tra log máy chủ thì thấy kết nối HTTP 200 hoàn tất.
Kiểm tra nhật ký luồng dữ liệu (raw-stream.jsonl):
{
"event": "assistant_text_stream",
"text_start": true,
"text_delta": "",
"text_end": true,
"delta": "",
"content": "",
"rawText": "",
"rawThinking": ""
}
Lifecycle của request kết thúc đầy đủ nhưng toàn bộ trường văn bản đều trả về chuỗi rỗng ""!
2. Nguyên nhân gốc rễ (Root Cause Analysis)
Nguyên nhân nằm ở sự thay đổi trong cách xử lý cơ chế suy luận (Thinking / Reasoning) của Google Vertex AI trên các thế hệ mô hình mới (như gemini-2.5-flash, gemini-3.1-pro-preview):
- Cơ chế suy luận ngầm (Hidden Thinking):
- Khi nhận prompt, mô hình Vertex AI tự động kích hoạt tiến trình suy luận nội tâm.
- Khi số lượng token suy luận chạm giới hạn hoặc khi kết thúc chu kỳ, Vertex AI trả về phản hồi với thông số:
{ "finishReason": "MAX_TOKENS", "thoughtsTokenCount": 1024, "parts": [] } - Số token đã bị "đốt" cho quá trình suy luận nội bộ (
thoughtsTokenCount > 0), nhưng phần văn bản hiển thị cho người dùng (parts) lại là một mảng rỗng[]!
- Provider Layer nuốt lỗi:
- Thư viện kết nối Vertex AI ở phía client coi sự kiện kết thúc này là một lần hoàn thành hợp lệ (valid completion) và đóng stream mà không bắn ra lỗi.
- Tầng ứng dụng bên trên nhận chuỗi rỗng và nuốt mất thông báo gửi về cho người dùng.
3. Bản vá kỹ thuật (The Patch)
Bản vá được áp dụng trực tiếp tại tầng xử lý nhà cung cấp (Provider Layer):
// Sửa đổi trong hàm streamGoogleVertex() hoặc complete()
export async function streamGoogleVertex(model: string, messages: any[], options: any) {
// 1. Luôn chủ động đặt maxOutputTokens an toàn
const maxTokens = options.maxTokens || 4096;
// 2. Tinh chỉnh thinkingConfig tường minh
const thinkingConfig = {
// Nếu không yêu cầu hiển thị suy luận, giới hạn quota thinking để không chiếm hết token trả về
thinkingBudget: options.thinking ? 2048 : 0,
};
// 3. Kiểm tra tính toàn vẹn khi kết thúc Stream
let hasEmittedVisibleText = false;
for await (const chunk of vertexResponseStream) {
const textPart = chunk.candidates?.[0]?.content?.parts?.[0]?.text;
if (textPart && textPart.length > 0) {
hasEmittedVisibleText = true;
yield { type: 'text', text: textPart };
}
}
// 4. BẪY CHẶN EMPTY COMPLETION:
if (!hasEmittedVisibleText && !options.isToolCall) {
throw new Error(
`VERTEX_EMPTY_COMPLETION: Mô hình ${model} kết thúc luồng nhưng không trả về nội dung hiển thị (FinishReason: MAX_TOKENS / Hidden Thinking).`
);
}
}
4. Kết quả sau khi khắc phục
- Với prompt đơn giản "reply with OK":
- Trước patch:
stopReason = length,text = ""(mô hình ngốn hết token vào suy luận ngầm). - Sau patch:
stopReason = stop,text = "OK"(trả lời chuẩn xác tức thì).
- Trước patch:
- Nếu Google Vertex AI thực sự bị nghẽn và không thể xuất text, lỗi sẽ được đẩy rõ ràng lên giao diện người dùng thay vì giữ im lặng khó hiểu.
5. Bài học rút ra
- Cảnh giác với cơ chế suy luận ngầm: Các mô hình AI thế hệ 2026 ngày càng tích hợp sâu CoT (Chain-of-Thought). Khi tích hợp qua API trực tiếp, luôn phải quản lý tham số
thinkingBudgetvàmaxOutputTokens. - Phải có Assertions ở tầng Gateway: Đừng bao giờ cho phép một phản hồi AI rỗng đi qua tầng Gateway mà không có lý do chính đáng (như Tool Call).