<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>技术 on 潘达窝</title><link>https://daidaij.github.io/categories/%E6%8A%80%E6%9C%AF/</link><description>Recent content in 技术 on 潘达窝</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>pandazhangs</copyright><lastBuildDate>Sun, 07 Jun 2026 13:12:44 +0800</lastBuildDate><atom:link href="https://daidaij.github.io/categories/%E6%8A%80%E6%9C%AF/index.xml" rel="self" type="application/rss+xml"/><item><title>AI 编程助手的上下文压缩：only-cc-lite 设计与 98 个真实会话的压测分析</title><link>https://daidaij.github.io/p/only-cc-lite-context-compression/</link><pubDate>Sun, 07 Jun 2026 13:12:44 +0800</pubDate><guid>https://daidaij.github.io/p/only-cc-lite-context-compression/</guid><description>&lt;img src="https://picsum.photos/seed/ab56a490/800/600" alt="Featured image of post AI 编程助手的上下文压缩：only-cc-lite 设计与 98 个真实会话的压测分析" />&lt;h2 id="为什么需要上下文压缩">为什么需要上下文压缩
&lt;/h2>&lt;p>AI 编程助手（Claude Code、Qwen Code、Cursor 等）的工作模式是&lt;strong>累积式&lt;/strong>的：每次用户发一条消息，客户端会把从会话开始到当前的所有消息一起发送给 LLM API。&lt;/p>
&lt;p>这意味着一个 30 轮的会话，第 30 次请求包含了前 29 轮的全部历史。如果中间执行了 &lt;code>cargo build&lt;/code>、&lt;code>npm run build&lt;/code>、grep 搜索等工具调用，大量构建日志、搜索结果会被反复发送。&lt;/p>
&lt;blockquote>
&lt;p>实际使用中，一个中等复杂度的调试会话，单次请求的 input tokens 轻松突破 10 万。累积下来，一个会话消耗几百万 tokens 很常见。&lt;/p>
&lt;/blockquote>
&lt;p>问题的核心：&lt;strong>大部分历史内容是重复的，但不能直接丢弃&lt;/strong>。LLM 需要上下文来理解代码变更的背景，而 LLM 提供商的前缀缓存（prompt cache）机制要求历史消息的字节序列保持不变。&lt;/p>
&lt;p>only-cc-lite 就是为解决这个问题而生的。&lt;/p>
&lt;h2 id="设计原则">设计原则
&lt;/h2>&lt;p>从 &lt;a class="link" href="https://github.com/chopratejas/headroom" target="_blank" rel="noopener"
>Headroom&lt;/a> 提取，但做了关键简化：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>特性&lt;/th>
&lt;th>headroom-core&lt;/th>
&lt;th>only-cc-lite&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>内容类型检测&lt;/td>
&lt;td>Magika ONNX + 正则&lt;/td>
&lt;td>纯正则&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Token 计数&lt;/td>
&lt;td>tiktoken-rs + HuggingFace&lt;/td>
&lt;td>字符密度估算&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>语义相关性评分&lt;/td>
&lt;td>fastembed ONNX&lt;/td>
&lt;td>BM25 纯关键词&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>二进制膨胀&lt;/td>
&lt;td>~50-80MB (ONNX)&lt;/td>
&lt;td>&amp;lt;5MB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>传递依赖&lt;/td>
&lt;td>4182&lt;/td>
&lt;td>~80&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>三个核心设计原则：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>零 ML 依赖&lt;/strong> — 不引入 ONNX Runtime、fastembed、HuggingFace tokenizers，纯 Rust 实现&lt;/li>
&lt;li>&lt;strong>增量压缩&lt;/strong> — 只压缩 user 侧的增量内容，assistant 消息原样保留&lt;/li>
&lt;li>&lt;strong>前缀缓存安全&lt;/strong> — 压缩后的请求，历史部分的字节序列与原始请求完全一致&lt;/li>
&lt;/ol>
&lt;h2 id="增量压缩模型">增量压缩模型
&lt;/h2>&lt;p>传统做法是压缩整个请求体。但这会破坏 LLM 提供商的前缀缓存——提供商通过 SHA-256 哈希请求的前 N 个字节来判断是否命中缓存，任何字节变化都会导致缓存失效。&lt;/p>
&lt;p>only-cc-lite 的做法是&lt;strong>只压缩最新 user 消息中的可压缩内容块&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">请求消息结构：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ user msg #0 (frozen) │ ← 不动
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ assistant msg #0 (frozen) │ ← 不动
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ user msg #1 (frozen) │ ← 不动
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ assistant msg #1 (frozen) │ ← 不动
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ... │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ user msg #N ← LIVE ZONE │ ← 只压缩这里
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ text block &amp;#34;请帮我修复...&amp;#34; │ ← 不压缩（短文本）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ tool_result: 构建日志 8000 tok │ ← 压缩！(log_compressor)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ tool_result: grep 结果 2000 tok│ ← 压缩！(search_compressor)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ tool_result: git diff 1500 tok │ ← 压缩！(diff_compressor)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ assistant msg #N (cache hot zone) │ ← 不动
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>对应的 Rust 实现入口：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">only_cc_lite&lt;/span>::&lt;span class="p">{&lt;/span>&lt;span class="n">compress_request&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Provider&lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">outcome&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">compress_request&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Provider&lt;/span>::&lt;span class="n">Anthropic&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;claude-sonnet-4-5-20250929&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">None&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// ccr_store，可选
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">match&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">outcome&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">Some&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">compressed&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 转发压缩后的 body 到上游 API
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">forward_to_upstream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">compressed&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">None&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// 无可压缩内容，转发原始 body
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">forward_to_upstream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>这个模型的核心洞察：编程助手会话中，assistant 消息（代码、解释）占比大但不适合压缩（格式敏感），而 user 消息中的工具结果（日志、搜索结果）是最理想的压缩目标——结构化、冗余高、可安全截断。&lt;/p>
&lt;/blockquote>
&lt;h2 id="四种压缩策略">四种压缩策略
&lt;/h2>&lt;p>压缩器通过正则检测内容类型，自动选择最合适的策略：&lt;/p>
&lt;h3 id="logcompressor--构建运行日志">LogCompressor — 构建/运行日志
&lt;/h3>&lt;p>识别 &lt;code>ERROR&lt;/code>、&lt;code>WARN&lt;/code>、&lt;code>Traceback&lt;/code>、&lt;code>panic&lt;/code>、&lt;code>failed&lt;/code> 等模式，提取关键错误信息，丢弃重复的编译输出。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="err">输入&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">8000&lt;/span> &lt;span class="n">tokens&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Compiling&lt;/span> &lt;span class="n">foo&lt;/span> &lt;span class="n">v0&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">1.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">warning&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">unused&lt;/span> &lt;span class="n">variable&lt;/span> &lt;span class="err">`&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="err">`&lt;/span> &lt;span class="o">--&amp;gt;&lt;/span> &lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">rs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">42&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">error&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">E0308&lt;/span>&lt;span class="p">]:&lt;/span> &lt;span class="n">mismatched&lt;/span> &lt;span class="n">types&lt;/span> &lt;span class="o">--&amp;gt;&lt;/span> &lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">lib&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">rs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="err">几百行编译输出&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">输出&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1200&lt;/span> &lt;span class="n">tokens&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="n">ERROR&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">lib&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">rs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">5&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">mismatched&lt;/span> &lt;span class="n">types&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="n">WARN&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">rs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">42&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">9&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">unused&lt;/span> &lt;span class="n">variable&lt;/span> &lt;span class="err">`&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="err">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="err">只保留错误和警告&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="searchcompressor--代码搜索结果">SearchCompressor — 代码搜索结果
&lt;/h3>&lt;p>匹配 &lt;code>file:line:content&lt;/code> 格式的搜索结果，保留匹配行，丢弃上下文。&lt;/p>
&lt;h3 id="diffcompressor--git-diff">DiffCompressor — Git Diff
&lt;/h3>&lt;p>解析 unified diff 格式，保留变更摘要（文件名、变更类型），压缩具体 diff 内容。&lt;/p>
&lt;h3 id="smartcrusher--json-数组">SmartCrusher — JSON 数组
&lt;/h3>&lt;p>解析 JSON 数组，保留首尾元素，中间用统计摘要替代。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 输入: 100 个元素的数组
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">},{&lt;/span>&lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;b&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>&lt;span class="err">...&lt;/span>&lt;span class="p">,{&lt;/span>&lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;zz&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 输出: 首尾 + 摘要
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="s2">&amp;#34;... 98 more items ...&amp;#34;&lt;/span>&lt;span class="p">,{&lt;/span>&lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;zz&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>SmartCrusher 的压缩效果最好，单轮可达 60-90%。但在真实编程助手中，触发频率最低——大部分工具调用返回的是日志和搜索结果，不是纯 JSON 数组。&lt;/p>
&lt;/blockquote>
&lt;h2 id="前缀缓存保护机制">前缀缓存保护机制
&lt;/h2>&lt;p>这是 only-cc-lite 最关键的设计。三层保护确保压缩不会破坏 LLM 提供商的前缀缓存：&lt;/p>
&lt;h3 id="第一层frozen-zone">第一层：Frozen Zone
&lt;/h3>&lt;p>历史消息（所有 assistant 消息 + 之前的 user 消息）完全不动。通过 &lt;code>frozen_message_count&lt;/code> 参数确定冻结边界。&lt;/p>
&lt;h3 id="第二层live-zone-限制">第二层：Live Zone 限制
&lt;/h3>&lt;p>只在最新 user 消息内寻找可压缩的 content blocks。Assistant 消息即使包含大段内容也不压缩。&lt;/p>
&lt;h3 id="第三层byte-range-surgery">第三层：Byte-range Surgery
&lt;/h3>&lt;p>这是最精妙的部分。&lt;code>apply_replacements()&lt;/code> 函数做字节级精确替换：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">out = body[..block_start] || replacement || body[block_end..]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>被替换的 block 通过 &lt;code>serde_json::value::RawValue&lt;/code> 的借用切片定位其在原始 buffer 中的偏移量。替换前后，block 外部的字节&lt;strong>逐字节复制&lt;/strong>，不做任何重新序列化。&lt;/p>
&lt;p>这意味着：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">原始请求: [prefix bytes...] [block A] [suffix bytes...]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">压缩后: [prefix bytes...] [block A&amp;#39;] [suffix bytes...]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↑
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 只有这里变了
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>前缀的 SHA-256 在压缩前后完全一致，LLM 提供商的 prompt cache 命中率不受影响。&lt;/p>
&lt;blockquote>
&lt;p>实际上，这也是为什么压缩器只压缩最后一条 user 消息——更早的消息即使是 user 消息，修改它们也会破坏前缀缓存。&lt;/p>
&lt;/blockquote>
&lt;h2 id="真实会话压测">真实会话压测
&lt;/h2>&lt;p>为了验证压缩效果，基于真实的 AI 编程助手会话日志做了评估。&lt;/p>
&lt;h3 id="测试数据">测试数据
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>维度&lt;/th>
&lt;th>数值&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>压测会话数&lt;/td>
&lt;td>&lt;strong>98 个&lt;/strong>（Claude Code 2 个 + Qwen Code 96 个）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>对话轮次&lt;/td>
&lt;td>4,210&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>总 input tokens&lt;/td>
&lt;td>3.28 亿&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>涉及模型&lt;/td>
&lt;td>mimo-v2.5-pro、minimax-m2.7、kimi-k2.6、glm-5、glm-4.7-flash、deepseek-v4-flash、qwen3.6-plus 等&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>会话来源通过环境变量自动定位（&lt;code>$USERPROFILE&lt;/code> 或 &lt;code>$HOME&lt;/code>），不依赖任何硬编码路径。会话文件格式包括 Anthropic Messages API（Claude Code）和 OpenAI Chat Completions（Qwen Code）两种。&lt;/p>
&lt;h3 id="总体指标">总体指标
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>指标&lt;/th>
&lt;th>数值&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>User 内容（可压缩）&lt;/td>
&lt;td>10.3 MB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Assistant 内容（保留不动）&lt;/td>
&lt;td>3.3 MB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tokens 节省&lt;/td>
&lt;td>103,256&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Bytes 节省&lt;/td>
&lt;td>452,770&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>整体 User 压缩率&lt;/strong>&lt;/td>
&lt;td>&lt;strong>4.4%&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>4.4% 看起来不高？这恰恰反映了编程助手的真实使用特征。&lt;/p>
&lt;h3 id="场景收益分析">场景收益分析
&lt;/h3>&lt;p>&lt;strong>收益明显的场景&lt;/strong>——user 消息中包含大块结构化内容：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>策略&lt;/th>
&lt;th>单轮压缩率&lt;/th>
&lt;th>触发条件&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>构建失败日志&lt;/td>
&lt;td>log_compressor&lt;/td>
&lt;td>50-100%&lt;/td>
&lt;td>&lt;code>cargo build&lt;/code>、&lt;code>npm run build&lt;/code> 输出大量错误&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>运行时错误堆栈&lt;/td>
&lt;td>log_compressor&lt;/td>
&lt;td>70-100%&lt;/td>
&lt;td>Python traceback、Node.js rejection&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>代码搜索结果&lt;/td>
&lt;td>search_compressor&lt;/td>
&lt;td>50-80%&lt;/td>
&lt;td>grep/ripgrep 返回大量匹配&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Git diff&lt;/td>
&lt;td>diff_compressor&lt;/td>
&lt;td>40-60%&lt;/td>
&lt;td>多文件变更的 diff 输出&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>JSON 数据&lt;/td>
&lt;td>smart_crusher&lt;/td>
&lt;td>60-90%&lt;/td>
&lt;td>API 响应、配置文件&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>典型案例：一个 28 轮会话中，第 1 轮就压缩了 14,790 tokens（构建日志），单轮压缩率 100%。&lt;/p>
&lt;p>&lt;strong>几乎无收益的场景&lt;/strong>——占绝大多数轮次：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>占比&lt;/th>
&lt;th>原因&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>短指令对话&lt;/td>
&lt;td>~60% 轮次&lt;/td>
&lt;td>&amp;ldquo;fix this bug&amp;rdquo;、&amp;ldquo;add a test&amp;rdquo; &amp;lt; 500 bytes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>纯文本粘贴&lt;/td>
&lt;td>~15% 轮次&lt;/td>
&lt;td>错误描述、需求说明等非结构化文本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>小文件内容&lt;/td>
&lt;td>~10% 轮次&lt;/td>
&lt;td>单个函数/配置片段 &amp;lt; 5KB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>对话式交互&lt;/td>
&lt;td>~10% 轮次&lt;/td>
&lt;td>追问、确认、解释&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>98 个会话中 73 个（74%）未产生任何压缩——整个会话没有出现大块结构化内容。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">会话压缩收益分布（98 个会话）：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">无压缩 ████████████████████████████████████████ 73 个 (74%)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt; 1% █████ 7 个 (7%)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1-5% ████████ 12 个 (12%)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5-15% ██████ 6 个 (6%)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>收益高度集中：&lt;strong>Top 5 会话贡献了 52% 的总 tokens 节省&lt;/strong>。&lt;/p>
&lt;h3 id="策略命中统计">策略命中统计
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>策略&lt;/th>
&lt;th>命中轮次&lt;/th>
&lt;th>贡献占比&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>log_compressor&lt;/td>
&lt;td>57 turns&lt;/td>
&lt;td>~85%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>diff_compressor&lt;/td>
&lt;td>8 turns&lt;/td>
&lt;td>~8%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>search_compressor&lt;/td>
&lt;td>2 turns&lt;/td>
&lt;td>~5%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>smart_crusher&lt;/td>
&lt;td>1 turn&lt;/td>
&lt;td>~2%&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>log_compressor&lt;/code> 是绝对主力。这也符合直觉——编程助手中最常见的&amp;quot;重内容&amp;quot;就是构建和运行日志。&lt;/p>
&lt;h3 id="与会话长度的关系">与会话长度的关系
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>会话轮次&lt;/th>
&lt;th>压缩情况&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>短会话 (&amp;lt; 10 轮)&lt;/td>
&lt;td>通常无压缩，除非首轮就有大日志&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>中等会话 (10-50 轮)&lt;/td>
&lt;td>部分有压缩，取决于是否触发构建/搜索&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>长会话 (50+ 轮)&lt;/td>
&lt;td>更可能遇到构建失败，但平均压缩率不一定更高&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>结论：压缩收益与会话长度无关，与是否触发&amp;quot;重内容&amp;quot;工具调用强相关。一个 338 轮的长会话可能压缩率很低（如果都是短指令），而一个 5 轮的调试会话可能单轮压缩 90%。&lt;/p>
&lt;/blockquote>
&lt;h2 id="运行自己的压测">运行自己的压测
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 克隆仓库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone https://github.com/daidaiJ/only-cc-lite.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> only-cc-lite
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 运行评估（自动发现本地会话文件）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cargo bench --bench eval_runner
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看报告&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat target/eval-report.txt &lt;span class="c1"># 人类可读&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat target/eval-report.json &lt;span class="c1"># 程序消费&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>支持的 Agent CLI：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Agent CLI&lt;/th>
&lt;th>会话目录&lt;/th>
&lt;th>日志格式&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Claude Code&lt;/td>
&lt;td>&lt;code>~/.claude/projects/*/*.jsonl&lt;/code>&lt;/td>
&lt;td>Anthropic Messages API&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Qwen Code&lt;/td>
&lt;td>&lt;code>~/.qwen/projects/*/chats/*.jsonl&lt;/code>&lt;/td>
&lt;td>OpenAI Chat Completions&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>only-cc-lite 的核心价值不在于压缩率有多高，而在于&lt;strong>压缩的安全性&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>零侵入&lt;/strong> — 只压缩最后一条 user 消息，不动历史，不破坏前缀缓存&lt;/li>
&lt;li>&lt;strong>零依赖&lt;/strong> — 纯 Rust，无 ONNX/tokenizer，&amp;lt;5MB 二进制&lt;/li>
&lt;li>&lt;strong>零风险&lt;/strong> — 压缩失败自动降级为 passthrough，不影响正常请求&lt;/li>
&lt;/ol>
&lt;p>在真实编程助手场景下，整体压缩率 4.4%，但&amp;quot;重内容&amp;quot;场景下单轮可达 50-100%。对于重度使用构建/调试/搜索的开发者，长期累积的 tokens 节省可观。对于以对话为主的轻量使用，压缩收益有限但也不会引入额外开销。&lt;/p>
&lt;blockquote>
&lt;p>本质上这是一个&amp;quot;有则赚，无则不亏&amp;quot;的设计——压缩器只在有明确收益时才出手，其他时候完全透明。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;p>&lt;strong>项目地址&lt;/strong>: &lt;a class="link" href="https://github.com/daidaiJ/only-cc-lite" target="_blank" rel="noopener"
>github.com/daidaiJ/only-cc-lite&lt;/a>&lt;/p>
&lt;p>&lt;strong>许可证&lt;/strong>: Apache-2.0&lt;/p></description></item></channel></rss>