减少飘移实践:CLAUDE.md、AGENTS.md与dir-graph.yaml结合
Harness Engineering实践:如何通过CLAUDE.md、AGENTS.md与dir-graph.yaml协同减少AI上下文飘移
AI辅助编程显著提升了产出速度,但伴随而来的一个关键问题是上下文更容易产生失配。
这里所说的“上下文飘移”,并非指AI偶尔输出错误代码,而是指其依赖的上下文、生成的产物与项目的实际状态逐渐脱节:需求已更新,Agent仍按旧版PRD编写代码;架构约定了边界,代码却自行参照相似模块补全逻辑;评审中提出的风险点,后续实现并未处理;代码已合并至主干,基线文档与追溯关系却未同步更新。
我们的团队对此高度敏感,这与自身背景密切相关。作为大型SaaS研发团队,我们管理着大量仓库、历史遗留系统,产品线与模块边界也相当复杂。需求、设计、代码、测试、发布记录中任一环节出现对齐偏差,都将引发返工、增加Review压力,甚至带来线上风险。
因此,当前的核心策略是通过三个文件将Agent的读取入口、行为约束与目录图谱进行版本化、结构化处理:即CLAUDE.md、AGENTS.md 和 dir-graph.yaml。
具体而言,CLAUDE.md 作为Claude类工具的轻量入口,负责将工具引导至统一的规则源;AGENTS.md 是行为规则入口,明确定义了读取顺序、四阶段研发流程、分支策略、worktree规则以及任务接收格式;dir-graph.yaml 则是结构事实的来源,定义了目录、文件类型、Skill读写范围、状态机、Pipeline及保护路径,并通过 VIEWS.yaml 挂接查询视图。
本文旨在阐述如何组合这三个文件,以有效降低AI编程过程中的上下文飘移问题。
一、整体方案:先统一入口,再约束流程与结构
整体方案的本质并不复杂:确保Agent在行动前,明确“遵从谁的指令、按何种流程推进、可在哪些区域读写”。
第一步,统一入口。不同工具可保留各自的启动文件,但不得沉淀各自独立的规则体系。CLAUDE.md 的职责仅限于告知Claude类Agent:优先读取 AGENTS.md,切勿在此处重复或覆盖团队规则。
第二步,集中流程。AGENTS.md 定义了所有Agent的读取顺序与研发动作:先读取行为约束,再读取目录图谱,接着结合用户意图定位任务文件;流程从 planning、requirement、architecture、coding 到 writeback,每个阶段都设有对应的门禁(gate)与产物边界。
第三步,结构化结构。dir-graph.yaml 将目录角色、仓库枚举、保护路径、Skill读写契约、状态机和Pipeline编写为机器可读的图谱。Agent不再依赖臆测目录工作,而是在图谱的约束下查找上下文、生成产物并执行回写。
这三个文件的组合,并非为了增加文档数量,而是为了精准减少四类飘移:工具入口飘移、研发阶段飘移、文件路径飘移、交付回写飘移。
简言之,目标不是让Agent“多读一些资料”,而是为其提供一条可信的行动路径:从哪个入口接入,按哪个流程执行,到哪些目录进行读写,最终将哪些结果回写成新的事实。
二、CLAUDE.md:精简但关键,防止规则入口分叉
此工作区中的 CLAUDE.md 内容非常简洁。
它不包含产品背景、编码规范或流程细节。其唯一职责是告知Claude类Agent:本工作区的行为规则入口位于 AGENTS.md。
它要求Agent在采取行动前,按以下顺序读取:
AGENTS.md;dir-graph.yaml;- 根据用户请求与目录图谱定位到的任务相关文件。
同时,它明确规定:不得在 CLAUDE.md 中重复或覆盖 AGENTS.md 的规则。如需变更行为,应修改 AGENTS.md,确保所有AI工具共享同一事实来源。
这一设计看似保守,实则极为关键。
原因在于,若不同工具有各自独立的入口,将极易引发一种令人困扰的飘移:Claude遵循一套规则,Codex遵循另一套,团队自定义Agent又采用第三套。各方都认为自身遵守了规范,但规范本身早已分叉。
因此,CLAUDE.md 在此处扮演的角色并非“项目说明书”,而是“入口转发器”。
其核心价值在于防止规则入口发生分叉。
内容精简,反而更稳定可靠。
之所以保留这一层级,也是因为我们团队同时使用Claude Code、Codex、Qoder等多种工具。工具可以各异,但统一入口是避免混乱的基础。
三、AGENTS.md:行为约束与流程入口的真正核心
AGENTS.md 才是这套机制中的行为层核心。
它首先规定了所有Agent的读取顺序:
先读取 AGENTS.md,获取本工作区的行为约束与偏好;再读取 dir-graph.yaml,获取目录、文件、类型及其关联的定义;最后结合用户意图与图谱,定位具体需要读写的文件。
此顺序至关重要。
若Agent先行搜索代码、读取README或自行猜测目录结构,它极易基于自身经验行动(尤其在团队规模较大、存在大量同名或语义相似的模块时)。但本工作区并非普通代码仓库,它同时承载着知识库、需求、设计、交付、CR、工具定义、Pipeline模板及worktree规则。
缺乏统一入口,Agent很可能将这些内容混淆处理。
例如,AGENTS.md 明确指出,知识库管理与代码开发是两个独立的操作域,不得混淆。在当前示例工作区中,默认仅有一个 knowledge-base 基础仓库;代码开发默认写入CR worktree下的 code/ 目录;仅当 dir-graph.yaml 显式配置 active role=code repository 时,才为独立代码仓库创建或使用独立的CR worktree。在团队级落地时,此模型会扩展到多个 role=code repository,而非局限于单一仓库。
若不明确说明此规则,Agent很可能直接在主工作区的 code/ 目录进行修改,或将知识库文件与代码文件混在同一个操作中处理。
AGENTS.md 还定义了主研发流程,即四阶段模型。
第一阶段是调研规划期,由 planning 命令触发,通过 product-planning.pipeline 编排,产物存放于 docs/product-planning/。
第二阶段是需求编写期,由 requirement 命令触发,通过 requirement-authoring.pipeline 编排。requirement-writer 调用 requirement-register 生成CR-ID,在 change-requests/_backlog.yml 中登记,创建 requirement/CR-YYYY-NNN 分支与CR worktree,随后编写 change-requests/CR-*/prd.md,并推进评审与审批。
——之所以设置上述两个阶段,是因为我们利用开源工具为产品同学构建了一个简易工具(类似丐版Claude Cowork,底层对接了多个常用工具的CLI,如CC的),并将所有设计好的skill等预置其中。这使得产品与研发的工作能够衔接起来,各角色的关键产物真正沉淀到同一个知识库(KB)中。
第三阶段是开发期。架构设计由 architecture 命令触发,其前置门禁为CR status=requirement-approved,产物为 change-requests/CR-*/sdd.md。代码编写由 coding 命令触发,前置门禁为CR status=tech-design-reviewed,流程涵盖开发计划、任务拆解、代码实现、代码评审及人工审批。
第四阶段是回写期,由 writeback 命令触发,前置门禁为CR status=code-approved,通过 feature-writeback.pipeline 执行合并、PRD/SDD回写、任务回写、追溯链回写及CR归档。
此流程的意义并非仅仅体现严谨性,而是确保Agent明确自身所处的阶段、应调用的Pipeline以及应读写的产物类型。
流程顺序一旦混乱,飘移便随之开始。
四、dir-graph.yaml:权威图谱,超越传统目录说明
如果说 AGENTS.md 是行为约束,那么 dir-graph.yaml 就是结构事实。
它并非一份普通的目录说明。它定义了工作区的知识结构、仓库枚举、目录角色、运行时状态、保护路径、意图信号、Skill读写契约、状态机及Pipeline,并通过 query-capabilities.views 指向 VIEWS.yaml,将查询视图从结构图谱中挂接出来。
此处最易被低估的价值在于:它将“人脑中的目录理解”转化为了Agent可读的图谱。
例如,对于单工程的monorepo,它声明:
- id 为
test001; - path 为
.; - trunk 为
main; - role 为
knowledge-base。
这意味着,在当前示例配置中,默认的代码开发并非随意选择一个目录开始,而是在 knowledge-base CR worktree 内的 code/ 目录中进行。仅当配置了 active role=code repository 后,才会进入独立的代码仓库 worktree。在更复杂的团队场景中,此处可进一步扩展为多代码仓库、多服务、多端联动的 repository 图谱。
再如,它将知识目录划分为以下几类:
specs/为特性驱动的核心知识,标准结构包含PRD.md、SDD.md、contracts/及traceability.yml;delivery/承载交付过程,包括delivery/task/、delivery/releases/及只读归档;change-requests/承载在途CR,标准结构包含cr.md、prd.md、sdd.md、plan.md、tasks/、review-annotations/、approval.yml及traceability.yml;docs/承载跨特性知识,例如技术笔记、竞品分析、市场洞察、参考资料、产品规划、创意及用户反馈;constraints/承载可执行约束,例如 schema 及 feature flags;_archived/为历史归档,对Agent默认设定为只读。
这有效避免了Agent将 docs/ 视为万能垃圾桶、将 specs/ 当作草稿目录、或将 change-requests/ 当作长期基线。
更关键的是,dir-graph.yaml 中还包含了 agent_hints.skill_context。
它直接告知Agent:不同Skill具体可读哪些内容、可写哪些内容。
例如:
write-requirement-prd读取change-requests/CR-*/cr.md,写入change-requests/CR-*/prd.md;review-requirement读取PRD与CR,写入review-annotations/requirement.yml;write-tech-design读取PRD与CR,写入sdd.md;write-dev-plan读取sdd.md,写入plan.md;write-dev-tasks读取plan.md与sdd.md,写入tasks/TASK-NN.md;implement-code读取PRD、SDD及tasks,写入CR worktree内的code/或显式code repository对应的worktree;writeback-prd-sdd读取CR下的PRD/SDD,回写至specs/{id}/PRD.md、specs/{id}/SDD.md及specs/_index.yml;writeback-traceability读取评审批注、backlog及任务索引,写入specs/{id}/traceability.yml。
此时,Agent不再是“凭感觉理解项目结构”,而是在一份清晰的读写契约下行动。
五、三文件协同,主要减少四大类飘移
单独审视这三个文件,每个都不算复杂。
但将它们组合使用,其价值便得以凸显。
CLAUDE.md 致力于解决入口一致性问题:Claude类Agent不再自行构建一套规则,而是转向 AGENTS.md。
AGENTS.md 致力于解决行为一致性问题:所有Agent均遵循相同的读取顺序、四阶段流程、worktree及分支策略。
dir-graph.yaml 致力于解决结构一致性问题:目录、文件类型、Skill读写范围、保护路径、状态机及Pipeline均拥有权威定义。
三者结合使用,主要减少以下四类飘移。
第一类,工具入口飘移。
过去,不同工具极易各自读取不同的说明。如今,CLAUDE.md 明确规定不承载独立规则,而是将Claude入口导向 AGENTS.md。这减少了规则源头的一个分叉点。
第二类,阶段飘移。
例如,需求审批尚未完成,便开始编写技术方案;技术方案未经评审,便开始拆解任务与编写代码;代码尚未批准,便开始回写基线。这类问题难以通过口头流程有效约束,因此 AGENTS.md 与 dir-graph.yaml 均将状态机与Pipeline明确写出:requirement-approved 后方可进入架构设计阶段,tech-design-reviewed 后方可进入编码阶段,code-approved 后方可进入回写阶段。
第三类,路径飘移。
PRD应撰写于何处?已发布的 specs/ 是否允许直接修改?代码应写入主工作区还是CR worktree?评审批注应散落在聊天记录中,还是沉淀至 review-annotations/ 目录?此类问题依赖“约定俗成”并不可靠。dir-graph.yaml 通过固定路径与读写契约,确保Agent不会随意书写。
第四类,回写飘移。
许多团队最常见的断点是:过程文档均存在于CR中,代码也已合并,但 specs/ 与 delivery/ 未同步更新。当下一次Agent或新人读取基线时,看到的仍是旧信息。feature-writeback.pipeline 通过串联 merge-feature-branch、writeback-prd-sdd、writeback-tasks、writeback-traceability 及 cr-archive,正是为了解决此断点。
这项操作虽然繁琐,但价值极高。
因为知识底座一旦失去可信度,Agent后续的所有努力都将大打折扣。
六、最值得借鉴的并非文件名,而是边界管控思路
不建议机械复制这三个文件。
每个团队的目录结构、研发流程、仓库关系及工具栈均有差异。你完全可以使用不同的文件名称,也可以不采用本文所述的CR状态机。
真正核心的是以下三件事。
第一,入口必须精简。
像 CLAUDE.md 这类工具专属入口,不应演变为第二套制度。其理想状态是将工具拉回统一的规则源。否则,工具越多,规则分叉越严重。
第二,流程必须集中。
类似 AGENTS.md 的文件应清晰回答Agent如何行动:优先读取什么、各阶段执行什么、哪些分支策略不可打破、worktree如何应用、接收任务时需确认哪些范围。
第三,结构必须机器可读——这一点至关重要。
目录结构、Skill读写范围、保护路径、状态机、Pipeline等,不应仅以自然语言描述。自然语言适用于解释,但难以长期有效约束Agent。真正能够减少飘移的,是可解析、可检查、可演进的结构化定义。
人阅读说明文档,Agent读取契约。两者不可混为一谈。
七、目前尚未完全解决的问题
这套实践并非万能解决方案。
当前仍存在几个棘手问题。
第一,dir-graph.yaml 本身也需要治理。一旦其内容过期,危害将更大,因为Agent会极其忠实地执行错误的契约。因此,配置文件本身也需要经过Review、版本管理及校验工具的处理。
第二,状态机不能设计得过于理想化。实际研发中总会存在插队需求、线上问题、客户定制及历史债务。流程设计必须允许例外情况存在,但每次例外都应留下可追溯的记录。
第三,跨仓库依赖仍然是一个难题。虽然整体方案支持跨仓库协同,但面对上千个仓库时,挑战依然巨大。当前我们只能依据一定原则缩小管控范围,未来能否适配整体架构,仍是未知数。
……
八、可以直接尝试的起步做法
如果你的团队也开始遇到Agent上下文飘移问题,可以从以下四件事入手。
- 首先,精简工具专属入口。例如,让
CLAUDE.md仅负责指向统一规则源,不再在其中编写另一套规则。 - 其次,编写一个统一的
AGENTS.md,明确读取顺序、阶段流程、分支策略、worktree规则及任务边界确认方式。 - 接着,使用一个最小化的目录图谱,定义核心目录角色、保护路径及Skill读写契约,优先管控最容易出错的路径。
- 最后,将评审意见、任务、回写操作及追溯链进行结构化处理,避免关键决策仅留存于聊天记录与PR评论中。——关于这一点,许多人有不同看法。
达成上述步骤后,即使没有复杂的自动化系统,也已能显著减少大量不必要的返工。
最后,补充说明 dir-graph.yaml 与ContextSearch之间的关系。
切勿将 dir-graph.yaml 理解为一篇“让Agent多读一些背景资料的文档”,也不应将其视为ContextSearch的替代品。它更像是为ContextSearch提供的一个结构化地图:哪些目录承担何种角色,哪些文件属于基线,哪些是过程状态,哪些仅可读取,某个具体Skill应从何处获取上下文、将结果写回何处——这些都需在图谱中预先明确定义。
ContextSearch的职责是找出相关上下文,但它不能仅依赖全文搜索与语义相似度进行无差别检索。否则,检索出的内容可能很多,却无法判断哪些是权威信息、哪些是旧版本、哪些仅是参考资料、哪些根本不应被当前任务修改。
因此,定位清晰:dir-graph.yaml 为ContextSearch提供范围、权重、可信度与权限边界;ContextSearch在此边界内召回上下文。前者解决“去哪里查找、什么内容更可信、哪些不可触碰”的问题,后者解决“将当前任务真正相关的内容精准找出”的问题。
两者相结合,才更接近Agent所需的工程化上下文。
九、后续演进方向的思考
当前方案能够解决部分飘移问题,但后续仍有几项工作需继续完善。
第一,为 dir-graph.yaml 开发linter。至少应能检查路径是否存在、Skill读写是否越权、Pipeline step是否有对应的Skill、状态机trigger是否形成闭环。否则,一旦图谱编写错误,Agent将非常认真地执行错误规则。
第二,为关键目录与文件补充 freshness / authority 元数据。ContextSearch不应仅知道“搜到了内容”,还须了解该内容是基线、草稿、历史归档、外部参考还是运行态数据。不同类型的上下文,其权重不应相同。
第三,引入漂移检查机制。例如,PRD发生变更后,自动检查SDD、tasks、测试用例及traceability是否需要同步更新;代码合并前,检查writeback是否完整。此检查不一定从一开始就全自动拦截,但至少应具备风险提示能力。
第四,设计例外处理机制。线上紧急修复、客户定制、临时绕开流程等情况都有可能发生。流程不能假装这些情况不存在,但每次例外都必须留下记录,否则所谓的流程很快会被现实击穿。
第五,AGENTS.md 与 dir-graph.yaml 的变更也应纳入Review流程。它们本质上是Agent的操作系统配置,不能像普通说明文档那样随意修改。
第六,ContextSearch最好根据任务类型制定检索策略。撰写PRD、编写SDD、开发代码、执行Review、进行writeback等不同任务,应拥有不同的召回范围与排序权重,而非使用一套通用的语义搜索方案。
第七,Agent应尽量采用 fail-closed 原则。当状态、路径、权限、CR-ID不明确时,不要让Agent自行猜测。宁可暂停执行并要求补充上下文,也不要带着错误假设一路生成下去。
工程化的核心思路仍是:让Agent减少凭感觉行事,更多地遵循工程事实来行动。
最后总结一句:
CLAUDE.md 负责防止入口分叉,AGENTS.md 负责定义行动路径,dir-graph.yaml 负责界定操作范围与权限。
这便是当前减少Agent飘移的基本思路。









