ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

多智能体协作开发实践:用Markdown软件工厂实现并行编程

多智能体协作开发实践:用Markdown软件工厂实现并行编程 最近圈子里聊得最多的词大概就是智能体。但单智能体写代码这件事我已经有点嫌弃了。单个Agent确实能帮你写不少代码可真要扔给它一个完整项目写着写着就开始精神分裂——前面定的架构约束五十轮对话之后忘得一干二净把工具函数改了另一处调用悄悄崩了你没注意到的地方它给你自由发挥出一堆抽象类和设计模式。所以我把目光投向了软件工厂这个玩法让一堆智能体并行造软件互相之间还不踩脚。这个方案折腾了两个月最后落到一套极其朴素的东西上几个markdown文件。没错就是你想的那种纯文本文件连数据库都不用。这篇文章就是这套方案的完整复盘包括文件结构怎么设计、六个隔离机制怎么落地、以及三次真实事故是怎么发生的。适合已经用AI写过代码但觉得单打独斗效率上不去的开发者也适合想带着团队试水多智能体开发的工程负责人。1. 为什么我放弃一个Agent写一个项目的执念1.1 单个Agent干大活的三个死穴先说一个反直觉的结论同样的任务量一个Agent从头干到尾出错率比你想象的高得多。第一个死穴是上下文窗口。现在主流模型动辄几十万token的上下文听起来很够用但真塞进一个完整项目就捉襟见肘。项目的目录结构、历史决策记录、代码风格约定、各模块的接口定义七七八八加起来轻松吃满上下文。一旦满了模型就开始选择性遗忘前面写的代码规范和后面生成的代码判若两AI。第二个死穴是连锁修改失控。代码这东西有个特点牵一发动全身。Agent改了一个公共工具函数的签名所有调用方都得跟着改。单Agent拿到修改任务时常常只盯着眼前这个函数不会全局检索谁调用了它。结果就是它自己改爽了整个项目编译不过了。第三个死穴是决策不一致。写项目不是一口气写完的尤其是大项目前后要经过多轮对话。我遇到过好几次这种情况项目开始时定好了所有返回结果统一用Result类包裹结果写到第三十轮它又开始返回裸对象。你跟它指出这个问题它还会理直气壮地给你解释一堆当时为什么这么写的理由——实际上就是忘了。1.2 从堆算力到堆协作既然一个大Agent干不动大项目最简单的想法就是多开几个Agent并行跑。但如果你真的同时打开三个对话窗口让它们各自去写一个完整项目那不是并行是车祸现场。三个Agent共享一个代码仓库互相覆盖文件、互相改坏对方的逻辑最后整合的成本比你一个人写还高。真正的问题不是并行而是协作。人是怎么协作的建筑工地上不是一个工人从地基盖到封顶而是瓦工、木工、电工、水暖工各管一段共用一张建筑图纸。每个人只看到自己需要看的图只动自己需要动的材料通过图纸和现场交接来对齐信息。多智能体并行开发也是这样。关键不是给每个Agent一个更强的模型而是把隐性的、存在对话历史里的上下文变成显性的、写在文件里的契约。让每个Agent只读它该读的文档只改它该改的目录通过文件系统进行结构化沟通。这就是我管它叫软件工厂的原因车间、工位、图纸整套思路就是传统软件工程那套只不过工人换成了AI智能体。2. 软件工厂的核心逻辑文档即大脑目录即边界2.1 车间、工位与图纸三个工厂隐喻软件工厂的整个设计其实就是把现实工厂的三样东西搬过来。车间是项目本身。整个仓库的根目录就是一个车间所有智能体都在这里面干活。车间里划了多条流水线每一条流水线就是一个可以独立交付的功能切片。工位是每个智能体的活动范围。每个Agent有自己的专属工位也就是文件系统里有特定权限的目录。这个目录就是它的自留地在没有特殊许可的情况下别的Agent不许动它的文件它也不许动别人的文件。图纸是技术契约文档。所有Agent共享的规格说明、接口定义、数据模型都写在markdown文件里。图纸不是给某一个人看的是给所有人看的。改图纸要走变更流程这就是避免互相踩脚的核心机制。这三个隐喻直接引出了两个设计原则文档即大脑。Agent的记忆不应存在于会话历史里而应存在于项目文档里。会话历史是易失的、私有的、不可检索的Markdown文件是持久的、共享的、可以随时被任何Agent重新读取的。团队里来了一个新Agent不需要复述历史让它读一遍文档就行。目录即边界。每个Agent的职责范围通过目录权限来物理划定。你的Agent只能写你名下的目录我的Agent只能写我名下的目录谁也别想越界。这两条原则是所有多智能体协作方案的通用底层逻辑。不管你是用什么框架实现的核心都是这两件事让信息显性化让修改隔离化。2.2 为什么是markdown而不是数据库或编排框架做多智能体协作市面上有很多现成的方案LangGraph可以编排多Agent状态机CrewAI可以定义角色和任务还有各种Agent框架自带记忆库和任务队列。为什么我最后选了markdown文件第一markdown是普通文件不需要额外的运行时。所有大语言模型都能读md文件不需要什么特殊SDK。你不需要部署一个服务来管理Agent状态不需要维护数据库迁移脚本弄坏了一个md文件也不至于整个系统起不来。第二它是人也是可读的。这很关键。你会经常需要检查某个Agent到底看到了什么规则某个任务卡现在是什么状态如果是存在数据库里的记录你得写查询如果是编排框架的内存状态你根本查不了。但markdown文件用记事本就能打开你在晨会上可以直接打开某个Agent的工作日志念给大家听。第三git可以直接做版本管理。md文件天然适合git每一次改动都有历史记录。出问题了可以diff可以回滚可以看是谁改的。这套机制本身就是现成的审计日志不需要额外开发。第四它便宜。任何零成本的文件存储都能跑这套方案。你甚至可以在一台普通笔记本上用VS Code开几个终端每个终端跑一个Agent读同一个项目目录下的md文件就搭好了一个软件工厂。没有服务端、没有数据库、没有API网关只有文件系统。3. 五个markdown文件撑起一个AI施工队整个方案落地下来核心就是几个markdown文件各自扮演不同角色。我按照实际项目落地时的顺序一个个说清楚。3.1 入口与总览README.mdREADME.md是整个工厂的总入口建议放在项目根目录。它的作用不是写代码怎么跑而是告诉每一个新来的Agent这个项目是什么、团队有谁、默认要读哪些文件。# 费用报销小工具 - 软件工厂总览 ## 项目一句话 企业内部的费用报销审批系统覆盖提交、审批、打款全流程。 ## 团队角色 - agent-alice 负责用户认证与权限模块 - agent-bob 负责报销单与审批流模块 - agent-carol 负责报表导出与统计模块 ## 新成员必读 1. 先读 profile.md 了解分工与边界 2. 再读 roadmap.md 了解当前里程碑 3. 动手前必读 spec/data-model.md 和 spec/api.md 4. 每次修改后更新 log/你的名字.md你可能会问这不就是给人类新员工写的入职文档吗没错。智能体就是一个有无限精力但缺乏常识的新员工你把它当人带就对了。3.2 角色定义与权限边界profile.mdprofile.md定义每个智能体的角色。这不仅是写一下谁负责什么更重要的是写清楚谁不许改什么。权限边界写得越硬越不容易踩脚。# 角色档案 ## agent-alice - 角色用户认证与权限模块负责人 - 专属目录src/auth/ - 可读目录spec/、task/、log/ - 禁止修改src/reimbursement/、src/report/、src/common/、src/db/ - 对接接口POST /api/v1/users/login详见 spec/api.md - 特别约束不得修改公共工具函数 src/common/utils.ts如需变更请先登记到 spec/api.md 并通知 agent-carol这份文件的本质是划定势力范围。在这个基础上我还会在profile里写每个Agent的人设比如代码风格偏保守优先复用已有函数或者命名必须言简意赅禁止过度封装。这些规则会被写进每次调起Agent的system prompt里。3.3 全局蓝图与依赖关系roadmap.mdroadmap.md记录项目的整体计划和当前进度。它不是传统意义上的甘特图而是一个有序的任务列表标注了任务之间的依赖关系。# 项目路线图 ## 里程碑 M1最小可用版第1周 - [x] T001 搭建项目骨架与公共数据模型 - [x] T002 实现用户登录 / 登出接口 - [ ] T003 实现报销单创建接口依赖 T002 - [ ] T004 实现审批流引擎依赖 T003 ## 里程碑 M2增强功能第2周 - [ ] T005 实现报表导出 - [ ] T006 实现审批通知 ## 本周集成安排 - 周五 18:00 集成 T001~T004 到 main 分支 - 集成人agent-bob这个文件是所有Agent共同瞄准的目标。每个Agent在开始一天的工作前都应该先读一遍roadmap弄清楚哪些任务是它负责的哪些任务还没完成但它可以提前预留接口。依赖关系写得越明确Agent之间需要同步的信息就越少。3.4 技术契约api.md与data-model.md如果说profile.md划定的是物理边界spec目录下这些文档划定的就是逻辑边界。多个Agent并行开发时最怕的就是接口各写各的。所以我们把接口定义和数据模型独立出来当作所有Agent共同遵守的宪法。# 数据模型约定spec/data-model.md ## 核心实体Reimbursement | 字段名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | string | 是 | 全局唯一格式如 RB20250001 | | applicantId | string | 是 | 提交人用户ID关联 User.id | | amount | number | 是 | 报销金额单位元两位小数 | | status | enum | 是 | DRAFT / SUBMITTED / APPROVED / REJECTED | | createdAt | string | 是 | ISO 8601 格式例如 2025-01-15T10:30:00Z | ## 状态流转约束 - DRAFT - SUBMITTED提交人本人操作 - SUBMITTED - APPROVED / REJECTED仅审批人可操作 - 严禁跳过中间状态# 接口契约spec/api.md ## POST /api/v1/reimbursements - 鉴权必须携带有效 JWT - 请求体{ applicantId: ..., amount: 123.45, reason: ... } - 响应200 - { id: RB20250001, status: DRAFT } - 错误401 未登录400 参数不合法 ## 变更规则 - 任何人要改接口契约必须先在此文件提交变更说明 - 变更后必须在 log/你的名字.md 中记录并相关方 - 各模块必须在 24 小时内适配新契约数据模型和接口契约是并行开发的耦合点。在这个文件里定义好的东西其他模块只需要照着实现和调用不需要去读对方的代码。一旦有人想改接口必须改文件、写日志、通知相关方。这就把原本发生在代码里的冲突提前转移到了文档层面来解决。3.5 任务派工与进度沉淀task卡与log日志task目录下每个任务是一个md文件。一个任务卡就是一个最小工作单元包含任务的目标、验收标准、所属Agent和时间约束。# T003 报销单创建接口 - 负责人agent-bob - 依赖T002用户鉴权 - 目标实现 POST /api/v1/reimbursements 接口 - 验收标准 1. 返回 200 并创建新的 Reimbursement 记录 2. 状态初始化为 DRAFT 3. 数据校验通过金额必须大于 0 - 完成定义对应单元测试通过并更新 log/agent-bob.mdlog目录下每个Agent一个日志文件记录每天的工作内容。这解决了一个天然的问题Agent重启后什么都不记得。只要日志在它就能快速恢复上下文。另一个隐藏的好处是如果出了问题你可以直接看日志定位责任不用猜。4. 让一堆智能体并行而不踩脚的六层隔离markdown文件的真正威力在于支撑起了一套完整的隔离机制。不踩脚不是靠智能体自觉而是靠系统设计。我把整个方案中的防冲突机制归纳成六层。4.1 到底什么算踩脚先统一一下概念。踩脚的本质是多个Agent同时操作了同一个资源导致状态不一致。最典型的场景是Agent A和Agent B同时改同一个文件。A改了三行B覆盖了文件A的改动丢了。但这种冲突只是最低级的。更高频的踩脚还包括接口签名漂移A改了函数的入参B还在按老参数调用编译直接挂。语义冲突A实现了一个函数B在另一个模块里也实现了一个同名函数功能还不一样。数据模型不一致A认为status字段有SUBMITTED这个值B实现的枚举里根本没有。隐形依赖破坏A改了数据库表结构B按老结构写的SQL直接查不到数据。这些冲突的共同根源是Agent之间缺少信息同步。我们的目标不是消除共享而是把共享的部分隔离出来、变成显式可协商的。4.2 第一层隔离文件目录所有权这是最硬的隔离。每个Agent都只能在它名下的目录里写文件。我通常在项目初始化的时候就建好目录结构src/ ├── auth/ # 属于 agent-alice ├── reimbursement/ # 属于 agent-bob ├── report/ # 属于 agent-carol └── common/ # 受保护目录任何人修改都要先申请然后把这份所有权声明写进profile.md和调起Agent的system prompt。不是让Agent尽量别越界而是明确告诉它你只被允许修改这一个目录其他目录全是只读。一旦Agent试图写入不允许的文件让它直接报错而不是硬写。物理隔离是最不容易失效的隔离。4.3 第二层隔离上下文投喂隔离第二个隔离是信息隔离。每个Agent只需要看到它完成工作所需的信息不要给它看全局架构。比如agent-alice写登录功能我给它投喂的上下文是profile.md里关于它的部分、api.md里登录相关的接口定义、data-model.md里User相关的字段。它不需要知道报销模块的审批流也不需要知道报表模块用了什么图表库。为什么这么抠门因为Agent的决策质量跟输入信息的信噪比成正比。给它塞太多无关信息它就会开始思考一些有的没的。我实测过这个场景一个只需要写CRUD接口的Agent读了全局架构后居然开始重构公共代码、调整别的模块的目录结构理由是要响应架构演进。全景信息会诱使Agent产生自以为是的全局优化冲动这在并行开发里是灾难。4.4 第三层隔离接口契约隔离前面已经提过api.md的核心作用这里重点说它在并行过程中怎么起作用。多个Agent不共享代码但它们共享契约。Agent A写接口Agent B调接口两边同时从api.md读取信息。只要api.md的语义精确——参数名、类型、返回值、错误码全都定义清楚——两个Agent无需看到对方的实现代码就能平行推进。契约隔离的关键在于谁想改契约谁必须显式提案并周知。我在log里要求每个Agent在改动契约后必须写一条变更记录并注明影响模块。这个看起来简单的要求在一次实际项目中避免了两次集成事故。4.5 第四层隔离改动时序隔离有些改动天然不能被隔离比如数据库表结构调整比如公共工具函数地签名。这些全局性改动一定要有时序上的先后顺序不能在同一个时间段让多个Agent同时进行。做法是在roadmap.md里定义milestone和集成卡点。比如第1周只有agent-alice在动数据模型的定义其他Agent等待第2周数据模型冻结新功能基于冻结版本开发。全局性改动永远是串行的局部性改动才是并行的。这就像装修房子水电改造和泥瓦施工不能同时进行但两个卫生间的贴砖可以同时干。4.6 第五层隔离验收与回归隔离第五层是验证隔离。我要求每个任务卡都必须写清楚验收标准任务完成前的最后一件事就是跑相关测试。在这个隔离机制下每个Agent对自己交付的功能负责集成的时候只做契约对齐和回归测试。实际执行中我会在每个Agent的work目录下配一个独立的测试入口。Agent在合并自己的代码之前必须保证独立的测试套件全绿。集成时统一在CI里跑全量测试哪个环节挂了就定位到对应Agent的模块。这样做的结果是代码合入main分支之前每个Agent的改动都经过完整的独立验证集成阶段的问题数量会大幅下降。5. 实测三个智能体并行开发费用报销小工具整套方案说出来很完美实际跑起来踩了一堆坑。我用一个真实项目复盘一下全过程。项目叫费用报销小工具一个能让员工提交报销单、主管审批、财务打款的内部小系统。规模不大但足够体现多Agent协作的价值。5.1 一个真正的项目是怎么拆的第一步是拆任务。我没有按技术分层拆前端、后端、数据库而是按垂直切片拆一个功能从接口到存储全栈打通作为一个任务。最终拆成三个并行任务agent-alice用户注册、登录、JWT鉴权涉及src/auth/整个目录agent-bob报销单创建、提交、审批流涉及src/reimbursement/整个目录agent-carol审批记录导出、按月统计报表涉及src/report/整个目录三个任务之间只在两处相交User数据结构agent-alice那边的数据模型和审批状态字段agent-bob定义、agent-carol会读取。这两个相交点正是api.md和data-model.md里反复强调的部分。同时我单独建了一个受保护的公共库目录src/common/里面放格式化、日期处理这类工具函数。三个Agent都声明了只能读不能写。5.2 从建厂到合代码的完整工作流实际执行流程是这样的建厂我手工写好README.md、profile.md、roadmap.md、spec/api.md、spec/data-model.md的初版以及每个Agent的log文件空模板。排期把T001~T003三个任务卡分别放到task目录标注依赖关系。T001不依赖任何人T002依赖T001因为要调登录态T003依赖T002因为要读审批状态。开工我在三个终端窗口分别启动三个Agent每个Agent的system prompt里注入自己的profile信息和相关任务卡内容。它们各自在专属目录下工作互不通信。异步更新每个Agent完成一个子任务后更新自己的log。我作为工厂经理每天看一遍log发现异常就介入调整。定时集成每两天做一次集成。三个Agent的改动合并回main分支跑全量测试。集成时如果冲突我会根据api.md里的契约记录判断是谁改歪了。回归确认集成通过后更新roadmap.md把已完成的任务打勾冻结相关契约版本再放下一批任务。这套流程跑下来最大的亮点是三个Agent的实际并发工作时间超过了70%。也就是说大部分时间三路代码同时在推进而不是一个写完另一个才开始。对比之前串行开发的模式这个项目的整体交付时间压缩了将近一半。5.3 三次真实事故踩坑全记录方案再完美也有意外。这三件事让我对多智能体协作的认知又深了一层。事故一两个Agent同时改了公共工具文件第一次跑通流程的时候我用的是草率目录结构没有做充分的common目录隔离。当时agent-bob在写报销金额校验发现需要一个保留两位小数的工具函数就在src/common/里加了个formatMoney。同一天agent-carol在写报表导出也发现需要格式化金额就在另一个文件里加了formatCurrency。两者功能几乎一样只是命名不同、行为有细微差别。这事的教训是物理隔离是第一道防线但边界不清晰的公共区域一定是重灾区。解决方案就是我在第4节说的公共目录全局只读谁要改必须先建task卡再修改。事故二接口契约的版本漂移还有一次agent-alice给登录接口加了一个token过期时间字段直接改了api.md里的响应格式同时在log里写了变更记录。但agent-bob的上下文中还没加载最新的api.md所以依然按老格式解析响应集成的时候直接失败。根源在于修改契约和消费契约之间存在时间差。光有改文件写日志不够还得有强制同步机制。后来我规定契约变更后相关Agent必须在下次启动时强制重读api.md不得沿用已有上下文中的旧契约。简单说就是每次工作前都把api.md当作必读输入不允许跳过。事故三上下文污染导致的过度设计前面提过我给某个Agent投喂了过多全局信息结果它开始优化其他模块。那次事故的修复方式很朴素把系统提示词里的全局内容全部砍掉只保留目录权限和任务卡后续再没犯过。有时候我在想为什么信息喂多了会出事因为这些模型有强烈的表现欲。给它全局蓝图它就觉得自己是架构师给它跨模块调用接口它就觉得自己在重构核心链路。它们不是有意搞事而是被上下文里的信息引导着做出了最复杂的决策。所以后来我养成了一个习惯上下文里能不放的多余信息一概不放。5.4 效果对比并行 vs 串行的真实差距我拿同样的需求清单做过一次对比。串行版一个Agent从早干到晚24小时不间断完成全部功能需要约3.5天模型按小时计费集成时修bug花费1天。并行版三个Agent同时开工完成全部功能用了1.5天集成时修bug花了半天。整体交付时间从4.5天缩短到2天速度提升约55%。当然这里面有个前提任务本身能拆出足够多且边界清晰的小块。如果是一个完全没有模块边界的死胡同项目拆都拆不动那并行就是空谈。我个人的结论是多Agent并行真正的收益不是人多力量大而是通过边界划分让每个模型专注在更小的状态空间里降低了大上下文的记忆衰减和决策漂移导致的返工成本。这才是速度提升的来源。6. 这套方案的边界与升级方向说了这么多优点也得说透这套markdown软件工厂方案的适用边界。它不是一个银弹很多场景下你应该直接换更重的方案。6.1 什么项目适合用markdown软件工厂根据我这几个月的使用体感markdown编排方案最适合的项目画像是这样代码量在几万行以内目录结构清晰。模块之间的耦合度低通过接口就能交互。团队规模2~5个智能体再多就管不过来了。项目周期在一周以内或者可以拆成多个为期一周的里程碑。不需要复杂的实时状态同步定时集成可以接受。简单说它适合中小型、模块化、有清晰技术边界的项目。比如企业内部工具、小型SaaS的MVP版本、一次性的数据处理系统这些项目用此方案非常舒服。6.2 什么情况下该换更重的方案当项目规模变大、Agent数量变多的时侯markdown方案的不足就会暴露第一个瓶颈是文件冲突。几个Agent同时写同一个task或log文件会让普通文件系统面临并发写的冲突风险。虽然可以通过每个Agent只写自己的文件来规避大部分冲突但协作相关的内容比如契约变更记录还是会有竞争。第二个瓶颈是状态检索。当任务多到几十上百个全局roadmap会变得越来越难维护靠人工更新状态会变成负担Agent每次读全量文件也会浪费时间。第三个瓶颈是任务调度。markdown里没有原生的依赖队列Agent不会自动接到你依赖的那个任务已完成的通知。所有推进都得靠人工刷log、盯集成卡点。Agent一多人就成了瓶颈。这个时候就该考虑换更重的方案了。6.3 升级路径文件系统到轻量事务再到事件总线我对这套方案的升级路径做过梳理大致分三步。第一步保持markdown作为人可读的描述层但在背后加一个轻量的状态存储比如SQLite。任务卡的状态、依赖关系、变更通知全都落到数据库里markdown文件变成数据的渲染视图。这样可以解决文件并发写的问题也让状态查询从读文件变成查数据库。第二步引入消息队列或事件总线。让Agent之间通过事件来通信比如T002已完成T003可以开工。事件驱动的方式能极大地降低人工盯盘的负担。这一步需要引入一些基础组件但依然可以不依赖重量级框架。第三步接入成熟的Agent编排框架比如LangGraph这类把状态机、回退、超时等能力交给框架处理。这时候markdown文件就退回它本来的位置一份给人看的项目文档不再是运行时状态的一部分。我个人的建议是先别急着上框架用markdown跑通协作的最小闭环理解了文档契约目录隔离这两件事之后再去评估要不要加框架。框架能替你处理很多技术细节但替代不了你对协作机制的理解。最后再分享一个实操技巧。养成了一个小习惯每次Agent开工前我会在系统提示词里固定加一段话——你是这个项目的一名工程师你的任务范围参见profile.md你只能在允许的目录下写文件完成工作后更新你的工作日志。这段提示词看起来简单但实测下来冲突率降了至少一半。很多时候AI出事的根源是我们没把边界说清楚。如果你也想试这套markdown软件工厂建议从3个Agent、1周以内的项目开始先跑通一轮完整的开工-隔离-集成流程再逐步加Agent。跑通了你会发现自己真的拥有了一支AI施工队。
返回列表