CrewAI 高级:输出格式、缓存与工作流编排
学习路径 : Day 6-8 | 难度 : ⭐⭐⭐⭐ | 前置知识 : 多 Agent 协作
📊 1. 输出格式控制
问题:如何让 Agent 输出结构化数据?
默认情况下,Agent 输出是自由格式的文本。但实际应用中,我们常常需要:
- JSON 格式(用于 API 交互)
- 特定结构(用于数据处理)
- 固定模板(用于报告生成)
解决方案:Prompt 工程
通过在任务描述中明确指定格式:
python
json_task = Task(
description="""
分析 AI 行业市场情况。
请严格按照以下 JSON 格式输出:
{
"title": "报告标题",
"summary": "摘要",
"key_points": ["要点1", "要点2", "要点3"],
"market_size": "市场规模",
"recommendations": ["建议1", "建议2"]
}
注意:
1. 必须是有效的 JSON 格式
2. 所有字段都要包含
3. 使用双引号
4. 不要添加其他文字
""",
expected_output="JSON格式的报告",
agent=analyst,
)
# 执行
result = crew.kickoff()
# 解析 JSON
import json
report_data = json.loads(result.raw)
print(report_data["title"])
print(report_data["key_points"])
关键技巧
- 提供 JSON 模板: 让 Agent 知道具体格式
- 强调规则: "必须是有效 JSON"、"不要添加其他文字"
- 错误处理: 使用 try-except 处理解析失败
python
try:
# 移除可能的 markdown 标记
cleaned = result.raw.strip()
if cleaned.startswith("```"):
cleaned = cleaned.split("```", 2)[1]
data = json.loads(cleaned)
print("解析成功!")
except json.JSONDecodeError as e:
print(f"解析失败: {e}")
💾 2. 任务缓存与上下文传递
任务缓存(cache)
作用: 避免重复调用 LLM,节省成本
python
crew = Crew(
agents=[researcher],
tasks=[task1, task2, task3],
cache=True, # ← 启用缓存
)
工作原理:
- 基于任务描述和输入生成哈希值
- 相同的任务不重复执行
- 缓存在内存中(程序重启后消失)
上下文传递(context)
作用: 让后续任务可以访问前面任务的输出
python
# 任务 1: 研究 Python
task1 = Task(
description="研究 Python 的特点",
agent=researcher,
)
# 任务 2: 研究 JavaScript(引用任务 1)
task2 = Task(
description="""
研究 JavaScript,并与 Python 对比。
请参考之前的 Python 研究结果。
""",
agent=researcher,
context=[task1], # ← 关键!传递 task1 的输出
)
# 任务 3: 研究 Go(引用任务 1 和 2)
task3 = Task(
description="""
研究 Go,并与 Python 和 JavaScript 对比。
请参考之前的研究结果。
""",
agent=researcher,
context=[task1, task2], # ← 传递多个任务的输出
)
数据流向
task1 输出: "Python 特点:简洁、易读、生态丰富"
↓ (通过 context 传递)
task2 看到:
"研究 JavaScript,并与 Python 对比。
请参考之前的 Python 研究结果。
Python 特点:简洁、易读、生态丰富"
↓
task2 输出: "JavaScript vs Python: ..."
↓ (通过 context 传递)
task3 看到: task1 和 task2 的所有输出
完整示例
python
from crewai import Agent, Task, Crew, Process
researcher = Agent(
role='技术研究员',
goal='研究编程语言特点',
llm="deepseek/deepseek-chat",
)
# 渐进式研究
task1 = Task(
description="研究 Python 的核心特点",
expected_output="Python 研究报告",
agent=researcher,
)
task2 = Task(
description="""
研究 JavaScript,并与 Python 对比。
请参考之前的 Python 研究结果。
""",
expected_output="JavaScript 及对比报告",
agent=researcher,
context=[task1],
)
task3 = Task(
description="""
研究 Go 语言,并与 Python 和 JavaScript 进行三方对比。
制作对比表格。
""",
expected_output="三种语言综合对比报告",
agent=researcher,
context=[task1, task2],
)
crew = Crew(
agents=[researcher],
tasks=[task1, task2, task3],
cache=True, # 启用缓存
)
result = crew.kickoff()
print(result) # 最终的综合对比报告
🔄 3. 工作流编排(Flows)
什么是工作流?
将多个 Crew 按顺序组合,形成复杂的多阶段流程。
研究 Crew → 写作 Crew → 编辑 Crew
↓ ↓ ↓
研究报告 文章初稿 最终文章
为什么需要工作流?
单个 Crew 的局限:
- 所有 Agent 在一个团队
- 适合简单任务
工作流的优势:
- 不同阶段用不同 Crew
- 更灵活,可以加入条件判断
- 适合复杂的多阶段任务
完整示例:内容生产流水线
python
from crewai import Agent, Task, Crew, Process
llm = "deepseek/deepseek-chat"
# ===== Crew 1: 研究团队 =====
researcher = Agent(
role='市场研究员',
goal='收集和分析市场数据',
llm=llm,
)
research_task = Task(
description="分析 AI 行业的最新发展趋势",
expected_output="市场研究报告",
agent=researcher,
)
research_crew = Crew(
agents=[researcher],
tasks=[research_task],
)
# ===== Crew 2: 写作团队 =====
writer = Agent(
role='技术作家',
goal='将研究报告转化为文章',
llm=llm,
)
writing_task = Task(
description="""
基于以下市场研究报告,撰写一篇面向创业者的文章。
市场研究报告:
{research_report}
要求:标题吸引人,语言通俗易懂。
""",
expected_output="面向创业者的文章",
agent=writer,
)
writing_crew = Crew(
agents=[writer],
tasks=[writing_task],
)
# ===== Crew 3: 编辑团队 =====
editor = Agent(
role='主编',
goal='优化文章质量',
llm=llm,
)
editing_task = Task(
description="""
审核并优化以下文章草稿。
文章草稿:
{draft_article}
请优化标题、结构和可读性。
""",
expected_output="优化后的最终文章",
agent=editor,
)
editing_crew = Crew(
agents=[editor],
tasks=[editing_task],
)
# ===== 执行工作流 =====
def run_workflow():
# 步骤 1: 研究
print("步骤 1/3: 市场研究")
research_result = research_crew.kickoff()
print("✅ 研究完成!")
# 步骤 2: 写作(传入研究报告)
print("\n步骤 2/3: 文章写作")
writing_result = writing_crew.kickoff(
inputs={"research_report": str(research_result)} # ← 传参
)
print("✅ 写作完成!")
# 步骤 3: 编辑(传入文章初稿)
print("\n步骤 3/3: 文章编辑")
editing_result = editing_crew.kickoff(
inputs={"draft_article": str(writing_result)} # ← 传参
)
print("✅ 编辑完成!")
return research_result, writing_result, editing_result
# 运行
research, writing, editing = run_workflow()
# 保存各阶段结果
with open("research.txt", "w") as f:
f.write(str(research))
with open("final_article.txt", "w") as f:
f.write(str(editing))
关键点解析
1. 为什么要用 str() 转换?
python
inputs={"research_report": str(research_result)}
因为 kickoff() 返回的是 CrewOutput 对象,但 inputs 只接受基本类型:
- ✅
str, int, float, bool, dict, list - ❌
CrewOutput对象
2. 参数如何传递?
python
# 传入参数
writing_crew.kickoff(
inputs={"research_report": "报告内容..."}
)
# Task 中使用占位符
writing_task = Task(
description="""
基于以下报告撰写文章:
{research_report} # ← 自动替换为实际内容
"""
)
3. 工作流模式
顺序模式(本示例):
python
result1 = crew1.kickoff()
result2 = crew2.kickoff(inputs={"data": result1})
result3 = crew3.kickoff(inputs={"data": result2})
条件模式:
python
result = crew1.kickoff()
if "正面" in str(result):
final = positive_crew.kickoff()
else:
final = negative_crew.kickoff()
并行模式:
python
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor() as executor:
future1 = executor.submit(crew1.kickoff)
future2 = executor.submit(crew2.kickoff)
result1 = future1.result()
result2 = future2.result()
💡 高级技巧总结
1. JSON 输出
python
# Prompt 中指定格式
task = Task(
description="""
请严格按照以下 JSON 格式输出:
{
"field1": "value1",
"field2": ["item1", "item2"]
}
"""
)
# 解析结果
import json
data = json.loads(result.raw)
2. 任务缓存
python
crew = Crew(
agents=[...],
tasks=[...],
cache=True, # 相同任务不重复执行
)
3. 上下文传递
python
task2 = Task(
description="基于之前的结果继续...",
context=[task1], # 传递 task1 输出
)
4. 工作流编排
python
# 阶段 1
result1 = crew1.kickoff()
# 阶段 2(传入阶段 1 结果)
result2 = crew2.kickoff(
inputs={"previous": str(result1)}
)
# 阶段 3(传入阶段 2 结果)
result3 = crew3.kickoff(
inputs={"previous": str(result2)}
)
🎓 实战建议
场景 1: 数据报告生成
python
# 工作流
数据收集 → 数据分析 → 报告生成 → 格式优化
# 使用
- cache=True(避免重复分析)
- context 参数(传递中间结果)
- JSON 输出(结构化数据)
场景 2: 内容创作
python
# 工作流
研究 → 写作 → 编辑 → 排版
# 使用
- 多个 Crew(不同阶段)
- inputs 传递(阶段间数据)
- 条件判断(根据研究结果决定写作方向)
场景 3: 代码开发
python
# 工作流
需求分析 → 架构设计 → 编码 → 测试 → 文档
# 使用
- Hierarchical 模式(项目经理协调)
- context 传递(设计文档传递给开发)
- cache(避免重复测试)
📝 总结
通过本文,你学会了:
- ✅ 使用 Prompt 工程控制 JSON 输出
- ✅ 任务缓存机制(cache=True)
- ✅ 上下文传递(context 参数)
- ✅ 工作流编排(多 Crew 组合)
- ✅ 阶段间数据传递(inputs 参数)