2.5 KiB
2.5 KiB
last_reviewed
| last_reviewed |
|---|
| 2026-06-13 |
llm_query_text 缺少 start/end 事件导致前端不显示思考过程
错误现象
- 前端只显示 "正在分析文件..." 的文字提示(来自 agent 事件的
agent_state_change) - LLM 返回的思考过程气泡和正式回答气泡都不显示
- 校验流程的气泡正常显示,但提取流程的气泡缺失
- 后端日志正常,LLM 请求成功返回
触发条件
llm_query_text()或_llm_query_multimodal()被 Agent 调度调用source_dir参数已传入(启用 SSE 流式事件)- 函数内部只发了
chunk和reasoning事件,缺少start和end
原因
前端 chat.js 的 handleLLMStream 状态机:
start -> _createLLMStreamBubble() // 创建聊天气泡 DOM
reasoning -> _appendLLMStreamReasoning() // 往气泡追加思考内容
chunk -> _appendLLMStreamChunk() // 往气泡追加正式回答
end -> _closeLLMStreamBubble() // 关闭气泡,切换完成样式
_appendLLMStreamReasoning 和 _appendLLMStreamChunk 的入口守卫:
if (!llmStreamState.reasoningContent) return;
if (!llmStreamState.textContent) return;
没有 start 事件,llmStreamState 就一直是初始空值,后续所有 reasoning 和 chunk 事件都会被静默丢弃。
为什么校验流程正常? 因为 validate_semantic_completeness() 在调用 llm_query_text 之前,自己手动发了 start 和 end 事件,绕过了这个问题。
修复方法
llm_query_text 和 _llm_query_multimodal 在 source_dir 非空时,必须在流式循环前后发送完整的事件序列:
# 循环前
if source_dir:
_emit_llm_stream(source_dir, "start", label="正在分析文件...")
try:
for resp in llm.stream_chat(...):
# ... chunk / reasoning ...
# 循环后
if source_dir:
_emit_llm_stream(source_dir, "end", label="分析完成")
except Exception as e:
if source_dir:
_emit_llm_stream(source_dir, "error", error=str(e))
防回归要点
修改 llm_query_text 或 _llm_query_multimodal 时,检查事件发送是否完整:
| 阶段 | 事件 | 必需性 |
|---|---|---|
| 循环前 | start |
必需(前端创建气泡) |
| 循环中 | chunk |
可选(无内容时不发) |
| 循环中 | reasoning |
可选(模型不支持时不发) |
| 成功 | end |
必需(前端切换完成样式) |
| 失败 | error |
必需 |
删除 start 或 end 会导致前端气泡丢失,是高频回归点。