把上下文当预算管:开源 Agent 套件 ECC 的窗口取舍是怎么落地的
本文基于 ECC 仓库 commit 591ab5c(2026-07-29)梳理,该项目仍在高频迭代,具体行为以仓库 https://github.com/affaan-m/ECC 最新代码与文档为准。
上下文窗口里最贵的那部分,往往不是你贴进去的代码,而是你还没开口就已经加载好的一堆组件描述。 你装的 agent 一次没调用过,它的 description 照样在每次 Task 工具的上下文里;你连上的 MCP server 一次没用过,它的工具 schema 照样常驻。谁值得占这个位置、谁该被挤出去,这是个需要判据的工程问题,不是”注意精简”四个字能解决的。
站内 上下文窗口是什么 讲的是窗口这个东西本身,Agent 的上下文预算怎么定 和 token 成本优化 讲的是通用方法论。这篇不重复那些,只看一个真实存在、你现在就能 clone 下来对照的开源项目——ECC 这套装在编码 Agent 之上的 harness 增强件——把这条线落成了哪些文件、哪些判据、哪些开关。它采用 MIT 许可证。
一、先看清”开口之前”已经花掉了什么
ECC 仓库里 agents/ 有 67 个 agent、skills/ 有 281 个技能、commands/ 有 94 个命令。这三个数字容易吓人,但它们本身不是问题——问题是这些东西里有多少是常驻的。
skills/context-budget/SKILL.md 这个技能就是干这件事的。它的第一阶段是清点,扫描对象明确列了五类:agents/*.md、skills/*/SKILL.md、rules/**/*.md、.mcp.json(或当前生效的 MCP 配置)、以及 CLAUDE.md 链(项目级加用户级)。
估算口径也写死了:散文按 words × 1.3,代码密集的文件按 chars / 4。这不是精确计数,是为了让不同类型的文件能放在同一把尺子上排序。
技能里有两条判断,比数字本身更值得记住:
一是 agent 的 description 是永远在场的。文档原话的意思很直白——即使这个 agent 从来没被调用,它 frontmatter 里的 description 字段也存在于每一次 Task 工具的上下文中。所以”我装了很多但很少用”这种自我安慰不成立。
二是 MCP 是最大的那根杠杆。技能给出的估算口径是每个工具 schema 约 500 tokens,并直接下了结论:一个 30 工具的 server 比你所有技能加起来还贵。这条判断决定了清理的先后顺序——先看 MCP,再看别的。
扫描时还有个容易忽略的细节:清点技能时会检查 .agents/skills/ 下是否存在重复拷贝,完全相同的直接跳过,避免同一份内容被算两遍。这种细节决定了报告数字能不能拿来做决策。
二、值得留 / 该挤掉:判据是可查证的事实,不是感觉
清点完是分档。context-budget 把每个组件塞进三个桶,判据全部是能查证的客观事实:
- 总是需要——被 CLAUDE.md 引用、支撑着某个在用的命令、或者与当前项目类型匹配。动作是保留。
- 有时需要——领域特定(比如某种语言的编码模式),但 CLAUDE.md 里没提到。动作是考虑改成按需激活。
- 很少需要——没有任何命令引用它、内容与别的组件重叠、或者跟这个项目根本对不上。动作是删掉或者懒加载。
注意这三条判据里没有一条是”我觉得有用”。是否被引用、是否支撑命令、是否与项目类型匹配,都可以 grep 出来。这是把口号变成可执行判断的关键一步。
接下来是找具体问题。这一步的判据分散在清点与检测两个阶段里,但都是明码标价的:frontmatter 里 description 超过 30 词算臃肿;agent 文件超过 200 行算重量级,因为它会在每次派生时撑大 Task 工具的上下文;单个 SKILL.md 超过 400 行、单个 rules 文件超过 100 行、CLAUDE.md 链合计超过 300 行都会被标出来;MCP 方面则盯两种情况——单个 server 工具数超过 20,以及 server 只是把本来就有命令行工具的东西包了一层(文档点名了 gh、git、npm、supabase、vercel 这类)。此外还有技能重复了 agent 的逻辑、rules 重复了 CLAUDE.md 的内容这种冗余。
第四阶段出报告,排序方式是按能省下的 token 从多到少,并给出 Top 3 优化动作。verbose 模式追加每文件明细、最重文件的逐行拆解、重叠组件之间具体哪几行重复、以及 MCP 工具清单加每个工具的 schema 体积估算。文档自己也提醒了:verbose 是用来定位问题文件的,不是日常体检该开的档位。
这套东西在仓库里不是孤立的一个文件,它周围还有一圈配套:
| 组成部分 | 它负责什么 | 仓库位置 | 你什么时候会碰到它 |
|---|---|---|---|
| 上下文预算技能 | 清点各类组件的窗口占用、分档、按可省 token 排序出报告 | skills/context-budget/SKILL.md | 会话变钝、或刚装了一批新组件时 |
| 旧斜杠入口垫片 | 保留 /context-budget 这个老入口,转交给技能执行 | legacy-command-shims/commands/context-budget.md | 你还在用老命令名时 |
| 策略性压缩技能 | 判断在哪些阶段边界上值得手动 /compact,以及压缩会丢什么 | skills/strategic-compact/SKILL.md | 长会话跨阶段切换时 |
| 压缩提示钩子 | 在 Edit/Write 之前,按上下文规模与工具调用数提醒你压缩 | scripts/hooks/suggest-compact.js(注册在 hooks/hooks.json) | 装了标准或严格钩子档位就会自动生效 |
| 上下文监控钩子 | 工具调用之后注入警告:上下文耗尽、成本、范围蔓延、工具循环 | scripts/hooks/ecc-context-monitor.js | 会话跑偏或窗口吃紧时 |
| 回答深度顾问技能 | 回答之前先让你选深度档位,控制输出体积 | skills/token-budget-advisor/SKILL.md | 你主动说”给我短版本”这类要求时 |
| 成本报告命令 | 汇总本地成本日志,按天、模型、会话聚合 | commands/cost-report.md | 想看本地累计消耗时 |
| 装备体检命令 | 含 Context Efficiency 维度的确定性打分 | commands/harness-audit.md + scripts/harness-audit.js | 想让上下文效率变成可回归的分数时 |
三、盘点是静态的,会话是动态的
清点解决的是”开局有多重”,解决不了”跑到一半胀起来”。这部分归 skills/strategic-compact/SKILL.md 和它背后的钩子。
suggest-compact.js 挂在 PreToolUse(Edit 和 Write)上,合并两个信号:
上下文规模是主信号。 它从钩子载荷里的 transcript_path 读会话记录,取最新一条 usage,把 input_tokens + cache_read_input_tokens + cache_creation_input_tokens 加起来——这才是这一轮真实的上下文规模。阈值按窗口大小缩放,越过之后每再增长一截重新提醒一次。
工具调用次数是次信号。 计数到阈值提醒一次,之后每隔一段再提醒。技能里明说了为什么这个信号弱:几次大文件读取或几个 MCP 大响应就能把窗口填满,而一堆细碎调用跨过次数阈值时窗口可能还几乎是空的。把次数当主判据,方向就反了。
可调的环境变量是 COMPACT_THRESHOLD、COMPACT_CONTEXT_THRESHOLD(设成 0 就关掉上下文信号)、COMPACT_CONTEXT_INTERVAL、COMPACT_STATE_TTL_DAYS,以及两个窗口大小覆盖项 ECC_CONTEXT_WINDOW_TOKENS 和 CLAUDE_CODE_AUTO_COMPACT_WINDOW(后者作为前者未设置时的回退)。窗口大小平时靠模型 id 里的 [1m] 标记自动探测,或者在观测到的 token 数已经很大时反推。探测不到就得手动设——不然阈值会按小窗口算,把你的占用说得比实际严重得多。
技能里还有一张表,我认为是整个设计里最该被记住的部分:压缩之后什么留下、什么消失。留下的是 CLAUDE.md 指令、TodoWrite 任务列表、~/.claude/memory/ 下的记忆文件、git 状态、以及磁盘上的文件;消失的是中间推理与分析、你之前读过的文件内容、多步对话的脉络、工具调用历史、以及你口头说过的偏好。这张表直接推出一条操作纪律:压缩前先把结论写进文件。
运行时的另一半是 scripts/hooks/ecc-context-monitor.js。它是 PostToolUse 钩子,读 ecc-metrics-bridge.js 写下的桥接文件,在跨过阈值时向 agent 注入警告,类别包括上下文耗尽、成本、范围蔓延和工具循环。用订阅制的人常被本地成本估算干扰,仓库为此留了单独开关 ECC_CONTEXT_MONITOR_COST_WARNINGS,设成 off 只压掉成本那一类,上下文耗尽、范围、循环三类警告照旧。
docs/token-optimization.md 里给的推荐配置是这样一段:
{
"model": "sonnet",
"env": {
"MAX_THINKING_TOKENS": "10000",
"CLAUDE_CODE_SUBAGENT_MODEL": "haiku"
}
}
这是仓库自己的推荐默认值,文档也说了高级用户应按工作负载再调。涉及模型服务商的计费与限制,各家规则不同且会调整,以官方最新说明为准。想接着看窗口管理的通用做法可以参考 Claude Code 的上下文管理。
四、边界与代价:它明确不管什么
这套设计有几件事是主动放弃的,用之前得清楚。
全部是估算,不是账单。 words × 1.3、chars / 4、每工具约 500 tokens,都是启发式口径。它给你的是一个排序,不是精确计数。拿这个数字去跟服务商对账会对不上,那不是它的用途。
它只管常驻开销,不管会话中途长出来的东西。 你一次读进来的大文件、MCP 返回的大 JSON,不在清点范围内;那部分归 suggest-compact 的运行时信号管。这是两套机制,别指望其中一个覆盖全部。
分档判据依赖 CLAUDE.md 的引用关系。 如果你的 CLAUDE.md 写得很薄、什么都没提,很多你天天在用的组件会被判成”很少需要”。这个方法对”CLAUDE.md 是否诚实反映了你的实际用法”有前置要求。
它只看体积和引用,不看内容质量。 一个 800 行的 SKILL.md 会被标成重量级,但它可能恰好是你最依赖的那个。标记的含义是”去看一眼”,不是”删掉”。
它会往你机器里写文件、挂钩子、连外部服务。 仓库 README 自己把这三件事并列,称作应当被当成可执行配置来对待——钩子会执行 shell 命令,MCP server 可能持有凭证,项目指令会进入 agent 的上下文。这个定性是诚实的,也意味着安装之前你应该自己读一遍 hooks/hooks.json 和 scripts/hooks/ 下的脚本,而不是照着 README 一路回车。
有些开关不是你以为的那个开关。 ECC_DISABLED_MCPS 只影响安装与同步流程生成的 MCP 配置输出(install.sh、npx ecc-install、Codex 的 MCP 合并),它不是运行时开关。想在运行时关掉一个已经加载的 MCP server,得用 /mcp,Claude Code 会把这个选择记在 ~/.claude.json 里;改 .claude/settings.json 或 .claude/settings.local.json 对已加载的 server 无效。
自动压缩的覆盖参数有争议,文档没装懂。 docs/token-optimization.md 提到,有社区反馈说 CLAUDE_AUTOCOMPACT_PCT_OVERRIDE 只能把压缩阈值调低,也就是可能比默认更早压缩而不是更晚。仓库给的建议是遇到这种情况就去掉这个覆盖,回到手动 /compact 加 strategic-compact 的提示。把它标成”社区报告”而不是结论,这个措辞比硬下断言可信。
装了它上下文不会自动变小。 删组件、关 server、执行压缩,每一步都要你自己点头。它做的是把决策所需的信息摆到你面前。
五、上手与避坑清单
先看盘点再动手,别先删。 会踩是因为看到”281 个技能”这种数字容易产生砍一半的冲动,但技能目录里的文件并不等于每次会话全量常驻,真正贵的是 agent 的 description 和 MCP 的工具 schema。避法是先跑一次清点拿到分档和排序,从”能省最多”那条往下做。
MCP 先看数量再看内容。 会踩是因为加 server 太容易,每一个都”以后可能用得上”,而每个工具的 schema 都是常驻成本。避法是用 /mcp 看当前生效清单,先摘掉那些只是包了一层命令行工具的;README 给的经验值是每个项目控制在 10 个以内。文档还点名说默认配置里的 memory server 没有被任何技能、agent 或钩子使用,可以考虑关掉。这块的取舍逻辑可以对照 MCP 工具数量怎么控。
关 MCP 要用对开关。 会踩是因为在设置文件里加了一行就以为关掉了,结果 schema 照样占窗口。避法是运行时一律用 /mcp,把 ECC_DISABLED_MCPS 当成安装期过滤器看待。
插件方式安装就别再手抄钩子。 会踩是因为 strategic-compact 文档里贴了一段 settings.json 示例,看起来就该复制过去。避法是先确认安装方式:那段示例是给手动安装(./install.sh)准备的,插件安装下 hooks/hooks.json 已经注册了 pre:edit-write:suggest-compact,再抄一遍会让它执行两次,而且插件安装下 ~/.claude/scripts/ 这个路径根本不存在。
大窗口模型先把窗口大小告诉它。 会踩是因为窗口探测依赖模型 id 里的标记,标记缺失时会退回默认档位,于是提醒来得过早、占用被说得过重。避法是显式设置 ECC_CONTEXT_WINDOW_TOKENS。
订阅制用户只关成本警告,别整个关掉监控。 会踩是因为本地估算跟实际账单对不上,一恼火就把整个监控关了,连上下文耗尽和循环检测一起丢掉。避法是只设 ECC_CONTEXT_MONITOR_COST_WARNINGS=off。
压缩之前先落盘。 会踩是因为压缩保留的是 CLAUDE.md、TodoWrite、记忆文件和磁盘文件,丢掉的恰恰是你脑子里那些”刚才查到的”——变量名、文件路径、半成品状态。改到一半压缩,代价最大。避法是按决策表挑边界:探索完、里程碑完、调试完、一条路走死之后压;实现中途、调试进行中、多文件重构期间不压。压之前把结论写进文件,并给 /compact 带一句下一步要做什么。
把上下文效率变成可回归的分数。 会踩是因为清理一次很爽,两周后又长回原样。避法是用 /harness-audit,它的评分里有一个 Context Efficiency 维度,检查项是确定性的文件与规则检查、同一个 commit 结果可复现;把它挂进例行检查,比”记得定期清理”这种自律要求靠谱得多。
收束
这套东西真正给出的东西只有一样:把”什么该占窗口”从审美问题降级成了查证问题。是否被 CLAUDE.md 引用、是否支撑某个在用的命令、工具 schema 有多少个、文件多少行——这些都能查,能查就能排序,能排序才谈得上取舍。至于服务商侧的计费和限制,各家规则不同且会调整,那部分不在这套机制的射程内。
想自己核一遍,按这个顺序读:skills/context-budget/SKILL.md 看判据,skills/strategic-compact/SKILL.md 看压缩边界和”压缩后什么会丢”那张表,docs/token-optimization.md 看配置项和已知坑,最后翻 hooks/hooks.json 和 scripts/hooks/ 确认它到底会在你机器上执行什么。第四步别跳过——前三步是收益,第四步是代价。
本文属于 ECC 开源 Agent 套件专题(共 40 篇,从选择性安装一直拆到内部机制)。同一批还写了另一种取向的项目——不给你资产、只给你纪律的 superpowers 方法论专题。