封面来源:由 封面生成器 制作

除特殊说明外,本文使用的 AI Agent + LLM 组合为 Codex + GPT 5.5

0. 前言

我接触 AI 编码的时间不长,2025 年底开始初步使用,2026 年 5 月初才在工作中大规模使用。与那些早就付费上班、紧跟 AI 浪潮的技术先锋相比,我在他们眼里就是个「青沟子娃儿」。这不是一篇最佳实践,文中使用的实践案例或许有更好的处理方式,这更像是一次分享与交流,向你们讲述我在使用 AI 过程中的所见所得。

1. 快的代价

AI Coding 发展过程中有一种「越快越好」的论调:

  • 一句话需求生成出代码
  • 让 AI 一次性生成整个项目
  • 能自动的事绝对不手动

那么代价呢?

  • 代码结构混乱,边界模糊,改一处牵一发动全身
  • 改一个 BUG 引入三个新 BUG
  • 代码加速腐化,技术债指数级增长
  • 没有人 Review,设计决策交给 AI,做出一堆没人用的功能
  • AI 产出各种小毛病,当察觉到痛苦时,一切都晚了
  • 代码量暴增但质量失控,形成「纸牌屋代码」,感觉良好,但一跑就崩

Cedric Chin(塞德里克·陈)认为人们对待 AI 有 三种态度

  • never AI:认为 AI 毫无用处
  • pragmatic engineer:认为 AI 是一个有用的工具,但仍需要遵循传统工程实践的人来控制
  • software dark factory:认为 AI 无所不能,鼓吹由 AI 来控制 AI、无需人类干预所带来的极致交付速度

将这三种态度划分为不同的派别,我认为可以是:

  • 反对派
  • 保守派
  • 激进派

毋庸置疑,我是坚定的保守派。我承认 AI 确实很强,但应该先减减速,遵循文档优先、人工复核、小步迭代,把 AI 当成打字很快但判断力不足的搭子。

2. 自由发挥的 AI

冰冻三尺非一日之寒,我也并不是从第一天开始就是坚定的保守派。

2026 年 1 月 25 日 14:59,我开始了我的 AI Coding 初体验。之所以记得这么准确,是因为我当时特意记录了开始时间,准备写一篇使用体验。后来懒狗附身,文章没写成,截图倒是留了下来。

2026-01-25-14-59

看着 AI 厂商在 PPT 和 Demo 演示里吹得那么天花乱坠,我自己也想体验下只用几句话就完成需求的感觉,然而自己正儿八经上手后才发现 AI 的输出连差强人意的地步都达不到,验证了一轮又一轮,纠错了一遍又一遍,到晚上 9 点,AI 总算输出了效果还不错的内容。

我突然意识到,AI 厂商会使用各种参差不齐的材料去训练 AI,那输出的内容多半也是参差不齐的。不对 AI 进行约束,就想达到期望的效果,其做法无异于在牌堆里抽出一张自己想要的牌。

对 AI 进行约束,让 AI 的输出符合自己的要求,这或许是未来开发工程师的主要工作内容。

要想知道如何约束 AI,我们先看看自由发挥的 AI 会是什么样子。

2.1 自以为是的理解

一句话实现需求不禁让人发出「Code is cheap」的感叹,但那只是 AI 厂商在发布会上宣传的手段。

你以为 AI 很懂,一切尽在不言中,但它不是你肚子里的蛔虫,它并不懂;它不懂也不会告诉你,它会根据它理解的相似性去猜,猜对了是运气,猜不对是常态。

真正的危险不是猜错,而是它猜得理直气壮,输出的内容那么笃定、完整、像模像样。

我的案例

曾经让 AI 分析某个模块的功能,结果它钻入另一个模块里一顿分析,最后输出的结果不说八九不离十,至少也是毫不沾边。

那它为什么会去分析另一个模块呢?

可能是 AI 觉得这两个模块很有相似性吧,就像老婆和老婆饼、Java 和 JavaScript 一样,不说沾亲带故,至少也是八竿子打不着。

2.2 不尽如意的实现

「能跑」不等于「写得好」:

  • 编码风格不统一
  • 出现数千行的上帝类(God Class,只有上帝才看得懂的类)或动辄百行的函数(或方法),违背单一职责(SRP,Single Responsibility Principle)
  • 重复「造轮子」,不知道代码复用
  • 性能、安全考虑不周
  • 过度设计(强行套用设计模式,疯狂打日志…),自发扩大修改范围

以前,一份代码可能要经过好几个迭代才会变成「屎山」,但现在不一样了,AI 会在一天之内输出远超人类理解范围的内容。愚公移山需要「子子孙孙无穷匮也」,而 AI 不出一天就能制造出太行、王屋级别的「屎山」。

只要胆子大,母猪放产假;只要胆子大,AI 代码全上架。

祈祷那些代码别在某个深夜对你大喊一声「Surprise, Motherfxxker!」,到时候究竟是代码能跑,还是人能跑。🤣

为什么会这样

  • AI 缺乏全局视角和架构审美。它是逐 token 生成的,可以理解为每一次输出是局部最优,而不是全局最优
  • AI 在训练集里学到的是「要把事情做全」。它在训练时看到的「好代码」往往是完整的、功能齐全的,所以它的默认行为就是「做多」而不是「做精」

我的案例

曾经让 AI 写一个 Spring 组件,它很「聪明」,知道使用 Spring 官方推荐的构造器注入,但只聪明了这一会儿。

那个组件是一个综合性组件,里面依赖了数十个其他组件,使用构造器注入后,形成了一个几十行的构造方法。这还没完,AI 为了编写单元测试,又根据这一个构造方法衍生出好多个只包含若干个参数的构造方法,最终这个类里前面 100 多行全是构造方法。

当 AI 把这个组件定义好在其他组件里使用时,它又不知道要注入了,它居然通过方法参数的形式将这个组件传入到其他组件里。 🤦‍♂️

2.3 顾此失彼的注意

AI 的上下文窗口是有限的。当需求涉及多个模块、多个文件时,又或者总是在一个会话窗口里解决所有问题时,上下文窗口会逐渐被占满,此时 AI 在修改 A 模块的代码时,可能完全忘了 B 模块的约束条件。

基于这个问题,许多 AI Agent 工具都提供了自动压缩和手动压缩的能力。

那压缩上下文就能解决上下文窗口有限的问题吗?

以 Codex CLI 为例,手动调用 /Compact 指令对现有上下文进行压缩后,会出现一行黄字:

⚠ Heads up: Long threads and multiple compactions can cause the model to be less accurate. Start a new thread when possible to keep threads small and targeted.

它告诉我们长会话或多次进行上下文压缩后可能导致 LLM 的准确性降低,要多新开会话,保证会话内容短小、处理目标明确。

那只要保持上下文窗口余量充足就可以保证 AI 一定遵守规则了?

AI 对规则的遵守是概率性的,不是确定性的 —— 只是在长对话中,这个概率会越来越低。

AI 有时还会出现 U 型注意力的现象,这在 LLM 研究里叫 Lost in the Middle。即使上下文窗口还没满,AI 的注意力分配也是不均匀的。它对上下文 开头(系统提示词、初始规则)和 结尾(最近几轮对话)的关注度高,但对 中间 部分的关注度最低。这意味着你在对话中期强调过的约束条件,恰好处在 AI 最容易忽略的位置。你以为它记住了,其实它根本没注意到。

这种注意力的不均匀最直观的体现就是规则遵守率的下降。比如你定义了 Skill,内部写了清晰的规则,AI 成功触发这个 Skill,还点头称是,然后落在执行层面就瞎搞,你发现之后纠正它,它立马道歉,运气好就到此为止,运气不好它换个方式继续犯同样的错误。

2.4 飘忽不定的输出

相同的提示词,不同时间段生成的代码可能完全不同。同一个需求,在不同的会话里,AI 可能选择完全不同的实现方案。

这不是上下文衰减的问题,其本质是 AI 的输出是概率性的,不是确定的。你不能像依赖编译器一样依赖 AI,相同的输入给编译器后永远输出相同的内容,但 AI 不能保证。

还有一种更夸张的问题 —— 模型升级,原来辛辛苦苦调好的 Skill 在模型更新后可能突然失效,工作流、提示词都没变,但 AI 的行为变了,这找谁说理去?

2.5 言听计从的执行

AI 不会说「不」。

你给它一个矛盾的需求,它不会指出矛盾,而是试图两个都满足,结果两个都没做好。你给它一个不合理的技术方案,它不会说「这个方案有问题」,它会照做,如果你纠正它,它会连忙道歉,就算你的纠正还有问题,它也会称赞你是多么正确。

本质上,AI 没有独立的工程判断力,它是一个顶级的 Yes Man。如果使用者本身判断力不够,AI 不会成为安全网,反而会成为错误的放大器。

2.6 浅尝辄止的推理

现在 AI Agent 的可用性已经非常强了,它能在写代码的同时去读代码、跑测试、看日志,如果出现问题,循环执行「读 - 写 - 测」步骤,直到完成用户指定的目标。

这种处理机制带给了我们一种假象 —— AI 很擅长推理。

AI 推理的最大问题是难以发现问题的本质,它在找到表面现象后就直接开始修。

比如出现空指针后,AI 的第一反应是加非空判断,这确实会让代码不再报错,但 AI 解决的是空指针这个现象,而不是「为什么会是空」的本质,更要命的是这还会让问题变得更加隐蔽,空指针是没了,但错误数据依旧在系统里流转,等到它真正爆发时,用户的投诉也接踵而至,而你已经找不到源头了。

AI 擅长解决症状,但不擅长定位病因。它不会给你说「修不好」,而让你产生「修好了」的错觉。

3. 规范驱动开发

自由发挥的 AI 并不可控。要想让它的输出 相对稳定,就必须让 AI 知道什么该做、什么不该做、该做的又应该怎么做得更好。

这正是规范驱动开发要解决的问题。

规范驱动开发,即 Spec-Driven Development(这里的 Spec 指的是 Specification),简称 SDD。

传统开发中,工程师遵循「先看文档,再写代码」的流程,核心工作是「编写代码(Coding)」。根据 SDD 的核心逻辑,在 AI Coding 时代,代码实现由 AI 完成,工程师的核心工作变成了「编写和维护规范(Specifying)」,通过制定一系列规范,指挥 AI 该怎么写代码。

由于 AI 本质是无状态的,没有时间感和项目记忆:

  • 不知道我们昨天决定了什么
  • 不知道这个模块上周已经重构过,后续不要再动它
  • 不知道这个方案已经试过并且失败了

AI 的所有记忆都是「外挂」给它的,文档是最常见的表现形式。

写进 Spec 的决定,是「已经想清楚,不再做第二次」的决定,它能够跨越 AI 会话的边界,被 AI 持续遵守。

AI 时代,SDD 的核心工作流是:

  1. 制定 Spec:开发者使用自然语言编写出规格说明书,明确需求功能、边界、约束条件等信息;
  2. AI 解析与对齐:Agent 读取 Spec,理解上下文、系统架构和开发规范,AI 与开发者完成双向确认,避免 AI 的理解偏差;
  3. 实现与测试:根据二次确认后的 Spec,AI 自动完成代码编写和单元测试;
  4. 迭代与演进:需求变更时,开发者先修改 Spec,AI 再根据修改后的 Spec 去修改代码。

4. Matt Pocock Skills

本节基于 v1.1.0 版本编写,注意不同版本之间的差异

4.1 是什么

mattpocock/skills 是由 Matt Pocock(马特·波考克)在 GitHub 上开源的一系列 Skill,其定位是「Skills For Real Engineers」,为真正的工程师准备的 Skills。

Matt Pocock 是 TypeScript 社区的知名开发者,其本职工作是卖课…

果然,计算机行业里最好赚的钱是程序员的钱。🤣

不评价 Matt 的课程质量怎么样,毕竟他是教 TypeScript,我不是他课程的目标用户,但他开源的这套 Skill 还是很不错的,目前在 GitHub 上已经狂揽 150k+ 的 Star,其中的 grill-me 曾在 SKILLS 上霸榜多次,甚至成为 AI Agent 工具(比如 Google 的 Antigravity)的内置指令。

与 GSD、BMAD、Spec-Kit 等工具相比,Matt 的这套 Skill 被设计得「小巧、易改、可组合」,它内部包含众多 Skill,但不要求使用者全部安装,而是按需挑选,并把它们融入到个人工作流。

4.2 为什么用

Matt Pocock 构建这些 Skill 是为了解决他在使用 Claude Code、Codex 等 AI Agent 时反复遇到的几个问题:

  1. The Agent Didn’t Do What I Want:Agent 没有按我的要求去做
  2. The Agent Is Way Too Verbose:智能体太啰嗦了
  3. The Code Doesn’t Work:代码无法运行
  4. We Built A Ball Of Mud:我们构建了一个大泥球

The Agent Didn’t Do What I Want

David Thomas(大卫·托马斯)和 Andrew Hunt(安德鲁·亨特)在 《程序员修炼之道》 里说:

1
2
原:No-one knows exactly what they want.
译:没有人准确地知道自己想要什么。

传统软件开发中最常见的问题是需求返工,本质是需求没对齐。

AI Coding 时代同样存在这个问题,更为常见的是在开发人员和 AI 之间产生。

如果想让 AI 实现某个需求,最常见的模式是直接告诉 AI 做什么,此时的主体是人类。由于 AI 不会追问,如果需求中有不清楚的地方,AI 就会用它自己的理解去实现。每个不清楚的小点都这么去做,最终做出来的东西往往就会偏离预期。

这问题的本质是人类不知道 AI 是怎么想的,或者说 AI 没有告诉人类它有哪些疑惑。

要想解决这个问题,就要让人类与 AI 交换主客体,让 AI 作为主体,让 AI 根据初始需求对人类进行拷问,直到对需求的理解完全清晰。

可以使用 Matt 提供的以下两个 Skill:

  1. $grill-me:非编码场景下使用
  2. $grill-with-docs:编码场景下使用,是对 $grill-me 的增强

The Agent Is Way Too Verbose

Eric Evans(埃里克·埃文斯)在 《领域驱动设计》 里说:

1
2
原:With a ubiquitous language, conversations among developers and expressions of the code are all derived from the same domain model.
译:借助统一语言,开发者之间的对话以及代码中的表达都源自同一个领域模型。

实际项目中通常都有一些非通用的项目术语,而 AI 往往无法准确理解这些术语。

要想让 AI 理解项目中「老婆」和「老婆饼」之间的区别和准确含义,可以建立一套统一语言,在项目里维护一份领域词汇表(后文简称术语表),让 AI 先读取术语表再开始推理和工作。

使用共享语言不仅可以减少冗长,还可以:

  • 变量、函数和文件里都统一命名
  • AI 更容易理解代码
  • AI 在思考上消耗的 token 也更少

Matt 使用 CONTEXT.md 文件维护术语表:

  • $grill-with-docs:拷问过程中同步更新术语表和 ADR
  • $domain-modeling$grill-with-docs 的底层依赖,可以在非拷问场景下自动维护术语表和 ADR

ADR 是 Architecture Decision Record 的缩写,即「架构决策记录」,记录有长期影响的技术决策和取舍理由。

The Code Doesn’t Work

David Thomas(大卫·托马斯)和 Andrew Hunt(安德鲁·亨特)在 《程序员修炼之道》 里又说:

1
2
原:Always take small, deliberate steps. The rate of feedback is your speed limit. Never take on a task that's too big.
译:始终采取小而慎重的步骤。反馈的速度就是你的速度限制。永远不要承担太大的任务

需求已经对齐,但 AI 产出的代码质量依然可能很差,其根本原因是缺少反馈循环(Feedback Loops),让 AI 一边写,一边测。

Matt 维护了几个 Skill 针对代码实现和问题诊断场景:

  • $implement:拿一张 Ticket 完成实现、验证和收口,会在适合时使用 TDD 并进行 code review
  • $tdd:测试驱动开发,提倡「红 - 绿 - 重构」循环(先写失败的测试,再让测试通过)
  • $diagnosing-bugs:排查复杂 BUG 或进行性能回归

我不会单独把 $tdd 作为主流程阶段。后端业务场景复杂,外部调用多,直接使用 $tdd 不一定方便,它更适合作为 $implement 在合适场景下调用的内部实践。

TDD 是 Test-Driven Development 的缩写,即「测试驱动开发」,先编写失败的测试,再实现代码让测试通过,并在通过后重构。

We Built A Ball Of Mud

Kent Beck(肯特·贝克)在 《解析极限编程》 里说:

1
2
原:Invest in the design of the system *every day*.
译:每天都要投入系统设计。

John Ousterhout(约翰·奥斯特豪特)在 《软件设计的哲学》 里说:

1
2
原:The best modules are deep. They allow a lot of functionality to be accessed through a simple interface.
译:最好的模块是深的。它们让大量功能可以通过简单的接口访问。

AI 以远超人类理解能力的速度生产代码,代码库的复杂度也以前所未有的速度膨胀。

Matt 认为,解决这个问题不能只靠实现阶段补救,还要在规划阶段把边界、测试切入点和架构决策写清楚,因此设计了这些 Skill:

  • $to-spec:在创建 Spec 前,询问涉及到哪些模块
  • $improve-codebase-architecture:改进代码库的架构,Matt 建议隔几天运行一次

总结

Matt 强调 软件工程的基本功比以往任何时候都更重要(Software engineering fundamentals matter more than ever),这套 Skill 正是他把这些基本功沉淀为可重复执行实践的结果。

4.3 怎么装

安装

终端执行以下命令:

1
npx skills@latest add mattpocock/skills

选择需要安装的mattpocock-skill

使用 ↑↓ 进行选择,使用空格选中需要安装的 Skill,可以多选,选择完毕后回车,安装所选 Skill。

我安装的 Skill 有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- codebase-design
- code-review
- diagnosing-bugs
- domain-modeling
- edit-article
- grilling
- grill-me
- grill-with-docs
- handoff
- implement
- setup-matt-pocock-skills
- tdd
- to-spec
- to-tickets
- writing-great-skills

部分 Skill 之间有依赖关系,务必一并安装:

  • grill-megrill-with-docs 底层都依赖 grilling
  • grill-with-docs 底层还依赖 domain-modeling
  • improve-codebase-architecture 底层依赖 codebase-designdomain-modeling
  • implement 依赖 tddcode-review

Matt 仍在对这些 Skill 进行优化,未来可能会新增其他依赖,实际使用时最好先阅读下目标 Skill(让 AI 读),避免没有安装依赖 Skill。

在 2026 年 6 月 17 日,mattpocock/skills 发布了 1.0.0 版本,在这个版本里新增了 ask-matt,这是一个路由类型的 Skill,如果你不熟悉 Matt 这套 Skill 该怎么用,就装上 ask-matt 并显式调用,它会告诉你的。

安装的 Skill 默认添加到用户目录中的 .agents/skills/ 目录下,常用 AI Agent(比如 Codex、Antigravity)都支持加载这个位置的 Skill,如果使用的是其他 Agent(比如 Claude Code、CodeBuddy),需要进行额外安装:

选择mattpocock-skills的安装路径

然后回车。

接下来选择安装范围,选择 Global,再回车。

最后执行安装,等待安装完成。

卸载

终端执行以下命令:

1
npx skills remove <skill-name> -g

<skill-name> 替换为实际安装的具体 Skill名,如 grill-me

4.4 怎么用

使用下面这些 Matt Skill 可以构成一个 AI Coding 最小工作流:

%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Microsoft YaHei, Arial", "primaryColor": "#F8FAFC", "primaryTextColor": "#0F172A", "primaryBorderColor": "#CBD5E1", "lineColor": "#94A3B8"}}}%%
flowchart LR
    subgraph init["一次性初始化"]
        setup(["setup-matt-pocock-skills"])
    end

    subgraph planning["需求规划与拆解"]
        direction LR
        grill(["grill-with-docs"])
        spec(["to-spec"])
        tickets(["to-tickets"])
    end

    subgraph implement["逐个 Ticket 实现"]
        direction LR
        impl["implement"]
        done["Ticket 完成"]
    end

    setup --> grill
    grill --> spec --> tickets
    tickets --> impl
    impl --> done
    done -.->|下一个 Ticket| impl

    classDef initNode fill:#F1F5F9,stroke:#94A3B8,color:#334155,stroke-width:1.5px;
    classDef main fill:#EFF6FF,stroke:#3B82F6,color:#172554,stroke-width:1.5px;
    classDef loop fill:#F0FDF4,stroke:#10B981,color:#14532D,stroke-width:1.5px;
    classDef finish fill:#FFF7ED,stroke:#F59E0B,color:#7C2D12,stroke-width:1.5px;
    class setup initNode;
    class grill,spec,tickets main;
    class impl loop;
    class done finish;
    style init fill:#F8FAFC,stroke:#64748B,stroke-width:1px;
    style planning fill:#F8FAFC,stroke:#64748B,stroke-width:1.5px;
    style implement fill:#F8FAFC,stroke:#64748B,stroke-width:1.5px;

步骤一:使用 setup-matt-pocock-skills 初始化项目

首次在项目里使用 Matt Skill 时 务必先执行 $setup-matt-pocock-skills 进行初始化,它会:

  • 设置 Ticket 的发布位置,是写入本地 tickets.md,还是发布到 GitHub Issues、Linear 等真实 Issue Tracker?我的项目没有使用 Issue Tracker,因此我选择把 Ticket 放在本地 Markdown 文件里
  • 定义 Ticket 标签,比如 ready-for-agentneeds-review,后续的 $to-tickets$triage 会依赖这个定义。如果你觉得英文不够明确,可以要求 AI 使用中文表达
  • 明确术语表和 ADR 的存放位置

步骤二:使用 grill-with-docs 进行需求澄清

$grill-with-docs 融合了 $grill-me$domain-modeling

使用这个 Skill 后,AI 会基于当前项目文档(术语表、ADR)对需求进行逐项追问,直到需求明确,同时还会将追问过程中出现的新术语和 ADR 补充到文档里。

需求越模糊,追问过程就越长。对于那种一句话需求,这个追问说不定要进行几十、上百次才完成。

为了减少追问次数,将追问内容聚焦于真正模糊的内容,我通常会先编写一个需求草案,然后让 AI 根据这份草案进行追问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 1. 需求概括

用一两句话概括需求。

# 2. 设计草案

围绕需求概括说明当前能想到的实现方向:

- 要做什么事,准备怎么做;
- 需求入口在哪里;
- 影响范围和边界在哪里。

# 3. 可供参考的实现细节

当前项目中可能有一些 AI 无法知道的业务细节或实现细节,在这里用自问自答方式补充。

> 问题 1

回答 1

> 问题 2

回答 2

# 4. 注意事项

1. 当前需求只针对 xxx 模块,不涉及其他模块。
2. **绝对不能** 影响已经存在的功能,**必须保持** 现有功能的正确性。

步骤三:使用 to-spec 生成详细 Spec

在使用 $grill-with-docs 对需求澄清完毕后,使用 $to-spec 结合当前会话内容生成 Spec。此时 AI 不会再对用户进行询问,只压缩汇总先前追问的内容。

由于先前我已经创建了一份需求草案,因此我通常会让 $to-spec 把澄清后的需求补充到需求草案里,形成完整的 Spec,提交代码时也一并提交 Spec。

步骤四:使用 to-tickets 拆分 Ticket

如果 Spec 很小,可以直接交给 AI 实现;但 Spec 较大时,不能直接开工,需要使用 $to-tickets 对 Spec 进行垂直拆分,形成一个个包含依赖关系、可独立实现的 Ticket(可简单理解成把「大 Spec」拆成「小 Spec」)。

此时每个 Ticket 都是一个原子任务,这么做有几个好处:

  • 缩小 AI 的单次实现范围
  • 减少上下文污染
  • 每个 Ticket 可单独实现,更容易验证,出错后也更容易回滚
  • 支持并行实现每个 Ticket

步骤五:实现每个原子任务

$implement 能够以 TDD 的方式完成编码任务,依赖 $tdd$code-review 两个 Skill。

如果任务无法以 TDD 的形式开展,或者按 TDD 实现太繁琐,也可以使用其他编码 Skill 来实现,甚至还可以直接口述让 AI 实现,总之,适合自己的就是最好的。

在未使用 $implement,或者实现步骤中不包含 Code Review 时,记得让 AI 对改动的代码进行 Review,如果有阻塞问题,让 AI 再改,改完后继续 Review,直到不出现阻塞问题。

进行 Code Review 时,尽量新开会话窗口,或使用 subagents 完成。

Skill 工作流串联

前面几个步骤里使用到的 Skill 也是我日常开发中经常使用的 Skill,并且我还在这条流水线的基础上加入了一些自己编写的 Skill,最终构成了一条完整的、符合我个人开发习惯的 AI Coding 工作流。

5. 文档驱动工作流

5.1 建立检索文档

我目前维护的是一个大型项目,它:

  • 代码规模庞大
  • 模块划分细密
  • 业务规则复杂
  • 历史逻辑厚重
  • 变更影响面广
  • 术语体系庞杂
  • 隐性约定密布

使用 AI 从零开始写一个项目很容易,但如何在已有大型项目里使用 AI 呢?尤其是如何让 AI 准确理解输入的提示词更是重中之重。

这是我在接触 AI Coding 后面对的第一个问题。

我观察到:当我把提示词发送给 Codex 后,Codex 会对提示词进行拆分,找到其中的关键词,再使用 rg 命令在代码库里检索这些关键词及语义相近的内容。

由于项目里包含大量自创术语,AI 的理解难免会出现偏差。这就导致前置搜索极其不稳定,同时后续推理严重依赖这份搜索结果,最终导致一步错,步步错。

如何让 AI 实现「精准打击」,不要让 AI 根据提示词按训练语义进行推测呢?

AI 不知道从哪里入手,因此就只能先进行推测,然后再搜索。开发人员作为模块维护者,知道该从哪里入手。先给 AI 一个业务代码入口,让它从这个入口出发,熟悉这块业务,然后输出一份「AI 检索文档」,内部包含准确的检索词、关键结论、关联链路等内容。后续需要 AI 对这块业务进行调整时,先让 AI 阅读这份检索文档,实现精准打击,提高检索效率和稳定性,一定程度上还能减少 token 的消耗。

为什么是「AI 检索文档」,而不是「代码说明文档」呢?

  • 当业务达到一定复杂度时,代码说明文档的内容将非常多,不仅难以维护,而且读取代码说明文档消耗的 token 和上下文搞不好比直接搜索还多
  • 除非强制规定,AI 基本不会老老实实只读代码说明文档,它仍然会去阅读代码,这又进一步加剧 token 和上下文窗口的消耗
  • 相比于对文字内容的理解,AI 更擅长阅读结构清晰的内容,而代码天生具备这样的优势

因此,我自定义了一个名为 ai-retrieval-docs 的 Skill 来解决这个问题,并支持在代码变更后自动更新对应的 AI 检索文档。

5.2 按需加载文档

光有检索文档还不够。

如果只为大型项目抽取出一份检索文档,其内容也会有很多。这时所有检索信息都混在一份文档里,当 AI 要对特定业务进行检索时,会加载一些不必要的信息。

很容易想到将一份大而全的文档拆分为多个子文档。

存在多个子文档时,又应该怎么正确加载对应业务的文档呢?

如果能像加载 Skill 一样,渐进式加载必要的上下文信息就好了。

于是设计出 load-project-context Skill。使用后,AI 从项目入口出发,沿文档路由加载当前任务真正需要的领域术语和检索文档。

以一个经过 Matt Skill 初始化的多上下文项目为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/
├── AGENTS.md                                  
├── CONTEXT-MAP.md                             # 多上下文地图,按任务选择相关领域
├── docs/
│   ├── agents/
│   │   └── domain.md                         # Matt Skill 生成的领域文档消费规则
│   └── adr/                                   # 跨领域架构决策
└── src/
    ├── order/
    │   ├── CONTEXT.md                         # 稳定领域词汇、概念边界和关系
    │   └── docs/
    │       ├── 00-AI上下文入口.md              # 当前领域的能力路由
    │       ├── order-validation/
    │       │   ├── 00-AI上下文入口.md          # 文档较多时继续按专题分流
    │       │   ├── 10-订单校验-AI检索说明.md
    │       │   └── 20-状态流转-AI检索说明.md
    │       └── price-calculation/
    │           └── 10-价格计算-AI检索说明.md
    └── billing/
        ├── CONTEXT.md
        └── docs/
            └── 00-AI上下文入口.md

其中:

  • docs/agents/domain.mdCONTEXT-MAP.mdCONTEXT.md 由 Matt Skill 维护,使用 $setup-matt-pocock-skills 后就会生成这些内容,主要用于维护项目术语表和 ADR
  • ***-AI上下文入口.md***-AI检索说明.mdai-retrieval-docs 维护,为 AI Agent 提供稳定的检索信息

由于 AI Agent 总是会 读取项目根目录下的 AGENTS.md,把它作为切入点,逐步引导 AI 补全上下文:

%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Microsoft YaHei, Arial", "primaryColor": "#F8FAFC", "primaryTextColor": "#0F172A", "primaryBorderColor": "#CBD5E1", "lineColor": "#94A3B8"}}}%%
flowchart LR
    agents(["AGENTS.md"])
    domain["domain.md"]
    map["CONTEXT-MAP.md"]
    context["CONTEXT.md"]
    entry["AI 上下文入口"]
    retrieval["AI 检索说明"]
    code(["具体代码"])

    agents --> domain
    domain -->|多上下文| map --> context
    domain -->|单上下文| context
    context --> entry
    entry --> retrieval
    retrieval --> code

    classDef agentEntry fill:#F1F5F9,stroke:#94A3B8,color:#334155,stroke-width:1.5px;
    classDef mattSkill fill:#EFF6FF,stroke:#3B82F6,color:#172554,stroke-width:1.5px;
    classDef retrievalDocs fill:#F0FDF4,stroke:#10B981,color:#14532D,stroke-width:1.5px;
    classDef actualCode fill:#FFF7ED,stroke:#F59E0B,color:#7C2D12,stroke-width:1.5px;
    class agents agentEntry;
    class domain,map,context mattSkill;
    class entry,retrieval retrievalDocs;
    class code actualCode;

每进入一层目录后,先读取文件列表,然后定位入口文档,阅读入口文档后再确定接下来读哪些文件,避免无差别读取所有文档内容,直到定位最终的 AI 检索文档。

Matt Skill 让 AI Agent 理解项目术语,ai-retrieval-docs 负责维护入口路由和末端检索说明,load-project-context 则沿整条链路按需加载文档。

5.3 约束编码行为

AI Coding 全流程由「读」「写」两部分组成,前面解决的都是怎么精准地读,那怎么让 AI 优秀地写呢?

Andrej Karpathy(安德烈·卡帕西),前 OpenAI 联合创始人(现已加入 A ➗),「Vibe Coding」「LLM Wiki」都是他提出的概念。

Andrej 在社交媒体上分享过许多 LLM 编码经验,后经人整理后得到 andrej-karpathy-skills,规范 AI 在编码时:

  • 不擅自假设,不掩盖困惑,把取舍讲清楚
  • 只写解决问题所需的最少代码,不做任何预设性扩展
  • 只改必须改的内容,只清理 AI 自身造成的问题
  • 先定义成功标准,再循环推进,直到验证通过

原版 Skill 更适合在英文环境下的 Claude Code 中工作,我将其翻译为中文,并结合 Codex 的机制进行改写,形成 coding-guidelines,希望随着模型和 Agent 能力的提升,未来能够废弃这个 Skill。

这个 Skill 可以作为通用编码技能,在编程场景的细分领域下,可以再搜索或自定义一些符合自己要求的 Skill。

5.4 串联完整流程

以 Matt 工作流为主体,再融合一些符合个人开发习惯的步骤,最终形成了一套以文档驱动的需求开发工作流,我将其简单命名为 feat 工作流。

%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Microsoft YaHei, Arial", "primaryColor": "#F8FAFC", "primaryTextColor": "#0F172A", "primaryBorderColor": "#CBD5E1", "lineColor": "#94A3B8"}}}%%
flowchart TD
    A(["人工需求拆分"])
    B(["需求草稿"])
    C(["需求澄清"])
    D(["需求完善"])
    E{{"Feature DoR"}}
    F(["Ticket 拆分"])
    G{{"Ticket DoR"}}
    H(["实现 / 修复 Ticket"])
    I(["Review Ticket"])
    J{"Review 通过?"}
    M{{"Ticket DoD"}}
    K{"还有 Ticket?"}

    L(["归档"])

    A --> B --> C --> D --> E
    E -->|否| D
    E -->|是| F --> G
    G -->|否| F
    G -->|是| H --> I --> J
    J -->|否| H
    J -->|是| M
    M -->|否| H
    M -->|是| K
    K -->|否| L
    K -->|是| G


    classDef design fill:#EFF6FF,stroke:#3B82F6,color:#172554,stroke-width:1.5px;
    classDef implementation fill:#F0FDF4,stroke:#10B981,color:#14532D,stroke-width:1.5px;
    classDef archive fill:#FFF7ED,stroke:#F59E0B,color:#7C2D12,stroke-width:1.5px;
    classDef unintroducedDesign fill:#EFF6FF,stroke:#EF4444,color:#172554,stroke-width:1.5px,stroke-dasharray:6 3;
    classDef unintroducedImpl fill:#F0FDF4,stroke:#EF4444,color:#14532D,stroke-width:1.5px,stroke-dasharray:6 3;
    class B,C,D,F design;
    class A,E,G unintroducedDesign;
    class H,I,J,K implementation;
    class M unintroducedImpl;
    class L archive;

在这个流程图里出现了几个在前文中没有介绍的内容:

  1. 人工需求拆分
  2. DoR 和 DoD

人工需求拆分

接到一个新需求后,一定要先 人工 理解这个需求的内容,尤其是要判断这个需求是不是大型需求。

如果是大型需求,必须先由人工将其拆分为可交给 AI 完成的中小型需求。

后面不是还有「Ticket 拆分」步骤吗,为什么还要先进行「人工需求拆分」呢?

如果不进行拆分,交付给 AI 一个大型需求,「需求草稿」阶段要手写多少内容,后续「需求澄清」步骤又要进行多少轮澄清。每进行一轮澄清都会消耗一定的上下文窗口,大型需求会消耗更多的上下文窗口,后续再让 AI 对需求进行完善时就可能出偏差,每叠加一点偏差,最终的实现效果就会与期望严重偏离。

「人工需求拆分」往往依赖开发人员的经验,如何把需求大小拆得刚刚好,是 AI 在后续能否出色完成任务的关键。未来程序员的核心能力或许是「需求拆分」与「需求澄清」。

DoR 与 DoD

DoR 是 Definition of Ready 的缩写,即「就绪定义」,人工 确认当前阶段的产出是否具备进入下一阶段的条件:

  • Feature DoR:检查目标、非目标和验收标准是否明确,不明确就不能拆 Ticket
  • Ticket DoR:检查 Ticket 的实现边界、前置依赖和验收方式是否明确,不明确就不能开始编码

DoD 是 Definition of Done 的缩写,即「完成定义」,人工 检查 Ticket 是否真正完成,包括代码实现、测试验证和文档同步等。

简单来说,每当 AI 完成一个阶段,都要 人工 检查产出是否符合要求,只有通过检查,才能继续向前推进。

实践建议

  • 人工拆分需求后,可以使用 git worktree 命令新建工作区,这在并行开发与热修的场景下很有帮助
  • 针对每个 Ticket,应该新开会话进行实现,避免上下文腐化
  • 每完成一个 Ticket 后,可以把变更提交到 Git 本地仓库,隔离每个 Ticket 的内容,不仅便于出错回滚,减少 Git 暂存区里的内容还可以让 AI 更集中地完成下一个 Ticket

5.5 应对已知问题

相比一句话实现需求,走完这套工作流需要更多的时间,但这些时间都用在限制 AI 的自由发挥,让它做得更对:

  • 自以为是的理解:AI 问,人来答,全部清楚后才开工
  • 差强人意的实现:用 coding-guidelines 约束常见的编码问题,遇到细分领域的问题再针对性处理
  • 顾此失彼的注意:人工拆分大型需求,单个窗口单个小任务,用文档跨会话传递上下文
  • 飘忽不定的输出:用 Spec 固定需求和约束,再通过 DoR、DoD 和 Review 检查 AI 的输出
  • 言听计从的执行、浅尝辄止的推理:关键节点由人工把关,需求不明确就继续澄清,没验证通过不算完成

6. Skill 的设计与迭代

6.1 设计原则

先前介绍的工作流里涉及到许多 Skill,这些 Skill 不是一次写好的,也不是一写出来就能正常工作的,你必须在日常使用过程中仔细观察 Skill 是否按要求触发、是否正确遵守约束,然后不断迭代、打磨,使其符合你的想法。

我认为一个好的 Skill 应该有以下关键特征:

明确的触发边界

清楚地定义:

  • 什么时候触发

  • 什么时候不触发

边界模糊就会造成执行模糊,该触发的时候没有触发,不该触发的时候频繁触发。

门禁机制

Skill 不是每次都从头跑到尾的脚本,它应该在关键节点设置门禁。条件不满足就停下,而不是硬着头皮继续跑

反例黑名单

只告诉 AI「该怎么做」往往不够,还要告诉它「什么绝对不能做」。

只写 AI 不知道的

不应该把 Skill 的篇幅花在 AI 已经稳定掌握的通用知识上。Skill 的价值在于用尽量少的文字,写出那些模型掌握不稳定、但对当前项目重要的规则。

文档权威边界

当 Skill 里涉及多种文档时,必须明确每种文档的职责,比如:

  • 需求文档管「应该发生什么」
  • ADR 管「为什么有这个决策」
  • 检索文档管「应该按什么高效检索」

不划清边界,AI 就会臆想,把 ADR 写成架构设计文档,把检索文档写成说明文档,不仅文档失去本身的意义,还会让 AI 更难理解,消耗更多 token。

适合自己的才是最好的

每个人的口味都不一样。你喜欢甜粽子,我喜欢咸粽子,我不能把我的口味强加给你,你也不能把你的口味硬塞给我。

包自己口味的粽子,写符合自己风格、习惯的 Skill,适合自己的才是最好的。

6.2 迭代方法

无论是 Codex,还是 Claude Code,它们都内置了一个「写 Skill」的 Skill,比如在 Codex 里叫 skill-creator

在 Matt Skill 里也有一个类似的 Skill —— writing-great-skills,可以按需安装。

之后你可以在输入框里尽情描述自己的想法,先让 AI Agent 生成一份初版 Skill。

为了优化 Skill,再推荐一个 Skill —— darwin-skill,它能够对某个 Skill 进行多维度的评分。当分数达到 85 分及以上时,可以认为这是一个优秀的 Skill。如果评分不足,就让 AI 改,然后再评分。

我通常在 Codex 里使用以下提示词对某个 Skill 进行评估:

1
启用 subagents。使用 $darwin-skill 对 xxx 这个 Skill 进行全面的、细致的、full_test 级别的评估。

7. 写在代码之外

7.1 记录胜于记忆

AI Coding 时代,文档不再只是代码的补充,而是 AI 理解目标与约束、跨会话传递上下文的主要依据。

工程师的核心工作也从编写代码变成了编写文档。

文档越准确、清晰、可追溯,AI 的实现就越稳定,人的经验也越容易沉淀和复用。

文档层的陷阱

AI Coding 时代,因疏忽而产生的代码错误大大减少,取而代之的是文档层面的错误。

如果原始文档就存在错误,那么 AI 按照文档实现的功能自然也会出错。

更糟糕的是,当尝试用 AI 来修复这类 BUG 时,AI 会优先相信那份错误的文档,在一开始就偏离方向。

当 AI 根据文档始终无法解决问题时,就该质疑文档的准确性了。

7.2 精准胜于模糊

越模糊,越失控;越清晰,才越准确:

  • 像机器一样表达:简洁、清晰,不含蓄
  • 把 AI 当蠢蛋:提供的信息越准确,AI 越容易理解
  • AI 更擅长阅读结构清晰的内容:以 Markdown 格式输入,该分点时就分点,不要把内容堆在大段文字里。如果要贴代码,单行代码用反引号 ` 括起来,多行代码用代码块的形式书写

7.3 边界决定聚焦

没有边界,AI 就只能猜,最后的输出自然不稳定。

明确该做什么、不该做什么、什么可妥协、什么不能妥协,结果才会真正聚焦。

7.4 底层决定上限

AI 输出的上限受以下两点制约:

  1. 工程师的专业能力
  2. 代码库本身的质量

AI 可能会放大工程师之间的能力差距,它能快速完成大约 70% 的工作,但剩下的 30% 往往依靠工程师的专业技能和经验。

除此之外,代码库也是 AI Agent 的重要上下文来源。

AI 会模仿已有代码。已有代码的质量越高,AI 产出的代码质量也越高,反之,AI 则是在帮助「堆屎山」。

换言之,代码架构也是 AI 的提示词。在 AI Coding 时代,优秀的架构既要让人理解,也要让 AI 理解。Code is not cheap.

7.5 验证胜于说明

AI 说完成不代表真的就完成了,AI 写的测试也不一定可靠。

真正可靠的方式是持续验证 —— 人工测试、自动化测试、集成测试…

相信可验证的结果,而不是 AI 对结果的解释。

8. 面向未来

8.1 认清方向

AI 越强,人越焦虑?AI 会不会替代我?软件工程已死?

过去,编码的成就感来自实现功能和修复问题。AI 的到来大大缩短了从想法到结果的距离,让正反馈来得更快。使用 AI 越多,我越能体会到编码的乐趣。

现在真正的工作不再只是编码本身,而是:

  • 找到可以用代码解决的问题
  • 提出方案解决这些问题
  • 验证解决方案是否有效

AI 可以承担大量编码工作,但仍然需要有人去发现问题、定义问题并确认问题得到解决。

AI 越强,这些编码之外的工作就越重要。

8.2 保持清醒

AI 确实很强,但如果长期把思考、表达和判断外包给 AI,我们可能逐渐沦为工具的奴隶:

  • 不要把写作交给 AI:写作的目标不只是写完,而是提升自己与读者的理解。每一次写作都是进入一片混沌中,再带着结构和理解出来。让 AI 替你写作,就像花钱请别人替你健身一样

  • 不要把自己降格为执行者:工具不会定义你的价值,只会揭示你真正看重的是什么。如果只把自己看作「敲代码的人」,在 AI 时代确实容易感到过时;但如果把自己看作解决问题的软件工程师,AI 的洪流只会让你更加强大

  • 主动慢下来:给自己一点时间,想清楚到底在构建什么、为什么要构建,与 AI 来回沟通的延迟远高于人类自身的思考速度,过度把人类排除在软件开发之外,反而会让开发变得更慢

8.3 拥抱变化

从 2025 年底 AI Coding 开始真正可用到现在不过半年,现在所讨论的一切都还只是早期实践。

随着科技的发展和 AI 的迭代,一句话实现需求或许真的可行。上下文窗口越来越大,模型理解能力越来越强,现在复杂的工作流到时候一次对话就能完成。人类与 AI 的交互方式不被限制在输入框里,脑机接口成为现实,一个念头,AI 就能明白你的想法。AI 不再只是纯粹的执行者,而是能主动发现问题、提出质疑的搭档。

当 AI 真能用一句话实现需求时,保守派还有存在的必要吗?

当 AI 能主动提出质疑,今天这些 AI Coding 工作流又会剩下什么?

我想,无论未来怎么样,总要有人愿意慢下来,把问题想清楚。

9. 参考链接

附录 1. Skill 优化日志

日期 里程碑
05-13 成功出色地完成一个复杂任务项
05-14 使用 $skill-creator 创建符合自己习惯的 Skill
05-18 $grill-me 融入 AI Coding 开发流程
05-19 跨多个工程完成一个复杂任务项;定义文档加载方式、Workspace 工作规则;沉淀多种个人 Skill
05-20 开始使用 mattpocock/skills 工作流
05-21 加深对 mattpocock/skills 的理解:$to-spec 生成规格文档、$to-tickets 拆分 Ticket、$handoff 交接文档
05-22 优化 $ai-retrieval-docs Skill
05-25 优化 $java-backend-code Skill;在 GitHub 新建 codex-profile 仓库备份配置
05-26 ~ 27 迭代 Skill
05-28 新增 $feat,构建个人工作流
05-29 优化 $feat 工作流决策树逻辑,引入 $write-a-skill 对 Skill 进行检查
05-30 评估是否在 $feat 工作流里引入 Git Worktree 和 subagents
06-05 引入 $zoom-out 按模块、功能生成 AI 检索文档
06-10 使用 darwin-skill 对现有 Skill 进行优化
06-25 优化 $ai-retrieval-docs 的索引化文档写作规则
07-09 对齐 mattpocock/skills v1.1.0 版本
持续优化中…

附录 2. Codex 的安装与配置

附录 2.1 如何安装

前置依赖: Codex 依赖 Node.js 环境,并且 Codex 在运行时偶尔也会运行一些 Python 脚本,请提前安装并配置好 Node.js 和 Python 环境。

以管理员身份运行一个终端,输入以下命令安装 Codex:

1
npm install -g @openai/codex

附录 2.2 在哪配置

通过 OpenAI 账号

如果你有 OpenAI 的账号,或者有美国亲戚可以帮你申请账号,那很简单,直接登录就行了。

我都没有,只有一个 API Key,所以跳过这里的配置。🤣

推荐: 通过 CC Switch

前往 CC Switch GitHub 的 Releases 页下载最新版本的 CC Switch。

Windows 版本中带有 -Portable 后缀的表示「便携版」,下载后得到的是一个压缩包,内部包含 .exe 可执行文件和 .ini 配置文件,「便携版」无法自动更新,需要更新时只能再去 Releases 页下载最新版本。

CC Switch 的配置很简单,这里不做过多介绍。

如果想让 DeepSeek、Kimi 接入 Codex,需要配置 CC Switch 的路由信息:

  1. 打开「编辑供应商」页面的「高级选项」进行配置:

    CC-Switch开启供应商的本地路由映射

  2. 进入 CC Switch 的「设置」页面,开启本地路由:

    开启CC-Switch的本地路由

通过配置文件

CC Switch 的原理就是直接修改 AI 工具的配置文件,不想额外安装 CC Switch 可以通过以下方式完成 Codex 与 API Key 的集成:

  1. Codex 安装成功后,前往用户目录下的 .codex 目录,里面有个 config.toml 文件,没有就新建一个;

  2. config.toml 文件里增加以下信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    model_provider = "custom"
    # 模型名称
    model = "gpt-5.5"
    # 推理级别
    model_reasoning_effort = "high"
    personality = "pragmatic"
    
    [model_providers.custom]
    name = "custom-api"
    base_url = "your_base_url"
    wire_api = "responses"
    env_key = "YOUR_API_KEY"
  3. 增加环境变量 YOUR_API_KEY,值为对应的 API Key

  4. 在 VS Code 里下载 Codex 插件即可使用

附录 2.3 怎么用好

提及 subagents 或子代理时出现 Reconnecting…

截止 Codex CLI 0.139.0 版本,此问题仍未解决,可以将 Codex CLI 回滚至 0.130.0

1
npm install -g @openai/codex@0.130.0

注意,就算版本回滚了,在 Codex VS Code Plugin 和 Codex App 中也还有这个问题。

2026 年 7 月 9 日:基于 0.130.0 版本进行验证,subagents 能够在 最新版 的 VS Code Plugin 和 Codex App 中正常工作,将 Codex CLI 更新至 0.143.0 版本后,subagents 仍能够在 CLI、App 和 VS Code Plugin 下正常工作,推测这个问题归属于 Codex App 和 VS Code Plugin,而不是 Codex 本身。(不愧是大公司,一个 BUG 能修半年,让你 🐮 完了)。

Codex App 无法加载 Archived chats

被归档的会话存储在 .codex/archived_sessions/ 目录中。

如果要删除所有归档的会话,可以直接清空这个目录。

那如果要还原某个被归档的会话呢?

开个 VPN 就能在 Codex App 里正常显示了。

这些内容明明是保存在本地的,居然还要开 VPN 才能正常显示… 😑