第 6 章 · 记忆与上下文

03 · 项目规则加载

从 AGENTS.md 加载项目级规则,让 agent 自动感知项目的代码规范和目录约定。

为什么需要项目规则

不同的项目有不同的约定:

  • 有的项目用 TypeScript strict 模式,有的不用
  • 有的项目测试文件放在 test/,有的放在 __tests__/
  • 有的项目要求 JSDoc 注释,有的不需要
  • 有的项目用 pnpm,有的用 npm

如果 agent 不知道这些约定,它就会按"通用方式"操作——可能用 npm 安装依赖(但项目用 pnpm),可能不写注释(但项目要求 JSDoc)。

Claude Code 的做法是读取项目根目录下的 CLAUDE.md 文件——项目维护者可以在里面写任何规则,agent 会在执行任务时参考这些规则。

我们用同样的思路,支持读取 AGENTS.md 文件。

ProjectRulesLoader 实现

src/memory/rules.ts 中:

import { readFile } from "node:fs/promises";
import { join } from "node:path";

export class ProjectRulesLoader {
  private static readonly RULE_FILES = ["AGENTS.md", ".agents.md"];

  async load(workingDir: string): Promise<string | null> {
    for (const filename of ProjectRulesLoader.RULE_FILES) {
      try {
        const filePath = join(workingDir, filename);
        const content = await readFile(filePath, "utf-8");
        if (content.trim().length > 0) {
          return content.trim();
        }
      } catch {
        continue;
      }
    }
    return null;
  }
}

设计要点:

两个候选文件名。 AGENTS.md 是主文件,.agents.md 是隐藏文件形式的替代。有些项目维护者不想让规则文件太显眼,就可以用点文件。只加载找到的第一个,不做合并。

失败时静默返回 null。 文件不存在、权限不足、编码错误——任何情况都不应该让 agent 崩溃。没有规则文件,agent 就用默认行为。

空白文件等于没有文件。 content.trim().length > 0 确保空白文件被跳过。

规则加载的时机

规则在 agent 启动时加载一次,在整个会话期间保持不变。这是在 main.ts 中做的:

const rulesLoader = new ProjectRulesLoader();
const projectRules = await rulesLoader.load(workingDir);
if (projectRules) {
  console.log("已加载项目规则: AGENTS.md\n");
}

为什么不在每次 runAgent 调用时重新加载?两个原因:

  1. 性能。 读文件是 I/O 操作,没有必要每次都读同一个文件。
  2. 一致性。 如果规则在会话中途变了(比如用户在另一个终端编辑了 AGENTS.md),每次重新加载可能导致 agent 行为不一致。

注入系统提示词

项目规则通过 RunAgentOptions.projectRules 传入 agent 循环,然后在构建系统提示词时注入:

const systemParts: string[] = [
  "你是一个代码仓库助手...",
  // ... 工具列表、工作方式、行为准则
];

if (options?.projectRules) {
  systemParts.push(
    "",
    "## 项目规则",
    "",
    "以下是从项目 AGENTS.md 文件加载的规则,请遵守:",
    options.projectRules,
  );
}

if (contextText) {
  systemParts.push("", contextText);
}

注入顺序很重要:项目规则在行为准则之后、工作目录之前。 这样系统提示词的结构是:

  1. 角色定位
  2. 工具列表
  3. 工作方式
  4. 行为准则
  5. 项目规则(如果有)
  6. 会话上下文(如果有)
  7. 工作目录

项目规则和会话上下文是可选的补充信息,放在核心指令之后。模型会先看到"我是谁、能做什么、该怎么做",然后才看到"这个项目有什么特殊要求"。

举个例子

假设项目根目录有一个 AGENTS.md

# 项目规则

- 使用 pnpm,不要用 npm 或 yarn
- 所有代码使用 ESM 模块格式(import/export)
- 测试文件放在 test/ 目录,使用 vitest 框架
- 注释使用 JSDoc 风格

加载后,agent 的系统提示词会多出这一段:

## 项目规则

以下是从项目 AGENTS.md 文件加载的规则,请遵守:

# 项目规则

- 使用 pnpm,不要用 npm 或 yarn
- 所有代码使用 ESM 模块格式(import/export)
- 测试文件放在 test/ 目录,使用 vitest 框架
- 注释使用 JSDoc 风格

模型看到这段规则后,在执行任务时会遵守这些约定——比如执行命令时用 pnpm 而不是 npm,创建测试文件时放在 test/ 目录。

测试覆盖

ProjectRulesLoader 的测试使用临时目录来模拟文件系统:

it("加载 AGENTS.md 文件", async () => {
  await writeFile(join(testDir, "AGENTS.md"), "# 规则\n使用 TypeScript");
  const rules = await loader.load(testDir);
  expect(rules).toBe("# 规则\n使用 TypeScript");
});

测试覆盖了:正常加载、.agents.md 替代文件、优先级、无文件时返回 null、空白文件跳过等场景。完整的测试在 test/memory/rules.test.ts 中。

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page