docs: calibrate agent context gates (#296)
Use official Codex, Claude, and Hermes context guidance as advisory working budgets with bounded hard ceilings and actionable remediation. Local cross-platform repository hygiene checks pass; GitHub-hosted steps were not started because of the account billing state.
This commit is contained in:
1 parent
3d960dc5bd
commit
6163cf389f
3 files changed
+131
-21
No files matched your search
+4
-2
@@ -25,7 +25,9 @@
|
||||
|
||||
## 顶级指令卫生
|
||||
|
||||
[官方调研](./research/agent-context-governance.md)的共同结论是:顶层指令应是短而稳定的地图,流程和专业细节按需加载,并定期删除冲突或陈旧规则。因此本仓库的 `AGENTS.md` 保持在 100 行内,而不是把公开上限当作填充目标。模块特有规则只有在对应代码存在且每次读取确有必要时,才放到最近目录的 `AGENTS.md`;流程说明进入 `CONTRIBUTING.md`,当前工作进入 `docs/todo.md`,证据与理由进入其 owner。
|
||||
[官方调研](./research/agent-context-governance.md)的共同结论是:顶层指令应是短而稳定的地图,流程和专业细节按需加载,并定期删除冲突或陈旧规则。检查器把官方边界分成推荐线和硬顶:Codex 项目指令链在默认 32 KiB 上限的 80% 提示、到上限失败;Claude 的有效启动指令在官方建议的 200 行提示,并容许到 250 行的项目缓冲;Hermes 在动态下限 20,000 字符的 80% 提示、到下限失败。`CLAUDE.md` 导入的 `AGENTS.md` 计入有效行数;每次任务必读的 repo STS skill 使用同一保守包络。
|
||||
|
||||
推荐线只触发园艺提醒,不是压缩目标。硬顶失败时应把目录专属规则迁到嵌套或 path-scoped context,把流程和参考资料迁到按需 skill/文档,简化重复措辞,并删除已完成历史或陈旧结论;不要为了过闸损失仍有消费者的约束。模块特有规则只有在对应代码存在且每次读取确有必要时,才放到最近目录的 `AGENTS.md`;流程说明进入 `CONTRIBUTING.md`,当前工作进入 `docs/todo.md`,证据与理由进入其 owner。
|
||||
|
||||
## 真相更新顺序
|
||||
|
||||
@@ -61,6 +63,6 @@ TURN 的减负证据由 [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md)
|
||||
|
||||
- 迭代中运行覆盖当前改动的最小定向检查;完整套件、真实浏览器矩阵、网络整形、跨设备、长时稳定性和部署检查在相关候选的验收边界批量运行。
|
||||
- 文档改动默认运行本地 Markdown 链接、whitespace、diff 和 repository-hygiene 检查;不因无关文档变化重跑媒体基准。
|
||||
- 断链、缺失必需 owner 和格式损坏是 hard failure。每次注入的 `AGENTS.md`、repo STS skill 与只负责导入的 `CLAUDE.md` 也使用 hard context budget,避免规则稀释。Product、ADR、research、operations 等按需文档的行数/字节阈值只是 warning:它提示检查是否应拆分二级 owner、删除历史或移出过程信息,不要求为通过门禁压缩合理正文,也不是交付质量指标。
|
||||
- 断链、缺失必需 owner 和格式损坏是 hard failure。每次注入的有效 `AGENTS.md`/`CLAUDE.md` 上下文与每次必读的 repo STS skill 使用“推荐线 warning、硬顶 failure”,避免把建议值误当死线,也避免超过工具加载边界。Product、ADR、research、operations 等按需文档的行数/字节阈值始终只是 warning:它提示检查是否应拆分二级 owner、删除历史或移出过程信息,不要求为通过门禁压缩合理正文,也不是交付质量指标。
|
||||
- 昂贵证据记录 commit、环境、结果和适用边界。只有相关路径、协议、配置、依赖、环境或门槛改变,或旧证据失败/含糊时才重跑。
|
||||
- `.githooks/pre-commit`、`scripts/check-project-state.sh` 和 PowerShell 等价入口共享 `scripts/required-project-paths.txt`。Hook 只做快速、确定、可复现的检查;工程判断留给评审和测试。
|
||||
@@ -14,24 +14,42 @@ versioned knowledge base. Its reported failure mode for one large instruction
|
||||
file is the same one observed in Screener: scarce context is crowded out, every
|
||||
rule appears equally important, stale guidance accumulates, and ownership is
|
||||
hard to verify. The recommended corrective structure is progressive disclosure,
|
||||
cross-links, and mechanical freshness/structure checks.
|
||||
cross-links, and mechanical freshness/structure checks. Codex concatenates the
|
||||
applicable project instruction chain until `project_doc_max_bytes`, whose
|
||||
default is 32 KiB.
|
||||
|
||||
Anthropic likewise recommends concise, specific project instructions and moves
|
||||
multi-step procedures or path-specific rules to on-demand skills or scoped rule
|
||||
files. A size ceiling is not a target.
|
||||
files. Claude Code's stated target is under 200 lines per `CLAUDE.md`; imported
|
||||
files still enter the startup context, so an import is organization rather than
|
||||
context reduction.
|
||||
|
||||
Hermes distinguishes project conventions (`AGENTS.md`) from user preference,
|
||||
personality, and learned memory. Its persistent memory is deliberately bounded
|
||||
and curated; full session history remains searchable on demand rather than being
|
||||
injected into every prompt. Memory is a frozen session-start snapshot, so it
|
||||
cannot replace current repository truth. Nested context is useful only when a
|
||||
subtree has genuinely distinct rules.
|
||||
cannot replace current repository truth. Its context-file limit is configurable
|
||||
or dynamically derived with a 20,000-character floor, and it recommends staying
|
||||
below that value and using nested context for genuinely distinct subtrees. Its
|
||||
bounded-memory guidance starts consolidation at 80% rather than treating the
|
||||
hard limit as the normal operating target.
|
||||
|
||||
Screener therefore warns at the published recommendation or 80% of a loader
|
||||
limit, then fails only at a real loading boundary or a small explicit buffer:
|
||||
32 KiB for the Codex chain, 20,000 characters for Hermes, and 250 effective
|
||||
Claude lines above the 200-line recommendation. The same envelope applies to the
|
||||
repo STS skill because `AGENTS.md` requires it on every task. A failure tells the
|
||||
maintainer to use nested/path-scoped context, on-demand skills or linked docs,
|
||||
remove duplication and completed history, and delete stale conclusions; raising
|
||||
the ceiling is not the default remedy.
|
||||
|
||||
Sources:
|
||||
|
||||
- [OpenAI Harness engineering](https://openai.com/index/harness-engineering/)
|
||||
- [OpenAI Codex `AGENTS.md`](https://learn.chatgpt.com/docs/agent-configuration/agents-md)
|
||||
- [Claude Code memory](https://code.claude.com/docs/en/memory)
|
||||
- [Hermes file ownership](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/which-file-does-what.md)
|
||||
- [Hermes context files](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/context-files.md)
|
||||
- [Hermes persistent memory](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/memory.md)
|
||||
|
||||
### Documentation Shape
|
||||
|
||||
+105
-15
@@ -19,11 +19,64 @@ const failures = [];
|
||||
const warnings = [];
|
||||
const anchorCache = new Map();
|
||||
|
||||
const exactHardBudgets = new Map([
|
||||
["AGENTS.md", [100, 8_000]],
|
||||
["CLAUDE.md", [10, 1_000]],
|
||||
[".agents/skills/stop-that-shit/SKILL.md", [140, 8_000]],
|
||||
]);
|
||||
const contextBudgetRemediation =
|
||||
"Move directory-specific rules to nested or path-scoped context, move " +
|
||||
"procedures and reference material to on-demand skills or linked docs, " +
|
||||
"simplify repeated wording, and remove completed history or stale conclusions.";
|
||||
const contextWarningRatio = 0.8;
|
||||
const codexProjectDocMaxBytes = 32 * 1_024;
|
||||
const claudeRecommendedLines = 200;
|
||||
const hermesMinimumContextChars = 20_000;
|
||||
|
||||
const agentInstructions = readFileSync(resolve(root, "AGENTS.md"), "utf8");
|
||||
const claudeWrapper = readFileSync(resolve(root, "CLAUDE.md"), "utf8").replace(
|
||||
/^@AGENTS\.md\s*(?:\r?\n)?/u,
|
||||
"",
|
||||
);
|
||||
const stsInstructions = readFileSync(
|
||||
resolve(root, ".agents/skills/stop-that-shit/SKILL.md"),
|
||||
"utf8",
|
||||
);
|
||||
const topLevelContextBudgets = [
|
||||
{
|
||||
name: "Codex project instruction chain",
|
||||
text: agentInstructions,
|
||||
recommended: {
|
||||
bytes: Math.floor(codexProjectDocMaxBytes * contextWarningRatio),
|
||||
},
|
||||
hard: { bytes: codexProjectDocMaxBytes },
|
||||
},
|
||||
{
|
||||
name: "Claude effective project instructions",
|
||||
text: `${agentInstructions}\n${claudeWrapper}`,
|
||||
recommended: { lines: claudeRecommendedLines },
|
||||
hard: {
|
||||
lines: Math.ceil(claudeRecommendedLines / contextWarningRatio),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Hermes root project context",
|
||||
text: agentInstructions,
|
||||
recommended: {
|
||||
chars: Math.floor(hermesMinimumContextChars * contextWarningRatio),
|
||||
},
|
||||
hard: { chars: hermesMinimumContextChars },
|
||||
},
|
||||
{
|
||||
name: "Required stop-that-shit skill",
|
||||
text: stsInstructions,
|
||||
recommended: {
|
||||
lines: claudeRecommendedLines,
|
||||
chars: Math.floor(hermesMinimumContextChars * contextWarningRatio),
|
||||
bytes: Math.floor(codexProjectDocMaxBytes * contextWarningRatio),
|
||||
},
|
||||
hard: {
|
||||
lines: Math.ceil(claudeRecommendedLines / contextWarningRatio),
|
||||
chars: hermesMinimumContextChars,
|
||||
bytes: codexProjectDocMaxBytes,
|
||||
},
|
||||
},
|
||||
];
|
||||
const exactWarningBudgets = new Map([
|
||||
["docs/README.md", [180, 16_000]],
|
||||
["docs/deployment.md", [250, 20_000]],
|
||||
@@ -54,6 +107,8 @@ for (const file of markdownFiles) {
|
||||
for (const raw of targets) checkLink(file, absolute, raw);
|
||||
}
|
||||
|
||||
checkTopLevelContextBudgets();
|
||||
|
||||
if (warnings.length > 0) {
|
||||
process.stderr.write(`${warnings.map((warning) => `warning: ${warning}`).join("\n")}\n`);
|
||||
}
|
||||
@@ -74,7 +129,6 @@ function checkFormat(file, text) {
|
||||
}
|
||||
|
||||
function checkBudget(file, text) {
|
||||
const hardBudget = exactHardBudgets.get(file);
|
||||
const warningBudget =
|
||||
exactWarningBudgets.get(file) ??
|
||||
prefixWarningBudgets
|
||||
@@ -82,15 +136,6 @@ function checkBudget(file, text) {
|
||||
.map(([, maxLines, maxBytes]) => [maxLines, maxBytes])[0];
|
||||
const lines = logicalLines(text);
|
||||
const bytes = Buffer.byteLength(text);
|
||||
if (hardBudget) {
|
||||
const [maxLines, maxBytes] = hardBudget;
|
||||
if (lines > maxLines || bytes > maxBytes) {
|
||||
failures.push(
|
||||
`${file}: exceeds required ${maxLines}-line/${maxBytes}-byte ` +
|
||||
`top-level context budget (${lines} lines, ${bytes} bytes)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (warningBudget) {
|
||||
const [maxLines, maxBytes] = warningBudget;
|
||||
if (lines <= maxLines && bytes <= maxBytes) return;
|
||||
@@ -101,6 +146,51 @@ function checkBudget(file, text) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkTopLevelContextBudgets() {
|
||||
for (const budget of topLevelContextBudgets) {
|
||||
const measures = {
|
||||
lines: logicalLines(budget.text),
|
||||
chars: [...budget.text].length,
|
||||
bytes: Buffer.byteLength(budget.text),
|
||||
};
|
||||
if (exceeds(measures, budget.hard)) {
|
||||
failures.push(
|
||||
`${budget.name}: exceeds hard context ceiling ${formatLimits(budget.hard)} ` +
|
||||
`(${formatUsage(measures, budget.hard)}). ${contextBudgetRemediation}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (exceeds(measures, budget.recommended)) {
|
||||
warnings.push(
|
||||
`${budget.name}: exceeds recommended working budget ` +
|
||||
`${formatLimits(budget.recommended)} ` +
|
||||
`(${formatUsage(measures, budget.recommended)}). ` +
|
||||
`${contextBudgetRemediation}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function exceeds(measures, limits) {
|
||||
return Object.entries(limits).some(([metric, limit]) => measures[metric] > limit);
|
||||
}
|
||||
|
||||
function formatLimits(limits) {
|
||||
return Object.entries(limits)
|
||||
.map(([metric, limit]) => `${limit} ${metricLabel(metric)}`)
|
||||
.join(" / ");
|
||||
}
|
||||
|
||||
function formatUsage(measures, limits) {
|
||||
return Object.keys(limits)
|
||||
.map((metric) => `${measures[metric]} ${metricLabel(metric)}`)
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
function metricLabel(metric) {
|
||||
return metric === "bytes" ? "UTF-8 bytes" : metric;
|
||||
}
|
||||
|
||||
function checkLink(file, sourcePath, raw) {
|
||||
const target = linkTarget(raw);
|
||||
if (!target || isExternal(target)) return;
|
||||
|
||||
Reference in new issue
Block a user