如今,借助 AI 的软件开发往往遵循一个简单直接的流程:
撰写任务描述(prompt)。
让 AI 给出分析和建议的解决方案。
准备 skill、agent 和 subagent。
开始实现。
让 AI 编写代码、测试并修复发现的问题。
乍一看,这似乎是理想的开发工作流。AI 自主运行,在各个 agent 之间分配任务,编写测试,并逐步完成所要求的功能。
对于较小的项目,这种方法可以非常有效。
如果你要构建一个简单的电商商店、一个五页的公司网站,或一个基础的管理系统,AI 往往可以完成其中相当大的一部分工作,而且效果很好。
为什么?
因为数以百万计的开发者已经构建过类似的软件。
想象两家网店。一家卖玩具,另一家卖苹果。
从顾客的角度看,这是两门不同的生意。但从软件架构的角度看,它们的运作方式非常相似:
按类别组织的商品。
定价和库存管理。
购物车。
订单处理。
支付方式。
客户管理。
变化的主要是数据、具体的业务规则和某些功能。核心的架构原则基本保持不变。
AI 能识别这些模式,因为它在训练中见过类似的结构。
当你不再构建常规应用,而开始开发大型企业级软件时,问题就出现了。
当系统包含 30 万到 50 万行代码时会发生什么?
想象一个企业级应用,包含数十万行代码、数十个模块、数百个依赖项和复杂的业务规则。
这样的系统可能已经演进了好几年。在此期间,架构决策、例外情况、历史依赖和业务规则不断累积。
并非所有这些都有文档记录。
一个方法可能影响其他十个模块。修改一个参数,就可能扰乱开票、用户权限或订单处理。
现在设想把一个 AI agent 引入这样的环境。
你交给它一个任务:
"修改支付流程以支持一种新增的支付方式。保持向后兼容并测试所有内容。"
agent 制定计划,在 subagent 之间分配工作,实施修改,运行测试并返回结果。
一切看起来都可能是正确的。
但是,测试通过并不一定意味着软件保留了所有既有的业务规则。
这就引出了六个月前彻底改变我对 AI agent 看法的那个问题。
改变我对 agent 系统看法的实验
六个月前,我做了一个实验,它让我选择了另一条道路,而不是单纯地构建更多的 agent 和 subagent。
在那之前,我一直抱有许多开发者共同的假设:
我把任务在各个专业 agent 之间分配得越好,引入的验证机制越多,结果就会越可靠。
但在实验中,我遇到了一个根本性的问题:
如果 agent 根本不知道某条业务规则的存在,它怎么可能正确地实现这条规则?
而另一个 agent 如果同样缺乏关于系统应有行为的完整信息,又怎么能可靠地验证结果?
我开始研究上下文是如何在 agent 之间传递的。
我发现,信息传递和被解读的方式,可能是整个流程中最薄弱的环节之一。
agent 和 subagent 实际上是如何工作的?
在实践中,AI agent 之间的通信有两种常见方式。
1. 层级模型——由主 agent 协调 subagent
这种架构通常被称为 Hub-and-Spoke(中心辐射)或 Star Topology(星型拓扑)。
原理很直接:
用户把任务提交给主 agent。
主 agent 分析任务并将其拆分为更小的部分。
它创建或激活 subagent A 并给出指令。
subagent A 完成工作并返回结果。
主 agent 评估结果,并可能把进一步的工作委派给 subagent B。
所有部分完成后,主 agent 合并结果。
subagent A 不一定知道 subagent B 在做什么。通信和信息交换由主 agent 协调。
优点: 集中控制,状态跟踪更容易,职责更清晰,agent 之间失控通信的风险更低。
缺点: 主 agent 成为解读任务指令和结果的中心节点。如果它在委派工作时遗漏了重要信息,subagent 就可能在不完整的上下文中工作。
2. 点对点——agent 之间相互通信
第二种方式涉及 agent 之间的直接或持续通信,例如通过共享的对话线程或消息系统(*Message Bus*)。
设想如下场景:
agent A 编写代码并发布 CodeWritten 事件。
负责测试的 agent B 收到该事件并启动测试。
agent C 同时准备文档。
各 agent 持续交换结果并对变化做出反应。
优点: 任务并行执行,信息交换快速,对单一中心 agent 的依赖降低。
缺点: 协调更复杂,存在使用过时信息的风险,可能出现相互冲突的修改,追查错误根因也更困难。
这两种方式都可能非常有用。
但单靠任何一种都无法解决根本问题。
一个 agent 可能拥有定义完美的任务,但如果不理解系统真实的业务逻辑,它可能给出技术上正确、业务上却错误的方案。
agent 工作流中的错误是如何产生的?
AI 不是把指令直接翻译成源代码的确定性编译器。语言模型会解读可用信息,并基于该上下文生成解决方案。
当信息缺失时,它可能做出错误的假设。当可用信息不完整或相互矛盾时,它可能选择不恰当的方法。
让我们来看一个较大任务的典型工作流。
步骤 1 - 解读用户 prompt
主 agent 收到任务,并尝试理解所要求的变更。
在这个阶段,它可能忽略某个依赖,误解某个约束,或者假设了从未被明确说明的内容。
步骤 2 - 为 subagent 创建指令
主 agent 创建更具体的任务描述。它从原始 prompt 中选取它认为相关的信息。
但是,如果它不了解某些业务规则,就无法正确地传达这些规则。
步骤 3 - 实现
subagent 收到指令,分析代码库的一部分,并选择实现策略。
它可能产出语法正确、质量很高的代码,却无意中改变了本应保持不变的行为。
步骤 4 - 评估结果
主 agent 收到 subagent 的输出,对其进行评估并判断任务是否完成。
但它可能仍然基于与之前相同的不完整假设在工作。
步骤 5 - 测试和修复错误
随后是单元测试、集成测试、E2E 测试、静态分析和文档审查。
这些机制显著提高了软件的可靠性,但它们也有局限。
测试验证的是具体的预期行为。如果某条重要的业务规则不在测试规格中,实现就可能通过所有测试,却违反了该规则。
而且当 AI 修复一个问题时,可能会无意中影响系统的另一部分。
这就形成了一个熟悉的循环:
实现 → 测试 → 修复 → 回归 → 更多测试 → 又一次修复。
因此,单纯增加 agent 的数量或更频繁地重复测试,并不会自动保证得到正确的结果。
为什么错误会在 agent 链中累积?
让我们考虑一个简化的数学模型。
假设有三个连续的步骤,每个步骤被正确执行的假设概率为 95%。
如果这些步骤相互独立,并且三个都必须成功,则概率为:
0.95 × 0.95 × 0.95 = 0.8574
也就是说,三个步骤全部成功的概率约为 85.7%。
如果有十个这样的独立步骤,结果将是:
0.95¹⁰ ≈ 59.9%
这是一个说明性的计算,而不是对 AI agent 成功率的实测。在真实系统中,错误并不总是相互独立的,验证机制可以发现错误,各个步骤也可以纠正之前的失误。
不过,这个例子说明了一个重要的原理:
单个 agent 的可靠性并不会自动保证整条链的可靠性。
当不完整的指令在每个阶段被反复解读时,就会出现更多做出错误决策的机会。
这就是为什么我认为,单纯增加更多的 agent、测试和验证机制是不够的。
我们需要改变 AI 获取系统本身信息的方式。
关键区别:测试验证结果,业务逻辑界定边界
传统的 AI 辅助开发方式是被动反应式的。
你提交一个需求,AI 实现一个方案,然后用测试来判断是否有东西被破坏。
这当然比完全没有测试要好。
但仍然存在一个根本性的弱点:
测试无法可靠地验证那些无人知晓、也不在测试规格内的业务规则。
因此我认为,更好的做法是在实现开始之前,就向 AI 提供关于系统实际行为的信息。
不只是 需要改变什么,还包括:
哪些业务规则适用于受影响的代码。
还有哪些其他模块依赖这项功能。
哪些输入和输出必须保持兼容。
哪些场景必须保持不变。
存在哪些例外和历史约束。
实现的哪些部分 AI 可以修改,哪些必须保留。
这大大减少了 AI 必须自行推断的行为量。
同一任务的两种方法
方法 A - Prompt + 测试
AI 收到任务,根据可用的上下文选择一种实现,产出方案并运行测试。
如果测试不完整,对既有业务规则的违反可能无法被发现。
方法 B - Prompt + 业务逻辑 + 测试
AI 收到任务的同时,还收到相关的规则、依赖、契约和约束。
因此,在实现开始之前,它就知道哪些行为必须保留。
这样,测试不仅仅是发现错误,还会验证具体的业务契约。
区别不在于我们不再测试。
区别在于,我们试图在错误被引入之前就加以预防。

实际示例:添加一种新的支付方式
假设我们有一个名为 CheckoutService.ts 的文件,负责电商应用中的支付。
我们想添加银行卡支付作为一种额外的支付方式。
方案 A:常规 prompt
任务:
> 在 CheckoutService.ts 中添加银行卡支付。保留现有功能并运行所有测试。
AI 修改代码,添加新的支付分支,并更新测试。
所有测试通过。
任务被标记为完成。
然而,系统中存在一条重要的业务规则:
公司账户必须以银行转账作为默认支付方式。
这条规则是几年前引入的。实现它的开发者已经不在公司工作了。该规则没有清晰的文档,也没有专门的回归测试。
AI 把银行卡支付设为所有客户的默认方式。
从实现的角度看,新功能运行正确。
从业务逻辑的角度看,系统已经被破坏。
而测试没有发现这个问题。
方案 B:在 AITM 中使用带业务逻辑的结构化任务
现在设想 AI 收到同样的请求,但这次任务中包含相关的业务契约、相关变量、指向历史任务的链接,以及对允许修改范围的精确界定。
任务可能是这样的:
READ CLAUDE.md first.
TASK:
Add card payment as an additional payment method
in CheckoutService.ts.
Contract:
%CHECKOUT-019 — "For company accounts
(companyId != null), bank transfer must remain
the default payment method. Card payment may
be offered only as an additional option."
Related variables:
BL.CHECKOUT.PAYMENTMETHOD_DEFAULT
BL.CHECKOUT.PAYMENTMETHOD_OPTIONS
Related task:
#<link to the task that originally implemented bank-transfer-as-default checkout>
In scope:
- Add a new "card" option to the available payment methods in CheckoutService.ts.
- Expose it as a selectable option in the existing payment method selection flow.
Out of scope:
- Changing the default payment method for any account type.
- Any UI/checkout-flow redesign beyond adding the new option.
- New payment provider integration logic beyond the method registration itself.
Must not break:
- Existing default payment selection logic (company accounts → bank transfer stays default).
- Company account checkout behavior end-to-end.
- Existing bank transfer processing path.
- Compatibility with the current payment API contract.
File path:
src/Services/CheckoutService.ts
Confirm the exact path via contractsForFile before editing.
Verify:
- Company account checkout still selects bank transfer by default
(companyId != null).
- Card payment appears as a selectable, non-default option.
- Existing bank transfer processing is unchanged.
- Run unit and integration tests covering CheckoutService.
关于其在 AITM 中如何运作的更多信息:https://aitm.cz/Docs/KnowledgeSharing
为什么这种格式很重要?
乍一看,它可能只是一个更详细的 prompt。
但真正的区别在于,这个任务与可以检索、并能对照实际项目进行验证的信息相连接。
1. 契约 %CHECKOUT-019
标识符 %CHECKOUT-019 不只是一条文字备注。
它是一个示例性的契约标识符,agent 可以通过 contractsForFile 或 contractsForVariable 等 GraphQL 操作检索到它。
agent 不必只依赖 prompt 中写下的规则,而是可以检索其结构化表示,并对照当前代码库进行验证。
2. 业务逻辑变量 _BL.
诸如 _BL.CHECKOUT.PAYMENT_METHOD_DEFAULT 和 _BL.CHECKOUT.PAYMENT_METHOD_OPTIONS 之类的标识符,代表以 AITM 通信语言表达的业务逻辑。
它们有助于把具体需求与决定系统行为的规则和变量联系起来。
3. 对相关任务的引用
Related task 部分引用了实现该业务规则的原始任务。
这为 AI 提供了额外的上下文,不仅让它知道系统当前如何运行,还知道当初为何会做出某个特定决定。
4. 清晰界定的变更边界
In scope、Out of scope 和 Must not break 三个部分定义了三个独立的类别:
AI 必须实现什么。
AI 不得实现什么。
AI 必须保留哪些既有行为。
AI 收到的不是一个可以随意解读或扩展的开放式任务,而是一个精确定义的实现范围。
5. 编写代码之前先验证
文件路径不会被自动假定为正确。
agent 被指示先使用 contractsForFile,确认实际路径,并找出相关联的契约。
然后才应开始修改代码。
6. 通过 TPV 进行 prompt 验证
AITM 方法的一个重要组成部分是 TPV prompt 验证器。
它在任务提交执行之前检查必备的任务章节。
例如,它会验证是否存在范围定义、限制条件、契约、文件路径和验证要求。
这把一部分错误预防从测试已完成的代码,前移到了准备任务本身。
注意: 本示例中的契约标识符和业务逻辑变量仅为示意性的占位符。在真实项目中,优化器会通过 GraphQL 从具体项目中检索它们。不应在未经验证的情况下猜测或生成它们。
这种方法带来了什么变化?
在传统方式中,agent 先解读一个开放式任务,并自行决定哪些文件、依赖和业务规则重要。
而在 AITM 中,任务与具体的契约、业务逻辑变量、历史任务以及清晰界定的变更边界相连接。
因此,agent 可以基于可验证的信息做出决策,而不是主要依赖假设。
这就是我认为的根本区别所在。
业务逻辑不再仅仅是文档的一部分。它成为实现控制流程本身的一部分。
这就是 AITM 背后的原理
当我们开始设计 AITM(AI Task Manager)时,我们并不想构建又一个只是启动 agent、分发任务、事后检查结果的系统。
我们关注的是另一个问题:
如果 AI 能在做出任何修改之前,直接从现有源代码中获得相关的业务逻辑,会怎样?
不只是某人一年前写下的文档。
不只是存放在 skill 中的通用指令。
也不只是 agent 碰巧选中的几个文件。
而是描述受影响的系统部分实际如何运行、受哪些契约约束、以及有哪些其他组件依赖它的结构化信息。
因此,AITM 的核心原则是:用从代码库相关部分识别出的业务逻辑来丰富工作 prompt。
agent 获得的不仅是关于它必须做什么的信息,还有关于它必须保留什么的信息。
这在大型软件系统中尤其重要,因为单个 prompt 可能无法包含每一个相关的依赖或约束。
业务逻辑:AI 的护栏
把业务逻辑想象成道路两侧的护栏。
没有护栏,AI 可以走许多不同的路径。有些可能通向正确的方案,而另一些则可能违反系统中隐藏的规则。
通过提供相关的契约和约束,我们大大缩小了出现不良决策的空间。
AI 仍然可以独立选择实现策略、算法和代码结构。
但它必须遵守已知的业务规则。
随后的测试会验证这些规则是否真的得到了保留。
这代表着理念上的根本转变。
我们不只是试图修复 AI 产生的错误。我们是在试图降低这些错误一开始就被引入的概率。
这是否意味着我们不再需要 agent 和测试?
当然不是。
agent 适用于并行执行、专业化活动和大型任务的自动化。
单元测试、集成测试和 E2E 测试仍然是软件质量保证的重要组成部分。
但 agent 编排和自动化测试无法取代对业务逻辑的了解。
如果 agent 不理解系统必须保留什么,即使是组织得再完美的 subagent 团队也解决不了根本问题。
而如果测试没有覆盖相关场景,那么所有测试都可能通过,而应用从业务角度看仍然行为不正确。
正确的做法不是在业务逻辑和测试之间二选一,而是将两者结合。
首先,识别系统的实际行为。
然后,把这些行为表达为清晰的规则和契约。
接着,把这些规则连同任务一起提供给 AI。
只有这时,才进入实现、验证和测试。
这种方法可以减少不必要的修复、回归和重复的实现周期。
结论:关键不在于 agent 的数量,而在于信息的质量。
对于简单的应用,即使指令相对简短,AI 也能交付出色的结果。
但对于拥有五十万行代码的企业级软件,仅仅假设 agent 会正确理解一切、发现每一个依赖,然后完美地测试自己的工作,是不够的。
系统越大,理解并保护其真实的业务行为就越重要。
你可以让十个 agent 完美协作。但如果它们都不知道某条关键的业务规则,它们就可能共同产出一个经过完美测试、却从根本上错误的方案。
这就是我们在 AITM 中采用不同方法的原因。
首先是上下文、规则和契约。
然后是实现。
最后是验证和测试。
因为目标不是让 AI 在最短时间内写出尽可能多的代码。
目标是让 AI 精确地修改需要修改的地方,而不破坏已经正常运行的部分。