问题概述
当 远程访问 pi-web 时(通过 VPN / 公网),带宽消耗极大:events 流在每次流式更新时都会重发已累积的完整消息。实测放大倍数约 150 倍:模型输出 ~15 KB 内容,实际传输 ~2.2 MB;thinking: high 时可达 6 MB 以上。
环境
- pi-web 0.8.6,
@earendil-works/pi-coding-agent 0.83.0
- 浏览器客户端通过 VPN 隧道访问(高延迟 / 按流量计费的网络下尤其明显)
根因
RPC 协议中每条 message_update 事件都携带完整的 message 对象(已生成的全部 text/thinking 累积内容)以及 assistantMessageEvent delta(见 docs/rpc.md):
{"type":"message_update","message":{...完整累积消息...},"assistantMessageEvent":{"type":"text_delta","delta":"Hello"}}
pi-web 的 events 路由(.next/server/app/api/agent/[id]/events/route.js)把这些事件几乎原样转发给浏览器——只删掉了 assistantMessageEvent 字段:
let j = new Set(["turn_start","turn_end","tool_execution_update"]);
// ...
if ("message_update" === a.type) {
let b = { ...a };
delete b.assistantMessageEvent; // 保留了完整的 message
return b;
}
前端(.next/static/chunks/app/page-*.js)则在每次 message_update 时重新渲染整条消息。
由于流式输出大约每 50 字符产生一个更新块,每个块都重发此前生成的全部内容 → O(n²) 传输:
| 场景(回答 10,000 字符) |
实际发送到浏览器的字节 |
相对内容的放大倍数 |
| thinking off |
~1.0 MB |
~105x |
| thinking low |
~1.4 MB |
~120x |
| thinking high |
~6.2 MB |
~255x |
| 理想增量传输(仅 delta) |
~15 KB |
1x |
本机访问无感;远程通过 VPN / 按流量计费的网络使用时问题很明显。
建议修复方向(任一均可)
- 服务端节流合并(最简单,无需改前端):对
message_update 事件做短时间窗口缓冲(如 50–100 ms),每个窗口只发最新一条完整 message。不用动前端即可消除 O(n²) 曲线。
- 前端增量渲染:用
assistantMessageEvent 的 delta 增量拼接,而不是每次事件都重新渲染完整消息。
- 两者结合:服务端对突发流做合并,前端做 delta 追加渲染,兼顾平滑度。
如果维护者认可方案 1,我可以提交对应 PR。
问题概述
当 远程访问 pi-web 时(通过 VPN / 公网),带宽消耗极大:events 流在每次流式更新时都会重发已累积的完整消息。实测放大倍数约 150 倍:模型输出 ~15 KB 内容,实际传输 ~2.2 MB;
thinking: high时可达 6 MB 以上。环境
@earendil-works/pi-coding-agent0.83.0根因
RPC 协议中每条
message_update事件都携带完整的 message 对象(已生成的全部 text/thinking 累积内容)以及assistantMessageEventdelta(见docs/rpc.md):{"type":"message_update","message":{...完整累积消息...},"assistantMessageEvent":{"type":"text_delta","delta":"Hello"}}pi-web 的 events 路由(
.next/server/app/api/agent/[id]/events/route.js)把这些事件几乎原样转发给浏览器——只删掉了assistantMessageEvent字段:前端(
.next/static/chunks/app/page-*.js)则在每次message_update时重新渲染整条消息。由于流式输出大约每 50 字符产生一个更新块,每个块都重发此前生成的全部内容 → O(n²) 传输:
本机访问无感;远程通过 VPN / 按流量计费的网络使用时问题很明显。
建议修复方向(任一均可)
message_update事件做短时间窗口缓冲(如 50–100 ms),每个窗口只发最新一条完整 message。不用动前端即可消除 O(n²) 曲线。assistantMessageEvent的 delta 增量拼接,而不是每次事件都重新渲染完整消息。如果维护者认可方案 1,我可以提交对应 PR。