在实际的 AI 应用开发中,多智能体系统已经不是概念阶段的东西,而是一类可落地的工程范式。CrewAI 是其中使用成本较低、理解门槛也较友好的 Python 框架:开发者可以把一个复杂目标拆成多个带身份的 AI 智能体,每个智能体负责一个明确角色,再通过任务编排把这些角色的产出串联成一条自动化工作流。说得直白一点,它解决的并不是“某个 Prompt 怎么写得更好”,而是“一个完整工作流如何由多个模型协作完成”。
这篇文章会围绕 CrewAI 的核心概念、智能体定义、任务编排和工作流构建展开。你会看到 Agent、Task、Crew 和 Process 这几个最基础的对象之间如何配合,也会得到一个可运行的“调研 -> 撰写 -> 质检”三段式示例。整体内容适合已经调用过 LLM API、但还没系统接触过 Agent 编排的开发者。学习环境只需要一个 Python 3 虚拟环境和可用的模型 API Key,生产化部分会在最后单独讨论。
1. 先理解 CrewAI 多智能体框架的四条核心主线
1.1 CrewAI 解决什么问题
如果只调用一次模型接口,输入一条 Prompt,得到一段输出,这种模式适合关键词生成、代码解释、文本改写等单点任务。但很多业务场景天然是多环节的,例如“先收集行业资料,再撰写分析报告,最后检查报告是否有事实错误”。如果让一个模型从头做到尾,输出质量往往取决于它是否能在长上下文里一直记住自己的角色和当前阶段目标。
CrewAI 的解法是模拟真实团队。它把一次完整业务目标拆成多个工作单元,每个工作单元由一个具备独立角色设定的智能体负责。因为每个智能体的 Prompt 都是独立控制的,系统可以给“调研员”强调客观来源,给“作者”强调文章结构,给“质检员”强调逻辑和事实核查。结果就是,相比单次超大 Prompt,多智能体方式更容易控制每一段产出的质量。
1.2 Agent(智能体):给模型一个“岗位”
CrewAI 里的 Agent 是执行单元,本质上是一个带角色、目标和背景的 LLM 实例。一个 Agent 通常包含 role、goal、backstory 三个关键字段,这三个字段最终会组合成该模型收到的系统提示词。
用一个通俗类比来理解:Agent 不是工具,而是一个“岗位”。岗位说明书里写了这个岗位负责什么、要达到什么目标、以前有什么经验。模型看到这份说明书后,会以岗位身份去思考和工作。这样做的好处是:同一个底层模型,只要设定不同角色,输出风格和关注点就会有明显差异。
一个常见的理解误区是,Agent 名称起得越复杂越好。其实框架并不在意智能体叫“研究员”还是“研究员一号”,真正影响输出的是 role、goal、backstory 的措辞是否清晰、边界是否明确。
1.3 Task(任务):把工作成果变成可传递的对象
有了岗位之后,还需要给岗位安排具体活。Task 是最小的工作单元,它描述“做什么”“做到什么程度”“谁来做”。
CrewAI 的 Task 不只是一个描述字符串,它还可以携带输出文件路径、结构化输出格式、依赖的上游任务、要使用的工具。也就是说,Task 承担了“工作指令 + 产出契约 + 数据传递”三层作用。一个设计良好的 Task,必须让模型清楚知道最终交付物长什么样,否则下游任务拿到上游结果时,很难维持稳定格式。
1.4 Crew(团队)与 Process(流程):谁来排执行顺序
Crew 是把若干 Agent 和若干 Task 组装在一起的整体执行单位。它负责定义“有哪些角色参与”“按什么顺序执行”“执行时的日志和内存如何管理”。
Process 则表示任务之间的协作方式。CrewAI 中常见的是顺序执行和层级执行两种:顺序执行按任务列表自上而下跑,每个任务完成后把结果交给下一个任务;层级执行会有一个管理性质的智能体动态分派工作。这一节先记住结论:流程设计决定了任务编排的复杂度,也决定了 Token 消耗量。后面的章节会专门展开两者的选择逻辑。
2. 准备运行环境:依赖、密钥和最小工程结构
2.1 检查 Python 环境和创建虚拟环境
CrewAI 是基于 Python 的框架,开始前先确认本机已经安装了可用的 Python 版本。框架不同发行版本对 Python 版本范围要求不一样,官方文档通常建议使用 Python 3.10 及以上,但不要假设任何环境都能直接跑,安装前看一遍该 CrewAI 版本的项目说明最稳妥。
为了避免把依赖装进系统 Python,强烈建议先创建虚拟环境。
python --version python -m venv .venv source .venv/bin/activateWindows 环境下激活命令不同:
python -m venv .venv .venv\Scripts\activate激活后命令行前缀会变为(.venv),此时再执行 pip 安装,依赖都会落到当前虚拟环境里,不会污染其他项目。
2.2 安装 CrewAI 并确认版本
最小安装可以直接执行:
pip install crewai如果需要使用 CrewAI 内置的网络搜索、网页解析等工具,再考虑安装扩展包:
pip install "crewai[tools]"实际项目中,更推荐把依赖写进 requirements.txt,固定大版本或精确版本,避免框架版本升级后行为变化导致代码不可用。
crewai>=0.80.0,<0.99.0 python-dotenv>=1.0.0上面的版本范围只是一个示例思路,落地前要以当前实际能安装到的版本为准。安装完成后可以通过 Python 确认导入是否正常:
pip show crewai | grep -E "Name|Version" python -c "import crewai; print(crewai.__version__)"如果第二行命令抛错,说明当前版本没有暴露__version__属性,这并不代表安装失败,只要 import 不报错即可。
2.3 配置 LLM 密钥:不要把密钥写进代码
CrewAI 在运行 Agent 时,默认会到环境变量里读取模型服务的密钥。最简单的方式是使用 OpenAI 兼容配置。先在项目根目录创建.env文件:
OPENAI_API_KEY=你的密钥 OPENAI_MODEL_NAME=gpt-4o-mini代码中通过dotenv加载:
from dotenv import load_dotenv load_dotenv()这里要考虑两件事。第一,不要把真实密钥写死在 Python 文件里或提交到 Git 仓库,否则一旦仓库公开,密钥就会泄露。第二,OPENAI_MODEL_NAME在不同版本的 CrewAI 中可能默认值不同,建议显式设置当前账号可用的模型名称,而不是依赖框架默认值。
如果项目使用其他兼容 OpenAI 协议的部署,还需要设置接口地址环境变量。企业内网模型网关通常有自己的接入规则,具体字段以平台说明为准。
2.4 最小工程结构
一个适合初学的目录结构如下:
multi_agent_demo/ ├── .env ├── main.py └── requirements.txtmain.py是入口,.env保存密钥,requirements.txt保存依赖。后续所有实现都在main.py中完成。等到项目复杂后,再把 Agent 定义、Task 定义、Crew 定义拆成不同模块。
这个小结构的价值在于:先让环境和入口足够简单,避免一开始就陷入复杂的包管理问题。只要能跑通最小示例,再去加工具、加记忆、加外部系统就很自然。
3. 定义智能体:role、goal、backstory 如何组合成可用角色
3.1 一个最小 Agent 定义
CrewAI 中创建一个 Agent 的代码非常直观。以内容生产场景为例,先定义一个负责收集信息的“行业研究员”:
from crewai import Agent researcher = Agent( role="行业研究员", goal="围绕用户指定的主题,收集并整理有依据的关键信息", backstory=( "你是一名长期关注企业数字化和 AI 应用的行业分析师。" "你习惯于先列出信息维度,再逐项收集,不接受没有来源的猜测。" ), verbose=True, allow_delegation=False, )这段代码里,role告诉模型它现在是什么身份,goal说明它要完成什么目标,backstory则补充它的工作风格和历史上下文。verbose=True表示执行时输出详细过程,方便调试。
3.2 Agent 参数速查
除三要素外,Agent 还有多个实用参数,下表列出常见项:
| 参数 | 作用 | 使用建议 |
|---|---|---|
| role | 智能体身份或岗位名称 | 使用业务语义清晰的名词,如“数据分析师”。 |
| goal | 智能体要达成的目标 | 尽量可验证,避免“做得更好一点”这类模糊描述。 |
| backstory | 背景、经验、工作风格 | 用来约束输出语气和上下文范围。 |
| llm | 指定使用的语言模型 | 不同任务可以绑定不同模型。 |
| tools | 绑定工具列表 | 只有明确需要外部数据时才添加。 |
| allow_delegation | 是否允许把子任务交给其他 Agent | 多智能体协作时按需开启,默认场景建议关闭。 |
| verbose | 是否展示详细执行日志 | 开发调试时开,生产环境可关闭或接入独立日志。 |
| max_iter | 单次任务允许的最大迭代次数 | 防止模型反复自我纠错刷 Token。 |
| memory | 是否启用该 Agent 的记忆能力 | 需要跨任务复用信息时开启。 |
这里要解释一下llm参数的价值。多个 Agent 不一定使用同一个模型。比如“信息提取”任务适合便宜、响应快的模型,“最终审核”任务可以用能力更强的模型。实际项目中,可以通过 Agent 的llm参数为不同角色分配不同模型,这样既控制成本,也保证关键节点的质量。
3.3 为什么三个文本字段不能省
很多初学者会问:我只写goal不行吗?为什么还要写role和backstory?
原因是这三个字段共同构成了智能体的系统提示词。role提供身份锚点,模型知道要用什么视角看问题;goal提供任务驱动,模型知道自己要往哪个方向输出;backstory提供约束风格,相当于真实员工的工作经验说明。
如果一个 Agent 只有 goal,没有 backstory,模型会缺少“应该用什么方式做事”的信息。比如收集资料时,有的模型会凭记忆编造统计数据;如果 backstory 明确写了“不接受没有来源的猜测”,它就更倾向于输出“未找到权威来源”而不是硬编。相比反复在 Task 描述里强调规则,把稳定约束放到 Agent 定义中是更省 Token 的做法。
3.4 一个团队里的角色边界要如何划分
定义多个 Agent 时,最常见的失败原因是角色边界重叠。比如“内容作者”和“内容编辑”的目标如果都写成“写出一篇优质文章”,那后一个 Agent 大概率会重写前一个 Agent 的内容,而不是做真正的编辑检查。
建议在定义团队前先画出任务链条,把每个环节的输入和输出写清楚,再根据输入输出确定角色。比如调研环节需要输出“分节调研笔记”,作者环节需要把笔记变成“文章初稿”,质检环节只需要输出“修改清单”。当每个 Agent 的 goal 都指向自己那一环的交付物时,协作才不会互相干扰。
4. 任务编排:Task 的依赖、上下文与输出约束
4.1 定义第一个调研任务
Agent 是“谁来做”,Task 是“做什么”。创建一个 Task 时,需要指定它属于哪个 Agent,并写清楚任务描述和预期输出:
from crewai import Task research_task = Task( description=( "主题:{topic}\n" "请从市场现状、关键技术、落地案例、常见困难四个维度收集信息。" "不允许编造统计数据,找不到确切数据时写明‘未找到权威来源’。" ), expected_output=( "一份分节 Markdown 调研笔记,包含:\n" "1. 每个维度不少于 3 条要点;\n" "2. 每条要点用一句结论开头,后面补充细节;\n" "3. 在文末列出尚未确认的问题。" ), agent=researcher, )这里{topic}是模板占位符,最终执行时由Crew.kickoff(inputs={"topic": "..."})填充。使用模板的好处是,同一个 Crew 可以反复执行不同主题,而不需要每次修改代码。
expected_output参数很容易被忽略,但它非常关键。模型输出没有明确预期格式时,很容易出现内容正确但结构混乱的结果。给定了结构示例后,Task 的输出会更稳定,也更方便下一个 Agent 解析。
4.2 expected_output 是任务之间的约定
在实际工作流里,Task 的输出不只是给用户看的,它往往要送给下游 Agent 继续处理。如果上游 Task 只写“收集资料”而没有规定输出格式,下游 Agent 可能拿到一段不知道分了几节的文字,后续想基于它写文章就会很吃力。
因此,expected_output既要描述结构,也要描述格式。推荐写清“Markdown 分节”“每条要点由结论开头”“最后列出未决问题”这类可执行要求。它不是给机器解析的语法,而是给模型看的格式契约。
4.3 通过 context 建立任务依赖
顺序执行时,Crew 会按任务列表顺序运行,但如果你想明确告诉某个任务“你的输入来自另一个任务”,可以使用context参数:
draft_task = Task( description="根据调研笔记撰写一篇结构清晰、面向技术读者的文章初稿", expected_output="一篇可直接评审的 Markdown 文章初稿,全文不少于 1500 字", agent=writer, context=[research_task], )context=[research_task]表示:执行 draft_task 前,先拿到 research_task 的执行结果,并把它放进当前任务的上下文。这样即使以后调整任务顺序,只要 context 关系还在,依赖关系仍然明确。
使用context需要注意两点。一是不要让任务依赖不存在的任务,否则运行时会因为找不到输入报错。二是不要形成循环依赖,任务 A 依赖 B 的同时 B 又依赖 A,这在编排逻辑上就是死锁。
4.4 需要人工介入时使用 Human in the Loop
有些工作流并不希望全自动跑完。例如“最终发布”或“对外发送邮件”这类操作,一旦出错代价很高。CrewAI 的 Task 支持人工输入机制,在任务执行到某个点时暂停,等待人工确认或补充信息。
human_input=True是较常用的配置。实际项目中,建议把“高风险动作”独立成任务,并开启人工确认;把“读取、分析、生成草稿”这类低风险动作交给全自动执行。这样能把人的判断放在真正的决策点上,而不是每步都停下来。
5. 完整示例:一个三位智能体协作的文章流水线
5.1 场景设计与角色分工
演示场景设定为“输入一个主题,自动输出经过质检的文章初稿”。不需要接入外部系统,也不涉及复杂工具,只看多智能体编排本身。
角色链设计如下:
| 角色 | 职责 | 交付物 |
|---|---|---|
| 行业研究员 | 收集主题背景信息 | 分节调研笔记 |
| 技术编辑 | 把调研笔记改写成文章 | Markdown 文章初稿 |
| 内容质检员 | 审核初稿并给出修改清单 | Checklist 格式问题清单 |
三个角色对应三个任务,全部由顺序流程串联。这样的设计体现的是“信息逐步精炼”的典型模式。
5.2 完整代码:main.py
先创建.env,填入模型密钥和模型名:
OPENAI_API_KEY=你的密钥 OPENAI_MODEL_NAME=gpt-4o-mini再在main.py里写入完整逻辑:
import os from dotenv import load_dotenv from crewai import Agent, Crew, Process, Task load_dotenv() # 模型名称可以从环境变量读取,方便切换 os.environ.setdefault("OPENAI_MODEL_NAME", "gpt-4o-mini") # 1. 定义三个智能体 researcher = Agent( role="行业研究员", goal="围绕用户指定的主题,收集并整理有依据的关键信息", backstory=( "你是一名长期关注企业数字化和 AI 应用的行业分析师。" "你习惯先列出信息维度再收集,不接受没有来源的猜测。" ), verbose=True, allow_delegation=False, ) writer = Agent( role="技术编辑", goal="把调研笔记改写成结构清晰、适合技术人员阅读的文章初稿", backstory=( "你是一名科技媒体编辑,擅长把零散素材整理成有段落、有结论的正文。" "你坚持每段只讲一个观点,不使用空泛套话。" ), verbose=True, allow_delegation=False, ) editor = Agent( role="内容质检员", goal="检查初稿中的逻辑断档、事实依据和格式问题,输出修改清单", backstory=( "你是出版机构的内容质检员,对逻辑一致性和可读性非常敏感。" "你只输出必须修改的问题,不做无意义的修改。" ), verbose=True, allow_delegation=False, ) # 2. 定义三个任务 research_task = Task( description=( "主题:{topic}\n" "请从市场现状、关键技术、落地案例、常见困难四个维度收集信息。" "不允许编造统计数据,找不到确切数据时写明‘未找到权威来源’。" ), expected_output=( "一份分节 Markdown 调研笔记,包含:\n" "1. 每个维度不少于 3 条要点;\n" "2. 每条要点用一句结论开头,后面补充细节;\n" "3. 在文末列出尚未确认的问题。" ), agent=researcher, ) draft_task = Task( description=( "主题:{topic}\n" "根据调研笔记撰写一篇面向技术读者的文章初稿," "要求有引言、方法或案例、结论,全文 1500 字以上。" ), expected_output="一篇可直接评审的 Markdown 文章初稿。", agent=writer, context=[research_task], ) review_task = Task( description=( "检查文章初稿,输出修改清单。" "重点检查结论是否有依据、段落是否重复、术语是否解释清楚。" ), expected_output=( "以 Checklist 形式列出:问题位置、问题描述、修改建议。" "如果没有问题,就明确输出‘无需修改’。" ), agent=editor, context=[draft_task], ) # 3. 组装 Crew 并运行 crew = Crew( agents=[researcher, writer, editor], tasks=[research_task, draft_task, review_task], process=Process.sequential, verbose=True, ) result = crew.kickoff( inputs={"topic": "本地生活场景中的 AI 助手落地方式"} ) print("\n=== Crew 最终输出 ===") print(result)5.3 运行命令与预期表现
在虚拟环境激活状态下执行:
python main.py如果密钥和网络都正常,你会先看到researcher的阶段日志,然后是writer的写作过程,最后是editor的质检结果。因为verbose=True,每步会打印出当前任务名称和阶段状态,这是判断执行流程是否正确的第一手依据。
crew.kickoff()的返回值在不同版本中可能不同。有的版本返回字符串,有的版本返回包含raw、tasks_output等字段的结果对象。为了避免版本差异造成的困惑,打印前可以用兼容方式处理:
def show_result(result): raw = getattr(result, "raw", None) if raw: print(raw) else: print(result) show_result(result)这段代码先尝试读取结果对象的raw字段,如果不存在就直接把结果转成字符串打印,兼容两种常见情况。
5.4 如何验证输出是否达到预期
程序能跑通不等于流程正确。至少要做三个验证:
- 三个任务是否按顺序执行,没有跳过或重复。
- 第二个任务的输出是否真正基于第一个任务的调研笔记,而不是凭自己的知识重写。
- 质检输出是否真的指出了初稿的问题,而不是简单说“写得不错”。
验证第 2 点可以打开verbose=True的日志,检查writer执行时是否引用了上游调研内容。验证第 3 点可以故意在任务描述里加一个可挑剔的要求,比如“初稿中必须包含两个数字结论”,然后观察质检员是否按这个要求检查。这个方法比只跑一次 Happy Path 更能暴露编排设计的问题。
6. Process 参数深入:顺序执行与层级执行如何选择
6.1 Process.sequential:稳定的流水线模式
顺序执行是最容易理解的工作流形态。Crew 启动后,按tasks列表的顺序执行任务。前一个任务完成后,结果自动进入下一个任务的上下文。这个模式的优点是可控、日志清晰、Token 消耗可预测。
示例中的文章流水线就适合顺序执行,因为“调研 -> 撰写 -> 质检”是天然的前后依赖关系。只要任务链是固定的,使用顺序执行就够了,不需要额外引入管理 Agent。
顺序执行的使用限制是:所有分支都必须在任务列表里静态设计好。它不适合“过程中根据结果决定下一步做什么”的场景。例如,系统要先去搜索资料,如果资料充足就走深度分析,如果资料不足就先补一轮追问,这种动态分支更适合层级执行或事件驱动流程。
6.2 Process.hierarchical:有管理节点的分派模式
层级执行会引入一个 Manager Agent,由它判断当前哪个 Agent 更适合完成某个任务。Crew 不再按照静态列表跑任务,而是由管理节点动态进行任务分派。
最简单的层级配置是只指定manager_llm:
crew = Crew( agents=[researcher, writer, editor], tasks=[draft_task], process=Process.hierarchical, manager_llm="gpt-4o", verbose=True, )也可以自定义管理 Agent:
manager = Agent( role="项目负责人", goal="根据任务阶段把工作分派给最合适的成员,并汇总最终交付物", backstory="你管理过多个内容生产小组,擅长判断任务归属和推进节奏。", allow_delegation=True, ) crew = Crew( agents=[researcher, writer, editor], tasks=[draft_task], process=Process.hierarchical, manager_agent=manager, verbose=True, )层级执行更灵活,但也有代价:每一次分派和管理判断都会产生额外的模型调用,Token 开销明显比顺序执行高;同时,管理 Agent 的能力直接决定整个流程的质量,如果管理节点判断错误,下游任务会一起跑偏。
6.3 两种 Process 的选择逻辑
判断标准很简单:任务链是否稳定。稳定就用顺序,不稳定、经常需要动态分工的就用层级。
| 维度 | Process.sequential | Process.hierarchical |
|---|---|---|
| 执行规则 | 按任务列表固定顺序执行 | 管理 Agent 动态分派 |
| 依赖表达 | 任务顺序或 context 参数 | 由管理节点判断 |
| Token 消耗 | 相对较低 | 高,额外增加管理节点调用 |
| 适用场景 | 调研、生成、提取等固定链路 | 复杂决策、多分支、动态调度 |
| 典型问题 | 流程僵硬,无法动态调整 | 管理节点误判,成本失控 |
还有一种更细的编排方式:CrewAI 的 Flow 事件机制。它允许开发者根据事件触发不同任务,而不是只靠顺序列表。这种方式适合异步、带分支的自动化工作流。具体 API 结构在不同版本迭代较快,使用前建议以当前版本官方示例为准。
6.4 Crew 的其他关键参数
Crew还提供一些影响执行行为的重要参数:
| 参数 | 作用 | 建议 |
|---|---|---|
| verbose | 是否显示执行过程 | 调试开,生产建议关闭并转日志。 |
| memory | 是否启用 Crew 级别记忆 | 跨多次运行需要记忆时开启。 |
| max_rpm | 每分钟最大请求数 | 防止高并发时触发限流。 |
| planning | 是否在任务前先生成执行计划 | 复杂流程可以开,简单流程没必要。 |
| manager_agent | 指定层级流程的管理 Agent | 层级流程中使用。 |
| manager_llm | 指定层级流程管理节点的模型 | 当没有自定义 manager_agent 时使用。 |
开启memory=True后,Crew 会保存历史执行记忆,让后续任务可以参考之前的运行结果。但记忆功能依赖向量化存储,需要提前确认嵌入模型配置。如果只是跑一次性任务,不建议开启 memory,避免无谓的资源占用。
7. 开发与部署中的高频问题排查
7.1 报错现象速查表
多智能体项目常见的错误表现为以下几类:
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 请求返回 401 或 403 | API Key 错误或没有权限 | 检查.env是否被加载 | 确认环境变量名和密钥有效性 |
| 返回 429 或限流错误 | 请求频率超过账号额度 | 查看是否开启了 memory、是否有循环调用 | 调低并发,增加max_rpm;检查 Agent 之间是否互相委托形成循环 |
| ModuleNotFoundError: No module named 'crewai' | 未安装依赖或在错误环境运行 | 执行pip list确认虚拟环境 | 先激活虚拟环境再安装 |
| 任务执行时间过长 | Token 盲目重用或模型反复纠正 | 查看 verbose 日志中迭代次数 | 减少任务描述冗余,设置max_iter |
| 输出的内容没有使用上游任务结果 | 没有为下游任务配置 context | 检查 Task 的 context 参数 | 显式传入上游 Task 或保持任务顺序正确 |
| 字段在某个版本中失效 | CrewAI 版本差异较大 | 查看项目使用的版本 | 阅读该版本的迁移说明或官方示例 |
7.2 典型排查链路
遇到问题不要先改代码,按顺序排查效率更高:
- 确认密钥能独立调用模型。可以先写一个最简模型调用脚本,排除密钥和网络问题。
- 确认虚拟环境激活。很多 ModuleNotFoundError 都源于在非虚拟环境执行。
- 确认
.env被正确加载。检查load_dotenv()是否在读取环境变量之前执行。 - 缩小到一个 Agent、一个 Task 的最小 Crew。先跑通单任务,再逐步添加第二个任务。
- 查看 verbose 日志中最后输出位置。日志停在哪一步,问题大概率就在哪一步。
- 检查上下文是否过长。如果上游输出太大,可以要求它压缩或只传递摘要。
7.3 三个容易忽视的坑
第一个坑是 Agent 边界重叠。很多新人会把“写作者”和“审校者”的 goal 都写成“生产一篇好文章”。结果审校 Agent 会重写全文而不是审校。正确做法是让质检 Agent 只输出问题清单,而不是替作者产出最终文章,也就是“输出行为的边界要分开”。
第二个坑是上下文越传越长。顺序流程每传一个任务,结果都会叠加到后续 Agent 的上下文里。如果第一个任务输出 5000 字,第二个任务再输出 5000 字,第三个任务可能面临超长上下文。要在每个 Task 的expected_output中控制输出体量,必要时让 Agent 在向上传递前先做摘要。
第三个坑是忽略成本监控。多智能体不是真的不花钱,每增加一个 Agent、每次内部迭代都会调用模型。生产环境最好记录每个任务的实际统计信息。很多人的项目是从三四个 Agent 开始膨胀到二十个的,在没有日志监控的情况下,账单突然飙升往往查不到原因。建议早期就为每个任务编号,并在外部日志中记录每个 Task 的耗时和调用次数。
8. 从示例到生产:可执行的最佳实践与扩展方向
8.1 上线前检查清单
把演示代码搬到生产前,至少检查以下几点:
- [ ] Agent 的角色、目标、背景是否使用外置配置,而不是硬编码在代码里。
- [ ] 每个 Task 的
expected_output是否定义了可解析的结构。 - [ ] 每个高风险任务是否设置了人工确认。
- [ ] 模型密钥是否通过密钥管理或环境变量注入,而不是提交到仓库。
- [ ] 是否关闭了生产环境的 verbose,改为结构化日志。
- [ ] 是否记录每次运行的输入、输出摘要、耗时和 Token 消耗。
- [ ] 是否为外部 API 调用预留了限流和重试机制。
- [ ] 是否准备好失败回退方案:某个 Agent 执行失败时,是重试还是降级为人工处理。
这份清单的核心思路是:多智能体系统本质上是代码、模型和外部系统的组合,任何一个环节都可能失败。代码写得再漂亮,缺少日志、重试和人工兜底,就无法在生产环境稳定运行。
8.2 给智能体接真实工具的路线
纯文本生成的功能最终会碰到边界,因为模型不具备访问真实世界数据的能力。CrewAI 支持为 Agent 绑定工具,内置工具包括网页搜索、网页内容解析等。实际项目里,更常见的是把内部业务 API 封装成自定义工具。
一个通用的自定义工具结构如下:
from crewai.tools import tool @tool("订单查询工具") def query_order(order_id: str) -> str: """根据订单号返回订单状态。""" # 这里调用内部订单系统的 API return f"订单 {order_id} 当前状态:已发货"工具函数名、描述字符串和入参说明会共同组成模型可以理解的函数声明。函数名要语义清晰,docstring 要写清楚这个工具能干什么、参数是什么。模型正是通过这个描述决定何时调用工具。
接入工具时注意权限边界。不要让“研究员”角色拥有写数据库或发邮件的工具。生产设计要遵守最小权限原则:每个 Agent 只拥有完成自身任务所需的工具。
8.3 扩展方向与学习路径
如果上面这些内容已经掌握,可以从三个方向继续深入。
第一个方向是把固定 Crew 改造成可配置的服务。例如用 YAML 描述 Agent 和 Task,通过配置中心下发,而不是为每个流程单独写代码。这样可以降低新增流程的成本。
第二个方向是接入事件驱动的 Flow 设计。顺序流程适合固定链路,但真实业务经常有“如果 A 成功就走 B,如果 A 失败就通知人工”的分支。事件驱动模式更适合表达异步和条件逻辑。
第三个方向是补齐工程质量。包括执行日志落库、运行指标上报、任务重试策略、结果版本对比、异常告警和人工审核界面。多智能体系统上线后,大多数工作不是调 Prompt,而是把这些周边能力做扎实。
回到最初的问题:CrewAI 的价值不在于“多个 Agent 一起跑”这个形式,而在于它迫使你把一个模糊目标拆成可定义、可传递、可验证的任务链。先把 Agent 和 Task 的边界设计清楚,再谈自动化工具和复杂流程,才是这类项目最值得投入精力的部分。