第七章:CrewAI 角色扮演与 Flow 事件驱动
本章目标:深入理解 CrewAI 的设计哲学——”Role Play is All You Need”,掌握 Agent/Crew/Task 三层架构以及 2025 年新增的 Flow 事件驱动层,分析它与 tRPC-Agent、LangGraph 的差异化定位。
7.1 设计哲学:Role Play is All You Need
日常类比:剧组拍电影
想象你是一个电影导演,要拍一部动作片。你不会对着摄影师说”给我一个 128x128 像素的矩阵”,你会说:
- “你是首席摄影师,有 20 年经验,擅长运动镜头”(Role + Backstory)
- “你的目标是让每个动作场面都有视觉冲击力”(Goal)
- “现在拍这场追车戏,要体现速度感”(Task)
CrewAI 的核心洞察是:与其精确编程每一步执行逻辑,不如给 LLM 一个角色设定,让它”入戏”后自主完成任务。这不是玩具般的 prompt hack——这是经过验证的结构化 prompt engineering 方法论。
为什么角色扮演有效?
学术研究和工程实践都表明,给 LLM 一个具体角色设定能显著提升输出质量:
- 激活相关知识:告诉 LLM “你是资深安全工程师” 会让它优先激活安全相关的知识
- 约束输出风格:角色设定隐式定义了回答的专业程度、用语习惯、关注重点
- 提供决策框架:面对模糊指令时,角色身份帮助 LLM 做出”这个角色会怎么做”的判断
- 减少幻觉:具体的角色比”你是一个 AI 助手”更能约束 LLM 不超出能力范围
CrewAI 把这个洞察系统化了——它不是让开发者手搓 prompt,而是提供了结构化的角色定义 API。
7.2 Agent 定义三要素:Role / Goal / Backstory
7.2.1 核心三元组
from crewai import Agent
senior_researcher = Agent(
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI agent frameworks",
backstory="""You are a veteran technology researcher with 15 years
of experience tracking AI/ML trends. You've published extensively
in top conferences and have a knack for identifying which technologies
will become mainstream. You're known for your rigorous methodology:
always citing sources, cross-referencing claims, and distinguishing
hype from substance.""",
)
三个字段各自的作用:
Role(角色):一句话定义”你是谁”。这是最表层的身份标签,决定了 LLM 回答时的基本立场。
Goal(目标):定义”你要达成什么”。这是任务层面的方向指引——当有多个可能的行动路径时,Goal 帮助 LLM 选择最符合目标的那条。
Backstory(背景故事):这是最容易被误解为”装饰”的字段,但实际上它是最重要的结构化 prompt engineering 手段。
7.2.2 为什么 Backstory 不是装饰
很多人看到 “backstory” 会觉得这是 RPG 游戏里的”角色背景”——有趣但不重要。这是错误的。在 CrewAI 中,Backstory 承担了关键的工程功能:
- 技能声明:告诉 LLM 这个角色掌握哪些技能(”published in top conferences” → 它知道如何做学术研究)
- 方法论注入:”rigorous methodology: always citing sources” → 约束输出必须有来源
- 行为约束:”distinguishing hype from substance” → 减少 LLM 生成空洞的正面描述
- 隐式工具使用引导:如果 backstory 提到”你擅长用 Google Scholar 搜论文”,LLM 会更倾向于调用搜索工具
对比两种 backstory 的效果:
# 坏的 backstory(装饰性,不提供信息)
backstory_bad = "You are a smart and helpful AI assistant."
# 好的 backstory(结构化,功能性)
backstory_good = """You are a senior backend engineer at a fintech company,
specializing in distributed systems and payment processing. You've handled
3 major incidents involving race conditions in transaction processing.
Your debugging approach: reproduce first, then bisect, then fix.
You never guess—you always verify with logs and metrics."""
好的 backstory 实质上是把”开发者对 Agent 行为的期望”编码成了自然语言约束。
7.2.3 Agent 的完整字段列表
除了核心三元组,CrewAI Agent 还有丰富的配置字段:
from crewai import Agent, LLM
agent = Agent(
# === 核心三元组 ===
role="Senior Data Analyst",
goal="Provide accurate, actionable insights from complex datasets",
backstory="...",
# === LLM 配置 ===
llm=LLM(model="gpt-4", temperature=0.1), # 指定模型
# === 工具能力 ===
tools=[search_tool, calculator_tool], # 可用工具列表
# === MCP 服务器(2025新增)===
mcps=[
MCPServer(url="http://localhost:3000"), # 连接 MCP 服务
],
# === 技能(Skills,2025新增)===
skills=[data_analysis_skill, visualization_skill],
# === 知识源 ===
knowledge_sources=[ # RAG 知识库
PDFKnowledgeSource(path="company_data.pdf"),
StringKnowledgeSource(content="公司规则..."),
],
# === 行为控制 ===
max_iter=15, # 最大思考迭代次数(防止无限循环)
allow_delegation=True, # 是否允许将任务委托给其他 Agent
verbose=True, # 是否打印思考过程
# === 推理能力(2025新增)===
reasoning=True, # 启用 Chain-of-Thought 增强推理
# === 内存 ===
memory=True, # 启用长期记忆
# === 缓存 ===
cache=True, # 缓存工具调用结果
# === 执行控制 ===
max_rpm=10, # 每分钟最大请求数(限流)
max_retry_limit=2, # 失败重试次数
)
7.2.4 字段间的协同效应
这些字段不是独立工作的——它们组合成一个连贯的 Agent 行为模式:
role + backstory → System Prompt 的核心内容
goal → 嵌入到每次 LLM 调用中,引导响应方向
tools → 拼接到 prompt 中的工具描述部分
max_iter → ReAct 循环的硬性终止条件
allow_delegation → 是否在 prompt 中注入"你可以请求其他人帮忙"
knowledge_sources → RAG 检索后注入到 context 中
最终发给 LLM 的 prompt 大致是:
[System]
You are {role}. {backstory}
Your personal goal is: {goal}
You have access to the following tools: {tools_description}
{if allow_delegation: "You can delegate tasks to: {other_agents}"}
{if knowledge_sources: "Relevant context: {retrieved_knowledge}"}
[Current Task]
{task.description}
Expected output: {task.expected_output}
7.3 Crew 类:组装你的团队
7.3.1 Crew 的核心概念
Crew 是 CrewAI 的”团队”抽象——把一组 Agent 和一组 Task 组合起来,加上执行模式(Process),就构成了一个可执行的工作流。
from crewai import Crew, Process
crew = Crew(
agents=[researcher, writer, editor], # 团队成员
tasks=[research_task, write_task, edit_task], # 任务列表
process=Process.sequential, # 执行模式
verbose=True,
)
# 启动执行
result = crew.kickoff(inputs={"topic": "AI Agent 2025 趋势"})
日常类比:Crew 就是一个项目组。你把人(agents)、活(tasks)、工作方式(process)定义好,然后一声令下开干。
7.3.2 Crew 的完整配置
crew = Crew(
# === 核心配置 ===
agents=[...], # Agent 列表
tasks=[...], # Task 列表
process=Process.sequential, # 执行模式
# === 管理者配置(仅 Hierarchical 模式)===
manager_llm=LLM(model="gpt-4"), # Manager Agent 的 LLM
manager_agent=None, # 或直接提供自定义 Manager Agent
# === 执行配置 ===
verbose=True, # 打印执行过程
memory=True, # 启用团队共享记忆
cache=True, # 缓存工具调用
max_rpm=25, # 全团队请求限流
# === 输出配置 ===
output_log_file="crew_log.txt", # 日志文件
# === 高级配置 ===
planning=True, # 启用任务前自动规划
planning_llm=LLM(model="gpt-4"), # 规划用的 LLM
)
7.4 Process 双模式
7.4.1 Sequential 模式——流水线
Task 1 Task 2 Task 3
┌─────────┐ ┌─────────┐ ┌─────────┐
│Researcher│ → │ Writer │ → │ Editor │
│ │ │ │ │ │
│ 研究主题 │ │ 写文章 │ │ 润色编辑│
└─────────┘ └─────────┘ └─────────┘
output ────→ context ────→ context
Sequential 模式下,Task 按定义顺序依次执行。每个 Task 的输出自动成为下一个 Task 的上下文(context)。
from crewai import Crew, Task, Process
# 任务按顺序定义
research_task = Task(
description="Research the topic: {topic}",
expected_output="A comprehensive research report with sources",
agent=researcher,
)
write_task = Task(
description="Write an article based on the research",
expected_output="A 2000-word article ready for editing",
agent=writer,
context=[research_task], # 显式声明依赖上一个任务的输出
)
edit_task = Task(
description="Edit and polish the article",
expected_output="A publication-ready article",
agent=editor,
context=[write_task],
)
crew = Crew(
agents=[researcher, writer, editor],
tasks=[research_task, write_task, edit_task],
process=Process.sequential, # 流水线模式
)
适用场景:任务之间有明确的前后依赖关系,每一步的输出是下一步的输入。
日常类比:工厂流水线——先切菜、再炒菜、再摆盘。每一步必须等前一步完成。
7.4.2 Hierarchical 模式——经理自动分配
┌──────────────────┐
│ Manager Agent │ ← 自动合成(或自定义)
│ "项目经理" │
└───┬──────┬───┬──┘
│ │ │
┌─────────┤ │ ├─────────┐
▼ ▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│Researcher│ │ Writer │ │ Editor │
└──────────┘ └──────────┘ └──────────┘
Hierarchical 模式下,CrewAI 自动创建(或使用你提供的)一个 Manager Agent。Manager 负责:
- 分析所有 Task
- 决定将每个 Task 分配给哪个 Agent
- 协调执行顺序
- 综合各 Agent 的输出
- 质量把控和重新分配
crew = Crew(
agents=[researcher, writer, editor],
tasks=[research_task, write_task, edit_task],
process=Process.hierarchical, # 层级模式
manager_llm=LLM(model="gpt-4"), # Manager 使用的 LLM
)
适用场景:任务之间的依赖关系不那么线性,需要动态调度,或者你不确定最优执行顺序。
日常类比:公司管理模式——老板看了需求后自己决定谁做什么、先做哪个,做完了不满意可以让人重做。
7.4.3 两种模式的对比
| 维度 | Sequential | Hierarchical |
|---|---|---|
| 控制权 | 开发者显式定义执行顺序 | Manager Agent 动态决策 |
| 确定性 | 高(同样的输入,同样的执行顺序) | 低(Manager 可能做不同的决策) |
| 灵活性 | 低(固定流水线) | 高(可动态调整) |
| Token 消耗 | 较低 | 较高(Manager 的决策也消耗 token) |
| 调试难度 | 低(清晰的线性日志) | 较高(需要理解 Manager 的决策逻辑) |
| 适用场景 | 任务依赖明确 | 任务复杂、需要动态协调 |
7.4.4 与 tRPC-Agent Team 模式的对比
CrewAI 的两种 Process 模式与 tRPC-Agent 的 Coordinator/Swarm 有对应关系:
CrewAI Sequential ≈ tRPC-Agent 无直接对应(需要用 StateGraph)
CrewAI Hierarchical ≈ tRPC-Agent Coordinator 模式
但区别在于:
- CrewAI Hierarchical 的 Manager 是自动合成的
- tRPC-Agent Coordinator 需要你定义 Coordinator Agent 的 prompt
- tRPC-Agent 还有 Swarm 模式(去中心化),CrewAI 没有直接对应
7.5 Task 类的设计
7.5.1 Task 的核心字段
Task 是 CrewAI 中最重要的连接组件——它把 Agent 的能力和具体的工作需求对接起来。
from crewai import Task
task = Task(
# === 核心定义 ===
description="分析竞品 X 的技术架构,重点关注其 Agent 编排层的实现方式",
expected_output="一份 2000 字的技术分析报告,包含架构图、核心设计决策、优缺点分析",
agent=researcher, # 指定负责的 Agent
# === 上下文依赖 ===
context=[previous_task_1, previous_task_2], # 依赖哪些前置任务的输出
# === 输出配置 ===
output_file="reports/competitor_analysis.md", # 将输出保存到文件
output_json=AnalysisReport, # 输出为结构化 JSON(Pydantic model)
output_pydantic=AnalysisReport, # 同上,用 Pydantic 验证
# === 执行配置 ===
async_execution=False, # 是否异步执行
# === Human-in-the-Loop ===
human_input=True, # 执行前请求人工确认/补充
# === 回调 ===
callback=on_task_complete, # 完成后的回调函数
)
7.5.2 expected_output 的重要性
expected_output 不是”文档注释”——它被直接注入到 LLM prompt 中,作为输出格式和质量的约束:
# 坏的 expected_output(太模糊)
expected_output_bad = "A good report"
# 好的 expected_output(具体、可验证)
expected_output_good = """一份结构化的竞品分析报告,包含:
1. 架构概览图(文字描述)
2. 核心组件列表及其职责
3. 关键设计决策(至少 3 个)及其 trade-off 分析
4. 与我们方案的对比表格
5. 总结:3 个可借鉴的优点 + 2 个应避免的缺点"""
好的 expected_output 相当于给 LLM 一个评分 rubric——它知道自己的输出会被这个标准衡量,就会朝这个方向努力。
7.5.3 context 字段的数据流
context 字段建立了 Task 之间的数据依赖关系:
# Task 1 的输出
research_output = "AI Agent 框架趋势报告..."
# Task 2 声明依赖 Task 1
write_task = Task(
description="基于研究报告写一篇博客",
context=[research_task], # ← 这里
agent=writer,
)
# 实际执行时,writer 看到的 prompt 是:
# [System] You are a Writer...
# [Context from previous tasks]
# Task: Research Analyst's output: "AI Agent 框架趋势报告..."
# [Current Task]
# 基于研究报告写一篇博客
这是 CrewAI 简洁性的来源——你不需要手动传递数据,框架自动把上游 Task 的输出注入到下游 Task 的 context 中。
7.6 Flow 事件驱动层(2025 新增)
7.6.1 为什么需要 Flow?
Crew 的 Sequential/Hierarchical 模式处理的是”一组 Agent 完成一组 Task”的场景。但现实中,很多工作流不是”一组任务”能概括的:
- 需要条件分支:审核通过走路径 A,不通过走路径 B
- 需要循环:生成→评估→修改→再评估
- 需要人工干预:在特定节点等待用户输入
- 需要多 Crew 协作:研究 Crew 完成后交给写作 Crew
Flow 就是为了解决这些更复杂的编排需求而设计的——它在 Crew 之上提供了一个事件驱动的控制层。
日常类比:如果 Crew 是”一个项目组做一个项目”,Flow 就是”项目管理办公室协调多个项目组的工作流”。
7.6.2 Flow 的核心装饰器
Flow 通过 Python 装饰器定义事件监听和路由逻辑:
from crewai.flow.flow import Flow, listen, start, router, or_, and_
class ContentPipeline(Flow):
@start()
def begin(self):
"""标记为起始节点——Flow 从这里开始执行"""
print("Pipeline started!")
return {"topic": self.state.topic}
@listen(begin)
def research(self, event):
"""监听 begin 事件——begin 完成后自动触发 research"""
research_crew = Crew(agents=[researcher], tasks=[research_task])
result = research_crew.kickoff(inputs=event)
return {"research": result.raw}
@listen(research)
def write_draft(self, event):
"""监听 research 事件——research 完成后触发"""
writing_crew = Crew(agents=[writer], tasks=[write_task])
result = writing_crew.kickoff(inputs=event)
return {"draft": result.raw}
@router(write_draft)
def review_router(self, event):
"""路由节点——根据条件决定下一步去哪"""
if self.state.quality_score > 0.8:
return "publish" # 质量合格 → 发布
elif self.state.iterations >= 3:
return "publish" # 达到上限 → 强制发布
else:
return "revise" # 需要修改 → 返回修改
@listen("publish")
def publish(self, event):
"""监听路由的 'publish' 输出"""
print(f"Published: {self.state.draft[:100]}")
@listen("revise")
def revise(self, event):
"""监听路由的 'revise' 输出——形成循环"""
self.state.iterations += 1
# 修改后重新触发 write_draft
return self.write_draft(event)
7.6.3 @start 装饰器
@start()
def initialize(self):
"""Flow 的入口点。一个 Flow 可以有多个 @start 方法(并行启动)"""
pass
- 标记一个方法为 Flow 的起始节点
- Flow.kickoff() 调用时,所有 @start 方法同时执行
- 可以有多个 @start 方法——它们并行执行
7.6.4 @listen 装饰器
@listen(source_method) # 监听另一个方法的完成事件
def handler(self, event):
pass
@listen("route_name") # 监听 router 返回的字符串
def handler(self, event):
pass
- 声明”当某个方法/事件完成后,执行我”
- 这是 Flow 中构建数据流的核心机制
- event 参数接收上游方法的返回值
7.6.5 @router 装饰器
@router(source_method)
def my_router(self, event):
"""返回字符串,决定触发哪个下游 @listen"""
if condition_a:
return "path_a"
else:
return "path_b"
- 条件分支的核心机制
- 返回的字符串对应下游
@listen("path_a")和@listen("path_b") - 结合 @listen 实现循环(router 返回的路径指向之前的节点)
7.6.6 @human_feedback 装饰器(2025 新增)
from crewai.flow.flow import human_feedback
class ReviewFlow(Flow):
@listen(generate_draft)
@human_feedback("请审核以下草稿,输入修改意见或输入 'approve' 通过:")
def human_review(self, event, feedback):
"""等待人工输入"""
if feedback.lower() == "approve":
self.state.approved = True
else:
self.state.feedback = feedback
self.state.approved = False
return {"approved": self.state.approved}
- 在节点执行时暂停,等待人工输入
- feedback 参数接收用户输入的文本
- 这是 CrewAI 的 Human-in-the-Loop 实现
7.7 FlowState 与 StateProxy 线程安全
7.7.1 FlowState 定义
from crewai.flow.flow import Flow
from pydantic import BaseModel
class ArticleState(BaseModel):
"""Flow 的状态模型——用 Pydantic 定义"""
topic: str = ""
draft: str = ""
feedback: list[str] = []
quality_score: float = 0.0
iterations: int = 0
approved: bool = False
class ArticleFlow(Flow[ArticleState]):
"""泛型参数指定 State 类型"""
@start()
def begin(self):
# 通过 self.state 访问和修改状态
self.state.topic = "AI Agent 框架对比"
print(f"Starting with topic: {self.state.topic}")
FlowState 与 LangGraph/tRPC-Agent 的 State 类似,但有几个特点:
- 基于 Pydantic:自带类型验证和序列化
- 通过 self.state 访问:不是通过函数参数传递
- 可变的:直接修改 self.state 的字段即可
7.7.2 StateProxy 线程安全
当 Flow 中有并行执行的节点时(多个 @start 或 or_ 触发),多个方法可能同时修改 self.state。CrewAI 通过 StateProxy 实现线程安全:
class StateProxy:
"""线程安全的状态代理"""
def __init__(self, state: BaseModel):
self._state = state
self._lock = threading.RLock()
def __getattr__(self, name):
with self._lock:
return getattr(self._state, name)
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value)
else:
with self._lock:
setattr(self._state, name, value)
这意味着:
- 读取
self.state.topic是线程安全的 - 写入
self.state.topic = "new"也是线程安全的 - 但复合操作(读-改-写)不是原子的——需要额外注意
# 不安全的复合操作(可能产生竞态条件)
self.state.count = self.state.count + 1
# 线程 A 读到 count=5,线程 B 也读到 count=5
# 两者都写入 6,实际应该是 7
# 安全做法:使用 list append(原子操作)
self.state.feedback.append("新反馈")
# 或者使用 Flow 提供的原子操作方法
7.8 or_ / and_ 逻辑运算符
7.8.1 or_ —— 任一完成即触发
from crewai.flow.flow import or_
class ParallelFlow(Flow):
@start()
def search_google(self):
return {"source": "google", "results": [...]}
@start()
def search_bing(self):
return {"source": "bing", "results": [...]}
@listen(or_(search_google, search_bing))
def process_first_result(self, event):
"""只要 google 或 bing 任一个返回结果,立即开始处理"""
print(f"Got first result from: {event['source']}")
or_ 的语义是”任一上游完成就触发”——类似 JavaScript 的 Promise.race()。
适用场景:
- 多个搜索引擎并行查询,用最快返回的结果
- 多种策略并行尝试,第一个成功的就用
- 超时备选方案
7.8.2 and_ —— 全部完成才触发
from crewai.flow.flow import and_
class GatherFlow(Flow):
@start()
def research_market(self):
return {"market_data": [...]}
@start()
def research_technology(self):
return {"tech_data": [...]}
@start()
def research_competition(self):
return {"competition_data": [...]}
@listen(and_(research_market, research_technology, research_competition))
def synthesize(self, events):
"""三个研究全部完成后,综合分析"""
# events 是一个列表,包含所有上游的返回值
all_data = merge_research(events)
return {"synthesis": all_data}
and_ 的语义是”所有上游都完成才触发”——类似 JavaScript 的 Promise.all()。
适用场景:
- 需要多方数据汇总后才能分析
- 并行执行多个独立任务后汇聚结果
- 分布式计算的 reduce 阶段
7.8.3 组合使用
@listen(and_(
or_(fast_search, slow_search), # 搜索:任一个返回即可
mandatory_validation, # 验证:必须完成
))
def proceed(self, events):
"""搜索(快的或慢的)完成 AND 验证完成 → 继续"""
pass
7.9 Flow 与 Crew 的组合
7.9.1 Flow 内嵌多个 Crew
这是 CrewAI 最强大的编排模式——Flow 作为宏观控制器,每个节点内部运行一个独立的 Crew。
class ProductLaunchFlow(Flow[LaunchState]):
"""产品发布流程:研究 → 内容制作 → 审核 → 发布"""
@start()
def market_research(self):
"""阶段 1:市场研究(一组 Agent 协作)"""
research_crew = Crew(
agents=[
Agent(role="Market Analyst", ...),
Agent(role="Competitor Researcher", ...),
Agent(role="User Researcher", ...),
],
tasks=[
Task(description="分析目标市场规模和趋势", ...),
Task(description="调研主要竞品的定位和策略", ...),
Task(description="总结目标用户的痛点和需求", ...),
],
process=Process.sequential,
)
result = research_crew.kickoff()
self.state.research_report = result.raw
return result.raw
@listen(market_research)
def content_creation(self, research):
"""阶段 2:内容制作(另一组 Agent 协作)"""
content_crew = Crew(
agents=[
Agent(role="Copywriter", ...),
Agent(role="Designer", ...),
Agent(role="SEO Specialist", ...),
],
tasks=[
Task(description="撰写产品文案", context_from=research, ...),
Task(description="设计视觉素材方案", ...),
Task(description="优化 SEO 关键词", ...),
],
process=Process.hierarchical, # 由 Manager 动态分配
manager_llm=LLM(model="gpt-4"),
)
result = content_crew.kickoff()
self.state.content_package = result.raw
return result.raw
@router(content_creation)
def quality_gate(self, content):
"""阶段 3:质量门禁"""
# 可以用 LLM 做自动评分
score = self._evaluate_quality(content)
self.state.quality_score = score
if score >= 0.85:
return "approved"
elif self.state.revision_count >= 2:
return "force_publish"
else:
return "needs_revision"
@listen("needs_revision")
def revise(self, event):
"""修改循环"""
self.state.revision_count += 1
return self.content_creation(self.state.research_report)
@listen(or_("approved", "force_publish"))
def publish(self, event):
"""发布"""
publish_crew = Crew(
agents=[Agent(role="Publisher", ...)],
tasks=[Task(description="执行发布流程", ...)],
)
publish_crew.kickoff()
print("Product launched!")
# 启动整个流程
flow = ProductLaunchFlow()
flow.kickoff(inputs={"product_name": "AI Writer Pro"})
7.9.2 架构层次
┌─────────────────────────────────────────────────────────┐
│ Flow │
│ (事件驱动层:控制宏观流程、条件分支、循环) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Crew 1 │ → │ Crew 2 │ → │ Crew 3 │ │
│ │(研究团队) │ │(创作团队) │ │(发布团队) │ │
│ │ │ │ │ │ │ │
│ │ Agent A │ │ Agent D │ │ Agent G │ │
│ │ Agent B │ │ Agent E │ │ │ │
│ │ Agent C │ │ Agent F │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ↑ ↑ ↑ │
│ │ │ │ │
│ Sequential Hierarchical Sequential │
│ │
└─────────────────────────────────────────────────────────┘
这种分层设计的好处:
- 关注点分离:Flow 管宏观流程,Crew 管微观协作
- 可复用:同一个 Crew 可以在不同的 Flow 中使用
- 独立测试:每个 Crew 可以独立测试和调优
- 灵活扩展:新增一个阶段只需在 Flow 中加一个方法
7.10 与 tRPC-Agent 和 LangGraph 的对比
7.10.1 抽象层级对比
高层抽象 ──────────────────────────────────────── 底层控制
│ │
▼ ▼
CrewAI tRPC-Agent LangGraph
(Crew+Flow) (Team+StateGraph) (StateGraph+Pregel)
Role Play Coordinator/Swarm Channel/Reducer
角色驱动 模式驱动 数据驱动
7.10.2 核心设计差异
| 维度 | CrewAI | tRPC-Agent | LangGraph |
|---|---|---|---|
| 核心抽象 | Agent=角色 | Agent=工具执行者 | Node=函数 |
| Agent 定义 | Role/Goal/Backstory | Name/SystemPrompt/Tools | 无内置 Agent 概念 |
| 任务定义 | Task 类(描述式) | 隐含在 Agent prompt | 隐含在 Node 函数 |
| 编排方式 | Crew(process) + Flow(event) | Team(mode) + StateGraph(graph) | StateGraph(graph) |
| 循环实现 | Flow @router + @listen | StateGraph 条件边 | Channel 版本号驱动 |
| Human-in-the-Loop | @human_feedback | WithInterruptBefore/After | interrupt_before/after |
| 并行 | @start 多入口 / or_ / and_ | MultiConditionalEdges / JoinEdge | BSP 自动并行 |
| 状态管理 | Pydantic FlowState | Go struct | TypedDict + Annotated |
| 语言 | Python | Go | Python |
7.10.3 适用场景对比
选 CrewAI 当:
- 你的场景天然是”一群专家协作完成任务”
- 更看重 Agent 的”智能性”而非流程的”精确性”
- 快速原型验证,不想写太多底层代码
- 任务可以自然语言描述,不需要精确编程
选 tRPC-Agent 当:
- 在 Go 微服务体系中,需要原生集成
- 需要编译时类型安全
- 同时需要简单场景(Team)和复杂场景(StateGraph)的支持
- 企业级部署,需要 gRPC 流式支持
选 LangGraph 当:
- 需要最精细的状态控制(自定义 Channel/Reducer)
- 需要强大的 Checkpoint(Time Travel、分支执行)
- 工作流极其复杂,需要 BSP 的确定性保证
- 需要与 LangChain 生态深度集成
7.10.4 代码风格对比
同一个需求:”研究一个主题 → 写文章 → 审核(不通过则重写)→ 发布”
CrewAI 写法:
class ArticleFlow(Flow):
@start()
def research(self):
crew = Crew(agents=[researcher], tasks=[research_task])
return crew.kickoff().raw
@listen(research)
def write(self, data):
crew = Crew(agents=[writer], tasks=[write_task])
return crew.kickoff(inputs={"research": data}).raw
@router(write)
def review(self, draft):
return "publish" if quality_ok(draft) else "revise"
@listen("revise")
def do_revise(self, _):
return self.write(self.state.research)
@listen("publish")
def publish(self, _):
print("Done!")
tRPC-Agent 写法:
graph := NewStateGraph(ArticleState{})
graph.AddAgentNode("research", researchAgent)
graph.AddNode("write", writeFunc)
graph.AddNode("review", reviewFunc)
graph.AddNode("publish", publishFunc)
graph.AddEdge("research", "write")
graph.AddEdge("write", "review")
graph.AddConditionalEdges("review", reviewRoute, map[string]string{
"pass": "publish", "revise": "write",
})
graph.SetEntryPoint("research")
graph.SetFinishPoint("publish")
compiled, _ := graph.Compile()
LangGraph 写法:
graph = StateGraph(ArticleState)
graph.add_node("research", research_node)
graph.add_node("write", write_node)
graph.add_node("review", review_node)
graph.add_node("publish", publish_node)
graph.add_edge("research", "write")
graph.add_edge("write", "review")
graph.add_conditional_edges("review", review_route, {
"pass": "publish", "revise": "write"
})
graph.set_entry_point("research")
graph.set_finish_point("publish")
app = graph.compile()
可以看到:
- CrewAI 最”业务化”——代码读起来像业务流程描述
- tRPC-Agent 和 LangGraph 结构几乎相同——都是声明式图定义
- CrewAI 的 Agent 定义(Role/Goal/Backstory)比另外两个更”人性化”
7.11 CrewAI 的高级特性
7.11.1 Memory 系统
CrewAI 支持多层记忆系统:
from crewai.memory import ShortTermMemory, LongTermMemory, EntityMemory
crew = Crew(
agents=[...],
tasks=[...],
memory=True, # 启用记忆系统
# 可自定义记忆存储
short_term_memory=ShortTermMemory(), # 当前会话记忆
long_term_memory=LongTermMemory(), # 跨会话持久记忆
entity_memory=EntityMemory(), # 实体知识图谱
)
- 短期记忆:当前 Crew 执行中,Agent 之间共享的上下文
- 长期记忆:跨次执行保留的经验(”上次这个任务用方法 A 效果更好”)
- 实体记忆:识别和记忆出现过的实体(人名、公司名、技术术语)
7.11.2 Knowledge Sources(知识源)
from crewai.knowledge.source import (
PDFKnowledgeSource,
TextFileKnowledgeSource,
CSVKnowledgeSource,
JSONKnowledgeSource,
)
researcher = Agent(
role="Research Analyst",
knowledge_sources=[
PDFKnowledgeSource(
file_paths=["papers/agent_survey_2025.pdf"],
chunk_size=1000,
chunk_overlap=200,
),
TextFileKnowledgeSource(
file_paths=["docs/internal_guidelines.md"],
),
],
)
Knowledge Sources 本质上是内置的 RAG——Agent 在执行任务前,会自动从这些来源检索相关信息并注入到 context 中。
7.11.3 Structured Output(结构化输出)
from pydantic import BaseModel
class CompetitorReport(BaseModel):
company_name: str
strengths: list[str]
weaknesses: list[str]
market_share: float
recommendation: str
task = Task(
description="分析竞品 X 的市场表现",
expected_output="结构化的竞品分析报告",
output_pydantic=CompetitorReport, # 强制输出为此结构
agent=analyst,
)
# result.pydantic 就是一个 CompetitorReport 实例
result = crew.kickoff()
report: CompetitorReport = result.pydantic
print(report.company_name) # 类型安全访问
7.11.4 Planning(自动规划)
crew = Crew(
agents=[...],
tasks=[...],
planning=True, # 启用自动规划
planning_llm=LLM(model="gpt-4"), # 规划用的 LLM
)
启用 planning 后,Crew 在执行 tasks 之前会先让一个 LLM 分析所有任务,生成执行计划:
- 评估任务之间的依赖关系
- 决定是否有可以并行的任务
- 预估每个任务需要的时间和资源
- 生成更详细的执行步骤
这类似于 tRPC-Agent Coordinator 模式中 Coordinator Agent 的规划阶段。
7.12 CrewAI 的设计权衡
7.12.1 优势
- 上手极快:定义角色 → 定义任务 → kickoff,三步搞定
- 自然语言驱动:任务描述用自然语言,不需要编程式定义状态流转
- 角色抽象强大:好的 Role/Goal/Backstory 能让 LLM 表现出惊人的”专业性”
- Flow 层补全了编排能力:2025 年加入 Flow 后,终于能做复杂工作流了
- 生态丰富:内置 Memory、Knowledge、Planning、Structured Output
7.12.2 劣势
- 确定性不如图引擎:角色扮演的输出有随机性,同样的输入可能产生不同质量的输出
- 调试困难:当 Agent “入戏太深”输出了不相关的内容,很难从系统层面诊断
- Token 消耗大:Role+Goal+Backstory+Context 每次都要发给 LLM,context window 压力大
- Fine-grained 控制弱:你很难精确控制 Agent 在特定条件下的行为——它更像”引导”而非”命令”
- Flow 还比较年轻:相比 LangGraph 的 Pregel,Flow 的状态管理和 Checkpoint 不够成熟
7.12.3 什么时候 CrewAI 比图引擎更好?
核心判断标准:你的任务是”目标导向”还是”流程导向”?
- 目标导向(”帮我写一篇关于 X 的报告”)→ CrewAI 更适合
- 流程导向(”先做 A,如果 A>5 做 B,否则做 C,B 和 C 完成后做 D”)→ 图引擎更适合
另一个判断维度:你信任 LLM 的判断力到什么程度?
- 信任度高(”让 LLM 自己决定怎么搜索、怎么组织”)→ CrewAI
- 信任度低(”我要精确控制每一步的输入输出”)→ 图引擎
7.13 实战:构建一个完整的 Flow + Crew 系统
7.13.1 需求:自动化技术博客生产线
用户输入主题 → 研究 → 写作 → 技术审核 → SEO 优化 → 发布
↑ |
└── 不通过 ───────┘
7.13.2 完整代码
from crewai import Agent, Task, Crew, Process, LLM
from crewai.flow.flow import Flow, listen, start, router
from crewai.tools import SerperDevTool
from pydantic import BaseModel
# ===== 状态定义 =====
class BlogState(BaseModel):
topic: str = ""
research_data: str = ""
draft: str = ""
review_feedback: str = ""
seo_optimized: str = ""
quality_score: float = 0.0
revision_count: int = 0
published: bool = False
# ===== Agent 定义 =====
researcher = Agent(
role="Senior Tech Researcher",
goal="Find comprehensive, accurate, and up-to-date information on technical topics",
backstory="""You are a veteran technology journalist with 10+ years of experience
covering AI/ML, distributed systems, and developer tools. You have a PhD in Computer
Science and maintain active connections with researchers at top labs. Your research
methodology: start with primary sources (papers, official docs), cross-reference
with multiple sources, and always note the recency and reliability of each source.""",
tools=[SerperDevTool()],
llm=LLM(model="gpt-4"),
max_iter=10,
)
writer = Agent(
role="Technical Content Writer",
goal="Produce clear, engaging, and technically accurate blog posts",
backstory="""You are a technical writer who combines deep engineering knowledge
with excellent storytelling ability. You've written for major tech publications
and your posts consistently rank in the top 10% for reader engagement. Your
writing philosophy: lead with the 'why', use concrete examples before abstract
definitions, and always include runnable code snippets. You target an audience
of intermediate developers (2-5 years experience).""",
llm=LLM(model="gpt-4"),
max_iter=5,
)
reviewer = Agent(
role="Technical Editor & Fact-Checker",
goal="Ensure technical accuracy, logical flow, and readability",
backstory="""You are a senior technical editor at a top-tier engineering blog.
You've caught hundreds of subtle technical errors that would have embarrassed
authors. Your review checklist: factual accuracy, code correctness, logical flow,
jargon explanation, missing context, and readability score. You give constructive
feedback with specific suggestions, not vague complaints.""",
llm=LLM(model="gpt-4"),
max_iter=3,
)
seo_specialist = Agent(
role="Technical SEO Specialist",
goal="Optimize content for search engines without sacrificing technical quality",
backstory="""You specialize in SEO for technical content. You understand that
developer audiences hate keyword-stuffed content, so your approach is subtle:
optimize headings, meta descriptions, and internal linking while preserving
the author's voice. You focus on semantic SEO and topic clustering.""",
llm=LLM(model="gpt-4"),
max_iter=3,
)
# ===== Flow 定义 =====
class BlogProductionFlow(Flow[BlogState]):
@start()
def kickoff_research(self):
"""阶段 1: 深度研究"""
research_task = Task(
description=f"""Research the topic: '{self.state.topic}'
Gather: key concepts, recent developments (2024-2025),
code examples, expert opinions, and common misconceptions.
Minimum 5 distinct sources required.""",
expected_output="""A structured research brief with:
- Executive summary (3 sentences)
- Key findings (5-8 bullet points with source citations)
- Code examples or architecture diagrams
- Contrarian viewpoints or limitations
- Suggested article structure""",
agent=researcher,
)
crew = Crew(agents=[researcher], tasks=[research_task], verbose=True)
result = crew.kickoff()
self.state.research_data = result.raw
return result.raw
@listen(kickoff_research)
def write_article(self, research_data):
"""阶段 2: 撰写文章"""
write_task = Task(
description=f"""Write a technical blog post about '{self.state.topic}'.
Use the following research: {research_data}
Requirements:
- 2000-3000 words
- Start with a compelling hook
- Include at least 2 code examples
- End with actionable takeaways
- Target audience: intermediate developers""",
expected_output="A complete, publication-ready blog post in markdown format",
agent=writer,
)
crew = Crew(agents=[writer], tasks=[write_task], verbose=True)
result = crew.kickoff()
self.state.draft = result.raw
return result.raw
@listen(write_article)
def technical_review(self, draft):
"""阶段 3: 技术审核"""
review_task = Task(
description=f"""Review this technical article for accuracy and quality:
{draft}
Check for: factual errors, code bugs, logical gaps,
unclear explanations, missing context.
Rate overall quality 0-1 and provide specific feedback.""",
expected_output="""Review report with:
- Quality score (0-1)
- List of issues found (if any)
- Specific improvement suggestions
- Final verdict: APPROVE or NEEDS_REVISION""",
agent=reviewer,
)
crew = Crew(agents=[reviewer], tasks=[review_task], verbose=True)
result = crew.kickoff()
# 解析审核结果
self.state.review_feedback = result.raw
self.state.quality_score = self._extract_score(result.raw)
return result.raw
@router(technical_review)
def review_decision(self, review_result):
"""路由:根据审核结果决定下一步"""
if self.state.quality_score >= 0.8:
return "approved"
elif self.state.revision_count >= 2:
return "force_approve" # 防止无限循环
else:
return "needs_revision"
@listen("needs_revision")
def revise_article(self, _):
"""修改文章(循环)"""
self.state.revision_count += 1
revision_task = Task(
description=f"""Revise this article based on reviewer feedback:
Original article: {self.state.draft}
Reviewer feedback: {self.state.review_feedback}
Address all issues raised by the reviewer.""",
expected_output="A revised article addressing all feedback points",
agent=writer,
)
crew = Crew(agents=[writer], tasks=[revision_task])
result = crew.kickoff()
self.state.draft = result.raw
# 重新送审
return self.technical_review(result.raw)
@listen(or_("approved", "force_approve"))
def seo_optimization(self, _):
"""阶段 4: SEO 优化"""
seo_task = Task(
description=f"""Optimize this article for SEO:
{self.state.draft}
Focus on: heading optimization, meta description,
internal linking suggestions, keyword placement.""",
expected_output="SEO-optimized version of the article with meta tags",
agent=seo_specialist,
)
crew = Crew(agents=[seo_specialist], tasks=[seo_task])
result = crew.kickoff()
self.state.seo_optimized = result.raw
return result.raw
@listen(seo_optimization)
def publish(self, final_article):
"""阶段 5: 发布"""
self.state.published = True
print(f"\n{'='*60}")
print(f"Article Published!")
print(f"Topic: {self.state.topic}")
print(f"Revisions: {self.state.revision_count}")
print(f"Quality Score: {self.state.quality_score}")
print(f"{'='*60}\n")
return final_article
def _extract_score(self, review_text: str) -> float:
"""从审核文本中提取分数"""
import re
match = re.search(r'(\d+\.?\d*)\s*/\s*1|score[:\s]*(\d+\.?\d*)',
review_text.lower())
if match:
score = float(match.group(1) or match.group(2))
return min(score, 1.0)
return 0.5 # 默认中等分数
# ===== 运行 =====
if __name__ == "__main__":
flow = BlogProductionFlow()
result = flow.kickoff(inputs={"topic": "Building AI Agents with LangGraph in 2025"})
print(f"\nFinal article length: {len(flow.state.seo_optimized)} chars")
7.13.3 执行流程可视化
┌─────────────────────────────────────────────────────────────┐
│ BlogProductionFlow │
│ │
│ @start │
│ ┌────────────────────┐ │
│ │ kickoff_research │ Crew: [researcher] │
│ │ "深度研究" │ Process: sequential │
│ └─────────┬──────────┘ │
│ │ @listen │
│ ▼ │
│ ┌────────────────────┐ │
│ │ write_article │ Crew: [writer] │
│ │ "撰写文章" │ │
│ └─────────┬──────────┘ │
│ │ @listen │
│ ▼ │
│ ┌────────────────────┐ │
│ │ technical_review │ Crew: [reviewer] │
│ │ "技术审核" │ │
│ └─────────┬──────────┘ │
│ │ @router │
│ ▼ │
│ ┌──────┴──────┐ │
│ │ │ │
│ "approved" "needs_revision" │
│ "force_approve" │ │
│ │ ▼ │
│ │ ┌────────────────┐ │
│ │ │ revise_article │ → technical_review(循环) │
│ │ └────────────────┘ │
│ │ │
│ ▼ @listen(or_(...)) │
│ ┌────────────────────┐ │
│ │ seo_optimization │ Crew: [seo_specialist] │
│ └─────────┬──────────┘ │
│ │ @listen │
│ ▼ │
│ ┌────────────────────┐ │
│ │ publish │ "发布!" │
│ └────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
7.14 本章总结
CrewAI 代表了 Agent 框架设计的一种独特哲学——不是让程序员更好地控制 AI,而是让 AI 更好地”成为”某个角色。
核心架构:
Flow(事件驱动控制层)
└── Crew(团队协作层)
└── Agent(角色扮演层)
├── Role / Goal / Backstory
├── Tools / Knowledge / Memory
└── Task(任务执行单元)
关键设计决策:
- 角色扮演 > 精确编程:通过 Backstory 注入行为约束而非硬编码
- 描述式任务 > 命令式任务:Task 用自然语言描述”要什么”而非”怎么做”
- Flow 补全编排:Crew 适合单阶段协作,Flow 适合多阶段复杂工作流
- or_ / and_ 表达并行语义:比图引擎的并行边更直观
- Pydantic FlowState:类型安全 + 自动序列化
与其他框架的核心差异:
- vs tRPC-Agent:CrewAI 更”高层”(角色抽象 vs 图/模式抽象)
- vs LangGraph:CrewAI 更”语义化”(自然语言驱动 vs 版本号/Channel 驱动)
- 三者不是竞争关系——它们适合不同的场景和不同的”信任 LLM 程度”
7.15 思考题
-
设计题:为一个”AI 面试官”设计 Agent 三元组(Role/Goal/Backstory)。这个面试官需要:能问技术问题、能追问深入细节、能给出评价分数、对候选人保持友好但不降低标准。请写出至少 200 字的 Backstory,并解释你的每个设计选择为什么能影响 LLM 的行为。
-
架构选择:一个在线教育平台想用 AI 批改编程作业。流程是:学生提交代码 → 语法检查 → 运行测试 → 代码质量评估 → 生成反馈报告 → 如果分数低于 60 分给出改进建议。你会用 CrewAI 的 Sequential Process 还是 Flow + 多 Crew?给出理由并画出架构图。
-
对比分析:CrewAI 的
@router+@listen("route_name")vs LangGraph 的add_conditional_edgesvs tRPC-Agent 的AddConditionalEdges。这三种条件分支机制在以下场景中各有什么优劣:(a) 简单二选一;(b) 多条件分支(>5 个去向);(c) 动态决定并行分支数量。 -
深度思考:CrewAI 的 “Role Play is All You Need” 哲学在什么条件下会失效?列出至少 3 个”角色扮演不够用,必须用图引擎精确控制”的场景,并解释为什么。
-
实践题:设计一个 Flow,实现以下工作流:用户输入一段需求描述 → 产品经理 Agent 细化需求 → 架构师 Agent 设计方案 → (并行)前端 Agent 实现UI + 后端 Agent 实现API → (汇聚后)测试 Agent 验证 → 如果有 bug 回到对应的开发 Agent 修复。请特别注意:如何用 and_ 实现汇聚?如何用 @router 实现”回到对应的开发 Agent”而非回到所有开发 Agent?
上一章:第六章 LangGraph Channel 与 Pregel 引擎精读
返回目录:Agent 框架深度导读系列