RTK
Rust Token Killer — 高性能 CLI 代理,最小化 LLM token 消耗
架构概览
问题
一个普通的 git log 命令会输出大量信息 —— commit hash、完整作者信息、日期、merge commit、Signed-off-by trailer。当这些内容作为上下文发送给 LLM 时,可能消耗数千 token,其中大部分是噪声。
RTK 的位置
RTK 作为 CLI 代理插入在用户(或 AI agent)和系统命令之间。它拦截命令输出,在内容到达 LLM 上下文之前进行过滤和精简。整个过程对上层透明 —— 只需把 git 替换成 rtk git。
两层过滤
RTK 的过滤能力分两层:第一层是 Rust 硬编码的专用 filter,为 40+ 个常用命令(git、cargo、npm 等)编写了精确的解析和优化逻辑。第二层是 TOML 声明式 filter,通过配置文件覆盖另外 60+ 个命令。两层通过 fallback 机制串联。
效果
同一个 git log 命令,经过 RTK 过滤后从 ~4,231 token 压缩到 ~1,384 token,缩减 67%。额外延迟 <10ms,零 LLM 调用成本。信噪比大幅提升 —— 每个 commit 只保留核心信息。
Author: Alice <alice@example.com>
Date: Mon Apr 14 10:23:45 2026 +0800
feat: add user authentication module
Implements JWT-based auth with refresh tokens.
Closes #142
Signed-off-by: Alice <alice@example.com>
Co-authored-by: Bob <bob@example.com>
Author: CI Bot <ci@example.com>
Date: Mon Apr 14 09:00:00 2026 +0800
Merge pull request #141 from feature/logging
40+ 命令的专用过滤器,每个命令有独立的解析和优化逻辑。
60+ 命令通过 TOML 配置文件定义过滤规则,无需写 Rust 代码。
代码走读
以一次 rtk git log -10 调用为线索, 从入口到输出,走读 4 个关键函数。
入口:main() → run_cli()
程序从 main() 启动,核心逻辑在 run_cli() 中。Clap 库自动解析命令行参数为 Rust 枚举,然后通过 match 进行命令路由。
? 操作符在错误发生时立即 return,将错误沿调用链向上传播。match 穷举所有命令变体,确保每个命令都有处理逻辑。
核心流程:run_log()
当用户执行 rtk git log -10 时,路由到此函数。它构建 git 命令、注入 RTK 优化参数(自定义 format、limit、排除 merge)、执行命令,最后对输出进行后处理。
TimedExecution 计时器从头到尾记录执行耗时,用于 token 追踪分析。
过滤引擎:filter_log_output()
实际的过滤逻辑。按 ---END--- 标记分割 commit 块,每个 commit 只保留标题行和最多 3 行有意义的 body,去掉 trailer,截断长行。
迭代器链(.iter().take().map().collect())是 Rust 函数式风格的核心模式,零额外内存分配。
闭环:Token 追踪
过滤完成后,track() 记录原始输出和精简输出的 token 估算值到 SQLite。估算方式简单粗暴:每 4 字符约 1 个 token(len / 4)。不精确但 overhead 为零,适合高频命令的实时追踪。
数据写入 ~/.local/share/rtk/tracking.db,支持 90 天自动清理和日/周/月聚合查询。
// src/main.rsfn main() {let code = match run_cli() {Ok(code) => code,Err(e) => {eprintln!("rtk: {:#}", e);1}};std::process::exit(code);}fn run_cli() -> Result<i32> {let cli = Cli::try_parse()?;// 命令路由:match 穷举所有子命令let code = match cli.command {Commands::Ls { args } => ls::run(&args, cli.verbose)?,Commands::Git { command, .. } => {git::run(command, &args, cli.verbose)?}// ... 40+ 命令};Ok(code)}
// src/cmds/git/git.rsfn run_log(args: &[String], verbose: u8, global_args: &[String]) -> Result<i32> {let timer = tracking::TimedExecution::start();let mut cmd = git_cmd(global_args);cmd.arg("log");// 智能默认值:仅在用户未指定时注入if !has_format_flag {cmd.args(["--pretty=format:%h %s (%ar) <%an>%n%b%n---END---"]);}if !has_limit_flag {cmd.arg("-10");}cmd.arg("--no-merges");// 执行真实 git 命令let output = cmd.output().context("Failed to run git log")?;let stdout = String::from_utf8_lossy(&output.stdout);// 过滤输出 + 记录 token 节省量let filtered = filter_log_output(&stdout, limit, user_set_limit, has_format_flag);println!("{}", filtered);timer.track("git log", "rtk git log", &stdout, &filtered);Ok(0)}
// src/cmds/git/git.rsfn filter_log_output(output: &str, limit: usize, user_set_limit: bool, user_format: bool) -> String {// 按 commit 块分割let commits: Vec<&str> = output.split("---END---").collect();let max_commits = if user_set_limit { commits.len() } else { limit };let mut result = Vec::new();for block in commits.iter().take(max_commits) {let block = block.trim();if block.is_empty() { continue; }let mut lines = block.lines();let header = truncate_line(lines.next().unwrap_or("").trim(), 80);// 过滤 body:去 trailer,限 3 行let body_lines: Vec<&str> = lines.map(|l| l.trim()).filter(|l| {!l.is_empty()&& !l.starts_with("Signed-off-by:")&& !l.starts_with("Co-authored-by:")}).take(3).collect();result.push(format_entry(&header, &body_lines));}result.join("\n")}
// src/core/tracking.rspub fn estimate_tokens(text: &str) -> usize {// ~4 chars per token(近似值,零开销)(text.len() as f64 / 4.0).ceil() as usize}impl TimedExecution {pub fn track(&self, original_cmd: &str, rtk_cmd: &str, input: &str, output: &str) {let elapsed_ms = self.start.elapsed().as_millis() as u64;let input_tokens = estimate_tokens(input);let output_tokens = estimate_tokens(output);if let Ok(tracker) = Tracker::new() {// 写入 SQLite,失败不阻断命令执行let _ = tracker.record(original_cmd, rtk_cmd,input_tokens, output_tokens, elapsed_ms,);}}}
Rust 概念速查对照表
| Rust 概念 | 你可能熟悉的对应物 | RTK 中的例子 |
|---|---|---|
| enum 带数据 | TypeScript tagged union / Kotlin sealed class | Commands::Git { command, ... } |
| match | switch + 解构 + 穷举检查 | 命令路由、错误处理 |
| Result<T, E> | Java checked exception / Go (val, err) | 几乎每个函数的返回值 |
| Option<T> | Java Optional<T> / Kotlin T? | max_lines: Option<usize> |
| ? 操作符 | throw / raise / return err | cmd.output()? |
| &str vs String | string_view vs std::string (C++) | 参数用 &str,存储用 String |
| &T(借用) | 传引用 / 传指针 | &args, &self |
| mut | 默认 const,显式 mutable | let mut cmd = ... |
| 迭代器链 | Java Stream / JS array 链式调用 | .iter().filter().map().collect() |
| Vec<T> | ArrayList<T> / list[T] | Vec<String> |
| #[derive(...)] | Java @Lombok / Python @dataclass | #[derive(Parser)] |
| lazy_static! | Java static final + 延迟初始化 | 所有正则表达式 |