4.4. CrewAI 的角色化分工最适合结构化的团队任务

想象这样一个星期三的下午:你的团队刚接下一个紧急项目,需要在一天内完成一份竞品分析报告——既要搜集市场信息,又要写成专业文档,还要让格式无可挑剔。如果你只有一个人,这几乎是不可能完成的任务;但如果能用三个 AI 角色——研究员、撰稿人、审查员,各司其职、又互相协作,情况就完全不同了。

上一章,我们见识了 AutoGen 如何通过对话驱动灵活的多 Agent 协作。这种“自由聊天”的模式非常适合开放式的探索,但一旦任务流程明确、分工固定,我们就需要一种更“结构化”的编排方式。CrewAI 正是为此而生——它把 Agent 抽象为“角色(Role)-目标(Goal)-任务(Task)”三层,让你像组建一支纪律严明的团队一样,快速搭建出专业分工清晰、流程可控的 Agent 团队。本章我们就来实战——用 CrewAI 完成一份完整的竞品分析报告。


你需要什么

  • Python 3.10+
  • 已配置好 OpenAI API key 的环境
  • 一个文本编辑器或 Jupyter Notebook

预计用时: 45 分钟(包含首次运行等待时间)。

最终成果

你将得到一个 Python 脚本,它定义了三个角色:研究员、撰稿人、审查员,并通过顺序任务流程自动生成一份结构化的竞品分析报告(Markdown 格式)。整个流程无需手动干预,每个角色的输出会作为下一个角色的输入,最终产出一份可直接提交的报告。

为什么要做这个?
因为它完整展示了 CrewAI 的核心价值:将“应该由谁做什么”和“怎么做”彻底分离开来,用声明式的方式定义团队和任务,让 AI 团队像精密机器一样运转。


角色、任务与工具链的声明式定义

在 CrewAI 中,搭团队的第一步是“定义角色”——不是泛泛地说“我需要一个写手”,而是精确到职业背景、擅长领域、甚至性格特质。官方文档特别强调一个 80/20 原则:把 80% 的精力花在设计任务说明上,只留 20% 给 Agent 本身的角色定义。因为再出色的专家,如果拿到的任务指令含混不清,也发挥不出水平。

Agent 的定义骨架:Role、Goal、Backstory

让我们先创建第一个角色:研究员。

from crewai import Agent

researcher = Agent(
    role="市场调研专家",
    goal="深入搜集指定产品的竞品信息、市场数据和用户评价,并整理成结构化摘要",
    backstory=(
        "你曾在一流咨询公司担任市场分析师,擅长从公开资料中提取关键情报。"
        "你思维缜密,注重事实,讨厌凭空猜测。"
    ),
    verbose=True,          # 输出详细日志,便于追踪
    allow_delegation=False # 研究员不委派任务,专注自己工作
)

注意这里三个核心字段:

  • role:专业化角色名,对接真实职业原型。避免使用“助手”这类泛称。
  • goal:以结果为导向的目标,最好包含质量标准和成功准则。
  • backstory:赋予 Agent 经验、视角甚至个性。它不仅影响决策风格,还能让最终输出带上一丝“人味”。

⚠️ 踩坑经验:如果不设 verbose=True,出问题时你将只能看到一行“任务完成”,完全无法追踪内部推理。对于首次搭建,永远开启 verbose

绑定工具(Skills)作为 Agent 的能力

研究员需要一个搜索工具才能工作。CrewAI 内置了工具集,也可以直接接入外部工具。我们这里用 SerperDevTool(需要设置 SERPER_API_KEY)联网搜索,用 ScrapeWebsiteTool 抓取网页内容。

from crewai_tools import SerperDevTool, ScrapeWebsiteTool

search_tool = SerperDevTool()
scrape_tool = ScrapeWebsiteTool()

researcher.tools = [search_tool, scrape_tool]
# 或者直接在 Agent 初始化时传入 tools 参数

同样的模式,我们定义撰稿人和审查员:

writer = Agent(
    role="资深技术撰稿人",
    goal="将研究员提供的信息,编写成结构清晰、语言流畅、专业且易读的竞品分析报告",
    backstory=(
        "你做过十年科技记者,擅长将复杂的技术概念转化为大众能懂的语言。"
        "你写的文章总是逻辑清晰、段落分明。"
    ),
    tools=[],   # 撰稿人通常不需要外部工具,专注文本生成
    verbose=True,
    allow_delegation=False
)

reviewer = Agent(
    role="质量审核编辑",
    goal="检查报告的事实准确性、段落连贯性、格式规范,确保最终报告无低级错误",
    backstory=(
        "你曾在一家顶级出版社担任主编,对文字和细节有近乎苛刻的要求。"
        "你不会遗漏任何一个事实错误或语句不通。"
    ),
    tools=[],   # 审核只需专注文本,使用大模型本身的能力
    verbose=True,
    allow_delegation=False
)

角色-工具绑定清单(一眼看清整体设计):

角色 工具/Skill 说明
研究员 SerperDevTool, ScrapeWebsiteTool 搜索与网页抓取
撰稿人 无(仅用自身语言模型) 专注内容生成
审查员 无(仅用自身语言模型) 专注审查与润色

这种声明式的定义方式,让团队成员之间的技能边界非常清晰,也方便后续调整——比如哪天研究员需要新增一个 PDF 解析工具,改动只局限在它的 tools 列表里。


顺序与分层任务流程

有了角色,接下来要把工作流程化。CrewAI 用 Task 来定义每个环节的输入、期望输出,以及由哪个角色负责。任务之间可以设置依赖关系,前一个任务的输出自动成为下一个任务的上下文。

定义三个关联任务

我们采用 顺序流程(sequential process),这也是 CrewAI 默认的模式:任务一个接一个执行,前序完成后再开启后续。

from crewai import Task

research_task = Task(
    description=(
        "对 {product_name} 进行竞品分析。\n"
        "1. 找出至少 3 个主要竞品。\n"
        "2. 搜集每种竞品的核心功能、价格区间、用户评价摘要。\n"
        "3. 将搜集到的信息整理成结构化摘要,用中文输出。"
    ),
    expected_output="一份包含竞品名称、核心功能、价格、用户评价的结构化摘要。",
    agent=researcher,
    # 这里用占位符 {product_name},运行时动态传入
)

writing_task = Task(
    description=(
        "根据研究员提供的竞品信息摘要,撰写一份专业的竞品分析报告。\n"
        "要求:\n"
        "- 包含标题、引言、每个竞品的详细分析、总结与建议。\n"
        "- 语言简洁专业,条理清晰。"
    ),
    expected_output="完整的竞品分析报告(Markdown 格式)。",
    agent=writer,
    context=[research_task]  # 明确指定依赖:必须在研究任务完成后执行
)

review_task = Task(
    description=(
        "逐项审查撰稿人提供的报告。\n"
        "检查:事实是否准确,数据是否前后矛盾,语言是否流畅,格式是否规范。\n"
        "若有问题,直接修改后输出最终版。"
    ),
    expected_output="经过审核和润色的最终版竞品分析报告(Markdown 格式)。",
    agent=reviewer,
    context=[writing_task]  # 依赖撰稿任务
)

注意每个任务的 context 列表。CrewAI 会自动找出依赖关系,并将前序任务的实际输出放入后面任务的提示词中。你无需手动拼接字符串——这就是结构化的威力。

组建 Crew 并执行

最后一步,把所有角色和任务“装配”成一个 Crew,并设置运行参数。

from crewai import Crew, Process

crew = Crew(
    agents=[researcher, writer, reviewer],
    tasks=[research_task, writing_task, review_task],
    process=Process.sequential,  # 顺序执行模式
    verbose=True                 # 输出整体流程日志
)

result = crew.kickoff(inputs={"product_name": "Notion"})
print(result)

执行 kickoff 后,你会在控制台看到类似这样的日志(简略示意):

[DEBUG] Working Agent: 市场调研专家
[DEBUG] Starting task: research_task ...
...
[DEBUG] Working Agent: 资深技术撰稿人
[DEBUG] Starting task: writing_task ...
...
[DEBUG] Working Agent: 质量审核编辑
[DEBUG] Starting task: review_task ...
...

最后 result 中就是经过三个角色接力产出的正式报告。整个过程无需任何手动粘合。

⚠️ 踩坑经验:在顺序流程中,如果某个任务执行超时或出错,整个 Crew 会中断。可以设置 max_execution_time 限制单个任务时间,并结合 max_iter 防止死循环。例如,在研究任务中加入 max_execution_time=300(秒),防止网络搜索卡死。

对比:顺序流程 vs. 分层流程

CrewAI 除 Process.sequential 外,还支持 Process.hierarchical(分层流程)。在分层模式下,会有一个类似“项目经理”的协调 Agent 自动把子任务分配给不同角色,无需手动设置任务间的 context。它适合动态性更高的场景。

模式 适用场景 手动编排工作量
Sequential 流程固定、依赖明确的任务链(如本例) 需要显式定义任务顺序和依赖
Hierarchical 任务不确定、需要动态指派子任务(如多步骤研究) 低,但输出可控性稍差

对于结构化团队任务,顺序模式通常是最佳选择,因为你可以精准控制每个环节的输入输出,便于调试和质量把控。


性能评估与迭代优化

第一次运行 Crew 后,你可能会发现结果不如预期:研究员搜来的信息不痛不痒,撰稿人写的报告格式混乱,审查员放过明显错误……这时候就该进入“性能评估与迭代优化”环节。

利用日志识别瓶颈

CrewAI 的日志提供了每一步的决策依据。除了 verbose=True 打开详细日志外,你还可以通过 step_callback 自定义监控函数:

def log_step(step_info):
    with open("crew_log.txt", "a") as f:
        f.write(f"{step_info.agent_role} 完成任务: {step_info.task}\n")

researcher.step_callback = log_step

回到我们的例子,分析日志时我们发现了几个典型问题及优化方案:

问题 1:研究员输出过于简略
日志显示研究员只搜索了一次并返回了很短的摘要。优化手段:在 research_task.description 中加入更明确的要求,比如“每个竞品至少写出三点核心功能和三条用户评价”。同时,可以设置 max_iter=5 让它有更多思考步骤。

问题 2:撰稿人忽略格式要求
如果报告没有按 Markdown 标题层级编写,可以在撰稿人的 backstory 中加入“你总是严格遵守 Markdown 格式”,并在写作任务的 expected_output 里贴上期望的格式示例(如“请务必使用 ## 标题分隔各部分”)。

问题 3:审查员放水
如果审查员敷衍了事,可以在 review_task.description 中加入具体的检查清单(例如逐项核对“竞品名称是否全部出现”、“价格区间是否有误”),或者给审查员设定一个更高的 goal:“宁可误报,绝不漏过任何一处错误”。

优化角色分配和提示词

官方文档在“Crafting Effective Agents”中反复强调:花大精力写好任务描述。这里有一组实战验证过的优化原则:

  1. 任务描述越具体,输出越可预期。多用量化指标(“至少 3 个竞品”、“每个竞品分析不少于 200 字”)。
  2. 目标要包含“成功标准”。例如,“准确率:所有价格数据必须有明确来源”。
  3. 利用背景故事引导风格。不只是“他是一个研究员”,而是“他讨厌模糊描述,会反复确认数据来源”。
  4. 善用 expected_output 作为格式锚点。可以直接在里面贴一个理想输出的框架,模型更容易遵循。

迭代循环演示

实际工作中,我们通常会反复调整。比如,在发现研究员输出不够结构化后,我们将研究任务修改为:

research_task.description = (
    "对 {product_name} 进行竞品分析。\n"
    "请务必按以下 JSON 格式输出每一个竞品:\n"
    '{"name": "竞品名", "features": ["特征1","特征2"], "price": "区间", "user_feedback": "简要评价"}\n'
    "至少输出 3 个竞品。"
)

然后重新运行,再检查撰稿环节,如此循环。CrewAI 的声明式定义让这种迭代只改一行描述,无需动整体架构。


回顾

从“一团乱麻”到“结构化产出”,我们在这 45 分钟里做了以下动作:

  1. 定义了三个专业 Agent(研究员、撰稿人、审查员),声明了各自的角色、目标、背景与工具。
  2. 设计了三个顺序执行的任务,并明确依赖关系,让前序产出自动流入后续任务。
  3. 组建 Crew 并执行,生成了完整的竞品分析报告。
  4. 利用日志分析问题,优化了任务描述和角色设定,提高了输出质量。

整个过程中,CrewAI 的结构化抽象始终让我们聚焦在“谁该做什么”和“应该做成什么样”,而无需操心底层的消息传递和对话管理。


行动清单

  • [ ] 安装 crewaicrewai_tools 包,并配置 API Key。
  • [ ] 按照“角色-目标-背景”框架定义你的第一个 Agent,先不求多,只定义一个。
  • [ ] 为该 Agent 编写一个具体的任务,并实际运行一次 Crew
  • [ ] 打开 verbose=True,观察日志,根据输出结果迭代任务描述。
  • [ ] 尝试添加第二个 Agent,并用 context 建立任务依赖,体验接力效果。

下一步:让角色能力跨越框架

现在,你已经有了一支能完成结构化报告的专业团队。但很快你可能就会想:这个研究员的能力(搜索 + 网页抓取)能不能也让它在 AutoGen 或 LangGraph 中用?能不能今天在 CrewAI 中当“分析师”,明天换个框架照样用?下一章《跨框架 Skills 互操作是避免供应商锁定的关键》将解答这个问题——我们将设计一套适配层,让你的 Agent 技能定义一次,在多个框架中复用。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~