--- 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` 的入口守卫: ```js 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` 非空时,必须在流式循环前后发送完整的事件序列: ```python # 循环前 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` 会导致前端气泡丢失,是高频回归点。