用 Codex Skill 管理项目文档让 Code Agent 读懂你的业务Code Agent 的上限不在模型能力而在你的仓库里有没有它能读懂的知识。目录Agent 为什么会把需求做烂为什么选 Skill 而不是 MCP 或 SDDNVC Practice 的知识架构全景第一层repo-map 做路由第二层7 个 Skill 覆盖全部业务域第三层References 按需展开渐进式披露的上下文经济学从有什么知识到怎么用知识防腐化让知识不会越用越错实操建议Agent 为什么会把需求做烂NVC Practice 是一个基于非暴力沟通理论的 AI 练习平台用户选择场景后和 AI 角色扮演对话系统按 NVC 四步法观察→感受→需要→请求实时评分。后端用 Spring Boot 4 Spring AI 2.0涉及多 Agent 协同评分、RAG 知识库检索、场景关卡引擎等模块。项目不大但业务耦合度不低。刚开始用 Code Agent 做需求时碰到一个问题改练习评分逻辑Agent 找到了评分引擎的代码按每个维度独立打分的思路写了一段实现。看起来没毛病但它不知道这个项目里 NVC 四步评分不是孤立的——观察、感受、需要、请求四个维度之间有依赖关系如果用户根本没表达感受那需要和请求的评分权重应该降权而不是独立计算。这个规则写在 PRD 里没写在代码注释里Agent 自己翻不到。类似的坑不止一个。练习会话有闯关和自由练习两种模式状态机不同场景关卡的解锁依赖前置场景的通过但解锁逻辑散落在ScenarioService和PracticeSessionService两个类里sessionId在练习域和用户画像域里含义不同混用会导致数据归属错误。这些问题的根因一样领域知识没有被结构化地记录在仓库里。代码告诉你怎么做但说不清为什么这样做、“哪些约束不能碰”。参考阿里妈妈团队在 AI Coding 实践中的总结——Code Agent 在复杂项目中撞墙根因是人读不到的知识Agent 也读不到。NVC Practice 的做法是把项目知识写进仓库用 Codex Skill 作为载体。为什么选 Skill 而不是 MCP 或 SDDMCP 接外部文档试过效果不好。项目文档本身有大量过时内容而且缺乏结构化组织一次读取直接撑爆上下文窗口。** SDDSpec-Driven Development** 也考虑过。从零开始的新项目用 SDD 效果不错但 NVC Practice 是持续迭代的存量项目——一个两三百行的代码改动spec 要写五六百行维护成本倒挂。spec 随用随弃知识不累积。Skill 的渐进式披露机制天然适合这个场景元数据常驻用于路由判断主文件按需加载引用详情延迟读取。“大量知识但每次只需要一部分”——这正好是复杂项目里 Agent 的工作模式。NVC Practice 的知识架构全景整个知识体系分三层对应 Agent 拿到需求后的认知路径第一层AGENTS.md repo-map Skill全局索引与路由 ↓ 读完这层Agent 知道该去哪个业务域 第二层6 个业务域 Skill模块核心知识 ↓ 读完这层Agent 理解业务语义和代码结构 第三层references/ 目录下的详情文件按需展开 Agent 判断需要更多信息时才读加上一个跨域术语字典 Skill总共 7 个 Skill、约 30 个引用文件。下面逐层展开。第一层repo-map 做路由AGENTS.md是 Agent 拿到需求后读的第一个文件Token 消耗很少但完成最关键的决策我应该读哪个模块的知识。nvc-repo-mapSkill 承担路由层。核心是一张需求关键词到业务域的映射表登录、注册、token、当前用户、权限 → nvc-auth-user 画像、MBTI、沟通风格、用户分析 → nvc-profile-domain 场景、关卡、解锁、专题、进度 → nvc-scenario-domain 练习、对话、轮次、闯关、自由模式 → nvc-practice-domain NVC 评分、四步法、维度打分、雷达图 → nvc-scoring-engine 知识库、RAG、文档上传、向量检索 → nvc-knowledge-base 接口、DTO、配置一起改、跨模块 → nvc-change-playbook附带反面判定规则防止误路由看起来像对话不一定归练习域——如果它涉及场景解锁逻辑主域是场景看起来像评分不一定归评分引擎——如果它涉及练习会话的状态推进主域是练习看起来像用户不一定归用户模块——如果它涉及画像分析和 MBTI 推断主域是画像第二层7 个 Skill 覆盖全部业务域每个 Skill 是一个 SKILL.md 主文件加上一组 references/ 引用文件。主文件控制在 3000 字以内覆盖四个维度。业务域 Skills以nvc-practice-domain为例路由判定需求命中练习会话创建、对话轮次推进、闯关/自由模式切换、练习报告生成时使用。使用顺序先看lifecycle.md确认会话处于哪个阶段再看round-pipeline.md确认单轮对话的完整流程改状态推进时看state-machine.md改报告生成时看practice-report.md。关键入口直接列出代码入口的完整路径——app/src/main/java/nvc/practice/modules/practice/api/PracticeController.java app/src/main/java/nvc/practice/modules/practice/service/PracticeSessionService.java app/src/main/java/nvc/practice/modules/practice/service/PracticeRoundService.java app/src/main/java/nvc/practice/modules/practice/service/PracticeReportService.java必守约束练习会话的闯关模式和自由练习是两套状态机不要混写一轮对话里 AI 的回复和用户的回复必须成对存在不能只存一半练习报告的雷达图数据来源于每轮评分的维度均值不是最后一轮的分数闯关模式下只有当轮评分全部达标才能解锁下一轮不能跳过评分引擎 Skillnvc-scoring-engine是项目里最复杂的模块。主文件要覆盖四步评分的依赖关系观察→感受→需要→请求不是四次独立调用。如果用户没表达感受需要和请求的评分应该降权。评分引擎内部有一个NvcDependencyGraph定义维度间的依赖关系。Multi-Agent 编排5 个 Agent观察官、感受官、需要官、请求官、综合官的执行顺序和并行策略。观察官和感受官可以并行但需要官必须等感受官的结果。结构化输出契约每个 Agent 返回的 JSON 结构、评分范围0-10、反馈字段格式必须和前端的雷达图组件对齐。跨域支撑 Skillsnvc-change-playbook当改动同时涉及接口、DTO、评分逻辑和数据库表结构时提供影响检查清单和回滚策略。第三层References 按需展开以nvc-practice-domain为例引用文件覆盖练习生命周期的每个切面文件内容lifecycle.md会话状态CREATED → IN_PROGRESS → COMPLETED / ABANDONED和推进链路round-pipeline.md单轮对话执行顺序幂等检查 → AI 生成回复 → 用户回复 → 四步评分 → 推进轮次state-machine.md闯关模式和自由练习两套状态机的完整定义practice-report.md报告生成逻辑维度均值计算、雷达图数据结构、AI 总结生成gotchas.md易错点sessionId 归属校验、轮次编号连续性、评分回滚Agent 不会一开始就加载全部文件。改评分逻辑只读round-pipeline.md改状态推进只读state-machine.md。渐进式披露的上下文经济学Skill 的三级加载设计直接决定了上下文消耗层级何时加载Token 消耗作用元数据name description始终可见~100 字路由判断这个 Skill 跟当前需求有关吗主文件正文SKILL.mdSkill 被触发时~3000 字理解业务语义、代码结构、制定方案引用详情references/Agent 判断需要时按需展开具体流程、API 定义、易错点对比把所有知识塞进一个大 prompt渐进式披露让 Agent 在合适的时机获取正确的知识。上下文窗口不会被撑爆注意力不会被无关信息稀释。从有什么知识到怎么用知识NVC Practice 的 Skills 经历了一轮关键迭代。最初版本只写了业务规则没有标注这些规则对应代码中的哪些流程。Agent 需要自行推理这条规则大概和哪段代码有关——大部分时候能猜对但准确率不稳定。比如它知道NVC 评分要考虑维度间的依赖但不知道这个依赖关系定义在NvcDependencyGraph里可能会去改NvcScoreService的打分逻辑而不是调整依赖图。改进后每个 Skill 的主文件直接列出关键入口的完整代码路径引用文件中的每条业务规则都标注了对应的代码位置。round-pipeline.md里写的不是评分前要检查依赖关系而是1. 调 NvcDependencyGraph.resolveDependentDimensions(currentDim) 获取前置维度 2. 检查前置维度的评分是否已产出未产出则标记为降权 3. 调 ObservationAgent / FeelingAgent / NeedAgent / RequestAgent 分别评分 4. 调 SummaryAgent 汇总生成结构化评分结果 5. 成功后写入 practice_turn 表失败则回滚本轮状态Agent 读完这层不需要猜直接知道改哪里。防腐化让知识不会越用越错过时的知识比没有知识更危险。当 Skill 文件说评分流程是 A→B→C而实际代码已经改成了 A→CAgent 会按错误流程写代码而且不会怀疑知识的正确性。使用时反向校验Agent 读完 Skill 再去看实际代码时天然处于一个能交叉比对的位置。发现知识描述和代码现状不一致就在输出方案的同时标注差异。几乎没有额外成本——Agent 本来就要读知识、读代码比对只是顺带完成。变更时同步更新nvc-change-playbook的主文件里有一条硬约束改完评分逻辑或场景配置后必须检查对应的 Skill 文件是否需要同步更新。nvc-scoring-engine的 scripts 目录下放一个extract_agent_contracts.py从评分 Agent 的代码中提取输出字段契约。跑一次脚本生成的契约如果和 Skill 里写的不同说明知识该更新了。这套机制不是为了自动化而是为了在知识和代码之间建一个一致性锚点。实操建议从一个模块开始。NVC Practice 的 7 个 Skill 不需要一次性写完。先写练习域最复杂的那个跑通几个真实需求后再扩展到评分引擎、场景域、画像域。触发描述比正文更重要。元数据里的description字段决定了 Agent 会不会读这个 Skill。写得太窄会漏触发写得太宽会误触发。做法是把触发条件写成需求命中某个 API 路径或涉及某个业务动作同时附带反面条件。统一流程命名。每段最小划分的业务逻辑确定唯一的流程名词。“单轮对话评分在练习域、评分引擎域、变更剧本里都叫同一个名字不叫轮次评估也不叫维度打分”。跨模块推理时 Agent 靠名字匹配关联关系命名不统一直接导致推理断裂。把约束写成人话。比起评分维度间存在依赖关系需在评分前解析依赖图如果用户没说感受需要和请求的分数要往下降更容易被 Agent 正确执行。口语化的约束比格式化的规范文档更有效。脚本是校验锚点不是替代品。自动生成的索引文件用来和手工维护的知识交叉比对。两者不一致以代码为准更新 Skill。写在最后NVC Practice 的 7 个 Skill、约 30 个引用文件回答的是一个问题如果明天换一个完全不了解这个项目的人或 Agent来改代码他需要知道什么才能不犯错答案不在代码注释里不在 wiki 里也不在每次口头交接的记忆里。答案在仓库里在 Agent 每次动手前能读到的那份结构化知识里。