[Feature] 流式回复期间明确 Token 统计状态
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 问题描述
当前流式回复开始后,状态栏中的 Token 数量可能显示为 `0`,并在回复结束后才一次性变为最终值。
这不一定表示本轮没有消耗 Token,而是因为部分模型或代理只在 API call 结束时返回 usage。当前界面没有区分“尚未收到 usage”和“实际 Token 为 0”,容易造成误解。
## 复现步骤
1. 新建会话,选择 Pi。
2. 发送一条需要生成较长中文内容的提示词,例如:
> 请不要调用工具。请一次性撰写一份不少于 3000 个中文字符的《Cindy 灰度发布回归方案》。必须依次包含:一、背景与目标;二、按 1-7 编号的执行步骤,每步至少两句;三、一个 5 列、至少 5 行的 Markdown 表格;四、一个不少于 20 行的 TypeScript 代码块;五、风险与回滚方案。不要省略任何章节,不要用“内容同上”或占位文本。
3. 在正文持续生成期间观察 Token 状态。
4. 使用相同提示词切换 Claude 或其他模型重复测试。
## 实际结果
- 部分模型在流式开始和正文生成期间显示 `0`。
- 回复结束后,Token 数量一次性更新为最终值。
- 如果 `message_start` 提供了输入 Token,界面可能显示一个固定的初始值,但该值不代表输出 Token 正在实时累计。
- 不同模型经过相同 Responses bridge 时,可能表现出相同的初始 `0`。
## 原因定位
当前 Token 统计依赖上游 usage 事件,而不是根据流式正文增量本地估算。
- Pi 在 `agent_start` 时重置 Token;`message_update` 只处理正文;`message_end` 才应用 usage。
- Claude 在 `content_block_delta` 中只处理正文和 thinking;usage 主要在 `message_start` 或 `message_delta` 处理。
- `anthropic-responses-bridge` 生成的 `message_start` 会填充全为 `0` 的 usage,上游真实 usage 只在响应完成时写入 `message_delta`。
相关源码:
- [`packages/maker-core/src/agents/pi/translator.ts`](https://github.com/makecindy/cindy/blob/main/packages/maker-core/src/agents/pi/translator.ts)
- [`packages/maker-core/src/agents/claude-code/translator.ts`](https://github.com/makecindy/cindy/blob/main/packages/maker-core/src/agents/claude-code/translator.ts)
- [`packages/anthropic-responses-bridge/src/translate-sse.ts`](https://github.com/makecindy/cindy/blob/main/packages/anthropic-responses-bridge/src/translate-sse.ts)
- [`packages/maker-core/src/agents/shared/usage-tracker.ts`](https://github.com/makecindy/cindy/blob/main/packages/maker-core/src/agents/shared/usage-tracker.ts)
定位版本:`origin/main@3a3404b64fb248dbddcf689d5a4016aafe438cf5`
## 预期结果
1. 尚未收到 usage 时,不显示容易误解的 `0`,可显示“统计中”或 `--`。
2. 收到上游权威 usage 后,显示真实 Token 数量。
3. 如果未来增加本地估算,应明确标注为“估算”,并在回复结束后使用权威 usage 校准。
4. 不要求所有模型在流式过程中提供真实的逐 Token 增长,也不要求通过文本长度推断精确 Token。
## 验收标准
- Pi、Claude 以及经 Responses bridge 的模型,在 usage 尚未返回时不会将状态误导为“Token 为 0”。
- usage 返回后仍显示准确的最终 Token。
- 多 API call、工具调用循环、缓存 Token 和重试不会重复累计。
- 状态文案能够区分“统计中”“估算值”和“权威值”。
- 不改变现有最终 Token、上下文占用和费用统计逻辑。
## 补充说明
旧版本截图中出现的 `7.9k tokens`,可能是 API 开始时返回的输入 Token,并不能证明输出 Token 在流式过程中动态累计。
Contributor guide
Research direction
Start by tracing usage handling in packages/maker-core/src/agents/shared/usage-tracker.ts, then compare the Pi and Claude translators and the SSE translation in packages/anthropic-responses-bridge/src/translate-sse.ts. Verify how agent_start, message_start, message_update, message_delta, and message_end affect displayed usage. Done means pending usage is distinguishable from zero, authoritative usage remains accurate across calls, tools, caching, and retries, and final context and cost statistics are unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100