TUI 流式文本在 Kimi 模型下显示卡顿 / 一跳一跳的
问题描述
在 kimi-code 的 TUI 中使用 Kimi 模型 时,助手回复的流式显示不够平滑,文字会明显“一跳一跳”地更新。同样的代码在 DeepSeek 模型 下就流畅很多。
同一个 Kimi 模型,在 oh-my-pi 的 TUI 中流式输出是流畅的。这说明卡顿不是 Kimi API 本身的问题,而是 kimi-code 当前的渲染/flush 策略和 Kimi provider 的 delta 节奏不匹配。
oh-my-pi 设置中的 Smooth Streaming 选项(已开启),其说明为 "Reveal assistant text and streamed tool input smoothly while chunks arrive":

复现步骤
- 启动
kimi-code 并使用 Kimi 模型(例如 kimi k2.7 code)。
- 请求一个较长的 prose 回复,例如“给我讲讲这个代码库”。
- 观察输出:字符以短而不规则的 bursts 出现,视觉上卡顿。
- 切换到 DeepSeek 模型,使用同样的 prompt,输出明显更平滑。
原因分析
-
固定 50ms flush 窗口
apps/kimi-code/src/tui/constant/streaming.ts:
export const STREAMING_UI_FLUSH_MS = 50;
第一个 delta 立即 flush,但后续 delta 必须等至少 50ms。Kimi provider 的 delta 间隔较长、每次内容较少,导致 UI 按 provider 的节奏“蹦字”显示。
-
每次 flush 都重建 Markdown 组件
apps/kimi-code/src/tui/components/messages/assistant-message.ts:
updateContent(text: string): void {
this.contentContainer.clear();
if (displayText.trim().length > 0) {
this.contentContainer.addChild(new Markdown(displayText.trim(), 0, 0, createMarkdownTheme()));
}
}
每次更新都 clear() 并 new Markdown(...),导致 pi-tui 的缓存完全失效,每次都要对全文重新解析、重新折行。消息越长越卡。
-
缺少平滑 reveal 层
当前 UI 直接显示当前累积的全部文本,没有动画层把不均匀的 delta 流转换成稳定帧率的逐字显示。Kimi 的 delta 节奏一不均匀,观感就是卡顿。
修复建议
参考 oh-my-pi 的做法:
-
复用 Markdown 实例
在 AssistantMessageComponent 中不要每次 updateContent 都重建 Markdown,而是持有一个实例,只调用 setText(fullText)。thinking.ts 里已经这么做了,可以照搬。
-
降低或自适应 flush 间隔
把 STREAMING_UI_FLUSH_MS 从 50ms 降到 20–30ms,或者改成自适应策略:delta 来得密集时合并,间隔较长时立即 flush。
-
增加平滑 reveal 动画层
在 StreamingUIController 和 AssistantMessageComponent 之间加一个 StreamingRevealController:
- 维护一个已显示的 grapheme 计数。
- 以固定 30fps 逐帧推进。
- 每帧至少推进几个字符,最多推进剩余未显示字符的 1/8。
- 消息结束时(
message_end)再一次性显示完整文本并做语法高亮。
这样不管 API 多久来一个 delta,TUI 都按自己的节奏平滑显示。
-
流式渲染跳过同步语法高亮
给流式中的渲染加一个 transient 标记,最终消息到达前不做同步 highlightCode,结束时再补一次完整渲染。
参考实现
oh-my-pi-main/packages/coding-agent/src/modes/controllers/streaming-reveal.ts
oh-my-pi-main/packages/coding-agent/src/modes/components/assistant-message.ts
oh-my-pi-main/packages/tui/src/components/markdown.ts
相关代码
apps/kimi-code/src/tui/components/messages/assistant-message.ts
apps/kimi-code/src/tui/components/messages/thinking.ts
apps/kimi-code/src/tui/controllers/streaming-ui.ts
apps/kimi-code/src/tui/constant/streaming.ts
TUI 流式文本在 Kimi 模型下显示卡顿 / 一跳一跳的
问题描述
在
kimi-code的 TUI 中使用 Kimi 模型 时,助手回复的流式显示不够平滑,文字会明显“一跳一跳”地更新。同样的代码在 DeepSeek 模型 下就流畅很多。同一个 Kimi 模型,在 oh-my-pi 的 TUI 中流式输出是流畅的。这说明卡顿不是 Kimi API 本身的问题,而是
kimi-code当前的渲染/flush 策略和 Kimi provider 的 delta 节奏不匹配。复现步骤
kimi-code并使用 Kimi 模型(例如kimi k2.7 code)。原因分析
固定 50ms flush 窗口
apps/kimi-code/src/tui/constant/streaming.ts:第一个 delta 立即 flush,但后续 delta 必须等至少 50ms。Kimi provider 的 delta 间隔较长、每次内容较少,导致 UI 按 provider 的节奏“蹦字”显示。
每次 flush 都重建 Markdown 组件
apps/kimi-code/src/tui/components/messages/assistant-message.ts:每次更新都
clear()并new Markdown(...),导致pi-tui的缓存完全失效,每次都要对全文重新解析、重新折行。消息越长越卡。缺少平滑 reveal 层
当前 UI 直接显示当前累积的全部文本,没有动画层把不均匀的 delta 流转换成稳定帧率的逐字显示。Kimi 的 delta 节奏一不均匀,观感就是卡顿。
修复建议
参考
oh-my-pi的做法:复用 Markdown 实例
在
AssistantMessageComponent中不要每次updateContent都重建Markdown,而是持有一个实例,只调用setText(fullText)。thinking.ts里已经这么做了,可以照搬。降低或自适应 flush 间隔
把
STREAMING_UI_FLUSH_MS从 50ms 降到 20–30ms,或者改成自适应策略:delta 来得密集时合并,间隔较长时立即 flush。增加平滑 reveal 动画层
在
StreamingUIController和AssistantMessageComponent之间加一个StreamingRevealController:message_end)再一次性显示完整文本并做语法高亮。这样不管 API 多久来一个 delta,TUI 都按自己的节奏平滑显示。
流式渲染跳过同步语法高亮
给流式中的渲染加一个
transient标记,最终消息到达前不做同步highlightCode,结束时再补一次完整渲染。参考实现
oh-my-pi-main/packages/coding-agent/src/modes/controllers/streaming-reveal.tsoh-my-pi-main/packages/coding-agent/src/modes/components/assistant-message.tsoh-my-pi-main/packages/tui/src/components/markdown.ts相关代码
apps/kimi-code/src/tui/components/messages/assistant-message.tsapps/kimi-code/src/tui/components/messages/thinking.tsapps/kimi-code/src/tui/controllers/streaming-ui.tsapps/kimi-code/src/tui/constant/streaming.ts