Agent 的上下文账单越滚越大:用 Turo 在代理层压缩输入前,先划清可损失语义的边界
AI 编程 Agent 的成本并不只来自一次“写代码”的回答。长时间任务会反复把系统提示、项目说明、工具返回、日志片段和对话历史送进模型;当同一份信息被多轮携带时,输入 token 很容易成为持续支出。更麻烦的是,开发者往往只能在模型供应商的账单里看到总额,难以判断究竟是哪一段上下文开始失控。
Turo 是一个面向命令行 Agent 的 Go 工具:它把英文散文压缩为更短的内容词,并可作为本地代理插在 Agent 与模型 API 之间。它支持 Claude Code、Codex、Gemini、Cursor、Windsurf、Cline、Copilot 等二十多种 Agent 的接入方式。它不是模型路由器,也不会减少模型输出;它的工作对象是送往模型前的输入文本。因此,更准确的使用目标不是“免费获得更大上下文”,而是把适合压缩的、重复性强的自然语言减负,同时保留代码和关键指令的原样传递。
先理解它压缩了什么
Turo 的默认流程有四个阶段:删除礼貌语、语气词等 filler;把部分词替换为更省 token 的同词性词;把词替换为定义中更短的词;最后仅保留内容词,并在 ultra 级别按词元去重。项目说明称,代码块、内联代码、URL、文件路径、版本号和技术标识符会原样保留;如果处理结果不比输入更短,则会回退为原文。
这套规则天然有取舍。对于“请仔细检查变更、确认是否补充测试、发现风险请提出修改”等重复性英文说明,去掉修饰语通常影响不大。README 给出的示例中,一段 138 token 的英文 PR 审查说明被默认处理为 54 token,标注为减少 61%;--level ultra 的示例为 41 token、减少 70%。这些数字是该仓库用 cl100k 计数器对示例文本测得的结果,不能直接当作任意模型、任意仓库的节省承诺。
风险主要来自语义被故意压扁。Turo 的 gloss 阶段会以定义中的短词替换原词,项目文档也承认这是最有损的一步;同义词替换可能受一词多义影响。换句话说,压缩后的内容不再适合承担精确的需求、权限边界、删除条件或安全例外。把“不要删除生产数据,除非工单已批准”压成关键词列表,可能保留了 delete、production、approved,却丢失了否定和条件关系。
从单文件试验开始,而不是全局接管
先把项目说明或一段历史日志过一遍,观察结果是否仍够用。官方提供了 Homebrew、Go、Shell 与发布页四种安装方式;对于已有 Go 环境的机器,可以使用:
go install github.com/kdeps/turo@latest cat CLAUDE.md | turo -passes 1 > /tmp/CLAUDE.compact.md diff -u CLAUDE.md /tmp/CLAUDE.compact.md
默认的 -passes 0 会持续运行到输出不再变化;README 提醒,后续轮次会逐步压平结构并跨段去重。对于 Markdown 规范、运行手册和设计文档,建议先用 -passes 1,因为标题和段落组织本身就是人类复核时的重要线索。
需要更小输入时,可按风险从低到高逐项开启:默认 full/ultra 以外,-filler=false 关闭 filler 删除,-synonyms=false 关闭同义词替换,-gloss=false 关闭最有损的定义词替换,-arrows 才会把多词因果连接词替换为 ->。一个保守的基线是只保留词类删减、禁用 gloss:
cat review-notes.txt | turo \ -gloss=false \ -synonyms=false \ -passes 1
不要把上面的输出当作可直接执行的 Agent 指令。更稳妥的办法是对同一组固定任务运行原文与压缩版:比较测试是否通过、生成 diff 是否相同、工具调用是否出现权限扩大,再记录真实输入 token 和完成时间。只有在任务成功率没有明显下降时,压缩比例才有业务意义。
两种接入方式,对应两类边界
如果只是临时压缩上下文,管道模式最透明:cat CLAUDE.md | turo --preamble 会输出可用于系统提示前缀的紧凑文本。它的优点是开发者明确知道哪一段被处理;缺点是需要自己决定何时、如何把结果交给 Agent。
如果希望一个会话内持续处理请求,Turo 提供 turo run。例如:
turo run claude turo -proxy -upstream https://api.openai.com export OPENAI_BASE_URL=http://127.0.0.1:8787/v1
项目文档说明,代理默认只压缩 user 与 tool 内容,system 和 assistant 历史保持原样;-proxy-all 才会处理所有角色。这个默认值很值得保留:系统规则与既有回答通常包含上下文约束,贸然改写比节省 token 更可能造成行为漂移。代理会透传认证头,且非聊天路径会原样转发,因此它应被视为一个会接触请求内容的本地组件。不要在共享开发机上无审查地把它暴露到外部接口,也不要把真实生产密钥写入演示脚本。
中文模型与 wenyan 不是通用捷径
Turo 还有 --level wenyan:在 ultra 后把词表中可映射的英文内容词替换为单个文言字符。README 的示例显示,它在 Qwen、DeepSeek、GLM 等“常见汉字约一 token”的计数假设下可能更省;但在 OpenAI 的 cl100k 上,一个汉字通常是两到三个 token,示例中 wenyan 反而比 ultra 更大。
这说明 tokenizer 才是优化对象,语言外观不是。即使使用 CJK 优化模型,也应确认英文术语、代码名和未被词表覆盖的词是否混入输出;更不应因为字符数变短,就推断 API 账单一定下降。先从供应商的实际 usage 数据取得基线,再用同一提示集和同一模型复测,才可以决定是否为某个团队默认启用。
先做“不可压缩清单”,再谈默认开关
在团队层面启用前,可以把输入分成四类并写进仓库约定。第一类是不可压缩内容:系统提示、权限策略、生产变更步骤、SQL 条件、删除或付款等带否定词和阈值的命令;它们应直接传递,并保留人工可读的原文。第二类是可单次轻量处理内容:项目背景、模块说明和较长的设计摘要,推荐禁用 gloss、保留一次处理后的 diff 供评审。第三类是可代理处理内容:工具日志、测试输出、重复性的进度报告和大段英文对话。第四类是二进制、代码、URL、文件路径与版本号;Turo 的设计是尽量保护这些片段,但仍应通过真实任务确认项目特有格式没有被意外拆开。
还要为回滚准备一个简单的开关。Turo 文档列出了 KDEPS_TURO=off 与 TURO_DISABLED=1 用于停用处理;把其中一个放进 CI 或本地启动脚本,比让每位开发者记住一串代理参数可靠。发生测试波动、工具调用失败或模型误解需求时,先用原始输入重跑同一任务;若问题消失,就把对应文本类型移回不可压缩或轻量处理层。这样能把“压缩导致的行为变化”变成可定位的实验变量,而不是一次难以复盘的模型异常。
把“压缩”放进可回滚的工程流程
Turo 最适合的场景是:重复出现的英文工具日志、长而模板化的审查提示、由多个 Agent 共享的说明性上下文,以及成本敏感但可以离线验收的开发任务。它不适合直接压缩法律条款、事故处置步骤、精确迁移方案、含有否定条件的权限指令,或任何一旦误解就会改变外部状态的操作。
一个可落地的策略是建立三层输入:第一层是不可改写的系统规则、密钥边界和命令模板;第二层是仅做一次轻量压缩的项目背景;第三层才是可以使用 ultra 或代理模式处理的日志、进度总结和重复性自然语言。每次更新 Turo、模型或 Agent 客户端后,都重新跑一组固定任务。把压缩率、失败类型、重试次数和人工返工一起记录,才能看出“少发 token”是否真的换来了更低的总成本。
真正需要控制的不是某次请求的字符数,而是 Agent 在可靠性、可读性与预算之间的边界。把压缩工具放在可观察、可关闭、可回归测试的位置,才不会让省下来的输入 token 变成更贵的排障时间。