> 参考对象:Rust 社区那种"把底层机制暴露给你看"的工程文档风格,像看 ripgrep 或 fd 的源码一样直接。
一、问题:命令行输出是 Token 黑洞
用 Claude Code 或 Cursor 写代码时,AI 助手会频繁调用 shell 工具——git status、cargo test、ls -la、docker ps。这些命令的原始输出充斥着对 AI 无用的信息:权限位、时间戳、进度条、重复日志、ASCII 装饰线。
RTK 的 README 给了一组典型数据:一次 30 分钟的 Claude Code 会话,如果不做任何处理,命令行输出会吃掉约 118,000 个 token。经过 RTK 过滤后,这个数字降到 23,900——省了近 80%。按 Claude 3.5 Sonnet 的定价,这相当于每次会话少花 1-2 美元。
但"省 80%"只是结果。真正值得看的是:它怎么做到的?安全吗?对不同 AI 工具怎么接入?源码里有没有藏坑?
二、核心架构:四种过滤模式 + 块级流式引擎
RTK 是用 Rust 写的单二进制 CLI。它的核心不是"压缩算法",而是一套命令输出重写系统——先识别你跑的是什么命令,再决定怎么重写这条命令的调用方式,最后对输出做实时过滤。
2.1 四种 FilterMode
从 src/core/stream.rs 可以看到,run_streaming 函数支持四种 stdout 处理模式:
| 模式 | 行为 | 适用场景 |
|---|---|---|
| Streaming | 逐行实时过滤,边读边输出 | 大多数命令(git status、test 输出) |
| Buffered | 等命令跑完,一次性处理整个输出 | 需要全局上下文的过滤(如去重) |
| CaptureOnly | 只捕获不过滤,用于统计原始 token 数 | 遥测和对比基准 |
| Passthrough | 完全透传,RTK 只做代理不做任何事 | 未识别命令的安全回退 |
2.2 块级过滤引擎:BlockStreamFilter
真正干活的是 BlockStreamFilter。它的工作方式像编译器的词法分析器:
// 伪代码,基于 stream.rs 的 trait 定义
trait BlockHandler {
fn should_skip(&mut self, line: &str) -> bool; // 这行是不是噪音?
fn is_block_start(&mut self, line: &str) -> bool; // 这是不是个新块的开始?
fn is_block_continuation(&mut self, line: &str, block: &[String]) -> bool; // 继续这个块?
fn format_summary(&self, exit_code: i32, raw: &str) -> Option<String>; // 最后补个摘要
}
举个例子,cargo test 的输出被 cargo 模块的 handler 处理时:
1. should_skip 跳过 Compiling foo...、Downloading... 这类进度噪音
2. is_block_start 识别 test utils::test_parse ... ok 或 FAILED: 行
3. is_block_continuation 收集失败测试的堆栈跟踪(缩进行)
4. format_summary 最后输出:FAILED: 2/15 tests
关键实现细节:
- 10 MiB 硬上限 (
RAW_CAP = 10_485_760):防止某个命令输出爆炸把内存撑爆 - ChildGuard RAII:
struct ChildGuard(std::process::Child)在 Drop 时主动wait(),防止僵尸进程(源码注释提到这修复了 kernel panic 的问题) - 双线程 + mpsc:stdout 和 stderr 各一个线程读取,通过 channel 合并到主线程处理,避免阻塞
2.3 按语言分配过滤策略
src/core/filter.rs 里有一个 Language 枚举,覆盖了 12 种语言/工具链:
enum Language {
Rust, Python, JavaScript, TypeScript, Go,
C, Cpp, Java, Ruby, Shell, Data, Unknown
}
不同语言的构建/测试工具输出格式完全不同,所以 RTK 不是"一个通用压缩器",而是每个生态有专门的 handler。从 src/main.rs 可以看到它 re-export 了几十个命令模块:git、go、js、python、ruby、dotnet、cloud、jvm...每个模块有自己的 BlockHandler 实现。
这解释了为什么 RTK 能处理 100+ 命令——它不是正则表达式硬匹配,而是结构化地理解每种工具的输出格式。
三、Hook 机制:透明拦截,零学习成本
RTK 最有效的使用方式不是让用户手动打 rtk git status,而是让 AI 助手自己把 git status 改成 rtk git status。
3.1 工作原理
从 src/hooks/rewrite_cmd.rs 可以看到核心逻辑:
const ENV_PREFIX: &str = "rtk_cmd=";
fn check_command(raw: &str) -> (Option<Classification>, Option<PermissionVerdict>) {
// 1. 匹配注册表 RULES
// 2. 返回分类和权限裁决
}
AI 工具(如 Claude Code)在调用 Bash 工具前,会先执行一个 hook。这个 hook 把原始命令(如 git status)传给 rtk hook rewrite,RTK 返回:
| Exit Code | 含义 | 行为 |
|---|---|---|
| 0 | Allow | 命令被重写为 rtk git status,hook 自动放行 |
| 1 | Default / No RTK equivalent | 没有对应的 RTK 过滤器,直接透传原命令 |
| 2 | Deny | 命中拒绝规则,交给原生 deny 处理 |
| 3 | Ask | 命中询问规则,重写但提示用户确认 |
git status,hook 悄悄改成 rtk git status,Claude 拿到的是压缩后的输出,完全无感知。3.2 注册表:100+ 命令怎么分类
src/discover/registry.rs 定义了命令分类系统:
enum Classification {
Supported {
rtk_equivalent: String, // "rtk git status"
category: String, // "git"
estimated_savings_pct: f64, // 80.0
status: FilterStatus, // Stable / Beta / Experimental
},
Unsupported { base_command: String }, // 有命令但无 RTK 等价物
Ignored, // 明确忽略(如 cd、echo)
}
RULES 常量(编译期确定)包含所有支持命令的正则匹配规则。RegexSet 用于批量匹配,IGNORED_EXACT 和 IGNORED_PREFIXES 定义白名单——这些命令不会被尝试重写,避免无意义的 hook 开销。
四、Token 统计:SQLite 持久化 + 项目级追踪
RTK 不只是帮你省钱,还帮你看见省了多少钱。
src/core/tracking.rs 实现了一套完整的统计系统:
4.1 数据库设计
- 位置:
~/.local/share/rtk/tracking.db(SQLite) - WAL 模式 + busy_timeout,支持多个 Claude Code 实例并发访问
- 90 天自动清理(
cleanup_old) - 两张表:
commands(执行记录)和parse_failures(解析失败分析)
4.2 记录什么
每条记录包含:
- 原始命令、RTK 命令
- 输入 token 数(原始输出长度估算)
- 输出 token 数(过滤后长度)
- 节省 token 数和节省百分比
- 执行时间(毫秒)
- 项目路径(project-scoped tracking)
4.3 查询能力
rtk gain 命令可以输出:
- 总览:总命令数、总输入/输出 token、平均节省率
- 按命令 Top 10(哪些命令帮你省最多)
- 按天/周/月时间线
- 按项目过滤:只看你当前项目的统计数据
GLOB 而不是 LIKE,因为路径里经常有 _,而 LIKE 会把 _ 当单字符通配符。这体现了 Rust 工程思维——边界 case 提前处理。4.4 遥测(可选、匿名、默认关闭)
RTK 的遥测设计值得肯定:
- 默认关闭,必须显式同意
- 收集的是聚合数据:命令类别分布(git 45%、cargo 20%...)、token 节省总量、Top 5 未覆盖命令(0% savings)
- 绝不收集:源代码、文件路径、命令参数、secrets
- 可以
rtk telemetry forget要求服务端删除数据
五、多工具生态:13 个 AI 编程工具的安装机制
RTK 不只支持 Claude Code。从 src/hooks/init.rs 可以看到它支持 13 个工具,每个有不同的 hook 机制:
| 工具 | Hook 方式 | 安装命令 |
|---|---|---|
| Claude Code | PreToolUse hook (settings.json) | rtk init -g |
| GitHub Copilot | PreToolUse hook | rtk init -g --copilot |
| Cursor | preToolUse hook (hooks.json) | rtk init -g --agent cursor |
| Gemini CLI | BeforeTool hook | rtk init -g --gemini |
| Codex | AGENTS.md + RTK.md 指令注入 | rtk init -g --codex |
| Windsurf | .windsurfrules 项目级规则 | rtk init --agent windsurf |
| Cline / Roo Code | .clinerules 项目级规则 | rtk init --agent cline |
| OpenCode | TypeScript 插件 | rtk init -g --opencode |
| OpenClaw | 插件 TS (before_tool_call) | openclaw plugins install |
| Hermes | Python 插件适配器 | rtk init --agent hermes |
| Kilo Code | .kilocode/rules/rtk-rules.md | rtk init --agent kilocode |
| Google Antigravity | .agents/rules/ | rtk init --agent antigravity |
| Mistral Vibe | Planned | — |
5.1 安装细节:原子写入 + 遗留迁移
init.rs 里有几个值得注意的工程实践:
- 原子写入:用
NamedTempFile写新内容,然后persist()原子替换,防止写入过程中 crash 导致配置文件损坏 - settings.json 备份:修改前自动复制
.json.bak - 遗留脚本迁移:早期版本用
rtk-rewrite.shbash 脚本做 hook,新版本改成直接调用rtk hook claude二进制命令。migrate_old_hook_script函数会自动清理旧脚本和旧配置 - Idempotent:重复运行
rtk init不会重复安装,会检测已有配置
六、关键设计与取舍
6.1 安全模型:Deny > Ask > Allow > Default
PermissionVerdict 的优先级设计体现了安全优先:
- Deny(exit 2):命中拒绝规则,直接不执行。比交给 AI 工具的默认 deny 更严格
- Ask(exit 3):需要用户确认后才执行。用于高风险命令
- Allow(exit 0):RTK 有对应的过滤器,安全重写
- Default(exit 1):没有 RTK 等价物,直接透传原命令
6.2 性能取舍:流式 vs 缓冲
大部分命令用 Streaming 模式(逐行过滤),但有些命令需要 Buffered(等全部输出再处理)。Streaming 的好处是低延迟——AI 不用等命令跑完就能看到部分结果。Buffered 的好处是可以做全局优化——比如跨行的去重、统计。
RTK 的选择是:默认 Streaming,具体命令模块自己决定。这让 cargo test 可以边跑边过滤,而 rtk log 可以等全部读完再做日志去重。
6.3 Token 估算:近似但够用
RTK 的 token 统计不是精确到 GPT tokenizer 的计数,而是基于字符数的近似估算。tracking.rs 里的 input_tokens 和 output_tokens 实际上是字符长度或行数乘以经验系数。这足够用来算节省百分比,但不够精确到计费。
这是一个合理的工程取舍:精确 tokenizer 需要依赖 tiktoken 之类的库,增加二进制体积和复杂度。RTK 的目标是"知道省了大概多少",不是"精确到小数点后两位的账单"。
七、局限与风险
7.1 不是万能压缩器
RTK 对未覆盖的命令完全无能为力。README 坦诚地说:"Claude Code built-in tools like Read, Grep, and Glob bypass the hook"——这些工具不走 Bash hook,所以不会被自动重写。如果你让 Claude Code 用内置的 Read 工具读文件,RTK 帮不上忙。
7.2 过滤可能丢失上下文
Aggressive 过滤模式会去掉"看起来没用"的信息,但有时候那些信息对 AI debugging 是有价值的。比如 cargo test 只输出失败项,但如果失败是由测试顺序或环境状态导致的,你丢失了通过测试的上下文。
RTK 的缓解措施:
tee机制:命令失败时自动保存完整原始输出到~/.local/share/rtk/tee/,AI 可以读取--verbose标志可以逐步增加输出详细度
7.3 安装复杂度
虽然宣传是"一行命令安装",但实际上不同 AI 工具的安装方式差异很大:
- Claude Code 需要改
settings.json - Windsurf/Cline 需要项目级规则文件
- Codex 需要改
AGENTS.md - Windows 原生不支持 hook,只能用 WSL
7.4 生态锁定风险
RTK 越深度集成到你的 AI 工作流,你对它的依赖就越强。如果 RTK 停止维护,或者某个 AI 工具的 hook API 变了,你的配置可能突然失效。不过 RTK 是 MIT 开源的,且 hook 机制相对简单,这个风险可控。
八、结论:值得装,但要知道它在做什么
RTK 的价值不是"魔法压缩",而是工程化的命令输出重写系统。它的核心优势:
1. 结构化理解:不是正则硬匹配,而是按生态(Rust/JS/Python/AWS...)分别实现 handler 2. 流式处理:逐行实时过滤,延迟 <10ms 3. 透明拦截:Hook 机制让 AI 工具无感知使用 4. 安全回退:不认识就透传,不会瞎改 5. 可观测:SQLite 统计让你看到真金白银省了多少钱 6. 跨工具:13 个 AI 编程工具的统一基础设施
对于每天用 AI 编程助手写代码的开发者,RTK 的 ROI 很明确:安装一次,每次会话省 60-80% token。按 Claude Code 重度用户每天 2-3 次会话算,一个月能省几十到上百美元。
但要记住:它是辅助工具,不是银弹。对于未覆盖的命令、内置工具调用、或者需要完整上下文的 debug 场景,它帮不上忙。把它当作"常见命令的自动压缩器"来用,期望值就对了。
---
源码仓库: https://github.com/rtk-ai/rtk
项目主页: https://www.rtk-ai.app
安装: brew install rtk 或 curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh
#RTK #Rust #AI编程 #Token优化 #CLI工具 #开源