Claude Code 的 CLAUDE.md 如何消耗上下文,以及如何將智能體記憶控制在 token 預算內
Your CLAUDE.md Is Eating Your Context. Keep Agent Memory Under a Token Budget
文章提出管理 Claude Code 記憶檔的方法,量度每次工作階段載入的 CLAUDE.md 等指令檔所佔 token,並透過拆分與清理控制上下文成本。可用 /context 和 Anthropic 的 token counting endpoint 檢查用量,再把多步驟流程放入技能檔、按路徑載入的規則設為 path-scoped rules,固定執行的檢查改用 hooks。
- Book: Prompt Engineering Pocket Guide
- Also by me: Thinking in Go (2-book series) — Complete Guide to Go Programming + Hexagonal Architecture in Go
- My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools
- Me: xgabriel.com | GitHub
Claude Code's own docs tell you when to add a line to CLAUDE.md: when Claude makes the same mistake a second time, or when you type the same correction you typed last session (memory docs). It's good advice. Nobody tells you when to take a line out.
So the file grows. The pnpm rule. The rule about the legacy billing folder. A release checklist you run once a quarter. A directory tree pasted in before a refactor moved half the folders. All of it loads at the start of every session, before the agent opens a single file of your code. Subagents load their own copy too, counted against their own context (context window docs). The built-in Explore and Plan agents skip it, and a custom subagent can opt out with omitClaudeMd: true (subagent docs).
The same docs set a target: under 200 lines per CLAUDE.md file, because "longer files consume more context and reduce adherence." You pay twice. Once in tokens on every session, and again in rules followed less reliably because they compete with each other.
Measure what loads before your first prompt
Start with what actually loads. In a Claude Code session, run /context and look under Memory files. Claude Code loads CLAUDE.md and CLAUDE.local.md from your working directory and every directory above it, plus your personal ~/.claude/CLAUDE.md, plus any .claude/rules/ file without a paths: field. Auto memory adds the first 200 lines or 25KB of its MEMORY.md index. And @path imports don't help: the docs say they organize a long file "but don't reduce its context cost, because imported files also load at launch."
If a file is over the recommended length, Claude Code warns you at startup and when you run /status.
Line counts are a rough proxy. For tokens, use Anthropic's token counting endpoint. It's free to call (rate-limited by usage tier) and counts with the tokenizer of whichever model you name. CLAUDE.md reaches Claude as a user message after the system prompt, so counting each file as one user message is a fair approximation. This script needs jq and curl:
#!/usr/bin/env bash
# count-context.sh: estimated tokens per instruction file
set -euo pipefail
model="${MODEL:-claude-opus-5-5}"
for f in "$@"; do
n=$(jq -Rs --arg m "$model" \
'{model: $m, messages: [{role: "user", content: .}]}' \
"$f" |
curl -s https://api.anthropic.com/v1/messages/count_tokens \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d @- | jq '.input_tokens')
printf '%7s %s\n' "$n" "$f"
done
Point it at your instruction files. Count path-scoped rules separately: they only cost context when they load.
chmod +x count-context.sh
./count-context.sh ~/.claude/CLAUDE.md CLAUDE.md \
.claude/rules/*.md
If a row prints null, the API returned an error for that file; run the curl by hand to read it. Set MODEL to the model your agent actually runs.
Read the numbers with a few caveats. The docs call every count an estimate, and a count may include a few tokens Anthropic adds for system optimizations, which you aren't billed for. Block-level HTML comments (<!-- like this -->) are stripped from CLAUDE.md before it reaches the model, so a file full of maintainer notes counts higher here than it costs in a session. And recount when you switch models: Claude 4.7 and later use a newer tokenizer that produces roughly 30 percent more tokens for the same text, per the same token counting page.

Every always-loaded rule rides along on every session, and on most subagents.
Split it: one short file, the rest on demand
Every line in CLAUDE.md should pass one test: does the agent need this in every session? The memory docs answer most of the rest. Multi-step procedures belong in a skill. Rules for one part of the codebase belong in a path-scoped rule. Anything that must happen at a fixed point, like a lint before every commit, belongs in a hook, which runs as a shell command instead of sitting in context.
| What the line is | Where it goes | When it costs context |
|---|---|---|
| Build and test commands, non-default conventions | CLAUDE.md |
Every session |
| Rules for one area of the code |
.claude/rules/*.md with paths:
|
When a matching file is read or edited |
| A multi-step procedure | .claude/skills/<name>/SKILL.md |
Description always, body when used |
| Must run every time | A hook | Not in context |
| Directory trees, dependency lists | Delete | Claude can read the code |
A path-scoped rule is a markdown file with a paths list in its frontmatter. It loads when Claude reads, writes or edits a file that matches:
---
paths:
- "src/billing/**/*.ts"
---
# Billing
- Amounts are integer cents. Never use floats.
- Every write goes through LedgerService.
One catch: a path-scoped rule loads into message history, so /compact summarizes it away until a matching file is touched again. A rule that must survive compaction belongs in the project-root CLAUDE.md, which is re-read from disk after /compact.
The quarterly release checklist becomes a skill. Only the description sits in context until the skill is used:
---
description: Cut a release. Use when asked to release or tag.
---
1. Run `pnpm test` and stop on any failure.
2. Bump the version in package.json.
3. Add the merged PRs to CHANGELOG.md.
4. Tag vX.Y.Z and push the tag.
Save it as .claude/skills/release/SKILL.md. The skills docs put it this way: "a skill's body loads only when it's used, so long reference material costs almost nothing until you need it."

The short card is read every time. The shelf is opened only when a task needs it.
Prune with evidence
Splitting moves the cost around. Pruning removes it, and two kinds of evidence are cheap to collect.
The first is how often each on-demand file actually loads. Claude Code fires an InstructionsLoaded hook whenever a CLAUDE.md or rules file loads, with the file path and a load_reason of session_start, nested_traversal, path_glob_match, include or compact. It can't block anything; it exists for logging. Save this as .claude/hooks/log-load.sh:
#!/bin/sh
# One JSON line per lazily loaded instruction file.
jq -c '{at: (now | todate), file: .file_path,
why: .load_reason, by: .trigger_file_path}' \
>> "$CLAUDE_PROJECT_DIR/.claude/instructions.log"
Make it executable, then register it in .claude/settings.json for lazy loads only:
{
"hooks": {
"InstructionsLoaded": [{
"matcher": "path_glob_match|nested_traversal",
"hooks": [{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/log-load.sh"
}]
}]
}
}
If your project path contains spaces, wrap the variable in escaped quotes the way the hooks guide does. Add .claude/instructions.log to .gitignore. After a couple of weeks of normal work:
jq -r .file .claude/instructions.log | sort | uniq -c | sort -rn
The top of that list is where the tokens go. A path-scoped rule that loads in nearly every session has a glob so broad that it costs about as much as an always-loaded line, so tighten the glob or move the rule into CLAUDE.md on purpose. A path-scoped rule that never appears costs nothing, but it either has a glob that matches nothing or covers code nobody touched in that window. Fix the glob, or decide whether that code still needs a rule at all. Rules files without paths: load at session_start, which this hook skips, so judge those by the second kind of evidence.
The second kind of evidence is a reason for every line that stays always-loaded. Since block-level HTML comments are stripped before injection, the reason costs nothing:
- Package manager: pnpm. Never npm or yarn.
<!-- Added 2026-09: agent ran npm install, lockfile churn -->
A line with no reason, or a reason that no longer applies, is a candidate. Move the doubtful ones into a file nothing imports, work normally for a week, and put back any rule whose mistake returns. The rest were dead weight.
Claude Code will also do a first pass. /doctor prompt-audit (v2.1.283 or later) looks for instructions written for older models, references to files or commands that don't exist, and files that contradict each other. It changes nothing until you ask. The plain /doctor checkup proposes trims for a checked-in CLAUDE.md, cutting what Claude can derive from the codebase, such as directory layouts and dependency lists.

Keep the rules with a reason. Park the rest and see what you miss.
Codex and AGENTS.md: a byte cap
Codex reads AGENTS.md starting at the project root and walking down to your working directory, at most one file per directory, and concatenates them with nearer files later. It stops adding files once the combined size reaches project_doc_max_bytes, 32 KiB by default, and the docs list "Instructions truncated" among the problems to troubleshoot. Check where you stand:
find . -name AGENTS.md -not -path '*/node_modules/*' \
-exec wc -c {} +
The token script works on AGENTS.md too, and that matters if you share one file across agents. Claude Code v2.1.277 and later reads AGENTS.md directly when no CLAUDE.md or CLAUDE.local.md sits in your working directory or above it. If one does, Claude reads that instead, unless the CLAUDE.md imports @AGENTS.md or you change the Project instructions setting in /config. One shared file means one budget to keep, read by both agents.
The same split in Hermes IDE
I build Hermes IDE, so weigh this section with that in mind. Hermes runs Claude Code, Codex and other CLI agents side by side, each in its own terminal, and its project memory follows the split above.
The derivable half is generated. Hermes scans each project for its languages, frameworks, architecture and conventions, and points your agent at a context file with what the scan found. That file is kept within a token budget you can set per project (README). The stack and layout stop being lines you maintain by hand in CLAUDE.md, and the generated version comes with a cap.
The facts half is pinned. Memory and context pins save facts and pinned files for a project, and they reach every later session's agent, also after a restart. "The API tests need a local Redis instance" is a pin: it should outlive the session where you found it out.
The README describes the budget for the scanned context file, not for the CLAUDE.md or AGENTS.md you wrote, so measuring, splitting and pruning still apply to those. Hermes can also attach several projects to one session, and their context reaches the agent together, so set the budget on each project you attach.
Hermes is free, source-available under BSL 1.1, and runs on macOS, Windows and Linux. Release notes for each version are on GitHub. It is not affiliated with Anthropic, OpenAI, Nous Research or any agent vendor.
If this was useful
Prompting a coding agent is mostly context management now: what loads, when, and what it costs per session. My Prompt Engineering Pocket Guide covers system prompts and context engineering in short form. If you run more than one agent on a repo, download Hermes IDE, and if the per-project budget saves you tokens, star it on GitHub.
來源:DEV Community · dev.to
