# AI 助手流式改造 + fetch 错误处理陷阱（2026-09-08）

## 陷阱：fetch 非200 静默丢弃 → 永远卡「正在连接…」

**症状**：AI 助手发问题后永远卡「正在连接…」——3 个「正在连接…」后弹「AI 回答超时」。注意：3 个「正在连接…」= 前端把 CHAT_APIS 里 3 条端点都试了一遍、全失败，不是思考慢。

**根因**：流式改造的 tryNext 里写成了：
```js
if (!r.ok) {
  return r.json().then(function(j){ return {_err:...}; }).catch(function(){ return {_err:...}; });
}
```
返回的 `{_err}` 对象**无人接住**（链上没有下一个 .then）——promise 静默结束，既不降级下一端点也不 settle 报错，界面停在初始「正在连接…」。

**解决**：非200 → 清 timer + 降级：
```js
if (!r.ok) {
  if (timer) clearTimeout(timer);
  console.error('[AI助手] HTTP ' + r.status + ': ' + url);
  tryNext();   // 降级下一端点
  return;
}
```

## 陷阱：思考模式静默期被硬超时切断

**症状**：复杂问题思考（reasoning）阶段 60~180s 无 token 输出，前端 240s 硬超时切断（后端 BrokenPipe 500）。

**根因**：DeepSeek 思考模式下首 token 前有长静默；硬超时（总时长）在思考阶段就被触发。

**解决**：前端超时改**空闲超时**（240s 无数据才断、有 token 就重置），覆盖思考静默期。流式后端用生成器 yield `status/delta/done/error` 事件，前端 ReadableStream 边收边显示「正在检索/正在思考」状态。

## 流式实现要点

- 后端（ai_chat.py）：`_call_hermes_stream` 读网关 `stream:true` 逐段 yield delta 文本（把 usage 写进可变 dict 兜底统计）；`handle_stream` 生成器先 yield `status`（正在检索/正在思考）再 yield `delta`，最后 `done`。
- 服务层（销售工作台服务.py）：`_stream_sse` 逐事件写 `data: {json}\n\n`，headers 送 `Content-Type: text/event-stream` + `Access-Control-Allow-Origin: *` + `X-Accel-Buffering: no`。
- 前端：`fetch` 后判 `Content-Type` 含 `text/event-stream` → `r.body.getReader()` + `TextDecoder`，按 `\n` 拆 `data:` 行，`JSON.parse` 后按 type 分发；保留 JSON 回退分支（后端旧版时降级）。
