abtop上下文窗口计算原理:为什么cache_creation token会被重复计数,以及如何避免
【免费下载链接】abtopLike htop, but for AI coding agents. Monitor Claude Code & Codex CLI sessions, tokens, context window, rate limits, and ports in real-time.项目地址: https://gitcode.com/gh_mirrors/ab/abtop
abtop是一个面向 AI 编程智能体的实时监控系统,就像经典系统监控工具 htop 之于 CPU 和内存——它让你在一块屏幕上同时查看所有 Claude Code、Codex CLI 与 OpenCode 会话的 token 消耗、上下文窗口占用百分比、速率限制和开放端口。在它的 context 面板 中,上下文占用率只用input_tokens + cache_read_input_tokens来计算,刻意排除了 cache_creation。为什么这么做?如果天真地把三种 token 相加会发生什么?这篇指南带你彻底搞懂 abtop 上下文窗口的计算原理。
先认识 abtop:AI Agent 版的 htop
abtop 全程只读本地文件与进程状态,不需要 API 密钥,也不需要登录。启动后你会看到每个会话一行状态:模型版本、运行时长、上下文百分比、token 速率,以及每个会话独立的上下文窗口进度条。
要理解上下文计算原理,先要知道 abtop 的数据来自哪里。对 Claude Code 而言,每个会话都会把完整对话记录追加写入一个 JSONL 转录文件(格式见 AGENTS.md)。每条assistant消息里都带着一段usage统计:
| 字段 | 含义 |
|---|---|
input_tokens | 本轮未命中缓存、按全价重新发送的输入 token |
output_tokens | 本轮模型生成的输出 token |
cache_read_input_tokens | 本轮命中提示缓存、直接读出的输入 token |
cache_creation_input_tokens | 本轮新写入提示缓存的输入 token |
提示缓存(prompt caching)是 Anthropic 的计费与加速机制:把冗长的系统提示、工具定义和历史对话缓存起来,下一轮直接读取,既快又省钱。上面四个字段就是 abtop 计算上下文的全部原料。
abtop 上下文窗口占用的计算公式
abtop 的核心规则只有一句话:上下文 = 最近一条 assistant 消息的 input_tokens + cache_read_input_tokens。
对应的源码在 src/collector/claude.rs:
// Context = input_tokens + cache_read (excludes cache_creation, #54) let current_context = if cr == 0 && cc > 0 { inp + cc } else { inp + cr }; result.last_context_tokens = current_context;得到last_context_tokens后,再除以该模型的上下文窗口上限,就得到面板上那根百分比进度条:
- 窗口上限由 context_window_for_model() 决定:默认200K;当模型名带
[1m]标记或实测上下文已超过 200K 时,自动切换为1M; context_percent = last_context_tokens / context_window × 100,超过 75% 会显示!、超过 90% 显示⚠警告(渲染逻辑在 src/ui/context.rs)。
为什么 cache_creation 会被"重复计数"
这是整篇文章的关键问题。先看一个正常轮次的 token 分布:
input_tokens = 2 ← 少量新增内容 cache_read_input_tokens = 11313 ← 大部分历史走缓存命中 cache_creation_input_tokens = 4350 ← 本轮新内容写入缓存直觉上,"上下文总大小"似乎应该是三者之和。但项目维护文档 AGENTS.md 明确指出:
在compaction(上下文压缩)轮次中,同一批 token 会同时被报告为
cache_creation和cache_read——旧缓存被作废旧重写,写入了新的缓存条目,同时新前缀又被命中。三项相加,这批 token 就被计了两次。
这正是 abtop 历史上 issue #54 的根源:如果简单相加,一次压缩之后上下文占用率会瞬间虚高,甚至"突破"窗口上限,你会误以为会话要爆了,其实真实占用根本没变。所以 abtop 选择只信input + cache_read这个口径——它也与 Claude Code 自带的 statusline 显示保持一致(参考 scripts/abtop-statusline.sh)。
特例:新会话为什么又要加 cache_creation?
如果永远排除 cache_creation,会漏掉另一种场景。
会话刚开始时,还没有任何缓存可命中,第一条 assistant 消息的报告通常是:cache_read = 0,而cache_creation很大(整个初始提示正在被写入缓存)。此时真正代表"当前上下文大小"的就是input_tokens + cache_creation_input_tokens,若仍按input + cache_read算,会读出接近 0% 的假象。
所以源码里那个if就是干这个的:当cache_read == 0且cache_creation > 0时,改用input + cache_creation(相关测试见 test_parse_transcript_fresh_session_uses_cache_creation_for_context)。
一句话总结这套口径:
| 场景 | 缓存状态 | 上下文取法 |
|---|---|---|
| 常规轮次 | 有缓存命中 | input + cache_read |
| 压缩轮次 | 同一批 token 双份上报 | input + cache_read(不能加 cache_creation) |
| 新会话首轮 | 无缓存、正在写缓存 | input + cache_creation |
如何避免重复计数:4 条实用建议
✅自己写状态行/监控时,坚持input + cache_read口径。这是与 Claude Code 官方 statusline 对齐的做法,天然免疫压缩轮次的双份上报。
✅用"断崖式下跌"识别 compaction,而不是绝对值。abtop 的判断条件是:上下文相比上一轮骤降超过 30%,且cache_read相比上轮跌至原来的 1/5 以下(旧缓存被整体作废)。单看某一项波动都可能误报,两个条件同时成立才算一次真正的压缩(检测逻辑在 src/collector/claude.rs)。压缩次数会显示在上下文栏的C2这类标记里。
✅区分"缓存命中波动"和"真实上下文变化"。普通对话中缓存命中率自然起伏,总上下文量会有正常抖动;只要没有断崖式下跌,就不该当作窗口将满的信号。
✅留意窗口上限会动态变化。同一模型在[1m]配置下上限是 1M 而非 200K,固定写死 200K 会在新会话里算出虚高的百分比。abtop 的做法是同时参考转录中的模型名与实测最大值,自动选择上限。
小结
- abtop 的上下文占用 =
input_tokens + cache_read_input_tokens÷ 模型窗口上限,cache_creation 默认不计入; - 重复计数发生在 compaction 轮次:同一批 token 被同时报成
cache_creation与cache_read,三项相加即虚高(#54); - 唯一例外是新会话首轮
cache_read = 0的场景,此时cache_creation才代表真实上下文大小; - 记住"两项之和 + 一个例外",你就能在任何 AI Agent 监控工具里算出不会骗人的上下文窗口占用。
【免费下载链接】abtopLike htop, but for AI coding agents. Monitor Claude Code & Codex CLI sessions, tokens, context window, rate limits, and ports in real-time.项目地址: https://gitcode.com/gh_mirrors/ab/abtop
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考