Snapcompact: 通过图像节省Token

Reddit r/LocalLLaMA 工具

摘要

Snapcompact 是一种技术,它将文本渲染为密集的像素字体图像,用更便宜的图像Token替换文本Token,以极低的输入成本实现近乎逐字逐句的召回。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/13 22:28

# Snapcompact:最先进的压缩技术——即时、本地、免费。三项全占 来源:https://blog.can.ac/2026/06/10/snapcompact/ 一个1568×1568的PNG图片,使用6×10像素字体,大约能容纳4万个字符的文本。这相当于约1万个token的文本量,而按Anthropic的像素计费公式,这仅需3,279个图像token。你明白我的意思了吗? **Snapcompact**:当上下文窗口填满时,将其渲染成密集的像素字体位图,然后作为图像返回。“一图胜千言”字面上成真了——模型几乎逐字地读取回来,而输入价格只需三分之一。请看基准测试: 这最初只是一个玩笑(“免费token漏洞,哈哈”)。然后我对其进行了基准测试,找出了问题所在,拆解了Qwen的注意力层,修复了问题,再次进行基准测试——现在我把这一切写下来,因为它在前沿模型上也表现出色。 --- ## ¶ (https://blog.can.ac/2026/06/10/snapcompact/#0x0-the-case-against-compaction)0x0:反对压缩的理由 我并不是压缩的忠实粉丝。在每个测试框架中,包括我自己的,我一直觉得它会“削弱”模型,以至于还不如开启一个全新的会话。 省略工具结果是一个不错的替代方案——即时、确定——但有时并不足够。它偶尔还会让模型对工具调用感到困惑。LLM会完成故事;如果你的一半故事是`[省略...]`,你觉得它们对使用这些工具的信心会有多大? 交接(Handoffs)是目前最好的方法——但与计划不同,你通常不会主动引导交接,当你不引导时,代理会浪费宝贵的上下文来写一篇不必要的详细日记,接着是一份待办事项列表,这几乎是在乞求下一个代理宣布目标不可能实现,然后发布一个“MVP”版本。 我的想法基本上是,如果你经常需要压缩,那你肯定做错了什么:要么计划的范围出现了蔓延,要么应该通过子代理明确协调,这样主代理才能对整个范围负责。 然而,被1M上下文窗口宠坏了,现在我在会话结束时经常会达到50万token的标记——这在几个月前对我来说是不可饶恕的罪过。但对于长期任务,由一个一致的代理不间断地驱动计划会更好,即使有积极的委派,这也很容易达到那个级别。 所以,当我看到5小时使用限制条变红,而这个东西对我咧嘴笑时,我在想:也许我应该定期压缩…… 好吧。但如果我必须压缩,那它不能丢失任何东西。 --- ## ¶ (https://blog.can.ac/2026/06/10/snapcompact/#0x1-a-stupid-experiment)0x1:一个愚蠢的实验 一切始于一个328KB的会话日志和一个简单的问题:如果我把这个东西打印出来,然后用它开始会话会怎样? 第一次尝试最大程度地贪婪:使用了Tom Thumb (https://rob.lag.net/2010/01/23/tiny-monospace-font.html),一种3×5像素的字体,单个图像中包含122,696个字符。 我将其发送给一个全新的代理会话,没有做任何解释,然后收到了回复: > 图像似乎是纯噪声,带有随机像素,这表明它可能已损坏或是一个被错误命名为PNG的文件。 好吧。第二次尝试使用了X11的`6x10`字体(字形实际设计用于该单元格大小),共40,716个字符,每行文本循环使用六种颜色。相同的模型,结果如下: - 它识别出会话的主题,并**逐字向我引用**。 - 它100%准确地命中了日志中的18个标识符。 - 当被问及图像最底行(日志截断处)的一个赋值时,它有所保留(“我猜可能是`0`”)——但猜对了状态。 1万个token的文本,由3,279个图像token承载,以近乎完美的精度回忆起来。好吧,现在我投入了。 --- ## ¶ (https://blog.can.ac/2026/06/10/snapcompact/#0x2-optimizing-the-fonts)0x2:优化字体 字体能小到什么程度?我扫描了一些字体配置,并要求模型转录固定区域,根据真实文本计算编辑相似度: | 字体 (px²/字符) | 字符数 | 图像 token (估计) | 转录准确率 | 标识符读取 | |--------|--------|-------------------|------------|------------| | 8×13 | 1,042 | 23,520 | 1.00 | 20/20 | | 6×10 | 60 | 40,716 | 0.79 | 20/20 | | 5×8 | 40 | 61,348 | 0.37 | 17/19 | | 5×7 | 35 | 70,112 | 0.30 | 10/20 | | 4×6 | 24 | 102,312 | 0.02 | 9/20 | 悬崖很陡,大约在**每个字符35-40 px²**左右。在此之上,精确转录会退化,但*标识符级别*的回忆却出奇地强:模型无法重现每个字节,但它能读出名称。在此之下,则一无所获。 有趣的是,这一节比无用还糟糕——这个精确的优化稍后会反过来咬我们一口。 --- ## ¶ (https://blog.can.ac/2026/06/10/snapcompact/#0x3-thinking)0x3:思考…… 关于我自己日志的轶事并不具有普遍性,所以让我们做一个合适的基准测试:SQuAD v1.1,包含标准答案的抽取式问题。测试框架将文本段落按每种技术的承载能力分块,从每个块中均匀抽取30个问题(因此答案分布在图像的每一行,从上到下),并在同一个语料库上运行每种技术: - **text**——直接传递语料库;上限 - **handoff**——一个简单的交接提示 - **compact**——提供商端压缩(如果可用),否则使用摘要调用 - **img-{font}-{variant}**——snapcompact,其中变体为**bw**(纯黑底白字)或**sent**(字形墨水按句子循环颜色) 分数为SQuAD F1;模型被告知在无法提取事实时回答UNREADABLE。 | 技术 | fable-5 | opus-4.8 | gpt-5.5 | gemini-3.5-flash | |---------------------|-------------|-------------|-------------|------------------| | text (上限) | 0.904 $0.498| 0.911 $0.636| 0.861 $0.084| 0.898 $0.057 | | handoff | 0.540 $1.224| 0.248 $1.006| 0.368 $0.238| **0.889** $0.175 | | compact | 0.406 $0.942| 0.000 $0.757| **0.896** $0.339| 0.000 $0.042| | img-6×10-sent | **0.882** $0.640| 0.601 $0.243| 0.822 $0.245| 0.805 $0.097 | | img-6×10-bw | 0.856 $0.756| **0.652** $0.236| 0.792 $0.302| 0.767 $0.113 | | img-5×8-sent | 0.773 $0.453| 0.409 $0.162| 0.751 $0.181| 0.738 $0.100 | | img-5×8-bw | 0.830 $0.686| 0.425 $0.161| 0.778 $0.235| 0.674 $0.094 | 第一次尝试还不错……等等,我的token节省呢?怎么会更贵?让我们看看另一个表。 ### ¶ (https://blog.can.ac/2026/06/10/snapcompact/#tokens-inputoutputthinking)Token:输入/输出/思考 | 技术 | fable-5 | opus-4.8 | gpt-5.5 | gemini-3.5-flash | |---------------------|---------------------------|---------------------------|---------------------------|---------------------------| | text (上限) | 37,793 / 2,410 / 1,435 | 37,793 / 931 / 0 | 17,761 / 2,998 / 2,298 | 24,535 / 10,745 / 9,983 | | handoff | 49,363 / 14,609 / 3,717 | 43,237 / 4,773 / 0 | 25,032 / 11,710 / 4,835 | 49,387 / 36,577 / 11,959 | | compact | 45,130 / 9,828 / 3,248 | 40,436 / 2,014 / 0 | 45,562 / 15,329 / 1,032 | 26,151 / 6,582 / 5,422 | | img-6×10-sent | 11,816 / 10,437 / 9,483 | 11,816 / 877 / 0 | 10,188 / 14,049 / 13,368 | 4,991 / 23,491 / 22,764 | | img-6×10-bw | 11,816 / 12,772 / 11,782 | 11,816 / 796 / 0 | 10,188 / 17,639 / 16,958 | 4,991 / 27,616 / 26,879 | | img-5×8-sent | 7,955 / 7,474 / 6,897 | 7,955 / 577 / 0 | 4,519 / 10,773 / 10,336 | 3,355 / 24,659 / 24,196 | | img-5×8-bw | 7,955 / 12,141 / 11,559 | 7,955 / 568 / 0 | 6,823 / 13,892 / 13,463 | 3,355 / 23,014 / 22,542 | 几个结论: 1. **它确实有效**:在fable上,我测试的每个语料库长度上,F1得分在0.86-0.96之间,以三分之一的输入价格承载相同的信息。太棒了。 2. **输入节省并非免费**:模型通过*推理*来解码密集图像,而思考成本约为文本条件下输出token的5倍(在此示例中)。 按Anthropic的输出定价,解码税一次就可能吃掉输入节省。这在4万个token范围内是一个吹毛求疵的小问题——(a)没人会在这个范围内进行压缩,(b)解码只发生一次,而不是每轮——但仍然不够理想。 基线结果大多证实了为什么这值得做。散文式压缩是事实粉碎机:在压缩后的上下文中,Gemini在**240次中**回答UNREADABLE**240次**,Opus回答209次——摘要保留了你*在做什么*,而不是你*知道什么*。有两个例外:OpenAI的不透明服务器端压缩几乎保留了所有内容(但可能他们只是跳过了压缩,谁知道呢?),而Gemini的交接文档违反了提示的精神,写下了琐事,哈哈。 无论如何——该技术是否有效是每个模型视觉栈的经验属性,你必须进行测试。所以现在我们要学习视觉栈实际上是如何工作的。 --- ## ¶ (https://blog.can.ac/2026/06/10/snapcompact/#0x4-two-carriers-one-state)0x4:两种载体,一种状态 更强的说法——使snapcompact成为一种内存格式而不是一个花招的说法——是模型对两种载体的*思考*方式相同。我们知道它能读取图像;问题是内部结果是否具有文本的形状。 设置,在本地Qwen2.5-VL-7B-Instruct上:取一个SQuAD块和关于它的十二个问题。每个问题运行两次:一次将块作为纯文本放在提示中,一次将块作为1568²位图放在提示中——并捕获**最后一个提示token**处的隐藏状态,即模型“即将回答”的摘要,在每个解码器层。 原始状态看起来相似,原因无聊(相同的模板,相同的模型),所以比较减去每个载体的逐层均值——任何在中心化后存活下来的都是内容,而不是载体。然后进行三次测量: - **匹配对**(相同问题,文本 ↔ 图像):在第19层余弦相似度为**0.66**。**不匹配对**(不同问题):**−0.06**。状态编码的是*哪个问题对应哪个内容*,而不是哪种输入格式。 - **跨载体检索**:对于每个文本运行,找到最接近的图像运行。从第2层开始,相同的匹配**12次中12次**。 - **表征几何**:在文本载体内部计算的12×12问题相似性矩阵,与图像载体的矩阵在**r = 0.94**(第1层)时相关,在最终层稳定在**0.85**。两种载体几乎立即打印出相同的结构关系;随着深度增加的是每个问题的状态融合。 在行为上,两种载体产生相同的答案。这就是定价数学所利用的属性:PNG不是你上下文的*图像*——它收敛于*成为*你的上下文。 如果像素在模型内部变成文本,你可以问是在*哪里*。工具是logit透镜:在每个层,取覆盖答案词的视觉token的隐藏状态,通过最终的RMSNorm和LM头,检查top-1词汇条目。**锁定**是第一个其top-1是答案的BPE片段的层。 对于基线8×13渲染,包含“spectacular”尾部的补丁在十七层中解码为CJK噪声,在L18附近经过*字母形状*的噪声(`ALLERY`, `IGHL`——笔画组合成正字法),然后在**L24**翻转为`acular`,到最后一层达到p=0.39。 这很重要,因为锁定之前的层用于将像素转化为单词,而锁定之后的层可以自由地处理它们。所以我决定当一个头脑简单的人。注意力会积累证据,对吗?重复行,读取应该会更强? 另一件一旦你了解视觉token如何工作就非常明显的事情——Qwen将图像切分成28×28像素的窗口,每个窗口对应一个视觉token——是我们在文章开头纯粹凭感觉选择的字体大小不行。在6×10的情况下,每个token窗口包含大约13个字形,分布在三行文本中。 一个token窗口中的重叠垃圾更少:需要的思考更少。简单。 | 条件 | 锁定层 | 峰值p(答案) | 字符/视觉token | |------------------------|--------|------------|--------------| | 基线 8×13 | L24 | 0.39 | 7.5 | | 重复行 ×2,彩色 | L23 | 0.94 | 3.75 | | 对齐,4×2 字符/token | L24 | 0.74 | 8.0 | | 对齐,2×1 字符/token | L23 | 0.99 | 2.0 | | 对齐,1 字符/token | L22 | 0.99 | 1.0 | | 对齐 + 重复 | L23 | **1.00** | 1.0 | 深度拒绝移动(显著):最多L24到L22。这与OCR路由文献一致,其中视觉何时成为文本是一个架构属性 (https://arxiv.org/abs/2602.22918)。但*置信度*是完全可控的:仅行重复就将解码从0.39提升到0.94,同时仍承载每视觉token 3.75个字符。格式不能让模型更早读取;但它能使读取无歧义。笨模型不用想太多。 现在,在写文献综述时,我注意到这并非新想法:DeepSeek-OCR (https://arxiv.org/abs/2510.18234) 训练了一个自定义编码器用于光学上下文压缩,Karpathy 也 riffed (https://x.com/karpathy/status/1980397031542989305) 关于像素可能击败token作为输入媒介。 然而,在代理测试框架的上下文中,似乎人们看到浪费的思考量后就放弃了。但你看,我们为Qwen做的笨优化在前沿模型上通用得非常好! 1. **经过仔细调整的密集文本位图作为上下文载体,表现非常出色。** 在可读性下限处的合成像素字体渲染,经过基准测试——包括计费公式、静默降采样和自适应思考成本——与代理实际使用的压缩策略进行比较。PNG队加油! 2. **你可以在7B开放模型上将锁定驱动到确定性。** 从p=0.39到p=1.00,这么小的改变就达到了显著效果。是的,重复使像素面积翻倍——我们本可以获得比*仅仅*~3倍更显著的节省——但这仍然比文本便宜,现在具有近乎完美的回忆。如果你愿意,甚至可以将工具结果作为PNG返回。 这是它实时运行在测试框架中的样子: --- 测试框架再次获胜:模型本身没有变化;我们改变了它们周围的上下文。 *评估框架、字体渲染器、每个问题记录、白盒探针:omp (https://github.com/can1357/oh-my-pi/tree/master/packages/snapcompact/research)——`uv run final.py` 复现API网格(冷启动约$35,缓存后免费);表征运行需要本地GPU和Qwen2.5-VL-7B。* 即将在 oh-my-pi (https://omp.sh/) 上线!

相似文章

代码审查变得昂贵,重写变得廉价

Hacker News Top

LLMs 通过生成过度设计的代码,使代码审查变得更加昂贵,但重写现在变得廉价,从而将开发者的工作转向更多的前期规划和迭代简化。

文件系统是AI代理的新原语

Reddit r/AI_Agents

本文认为,文件系统因其悠久历史和在LLM训练数据中的广泛包含,为AI代理记忆提供了一种自然直观的原语,在探索性推理和持久化上下文方面优于传统数据库和API。