design-agent

design-agent

CrewAI代理设计与配置。用于创建、配置或调试CrewAI代理——选择角色/目标/背景故事、选择LLM、分配工具、调整max_iter/max_rpm/max_execution_time、启用规划/代码执行/委派、设置知识源、使用护栏,或在YAML与代码中配置代理。

31Star
11Fork
更新于 2026/6/16
SKILL.md
readonly只读
name
design-agent
description

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()——无需CrewTask的仪式。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风格循环,而是:

  1. 生成计划——一系列PlanStep,每个都有描述和可选的tool_to_use。存储为state.todos
  2. 通过StepExecutor执行每一步,在隔离的多轮LLM循环中(上限由max_step_iterations设置)。
  3. 通过PlannerObserver观察结果——每一步后:步骤成功了吗?剩余计划仍然有效吗?
  4. 根据代理的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:450observe_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)
  • [ ] 委派已禁用,除非确实需要

参考

有关特定主题的深入探讨,请参见:

  • 自定义工具——使用@tool装饰器和BaseTool子类构建自己的工具
  • 记忆与知识——记忆配置、知识源、嵌入器设置、作用域

相关技能:

  • getting-started——项目脚手架、选择正确的抽象、Flow架构
  • design-task——任务描述/expected_output最佳实践、护栏、结构化输出、依赖关系
  • ask-docs——查询实时CrewAI文档MCP服务器,解决这些技能未涵盖的问题