MoonshotAI / MoonshotAI/kimi-code
建议:TUI 流式输出增加平滑(匀速)释放机制
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
问题现象
当前 TUI 的流式输出是"突发式"的:LLM token 到达后按固定窗口合批,然后把整段累积文本一次性重绘到屏幕。实际观感是文字一坨一坨地蹦出来,回复越长、token 到达越快越明显,视觉上呈现为"一卡一卡"的不连续跳动。
现状分析
链路(apps/kimi-code/src/tui/controllers/streaming-ui.ts):
handleAssistantDelta→appendAssistantDelta→scheduleFlush(),按STREAMING_UI_FLUSH_MS = 50(src/tui/constant/streaming.ts)节流合批;flush()→onStreamingTextUpdate(fullText)→component.updateContent(fullText, { transient: true })+requestRender()——每次合批直接把全部已到达文本推给组件;- pi-tui 按
MIN_RENDER_INTERVAL_MS = 16渲染。
关键点:"已到达文本"和"已显示文本"是同一个指针。token 以任意速率、任意批量到达,显示速率就完全跟随到达速率波动——到达快时一帧蹦出几十上百字,到达慢时长时间不动,这是"卡"的直接观感来源。
另外,每次 flush 都对累积全文做 markdown 重解析 + 重排版(packages/pi-tui/src/components/markdown.ts 的 setText 使缓存全部失效后全量 lexer),单帧成本随消息长度线性增长,长回复时刷新节奏进一步劣化。
参考方案
oh-my-pi(同为 pi-tui 体系)的 StreamingRevealController 是一个验证过的成熟方案,核心思想是把"到达"和"显示"解耦成两个指针:
target记录已到达全文,revealed记录已显示的 grapheme 数;- 一个 30fps 的定时器按
nextStep(backlog) = max(3, ⌈backlog / 8⌉)推进显示指针:小流量时是恒速约 90 字/秒的打字机,大流量时按比例加速、约 8 帧(~266ms)内追平——视觉速率始终连续有界,不随 token 到达的突发而跳动; - 切分单位是 grapheme cluster(
Intl.Segmenter),不会切裂 emoji / 组合字符 / 代理对;append-only 文本做增量切分记忆化; - 消息结束、工具调用边界等处同步 snap 到全文,保证最终内容与转录顺序不受平滑影响;回放(replay)路径因 snap 是同步的而不会被打字机拖慢;
- 配套提供开关(oh-my-pi 为
display.smoothStreaming,默认开启)。
相关文件(oh-my-pi 仓库):
packages/coding-agent/src/modes/controllers/streaming-reveal.ts(核心算法)packages/coding-agent/src/modes/controllers/tool-args-reveal.ts(工具参数流同样处理)
期望行为
- 流式期间,大模型的所有流式回复(assistant 正文、thinking、工具调用参数等)都以匀速/自适应加速的节奏出现,而不是跟随 token 到达的批量大小跳动;
- 平滑只影响流式期间的可见前缀,不改变消息最终内容、转录顺序与布局;
- 消息结束、工具调用开始时立即补全显示,无残余延迟;
- 提供逃生开关(配置项或环境变量),可一键退回现有行为。
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with apps/kimi-code/src/tui/controllers/streaming-ui.ts and src/tui/constant/streaming.ts to trace the current flush path, then inspect packages/pi-tui/src/components/markdown.ts and the referenced oh-my-pi reveal controllers. Done means streaming text, thinking, and tool arguments render smoothly without changing final content or ordering, completion boundaries snap to the full text, and an escape switch restores the current behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100