Files
AgentCompany/docs/proposals/C3-real-tokenizer-followup.md
T

46 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Proposal C3-followup: 用真实 tokenizer 替换字符估算
> 来源:Roundtable 2026-04-20 surgical compactor 修复;C3 首轮决定先用保守常量 (2 chars/token) 落地
> 状态:implemented(2026-04-22:`js-tiktoken` / `cl100k_base` 已接入 `context-compactor.ts`)
> 提出:Karpathy (T2)
> Owner: TBD
> 估计工时:1 天(换依赖)+ 半天(灰度验证)
> Blocking dependencies:无硬阻塞;等 pi-ai 暴露 tokenizer 或接入 tiktoken/anthropic-tokenizer
## 问题陈述
`src/main/agent-runtime/context-compactor.ts` 曾用 `Math.ceil(chars / 2)` 做 token 估算,作为 120k 阈值的守门人。2026-04-22 已改成 `js-tiktoken` 的 `cl100k_base` 本地编码器,并保留每条 chat message 的固定结构开销。
问题:
1. **中文 BPE 实际约 1.5-2 tokens/char**:2 chars/token 的常量对 CJK 大致可用,但 mixed 中英 / emoji / code block 的实际比例会漂移。
2. **对拉丁文过度保守**:英文 BPE 约 4 chars/token,用 2 chars/token 会让英文 agent 的阈值提前 50% 触发——这是安全的方向(宁可早触发不要晚触发),但浪费了压缩空间。
3. **无法感知 tool 定义 / system prompt 开销**:`estimateTokens` 只数 messages 里的文本,tool schema JSON 和 system prompt 的 token 都没计。真实 input tokens 比估算大。
4. **Anthropic prompt caching 的 breakpoint 计算需要精确 token 数**:字符估算拿不到 cache hit / miss 的决策依据,未来接 cache 优化时必须换。
## 根因
pi-ai 0.67.68 没 export tokenizer。Compactor 作为 `transformContext` hook 在 renderer / main 都可能跑,依赖必须能在 Node 和 Electron main process 里用。
备选方案:
- **tiktoken** (OpenAI BPE):快,tree-shakable,但对 Anthropic 模型的真实 token 数只是近似(Anthropic 用私有 BPE,两者差异 5-10%)。
- **@anthropic-ai/tokenizer**:精确,但包体积较大(~2MB),且 Anthropic 自己建议用 `count_tokens` API 而不是本地估算(原因:模型更新会换 tokenizer)。
- **pi-ai 升级暴露 tokenizer**:理论上最干净,但要等上游;pi-mono 仓 `packages/ai/src/utils/` 目前只有 `overflow.ts` 做错误匹配,没 tokenizer utility。
- **Anthropic `count_tokens` API**:精确,但每次 compactor 估算都要额外 HTTP round-trip,违反 "compactor 必须是纯函数" 的现有契约。
## 建议路径
1. 已接入 `js-tiktoken` + `cl100k_base`,保持 compactor 同步纯函数。
2. pi-ai 后续若暴露 provider-native tokenizer,可在 `context-compactor.ts` 内替换编码器实现。
3. hook 调用保持不变,`compactMessagesIfNeeded(messages)` 继续返回原有 `CompactionOutcome`。
## 不做的事
- 不引入 `count_tokens` API 调用:compactor 必须是纯同步函数,不能发网络。
- 不做"运行时采样真实 tokenizer 校准常量"的启发式:引入额外系统状态,违反 let it crash。
## 验证标准
- 英文 + 中文 + code block 三种典型 transcript,估算值与 Anthropic 真实 input tokens 误差 ≤ 10%。
- 120k 阈值触发时机提前/延后不超过真实阈值的 5%。
- typecheck 全绿;现有 `compactMessagesIfNeeded` 调用点不动。