☰
用CrewAI编排多智能体,实现全自动软件开发全流程
2026/10/19 4:21:59 网站建设 项目流程

我一直觉得,多智能体(Multi-Agent)这个概念,听上去很聪明,但真要做成事,得先把“炫技”两个字从脑子里删掉。我用 CrewAI 的出发点很朴素:当时我要同时维护好几个内部工具项目,每天有大量时间花在写接口、补测试、修低级 bug 上。既然大模型已经能把单点任务做得不错,那为什么不把软件开发的完整流程也拆给不同模型,让一群模型像一家虚拟公司一样互相协作?

这个想法最终落地成一个能在命令行里跑起来的东西:全自动软件开发组。它不是一个全知全能的 AI 产品,而是一条由需求分析、系统设计、编码、测试四个角色组成的流水线。文章会把我从选型、设计到踩坑的完整过程复盘一遍,把能直接抄作业的结构和代码放出来。如果你正在研究 CrewAI,或者想用多智能体处理重复性软件工程任务,这篇内容应该能对上胃口。

它能做什么?你给它一段模糊的需求描述,它会在一次完整运行里输出需求清单、接口设计、可运行的代码、测试报告,甚至二次修复意见。它能解决的问题,正是单智能体最容易栽跟头的“全局不一致”——一个模型既写代码又测代码,还要挑自己的错,结果通常是对着同一个错误无限循环。心动的话,往下看。

1. 项目立意:为什么我要组一个“AI开发小组”

1.1 单智能体的失控现场

先聊聊单智能体的典型失控表现。早前我让某个模型负责一个小系统的新增模块,它很流畅地输出了路由、数据模型、业务逻辑,看起来代码像模像样。等我把代码部署到测试环境,才发现它把登录校验统一改成了关闭状态,注释里还写着一句“为了简化调试”。它并不是能力不行,而是没有人跟它互相校验,没有一组任务边界在约束它。

这种情况五花八门。上下文一长,模型会在三十行之后忘掉第一行约定的字段名;代码风格前后不一致;测试用例为了“通过”而通过,根本不覆盖异常分支。你会发现,把太多职责压给同一个模型,模型很容易把“省事”当成最优解。有人觉得换一个更大的模型就能解决,但实测下来,等任务复杂度足够高,大模型也会踩同样的坑,只不过出错的方式更隐蔽。根子在于单个上下文窗口里的多轮推理冲突,而不是模型智力。

所以“多智能体”并不是什么花活,而是把一个大而杂的任务拆成小而有边界的小任务,每一段只用解决一个小问题。就像你不可能让一个刚毕业的同学同时做产品经理、架构师、开发、测试运维,但你可以让一个成熟团队各司其职。我把这套工程理念搬到了 Agent 编排上。

1.2 为什么是 CrewAI,而不是自己写循环

其实自己写一个循环也能实现多阶段调用,无非就是调模型接口,把上一步输出塞给下一步。但一旦你要加第三个角色、第四个角色,复杂度会迅速爆炸:要自己维护角色人设、任务状态、上下文记忆、重试机制、异常分支。编排框架真正帮我解决的,是这层“管道”标准化的问题。

CrewAI 给我的体验是抽象程度刚好。它用三个朴素概念覆盖了多数场景:Agent 定义角色,Task 定义任务,Crew 负责把 Agent 和 Task 编排成完整流程。我最依赖的有三点。第一,Agent、Task、Crew 分层清晰,代码和思路一一对应,过后也好维护。第二,内置了顺序执行(Process.sequential)和层级执行(Process.hierarchical)两种流程,不用自己处理状态机。第三,任务输出会自动拼进下一个任务当上下文,这是一个我在手写循环时最容易搞崩的部分。

如果你打算自己做,我不劝退,但你会很快发现 Prompt 拼接的坑比想象中多:上一轮输出里有大段废话,要不要全量塞?塞进去之后上下文太长了怎么办?这些在 CrewAI 里虽然也要处理,但至少框架给了统一的上下文传递机制,不用我造轮子。

2. 蓝图设计:全自动软件开发组的角色配置

2.1 四个核心角色的分工和提示词要点

不少教程一上来就规划七八个角色,又是“架构师”又是“代码审查官”又是“文档撰写员”,配置看得很过瘾,但真正跑起来维护成本很高。我建议最少配 4 类角色就能形成一轮完整开发闭环,复杂项目可以在这些角色上微调,而不是无限新增。

我选的角色如下:

角色核心目标关键产出提示词注意点
需求分析师把模糊想法拆成可验收功能点需求清单 / 验收标准明确输入、输出、异常场景,别擅自替用户做决定
系统设计工程师把需求转成模块边界接口定义 / 数据模型要求接口字段明确,避免过度设计
后端开发工程师输出可运行代码完整代码 / 技术说明严格按接口名实现,不修改外部约定
测试工程师找出缺陷并复述测试报告 / 修改建议只做验证,绝不自动修复,不替代码辩护

需求分析 Agent 是我最看重的一环。很多项目最终做崩溃,不是因为编码不行,而是需求没拆明白。我给它设计的 backstory 是一个“容易较真”的资深产品经理,所有功能点都必须有输入、输出和异常处理。提示词里我会固定加一句:如果原始需求缺少必要信息,先明确写出假设,再用“待确认”标注,不要擅自替用户做决定。这样能极大减少下游编码 Agent 乱猜的可能性。

系统设计 Agent 经常被人省略,但它才是多智能体协作里“阶段隔离”的关键。你要让设计 Agent 只看需求清单,不要看用户的原话,它产出的接口和数据模型就会很克制。要是让它连用户的碎碎念一起看,设计文档很容易被情绪化描述带偏。它的输出应该是带字段说明的接口文档,而不是一篇把事情讲得很热闹的散文。

编码 Agent 的 backstory 不需要太丰富,你只要给它一个明确且保守的身份就够了。我会在提示词里强调“按照给定的数据字段和接口名称实现,不要改动外部约定”,否则它很容易根据自己的代码审美重构接口,造出一套全新的字段名,然后连带把下游调用处全改一遍。这个问题的出现率比我预想高很多,几乎是多智能体项目里最容易翻车的地方。

测试 Agent 的存在意义,是彻底不让写代码的模型检查自己的代码。真人团队里尚且知道不能既当运动员又当裁判,模型也一样。我把测试 Agent 定义为“只完成黑盒验证,不负责修复”,并要求它输出缺陷列表和复现步骤。这样它会更直白地指出问题,而不是用一句“功能基本实现,但有一点点小问题”来和稀泥。

2.2 任务流程与交接规则

整个流程是一条标准顺序链:需求分析 Agent 拿到原始需求,输出需求清单;系统设计 Agent 拿到需求清单,输出接口文档;编码 Agent 拿到接口文档,输出实现代码;测试 Agent 拿到实现代码,输出测试报告。

这正好对应 CrewAI 里的 Process.sequential。每个 Task 的输出默认会进入下一个 Task 的上下文,所以不需要手动拼接。但这里有个重要细节:上下文自动传递,不代表它会做去重和裁剪。测试 Agent 的输出如果又长又碎,后面再接修复任务时,大量无关内容会挤占上下文窗口。我的做法是在每个 Task 的 expected_output 里写清楚要结构化内容,让模型输出类似“缺陷编号、函数名、问题描述、复现步骤”的紧凑列表,而不是一大段“想一想再说”的废话。

你可以把每个角色的输出理解为下游系统需要的“文件”,需求清单是接口文档的输入,接口文档是编码的输入,测试报告是修复的输入。让它们用文件,而不是演讲词去交接,是整个流水线顺畅的基础。

3. 开始实操:CrewAI + Python 跑通第一版

3.1 环境准备与依赖安装

先说环境。Python 3.10 以上都可以,我自己是在 3.11 上跑的。安装很简单,一条命令:pip install crewai,然后安装你自己模型服务商提供的 Python SDK。CrewAI 本身不绑定某个特定模型,Agent 初始化时通过 llm 参数传一个模型连接对象进去就行。所以任何提供兼容聊天接口的模型服务都能用。

我这里不写某个平台的专属代码,因为各家的 client 差异比较大。你只需要先初始化一个能返回对话结果的 llm 对象,比如:

# 这段按你的模型服务商 SDK 来写,下面只是示意 llm = ChatLLM( api_key="你的密钥", base_url="你的服务地址", model="你选择的模型名" )

只要这个对象具备“输入消息、输出回复”的能力,CrewAI 就能直接接收。很多教程会把模型名和密钥写死,实际落地时我建议从环境变量读取,避免密钥泄露或者频繁改动。

3.2 最小 Crew 的代码实现

有了 llm 对象之后,最基础的 Crew 代码可以这样写。下面这段就是我可以直接放进工程里的最小模板:

from crewai import Agent, Task, Crew, Process # 假设 llm 已经初始化好,参考上面的示意代码 requirement_agent = Agent( role="需求分析师", goal="把模糊想法拆成明确、可验收、有异常边界的需求清单", backstory="你是一名资深需求分析师,擅长从口语化描述中抓取业务规则,并主动识别不合理的假设。", llm=llm, verbose=True, allow_delegation=False ) design_agent = Agent( role="系统设计工程师", goal="根据需求清单输出模块边界、接口定义和数据模型", backstory="你是一名系统设计工程师,追求接口清晰、模块职责单一。", llm=llm, verbose=True, allow_delegation=False ) coding_agent = Agent( role="后端开发工程师", goal="根据接口定义输出可直接运行的代码,并严格遵循约定", backstory="你是一名严谨的后端开发工程师,实现功能时严格遵循接口和数据字段,不使用未经确认的新特性。", llm=llm, verbose=True, allow_delegation=False ) testing_agent = Agent( role="测试工程师", goal="对代码做黑盒验证,输出缺陷列表和复现步骤", backstory="你是一名独立的测试工程师,不负责修复代码,只负责寻找缺陷。", llm=llm, verbose=True, allow_delegation=False ) task_requirement = Task( description="用户需求:{feature}。请整理成PRD,列出功能点、输入输出、异常场景。", expected_output="一份Markdown格式的需求清单,包含功能点列表和验收标准。", agent=requirement_agent ) task_design = Task( description="根据需求清单设计系统,输出接口定义和数据模型。", expected_output="API文档和数据结构定义。", agent=design_agent ) task_coding = Task( description="根据接口文档实现核心模块,给出完整代码。", expected_output="可直接运行的Python代码文件内容。", agent=coding_agent ) task_testing = Task( description="根据实现代码和需求清单完成黑盒测试,输出测试报告。", expected_output="测试结果、问题列表、建议解决方案。", agent=testing_agent ) crew = Crew( agents=[requirement_agent, design_agent, coding_agent, testing_agent], tasks=[task_requirement, task_design, task_coding, task_testing], process=Process.sequential, verbose=True ) result = crew.kickoff( inputs={"feature": "开发一个待办事项管理API,支持增删改查和状态筛选,数据需要持久化保存。"} ) print(result)

这段代码跑起来以后,四个角色会按顺序执行。由于 start 任务之间通过上下文自动衔接,你在代码里其实看不到“把上一个结果传给下一个”的显式操作,这是 CrewAI 比较顺手的地方。

跑完以后,result 是聚合后的完整输出。如果 verbose 开着,控制台会打印每个 Agent 的思考过程和决策轨迹,对一个初次尝试的人来说,这串日志很适合用来理解执行顺序。但生产环境不建议开,因为所有这些过程日志都会额外消耗 token,也会拖慢整体速度。

3.3 核心参数与取舍

CrewAI 的参数不多,但有几个值得认真调。

Process.sequential 是最简单的编排方式,适合流程稳定、角色明确的任务。Process.hierarchical 会在 Crew 中加入一个 manager Agent 自动委派任务,适合角色非常多、任务路径不确定的场景。但层级模式对模型的要求更高,消耗也更大,我目前只有角色超过六个时才会考虑它。

allow_delegation 值得单独说。在 hierarchical 模式下,Agent 可以互相委派任务;但在 sequential 模式下,我通常设为 False,避免编码 Agent 在遇到一点不确定时把事情推给其他 Agent,造成脱离控制的多轮交流。多智能体的价值是分工,不是闲聊,把每个 Agent 限制在自己的职责范围内更可控。

Agent 的模型参数通常在初始化 llm 对象时配置。不同类型角色适合不同温度:编码 Agent 温度我习惯设 0.1 到 0.2,让它保守地实现功能;需求分析 Agent 可以设在 0.5 左右,稍微留一点理解不同表述的余地。温度不是越高越好,尤其不要对编码任务用高温度,否则你会看到代码里出现大量风格各异、不知所云的写法。

还有一个容易忽略的参数:max_iter。它控制 Agent 在单个任务里的最大自省轮数。默认值对复杂任务可能不够,但对简单任务又会白白浪费 token。我一般会结合任务描述长度调整,如果只是一个接口生成任务,max_iter 控制在 3 到 5 就差不多了。

4. 模拟真实项目:任务链的完整闭环

4.1 任务链的完整定义

上一节的四段顺序流水线只能做一次性的“从左到右”,还没法做到“测试发现问题、开发修复、测试复测”的闭环。CrewAI 的 Task 支持一个 context 参数,可以用它构建修复任务。

我把闭环写成六个 Task:需求、设计、编码、测试、修复、复测。其中修复任务的上下文是测试报告,复测任务的上下文是修复后的代码。关键代码如下:

task_fix = Task( description="下面的测试报告标注了缺陷。请根据缺陷列表修复代码,只改动必要部分,把最终代码写出来。", expected_output="修复后的完整代码,以及每处修复的简要说明。", agent=coding_agent, context=[task_testing] ) task_retest = Task( description="复测修复后的代码,确认缺陷已解决,输出最终测试结论。", expected_output="通过的测试项和残留问题列表。", agent=testing_agent, context=[task_fix] ) crew = Crew( agents=[requirement_agent, design_agent, coding_agent, testing_agent], tasks=[ task_requirement, task_design, task_coding, task_testing, task_fix, task_retest ], process=Process.sequential, verbose=True )

这里值得注意的地方是,task_fix 在流程上仍然排在 task_testing 后面,但如果不写 context 参数,CrewAI 只会给它前一个 Task 的输出。加上 context 以后,它就能同时看到测试报告和测试 Agent 的主张,更准确地知道要改什么。这个机制在类似“评审后修改”的场景里非常好用。

4.2 一次真实运行观察

用这个闭环跑一个简单后端模块时,我观察到了特别典型的现象。第一次执行完,测试 Agent 输出里明确写了两个缺陷:

  • 待办列表接口在分页参数非法时返回了 500,而不是约定好的 400;
  • 更新接口没有对空标题做校验,导致空白字符串可以被保存。

随后修复 Agent 根据测试报告改动了对应函数,加入参数校验和字段去空逻辑,并在修复说明里解释了每一处改动。复测 Agent 再跑一遍,两个缺陷被标记为“已解决”,同时没有提出新的残留问题。从代码结构上看,修复之后的代码整体风格仍然和原始版本一致,没有出现“为了修一个问题把整个文件重写一遍”的情况。

这个结果不算是特别惊艳,但很稳定。稳定在工程里比炫技重要得多。整套流程跑下来,这个小模块大约消耗了 2.8 万 token。如果功能复杂度继续增加,token 消耗会成倍上涨,这也是后面要重点说的问题。

4.3 闭环修正机制的设计心得

使用一段时间之后,我对“闭环修正”有这么几条体会。

第一,不要在一个 Agent 内部同时做“写代码”和“反思”。如果你在编码 Agent 的提示词里加一句“写完以后自己检查一下”,它会倾向于说自己没有问题,因为模型在同一上下文里很难否定自己刚才的输出。让测试 Agent 以一个独立身份找毛病,效果完全不同。

第二,反馈信息必须结构化。测试报告要包含“函数名、输入参数、预期结果、实际结果”,而不是一句“感觉不太对”。只有足够具体的反馈才值得修复 Agent 去执行。设计 Task 时我就把 expected_output 限定为这种列表格式,产出质量明显提升。

第三,要设置修复轮数上限。我通常限制最多两轮,如果两轮后同一个问题还是原样,说明要么模型理解不到位,要么任务描述本身有歧义。这时候再跑多少轮都是烧钱,不如人工介入,把问题点直接挑明。

5. 踩坑实录与避坑建议

5.1 常见问题快查表

我把这段时间反复踩过的坑整理成一张速查表,方便你直接对照排查:

症状原因解决方案
Agent 不按约束修改,改动外部约定角色提示词里的约束不够强在任务描述末尾加上“只改动必要部分,不修改未提及的接口”
上下文爆长,成本失控把上一步输出完整传给下一步,没有裁剪把 expected_output 设定为结构化摘要,必要时手动压缩后再传入下游
测试 Agent 总替代码说话测试角色缺乏独立性要求在 backstory 里强调“独立审查、不负责修复、不要为代码找理由”
闭环一直循环不终止修复任务允许改动范围过大,模型反复“重写”严格限定改动范围,并设置最大修复轮数
Agent 上下文丢失原始需求上游输出被设计或代码任务改写太多让需求 Agent 以清单/JSON 格式输出,设计任务要保留需求引用

第一类问题,也就是 Agent 不按约束修改外部约定,出现频率最高。根因是编码 Agent 容易被“修复错误”的指令带偏,顺手优化了所有它认为“不够好”的地方。我在每个编码类任务的 description 里都会加一句“不要重构无关部分”,很大程度上抑制了这个问题。

第二类上下文爆长的根因,是 CrewAI 默认会拼接上下文,但不做压缩。当任务链变长,文本会在每个节点累加。我的应对手段是显式地要求每个任务输出“紧凑、字段化”的内容,特别是中间层级的分析,少允许模型输出长段落。如果任务链特别深,我甚至会在代码里手动截断无关输出,只把关键字段传给下一个 Task。

上下文的干净程度会直接影响最终代码质量。越是后面的任务,上下文里真正有用的东西占比越低,模型越容易被无关信息带偏。这是我确认过的规律,不是玄学。

5.2 成本和资源限制的权衡

多智能体项目最容易被忽视的就是成本。四个角色的 token 消耗不是四段独立对话相加那么简单,因为每一环节的输出都变成下一环的输入,上下文会重复,等于“滚雪球”。一个三百行代码的模块,完整跑一遍闭环可能就要十万 token 左右,如果修复轮数多,翻倍也很正常。

我有几条实用经验。一是让不同角色用不同大小的模型。需求分析和测试可以用轻量模型,把重型模型留给编码环节,这样能在保证质量的同时明显减少开销。二是测试输出默认很长,我会在任务描述里明确要求“只输出对应缺陷的简短描述和复现命令”,否则测试 Agent 会把每个函数从头到尾点评一遍。三是生产环境关闭 verbose,那玩意儿的逐字日志真的不便宜。四是给项目设一个每月预算上限,一旦超出就强制切换到更小的模型或者减少闭环轮数。

这里要特别提醒一件事:不要把多智能体当成免费劳动力。它在结构上比单智能体更费资源,但换来的是更高的稳定性和可维护性。你自己得算清楚这笔账,如果只是写个一次性脚本,单智能体反而更划算。

5.3 我给新人的几条建议

最后分享几条实践后的想法。

第一,先把手写流程跑通一次,再上框架。只要你亲手拼过一次上下文,你才会真正理解 CrewAI 帮你在背后处理的那些事,后面调参也有方向感。没有这层经验,遇到诡异问题容易瞎猜。

第二,Agent 之间协作的瓶颈,往往不是模型能力,而是“输出格式”。我给团队的各个角色规定了输出规范,它们之间的对接才会顺畅。你可以像设计 API 一样设计每个 Task 的 expected_output,让模型输出像一个结构体,而不是一篇美丽但难解析的散文。

第三,多智能体最适合的是“流程可复制、结果可验证”的任务。给你一套模糊需求去生产一套没有测试的代码,它大概率比你想象的不可靠;但如果你让测试 Agent 输出可执行的冒烟测试用例,再让编码 Agent 照着修,稳定性就会高一大截。

全自动软件开发组听起来全能,其实它更像一屋子能力不错但有点固执的同事,你需要给每个人讲清职责边界和交接格式,再制定一套有限次的修订机制。看到这些 Agent 在终端里跑成一整条流水线的那一刻,我最大的感受是:以前我是“让模型替我做一坨事”,现在更像是“给这些模型安排了一个它们各自擅长、也互相制衡的办公室”。这种感觉挺特别的,至少现在,我手里那些重复工程任务再也不会叫我一个人从早写到晚了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询