首页
/ RTK 解析基础设施:三层降级解析(Tier 1/2/3)如何保障工具输出的可靠性与 Token 效率

RTK 解析基础设施:三层降级解析(Tier 1/2/3)如何保障工具输出的可靠性与 Token 效率

2026-09-06 15:18:26作者:韦蓉瑛

本篇技术指南基于 RTK 仓库中的 Parser Infrastructure 文档src/parser 模块源码,系统讲解 rtk 如何为各类开发工具输出构建"JSON 全解析 → 正则降级 → 原始透传"的三层降级解析体系。读完后,你将理解 ParseResult<T>OutputParserTokenFormatter 三大核心抽象的设计与调用链,掌握如何为新工具实现解析器、按 verbosity 选择格式化模式(Compact/Verbose/Ultra),并通过 tier 校验测试验证解析器的降级行为。

一、为什么需要统一的解析基础设施

rtk 是一个面向 LLM 场景的 CLI 代理,其核心诉求是把 vitestpnpm 等工具冗长的原始输出压缩为 token 高效的摘要,再交给 LLM 或开发者阅读。这个过程中有一个关键风险:工具升级后输出格式变化,解析器静默失败、返回错误数据,比"不解析"更危险

模块文档头注释 明确给出设计目标:

This module provides a unified interface for parsing tool outputs with graceful degradation... The three-tier system ensures RTK never returns false data silently.

为此,rtk 在 src/parser/ 下构建了统一的基础设施,包含三个文件:

二、三层解析架构总览

原文档 给出的架构链路为:ToolCommand Builder → OutputParser<T> → Canonical Types → TokenFormatter。整体数据流是:

┌─────────────────────────────────────────────────────────┐
│                    ToolCommand Builder                   │
│  Command::new("vitest").arg("--reporter=json")          │
└─────────────────────┬───────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────┐
│                   OutputParser<T> Trait                  │
│  parse() → ParseResult<T>                               │
│    ├─ Full(T)           - Tier 1: Complete JSON parse   │
│    ├─ Degraded(T, warn) - Tier 2: Partial with warnings │
│    └─ Passthrough(str)  - Tier 3: Truncated raw output  │
└─────────────────────┬───────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────┐
│                  Canonical Types                         │
│  TestResult, LintResult, DependencyState, BuildOutput   │
└─────────────────────┬───────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────┐
│                  TokenFormatter Trait                    │
│  format_compact() / format_verbose() / format_ultra()   │
└─────────────────────────────────────────────────────────┘

三层各自的语义(见 ParseResult 定义):

层级 变体 数据内容 典型触发条件
Tier 1 (Full) Full(T) 完整结构化数据 工具支持 --reporter=json 且 JSON 解析成功
Tier 2 (Degraded) Degraded(T, Vec<String>) 部分数据 + 警告列表 JSON 解析失败,正则提取成功(如用户覆盖了 --reporter 参数)
Tier 3 (Passthrough) Passthrough(String) 截断后的原始输出 正则也失败,返回带 [RTK:PASSTHROUGH] 标记的原文

ParseResult<T> 提供了便于测试与调试的辅助方法(源码):

  • tier():返回层级号 1/2/3,是分层校验测试的断言基础;
  • is_ok()FullDegraded 均视为成功;
  • map(f):转换数据类型但保留层级Degraded 的 warnings 原样传递);
  • warnings():仅在 Degraded 时返回警告向量,否则为空;
  • parse_with_tier(input, max_tier):测试/调试用,结果层级超过 max_tier 时强制降级为透传。

对应地,原文档 给出的接入模式三步走:

  1. 为工具实现 OutputParser——先试 JSON(Tier 1),失败走正则(Tier 2),再失败走透传(Tier 3);
  2. 在命令模块中调用 Parser::parse(),再 data.format(FormatMode::from_verbosity(verbose))
  3. 降级警告:verbose 模式下打印 [RTK:DEGRADED],完全回退时打印 [RTK:PASSTHROUGH]

三、实战样例:VitestParser 的三层实现

仓库中 VitestParser 是三层模式的完整落地范例。它先按工具特有结构反序列化:

impl OutputParser for VitestParser {
    type Output = TestResult;

    fn parse(input: &str) -> ParseResult<TestResult> {
        // Tier 1: Try JSON parsing (with extraction fallback for pnpm/dotenv prefixes)
        let json_result = serde_json::from_str::<VitestJsonOutput>(input).or_else(|first_err| {
            if let Some(extracted) = extract_json_object(input) {
                serde_json::from_str::<VitestJsonOutput>(extracted)
            } else {
                Err(first_err)
            }
        });

        match json_result {
            Ok(json) => {
                let result = TestResult {
                    total: json.num_total_tests,
                    passed: json.num_passed_tests,
                    failed: json.num_failed_tests,
                    skipped: json.num_pending_tests,
                    duration_ms: None,
                    failures: extract_failures_from_json(&json),
                };
                ParseResult::Full(result)
            }
            Err(e) => {
                // Tier 2: Try regex extraction (only fires if user overrides --reporter flag)
                match extract_stats_regex(input) {
                    Some(result) => {
                        ParseResult::Degraded(result, vec![format!("JSON parse failed: {}", e)])
                    }
                    None => {
                        // Tier 3: Passthrough
                        ParseResult::Passthrough(truncate_passthrough(input))
                    }
                }
            }
        }
    }
}

几个值得注意的工程细节:

Tier 1 内的"前缀剥离"容错。 真实输出常被 pnpm banner、dotenv 提示污染,纯 JSON 解析会失败。extract_json_object() 的处理策略是:优先查找 vitest 特有的 "numTotalTests" 标记并向后回溯到其所属对象的 {;否则取首个以 { 开头的行;然后做带字符串状态机的大括号配平(跟踪 in_stringescape_next),正确处理嵌套大括号和 "test {should} not confuse parser" 这类字符串内大括号。mod.rs 的测试 覆盖了大量边界:pnpm 前缀、dotenv 前缀、嵌套对象、无 JSON 的纯文本、CJK/Emoji 多字节值等。

Tier 2 的触发场景很明确。 源码注释(L82)指出正则回退"only fires if user overrides --reporter flag"——即正常注入 --reporter=json 时几乎走不到这一层,它主要兜住用户自定义参数导致的格式变化。

命令模块侧的分支处理。 format_test_output() 展示了标准消费方式:

let parse_result = VitestParser::parse(stdout);
let mode = FormatMode::from_verbosity(verbose);
match parse_result {
    ParseResult::Full(data) => {
        if verbose > 0 {
            eprintln!("{} run (Tier 1: Full JSON parse)", framework);
        }
        FormattedTestOutput::new(data.format(mode))
    }
    ParseResult::Degraded(data, warnings) => {
        if verbose > 0 {
            emit_degradation_warning(framework, &warnings.join(", "));
        }
        FormattedTestOutput::new(data.format(mode))
    }
    ParseResult::Passthrough(_) => {
        emit_passthrough_warning(framework, "All parsing tiers failed");
        format_passthrough_output(stdout)
    }
}

可见降级警告只在 verbose > 0 时输出到 stderr(emit_degradation_warning/emit_passthrough_warning,见 mod.rs),不会污染 stdout 的正文输出——这对"输出要喂给 LLM"的场景很重要:警告只进日志流,不进数据流。

四、规范类型:跨工具的统一数据模型

原文档 定义了四类规范类型,当前 types.rs 中已落地的是 TestResultDependencyState 两套(LintResultBuildOutput 在文档中列出,从源码结构看属于该类型族的规划成员):

TestResult(测试运行器:vitest、playwright、jest 等)

字段与 源码定义 一一对应:

字段 类型 说明
total / passed / failed / skipped usize 计数统计
duration_ms Option<u64> 可选耗时
failures Vec<TestFailure> 失败明细,每项含 test_namefile_patherror_messagestack_trace: Option<String>

格式化策略(formatter.rs):Compact 模式输出 PASS (n) FAIL (n) 摘要 + 最多前 5 条失败详情,超出部分折叠为 ... +N more failures;Verbose 模式展开全部失败并附文件路径与前 3 行堆栈预览;Ultra 模式压缩为单行 [ok]N [x]N [skip]N (Xms)。源码中有两处刻意为 LLM 调试体验做的设计决策:

DependencyState(包管理器:pnpm、npm、cargo 等)

字段:total_packagesoutdated_countdependencies: Vec<Dependency>,其中 Dependencynamecurrent_versionlatest_version: Option<String>wanted_versiondev_dependency源码)。

formatter.rs 中一个容易被忽略的防误报逻辑:当 outdated_count == 0 且所有依赖都没有 latest_version 时,说明这是一次纯列表(如 pnpm list),此时输出真实包清单而非 "All packages up-to-date"——因为把"没有版本信息"误报为"全部最新"属于假阳性。列表展示上限 MAX_DEPS_LISTING 取自 core/truncate.rs 的 CAP_INVENTORY,超出部分折叠为 ... +N more。Upgrade 场景则输出 pkg: current → latest 升级路径,compact 模式最多列 10 条。

五、三种格式化模式与 verbosity 映射

FormatMode 通过 from_verbosity 与命令行详略级别直接挂钩:

verbosity 模式 行为 设计取向
0(默认) Compact 仅摘要,Top 5–10 条目 Token 最优
1 Verbose 完整细节,最多约 20 项 人类可读
2+ Ultra 符号化压缩([ok]/[x]/pkg:N ^N 等) 极致压缩

TokenFormatter trait 定义 format_compact() / format_verbose() / format_ultra() 三个方法,并提供默认实现的 format(mode) 作为统一入口——这正是前文 data.format(mode) 一步切换三档输出的来源。Ultra 模式的实际压缩效果可以直观对比:同样一条测试汇总,Compact 是 PASS (28) FAIL (1) 加失败详情,Ultra 只有 [ok]28 [x]1 [skip]0 (1500ms)

六、透传截断:Token 预算的最后一道闸门

Tier 3 的透传并非原样输出,而是按配置截断。truncate_passthrough() 读取 limits().passthrough_max_chars,默认值为 2000 字符LimitsConfig 默认值,可在 RTK 配置文件的 limits 段覆盖)。

truncate_output() 的实现要点:

  • char(而非字节)切片,多字节字符(CJK、Emoji)不会截出乱码——对应测试 专门用泰文与 Emoji 输入验证了 UTF-8 安全性;
  • 超限时在截断内容后追加标记:[RTK:PASSTHROUGH] Output truncated (1000 chars → 100 chars),让下游(人或 LLM)明确知道"这是残缺数据"。

七、错误处理与可观测性

原文档 列出的解析失败语义分为 JsonError / PatternMismatch / PartialParse / InvalidFormat / MissingField / VersionMismatch / EmptyOutput 七类。当前源码层面,这些语义主要以警告字符串 + 层级枚举的形式表达(如 Degraded(result, vec!["JSON parse failed: ..."])),并通过两类 stderr 标记实现可观测性:

[RTK:DEGRADED] vitest parser: JSON parse failed at line 42, using regex fallback
[RTK:PASSTHROUGH] playwright parser: Pattern mismatch, showing truncated output

这与 原文档 "Error Handling → Degradation Warnings" 一节给出的示例一致,输出函数即 emit_degradation_warning / emit_passthrough_warning

八、为新工具接入解析器:迁移指南与测试要求

迁移步骤

Migration Guide:把模块中直接调用 filter_*_output() 的旧逻辑替换为 Parser::parse() + FormatMode。关键改动有三点:

  1. 命令注入 --reporter=json(或等价 JSON 参数)作为 Tier 1 的前提;
  2. ParseResult 做 Full/Degraded/Passthrough 三分支匹配,而非假设一定解析成功;
  3. 统一用 data.format(mode) 输出,让 verbose 级别决定详略。

收益按文档归纳为:工具版本变化优雅降级(Tier 2/3 兜底)、永不静默失败或产出假数据、verbose 下降级标记可观测、结构化数据支撑更高压缩比、跨工具类型接口统一、以及基于 fixture 的多版本回归测试。

分层校验测试

原文档 对每个解析器的测试要求非常具体:运行 cargo test parser::tests,并做三层断言——合法 JSON fixture 断言 result.tier() == 1,正则可提取的输入断言 tier() == 2,完全乱码输出断言 tier() == 3。仓库内已有对应实践:VitestParser 的测试(vitest_cmd.rs 中的多组 VitestParser::parse(...) 用例)分别喂入合法 JSON、纯文本与非法输入验证降级路径;ParseResult 自身的 tier()/map()/截断行为也有独立单测 护航。

九、Roadmap:从文档到代码的演进方向

原文档 末尾列出的 Roadmap 与仓库现状可以互相印证:

  • Phase 4: Module Migration——vitest_cmd.rs → VitestParser 等 6 项中,vitest 一项已经落地VitestParser 已实现 OutputParser),playwright、pnpm、eslint、tsc、gh 的独立 Parser 仍是待办;
  • Phase 5: Observability——扩展 tracking.db 记录 parse_tier/format_mode、新增 rtk parse-health 命令、降级率超 10% 告警,这三项属于规划中能力,当前仓库尚未实现,使用时不必依赖。

小结

rtk 的 src/parser/ 基础设施用三个抽象回答了"LLM 代理压缩工具输出"的核心矛盾:ParseResult<T> 用显式层级拒绝静默失败,OutputParser 用 JSON→正则→透传的固定降级链换取对工具版本变化的鲁棒性,TokenFormatter 用三档模式在 token 预算与调试信息之间划出可调节的平衡。若你在自己的 CLI 代理或输出管道项目中需要处理"工具输出格式不可信"这一普遍问题,这套"层级枚举 + 特性对象 + 多档格式化"的组合是值得直接参照的实现范式;接入新工具时,对照 VitestParser 的实现 与三层 tier 断言测试即可快速复刻。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389