CrewAI代理设计与配置。用于创建、配置或调试CrewAI代理——选择角色/目标/背景故事、选择LLM、分配工具、调整max_iter/max_rpm/max_execution_time、启用规划/代码执行/委派、设置知识源、使用护栏,或在YAML与代码中配置代理。
CrewAI代理设计指南
如何设计具有合适角色、目标、背景故事、工具和配置的有效代理。
80/20法则
将80%的精力投入任务设计,20%投入代理设计。 设计良好的任务能提升即使是简单代理的表现。但即使是最好的代理也无法挽救模糊、范围不清的任务。先做好任务设计(参见design-task技能),再优化代理。
0. 你实际需要多少个代理?
默认使用一个代理。 只有当任务确实需要拆分时,才增加更多代理:
- 不同的工具或权限——例如,一个代理有Slack写入权限,另一个只能读取文档。
- LLM必须清晰切换的不同角色——写作者的语气不同于研究者的语气。
- 不同的LLM——机械步骤用廉价模型,综合用更强模型。
- 不同的护栏或输出模式——分离的代理使每个阶段的契约明确。
不要仅仅因为工作流有多个步骤就添加代理。 单个代理可以:
- 在一次kickoff中顺序调用多个工具(搜索→抓取→总结是一个代理的循环)。
- 在一次响应中生成结构化的多部分输出。
- 通过自身的工具使用循环进行迭代,无需你将其编排为多个代理。
成本计算: 每增加一个代理至少多一次LLM kickoff和一次上下文交接。将线性的、单一角色的工作拆分为多个代理会成倍增加token成本并增加脆弱性,而质量提升微乎其微。
反模式:将顺序机械步骤作为独立代理
❌ 三个代理完成本应是一个研究员的工作:
source_finder = Agent(role="通过Firecrawl搜索查找URL", tools=[firecrawl_search])
scraper = Agent(role="通过Firecrawl抓取URL", tools=[firecrawl_scrape])
writer = Agent(role="撰写报告", ...)
✅ 一个研究员完成收集循环;一个写作者综合——两个代理因为角色和LLM确实不同:
researcher = Agent(role="网络研究员", tools=[firecrawl_search, firecrawl_scrape], llm="anthropic/claude-haiku-4-5")
writer = Agent(role="技术报告撰写者", llm="anthropic/claude-sonnet-4-6")
研究员的task描述告诉它搜索、抓取,然后返回结构化发现。一个LLM循环,多次工具调用。
反模式:"总结然后发送"作为两个代理
❌ 两个代理读取字符串、总结并发送Slack私信:
summarizer = Agent(role="总结者")
slack_messenger = Agent(role="Slack发送者", apps=["slack"])
✅ 一个代理带有连接器和任务,告诉它先总结再发送私信:
slack_dm_agent = Agent(
role="Slack报告者",
goal="发送包含一段摘要和完整markdown正文的Slack私信。",
apps=["slack"],
)
# 任务:"阅读下面的报告。在顶部写2-3句执行摘要。
# 向{recipient_email}发送私信,包含摘要和完整正文。"
启发式规则
如果两个"代理"共享相同的角色、相同的工具集和相同的LLM,那么它们就是一个代理,只是任务描述更长。
一旦你决定"一个代理就够了"
在Flow方法中直接使用Agent.kickoff()——无需Crew或Task的仪式。Flow负责排序和状态;每一步都是单个代理的kickoff。参见下面的第4节——Agent.kickoff()——直接代理执行以了解完整模式,以及上游文档https://docs.crewai.com/en/concepts/agents#direct-agent-interaction-with-kickoff。
快速示例:
@listen(previous_step)
def my_step(self):
agent = Agent(role="…", goal="…", backstory="…", tools=[...])
result = agent.kickoff(
messages=f"使用上一步的输出:{self.state.prior_field}",
response_format=MyPydanticModel, # 可选
)
self.state.my_field = result.pydantic # 或 result.raw
仅当某一步确实受益于多代理协作(委派、分层管理、并行专家输入一个综合)时,才使用Crew.kickoff()。对于"一个代理做一件事",Flow监听器中的Agent.kickoff()是正确的原语。
只有在确定多代理合理后,再继续阅读如何设计每个代理。
1. 角色-目标-背景故事框架
每个代理需要三样东西:它是谁、它想要什么、以及为什么它合格。
角色——代理是谁
角色定义了代理的专业领域。要具体,不要泛泛。
| 差 | 好 |
|---|---|
Researcher |
Senior Data Researcher specializing in {topic} |
Writer |
Technical Blog Writer for developer audiences |
Analyst |
Financial Risk Analyst with regulatory compliance expertise |
角色直接影响LLM的推理方式。"高级数据研究员"与"研究助理"即使任务相同,也会产生不同的输出。
目标——代理想要什么
目标是代理的个人目标。它应该以结果为导向,并包含质量标准。
| 差 | 好 |
|---|---|
Do research |
Uncover cutting-edge developments in {topic} and identify the top 5 trends with supporting evidence |
Write content |
Produce publication-ready technical articles that explain complex topics clearly for non-technical readers |
Analyze data |
Deliver actionable risk assessments with confidence levels and recommended mitigations |
背景故事——为什么代理合格
背景故事确立了专业知识、经验、价值观和工作风格。它是代理的"个性提示"。
backstory: >
你是一位在AI/ML领域拥有15年经验的资深研究员。
你以能够找到晦涩但相关的论文,
并将复杂发现综合为清晰、可操作的见解而闻名。
你总是引用来源并明确标注不确定性。
背景故事中应包含的内容:
- 经验年限/深度
- 特定领域知识
- 工作风格和价值观(例如,“总是引用来源”、“偏好简洁输出”)
- 代理对自身要求的质量标准
不应包含的内容:
- 实现细节(工具、模型、配置)
- 任务特定指令(这些应放在任务描述中)
- 不影响输出质量的任意个性特征
2. 代理配置参考
基本参数
Agent(
role="...", # 必需:代理的专业领域
goal="...", # 必需:代理的目标
backstory="...", # 必需:上下文和个性
llm="openai/gpt-4o", # 可选:默认为OPENAI_MODEL_NAME环境变量或"gpt-4"
tools=[...], # 可选:工具实例列表
)
执行控制
Agent(
...,
max_iter=25, # 每个任务的最大推理迭代次数(默认:25)
max_execution_time=300, # 超时时间(秒)(默认:None——无限制)
max_rpm=10, # 速率限制:每分钟最大API调用次数(默认:None)
max_retry_limit=2, # 错误重试次数(默认:2)
verbose=True, # 显示详细执行日志(默认:False)
)
调整max_iter:
- 默认25很宽松——大多数任务在3-8次迭代内完成
- 对于定义良好的任务,降低到10-15以更快失败
- 如果代理持续达到max_iter,说明任务太模糊(修复任务,而不是限制)
工具配置
from crewai_tools import SerperDevTool, ScrapeWebsiteTool, FileReadTool
Agent(
...,
tools=[SerperDevTool(), ScrapeWebsiteTool()], # 代理级工具
)
关键规则:
- 没有工具的代理在被要求搜索、获取或读取文件时会编造数据——始终为需要外部数据的任务提供工具
- 优先选择更少、更专注的工具,而不是很多工具——太多工具会让代理困惑
- 工具也可以在任务级别分配,以实现任务特定的访问(参见
design-task技能) - 代理级工具对该代理执行的所有任务可用;任务级工具会覆盖该特定任务
LLM选择
Agent(
...,
llm="openai/gpt-4o", # 主要推理模型
function_calling_llm="openai/gpt-4o-mini", # 仅用于工具调用的更便宜模型
)
使用function_calling_llm节省成本:主llm处理推理,而更便宜的模型处理工具调用机制。
协作
Agent(
...,
allow_delegation=False, # 默认:False——代理独立工作
)
仅在以下情况下设置allow_delegation=True:
- 代理是与其他专业代理一起的crew的一部分
- 任务确实受益于代理移交子任务
- 你使用分层流程,其中管理者进行委派
警告: 没有明确任务边界的委派会导致无限循环或浪费迭代。
规划(计划与执行模式)
当代理上设置了PlanningConfig时,Agent.kickoff()(和Agent.execute_task())会通过新的crewai.experimental.AgentExecutor路由。代理不再使用单一的ReAct风格循环,而是:
- 生成计划——一系列
PlanStep,每个都有描述和可选的tool_to_use。存储为state.todos。 - 通过
StepExecutor执行每一步,在隔离的多轮LLM循环中(上限由max_step_iterations设置)。 - 通过
PlannerObserver观察结果——每一步后:步骤成功了吗?剩余计划仍然有效吗? - 根据代理的
reasoning_effort设置路由下一步动作(见下文)。
存在PlanningConfig即启用该模式。要禁用:不传递,或设置planning=False。
from crewai import Agent
from crewai.agent.planning_config import PlanningConfig
agent = Agent(
role="…",
goal="…",
backstory="…",
tools=[...],
planning_config=PlanningConfig(reasoning_effort="medium"), # 最常见
)
reasoning_effort——选择一个
| 级别 | 每一步后规划器... | 何时选择 |
|---|---|---|
"low" |
观察(验证成功),标记待办完成,继续。不重新计划,不优化。 | 你想要计划可见性(待办、观察),但信任代理线性执行。最快。 |
"medium"(默认) |
观察;仅在失败时重新计划。成功的步骤继续。 | 代理的工具可能失败(网络、执行、抓取),你希望优雅恢复,而不为每次成功支付优化成本。沙箱编码、研究和其他工具密集型循环的正确默认值。 |
"high" |
观察,然后通过decide_next_action路由,该路由可以在每一步后触发早期目标达成、完全重新计划或轻量级优化。 |
任务根据中间发现改变形状,或者你需要最大适应性。每次运行LLM调用最多。 |
来源:crewai/experimental/agent_executor.py:450(observe_step_result路由器)和crewai/agent/planning_config.py。
其他PlanningConfig参数
PlanningConfig(
reasoning_effort="medium",
max_steps=20, # 计划步骤上限(默认20)
max_replans=3, # 最终确定前最大完全重新计划次数(默认3)
max_attempts=None, # 计划生成期间的规划优化尝试次数
max_step_iterations=15, # 每步StepExecutor的最大LLM轮次(默认15)
step_timeout=None, # 每步的挂钟秒数;None = 无上限
system_prompt=None, # 自定义规划系统提示(None则使用默认)
plan_prompt=None, # 自定义初始计划提示;占位符:{description}, {expected_output}, {tools}, {max_steps}
refine_prompt=None, # 自定义优化提示
llm=None, # 用于规划的独立LLM(否则使用agent.llm)
)
使用llm="anthropic/claude-haiku-4-5"(便宜)作为规划器,同时保持agent.llm="anthropic/claude-opus-4-7"(强)用于执行——常见的成本优化。
何时启用
- 启用用于自主循环,代理自行选择步骤并且你需要故障恢复(例如,编码代理编写→运行→修补;研究代理搜索→抓取→修订)。
- 跳过用于单工具、单一目的的调用(例如,“总结这个字符串”、“发送这条Slack私信”)——观察开销不值得。
成本形状
每一步都有一个PlannerObserver LLM调用(每步约额外1次调用)。在"medium"下,失败的步骤会增加一次重新计划调用。在"high"下,每一步还会增加一次decide_next_action调用。对于一个N步计划,大致预期:
low:N次执行 + N次观察 = 2N次调用medium:2N + (失败次数 × 1次重新计划)high:约3N + 重新计划/优化
大规模时成本显著——在默认对所有任务使用high之前先测量。
自定义plan_prompt
如果你提供plan_prompt,请包含规划器模板期望的占位符:{description}、{expected_output}、{tools}、{max_steps}。规划器LLM会收到这些插值。保持自定义提示专注于项目特定规则;让description/tools(自动注入)携带动态内容。
代码执行
Agent(
...,
allow_code_execution=True, # 启用代码执行(默认:False)
code_execution_mode="safe", # "safe"(Docker)或"unsafe"(直接)——默认:"safe"
)
"safe"需要安装并运行Docker——在容器中执行"unsafe"直接在主机上运行代码——仅在受控环境中使用
上下文窗口管理
Agent(
...,
respect_context_window=True, # 自动总结以保持在限制内(默认:True)
)
当为True时,如果接近LLM的token限制,代理会自动总结先前的上下文。当为False时,溢出时执行停止并报错。
日期注入
Agent(
...,
inject_date=True, # 将当前日期添加到任务上下文(默认:False)
date_format="%Y-%m-%d", # 日期格式(默认:"%Y-%m-%d")
)
为时间敏感任务(研究、新闻分析、日程安排)启用。
代理护栏
def validate_no_pii(result) -> tuple[bool, Any]:
"""拒绝包含个人身份信息的输出。"""
if contains_pii(result.raw):
return (False, "输出包含个人身份信息。请删除所有个人信息并重试。")
return (True, result)
Agent(
...,
guardrail=validate_no_pii,
guardrail_max_retries=3, # 默认:3
)
代理护栏验证代理产生的每个输出。代理在失败时重试,最多guardrail_max_retries次。
知识源
from crewai.knowledge.source.text_file_knowledge_source import TextFileKnowledgeSource
Agent(
...,
knowledge_sources=[
TextFileKnowledgeSource(file_paths=["company_handbook.txt"]),
],
embedder={
"provider": "openai",
"config": {"model": "text-embedding-3-small"},
},
)
知识源通过RAG为代理提供领域特定数据。当代理需要引用大型文档、政策或数据集时使用。
3. YAML配置(推荐)
在agents.yaml中定义代理,实现配置与代码的清晰分离:
researcher:
role: >
{topic} 高级数据研究员
goal: >
发现{topic}的前沿发展,
并提供支持证据和来源引用
backstory: >
你是一位拥有15年经验的资深研究员。
以找到晦涩但相关的来源,
并将复杂发现综合为清晰见解而闻名。
你总是引用来源并标注不确定性。
# 可选覆盖(根据需要取消注释):
# llm: openai/gpt-4o
# max_iter: 15
# max_rpm: 10
# allow_delegation: false
# verbose: true
然后在crew.py中连接:
@CrewBase
class MyCrew:
agents_config = "config/agents.yaml"
tasks_config = "config/tasks.yaml"
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config["researcher"],
tools=[SerperDevTool()],
)
关键: 方法名(def researcher)必须与YAML键(researcher:)匹配。不匹配会导致KeyError。
4. Agent.kickoff()——直接代理执行
当你需要一个带有工具和推理能力的代理,而不需要Crew的开销时,使用Agent.kickoff()。这是Flow中最常见的模式。
基本用法
from crewai import Agent
from crewai_tools import SerperDevTool
researcher = Agent(
role="高级研究分析师",
goal="查找带有来源引用的全面、事实性信息",
backstory="以彻底、基于证据的分析而闻名的专家研究员。",
tools=[SerperDevTool()],
llm="openai/gpt-4o",
)
# 传递字符串提示——代理推理、使用工具并返回结果
result = researcher.kickoff("量子计算的最新发展是什么?")
print(result.raw) # str——代理的完整响应
print(result.usage_metrics) # token使用统计
结构化输出
from pydantic import BaseModel
class ResearchFindings(BaseModel):
key_trends: list[str]
sources: list[str]
confidence: float
result = researcher.kickoff(
"研究最新的AI代理框架",
response_format=ResearchFindings,
)
# 通过.pydantic访问(不是直接——Agent.kickoff包装了结果)
print(result.pydantic.key_trends) # list[str]
print(result.pydantic.confidence) # float
print(result.raw) # 原始字符串版本
注意:
Agent.kickoff()返回LiteAgentOutput——通过result.pydantic访问结构化输出。这与LLM.call()不同,后者直接返回Pydantic对象。
文件输入
result = researcher.kickoff(
"分析此文档并总结关键发现",
input_files={"document": FileInput(path="report.pdf")},
)
异步变体
result = await researcher.kickoff_async(
"研究量子计算突破",
response_format=ResearchFindings,
)
Flow中的Agent.kickoff()(推荐模式)
最强大的模式是在Flow内部编排多个Agent.kickoff()调用。Flow处理状态和排序;每个代理处理其特定步骤:
from crewai import Agent
from crewai.flow.flow import Flow, listen, start
from crewai_tools import SerperDevTool, ScrapeWebsiteTool
from pydantic import BaseModel
class ResearchState(BaseModel):
topic: str = ""
research: str = ""
analysis: str = ""
report: str = ""
class ResearchFlow(Flow[ResearchState]):
@start()
def gather_data(self):
researcher = Agent(
role="高级研究员",
goal="查找带有来源的全面数据",
backstory="擅长查找和验证信息的专家。",
tools=[SerperDevTool(), ScrapeWebsiteTool()],
)
result = researcher.kickoff(f"研究:{self.state.topic}")
self.state.research = result.raw
@listen(gather_data)
def analyze(self):
analyst = Agent(
role="数据分析师",
goal="从原始研究中提取可操作见解",
backstory="擅长模式识别和综合。",
)
result = analyst.kickoff(
f"分析此研究并提取关键见解:\n\n{self.state.research}"
)
self.state.analysis = result.raw
@listen(analyze)
def write_report(self):
writer = Agent(
role="报告撰写者",
goal="创建清晰、结构良好的报告",
backstory="使复杂主题易于理解的技术写作者。",
)
result = writer.kickoff(
f"根据此分析撰写综合报告:\n\n{self.state.analysis}"
)
self.state.report = result.raw
flow = ResearchFlow()
flow.kickoff(inputs={"topic": "AI代理"})
print(flow.state.report)
何时使用Agent.kickoff() vs Crew.kickoff():
- 当每一步是一个独立的代理且Flow控制排序时,使用
Agent.kickoff() - 当多个代理需要在单一步骤内协作处理相关任务时,使用
Crew.kickoff()
对话Flow路由中的Agent.kickoff()
在实验性对话Flow中,Flow拥有聊天生命周期和路由选择。应在路由处理程序内部调用代理,用于有边界的工具支持工作:研究、文档查找、账户操作、分类、草稿或升级准备。
from crewai import Agent, Flow
from crewai.flow import listen
from crewai.experimental.conversational import ConversationState
class SupportFlow(Flow[ConversationState]):
conversational = True
def research_agent(self) -> Agent:
return Agent(
role="支持研究专家",
goal="为用户当前问题查找带有来源的准确信息。",
backstory="你精确、以证据为导向,并明确标注不确定性。",
tools=[...],
)
@listen("RESEARCH")
def handle_research(self) -> str:
"""新鲜研究、当前查找和基于来源的综合。"""
result = self.research_agent().kickoff(self.state.current_user_message)
self.append_agent_result("research_agent", result, visibility="private")
reply = result.raw
self.append_assistant_message(reply)
return reply
设计含义:
- 保持对话
Flow负责会话ID、消息历史、路由、跟踪最终化和审批。 - 保持每个代理狭窄:一个路由、一个工具集、一项工作。
- 使用
append_agent_result(..., visibility="private")处理不应进入规范聊天历史的草稿工作。 - 使用
append_assistant_message(reply)处理用户可见的答案,以便下一轮拥有助手上下文。 - 不要创建拥有所有工具的"聊天代理"。先路由,然后为所选路由调用专注的代理。
参见入门参考中的Flow生命周期:skills/getting-started/references/conversational-flows.md。
5. 专家型代理与通才型代理
注意: 在确定你确实需要多个代理后(参见第0节),再应用本节。如果你只需要一个代理,"专家型与通才型"不是问题——问题只是如何设计那个代理。
当你确实需要多个代理时,优先选择专家型。 一个做好一件事的代理优于一个能接受地做很多事的代理。
何时使用专家型
- 任务需要深厚的领域知识
- 输出质量比速度更重要
- 任务足够复杂,受益于专注的专业知识
何时通才型可接受
- 具有清晰指令的简单任务
- 原型设计,后续会专业化
- 真正平等跨越多个领域的任务
专家型设计模式
不要使用一个"内容写作者"代理,而是创建:
technical_writer——深度技术准确性,代码示例copywriter——有说服力、面向受众的营销文案editor——语法、一致性、风格指南执行
每个专家都有狭窄的角色、特定目标和强化其专业知识的背景故事。
6. 代理交互模式
顺序(默认)
代理一个接一个工作。每个代理接收先前代理的输出作为上下文。
研究员 → 写作者 → 编辑
最适合:每一步建立在上一步基础上的线性流水线。
分层
管理者代理进行委派和验证。任务分配是动态的。
Crew(
agents=[researcher, writer, editor],
tasks=[research_task, writing_task, editing_task],
process=Process.hierarchical,
manager_llm="openai/gpt-4o",
)
最适合:任务分配取决于中间结果的复杂工作流。
代理间委派
当allow_delegation=True时,代理可以向另一个crew代理请求帮助:
lead_researcher = Agent(
role="首席研究员",
goal="协调研究工作",
backstory="...",
allow_delegation=True, # 可以委派给crew中的其他代理
)
代理会自动发现其他crew成员并根据需要委派子任务。
7. 常见代理设计错误
| 错误 | 影响 | 修复 |
|---|---|---|
| 泛泛的角色如"助手" | 代理产生不聚焦、浅薄的输出 | 使用特定专业知识:"高级财务分析师" |
| 数据收集任务没有工具 | 代理编造数据而不是搜索 | 当任务需要外部信息时始终添加工具 |
| 太多工具(10+) | 代理在选择工具时感到困惑 | 每个代理限制在3-5个相关工具 |
| 背景故事充满任务指令 | 代理混淆个性与任务执行 | 背景故事只关于代理是谁;任务细节放在任务中 |
默认allow_delegation=True |
代理浪费迭代在琐碎委派上 | 仅在委派确实有帮助时启用 |
| 简单任务max_iter太高 | 代理在模糊任务上不必要地循环 | 降低max_iter;改为修复任务描述 |
| 关键输出没有护栏 | 不良输出未经检查通过 | 为输入生产系统的输出添加护栏 |
| 使用昂贵LLM进行工具调用 | 机械操作的不必要成本 | 将function_calling_llm设置为更便宜的模型 |
8. 代理设计检查清单
在部署代理之前,验证:
- [ ] 角色是具体且领域专注的(不是"助手"或"帮手")
- [ ] 目标包括期望结果和质量标准
- [ ] 背景故事确立了专业知识和工作风格
- [ ] 工具已为任何需要外部数据的任务分配
- [ ] 没有多余工具——每个代理最多3-5个
- [ ] max_iter已根据预期任务复杂度调整(简单任务10-15,复杂任务20-25)
- [ ] max_execution_time已为生产代理设置以防止挂起
- [ ] 护栏已为关键输出配置
- [ ] LLM适合任务复杂度(不要为分类使用GPT-4)
- [ ] 委派已禁用,除非确实需要
参考
有关特定主题的深入探讨,请参见:
相关技能:
- getting-started——项目脚手架、选择正确的抽象、Flow架构
- design-task——任务描述/expected_output最佳实践、护栏、结构化输出、依赖关系
- ask-docs——查询实时CrewAI文档MCP服务器,解决这些技能未涵盖的问题






