首页 / 文章 / 如何管理代码库中的上下文文件,以从AI编码代理获得更好的输出
← 返回
IT技术

如何管理代码库中的上下文文件,以从AI编码代理获得更好的输出

✍️ zhirenhun 📅 2026/8/18 👁 99 阅读 ⏱ 56 分钟
如何管理代码库中的上下文文件,以从AI编码代理获得更好的输出

你让编码代理新建一个端点,九十秒后你就有了一个能工作的端点。

然后你查看 diff,发现它引入了一个不在你的 package.json 中的验证库,它用 Jest 编写测试,尽管你的团队去年春天已经迁移到了 Node 测试运行器,而且它从路由处理器内部访问了数据库,因为它无法知道代码库中的每个其他处理器都委托给一个服务。

代码能运行,它写的测试也能通过,但你仍需要重写大部分代码。

这些都不是模型推理能力的失败。它依据自己的理解提出了一个合理的解决方案,但它对问题的理解很糟糕,因为没有人告诉它这个特定代码库是如何运作的。

你的约定存在于团队成员的脑海中、代码评审的评论里,以及十八个月前做出的无人记录下来的决定中。代理无法看到这些,所以它只能退回到它所训练过的所有代码库的平均水平,而这正是你得到的结果。

解决方法不是更长的提示词,因为你每次会话都必须重新输入,而且你的队友每个人都会写出不同的版本。解决方法是存放在代码库中的一组文件,它们自动加载,并且以与你维护代码相同的方式来维护。

本教程向你展示如何组织这些文件,如何在不同的工具所期望的四五种格式之间保持单一事实源,以及最重要的一点——如何防止它们悄然过时。毕竟,一个描述你六个月前已删除代码库的上下文文件,比根本没有上下文文件更糟糕。

这里的所有内容都基于一个你可以克隆并运行的配套仓库:github.com/Adeniyikayodee/MCF。它没有依赖项,所以你只需要 Node 20 或更高版本。

目录

你开始前需要什么

你应该熟悉 Git 和终端,应该安装了 Node 20 或更高版本,并且应该在实际项目中至少使用过一种编码代理,如 Claude Code、Cursor、GitHub Copilot 或 Codex。

你不需要了解模型内部工作原理的任何内容,因为本教程的所有内容都关于磁盘上的文件。

为什么上下文窗口是真正的约束

代理在完成你的任务时所知道的一切都存储在一个称为上下文窗口的缓冲区中。该缓冲区包含系统提示、你的对话、代理打开的每个文件、运行的每条命令,以及这些命令打印的每个堆栈跟踪。

但重要的是要知道它是有限的,而且它的填充速度比大多数人预期的要快。例如,一次调试会话就可能消耗数万个 token,而代理还没有写一行代码。

对本教程而言,关键是当缓冲区填满时会发生什么。Anthropic 的工程团队描述了他们称之为 上下文腐烂 的效应,即模型检索特定指令的能力会随着 token 数量的增加而下降。模型不是出于固执而忽略你,它是在一个注意力预算下工作,而这个预算随着更多材料争夺注意力而变得越来越少。

这一事实颠覆了大多数人对上下文文件的直觉。写得更多感觉更安全,因为你覆盖了更多情况,留给偶然性的余地更少。但你添加的每一行都会与其他每一行竞争有限的注意力。

Claude Code 的文档明确指出了后果:臃肿的指令文件会导致代理忽略其中的规则。此外,它还指出,文件过长的症状是代理反复违反你明确写下的规则。

以下大致是会话预算在实际任务中的分配方式:

system prompt and tool definitions        ~12,000 tokens
context files loaded at startup            ~4,800 tokens
three source files the agent opened        ~9,000 tokens
one test run with a stack trace            ~3,500 tokens

该列表中的4,800 token上下文文件正在与代理为修复bug而需要读取的堆栈跟踪抢占空间。一个仅600 token、指明了正确路径的文件,将为代理留出空间去自行阅读代码,而这正是它所擅长的。

上下文文件首先是一个预算分配问题,然后才是一个文档问题,本教程中的几乎所有改进都源于认真对待这一点。

三层结构

有效的结构将上下文视为成本各不相同的三个不同层次。

始终加载层是位于仓库根目录下的单个文件,代理会在每次会话开始时读取它,无论任务是修复拼写错误还是迁移。你每次请求都需要为这个文件付费,因此它只包含适用于仓库中每个任务的内容。同时它必须保持足够小,小到你能在一分钟内朗读完。

范围加载层由嵌套文件组成,仅在代理于特定目录内工作时才加载。关于API层的规则存放在src/AGENTS.md中,因此只触及前端的任务永远不会为它们付费。

按需加载层是普通文档,根文件通过路径指向它们而不是内联。一个路径只需花费少量token,而它背后的文档可能花费两千token,因此代理只在任务确实需要时才花费这笔预算。

这正反映了新工程师的工作方式,他们不会在第一天就记住你的架构文档,而只是记得它存在,并在需要时去阅读。

配套仓库中的最终布局如下:

MCF/
├── AGENTS.md                          always loaded, budgeted
├── CLAUDE.md                          generated from AGENTS.md
├── .github/copilot-instructions.md    generated from AGENTS.md
├── .cursor/rules/testing.mdc          glob scoped, hand written
├── .claude/
│   ├── settings.json                  hook that runs the context linter
│   └── skills/add-endpoint/SKILL.md   workflow, loaded on demand
├── docs/
│   ├── architecture.md
│   ├── testing.md
│   └── decisions/0001-in-memory-store.md
├── scripts/
│   ├── context-lint.mjs
│   └── sync-context.mjs
├── src/
│   ├── AGENTS.md                      scoped to the source tree
│   ├── api/tasks.js
│   ├── services/tasks.js
│   ├── lib/validate.js
│   ├── router.js
│   └── server.js
└── tests/

选择一种无需维护四份副本的格式

每个供应商都为同一个想法选择了不同的文件名,这很烦人,但一旦你决定哪一个才是事实来源,就还可以管理。

AGENTS.md 是最接近共享约定的东西。它是纯 Markdown,没有必需的 schema,其治理归属于 Linux 基金会下属的 Agentic AI Foundation,并且它能被 Claude Code、Codex、Cursor、Copilot、Gemini CLI、Aider、Windsurf、Zed 以及一长串其他工具原生读取。

嵌套文件是规范的一部分,离正在编辑的代码最近的文件优先,而你直接在聊天中键入的任何内容都会覆盖所有规范。

工具特有的格式仍然与它并存。Claude Code 读取 CLAUDE.md,沿目录树向上遍历并拼接它找到的每一个文件,同时解析 @path/to/file 导入。Cursor 在 .cursor/rules/ 内使用带 YAML frontmatter 的 .mdc 文件,可以将规则限定到诸如 tests/**/*.js 的 glob 上,这使得它成为这些格式中表达能力最强、同时也是可移植性最差的,因为 Cursor 之外没有任何东西会读取它。GitHub Copilot 则读取仓库根目录下的单个 .github/copilot-instructions.md

实用的做法是只写一次 AGENTS.md,从它生成其余文件,并且仅当某个工具提供了共享格式无法表达的内容时,才手动编写一个单独的文件。实际上,这意味着 Cursor 的 glob 作用域。你可以使用符号链接来完成生成:

ln -s AGENTS.md CLAUDE.md

符号链接是最快捷的方式,尽管它们会给Windows上的贡献者和某些CI检出配置带来麻烦,因此伴随仓库改用一个小脚本。该脚本会为其生成的每个文件写入一个横幅,以阻止好心的队友编辑副本,从而避免在下次同步时丢失他们的工作。

// scripts/sync-context.mjs
const banner = ``;

export const targets = [
  // Claude Code resolves @path imports, so its file stays a pointer plus what is specific to it.
  { path: 'CLAUDE.md', render: () => `${banner}\n\n@${SOURCE}\n\n${CLAUDE_EXTRAS}` },
  // Copilot has no import syntax, so the source is inlined.
  { path: '.github/copilot-instructions.md', render: (source) => `${banner}\n\n${source}` },
];

因为Claude Code会解析导入,其生成的文件只是一个指针,加上少数仅对该工具有意义的指令,因此它保持在约130个token左右,而不是复制全部内容:



@AGENTS.md

## Claude Code specific

- Use plan mode for any change that touches more than three files, and skip it for a one line fix.
- Delegate codebase exploration to a subagent so the findings come back summarised rather than as
  a hundred file reads in the main context.

运行脚本会重新生成这两个文件,再次运行则不会有任何变化。这正是你在钩子(hook)或CI任务中反复调用时所期望的表现:

图1:终端显示npm run sync:context写入CLAUD.md和Copilot指令文件,随后git status将两者列为已修改

编写根文件

这里蕴藏着绝大部分价值,同时也是大多数人容易出错的地方,因为人们的本能是把所有内容都写下来。

对每一条你忍不住想要添加的内容使用一个编辑测试:删除这一行是否会导致智能体犯错?如果答案是否定的,那么这一行只是在消耗你的注意力预算,却没有任何收益,所以删掉它。只要如实应用这个测试,就能剔除人们在这些文件中写下的大部分内容。

下面就是该测试旨在捕捉的那类文件:

# AGENTS.md

## About this project
This project is a REST API for managing tasks. It was originally built in 2023 by the platform
team and has since been maintained by the core services group. The codebase is written in modern
JavaScript using ES modules.

## Code style
- Use meaningful variable names
- Write clean, maintainable code
- Follow the DRY principle
- Use const instead of var
- Add comments where the code is complex

## Structure
- `src/server.js` contains the server
- `src/router.js` contains the router
- `src/api/tasks.js` contains the task handlers
- `src/services/tasks.js` contains the task service

那里的每一行代码都无法通过测试。模型已经知道const的用途,它能看到名为router.js的文件包含路由器,而且了解2023年代码归属于哪个团队也不会改变它所做的任何一个决定。

与此同时,智能体真正无法自行推断出的一件事——即这个项目刻意不依赖任何外部库——却不在文件中。

以下是随附仓库中发布的版本:

# AGENTS.md

Task API used as the worked example for a tutorial on managing context files. This file is the
single source of truth for agent instructions, and `CLAUDE.md` plus
`.github/copilot-instructions.md` are generated from it by `npm run sync:context`, so edit this
file and never the generated ones.

## Commands

- Install: nothing to install, the project has zero dependencies
- Run the tests: `npm test`
- Start the server on port 3000: `npm start`
- Check the context files: `npm run lint:context`
- Regenerate the tool specific context files: `npm run sync:context`

## Conventions that are not obvious from the code

- The test runner is the Node built in runner invoked through `node --test`, so do not add Jest,
  Vitest, or any other test dependency to this repository.
- This project stays dependency-free on purpose, so solve problems with the Node standard library
  rather than by adding a package.
- Handlers in `src/api/` return `{ data }` or `{ error: { code, message } }` and never choose an
  HTTP status, because `src/router.js` owns the mapping from error code to status.
- Handlers never touch the store directly, so any logic that reads or writes tasks belongs in
  `src/services/tasks.js`.
- The store is module level state that survives between test cases, so any test file that creates
  a task has to call `resetTasks()` in a `beforeEach` hook.

## Definition of done

Run `npm test` and `npm run lint:context` before you report a task as finished, and paste the
output rather than asserting that it passed.

## Where to look

- Architecture and request flow: `docs/architecture.md`
- Testing conventions and how to add a case: `docs/testing.md`
- Why the store is in memory: `docs/decisions/0001-in-memory-store.md`
- Rules that apply only to the API layer: `src/AGENTS.md`

请注意每个部分的作用。命令之所以存在,是因为智能体无法可靠地猜测你的脚本名称,而猜错会导致运行失败。这些约定要么是从代码中无法看到的,要么是模型原本会假设的内容所直接矛盾的,而且每条约定都说明了原因,因为附有原因的规则在规则作者未预料到的情况下依然能发挥作用。最后一部分仅包含路径,这是按需层在履行其职责。

关于哪些内容值得写入的粗略指导:

应该包含 应该省略
智能体无法猜测的命令 从阅读代码即可看到的任何内容
与语言默认约定不同的惯例 模型已经熟悉的标准约定
测试运行器以及如何运行单个测试 详细的API文档,应提供链接
分支命名和拉取请求礼仪 每个迭代都会变化的信息
针对你项目的架构决策 冗长的解释和教程
环境怪癖和必需的变量 对目录树逐文件的描述
不明显的陷阱 诸如“编写整洁代码”之类的建议

找到合适的抽象层级

编写糟糕规则还有第二种方式,那就是将其定位在错误的特定层级。Anthropic的指南将此表述为找到合适的抽象层级,介于硬编码逻辑和模糊鼓励之间——前者会在遇到第一个未预料到的案例时就崩掉,后者则给模型提供不了任何可执行的依据。

Too rigid, and it breaks on the first handler that does not fit:
- Every route handler must be exactly 40 lines and call validate() on line 3.

Too vague, and it changes nothing about what the agent does:
- Write clean, maintainable code.

Right altitude:
- Route handlers parse and validate input, then delegate to a function in `src/services/`.
  Handlers do not touch the store directly. See `src/api/tasks.js` for the pattern to copy.

第三版向智能体说明了规则的形态、不得逾越的边界以及何处能找到工作示例,这大致类似于你在新员工入职第一天向一位称职的新人交代的内容。

将规则限定到目录

仅与树结构某一部分相关的内容应置于嵌套文件中,而判断规则是否适用的标准很简单:位于不同目录下的开发人员是否需要了解这一点?若不需要,则将其下移。


# Source layer

Rules below apply to everything under `src/`, and they sit on top of the root `AGENTS.md` rather
than replacing it.

## Adding an endpoint

1. Add the handler to `src/api/tasks.js` following the shape the neighbouring handlers use.
2. Add one entry to the `routes` array in `src/router.js` with its success status.
3. Add a case to `tests/api.test.js` that covers the success path and the failure path.

## Validation

Validators live in `src/lib/validate.js`, they return an array of problem strings rather than
throwing, and they report every failing field instead of stopping at the first one, so a caller can
show the user all of their mistakes at once.

这个验证规则是一个值得记录的好例子,因为仅代码本身无法解释其含义。阅读 src/lib/validate.js 的智能体会看到一个返回数组的函数,但无法判断这是刻意的约定还是某个实现中的偶然产物,因此它有理由在接下来编写的验证器中抛出异常:

// src/lib/validate.js
export function validateTaskInput(input) {
  if (typeof input !== 'object' || input === null || Array.isArray(input)) {
    return ['body must be a JSON object'];
  }

  const problems = [];

  if (typeof input.title !== 'string' || input.title.trim() === '') {
    problems.push('title is required and must be a non-empty string');
  } else if (input.title.length > TITLE_MAX) {
    problems.push(`title must be ${TITLE_MAX} characters or fewer`);
  }

  if (input.done !== undefined && typeof input.done !== 'boolean') {
    problems.push('done must be a boolean when present');
  }

  return problems;
}

指向而非内联

根文件中的Where to look部分是整个设置中最廉价的部分。四行路径的加载成本几乎为零,而在它们背后,是数千个token的架构说明、测试约定和决策记录,代理仅在任务需要时才引入这些内容。

架构决策记录是那些推理的自然归属之处,否则这些推理会让你的根文件变得臃肿。配套仓库中有一个记录解释了为什么任务存储是一个普通的Map而不是数据库,而其中最有用的一段是最后一段:

An agent working here should not add a database, an ORM, or a persistence layer unless the task
explicitly asks for one, and should treat the missing persistence as a deliberate choice rather than
a gap to fill.

没有这一点,要求“让API达到生产就绪状态”的智能体会主动添加Postgres。有了这一点,智能体会知道缺失是有意为之,并在修改前询问。那句话不会让你付出任何代价,直到有一天它帮你省下一个下午。

同样的逻辑也适用于偶尔才出现的工作流程。为添加端点提供逐步操作步骤确实很有用,但如果放在每次任务都会加载的文件里,就会成为累赘,因此它放在技能文件中,只有在有人实际请求端点时才会加载:

---
name: add-endpoint
description: Add a new endpoint to the task API following the layering this repository uses
---

# Add an endpoint

This workflow loads only when someone asks for a new endpoint, which is why it lives here instead
of in `AGENTS.md` where every session would pay for it.

Read `docs/architecture.md` first if you have not already, then work through these steps in order.

1. Decide which layer owns the new behaviour. Anything that reads or writes tasks belongs in
   `src/services/tasks.js`, and anything about request shape belongs in `src/api/tasks.js`.
2. Add or extend a validator in `src/lib/validate.js` if the endpoint accepts input, returning an
   array of problem strings so the handler can report every failure at once.
3. Add the handler to `src/api/tasks.js`, returning `{ data }` on success and
   `{ error: { code, message } }` on failure, and using an existing error code where one fits.
4. Register the route in the `routes` array in `src/router.js` with the success status it should
   return, and add the error code to `STATUS_BY_ERROR_CODE` if you introduced a new one.
5. Add at least one success case and one failure case to `tests/api.test.js`.
6. Run `npm test` and `npm run lint:context`, then paste both outputs into your summary.

Do not add a dependency, do not introduce a persistence layer, and do not set a status code inside
a handler.

让上下文文件可验证

到目前为止,一切内容都是相当标准的建议,而且单独来看,它的保质期很短。上下文文件腐烂的原因与文档腐烂完全相同,即当它们出错时,没有任何东西会被破坏。你将src/services/task.js重命名为src/services/tasks.js,而你的上下文文件仍然自信地指向一个不再存在的路径。你删除了typecheck脚本,六个月后一个代理浪费两轮尝试运行它。没有人注意到这些,因为你的管道中没有检查。

因此,在管道中加入检查并让它失败。配套仓库在scripts/context-lint.mjs中有一个linter,它运行四项检查,大约150行无依赖的JavaScript,你可以用一个下午将它适配到你自己的仓库中。

第一项检查是对启动时加载的每个文件进行令牌预算:

// Loaded at the start of every session whether the task needs them or not. When one of these keeps
// pushing against its ceiling, move the detail into docs/ and leave a path behind.
const ALWAYS_LOADED = [
  { path: 'AGENTS.md', budget: 800 },
  { path: 'CLAUDE.md', budget: 300 },
  { path: '.github/copilot-instructions.md', budget: 900 },
  { path: 'src/AGENTS.md', budget: 400 },
];

// Rough average for English prose. Precision is not the point, catching a file that doubled is.
const CHARS_PER_TOKEN = 4;

const estimateTokens = (text) => Math.ceil(text.length / CHARS_PER_TOKEN);

每个 token 四个字符是一个近似值,而不是真正的分词器计数,并且在代码密集的文件上运行时会略显乐观。这没问题,因为你关心的数字是上限。一个文件从 400 个 token 慢慢增加到 800 个就是信号,而绝对数字上偏差 8% 并不会改变你对它的应对方式。

第二项和第三项检查将你的上下文文件作为散文阅读,并验证它们提到的事物是否真实。任何在单个反引号中看起来像路径的内容都必须存在于磁盘上,任何 npm 脚本都必须存在于 package.json 中:

// Fenced blocks are stripped first so an example inside a snippet is never read as a real reference.
function inlineCodeSpans(text) {
  const prose = text.replace(/```[\s\S]*?```/g, '');
  return [...prose.matchAll(/`([^`\n]+)`/g)].map((match) => match[1].trim());
}

for (const span of spans) {
  if (looksLikePath(span)) {
    if (!existsSync(join(ROOT, span))) {
      problems.push(`${file} points at a path that does not exist: ${span}`);
    }
    continue;
  }

  const script = span.match(/^npm run ([\w:-]+)$/) ?? span.match(/^npm (test|start)$/);

  if (script && !scripts.includes(script[1])) {
    problems.push(`${file} mentions an npm script that is not in package.json: ${span}`);
  }
}

在扫描前剥离围栏代码块的重要性远超表面所见,因为你的文档中充满了仅供示例说明的代码,它们从未打算作为真实引用;如果 linter 在这些代码上失败,它会在一个星期内被关闭。

第四项检查以试运行模式重新执行同步脚本,如果任何生成的文件不再与 AGENTS.md 匹配,则检查失败;这能抓住那些无视横幅提示、直接编辑 CLAUDE.md 的队友。

在一个健康的仓库中,整个过程远不到一秒:

图2:npm run lint:context 的终端输出,显示四个上下文文件均未超出 token 预算,跨 9 个文件检查了 47 处引用,生成的文件同步一致,未发现任何问题。

而当某些东西腐化时,有趣的输出就出现了。向 AGENTS.md 中添加一行看起来合理的内容——提到一个已被删除的脚本和一个已被重命名的文件——会产生以下结果:

图3:npm run lint:context 的终端输出,报告了三个 agent 问题:一个不在 package.json 中的 npm 脚本、一个不存在的路径,以及一个与 AGENTS.md 不同步的生成文件。

该脚本以非零状态退出,因此将其接入 CI 只需四行配置,这意味着这些文件不会悄悄偏离:

# .github/workflows/ci.yml
      - name: Run the test suite
        run: npm test

      # The context files are checked on every pull request, which is what stops them from
      # drifting away from the code they describe.
      - name: Check the context files
        run: npm run lint:context
图4:MCF仓库的GitHub Actions运行结果,显示verify任务成功,测试套件和上下文检查器均为绿色。

如果我要放弃本教程中的其他所有内容,这部分是我一定会保留的。一个平庸但可验证为真实的内容文件,胜过一份文笔优美却描述去年架构的文件,因为代理无法区分两者,并会以同样的自信执行。

给代理一个可验证的标准

根文件中还有一行值得细究,那就是“完成”的定义。

当工作看起来完成时,代理就会停下来,而如果没有一个它能自行运行的检查,“看起来完成”就成了它唯一能获得的信号,这让你悄悄变成了验证回路。每个错误都要等你来发现。

命名一个返回成功或失败的命令,就能把它变成代理可以自行处理的事情,于是它编写代码、运行检查、读取结果,并一直迭代直到检查通过。

这就是为什么在将任务报告为完成之前,运行 npm test 和 npm run lint:context比任何你能写出的风格指导都更有助于提升输出质量。要求代理粘贴输出而不是只声称成功也很重要,因为审阅证据只需几秒,而自己重新运行验证则要几分钟。

不过,上下文文件中的说明只是建议,随着上下文不断填满,建议会逐渐丢失。当某件事必须在每次执行中无一例外地发生时,请使用钩子,它会在代理循环的固定点运行脚本,且无法被说服跳过:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint:context --silent"
          }
        ]
      }
    ]
  }
}

经验法则是,任何建议性的内容都应放在正文叙述中,而任何强制性的内容都应放在钩子(hook)或持续集成(CI)中。

检查它是否真正有效

你不应盲目相信这一切,有一种廉价的方法可以在你自己的仓库上测试。

选择一个形状明确正确的任务,写下提示词,以便在多次运行中保持一致,然后运行两次:一次在当前分支上,另一次在删除了上下文文件的分支上。在配套仓库中,一个很好的候选任务是“添加一个GET /tasks/count端点,返回打开任务的数量,并带有测试”。

然后在四个要点上比较两次运行:测试是否在你没有干预的情况下通过?你需要做出多少修正?代码是否遵循现有的分层结构,还是从处理器直接伸入到了存储层?是否出现了任何新的依赖?

这只是一个样例而非基准测试,你应该这样看待它。但它足以让你知道文件是否发挥了应有作用,并且当出现问题时,它能非常清楚地显示缺少了哪条具体规则。

图5:npm test的终端输出,显示路由和验证器共十四个测试通过

保持文件健康

对待这些文件要像对待代码一样,这意味着在出现问题时回顾它们,而不是按计划定期审查。

两种诊断方法可以覆盖你遇到的大多数情况。如果代理不断违反某条已写下的规则,那么文件几乎肯定太长,规则在噪音中丢失了。应积极删减,而不是增加强调。

如果代理问了你一个文件已经回答过的问题,说明措辞有歧义,所以重写那一行,而不是在旁边再添加一行。

此外,删除任何代理无需告知就会遵循的规则,因为模型的默认行为会随每次发布而改进,去年必要的规则现在可能已是累赘。

将 linter 输出中的 token 预算作为一种粗略的健康指标,因为一个不断逼近其上限的文件就是在告诉你,细节需要移入docs/

值得避免的错误

最常见的失败是“大杂烩”文件,任何人提到过的每一条约定都被添加进去,直到文件达到三千 token,而代理只大致遵循其中一半。解决办法是不带感情地应用删除测试。

第二个错误是将 README 复制到上下文文件中,这使每次会话的成本翻倍而没有任何增益,因为这两个文档有不同的受众,代理在需要时可以阅读 README。

第三个是记录模型自己就能看到的东西。判断标志是任何描述文件包含什么,而不是你期望代理做什么的行。

第四个是编写无法验证的规则,例如要求代码可读或性能良好,这些规则听起来合理,但让代理无法判断自己是否已遵守。

第五个,也是最终会困扰团队的错误,是让每个工具保留自己手工维护的副本。它们开始时相同,一个月内就会出现分歧,然后 Cursor 和 Claude Code 就在同一个仓库中根据相互矛盾的指令工作。应生成副本,并在 CI 中检查生成结果。

从哪里开始

如果读完本文后你只做一件事,那就是对已有的上下文文件进行 token 估算,然后逐行阅读,问自己删除每一行是否会导致错误。大多数人在第一遍时就会删掉三分之一到一半的内容,并发现代理更可靠地遵循剩余部分。

之后,添加指引,使你的文档可被触达而无需付出高昂代价,并将 linter 放入 CI,以便在底层代码库不断变化时整个体系保持真实。

完整设置,包括 linter、同步脚本、钩子和 CI 工作流,位于 github.com/Adeniyikayodee/MCF。克隆它,运行 npm run lint:context 看它通过,然后在 AGENTS.md 中破坏一些内容,再看它失败。

你可以根据自己的约定调整 linter,而不是逐字复制,因为值得运行的检查应与你的特定仓库容易偏离的方式相匹配。

如果你想拥有自己的副本进行实验,可以 fork 这个仓库,因为 fork 为你提供了一个分支点,你可以自由修改,同时不会丢失以后拉取更改的能力。如果你希望在那些更改落地时收到通知,请使用 Fork 旁边的 Watch 按钮,并选择 releases 或所有活动,因为那才是真正向你发送通知的控件,而 fork 只是捕获你 fork 当天的代码状态。

延伸阅读

——

🧑‍💻

zhirenhun

一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

← 上一篇
Claude的系统提示词从358词增至3235词:对生产AI团队的启示
下一篇 →
如何用vLLM扩展AI智能体的LLM推理

📌 相关推荐

GraphRAG 是推理问题,而非数据库问题
2026/8/30
构建市场时光机:使用 Python 和 WebSocket 重放交易会话
2026/8/30
如何自行基准测试LLM推理:值得信赖的数字设计标准
2026/8/30
← 返回文章列表