CrewAI实战:用Python多智能体拆分角色,搭建自动化内容生产流水线 前阵子我想让AI帮我产出一份比较完整的市场调研报告单开一个对话框问模型出来的内容总是“全而不深”。后来我换了个思路不再让一个模型同时干三份活而是把工作拆成三个人来干——一个找资料、一个写初稿、一个做校对。效果一下子就好了很多。这个思路落地的工具就是标题里写的CrewAI。CrewAI是一个用Python写的多智能体编排框架核心就是让你能用代码定义一个“数字小团队”给每个成员分配不同的角色、任务和协作规则然后让它自动跑完一整条工作流水线。这篇文章我不讲那种动辄上千行的架构设计就围绕“角色分配”这件事带你从零跑起来第一个多智能体项目。适合完全没写过智能体的新手也适合已经手动调过Prompt、想往多智能体方向走一点的人。1. 为什么单个提示词搞不定的活儿要拆给多个智能体干1.1 一个Prompt包打天下卡在哪儿你可以试一下让模型“帮我调研一下新能源行业然后写一篇有数据、有观点的分析文章”。这种看似合理的Prompt实际跑出来通常有两个毛病第一它会把“调研”和“写作”混在一次推理里完成。模型一边在脑子里找知识一边已经在组织文章结构结果就是数据没有出处、观点没有论证链条整篇文章像是把脑海里所有相关词句串了一遍。第二没有一个独立的“校对角色”来检查逻辑漏洞。单个模型完成任务就收工了它不会回头质疑自己这个结论和前面引用的数据矛盾了怎么办第二段和第四段说的是同一个意思怎么办这不是模型不够聪明而是我们在用一个模型承担三个岗位。任何一个正常人被要求“一边查资料一边写稿一边质检”表现都会打折扣大模型也一样。1.2 多智能体的本质把大Prompt拆成小岗位CrewAI的思路很朴素与其让一个Agent干完所有事不如把一条工作流拆成多个节点每个节点只关注一小块目标然后由一个编排器把它们串起来。这里说个我一直觉得特别贴切的类比你把CrewAI当成一个部门Agent是部门里的员工Task是工单Process是你们部门的工作流程。员工接到工单、完成交付下一个人拿到上一个的产出继续干活。大家职责清晰产出自然比一个人干四份活要稳定。所以第一个要建立的概念就是多智能体不是“很多模型在开会”而是“多个职责不同的模型在接力”。2. 动手前的关键认知CrewAI里的角色不是“人格”而是“岗位说明书”2.1 三个核心概念Agent、Task、Crew我先用一个最小模型把CrewAI的所有关键概念讲清楚你后面看代码就不会晕。Agent智能体相当于团队里的一个成员。在CrewAI里Agent不是一个人格而是一份岗位说明书。它通常由三个字段决定“这个员工是谁、要干什么、怎么干”role岗位名比如“资深行业研究员”。模型会把这个当成自己的身份标签。goal工作目标也就是你期望它完成什么KPI。backstory背景设定相当于入职培训材料可以写清楚工作习惯、关注点、输出风格。Task任务一张工单。它描述“这件事具体做什么”以及“交付物长什么样”。一个Agent可以干多个Task但更推荐一个Task对应一个明确的交付物。Crew团队把Agent和Task组合起来的地方。你需要在这里声明团队里有谁、任务有哪些、按什么顺序执行。Crew就是那个编排器跑起来之后会按照你配置的流程自动推进。一句话总结Agent定义“谁来做”Task定义“做什么”Crew定义“怎么配合着做完”。2.2 role、goal、backstory为什么会直接影响输出质量这是整篇最容易被忽略的点。CrewAI在底层调用大模型时会把Agent的role、goal、backstory以及当前Task的description、expected_output组装成系统提示词和任务提示词。也就是说你在这些字段里写了什么模型就会按什么人设、什么目标、什么工作习惯来回答。写“你是一个研究员”和写“你是一名专注新能源产业链的资深分析师擅长从年报数据中提炼结论会用对比表格呈现结果”模型产出的内容深度完全不同。这不是玄学而是提示词工程搬到多智能体框架里之后变成了代码里明确定义的字段。后面第五章我会放一组具体对比你先记住这个结论角色字段写得好不好直接决定团队产出质量。2.3 Sequential与Hierarchical两种团队协作模式怎么选CrewAI默认支持两种流程模式维度SequentialProcessHierarchicalProcess协作方式任务按顺序执行先A后B再C有一个Manager Agent负责拆解和分发任务适用场景流程固定的流水线任务不固定、需要动态决策的场景成本相对较低谁干多少活很清楚更高Manager本身也要消耗Token上手难度低新手首选较高需要设计Manager的角色和目标新手我的建议非常直接先别碰HierarchicalProcess。你只需要把任务按先后顺序排好用SequentialProcess跑通一遍理解Agent之间是怎么传递信息的后面再考虑要不要加一个“小领导”。3. 实战搭建用三位“数字员工”搭建一个内容生产小团队3.1 环境准备与安装第一步当然是装环境。CrewAI的安装非常简单pip install crewai crewai-tools建议你把CrewAI装在一个干净的Python虚拟环境里避免和其他项目的依赖冲突。Python 3.10到3.12基本没什么问题3.13版本有的依赖可能还没跟上容易踩坑。装完之后你需要准备一个大模型API的Key。CrewAI默认走OpenAI格式的接口在命令行设置环境变量即可export OPENAI_API_KEYsk-你的密钥如果你用的是其他大模型API只要服务商提供OpenAI兼容接口通常也能直接适配只是需要在代码里改一下base_url参数。提示如果你在Windows上开发用set OPENAI_API_KEY你的密钥在PowerShell里用$env:OPENAI_API_KEY你的密钥。3.2 完整代码研究、写作、编辑三段式流水线下面这段代码是我跑通第一个CrewAI项目时用的完整实现你直接复制到一个.py文件里就能运行。import os from crewai import Agent, Task, Crew, Process # 如果你已经把Key写进环境变量这里可以不用显式再写 os.environ[OPENAI_API_KEY] 你的API Key # 1. 定义一个Agent行业研究员 researcher Agent( role资深行业研究员, goal针对指定行业主题收集并整理出有数据支撑的关键趋势, backstory( 你在咨询公司做了8年行业研究擅长从公开报告中提炼核心数据。 你习惯用简洁的分点结构输出发现并会标注每条结论的数据来源类型。 ), verboseTrue, allow_delegationFalse, ) # 2. 定义一个Agent内容撰稿人 writer Agent( role商业分析文章撰稿人, goal将研究员的输出材料改写成结构清晰、语言流畅的专业文章, backstory( 你是一名商业编辑写过多篇深度行业分析。 你重视逻辑递进喜欢用事实说话避免空泛的形容词。 ), verboseTrue, allow_delegationFalse, ) # 3. 定义一个Agent内容校对编辑 editor Agent( role严格的内容校对编辑, goal检查文章的逻辑、数据一致性和表达清晰度并给出最终修改稿, backstory( 你做文字编辑工作超过10年对前后矛盾和数据来源不可靠的内容极其敏感。 你只输出修改后的完整文章不输出修改理由清单。 ), verboseTrue, allow_delegationFalse, ) # 三个Task按顺序衔接 research_task Task( description( 调研‘2025年充电桩行业的发展趋势’ 重点关注市场规模、政策方向、技术路线和主要企业的布局。 ), expected_output( 一份包含5条关键趋势的调研要点每条趋势包含具体数据或事实来源 ), agentresearcher, ) writing_task Task( description( 根据研究员的调研结果撰写一篇完整的行业分析文章。 文章需要有引言、分论点、数据支撑和总结。 ), expected_output一篇不少于800字的中文商业分析文章, agentwriter, ) editing_task Task( description( 对撰稿人产出的文章进行严格校对。重点检查数据是否前后矛盾 论点是否有数据支撑是否有重复表达。直接输出修改后的完整文章。 ), expected_output修改后的最终完整文章, agenteditor, ) crew Crew( agents[researcher, writer, editor], tasks[research_task, writing_task, editing_task], processProcess.sequential, verboseTrue, ) result crew.kickoff() print(最终输出) print(result)这段代码逻辑很直接三个Agent分别对应“找料的人”“写稿的人”“改稿的人”。三个Task按顺序排列先调研再写稿最后校对。crew.kickoff()就是启动整个团队工作的入口。我强烈建议第一次跑的时候把verboseTrue打开这样你能在终端里清楚看到每个Agent收到了什么指令、产出了什么内容对理解工作流非常有帮助。3.3 运行起来后你会看到什么运行上面这个脚本终端会输出类似这样的日志不同版本可能略有差异当前正在执行的任务名称比如“研究充电桩行业发展趋势”。被指派执行的Agent角色比如“资深行业研究员”。该Agent思考或者产出内容的过程摘要。最终一轮跑完后打印出整个Crew的综合输出。第一次看到这个运行过程你会非常直观地感受到什么叫“分工”研究员只输出调研要点撰稿人只基于要点写正文编辑只做修改。三个人各干各的流水线清晰可见。注意运行时间和消耗Token会明显高于单次Prompt调用。因为你在跑三个智能体每一个智能体都至少有一次完整的模型调用。任务越长、Agent越多时间越长这是正常现象不用担心。4. 角色分配里的真实“坑”我从三个失败案例学会的4.1 角色职责重叠两个Agent抢着做同一件事我第一次搭建这个团队时给“研究员”的角色描述里顺手写了“并输出一篇完整的分析文章”结果就是研究员在调研阶段就把文章写完了后面写手被迫“二次加工”输出的内容反而变啰嗦了。排查思路很简单打开verbose日志看某个Task实际是由哪个Agent执行的再看他收到的description原文基本就能定位问题。我当时是把自己写的角色描述当成了“给机器的指令”忘了它同时也是“给模型的人设边界说明”。这个问题在CrewAI里非常常见因为Agent的goal和Task的description是两套独立的字段。你要记住一个判断标准**如果一个Task的描述能被另一个Agent的Role顺理成章地完成说明你的分工有问题。**我在实践里的做法是写角色时先反问一句“这个岗位负责什么、不负责什么”并在backstory里明确写“不负责的事情不要做”。4.2 上下文传递断裂第二个Agent天然不知道第一个Agent干了啥这个坑比职责重叠更隐蔽也更重要。在CrewAI的执行过程中如果一个Task没有显式声明它需要上一个Task的输出那下一个Task理论上只有一个任务描述并不天然带着上文。结果就是撰稿人可能完全无视研究员写的调研要点自己凭空生成了文章或者把研究员提到的数据给改掉了。排查链路是这样的先看最终文章里的数据是不是研究员原文提到的再往前翻日志看“writing_task”这个任务的输入上下文里有没有包含“research_task的输出”。如果没有说明上下文没有传递过去。解决办法也很简单。在CrewAI里Task支持声明它的数据来源writing_task Task( description撰写文章, expected_output800字分析文章, agentwriter, context[research_task] # 显式声明依赖上一环节的产出 )加了context参数CrewAI就会把research_task的输出打包成上下文传给撰稿人。这种显式声明依赖的方式比我一开始“靠运气传递”要稳定得多。4.3 授权和工具不足Agent明明能调用工具却一直不用第三个坑是工具调用。CrewAI里的Agent默认是不会自己去搜网、查数据库、跑代码的除非你明确给它挂载工具。很多新手以为Agent就是全能的给一个角色就能自动上网查资料这不对。我当时让“研究员”去查某个行业最新市场规模它直接基于训练数据里的旧数字编了一个结果在日志里看起来还挺像那么回事。后来我才意识到应该给它挂一个联网搜索工具或者至少让它在输出里注明数据来源的时间。如果你需要联网信息可以接CrewAI配套的工具包或者自己定义一个搜索工具。这个我再第六节展开讲。这里你先记住结论Agent不是全知全能的不挂工具它就只能靠模型自己的知识库硬编。5. 角色定义质量决定团队上限一组真实输入对比5.1 敷衍版角色定义运行时会发生什么只看定义不跑任务你很难感受到差异。我在这里放一组实际对比让你看看同样的任务角色定义敷衍和精细会造成多大差别。敷衍版是这样的researcher_wrong Agent( role研究员, goal研究充电桩行业趋势, backstory你是一个研究员。, )看起来是不是很像你平时写Prompt的样子实际跑下来它输出的调研内容大概是“充电桩行业近年来快速发展市场规模持续扩大技术不断进步”。全是正确但空泛的废话没有具体数据没有可验证的结论甚至“市场规模持续扩大”这种话放在任何行业都成立。问题出在哪你只说“你是一个研究员”模型不知道自己是哪个领域的研究员、为谁研究、研究完用什么格式汇报。它只能选择最安全、最通用的表达方式于是产出了正确的废话。5.2 精确版角色定义相同成本下输出差距同一产品跑出的精确定义版本researcher_good Agent( role新能源产业链资深研究员, goal围绕充电桩行业给出5条有数据支撑的关键趋势判断, backstory( 你长期跟踪充电桩产业链熟悉上游元器件、中游制造、下游运营三个环节。 你输出结论时会主动提供数据支撑并用分点列表组织内容。 本次研究服务于一篇面向行业从业者的分析文章。 ), )跑下来的输出会明显不同它会主动拆成5条趋势每条都带市场规模、增长率、或典型企业的布局方向并且会用“根据某机构统计”这种来源性表述。差距的核心就是你把模型的“人设”划定得越具体它在生成时搜索自己知识库的范围就越聚焦发散的概率越低。这就是为什么我一直强调CrewAI里的Agent不是在扮演角色而是在按照岗位说明书工作。5.3 让多个Agent“交接”时信息不丢的三个技巧结合前面的实战经验我整理了三个让交接更稳定的技巧你可以在自己的团队里直接用。第一每个Task的expected_output要写清楚“交付格式”。比如“分别用5个要点输出每个要点必须包含一个具体数据”这个字段是模型判断“任务是否完成”的标准写清楚它后面交接时下游Agent拿到的材料才会规整。第二下游任务描述里主动“点”一下上游产出。在description里写“请严格基于研究员的调研结果展开不要新增未经证实的数据”这能有效减少下游Agent自己发挥的空间。第三善用context参数。CrewAI提供显式的任务依赖绑定把上游Task传给下游Task比让下游Agent“凭感觉”理解更可靠。你可以在下文第5.2节代码看到这个写法的具体位置。6. 从“脚本团队”到“能接活的小组”进阶扩展思路6.1 给数字员工配上工具箱跑通基础流程之后你大概率会发现一个问题Agent光靠模型自带知识很多事实性的东西会编。解决思路是给Agent挂工具。CrewAI里自定义工具非常简单用tool装饰器就能把一个函数转成Agent可调用的工具from crewai.tools import tool tool(读取本地资料文件) def read_local_file(file_path: str) - str: 读取指定路径下文本文件的内容 with open(file_path, r, encodingutf-8) as f: return f.read()然后在定义Agent时传入researcher Agent( role行业研究员, goal结合本地行业资料完成调研, backstory你习惯基于一手资料做分析。, tools[read_local_file], )这样研究员就能在需要的时候读取你准备好的资料文件而不是凭空编造数据。类似的工具你还可以自己写查询库存、读取数据库、调用内部接口等只要能返回文本信息都可以封装成工具给Agent用。实操心得工具一次不要挂太多。挂3个以上的工具模型在“该用工具还是不该用工具”之间反复权衡反而容易出错。我一般一个Agent最多挂2到3个和它的职责强相关的工具。6.2 换掉默认模型本地部署和更便宜的方案CrewAI默认调用云端大模型API但如果你做的是多轮内部测试Token消耗会上升得比较快。有一个省钱思路是本地部署模型通过Ollama这类工具跑一个本地模型CrewAI也支持自定义llm参数。from crewai import LLM local_llm LLM( modelollama/你的本地模型名, base_urlhttp://localhost:11434, ) researcher Agent( role行业研究员, goal完成调研, backstory你是一名研究员, llmlocal_llm, )本地跑模型的好处是免费、数据不出本机缺点是小参数量模型的复杂推理能力和工具调用效果确实不如云端大模型。我的建议是日常调试用本地模型省成本跑正式内容用云端大模型保障质量。6.3 让团队记住上下文memory与知识库的简单玩法前面提到过CrewAI的多智能体协作默认是一次性执行一个任务结束就翻篇了。如果你希望Agent能记住跑过的任务或在后续对话中复用之前的信息可以用记忆机制。在创建团队时开启记忆crew Crew( agents[researcher, writer, editor], tasks[research_task, writing_task, editing_task], processProcess.sequential, memoryTrue, )开启后Agent会保留短期记忆CrewAI还有长期记忆配置可以让Agent在后续多次执行时复用经验。不过要注意记忆功能会额外消耗存储和Token简单的流水线任务没必要开。我第一次跑CrewAI项目的时候最大的体会是多智能体的门槛其实不在写代码而在“拆活”的思维方式。你要能忍住不让一个Agent干所有事强迫自己把工作拆成一个一个小岗位。只要这个思维转过来CrewAI剩下的就是几个字段的事。建议你先照着上面的代码把三个人跑通再慢慢加工具、加流程、换模型最后你会发现自己已经能设计一支能接真实任务的数字小团队了。