跳转到内容

RTK — Rust Token Killer 深度解析:AI 编程时代的 CLI 输出压缩框架

当 AI 编程助手(Claude Code、Copilot、Cursor 等)执行一条 git status 时,它需要消耗 token 来处理命令的原始输出。git status 输出的行数从几行到几十行不等,而 LLM 按 token 收费。

RTK(Rust Token Killer) 是一个高性能 CLI 代理,通过对命令输出进行智能过滤和压缩,实现 60-90% 的 token 节省。一个 30 分钟的 Claude Code 编程会话,原本需要消耗约 118,000 tokens,使用 RTK 后只需约 23,900 tokens,节省 80%。

项目地址:https://github.com/rtk-ai/rtk 官网:https://www.rtk-ai.app


1. 核心问题:LLM 编程助手正在浪费大量 token

Section titled “1. 核心问题:LLM 编程助手正在浪费大量 token”

AI 编程助手处理 CLI 命令输出时,面临几个低效问题:

  • 大量噪音:ANSI 颜色序列、进度条、空行、重复的日志行
  • 冗余信息:测试通过的信息占 80-90%,只需要知道哪些失败了
  • 格式浪费:详尽的 diff、完整的环境变量列表不需要完整传给 LLM
  • 上下文浪费:上下文窗口被无用的命令输出填满,有价值的信息被截断

RTK 的核心思路是:不是减少 LLM 的处理量,而是改变命令输出的形态,让同样的信息用更少的 token 表示。


RTK 采用命令代理(Command Proxy)架构,工作原理非常清晰:

LLM Agent RTK 原始命令 输出
───────────────────────────────────────────────────────────────
git status ──▶ rtk git status ──▶ git status ──▶ 原始输出
│ │
└─── 过滤压缩 ──▶ ~200 tokens ──▶ LLM
Phase 1: PARSE Clap 解析器提取命令和参数
Phase 2: ROUTE main.rs match 路由到专门 filter 模块
Phase 3: EXECUTE std::process::Command 执行原始命令
Phase 4: FILTER 专用模块过滤(正则、JSON、状态机)
Phase 5: PRINT 着色输出到终端
Phase 6: TRACK SQLite 记录 token 节省量

关键设计原则:

  • 单线程、无 async:启动时间 < 10ms,无运行时开销
  • 优雅降级:过滤失败时回退到原始输出,不影响命令执行
  • 退出码传播:RTK 永远不吞掉非零退出码,保证 CI/CD 可靠性
  • 透明代理:未知命令透传,零配置即可工作

RTK 的源码按生态系统组织:

src/cmds/
├── git/ # git status/log/diff/add/commit/push/branch
├── rust/ # cargo test/build/clippy
├── js/ # pnpm/npm/npx, vitest/jest/playwright, tsc/eslint/prettier, prisma
├── python/ # ruff, pytest, pip
├── go/ # go test/build/vet, golangci-lint
├── dotnet/ # dotnet build/test
├── cloud/ # aws, docker, kubectl
├── system/ # ls, read, grep, find, json
├── ruby/ # rake, rspec, rubocop
└── mod.rs # 入口

核心基础设施(src/core/):

  • tracking.rs — SQLite token 追踪
  • filter.rs — 代码过滤(aggressive/minimal/none)
  • tee.rs — 命令失败时的完整输出保存
  • config.rs — TOML 配置系统

这是 RTK 最核心的部分。根据不同命令的输出特征,RTK 使用了 12 种不同的过滤策略:

策略 1:统计信息提取(Stats Extraction)

Section titled “策略 1:统计信息提取(Stats Extraction)”
原始: git log --oneline -5
abc123 Fix authentication bug
def456 Add user profile page
...
5 files changed, 142 insertions(+), 89 deletions(-)
过滤后: 5 commits, +142/-89 ✓
节省: 90-99%

策略 2:仅错误模式(Error Only)

Section titled “策略 2:仅错误模式(Error Only)”
原始: stdout + stderr 混合输出
过滤后: 仅 stderr 中的错误信息
节省: 60-80%

策略 3:按规则分组(Grouping by Pattern)

Section titled “策略 3:按规则分组(Grouping by Pattern)”
原始: 100 个 lint 错误散落在各文件
error: no-unused-vars (×15, files: a.ts, b.ts, c.ts...)
error: semi: missing semicolon (×23, files: x.ts, y.ts...)
过滤后: no-unused-vars: 15 semi: 23 prefer-const: 8
节省: 80-90%
原始: [ERROR] 相同的日志行重复 50 次
过滤后: [ERROR] Connection timeout (×50)
节省: 70-85%
原始: {"user": {"id": 123, "name": "Alice", "token": "sk-xxx..."}}
过滤后: {"user": {"id": "...", "name": "...", "token": "..."}}
节省: 80-95%

策略 6:代码过滤(Code Filtering)

Section titled “策略 6:代码过滤(Code Filtering)”
FilterLevel::None → 保留全部(0% 节省)
FilterLevel::Minimal → 去除注释(20-40% 节省)
FilterLevel::Aggressive → 去除注释 + 函数体(60-90% 节省)
支持语言: Rust, Python, JS, TS, Go, C, C++, Java
原始: 运行 100 个测试,95 通过,5 失败
过滤后: FAILED: 5 tests
test_auth_login: assertion failed at auth.rs:42
test_payment_refund: panic at payment.rs:18
节省: 94-99%

策略 8:树压缩(Tree Compression)

Section titled “策略 8:树压缩(Tree Compression)”
原始: 50 个文件平铺列表
过滤后: my-project/
├─ src/ (8 files)
├─ tests/ (5 files)
└─ Cargo.toml
节省: 50-70%

策略 9:进度条过滤(Progress Filtering)

Section titled “策略 9:进度条过滤(Progress Filtering)”
原始: 下载进度条 ANSI 彩色动画
过滤后: ✓ Downloaded package.json
节省: 85-95%

策略 10:JSON/Text 双模式(JSON/TEXT Dual Mode)

Section titled “策略 10:JSON/Text 双模式(JSON/TEXT Dual Mode)”
当工具有 JSON 输出可用时(ruff check --format=json):
→ 解析结构化数据,精确提取错误位置和规则
当仅文本输出时:
→ 正则回退解析
节省: 80%+

策略 11:状态机解析(State Machine Parsing)

Section titled “策略 11:状态机解析(State Machine Parsing)”
pytest 输出格式是文本状态机:
test_utils.py::test_parse ... ok
test_utils.py::test_format ... FAILED
================ 1 failed, 17 passed ================
RTK 跟踪 IDLE → TEST_START → PASSED/FAILED → SUMMARY 状态
仅提取关键信息
节省: 90%+
go test 输出是 NDJSON 流(每行一个 JSON):
{"Action":"run","Package":"pkg1","Test":"TestAuth"}
{"Action":"fail","Package":"pkg1","Test":"TestAuth"}
逐行解析,聚合结果:
2 packages, 3 failures (pkg1::TestAuth, pkg1::TestPayment)
节省: 90%+

RTK 最强大的特性是自动改写钩子(Auto-Rewrite Hook)

当 LLM 代理要执行 git status 时,Hook 拦截并改写为 rtk git status,整个过程对 LLM 透明。

Terminal window
rtk init -g # Claude Code(推荐)
rtk init -g --agent cursor # Cursor
rtk init -g --gemini # Gemini CLI
rtk init -g --codex # OpenAI Codex
1. LLM Agent 准备执行: git status
2. settings.json 中注册的 PreToolUse Hook 被触发
3. Hook 调用: rtk rewrite "git status"
4. rtk rewrite 查询命令注册表,返回: rtk git status
5. Hook 修改 Agent 的执行命令为: rtk git status
6. Agent 执行: rtk git status ──▶ 原始 git status
└─── 过滤压缩 ──▶ ~200 tokens

复合命令的处理非常精妙。以 cargo fmt --all && cargo test 2>&1 | tail -20 为例:

Step 1: 词法分析(Lexer)
cargo fmt --all && cargo test 2>&1 | tail -20
─────────────────────────────────────────────
Tokenize → [cargo][fmt][--all][&&][cargo][test][2>&1][|][tail][-20]
Step 2: 按操作符分割,独立改写
cargo fmt --all && cargo test 2>&1 | tail -20
├─ rtk cargo fmt ─┤ ├─ rtk cargo test ──┤ ├─ 保持原样 ─┤
rewrite rewrite 不 rewrite
Step 3: 重新组装
rtk cargo fmt --all && rtk cargo test 2>&1 | tail -20

关键设计:

  • &&; 两边都改写
  • | 管道只改写左边,右边保持原样(管道右侧通常是 head/wc 等消费端)
  • find/fd 在管道前永远不改写(输出格式与 xargs 不兼容)
  • 改写失败的片段保持原样,不会因为一个失败拖累整个命令
工具安装方式方法
Claude Codertk init -gPreToolUse Hook
GitHub Copilot (VS Code)rtk init -g --copilotPreToolUse Hook
Cursorrtk init -g --agent cursorpreToolUse Hook
Gemini CLIrtk init -g --geminiBeforeTool Hook
Windsurfrtk init --agent windsurf.windsurfrules
Cline / Roo Codertk init --agent cline.clinerules
OpenCodertk init -g --opencodeTS Plugin
OpenClawopenclaw plugins installPlugin TS
Hermesrtk init --agent hermesPython Plugin 适配器
Codex CLIrtk init -g --codexAGENTS.md
Kilo Codertk init --agent kilocode.kilocode/rules
Google Antigravityrtk init --agent antigravity.agents/rules
Mistral Vibe计划中等待上游支持

以一个 30 分钟的 Claude Code 编程会话为例:

操作频率原始 tokensRTK tokens节省
ls / tree10x2,000400-80%
cat / read20x40,00012,000-70%
grep / rg8x16,0003,200-80%
git status10x3,000600-80%
git diff5x10,0002,500-75%
git log5x2,500500-80%
git add/commit/push8x1,600120-92%
cargo test / npm test5x25,0002,500-90%
ruff check3x3,000600-80%
pytest4x8,000800-90%
go test3x6,000600-90%
docker ps3x900180-80%
合计~118,000~23,900-80%

当命令执行失败(非零退出码)时,RTK 有一个贴心的TEE 恢复机制

Terminal window
# cargo test 有 2 个测试失败
FAILED: 2/15 tests
[full output: ~/.local/share/rtk/tee/1707753600_cargo_test.log]

RTK 将完整的原始输出保存到文件,并在输出末尾附上文件路径。LLM 可以直接读取该文件来分析错误,而不需要重新执行命令(避免副作用)。


RTK 提供可选的匿名遥测(默认关闭),收集的内容完全匿名:

  • RTK 版本、操作系统、架构、安装方式
  • 命令计数、节省 token 量(24h/30d/总)
  • 解析失败次数、节省率 < 30% 的命令(用于发现需改进的过滤器)
  • 生态系统分布(git 45%, cargo 20%, js 15%…)

不收集:源代码、文件路径、命令参数、密钥、个人信息。


RTK 选择 Rust 有几个关键原因:

  1. 启动速度 < 10ms:Rust 无 GC,单线程二进制启动极快,不给 CLI 增加感知延迟
  2. 内存 < 5MB:lazy_static 延迟编译正则,全局借用,无多余分配
  3. 二进制体积 < 5MB:strip 后 ~5MB,单文件分发,无需运行时
  4. 错误处理:anyhow::Result + .context() 模式,filter 失败永远回退原输出

除了内置的 42 个 Rust 过滤器,RTK 还支持 TOML DSL 声明式过滤器:

[filters.my-project-lint]
command = "npm run lint"
strip = ["^\\s*$", "^✨.*$"]
truncate = 50
on_empty = "No lint errors"

可以放在:

  • 内置(编译进二进制)
  • 全局:~/.config/rtk/filters/
  • 项目级:.rtk/filters/(需要 trust 验证)

Claude Code 的 ReadGrepGlob 是内置工具,不经过 Bash Shell,所以 Hook 无法拦截。RTK 的文档建议:对这类操作使用 shell 命令(cat/head/tailrgfind)或直接调用 rtk readrtk greprtk find


RTK 是一个设计精良的 CLI 输出压缩框架,核心贡献在于:

  1. 系统性思维:不是做一个 git status 的过滤器,而是构建了一个完整的代理架构和过滤器框架
  2. 12 种过滤策略:针对不同输出类型(JSON、文本状态机、NDJSON、日志去重等)设计专门的解析器
  3. 透明自动化:Hook 系统让 LLM 零感知地获得压缩后的输出
  4. Rust 实现:极低的运行时开销,让压缩本身不成为瓶颈
  5. 可扩展:TOML DSL + 完整的贡献指南,降低了新过滤器开发的门槛

对于频繁使用 AI 编程助手的开发者,RTK 几乎是一个必装工具——它不仅节省 token 成本,更重要的是扩大了 LLM 上下文窗口的有效利用范围,让同样的上下文能处理更长的项目历史和更大的代码库。


参考链接: