在 Python 后端服务中对接 DeepSeek 对话补全 API 时,流式输出能显著降低首字延迟,但也引入 SSE 解析、超时控制和代理缓冲等新问题。本文围绕这些工程细节展开,不涉及模型能力本身。
启用流式返回
调用对话补全接口时,通过请求参数启用流式返回。具体参数名和取值需以官方 API 文档为准。启用后,服务端以 SSE(Server-Sent Events)格式逐块推送数据,每个事件通常由若干 data: 行组成,最后以特定结束标记收尾。客户端必须按行读取,不能假设一次网络读取就拿到完整事件。
逐块解析 SSE 与拼接 delta
SSE 解析的核心是维护一个行缓冲区:
- 从响应流中读取字节,按 UTF-8 解码后追加到缓冲区。
- 按换行符切分,保留最后一个不完整行继续缓冲。
- 对每个完整行,若以
data:开头,取出载荷;若载荷为结束标记,则终止读取。 - 将载荷按 JSON 解析,提取增量内容字段(通常位于 choices 下的 delta 中),追加到结果字符串。
需要注意:delta 可能只包含角色信息而不含文本,也可能为空;拼接时应只取实际文本字段,避免把控制字段混入输出。遇到无法解析的行应跳过并记录,而不是让整个请求失败。
超时与重试
流式请求的超时分为两个阶段:
- connect 超时:建立 TCP 连接的时间上限,建议设置较短,例如 5~10 秒。
- read 超时:两次数据块之间的最大间隔。流式场景下不能沿用普通请求的整包超时,否则长回答会被误判超时。建议按“首字节超时”和“块间超时”分别控制:首字节可设 10~30 秒,块间可设 30~60 秒。
重试策略要区分阶段:
- 连接失败或首字节前失败,可进行有限次退避重试(如 2~3 次,指数退避加随机抖动)。
- 一旦已经开始输出内容,重试会导致重复内容。此时应记录已输出的偏移量,要么放弃重试并向客户端报错,要么在应用层做去重。
反向代理下的缓冲问题
Nginx 默认会缓冲上游响应,导致 SSE 数据被攒批后一次性下发,表现为“流式不流”。需要在对应 location 中关闭代理缓冲,并禁用缓存。同时应确保不启用响应压缩,否则分块边界会被改变。若使用其他网关,也需检查是否有类似的缓冲或聚合行为。
工程建议
- 为每个流式请求设置总时长上限,防止连接长期占用。
- 在服务端向客户端转发时,同样以 SSE 格式输出,并定期发送注释行作为心跳,避免中间层因空闲而断开。
- 记录首字节耗时、块间最大间隔和结束原因,便于定位截断问题。
- 对上游返回的错误事件单独处理,不要与正常 delta 混在一起。
以上配置项的具体参数名和默认值,请以 DeepSeek 官方 API 文档和所用 HTTP 客户端文档为准。