犀牛鸟 2026 研究笔记 estelledc.github.io

第七章: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 像素的矩阵”,你会说:

CrewAI 的核心洞察是:与其精确编程每一步执行逻辑,不如给 LLM 一个角色设定,让它”入戏”后自主完成任务。这不是玩具般的 prompt hack——这是经过验证的结构化 prompt engineering 方法论。

为什么角色扮演有效?

学术研究和工程实践都表明,给 LLM 一个具体角色设定能显著提升输出质量:

  1. 激活相关知识:告诉 LLM “你是资深安全工程师” 会让它优先激活安全相关的知识
  2. 约束输出风格:角色设定隐式定义了回答的专业程度、用语习惯、关注重点
  3. 提供决策框架:面对模糊指令时,角色身份帮助 LLM 做出”这个角色会怎么做”的判断
  4. 减少幻觉:具体的角色比”你是一个 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 承担了关键的工程功能:

  1. 技能声明:告诉 LLM 这个角色掌握哪些技能(”published in top conferences” → 它知道如何做学术研究)
  2. 方法论注入:”rigorous methodology: always citing sources” → 约束输出必须有来源
  3. 行为约束:”distinguishing hype from substance” → 减少 LLM 生成空洞的正面描述
  4. 隐式工具使用引导:如果 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 负责:

  1. 分析所有 Task
  2. 决定将每个 Task 分配给哪个 Agent
  3. 协调执行顺序
  4. 综合各 Agent 的输出
  5. 质量把控和重新分配
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”的场景。但现实中,很多工作流不是”一组任务”能概括的:

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

7.6.4 @listen 装饰器

@listen(source_method)  # 监听另一个方法的完成事件
def handler(self, event):
    pass

@listen("route_name")   # 监听 router 返回的字符串
def handler(self, event):
    pass

7.6.5 @router 装饰器

@router(source_method)
def my_router(self, event):
    """返回字符串,决定触发哪个下游 @listen"""
    if condition_a:
        return "path_a"
    else:
        return "path_b"

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}

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 类似,但有几个特点:

  1. 基于 Pydantic:自带类型验证和序列化
  2. 通过 self.state 访问:不是通过函数参数传递
  3. 可变的:直接修改 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.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()

适用场景:

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          │
│                                                         │
└─────────────────────────────────────────────────────────┘

这种分层设计的好处:


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 当

选 tRPC-Agent 当

选 LangGraph 当

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()

可以看到:


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(),          # 实体知识图谱
)

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 优势

  1. 上手极快:定义角色 → 定义任务 → kickoff,三步搞定
  2. 自然语言驱动:任务描述用自然语言,不需要编程式定义状态流转
  3. 角色抽象强大:好的 Role/Goal/Backstory 能让 LLM 表现出惊人的”专业性”
  4. Flow 层补全了编排能力:2025 年加入 Flow 后,终于能做复杂工作流了
  5. 生态丰富:内置 Memory、Knowledge、Planning、Structured Output

7.12.2 劣势

  1. 确定性不如图引擎:角色扮演的输出有随机性,同样的输入可能产生不同质量的输出
  2. 调试困难:当 Agent “入戏太深”输出了不相关的内容,很难从系统层面诊断
  3. Token 消耗大:Role+Goal+Backstory+Context 每次都要发给 LLM,context window 压力大
  4. Fine-grained 控制弱:你很难精确控制 Agent 在特定条件下的行为——它更像”引导”而非”命令”
  5. Flow 还比较年轻:相比 LangGraph 的 Pregel,Flow 的状态管理和 Checkpoint 不够成熟

7.12.3 什么时候 CrewAI 比图引擎更好?

核心判断标准:你的任务是”目标导向”还是”流程导向”?

另一个判断维度:你信任 LLM 的判断力到什么程度?


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(任务执行单元)

关键设计决策:

与其他框架的核心差异:


7.15 思考题

  1. 设计题:为一个”AI 面试官”设计 Agent 三元组(Role/Goal/Backstory)。这个面试官需要:能问技术问题、能追问深入细节、能给出评价分数、对候选人保持友好但不降低标准。请写出至少 200 字的 Backstory,并解释你的每个设计选择为什么能影响 LLM 的行为。

  2. 架构选择:一个在线教育平台想用 AI 批改编程作业。流程是:学生提交代码 → 语法检查 → 运行测试 → 代码质量评估 → 生成反馈报告 → 如果分数低于 60 分给出改进建议。你会用 CrewAI 的 Sequential Process 还是 Flow + 多 Crew?给出理由并画出架构图。

  3. 对比分析:CrewAI 的 @router + @listen("route_name") vs LangGraph 的 add_conditional_edges vs tRPC-Agent 的 AddConditionalEdges。这三种条件分支机制在以下场景中各有什么优劣:(a) 简单二选一;(b) 多条件分支(>5 个去向);(c) 动态决定并行分支数量。

  4. 深度思考:CrewAI 的 “Role Play is All You Need” 哲学在什么条件下会失效?列出至少 3 个”角色扮演不够用,必须用图引擎精确控制”的场景,并解释为什么。

  5. 实践题:设计一个 Flow,实现以下工作流:用户输入一段需求描述 → 产品经理 Agent 细化需求 → 架构师 Agent 设计方案 → (并行)前端 Agent 实现UI + 后端 Agent 实现API → (汇聚后)测试 Agent 验证 → 如果有 bug 回到对应的开发 Agent 修复。请特别注意:如何用 and_ 实现汇聚?如何用 @router 实现”回到对应的开发 Agent”而非回到所有开发 Agent?


上一章:第六章 LangGraph Channel 与 Pregel 引擎精读

下一章:第八章 跨框架对比——状态管理哲学

返回目录:Agent 框架深度导读系列