Chatbot 如何知道 AI 回复结束了?—— [DONE] 与 Streaming 生命周期学习总结
Chatbot 如何知道 AI 回复结束了?—— [DONE] 与 Streaming 生命周期学习总结
系列专栏:从零开发 AI Chatbot(KK Chat)
项目版本:KK Chat v1.2.0
目录
前言
上一篇文章中,我学习了 delta.content,理解了 AI Streaming 为什么能够不断返回新增内容,并逐步拼接成完整回复。
整个过程中,浏览器会持续收到:
data: {...}
data: {...}
data: {...}
每解析一个数据块,就会更新一次聊天界面。
但是,一个新的问题出现了:
浏览器怎么知道 AI 已经回答完了?
总不能一直等待服务器继续发送数据。
因此,在 Streaming 中,还需要一种机制告诉客户端:
“这次回复已经结束,可以停止读取了。”
今天学习的内容,就是 Streaming 生命周期(Streaming Lifecycle) 与 [DONE] 结束标记。
一、为什么需要结束标记?
假设 AI 正在回答:
请介绍 Python。
浏览器不断收到:
Python
│
▼
是一种
│
▼
编程语言
│
▼
。
如果没有结束标记,浏览器根本不知道:
还有没有下一段?
因此,程序可能一直处于等待状态:
等待
│
▼
等待
│
▼
继续等待
这会导致:
- Loading 不会关闭;
- 发送按钮不会恢复;
- 整个聊天界面一直处于“AI 正在思考……”状态。
所以,必须有一个明确的结束信号。
二、什么是 [DONE]?
在 OpenAI 兼容接口中,Streaming 结束时,服务器通常会发送:
data: [DONE]
注意:它不是:
{
"done": true
}
而是:
[DONE]
一个特殊字符串。
它表示:
本次 Streaming 已经结束。
不是“还有一个 JSON”,而是“真正结束”。
三、Streaming 生命周期
完整流程如下:
用户发送消息
│
▼
Fetch 请求
│
▼
SSE 建立连接
│
▼
Streaming 开始
│
▼
不断收到 delta.content
│
▼
收到 [DONE]
│
▼
结束读取
│
▼
关闭 Loading
│
▼
恢复发送按钮
│
▼
等待下一次聊天
这就是一次 AI 回复的完整生命周期。
四、为什么不能一直读取?
ReadableStream 通常通过下面的方式读取:
const reader = response.body.getReader();
理论上,可以一直调用:
reader.read();
但是,当 Streaming 已经结束以后,继续读取没有意义。
因此,收到:
[DONE]
以后,程序应该:
结束循环
│
▼
释放资源
│
▼
恢复界面
五、KK Chat 中如何处理 [DONE]
整个流程如下:
ReadableStream
│
▼
TextDecoder
│
▼
Buffer
│
▼
JSON.parse()
│
▼
delta.content
│
▼
更新页面
如果解析到:
[DONE]
则执行:
停止读取
│
▼
reader.releaseLock()
│
▼
Loading = false
│
▼
按钮恢复
这样,用户才能继续下一次聊天。
六、实践内容
今天主要完成了以下内容:
- 学习 Streaming 生命周期;
- 理解
[DONE]的作用; - 学习如何判断 Streaming 已结束;
- 理解结束后为什么要恢复 Loading;
- 梳理 Streaming 请求的完整生命周期。
整个流程进一步完善为:
DeepSeek
│
▼
Streaming
│
▼
SSE
│
▼
ReadableStream
│
▼
TextDecoder
│
▼
Buffer
│
▼
JSON.parse()
│
▼
delta.content
│
▼
[DONE]
│
▼
恢复页面状态
七、遇到的问题
1. 为什么有时候没有收到 [DONE]?
原因:
并不是所有 AI 服务都采用相同的结束协议。
有些服务使用 [DONE];有些则直接关闭连接;还有一些会返回自己的结束字段。
解决方案:
根据具体 API 文档判断结束方式,而不要假设所有模型都使用 [DONE]。
2. 收到 [DONE] 后为什么还要关闭 Loading?
原因:
[DONE] 只是告诉程序:
AI 已经停止生成。
真正恢复页面,还需要:
Loading = false;- 恢复发送按钮;
- 更新停止按钮状态;
- 释放 Reader。
3. [DONE] 和 done = true 是一样的吗?
不是。
二者分别来自不同层级:
| 标记 | 所属层级 | 含义 |
|---|---|---|
[DONE] | AI 服务协议中的结束标记(应用层) | 表示本次 AI Streaming 回复结束 |
done = true | ReadableStream 读取结果(浏览器流层) | 表示当前流没有更多数据可读 |
在很多实现中,两者都会出现,但含义不同,也不能混为一谈。
八、今天的收获
今天最大的收获,是理解了:
Streaming 不只是不断接收数据,还需要有一个完整的生命周期。
整个过程包括:
- 建立连接;
- 持续接收;
- 增量解析;
- 收到结束信号;
- 清理资源;
- 恢复界面。
其中:
[DONE]或其他结束机制,就是整个 Streaming 生命周期的重要终点。
总结
今天学习了 Streaming 生命周期 与 [DONE] 结束标记。
理解了浏览器不仅要持续读取 AI 回复,还需要正确识别回复已经结束,并完成资源释放和界面恢复。
通过今天的学习,我进一步完善了对 AI Streaming 工作机制的理解,也为后续学习完整的 Streaming 架构打下了基础。
KJ
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)