聊一聊怎么节省 Token,又不让 AI 降智
AI Agent Token 节流:删噪声,不删证据
AI Agent 用久了,最明显的问题往往不是“模型不会写代码”,而是它太能花 Token。
一次简单修改,本来只需要定位一个函数、改几行代码、跑一次测试。Agent 实际执行时却可能变成一连串往返:读文件 A,解释文件 A,搜索关键字,读取搜索结果,再读文件 A,修改一处,重新读取文件 A,验证修改;接着读取文件 B,修改文件 B,跑构建,读取几百行日志,总结日志,再读文件 C,继续修复。
表面上它很勤奋,实际上大量 Token 花在重复读取、重复解释、无关日志和过程叙述上。真正支撑判断的信息,可能只占很小一部分。
很多人一提到节省 Token,就会想到压缩提示词、减少上下文、让 AI 回答短一点。这些方法有时有效,也很容易把 AI 变笨。上下文砍得太狠,模型看不到调用关系;日志压得太粗,错误行号没了;历史删得太干净,用户前面强调过的限制也跟着消失。
Token 节流应该从“区分有效信息和无效信息”开始。节省的目标不是让 AI 少知道,而是让它少吞重复、噪声和机械过程,同时保留判断所需的证据、约束和结构。
这篇文章结合几个开源项目和官方文档,讨论一套不降智的上下文治理思路。
1 删噪声,不删证据
1.1 “省 Token 省到降智”是怎么发生的
AI 降智最常见的原因,是压缩时删掉了关键信息。
构建失败日志里有:
src/main.cpp(128): error C2065: 'config' : undeclared identifier
src/main.cpp(129): error C2143: syntax error : missing ';' before '}'
如果压缩成:
构建失败,存在语法错误。
Token 少了,模型也基本没法继续判断。它不知道哪个文件失败、哪一行失败、错误码是什么,也不知道下一步应该读哪段代码。
更合理的压缩应该保留定位能力:
构建失败:
- src/main.cpp:128,C2065,config 未声明
- src/main.cpp:129,C2143,缺少分号,可能由上一行错误引发
下一步:
- 优先读取 src/main.cpp 第 120-135 行
- 检查 config 的声明、作用域和 include 路径
这段摘要比原始日志短得多,但关键证据还在:文件路径、行号、错误码、推断原因和下一步动作。模型拿到它,仍然能继续工作。
1.2 上下文也有价值分层
节省 Token 前,先要区分上下文价值。
高价值上下文包括:用户目标、用户限制、当前任务状态、相关文件路径、函数签名、类型定义、调用关系、错误码、错误行号、失败条件、已完成修改、测试结果和下一步动作。
低价值上下文包括:礼貌套话、操作过程作文、重复读取的完整文件、无关日志、已经失效的计划、大段构建噪声、同一结论的多次复述,以及工具返回里的冗余包装。
上下文窗口应该留给第一类内容。第二类内容越多,成本越高,模型越容易被噪声干扰。
1.3 提纯,不是压短
好的 Token 节省不只是把文本变短,而是让信息密度变高。
它至少要保留三种能力:
-
定位能力:模型知道下一步该读哪里;
-
恢复能力:长任务压缩后还能接上;
-
决策依据:模型知道为什么这样改,而不只是知道“之前改过”。
很多压缩方案只追求短,最后把有效信息也压没了。真正需要的是提纯:去掉噪声,留下证据。
2 Aider 的 Repo Map:用仓库地图替代整仓硬塞
2.1 大仓库不能直接塞给模型
面对大仓库,最粗暴的做法是整仓投喂。这样做的问题很明显:Token 消耗巨大,无关文件淹没关键文件,模型注意力被分散,每一轮还要重复付费,上下文窗口很快打满。
只给当前文件也不可靠。模型看不到接口定义、调用方、被调用方、配置入口和测试入口,容易修一个文件,坏一片逻辑。
更稳的方式,是先给模型一张仓库地图。
2.2 Repo Map 的做法
Aider 会向 LLM 发送一份简洁的 Git 仓库地图,包含重要类、函数、类型和调用签名,帮助模型理解正在编辑的代码与仓库其他部分的关系。
模型一开始不一定需要完整源码,但需要知道项目里有哪些关键符号。仓库地图通常包含文件列表、关键类、关键函数、方法签名、类型定义、重要接口和模块关系。
Aider 文档还提到,repo map 可以帮助模型仅凭关键 API 信息完成一部分任务;需要更多代码时,模型再根据地图判断应该读取哪些具体文件。这就省掉了大量盲目探索。
2.3 Token 预算控制
repo map 本身也可能很大。Aider 不是无脑发送完整地图,而是在 token budget 内选择最相关的部分。
对于大型仓库,它会分析完整 repo map,使用图排序算法选择最重要、最相关,并且能放进当前 token budget 的代码片段。--map-tokens 可以影响 repo map 的 token 预算,默认值为 1k tokens。
这提供了一个很好的范式:
不要把仓库当成文本整体,
要把仓库当成图结构。
先给地图,
再按需展开,
最后只读取真正要修改的文件。
2.4 仓库上下文的三层结构
可以把仓库上下文分成三层。
第一层是仓库地图,用来放目录结构、关键模块、核心类、核心函数、接口、调用关系、配置入口和测试入口。
第二层是相关片段,用来放当前任务涉及的函数、调用方和被调用方、类型定义、错误附近代码。
第三层是完整文件,只在真正需要修改、需要整体理解,或者涉及复杂状态流转时展开。
这种结构比整仓硬塞更省,也比只看当前文件更稳。
3 Repomix:把仓库打包成 AI 友好的上下文
Repomix 的路线和 Aider 不同。Aider 更像“动态仓库地图 + 按需读取”,Repomix 更像“把仓库整理成一份适合 LLM 消化的上下文包”。
Repomix 可以把整个仓库打包成一个 AI-friendly 文件,方便喂给 ChatGPT、Claude、Gemini、Grok、DeepSeek 等大模型。典型用法是运行:
npx repomix@latest
然后生成类似 repomix-output.xml 的文件,供 AI 分析。
它能避免几类常见浪费:把 node_modules、dist、build 等无关目录塞给模型;把 .gitignore 已经排除的内容塞给模型;不知道整包大概多少 token;不同模型需要不同格式却手工整理;把敏感信息误塞进上下文。
Repomix 的核心能力包括 AI-Optimized Output、Token Counting、Git-Aware、Security-Focused 和 Multiple Output Formats。它的节省方式更偏输入整理:把散乱仓库整理成单一上下文,让用户知道 token 规模,通过 ignore 和配置减少无关文件,用统一格式降低模型解析成本。
Repomix 更适合一次性场景,比如让 AI 快速理解一个项目、做全局代码审查、生成项目说明、分析架构、制定迁移计划,或者一次性向网页模型喂项目上下文。
持续修改时,每轮都把整仓包塞回上下文仍然会贵。更合理的做法是:第一次理解项目时用 Repomix 打包全局上下文,后续具体修改只传 repo map、相关片段和目标文件,阶段性总结时再重新生成摘要或差异包。
4 Gitingest:用 Digest 降低仓库投喂门槛
Gitingest 的定位更轻量。它可以把任意 Git 仓库转换成一个简单的代码库文本 digest,用于喂给任意 LLM。
它的页面提供了几个实用的上下文控制入口:Exclude、Include、Include files under: 50kB、Directory Structure、Files Content。它不是简单复制仓库,而是在生成 digest 时允许控制文件范围和大小。
它的核心思路是:把仓库转换成 LLM 容易读取的 digest,保留目录结构,按 include / exclude 控制范围,过滤过大的文件,生成可复制或下载的文本结果。
Gitingest 节省的是人工整理仓库上下文的成本,同时减少无关内容进入模型。它适合快速让网页 AI 看一个开源项目、讨论项目结构、生成初步 README、做代码库概览。
和 Repomix 类似,它更适合项目理解和上下文准备,不适合每轮 Agent 编辑中反复整仓投喂。用于持续开发时,可以把它当作第一轮上下文输入,后续再转为按需读取。
5 LangChain / LangGraph 的短期记忆治理
5.1 长对话为什么会浪费 Token
Agent 任务跑久以后,历史消息会越来越长。
里面可能有早期计划、工具调用结果、中间失败、用户纠正、已完成修改、旧日志、无效尝试和阶段总结。全部保留,Token 成本上升,模型还容易被旧信息干扰;全部删除,模型又会丢掉用户约束和任务状态。
5.2 LangChain 的三类策略
LangChain 的 short-term memory 文档提到,长对话可能超过 LLM 上下文窗口,常见方案包括 trim messages、delete messages 和 summarize messages。
trim messages 是在调用模型前移除部分消息;delete messages 是从 LangGraph state 中永久删除消息;summarize messages 是把早期历史总结成摘要替换原消息。
文档也提醒,简单 trim 或 delete 可能丢失信息,一些应用会受益于更复杂的摘要方法。
5.3 编程 Agent 的摘要应该像状态快照
对编程任务来说,“之前已经做了一些修改,目前还有构建错误,需要继续排查”这种摘要太虚。它看起来保留了上下文,实际上无法恢复任务。
更适合编程 Agent 的摘要应该像状态快照:
任务目标:
- 修复 Windows 下构建失败
用户约束:
- 不改公开接口
- 不引入第三方依赖
- 保持 VS2010 兼容
已确认事实:
- PowerShell 5.1 日志可能是 UTF-16LE
- src/a.cpp 存在非 ASCII 字符
- 当前构建使用 MSBuild
已修改:
- src/a.cpp:中文日志改为 ASCII
- src/b.cpp:static 成员定义移动到 cpp
当前失败:
- src/c.cpp:128 C2065,config 未声明
下一步:
- 读取 src/c.cpp 第 120-135 行
- 检查 config 的声明和 include 路径
这种摘要更长一点,但可恢复。旧上下文被删掉后,模型还能继续工作。
长对话压缩应保留目标、约束、已读事实、已做修改、当前错误、未解决风险、下一步动作,以及禁止重复的失败尝试。这比简单保留“最后 N 条消息”更可靠。
6 Claude Code 的 Memory / Rules:规则也需要节流
6.1 规则文件也会浪费 Token
很多团队把大量规则写进项目记忆文件:代码风格、目录结构、构建命令、测试命令、提交规范、前端规则、后端规则、数据库规则、部署流程、历史坑点。
这些规则如果每次会话都加载,就会长期占用上下文。
Claude Code 文档说明,CLAUDE.md 文件会在每个会话开始时读取,作为上下文使用;指令越具体、越简洁,Claude 越容易稳定遵循。文档也直接提醒:CLAUDE.md 会消耗上下文,建议每个文件控制在 200 行以内;超过 200 行会消耗更多上下文,并可能降低遵循效果。
6.2 分层加载
规则节省 Token 的核心,不是删掉规则,而是分层加载。
全局常驻规则只保留安全边界、项目核心约定、输出格式和构建入口。
路径级规则放在对应目录,比如 src/frontend 下的前端规则、src/backend 下的后端规则、database 下的迁移规则、legacy 下的老编译器规则。
任务级 Skill 则按任务启用,比如批量编辑、编译诊断、文档生成、代码审查、性能分析。
Claude Code 文档提到,子目录里的 CLAUDE.md 不会一开始全部加载,而是在 Claude 读取这些子目录文件时才进入上下文;大型 monorepo 也可以通过排除设置跳过不相关的 CLAUDE.md。
这说明规则也应该只在相关时加载。
6.3 通用方法
对任何 Agent 系统来说,规则文件都应该遵循几条原则:常驻规则短,路径规则准,任务 Skill 按需启用,重复规则不要多处复制,人类注释不要进入模型上下文,过期规则及时删除。
规则越多,模型不一定越聪明。很多时候,规则越短、越具体,遵循反而更稳。
7 Prompt Caching:稳定前缀也能省 Token
7.1 每轮都重复的内容
有些 Token 不是无效信息,而是每轮都必须重复出现的信息,比如系统提示、工具说明、输出格式、项目约定、固定示例和安全边界。
这类内容不能删,但可以缓存。
OpenAI 文档说明,模型提示经常包含系统提示和通用指令等重复内容;Prompt Caching 可以减少延迟和输入 token 成本。缓存命中依赖 prompt 前缀精确匹配,因此应该把静态内容放在提示开头,把变量内容放在末尾。
7.2 前缀和后缀的设计
适合放在前缀的内容包括系统角色、工具定义、输出协议、项目核心规则、少量固定示例和安全边界。
适合放在后缀的内容包括当前用户任务、当前文件片段、本轮工具结果、本轮错误日志、临时约束和下一步请求。
结构可以这样设计:
[稳定前缀]
- 系统规则
- 工具说明
- 输出格式
- 项目核心约定
[动态后缀]
- 当前任务
- 当前证据
- 当前错误
- 当前需要模型判断的问题
OpenAI 文档明确写到,缓存命中只可能发生在 prompt 的 exact prefix match 中,静态内容和示例应放在开头,变量内容放在末尾。
7.3 Anthropic 的 cache_control
Anthropic 也支持类似思路。Claude 平台文档提到,可以通过 cache_control 指定缓存行为;自动缓存方式会在请求体顶层添加 cache_control,系统自动把 cache breakpoint 应用到最后一个可缓存块。
能稳定复用的上下文,不要每轮重新全价处理。
但 Prompt Caching 不是万能药。它能降低重复前缀成本,不能解决上下文本身混乱的问题。一个巨大、过期、冲突的规则文件,即使命中缓存,仍然会干扰模型判断。
8 agent-thrift:合并工具调用,减少机械往返
8.1 具体浪费在哪
agent-thrift 是我做的一个开源 Skill,思路比较小而具体:减少 AI 编程助手在文件操作上的重复往返。
当 AI 改代码时,它常常反复“读文件 → 改一处 → 再读 → 再改 → 复核”。每一步都是一次 API 调用,又慢又费 token。agent-thrift 把这些零碎动作合并成单次 Python 脚本调用。
它的能力包括批量编辑、工程体检、编译诊断、改动对比、安全读写和自动备份。
批量编辑可以一次完成备份、多处替换和写回;工程体检可以扫描调试刷屏和非 ASCII 字符;编译诊断可以跑构建并解析报错。
8.2 节省点
agent-thrift 的节省点主要有几类:N 次工具调用压成 1 次;改完不复读;scan、diagnose、diff_bak 返回结构化摘要;输出零废话;脚本处理机械 I/O。
其中最关键的是工具调用合并。
AI 改代码真正的开销是工具调用往返:每改一处,都要把文件内容和工具结果重新塞回上下文;多次小改近似等于多次完整往返。
8.3 为什么不降智
它不隐藏模型需要判断的代码。需要看文件时照样完整读;砍掉的是重复重读和废话叙述,机械文件操作交给确定性 Python 脚本,模型的判断权仍在模型手里。
这类方案解决的是工具往返浪费,但不是全部答案。它应该和 repo map、短期记忆、prompt caching、规则按需加载结合使用。
9 不降智的 Token 节流架构:七层合起来
9.1 第一层:稳定前缀缓存
稳定前缀用于放系统规则、工具定义、输出协议、安全边界和项目核心约定。目标是提高 Prompt Caching 命中率,降低重复前缀成本。
9.2 第二层:短规则常驻
常驻规则必须短、准、稳定。不要把所有项目知识都塞进去,不要把只在某个目录生效的规则全局加载,也不要把历史坑点写成一整本书。
可以借鉴 Claude Code 的做法,把规则文件控制在合理长度,并让子目录规则在相关文件被读取时才进入上下文。
9.3 第三层:仓库地图
仓库地图解决全局结构问题。可以借鉴 Aider repo map:列出关键文件,抽取重要类和函数,保留方法签名,控制 token budget,按相关性动态选择片段。
9.4 第四层:按需展开代码
根据任务读取相关片段,比如错误附近 20 行、函数定义、调用方、被调用方、接口定义、配置入口和测试文件。
只有在需要整体理解时,才读取完整文件。
9.5 第五层:工具调用合并
把机械过程交给脚本:多次 grep 变成一次搜索摘要,多次 read 变成一次多文件读取,多次 edit 变成一次 batch-edit,build + log + diagnose 变成一次诊断结果,diff + count 变成一次结构化摘要。
这一层可以借鉴 agent-thrift。
9.6 第六层:长任务状态快照
长对话不要只保留最近 N 条消息。更好的做法是生成任务状态:目标、约束、已确认事实、已修改文件、当前失败、风险和下一步。
可以借鉴 LangChain 的 trim / delete / summarize 思路,但编程场景里要把摘要做成结构化状态。
9.7 第七层:阶段性仓库打包
需要全局理解时,可以用 Repomix 或 Gitingest 生成项目 digest。进入持续编辑阶段后,切回 repo map + 按需读取,避免每轮整仓投喂。
10 各方案对比
| 方案 | 核心方法 | 最适合的场景 | 节省 Token 的方式 |
|---|---|---|---|
| Aider Repo Map | 仓库地图 + 图排序 + token budget | 持续代码编辑、大仓库理解、跨文件修改 | 用低 token 地图提供全局感,需要时再展开具体文件 |
| Repomix | 仓库打包成 AI-friendly 单文件 | 一次性分析项目、代码审查、架构理解、网页模型投喂 | 过滤无关文件、尊重 gitignore、提前知道 token 规模 |
| Gitingest | 仓库转 digest + include / exclude 控制 | 快速分享仓库给 LLM、生成项目概览、轻量上下文准备 | 快速生成可投喂 digest,通过 include / exclude 控制范围 |
| LangChain / LangGraph | trim / delete / summarize messages | 多轮 Agent、长任务、任务状态延续、对话历史治理 | 裁剪无效历史,摘要早期消息,保留关键状态 |
| Claude Code Memory | CLAUDE.md + 目录级规则 + 路径级加载 | 长期项目协作、团队规范注入、Monorepo | 规则按范围加载,控制 CLAUDE.md 长度 |
| Prompt Caching | 稳定前缀 + exact prefix match | 重复系统提示、固定工具定义、多轮相同项目约定 | 复用稳定前缀,降低输入 token 成本 |
| agent-thrift | 批量编辑 + 诊断 + 结构化摘要 | AI 编程助手改代码、频繁文件操作、编译诊断 | N 次工具调用压成 1 次,脚本处理确定性 I/O |
Aider、Repomix、Gitingest 都在处理仓库上下文问题,但路线不同。Aider 是动态地图加按需读取,适合持续编辑;Repomix 是整仓打包,适合一次性理解;Gitingest 更轻量,适合快速概览。
LangChain 和 Claude Code 处理的是记忆和规则问题。LangChain 更关注长对话历史,Claude Code 更关注项目级规则。编程 Agent 通常需要两者配合:历史做状态快照,规则按范围加载。
Prompt Caching 处理的是前缀重复成本,和上下文质量本身不是一回事。它可以和任何方案配合省钱,但不能替代上下文治理。
agent-thrift 处理的是工具往返浪费。它和上述方案互补:仓库结构用地图表达,记忆做快照,规则按范围加载,缓存稳定前缀,机械文件操作交给脚本。
11 落地清单
11.1 输入侧
建立 repo map;过滤无关目录;忽略构建产物;控制单文件大小;按需读取具体片段;保留错误证据;不要整仓反复投喂。
11.2 记忆侧
短规则常驻;长规则按需;历史做状态快照;用户约束不可丢;失败尝试要记录;过期过程要删除。
11.3 工具侧
合并工具调用;批量编辑;批量读取;构建日志结构化;diff 结果数字化;搜索结果带上下文;编码和备份交给脚本。
11.4 输出侧
少写过程作文;多输出状态表;保留文件、行号、错误码;明确下一步;不要每轮复述背景;不要把完整日志贴回聊天。
11.5 缓存侧
静态内容放前缀;动态任务放后缀;保持工具定义稳定;不要频繁改变系统提示顺序;充分利用 Prompt Caching。
12 让 AI 少做搬运工,多做判断者
仓库结构用地图表达,完整代码按需展开,长历史压成状态快照,规则按范围加载,重复前缀交给缓存,机械文件操作交给脚本,工具结果输出结构化证据。
AI Agent 最宝贵的能力是判断,不是反复搬运文件、复述日志和写过程作文。
上下文治理做得好,AI 不但更省 Token,也更稳定。省掉的是噪声,留下的是证据;减少的是机械往返,保留的是推理依据。
13 参考项目与资料
Aider Repo Map
Repository map | aider
Repomix
GitHub - yamadashy/repomix: 📦 Repomix is a powerful tool that packs your entire repository into a single, AI-friendly file. Perfect for when you need to feed your codebase to Large Language Models (LLMs) or other AI tools like Claude, ChatGPT, DeepSeek, Perplexity, Gemini, Gemma, Llama, Grok, and more. · GitHub
Getting Started with Repomix | Repomix
Gitingest
Gitingest
LangChain Short-term Memory
Short-term memory - Docs by LangChain
Claude Code Memory / Rules
How Claude remembers your project - Claude Code Docs
OpenAI Prompt Caching
https://developers.openai.com/api/docs/guides/prompt-caching
Anthropic Prompt Caching
App unavailable in region | Claude by Anthropic
agent-thrift
GitHub - Lanqingsong/-agent-thrift · GitHub
更多推荐
所有评论(0)