
把三四个 AI 智能体丢进同一个代码仓库让它们各写各的模块、最后还能合到一起不出乱子——这件事我折腾了大概三个星期。最开始的想法很简单既然一个智能体能写代码那多开几个不就等于组了个外包团队结果第一次尝试就翻车了两个智能体几乎同时盯上了同一个文件一个在删旧接口另一个正在往里面加新功能最后保存时互相覆盖一个上午的活全白干。后来我把这套东西整理成了一个固定的流程名字就叫“软件工厂”。核心理念不复杂与其让智能体们靠聊天协调不如把分工和状态写进几个 markdown 文件里让它们各自去读、去写、去认领任务。文件是共享的“任务墙”智能体是墙前面干活的工人谁都能看谁也不会真的踩到别人的脚。这套方案不需要搭复杂的编排平台不依赖某个厂商的生态几个 markdown 文件加一个 git 仓库就能跑起来。这篇博文把整个方案完整拆开讲一遍文件怎么设计、任务怎么拆、并行怎么控制、踩过哪些坑以及一个可以直接抄走的模板。1. 软件工厂到底难在哪多智能体并行协作的三大痛点1.1 上下文“各说各话”聊天式智能体天然无法共享记忆很多人第一次尝试多智能体协作下意识会走“聊天协调”这条路让 A 智能体干完活之后把结果转述给 B 智能体B 再接着干。听起来像接力赛实际上是个灾难——大模型智能体的每一次交互都是“吃了就忘”的它并不存在一个持久的、全局共享的工作记忆。所谓上下文窗口只是当下这一次调用的输入你上次告诉它的内容如果没写进某个持久化文件里它转头就不记得。这就带来一个很典型的翻车场景A 智能体花了半小时把auth.py里的登录逻辑改成了 JWT 方案然后你把对话摘要丢给 B 智能体让它去写对应的测试。B 打开auth.py一看发现代码跟摘要里描述的根本对不上——因为 A 只是“口头”说了它要改什么会话结束之后它并没有真的把文件写回仓库。这种“各说各话”的后果就是要么 B 重复劳动要么 B 干脆凭想象写测试最后测的跟实际代码完全不沾边。所以我在设计“软件工厂”时定下的第一条铁律就是智能体之间不允许直接“对话协作”一切信息交换必须通过文件落盘。你说你改完了那就把改动提交到仓库里你遇到了问题那就把问题写进状态文件。文件是唯一的沟通语言谁都不许在会话里口头传递状态。1.2 文件级互踩多个智能体同时改同一份代码的后果第二个痛点比“失忆”更直接两个智能体真的可以同时改同一个文件。你可能觉得让它们各写各的模块不就行了实际代码项目里模块之间是有依赖的文件之间的耦合远比你想象得深。我踩过最疼的一个坑是让智能体 A 实现登录接口让智能体 B 重构数据库连接层。A 在它的实现里 import 了Database这个类B 觉得db.py里Database太长了顺手重命名成DBConn并且删掉了旧名字。B 做完之后测试是通的因为它自己也改了引用处但 A 那边的代码因为跑得慢还没改完等 A 提交之后一跑测试满屏的ImportError。根因就是B 根本不知道 A 用了旧命名二者对项目状态的认知完全不一致而代码仓库本身没有提供任何“你正在改这个区域的接口”的提示。这个问题在真人团队里靠的是默契你会知道隔壁同事正在重构数据库层你不会去动他的接口。但智能体没有这种默契你不给它一个明确的“占用标记”它就会认为“仓库里一切代码都归我管”。所以“软件工厂”一定要有锁的概念——不是传统意义上的文件锁而是任务级别的互斥哪个智能体现在占用哪个模块写清楚其他人看到就得绕开。1.3 任务墙混乱任务拆了没人跟进验收标准模糊第三个痛点是任务本身管理不起来。很多人在让多个智能体协作时任务就写在 prompt 里“你负责模块 A你负责模块 B”然后就没有然后了。智能体干到一半停下来问你“这个接口字段用user_id还是uid”你答了之后另一个智能体也在同一个项目里也遇到了同样的问题但你没法把答案广播给所有人。更麻烦的是“验收标准模糊”。你让一个智能体“实现订单列表接口”它觉得自己写完了但你打开代码一看没有分页、没有鉴权、没有异常处理。为什么因为你没在任务里写清楚“验收标准”它按自己的理解“能跑就算完成”。在一个并行流水线里这种模糊任务会被无限放大——评审的智能体不知道该按什么标准检查编码的智能体觉得委屈你自己夹在中间最后变成所有问题都来找你确认所谓的“并行”彻底退化成“串行问人”。这三个痛点叠加起来就形成了一个很讽刺的局面多智能体协作理论上能提高效率实际跑起来却比一个人干还慢因为大量时间都花在信息同步和返工上。“软件工厂”要解决的就是用最轻的方式把这三个痛点一次性按住。2. 设计思路为什么几个 markdown 文件就能编排一整个工厂2.1 Markdown 是最适合人机共读的“协议格式”决定用 markdown 之前我认真比过几个方案包括 JSON 配置文件、YAML、SQLite 数据库甚至是接一个专门的智能体编排平台。最后全部否掉原因可以归结为一句话markdown 是“人类可读性”和“机器可解析性”之间平衡最好的格式。JSON 和 YAML 更严谨但有两个问题。第一逗号、缩进、引号错了任何一个解析器就罢工而大模型生成 JSON 时经常会出现多加一个逗号、字符串忘了转义这种低级错误。第二你没法直接在 JSON 里写给人看的说明文字每行都是结构化数据检查起来非常费眼。数据库则更麻烦——Agent 要读任务信息得先学会你的 SQL schema写个SELECT还得保证字段名不出错这对大模型的“工具调用”能力要求太高了。markdown 的好处在于它本质上就是“带约定的纯文本”。智能体写 markdown 表格几乎不会出错因为格式足够宽容而人打开.md文件一眼就能看清任务列表、状态、负责人。更重要的是markdown 是 git 最友好的格式之一——diff 结果按行显示谁改了哪一行清清楚楚。你把它当一个协议格式它就是一个零依赖、跨平台、人机都看得懂的协议格式。2.2 软件工厂的三层架构总控者、执行者、工作区“工厂”这个比喻是准确的真实的产线从来不是所有工人一拥而上而是有明确的工种划分。“软件工厂”我设计了三个角色对应三个层次的职责。第一层是总控智能体我管它叫“工头”。它的核心职责不是写代码而是拆任务、派任务、更新任务状态。它读需求文档把需求拆成若干可并行的子任务写入任务队列文件并分配好每个子任务的负责人。工头还会定期查看进度看板哪个任务卡住了它负责介入处理。第二层是执行智能体也就是“产线工人”数量可以是 2 到 5 个。每个执行智能体只做一件事从任务队列里认领一个状态为“待认领”的任务把状态改成“进行中”然后去实现代码完成之后更新状态并提交。执行智能体之间完全不直接通信它们唯一的交互介质就是那几个 markdown 文件——谁认领了什么任务写在表格里谁完成了什么也写在表格里。第三层是评审智能体负责“质检”。执行智能体完成任务后评审智能体按照任务里的验收标准检查代码跑测试、看 diff、确认没有越界改动。通过之后它负责把代码从开发分支合入主分支并把任务状态更新为“已完成”。这三层各司其职最大的好处就是避免了“权限混乱”。总控不解代码、执行不碰路线、评审直接验收每个智能体的 prompt 里都写死了它的权力边界。这就好比你不会让产线工人自己决定产线的生产计划也不会让质检员去写代码——职责边界清晰踩脚的概率自然就低了。2.3 并行度的真相不是越多越好我在实验初期犯过一个错误为了追求“一口吃成胖子”同时开了 8 个执行智能体并行开发。结果是灾难。8 个智能体同时抢任务、同时更新状态文件导致两个问题一是状态文件频繁冲突总控智能体忙于协调“谁先改谁后改”反而没时间拆新任务二是任务拆得太碎模块之间依赖关系复杂互相等对方接口并行效率大打折扣。后来我逐步收敛得出的经验是执行智能体 3 到 5 个是最舒服的区间。这个数量下任务队列的竞争压力小状态文件的更新频率低每个智能体的工作颗粒度也足够大——一个人负责完整的一个模块或一个服务而不是负责一个文件里的一个函数。并行度不是越高越好真正决定效率的是任务之间的依赖关系是否被切干净了。依赖越少越适合并行依赖越多人再多也快不起来。3. 几个关键 markdown 文件的设计与模板任务墙的底层协议3.1factory.md工厂配置文件声明规则和角色这是整套系统的“宪法”所有智能体在开始工作之前必须先读这个文件。它定义了三件事有哪些角色、每个角色的权限边界、整个工厂的运行规则。我先展示一个简化模板这个模板是可以直接抄走用的# 软件工厂配置 ## 角色定义 - 总控工头唯一允许修改任务队列和状态看板的角色。 - 执行-前端只允许修改 src/frontend/ 目录不允许修改其他目录。 - 执行-后端只允许修改 src/backend/ 目录不允许修改其他目录。 - 评审只允许读代码和写评审报告不允许修改业务代码。 ## 运行规则 1. 任何角色开始任务前必须先阅读 queue.md 和 state.md。 2. 认领任务必须在 queue.md 中把状态改为“进行中”并填写认领人。 3. 同一时刻一个任务只有一个认领人。 4. 完成代码后更新 state.md 中的进度说明并提交 git。 5. 所有任务必须通过评审后才能标记为“已完成”。 6. 遇到全局性问题写入 questions.md等待总控处理。 ## 验收标准模板 每个任务必须包含以下字段 - 功能描述要解决什么问题 - 触发条件什么情况下调用该功能 - 输入输出输入参数、输出格式 - 边界情况如超时、网络异常、空数据 - 测试要求至少覆盖正常链路和一条异常链路这个文件的本质作用是减少智能体的“自由发挥空间”。你每写清楚一条规则就等于少了一次跟智能体反复解释的机会。规则不是用来吓唬人的而是为了让智能体在不确定时能自己查到答案而不是停下来打断你。3.2queue.md任务队列用表格拆解任务queue.md是整个工厂的“任务白板”核心内容是一张表格每一行是一个任务。设计这张表格时我反复调过字段最后稳定下来的字段有七个任务ID、模块、任务描述、依赖、认领人、状态、验收标准。# 任务队列 | 任务ID | 模块 | 任务描述 | 依赖 | 认领人 | 状态 | 验收标准 | |--------|------|----------|------|--------|------|----------| | T-001 | 后端 | 实现用户注册接口 | 无 | 执行-后端 | 待认领 | 输入手机号和密码返回 token重复注册返回错误 | | T-002 | 前端 | 实现注册页表单 | T-001 | 执行-前端 | 待认领 | 表单校验通过后调用 /register处理成功与失败两态 | | T-003 | 后端 | 实现用户登录接口 | T-001 | 执行-后端 | 待认领 | 校验密码连续失败 5 次锁定 10 分钟 |这个表格看起来简单但它是全系统最重要的设计。原因在于任务描述和验收标准明确写在同一条记录里智能体不需要猜。依赖字段则直接决定了并行度——只有 T-001 完成之后T-002 才能开始而 T-003 依赖 T-001不需要依赖 T-002意味着后端把 T-001 完成之后T-002 和 T-003 可以并行。这种依赖关系一旦在表格中可视化总控的调度工作就变得非常机械且可靠。3.3state.md状态看板记录谁在做什么如果说queue.md是“任务清单”那state.md就是“现场实时状态”。它的作用是解决我前面说的“文件级互踩”问题让每个智能体在动手之前先看一眼当前有哪些模块正在被占用。# 状态看板 ## 当前占用 | 模块路径 | 占用任务ID | 占用智能体 | 状态 | |----------|------------|------------|------| | src/backend/auth/ | T-001 | 执行-后端 | 进行中 | | src/frontend/pages/ | T-002 | 执行-前端 | 进行中 | ## 模块所有权声明 | 模块路径 | 默认所有者 | |----------|------------| | src/backend/** | 执行-后端 | | src/frontend/** | 执行-前端 | ## 近期变更记录 - [2025-06-10 10:20] T-001 完成等待评审 - [2025-06-10 10:30] T-002 提交代码等待测试通过模块所有权声明是关键。每个智能体在启动任务时先检查“我要改的路径”是否落在自己的所有权下如果不是直接拒绝。这一步虽然简单但能挡住 90% 的越界修改。状态看板上还能记录“当前阻塞因素”比如某个任务被外部依赖卡住了写在这里总控下次查看时一眼就能发现问题。3.4review.md评审清单合并前的最后一道闸最后一个文件是评审智能体的工作底稿。它不是一个必须存在的文件但加了之后明显减少了“评审之后才发现问题”的频率。评审智能体在检查代码时会把结果写进review.md形成一个可追溯的检查记录。# 评审记录 | 任务ID | 评审人 | 检查项 | 结论 | 备注 | |--------|--------|--------|------|------| | T-003 | 评审 | 登录接口鉴权 | 通过 | 密码错误次数记录已实现 | | T-003 | 评审 | 测试覆盖率 | 未通过 | 缺少连续失败锁定逻辑的测试 | ## 合入结论 - T-003未通过退回执行-后端补充锁定逻辑测试这个文件的额外价值在于当你发现某个智能体总是犯同类错误时你可以把规律直接写进factory.md的规则里形成“从事故中迭代规则”的正反馈。我跑了两个星期之后factory.md里的规则从最初的 6 条长到了 14 条智能体的产出质量也随之明显变稳。4. 实操从零跑通一个三智能体并行工厂4.1 初始化目录与文件实操部分我说得很细因为这是可以直接照着做的。首先准备一个 git 仓库并创建三个目录src/放业务代码、agents/放智能体相关的配置文件、docs/放需求文档。然后创建上面提到的四个 markdown 文件。mkdir software-factory cd software-factory git init # 创建目录结构 mkdir -p src/backend src/frontend agents docs tests # 导入初始文件 touch agents/factory.md agents/queue.md agents/state.md agents/review.md这里有一个值得强调的细节为什么要把这几个 markdown 文件放在agents/子目录里而不是仓库根目录因为业务代码和编排文件混在一起会导致智能体在扫描项目结构时把factory.md当成业务代码来修改。单独放一个目录能在 prompt 里明确告诉智能体“agents/ 目录里的文件是管理文件绝对不允许业务智能体修改只有总控可以动”。把管理逻辑和业务逻辑在物理上分开本身就是降低踩脚风险的有效手段。4.2 编写总控、执行、评审三个智能体的 Prompt接下来是配置智能体的 prompt。注意这里说的智能体可以是任何支持“自定义系统提示词 文件读写”的大模型应用比如开源的dify、coze或者直接用Claude 文件访问工具。我不推荐特定平台因为这套逻辑的底层只是“读 markdown、写 markdown、跑命令”不依赖任何平台能力。总控智能体的 prompt 核心是你是软件工厂的总控。你的唯一职责是管理任务队列agents/queue.md和状态看板agents/state.md。 你不写业务代码不修改 src/ 目录下任何内容。 你负责 1. 阅读 docs/ 下的需求文档将需求拆解为可并行执行的子任务。 2. 将任务写入 agents/queue.md 表格中初始状态为“待认领”。 3. 定期检查 state.md处理智能体的提问和阻塞。 4. 任务完成后把评审意见同步到 queue.md。 禁止事项你绝不允许修改 src/ 和 tests/ 目录下的任何文件。执行智能体的 prompt 核心是你是执行智能体你的唯一职责是从任务队列中认领任务并完成代码实现。 你的工作流程 1. 打开 agents/queue.md找到一个状态为“待认领”且“认领人”为你自己的任务。 2. 更新该任务状态为“进行中”填写认领人。 3. 检查 agents/state.md确认你负责的模块路径没有被其他任务占用。 4. 编写代码只允许修改你模块所有权下的文件。 5. 代码完成后运行相关测试更新 agents/state.md 中的进度记录。 6. 提交 git然后在 queue.md 中将任务状态改为“待评审”。 你只负责 src/backend/ 目录绝不修改其他目录。评审智能体的 prompt 核心是你是质检员你的唯一职责是检查已完成任务的代码质量。 你的工作流程 1. 扫描 agents/queue.md找到状态为“待评审”的任务。 2. 阅读该任务在 queue.md 中的验收标准。 3. 检查 git diff跑相关测试。 4. 将评审结论和问题写入 agents/review.md。 5. 如果通过在 queue.md 中将任务标记为“已完成”如果不通过标记为“进行中”并注明退回原因。注意到执行智能体的 prompt 里有一段“只负责 src/backend/ 目录”这跟state.md里的模块所有权声明是对齐的。prompt 约束 文件声明双重保险才能真的挡住越界。4.3 执行一次完整的工厂工作流初始化完成之后一次完整的工作流是这样的第一步总控读取需求文档把“用户注册登录模块”拆成 T-001 注册接口、T-002 注册页、T-003 登录接口三个任务写入queue.md状态都是“待认领”。第二步执行-后端启动看到 T-001 和 T-003 都是自己的任务但它只认领了 T-001因为 T-003 依赖 T-001 的数据库表结构需要等 T-001 完成再动。它把 T-001 状态改为“进行中”开始实现注册接口。第三步执行-前端启动看到 T-002 依赖 T-001状态还是“待认领”它发现 T-001 正在进行中于是没有认领 T-002而是等待。这里有一个很关键的细节执行智能体怎么“等待”不是挂着会话干等而是完成当前轮次后直接退出或休眠隔一段时间再重新启动检查任务队列。这也是整个方案能落地的原因——智能体不需要常驻文件系统就是天然的“消息中间件”。第四步执行-后端完成 T-001提交代码把任务状态改成“待评审”同时在state.md更新“T-001 完成等待评审”。评审智能体随后启动检查 T-001 的代码跑测试写review.md。通过之后把 T-001 状态改成“已完成”。第五步执行-后端再次启动看到 T-003 的依赖 T-001 已经完成于是认领 T-003。执行-前端也启动看到 T-002 依赖完成认领 T-002。现在 T-002 和 T-003 同时进入“进行中”两个智能体并行干活互不干扰。4.4 合入与验证不只是跑通还要盯住接口契约任务完成后合入不是简单地git merge。我建议在合入前做三个动作第一看git diff确认每个任务实际修改的文件是否都在它声明的模块路径内第二跑一遍全量测试不只是单测还有编译检查和关键的集成测试第三对照queue.md里的验收标准逐条确认防止智能体“自我评价良好但实际漏做了需求”。这里分享一个实用的检查命令# 查看某个任务涉及的改动文件 git diff main...feature/T-001 --stat # 跑后端相关测试 cd src/backend python -m pytest --tbshort如果 diff 里出现了不属于该任务所有权目录的文件百分百是越界了直接打回重做不要抱着“动都动了说不定有用”的心态放过它。越界改动是并行系统里最凶猛的踩脚来源发现一次就要追一次否则智能体下次还会犯。5. 常见问题与排查技巧实录5.1 两个智能体同时更新queue.md最后互相覆盖这是最经典的问题。两个执行智能体同时完成任务同时去更新queue.md后保存的那个人会把前一个人的修改覆盖掉。我试过用 git 分支锁、用文件锁工具最后发现最简单的方案是“串行化状态文件的写入”约定所有智能体在更新agents/下的文件之前先运行一个更新脚本脚本内部用原子写入保证一次只有一个进程能改。脚本逻辑不复杂先锁文件读取旧内容修改再写入。伪代码如下# 伪代码原子更新 queue.md flock -x agents/queue.md -c python3 agents/bin/update_queue.py这种方式实测很稳。另一个思路是让智能体不直接改queue.md而是把状态变化写入自己的“周报文件”然后总控定期汇总更新。后者更符合我前面说的“职责分离”执行智能体只改代码状态文件永远只有总控一个人改天然不会冲突。5.2 智能体不遵守规则越界改了别人的模块原因一般不是它“故意使坏”而是它觉得“顺手改一下更合理”。解决方案除了 prompt 里强调还需要在 git 层面加一道拦截。我用过 Git hooks在pre-commit阶段检查本次提交涉及的文件路径如果修改了非所有权的目录直接拒绝提交并打印错误信息。# .git/hooks/pre-commit 示例片段 for file in $(git diff --cached --name-only); do case $file in src/backend/*) ownerbackend ;; src/frontend/*) ownerfrontend ;; *) continue ;; esac # 检查当前提交者标识与文件owner是否匹配 if [ $OWNER ! $owner ]; then echo 错误越界修改 $file exit 1 fi done别小看这道拦截。它把“智能体自觉遵守规则”变成了“底层机制强制遵守”可靠性完全不在一个量级。5.3 并行是快了但代码合并时冲突依然成山如果合并阶段冲突很多说明任务拆分的边界没有切干净。我复盘过几次发现根源在于两个任务都动了同一个接口文件比如一个改了接口的入参类型另一个改了接口的出参结构。解决办法是把“接口契约”前置——在拆任务的时候先让总控定义好接口的字段、类型、异常约定写进queue.md的验收标准里然后所有任务以这份契约为准代码实现按契约去填内容而不是拍脑袋决定接口长什么样。这个思路就像修地铁先把管道线路规划好再让不同标段的工程队并行开挖。如果规划都没做就分头挖挖到中间必然撞车而且返工成本极高。5.4 任务队列变成一锅粥状态和实际进度对不上状态和实际进度对不上通常是总控没有定期清理任务。执行智能体干了一半就中断了后续也没人管这个任务在队列里永远是“进行中”。我的建议是给状态设定时效总控每次启动时把“进行中”超过 3 轮检查的任务自动重置为“待认领”并记录中断原因。这个操作叫“状态回收”是保证任务队列健康度的核心动作。5.5 常见问题速查表问题根因推荐解法状态文件互相覆盖多个智能体同时写同一文件改由总控统一写状态文件或加 flock 原子更新越界修改代码智能体对目录所有权认知不清Git Hooks 强制校验 prompt 双重声明合并冲突成山任务边界没切干净接口未定先定接口契约再拆任务并行实现任务状态与实际脱节缺少状态回收机制总控定期重置超时任务智能体互相等待死锁依赖链设计不合理调整任务拆解顺序减少跨任务依赖我自己跑这套流程跑了一个多月最大的感受是软件工厂的价值不在于把开发人数虚拟地扩大多少倍而在于把“任务拆解、状态同步、接口契约”这几件事制度化。AI 写代码再快快不过冲突带来的返工。如果你们团队也想试我建议从两个执行智能体加一个总控开始任务先拆 5 个以内跑通一个完整闭环再慢慢加人加任务。最后再分享一个小技巧每天早上让总控智能体念一遍state.md把昨天烂尾的活重新梳理一遍。这个习惯帮我避免了很多次“任务在文件里静悄悄地烂掉”你也值得试一下。