1. 项目概述为什么我们需要一个“AI缰绳”系统如果你正在一个大型、复杂的项目中尝试使用像Claude Code这样的AI编程助手你很可能已经遇到了一个瓶颈那个位于项目根目录的、唯一的、名为CLAUDE.md的指令文件。起初它是个完美的解决方案——你把项目架构、编码规范、API设计原则一股脑儿塞进去AI助手就能“理解”你的项目了。对于一个小型个人项目或者一个全新的代码库这招确实管用。但一旦项目规模膨胀团队协作介入技术栈变得多元这个“万能文件”就会迅速变成一个负担甚至成为阻碍AI高效工作的罪魁祸首。我最近在一个中大型的Laravel项目中就撞上了这堵墙。这个项目混合了传统的MVC Web控制器、现代化的API控制器、领域驱动设计DDD启发的Action类、React单页应用SPA以及一套复杂的数据库迁移和测试策略。根目录的CLAUDE.md文件很快增长到了数百行内容包罗万象从宏观的“六边形架构”理念到具体的“数据库迁移命名规范”再到“SPA组件中如何传递URL参数”这样的细枝末节。每次Claude开始工作无论它是要修复一个前端按钮的样式还是要编写一个新的API端点它都必须把这份巨长的“百科全书”整个加载到它的上下文窗口中。这直接导致了两个致命问题。首先是上下文窗口的无效膨胀。AI模型的上下文长度是宝贵的资源。当Claude在处理一个React组件时那些关于Laravel数据库迁移中json和jsonb字段类型选择的详尽说明完全就是“噪音”但它们却实实在在地占用了本可用于理解组件逻辑和周边代码的令牌Token。其次是信号噪声比严重恶化。当一份文档试图说明所有事情时对于任何一件具体的事情它的说明力都会变得薄弱。AI助手甚至人类开发者不得不在这片信息的海洋里费力搜寻只为找到与当前任务真正相关的寥寥数行。这极大地降低了AI的响应准确度和效率。于是“AI缰绳”Agent Harness系统的想法应运而生。它的核心思想非常简单用分层的、领域特定的指令文件取代单一的整体指令文件。这不是关于编写更复杂的AI工具而是关于如何更聪明地组织我们已经拥有的、最简单的工具——纯文本的Markdown文件。2. 缰绳系统架构分层加载与领域聚焦这个系统的架构直击上述痛点的核心。它利用了Claude Code以及许多类似AI工具的一个工作特性当AI在某个目录下操作时它不仅会读取根目录的CLAUDE.md还会自动寻找并加载当前所在目录及其父目录中的CLAUDE.md文件。这是一种分层叠加的加载机制。举个例子当Claude开始在app/Actions/目录下创建一个新的Action类时它会按顺序加载项目根目录的CLAUDE.md包含项目总览、核心架构、跨领域规则。app/Actions/CLAUDE.md如果存在则包含Action类的专属模式、规则和示例。这意味着AI获得的上下文是高度聚焦的。它既能看到全局的“战略地图”根文件又能获得眼前任务的“战术手册”子目录文件。而那些与当前任务无关的“战术手册”——比如database/migrations/CLAUDE.md里的内容——则不会被加载从而为理解相关代码腾出了宝贵的上下文空间。基于这个原理我为项目设计了一套缰绳文件布局。这个布局直接映射了代码库的物理结构和逻辑边界CLAUDE.md # 项目总览、架构、共享规则、缰绳索引 ├── app/ │ ├── Http/ │ │ ├── Controllers/ │ │ │ ├── Api/CLAUDE.md # API控制器模式增长方向 │ │ │ └── Web/CLAUDE.md # 遗留Web控制器规则仅修复Bug │ │ └── Resources/CLAUDE.md # API资源Eloquent API Resources约定 │ ├── Actions/CLAUDE.md # Action类模式、Result DTOs │ ├── Services/CLAUDE.md # 服务契约、策略模式 │ └── Policies/CLAUDE.md # 授权策略模式 ├── database/ │ └── migrations/CLAUDE.md # 迁移命名规范、列类型选择 ├── tests/CLAUDE.md # 测试模式、UserFactory、TDD工作流 └── resources/ └── js/ └── spa/CLAUDE.md # SPA架构、临时包装器模式这种设计的妙处在于其自解释性。当Claude打开一个位于app/Http/Controllers/Api/下的控制器文件时它获得的指令集是“根文件中的架构原则”加上“API控制器专属模式”。它不会看到“SPA组件模式”或“迁移命名规则”。上下文干净了相关指导的“信号”自然就强了。这不仅仅是优化AI也是在为团队建立清晰的、文档化的代码边界。2.1 每个缰绳文件的核心结构为了保证一致性和可维护性每个子目录的CLAUDE.md文件都遵循一个简单的四段式结构。这个结构旨在用最直接的方式回答AI和开发者在进入这个代码区域时最需要知道的问题。这是什么区域—— 用一句话定义这个目录的职责和边界。例如app/Actions/CLAUDE.md的开头可能是“本目录包含所有业务动作Action类它们封装了单一的业务用例是控制器与服务层之间的协调者。”设计方向—— 说明这个区域是处于“积极发展”Growth Area还是“维护模式”Legacy/Maintenance。这为AI的代码生成定下了基调。例如Web/CLAUDE.md会明确写道“此目录包含遗留的Blade渲染控制器。仅进行错误修复。所有新功能应通过API控制器实现并由前端SPA消费。”模式—— 这是文件的核心。提供具体的、可复制的代码示例。不要只讲理论要展示代码。例如在Action的缰绳中我会直接给出一个标准的Execute方法签名、一个ResultDTO的完整类定义以及一个调用服务的示例。规则与禁忌—— 明确列出“必须做”Do‘s和“绝不能做”Don‘ts的事项并附上简短的理由。这是将团队约定和过往教训固化成文的关键。例如“禁止在Action中直接返回Eloquent模型。必须返回一个ResultDTO以明确操作结果并封装附加数据。”这种结构强迫我们进行清晰的思考这个区域的精髓是什么我们想鼓励什么模式我们想避免什么陷阱最终产生的文件通常很精炼但信息密度极高。3. 核心缰绳文件解析与内容设计让我们深入几个关键的缰绳文件看看里面具体装了些什么“干货”。这些内容不是凭空想象的而是从大量的代码评审和AI“翻车”案例中提炼出来的。3.1 Actions 缰绳强制执行单一职责与明确输出app/Actions/CLAUDE.md可能是最关键的缰绳之一因为它定义了业务逻辑的核心单元。模式部分我会提供一个“黄金模板”?php namespace App\Actions\Things; use App\DataTransferObjects\Result; use App\Http\Requests\StoreThingRequest; use App\Models\Thing; use App\Services\ThingService; class CreateThingAction { public function __construct(protected ThingService $service) {} /** * 执行创建Thing的业务逻辑。 * param StoreThingRequest $request 经过验证的表单请求 * return Result 包含操作结果和数据的DTO */ public function execute(StoreThingRequest $request): Result { // 1. 准备数据从DTO或Request中获取 $data $request-validated(); // 2. 调用领域服务或直接处理 $thing $this-service-create($data); // 3. 触发领域事件如审计日志、通知 event(new ThingCreated($thing)); // 4. 返回明确的Result对象 return Result::succeeded($thing); } }同时必须附上ResultDTO的示例因为这是统一Action输出的关键?php namespace App\DataTransferObjects; class Result { public function __construct( public readonly bool $success, public readonly ?string $message null, public readonly mixed $data null, public readonly ?string $errorCode null, ) {} public static function succeeded(mixed $data null, ?string $message null): self { return new self(success: true, data: $data, message: $message); } public static function failed(string $message, ?string $errorCode null): self { return new self(success: false, message: $message, errorCode: $errorCode); } }规则与禁忌部分必须非常强硬和具体禁忌What NOT to Do禁止在Action中处理HTTP响应如return response()-json(...)。HTTP状态码和响应格式是控制器的职责。禁止在Action方法中直接注入Illuminate\Http\Request对象。只接受已经验证过的FormRequest对象或简单的值对象。禁止为简单的、仅涉及单个模型保存的CRUD操作创建Action。如果控制器可以直接调用$model-create($validated)那就让控制器来做。禁止在Action类中添加除execute之外的任何公共方法。一个Action一个公共职责。禁止返回裸Eloquent模型、数组或null。必须使用ResultDTO包装所有输出。这些“禁忌”直接来自于AI早期犯下的错误。例如AI曾生成过一个返回redirect()的Action导致无法在命令行任务中复用。这条规则就是为了彻底杜绝此类问题。3.2 Tests 缰绳守护质量与规范流程tests/CLAUDE.md文件是质量的守门员它需要规定如何测试而不仅仅是测试什么。首要规则是流程性的甚至比代码模式更重要## 测试执行流程必须遵守 1. **关键永远不要同时运行多个测试进程。** 这会导致数据库连接和临时文件冲突。 2. 在提交代码前**有且仅有一次**运行完整的 make test 命令作为最终检查。 3. **PHPUnit超时时间必须设置为至少20分钟**以适应完整的测试套件运行。 4. 如果变更**仅涉及前端**JS/TS/CSS文件则跳过耗时的 make test改为运行 make test-js 和 make lint。这条规则源于血泪教训。曾有开发者和AI在本地同时运行测试和开发服务器导致随机性的测试失败排查耗时极长。另一个核心模式是UserFactory的使用。为了防止AI或新开发者在测试中错误地直接使用User::factory()从而绕过我们精心设计的、预配置了角色和权限的测试门面Facade缰绳中必须明确// 正确做法 use Facades\Tests\Setup\UserFactory; // 注意这是我们的自定义门面不是Eloquent工厂 $admin UserFactory::admin()-create(); // 创建拥有管理员角色的用户 $userWithPermission UserFactory::withPermissions(projects.manage)-create(); // 禁止做法 // User::factory()-create(); // 这将创建一个没有角色/权限的“裸”用户导致授权测试失败。同时缰绳需要定义测试覆盖的优先级指导AI在编写测试时分配精力认证未登录用户访问返回401。授权登录用户但无权限访问返回403。验证无效输入返回422和错误信息。主流程有效输入返回200/201及正确数据。边界情况与业务规则。领域事件/通知是否被触发。这个顺序确保了最基本的安全和健壮性得到优先保障。3.3 Web Controllers 与 SPA 缰绳管理新旧架构的过渡在渐进式重构的项目中明确“新旧边界”至关重要。app/Http/Controllers/Web/CLAUDE.md文件可能是我写过的最短的缰绳但也是信息最明确的之一# Web控制器遗留代码 **仅修复错误。不添加新功能。不添加新路由。不创建新视图。** 所有新功能必须通过API控制器实现并由React SPA页面消费。它的简短本身就是一种强烈的信号。任何进入这个目录的开发者或AI都会立刻明白自己身处“考古现场”任务是“修复文物”而不是“大兴土木”。这避免了资源被错误地投入到正在被淘汰的技术栈上。与之相对resources/js/spa/CLAUDE.md则详细说明了新架构的模式。其中一个关键概念是“临时包装器”Interim Wrapper## 临时包装器模式 临时包装器通常是一个Blade视图**只是一种发布机制而非独立的架构**。 - **SPA组件永远是事实的唯一来源。** 所有业务逻辑和状态管理应存在于SPA组件中。 - 包装器**只负责**向SPA组件传递因环境如从服务器端注入的初始数据、CSRF令牌而不同的props。 - **新功能和Bug修复必须直接作用于SPA组件。** 永远不要在包装器内添加逻辑。这条规则是为了防止出现“逻辑重复”的噩梦——同样的功能在Vue/React组件里实现一遍又在包装它的Blade/PHP文件里以另一种方式实现一遍。缰绳明确规定了权力的归属SPA组件是“皇帝”包装器只是“传令官”。3.4 根目录CLAUSE.md总纲与索引根目录的CLAUDE.md是缰绳系统的基石和总目录。它不再试图描述所有细节而是专注于项目全景图技术栈、核心架构图如前后端分离示意图、领域模型的核心实体与关系。“什么功能去哪里的”决策树例如“用户管理界面去哪改→ 去SPA。”“订单状态变更的API去哪加→ 去app/Actions和Api控制器。”开发工作流Makefile中关键命令的说明make test,make analyse,make deploy-staging。最重要的部分缰绳索引表。这个索引表是系统的“地图”它回答了“关于X我应该去哪看”这个问题。领域缰绳文件路径核心摘要API 控制器app/Http/Controllers/Api/CLAUDE.md增长方向。使用查询构建器模式、返回API Resources、保持控制器精简。Web 控制器app/Http/Controllers/Web/CLAUDE.md遗留代码。仅进行错误修复不添加新功能。React SPAresources/js/spa/CLAUDE.md事实来源。所有新UI逻辑的归属使用临时包装器集成。服务层app/Services/CLAUDE.md契约优先。定义接口再实现。使用策略模式处理不同算法。动作类app/Actions/CLAUDE.md单一执行。一个Action一个execute()方法返回Result DTO。测试tests/CLAUDE.md质量关卡。使用UserFactory门面遵循TDD流程注意执行顺序。数据库迁移database/migrations/CLAUDE.md一致性。命名规范、常用列类型选择、避免的陷阱。授权策略app/Policies/CLAUDE.md基于角色/权限。在before方法中处理超级管理员豁免。没有这张表分散在各处的缰绳文件很容易被遗忘。有了它无论是AI还是新加入的团队成员都能在30秒内找到任何编码领域的“本地法律”。4. 缰绳系统的运作、演进与自检建立一个缰绳系统不是一劳永逸的关键在于让它“活”起来能够从错误中学习并自我完善。我们为此建立了一个简单的“反馈协议”。4.1 反馈协议从错误中学习的循环当AI或任何开发者提交的代码在评审中被发现问题时我们遵循一个三步协议更新对应的缰绳文件判断这个错误是偶然的疏忽还是暴露了指引的缺失。如果是后者立即编辑相关的CLAUDE.md文件。在“规则”或“禁忌”部分增加一条或者补充一个更清晰的“模式”示例。重新加载缰绳到上下文在当前的AI对话会话中手动让AI重新读取刚刚更新过的缰绳文件。这可以通过发送类似“请重新阅读app/Actions/CLAUDE.md文件的内容”的指令来实现。基于更新后的指引重做要求AI根据新的、更完善的指引重新尝试实现之前的变更。这个协议的精髓在于即时反馈和知识固化。错误不会白白发生它会立即被转化为一条防止未来再次发生同样错误的规则。并且由于是在同一会话中重新加载AI能立刻应用新知识让你看到立竿见影的改进。最后当你将更新后的CLAUDE.md文件提交到代码仓库这条新规则就对所有未来的会话和所有团队成员生效了。这形成了一个知识积累的棘轮效应——系统的整体指引只会越来越完善AI的“首轮通过率”也会随之稳步提升。4.2 让缰绳检查自己的工作集成质量门禁一个真正强大的缰绳不应该只告诉AI“怎么写”还应该告诉它“怎么确认自己写对了”。因此我们在根目录的CLAUDE.md中明确规定了提交前验证步骤并将其作为AI工作流的内在环节## 提交前验证必须全部通过 1. **代码风格检查**运行 make lint。所有PHP和JavaScript的代码风格问题必须被修复。 2. **PHP单元测试**运行 make test。所有PHPUnit测试必须通过。注意运行前确保数据库环境正确 3. **前端单元测试**运行 make test-js。所有Vitest/Jest测试必须通过。 4. **浏览器验证**仅针对UI变更对于修改了视觉效果或交互的更改需要在至少一个主流浏览器中手动快速检查。 5. **端到端测试**谨慎运行E2E测试耗时较长在运行前需在对话中确认。通常只在修改关键用户流程时运行。这意味着当AI完成一段代码修改后它的任务并没有结束。它的下一步是自动执行这些质量门禁。如果make lint失败了它需要根据错误信息调整代码风格如果make test失败了它需要分析测试报告并修复功能缺陷。只有当所有这些检查都通过后它才会生成最终的、可供人类评审的代码差异Diff。这彻底改变了协作模式。我不再需要反复在评审中说“你运行测试了吗”或“记得跑一下lint”。这些要求已经内化为AI工作流程的一部分。AI呈现的代码在理想情况下已经是通过了第一道质量关的、更成形的版本。人类评审者从而可以更专注于架构合理性、业务逻辑正确性等更高层次的问题。4.3 关键设计决策背后的思考在构建这套系统时我们面临并明确回答了几个关键的设计选择为什么用CLAUDE.md文件而不是开发自定义工具或插件答案是为了极简主义和可及性。Markdown文件是纯文本任何编辑器都能打开任何版本控制系统都能完美管理。它的修改历史清晰可见可以通过Pull Request进行评审。我们不需要编写、维护或向团队解释一个额外的工具。它的工作原理一目了然AI读取它所在目录的说明文件。这种简单性是其最大的优势。为什么采用“一个目录一个文件”的分层模式而不是一个更复杂的、带标签的巨型文件核心目标是上下文控制。AI的上下文窗口是有限的、昂贵的资源。分层加载确保了“在什么山头唱什么歌”。当AI在处理数据库迁移时它不需要被React钩子的规则干扰。这种设计最大限度地提升了相关信息的“信噪比”让AI能将有限的注意力集中在最相关的指令上。为什么每个文件都要包含具体的代码示例因为模式比规则更容易被遵循。告诉AI“返回一个Result DTO”是一条规则。但向它展示一个完整的、带有succeeded和failed静态工厂方法的Result类就是一个它可以立即复制和粘贴的模式。AI以及许多开发者是模式匹配的高手。提供一个好的模式通常比十条抽象的规则更有效。为什么必须包含“禁忌”部分这是为了划定明确的边界。AI通过学习大量公开代码进行训练它可能会“知道”很多种实现方式但其中一些可能不符合你项目的特定约定。仅展示“正确做法”可能不足以阻止它在面对新场景时采用一个你不想看到的“流行但错误”的做法。“禁忌”部分就像护栏明确地说“我知道你可能想这么做但在这里不行。”这能极大地减少意外和返工。