☰
多智能体框架实战:用CrewAI搭建多角色协作流程
2026/10/6 6:45:07 网站建设 项目流程

聊一个我最近重度使用的东西:开源社区5.9万Star的多智能体框架。前一阵我在做一个自动化运营工具,单Agent一直折腾得不顺,让一个AI大模型既查资料又写文案又做质检,指令一多就东一榔头西一棒子。后来我把这套多智能体框架接进去,把任务拆给不同的Agent协作完成,效果立刻不一样了。如果你已经用过大模型API、写过一点Python脚本,但还在纠结“多智能体到底怎么落地”,这篇教程应该能帮你省不少时间。我不会只贴官方文档的Hello World,而是从概念、安装到跑通一个多角色协作任务,把实际踩过的坑一并讲清楚。

开始之前先声明一下,这套框架仍然在快速迭代,我用的版本和官方最新版可能有小出入。文中代码以我实测通过的版本为准,API有大变化时建议顺手查一眼官方文档,思路是通用的。

1. 为什么一个“AI调度后台”能拿到5.9万Star

1.1 从“单体Agent”到“多智能体协作”的转变

很多人刚接触多智能体时会有一个误解,以为它是某种更聪明的单一大模型。其实它的核心思路特别朴素:与其让一个全能型AI干所有事,不如让多个专业型AI各自负责一个环节,再把结果拼起来。

我打个比方。单体Agent就像一个全能超人,写周报、查资料、做数据分析都能上手,但任务一复杂、环节一多,它的上下文会被各种指令塞满,最后容易互相干扰。你让它先查资料再写总结,它写到一半可能忘掉资料里的关键数据;你让它自己检查错误,它往往看不出自己埋的雷。而多智能体框架更像你在组织一个小团队:有人专门搜集信息,有人专门整理逻辑,有人专门写最终稿。每个人只管好自己的岗位,交接的时候只传最重要的成果物,整个流程会清晰很多。

从我实测的感受来说,单体Agent适合那种十几分钟就能搞定的一次性任务;一旦任务涉及多个子步骤、需要不同类型的信息,或者你希望不同环节用不同的大模型来处理,多智能体编排的价值就体现出来了。这也是这套框架能拿到5.9万Star的核心原因——它把“组织AI团队干活”这件事的成本大大降低了。

1.2 这套框架实际做了什么

如果拆开看,这个框架做的事情可以分成四块:

  • Agent管理:定义每个AI的角色、目标、背景故事,告诉它“你是谁、你负责什么、你说话什么风格”。
  • Task调度:把任务分配给对应的Agent,明确输入是什么、期望输出是什么。
  • 流程编排:决定Agent是按顺序干活,还是由某个“管理者Agent”动态规划下一步。
  • 工具挂载:给Agent挂上搜索、代码执行、文件读写等外部能力,让它能真正动手,而不只是动嘴。

说实话,这些能力如果你愿意折腾,用纯代码也能实现,但框架帮你把最繁琐的部分封装好了:上下文传递、重试机制、输出解析、日志打印。开源社区之所以认它,一个重要原因是它的抽象层级刚刚好——不复杂到劝退,也不简陋到只能跑Demo。你不需要学一套完整的分布式系统设计,只要懂Agent、Task、Crew这几个概念就能开始干活。

1.3 什么样的人适合现在入手

我在使用过程中接触过不少来咨询的人,总结下来适合这个框架的人大概有三类:

  • 已经在用大模型API做自动化脚本,发现单条Prompt处理复杂流程时效果不稳定;
  • 有明确的业务场景,比如内容生成、数据分析、客服工单处理,需要把流程沉淀成可复用的任务;
  • 想在架构上留出扩展空间,未来可能要接入更多工具和模型。

反过来,如果你只是想快速让AI生成一段文本,那直接用大模型API反而更简单;如果你的核心诉求是极低延迟的实时对话,多Agent串联带来的额外耗时你未必愿意承担。工具选型最重要的一点,是先确认你是不是真的需要它。

2. 动手前先把四个核心概念装进脑子

2.1 Agent:给大模型一份“岗位说明书”

很多教程会直接把Agent翻译成“智能体”,但这个称呼太虚。我在实际使用中更愿意把它理解成一份岗位说明书加上一个大脑。这个大脑是大模型,岗位说明书是围绕角色的约束条件。

创建一个Agent的代码非常直白:

from crewai import Agent, LLM llm = LLM( model="openai/deepseek-chat", api_base="https://api.deepseek.com/v1", api_key="你的key", ) researcher = Agent( role="资深行业研究员", goal="从公开资料中提取行业最新动态", backstory="你有十年券商行研经验,擅长从数据中挖掘趋势,输出内容严谨克制。", llm=llm, verbose=True, )

注意看这三个字段:role是岗位名称,goal是核心目标,backstory是背景设定。我一开始觉得backstory只是角色扮演的花架子,后来发现它对输出风格的影响非常大。你告诉它“你有十年券商行研经验”,它写出来的内容会更结构化;你告诉它“你是一个社区热心群友”,它会不自觉地带口语化语气。这不是玄学,而是模型在根据上下文调整生成概率。

一个容易忽略的地方是:每个Agent最好只负责一类职责。我之前试过让一个Agent既做资料搜集又做结论判断,结果它为了让自己“看起来专业”,在资料不足时也会编出看似合理的结论。职责拆得越细,边界越清晰,输出越稳定。

2.2 Task:把“请帮我分析”翻译成可执行的指令

Task在框架里就是实实在在的任务单。它给Agent指定要做什么,以及最终的验收标准。

继续拿行业研究举例:

from crewai import Task research_task = Task( description="搜索2025年新能源行业储能技术的最新进展。请列出至少5个关键技术方向,每个方向给出一个典型企业案例。", expected_output="一份包含5个技术方向的简要清单,每个方向不超过100字。", agent=researcher, )

我不厌其烦地提醒一句:description不要写“请帮我搜索新能源行业信息”这种模糊需求。模型对模糊需求的处理方式通常是给一个“看起来合理但其实什么都没说”的答案。你把约束写得越具体,比如“列出5个方向”“每个方向给一个案例”“总字数不超过500字”,它的输出就越接近你真正想要的东西。

expected_output同样重要,它就是任务验收标准。这个字段会让Agent在生成时自带一个“检查清单”,减少它对格式的随意发挥。

2.3 LLM:可插拔的“大脑”

Agent虽然有自己的角色设定,但真正负责“思考”的还是大模型。这个框架把LLM做成了可插拔组件,也就是说,你可以给不同Agent配不同模型。

from crewai import LLM llm_deepseek = LLM( model="openai/deepseek-chat", api_base="https://api.deepseek.com/v1", api_key="...", ) llm_ollama = LLM( model="ollama/llama3.1", api_base="http://localhost:11434/v1", api_key="ollama", )

用本地模型跑的好处是调试时不用烧钱,缺点是能力确实会弱一些。我的习惯是:开发调试阶段用本地小模型,跑正式任务时换成更强的商用模型。框架切换模型只需要替换Agent的llm参数,不用动业务逻辑。

这里有个很重要的底层机制——LLM返回的文本最终会进入Agent的“思考循环”。框架会自动把模型输出解析成文本或结构化JSON,传给下一步骤。这也是为什么你能在流程里混用不同模型,它们之间通过标准化的Task输入输出协议通信,不会因为模型厂商不同就断掉。

2.4 Crew与Process:怎么组织团队干活

Crew是编排容器,你可以理解为一个“项目组”。Process则是这个项目组的协作模式。框架最常用的两种模式是顺序执行sequential和层级执行hierarchical。

模式怎么工作适合场景
sequential按Task定义顺序逐个执行,前一个输出传给下一个流程固定、步骤清晰的场景
hierarchical由一个manager Agent动态规划任务、分配任务、汇总结果步骤不明确、需要当场拆解的复杂任务
from crewai import Crew, Process crew = Crew( agents=[researcher, analyst, writer], tasks=[research_task, analysis_task, writing_task], process=Process.sequential, verbose=True, ) result = crew.kickoff()

顺序模式很好理解,就是流水线。层级模式我建议新手先缓一缓,因为manager Agent需要额外的模型调用,成本更高,而且它“自由发挥”的空间大,调试起来比较费劲。我自己的经验是:能固定流程就尽量用sequential,等流程跑通了,再考虑用hierarchical做动态扩展。

3. 环境准备和先跑通单个Agent

3.1 安装依赖与版本锁定

先做环境准备。我强烈建议用Python虚拟环境,不要图省事直接装到全局。

python -m venv .venv source .venv/bin/activate # Windows 下是 .venv\Scripts\activate pip install crewai==0.80.0

这一步看着简单,我却见过不少人踩坑。这个框架迭代很快,隔一个月API可能就变了,所以锁定版本是一个好习惯。我写这篇教程时用的是0.80.x版本,如果你装的是最新版,某些参数名可能不一样,其他部分基本通用。

另外,Python版本建议3.10及以上。低于3.10有些语法和类型特性跑不起来,到时候报错会让人头疼。

3.2 大模型接入:把大脑换成你手上能用的API

框架本身不包含大模型,它依赖你提供一个模型接口。我用得最多的是OpenAI兼容接口,因为你只要把api_base换成任何遵守这个协议的服务地址,就能接上不同的大模型。

from crewai import LLM llm = LLM( model="openai/deepseek-chat", api_base="https://api.deepseek.com/v1", api_key="sk-xxx", )

如果是在本地跑Ollama,也只需要改两行:

llm_local = LLM( model="ollama/llama3.1", api_base="http://localhost:11434/v1", api_key="ollama", # 本地模式通常不校验 )

这里有个细节容易被忽略:model字段的前缀决定了框架用哪种方式解析请求。以openai/开头,框架会按OpenAI的接口协议去请求;以ollama/开头,会示本地Ollama的服务。如果你的服务商有特殊的模型名称,记得把前缀和模型名拼完整。

3.3 跑一个最小Demo:让Agent完成一次对话

装好环境、接好模型之后,我们来跑一个最小的、真正能被kickoff()调用的Demo。这次我不创建复杂协作,就一个Agent接一个Task。

from crewai import Agent, Task, Crew, LLM llm = LLM( model="openai/deepseek-chat", api_base="https://api.deepseek.com/v1", api_key="你的key", ) greeter = Agent( role="社区迎新助手", goal="用亲切的中文欢迎新成员,并介绍社区主要功能。", backstory="你是一个热心、幽默的社区志愿者,擅长用简短的文字让新人感受到友好氛围。", llm=llm, ) welcome_task = Task( description="新成员小明加入了社区,请欢迎他,并介绍三个核心功能:新手引导、开发者讨论、项目贡献。", expected_output="不超过150字的中文欢迎消息,语气自然亲切。", agent=greeter, ) crew = Crew( agents=[greeter], tasks=[welcome_task], process="sequential", verbose=True, ) result = crew.kickoff() print(result.raw)

跑完之后你会看到result.raw里是一段欢迎语。注意这里的数据结构,kickoff()返回的不是字符串,而是一个TaskResult对象,我们通过result.raw取文本,通过result.pydantic取结构化对象,通过result.tasks_output看每个子任务的输出。

3.4 跑通之后先别急着堆功能:观察日志和输出

第一次跑通后,我建议你花几分钟看一下verbose=True打印出来的执行日志。你会看到Agent内部的“Thought / Action / Observation”循环,大致是:模型先思考、然后决定调用工具或直接回答、观察返回结果、再决定下一步。这是理解框架运行逻辑的最好方式。

很多人第一次跑完之后急着加第二个Agent,结果发现问题根本不知道出在哪一步。我的建议是,先用一个Agent把完整链路跑顺,确认模型调用稳定、结果能正常解析,再往上加角色。排查问题的时候,一份清晰的最小复现比什么都管用。

4. 让多个Agent真正协作起来:写一份行业分析报告

4.1 把一个模糊需求拆成三个角色的活儿

跑通单Agent之后,我们来干点正事:让多个Agent写一份“新能源储能行业分析报告”。这个需求单独丢给一个大模型也能出结果,但质量通常比较泛。我的做法是先拆角色,再拆任务。

我把流程拆成三个阶段:

  • 研究员:负责搜集信息,输出素材清单;
  • 分析师:负责从素材中提炼行业趋势,输出分析框架;
  • 撰稿人:把分析结果写成一篇可发布的中文报告。

拆成三个角色而不是让一个Agent全干,核心好处是:每个Agent的上下文都相对精简,不会被前面环节的原始资料撑爆;每个环节的输出都有明确边界,出了问题你能快速定位到是搜集阶段不行、分析阶段不行还是写作阶段不行。

4.2 定义三个Agent,让每个角色有“记忆锚点”

from crewai import Agent researcher = Agent( role="行业研究员", goal="搜集并整理新能源储能领域的公开资料", backstory="你擅长从技术白皮书、企业公告、专业媒体中提取关键信息,输出简洁客观的素材清单。", llm=llm, ) analyst = Agent( role="行业分析师", goal="基于素材清单输出储能行业发展趋势分析", backstory="你有丰富的研究框架,能将零散素材归入市场规模、技术路线、政策环境、竞争格局四个维度。", llm=llm, ) writer = Agent( role="中文科技撰稿人", goal="将分析结果写成结构清晰、通俗易懂的行业报告", backstory="你长期为科技媒体供稿,擅长把专业内容转成普通读者也能看懂的叙述。", llm=llm, )

我特别想强调backstory的作用。它不只是“角色扮演”,更像是一个记忆锚点,帮模型在长任务中保持稳定风格。比如分析师的backstory里写了“将素材归入市场规模、技术路线、政策环境、竞争格局四个维度”,后面它真的会按这个框架来组织内容。你给的角色背景越具体,输出的稳定性就越高。

4.3 用context把上一步结果传给下一步

多Agent协作最容易踩的坑,就是任务之间没有传递上下文。Agent不是流水线上的工人,你不把前一个Agent的产出交给它,它就完全不知道前面发生了什么。

research_task = Task( description="搜集2025年储能行业的最新动态,覆盖技术路线、代表企业、政策关键词。", expected_output="一段800字以内的素材清单,包含至少8条关键信息,每条标注信息来源。", agent=researcher, ) analysis_task = Task( description="基于研究员提供的素材,分析储能行业的三个核心趋势,并与过去两年的情况进行对比。", expected_output="按市场规模、技术路线、政策环境、竞争格局四个维度输出的分析框架,不超过1200字。", agent=analyst, context=[research_task], # 关键:把research_task的输出传进来 ) writing_task = Task( description="基于分析师的趋势分析,展开写成一篇中文行业报告,开头要有摘要,结尾要有展望。", expected_output="全文800字左右,小标题清晰,适合发布在科技媒体专栏。", agent=writer, context=[analysis_task], )

注意context=[research_task]这个参数。它告诉框架:执行analysis_task之前,先把research_task的输出作为上下文注入。如果你不写这一步,分析师Agent很可能凭空脑补素材,产出再漂亮也是空中楼阁。

4.4 给研究员挂一个搜索工具

上面的Agent没有接工具,所有“资料”都来自模型自己的训练记忆。如果要让研究员真正去搜索最新信息,可以给它挂一个搜索类的工具。

crewai框架的工具本质就是一个函数:接收字符串参数,返回字符串结果。官方维护了一个crewai-tools扩展包,里面有联网搜索、网页内容提取、数据库查询等常见工具。用法是:

from crewai_tools import SerperDevTool search_tool = SerperDevTool( api_key="你的搜索服务key", ) researcher_with_tool = Agent( role="行业研究员", goal="搜集并整理新能源储能领域的公开资料", backstory="你擅长从技术白皮书、企业公告、专业媒体中提取关键信息,输出简洁客观的素材清单。", llm=llm, tools=[search_tool], )

接入工具之后,研究员Agent会在执行Task时先调用搜索工具,再把搜索结果喂给模型进行分析。这一步真正把“AI团队”从只会动嘴升级成了会动手。

5. 实测两周踩过的坑:从最常见到最隐蔽

5.1 明明是“多智能体”,第二个Agent却在那儿自说自话

我接触过的初学者里,十个有八个会犯这个错:定义了多个Agent和多个Task,但Task之间完全没写context依赖。结果就是,研究员的报告写得再详实,分析师一上来照样凭空开始“分析”,最后产出的报告和前面环节毫不相干。

这个问题的根因特别简单:框架不会自动把你所有Task的历史输出拼给下一个Agent。你需要显式地在Task里声明依赖。我习惯在创建Task时就把context参数写全,就像提前把项目组的汇报线定清楚一样。

排查方法也很简单:打开verbose=True看日志,如果某个Agent在执行时Observation部分是空的,或者输出里出现了“根据我的知识”这类话,就说明它压根没拿到前面的资料。

5.2 模型输出一飘,下游解析直接崩

多Agent流程跑起来之后,另一个高频问题就是模型的自由文本输出不稳定。同一个Task,这次模型返回了标准的JSON,下次就多了一句“好的,以下是结果:”,然后你的解析代码就崩了。

解决办法是使用Task的output_pydantic参数,强制模型按你定义的结构输出:

from pydantic import BaseModel class AnalysisResult(BaseModel): trend: str market_size: str policy_environment: str key_players: list[str] analysis_task = Task( description="分析储能行业趋势", expected_output="结构化的分析结果", agent=analyst, context=[research_task], output_pydantic=AnalysisResult, )

设置了这个参数后,框架会要求模型先按JSON Schema格式生成结果,再帮你解析成Pydantic对象。拿到结果后,直接result.pydantic.trend就能取出字段,比解析自由文本省心太多。不过也要提醒一句:不是所有模型都很听话,个别开源小模型即使给了Schema也偶尔会乱来。这种时候我一般会把模型换成能力更强的商用模型,或者用确定性代码做一层兜底解析。

5.3 多角色协作放大了幻觉

这是我调试过程中最警惕的一个坑。单个模型一本正经胡说八道,你很容易察觉;但多个Agent协作时,错误信息经过了“整理-分析-转述”三道工序,最后看起来会异常专业可信。研究员素材里编了一个不存在的企业,分析师会顺着这个素材展开分析,撰稿人再把它写得绘声绘色,一条假消息就完整出炉了。

我的应对策略有三层:

  • 在Task描述里强制要求“无法核实的信息必须标注为存疑”;
  • 给素材类Task加一条规则:每个关键结论必须附带来源;
  • 在流程最后加一个独立的“审核Agent”,专门挑毛病。

你需要接受一个现实:多Agent协作不会天然提升准确性,它只是让流程更清晰、产出更稳定。准确性最终取决于每个环节输入的可靠程度。

5.4 成本和延迟翻倍,别让多Agent替你全做了

多Agent串行执行最大的代价是时间和成本。每个Task都会调用大模型,而且后一个Agent的上下文会包含前面流程的关键信息,token消耗会越来越大。

我做过一次粗略统计:一个3个Agent的串行流程,跑一次大约消耗2万到4万token,耗时30秒到1分钟。如果是单Agent直接写报告,可能只需要一半的成本和三分之一的时间。多Agent的额外开销换来的,是更清晰的结构、更稳定的质量,以及更低的单步骤失败率。值不值取决于业务要求。

想控制成本,几个思路供参考:

  • 给Agent设置max_iter限制,防止它在单步上反复尝试;
  • 把长文档在传给下一个Agent之前先做摘要,避免上下文无限膨胀;
  • 不是所有环节都需要大模型,比如格式转换、字段提取这类确定性操作,用普通Python函数处理更便宜。

5.5 中文环境里的一些小毛病

这个坑不算深,但很磨人。如果你在Windows上跑,偶尔会遇到控制台报UnicodeEncodeError,这是因为终端默认编码不是UTF-8。设置一下环境变量即可:

# Windows PowerShell $env:PYTHONIOENCODING="utf-8"

如果装依赖时遇到冲突,建议用全新的虚拟环境安装,别和已有项目混在一起。我之前在旧环境里装crewai,和已有的Pydantic版本冲突,折腾了一个小时才定位到问题。换虚拟环境后五分钟解决。

6. 从Demo到能用:稳定落地的三点建议

6.1 把Agent和Task定义抽成配置,别堆在代码里

Demo阶段把Agent定义写在Python文件里没问题,但项目变复杂后,改动一个角色描述就要改代码、重启进程,非常烦。我后来把Agent和Task抽成了YAML配置:

agents: researcher: role: "行业研究员" goal: "搜集并整理新能源储能领域的公开资料" backstory: "你擅长从公开资料中提取关键信息" tasks: research: description: "搜索储能行业最新动态" expected_output: "包含8条关键信息的素材清单" agent: "researcher"

代码里只写一个加载配置的函数,动态创建Agent和Task。这样调整Prompt不需要碰代码逻辑,测试和灰度也方便很多。

6.2 加一层结果缓存,验证Prompt时不烧冤枉钱

这是我自己摸索出来的土办法,但非常管用。调试多Agent流程时,你可能只想改最后一个环节的Prompt,结果整个流程从头到尾重新跑一遍,白白烧掉前几个环节的token。

我给每个Task加了一个缓存层:以Task描述和上下文哈希作为key,把每次执行的原始输出和结构化结果存到本地JSON或SQLite里。运行时如果命中缓存,直接取回结果,跳过模型调用。改到哪里,哪里才会重新执行,调试效率一下提高了不少。你只需要注意,缓存前给每个Task加上版本号,否则改过Prompt后可能忘记失效。

6.3 记录Token消耗和耗时,再有选择地堆Agent

用这个框架最忌讳的事情,是看到什么功能都想往上加Agent。我见过有人明明只需要写一段摘要,硬是拆成三个Agent协作,最后效果还不如一个Agent直接处理。

我的习惯是,在每次kickoff()执行后记录一下任务耗时和token消耗,然后问自己三个问题:

  • 这个Agent的加入让最终质量提升了吗?
  • 如果去掉它,用确定性代码处理这部分,会不会更稳更省?
  • 这个环节的失败率,值得我用多一次模型调用去降低吗?

多智能体框架不是装饰品,它是用来解决单Agent搞不定的复杂流程的。你越清楚每个Agent存在的理由,最后的系统就越可靠。

最后说点个人体会吧。这套框架最打动我的不是5.9万Star这个数字,而是它逼着我把模糊需求变成清晰步骤。以前我写提示词,总想一个Prompt解决所有问题;现在我会先问自己:这件事如果外包给三个人,每个人分别干什么、最后谁汇总?想清楚了再动手,往往会发现很多流程连多Agent都不需要,一个顺序执行就能解决;而真正需要多Agent的场景,也因为你提前拆好了步骤,开发起来特别顺。这大概是它最值钱的地方。

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

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

立即咨询