You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
事件注释明确:"Chunk frames are transient; the loop appends one final v2 assistant/message or assistant/attempt with the same stream before a committed end frame." —— 即「实时演示」与「durable 结算」是并行的双通道,实时帧是 transient 的,不承担持久化职责。
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
背景
我们基于
deepseek-harness-sdk==0.1.5rc1(Python SDK + out-of-process runtime 二进制)构建服务端,已适配 0.1.5rc1 的流式协议变更:runtime 不再实时推送assistant/chunk,改为每个 step 结束发一条assistant/message,在data.stream携带整段流的压缩时间线(chunk条目 +text-chunks/reasoning-chunks/tool-call-chunks的 time0/dt/texts)。官方文档说明服务端只转发 durable fact,客户端按时间线自行回放——我们照做了,把时间线展开回 delta/reasoning/tool 事件并按 dt 定速回放,功能上等价。但有一个体验硬伤无法靠客户端回放解决:长思考(纯 reasoning、无工具调用)的 step 期间,out-of-process 消费端完全静默。首字节延迟 = 模型完成该 step 的时间,回放只能缩短「结算之后」的呈现时长,step 内的等待无解。
调查:实时通道在 runtime 内部是存在的
阅读本仓库源码后确认:durable 攒批只是 SDK 协议面的取舍,runtime 内部 agent loop 实际是逐 chunk 实时发布的。
1. agent-loop 逐 chunk emit 进程内实时帧
packages/core/agent-loop/src/agent.ts:每个 step 构造AssistantStreamAttempt,回调直接dispatch.emit('agent/assistant-stream', { frame });主循环里每收到一个 adapterStreamChunk就live.push(chunk)—— 即原生 token 级增量实时发布。2. 帧格式与双通道设计
packages/core/agent/src/runtime-types.ts的AssistantStreamFrame:start { attemptId, revision, turn, step }chunk { attemptId, revision, index, time, chunk: StreamChunk }(StreamChunk即text-delta/reasoning-delta/tool-call-delta等原生增量)end { attemptId, revision, index, ... }事件注释明确:"Chunk frames are transient; the loop appends one final v2
assistant/messageorassistant/attemptwith the same stream before a committed end frame." —— 即「实时演示」与「durable 结算」是并行的双通道,实时帧是 transient 的,不承担持久化职责。3. 官方 Web UI 的实时体验正是走这条通道
packages/api/session-controller/src/history.ts:host 内 session-controller 订阅agent/assistant-stream,follower 请求带assistantStream: true时把帧实时推入 feed;packages/api/session-controller/src/assistant-stream.ts:SessionAssistantStreamAccumulator.snapshot()为断线重连提供 mid-attempt 基线(对应 release notes 里「Web 断线自动恢复」的修复);packages/api/gateway/src/stream-protocol.ts:gateway 经 WebSocket 把帧推给浏览器;packages/api/session-controller/src/client/sessions/session.ts:前端case 'assistant-stream': publishAssistantEntry(acceptFrame(frame))—— 收帧即渲染。所以官方 Web 端是真·实时流(token 级增量 + 重连基线),并非攒批回放。
4. 而 SDK JSON-RPC server 不订阅这条通道
packages/sdk/server/src/server.ts构造函数只订阅 4 个事件:session/event、agent/status、session/created、subagent/end。其中session/event仅在 durable log append 时触发(packages/core/session/src/index.ts:this.log.push(event)后才 notify observers);handleRequestswitch 也只有initialize/session/prompt/shutdown三个方法。结论:out-of-process 的 SDK 消费端在 wire 上没有任何实时增量通道,与 in-process 的 Web UI 存在协议面上的流式能力差距。ACP 桥同样不向客户端转发实时帧(仅进程内使用),所以两个外部协议面一致。
当前规避
Python 侧把
assistant/message.data.stream时间线展开回原有事件流并按 dt 定速回放(可调倍速与单间隔上限),下游渲染层无改动。如前所述,这无法缓解 step 内静默。提议:给 SDK server 增加可选的实时通知
在 durability-first 语义完全不变的前提下,以 opt-in 方式暴露官方 Web UI 已在使用的同一条进程内通道:
initialize增加可选参数(如liveAssistantStream: true);agent/assistant-stream,向客户端转发一条新 notification(如session/assistant-stream,payload{ sessionId, frame },frame 即AssistantStreamFrame);assistant/message结算照旧,实时帧明确标注 transient,仅用于渲染(UI 增量、进度提示、打字机效果),不作为事实来源。改动面很小:server 构造函数加一个 disposer、protocol 包加一个通知类型、Python client 加回调。帧本身已是进程内现成事件,无需触碰 agent-loop 与持久化路径。
想请教的点
session/event的交错顺序如何约定?(loop 先 commitassistant/message再发 committedend帧,客户端可据此做对账/丢弃半途帧。)补充:这与 #4591(session/resume)是同一个「SDK 极简面」下的两个正交缺口——resume 补 durable 恢复,本帖补 live 演示通道。两者都补齐后,out-of-process 消费端就能达到与官方 Web UI 一致的体验。
All reactions