本次草稿依据提交记录,未逐行核对实现。依据:(2026-10-17,Jev chat v2, upstream/downstream SSE streaming, diary review flow)、(2026-10-16,Jev chat pipeline v2:类型化路由、有界读取、渐进式预览、迁移 019)。实证:AI 接收本次输入、同模式紧邻上一轮与动态读取的正式 Markdown,上一轮只解决指代;新人物/地点需显式确认,AI 新增事实直接提交;聊天活动状态、取消、owner、租约均存 PG,跨进程可查询和停止,租约失效后晚到结果不能发布;同账号 AI 对话单并发(PG 租约保证,POST /api/chat 不设每分钟上限)。
v2 要解决什么
v1 的问题不在“能不能聊”,而在“聊多了会怎样”。上下文越攒越长,读取越读越滥,生成越生成越不可控:聊了十轮,第十一轮还要把前面全塞给模型;要读一篇日记,就把整篇连同历史版本都读进来;生成到一半想取消,取消信号追不上已在跑的生成;生成结束才一次性看到结果,中间漫长等待里什么都没有。
v2 的关键词正好对应这些问题:类型化路由、有界读取、渐进式预览、上下游 SSE 流式、日记审阅。v2 不是加功能,是给聊天加约束,让系统在规模变大后仍可控。
上下游 SSE 如何接力
一次聊天有两段流。上游是模型到服务端:模型不是一次性返回完整文本,而是一个片段一个片段往外吐,服务端用 SSE 接住,连接保持打开,每吐一块就收到一块。下游是服务端到客户端:服务端收到上游片段,不应等整段结束再一次性发给客户端,而应边收边转发,收到一块就通过下游 SSE 发一块,客户端边收边渲染,用户看到文字一个字一个字长出来。
接力的关键,是服务端在中间不做“攒够了再发”的缓冲。理想形态是直通:上游来一块,下游走一块。上下游流速并不匹配:上游快、下游慢时不能无限缓冲;下游断开时,上游生成要及时停下来。通用 SSE 转发伪代码如下:
1 | 客户端 --SSE--> 服务端 --SSE--> 模型 |
重点只有两行:“立即转发”保证流式体验,“追加到持久记录”保证取消与恢复有据可查。
渐进式预览与有界读取
渐进式预览是生成未结束,用户已能看到有意义的部分结果,而非先显示转圈加载态、结束后再一次性替换全文。能看到进展就能判断方向对不对,方向不对可早点取消;预览也让中途干预成为可能。
有界读取有三个约束:一是“同模式紧邻上一轮”,模型只读紧邻上一轮,上一轮只解决指代,让模型知道“他”指谁、“那篇”指哪篇;二是“动态读取的正式 Markdown”,要读日记时读正式 Markdown而非草稿或缓存旧版本,需要时再读,读最新的;三是“新人物/地点需显式确认,AI 新增事实直接提交”,新人物、新地点不能由模型悄悄认定,必须经用户显式确认。三者画出一条线:模型能读什么、能认什么、能写什么都有边界,成本可控、行为可预测、出了问题可追溯。类型化路由是执行层:不同输入走不同路由,适用不同的读取边界。
取消与租约:防止晚到结果覆盖
流式生成最难的问题之一是“晚到结果”:用户点了取消,生成却还在跑;跑完了结果才姗姗来迟,该不该写进去?写进去,用户明明取消的东西又出现了;不写,又要确保它真的不会在某个角落被写进去。
存储文档记载的做法是:聊天活动状态、取消、owner、租约均存 PG,跨进程可查询和停止,租约失效后晚到结果不能发布。关键有三点:一是“存 PG”,状态存在 PostgreSQL而非进程内存,任何进程都能查、都能改,取消可从任何入口发起;二是“租约”,带有效期的执行权,过期即失效,执行者失联后系统可接管或清理;三是“租约失效后晚到结果不能发布”,发布时先校验租约,失效了哪怕结果已生成也要丢弃。同账号单并发也是同一机制的延伸:PG 租约保证同一账号同一时间只有一个 AI 对话在执行。POST /api/chat 不设每分钟上限,限流手段是“同一时间只能跑一个”,这是并发控制,不是频率控制。
日记审阅:确认关联
还提到日记审阅流程:AI 生成或提议的内容进日记前,要经过一道确认。确认不是形式上的“点一下确定”,而是“确认关联”——确认这段内容与哪篇日记、哪个人物、哪个地点关联。流程是:AI 产生提议,先以预览形态呈现;用户确认无误后才正式提交;不确认,提议就停在预览态,不进入正式数据。提议错了,代价只是一次预览,不是一次错误写入。
小结
Jev Chat v2 的核心不是“聊得更聪明”,而是“聊得更可控”:上下游 SSE 让生成过程可见,有界读取让生成有边界,取消与租约让结果有资格,日记审阅让结果落盘前有一次确认。这条链路的价值不在多聪明,而在多可靠。