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 搜尋尋找網址", tools=[firecrawl_search])
scraper = Agent(role="透過 Firecrawl 爬取網址", 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")
研究者的任務描述告訴它先搜尋、再爬取、最後回傳結構化的發現。一個 LLM 循環,多次工具呼叫。
反模式:將「摘要後傳送」設為兩個代理
❌ 兩個代理來讀取字串、摘要、然後發送 Slack 私訊:
summarizer = Agent(role="摘要者")
slack_messenger = Agent(role="Slack 發送者", apps=["slack"])
✅ 一個代理擁有連接器,並透過任務指示它先摘要再發送私訊:
slack_dm_agent = Agent(
role="Slack 回報者",
goal="發送 Slack 私訊,內容包含一段摘要加上完整的 Markdown 內文。",
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 以更快失敗
- 如果代理 consistently 達到 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]:
"""拒絕包含 PII 的輸出。"""
if contains_pii(result.raw):
return (False, "輸出包含 PII。請移除所有個人資訊後重試。")
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() 與 Crew.kickoff():
- 當每個步驟是不同的代理,且 Flow 控制排序時,使用
Agent.kickoff() - 當多個代理需要在單一步驟內協作處理相關任務時,使用
Crew.kickoff()
對話流程路由中的 Agent.kickoff()
在實驗性的對話流程中,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負責 session id、訊息歷史、路由、追蹤最終化與核准。 - 讓每個代理保持狹窄:一條路由、一個工具範圍、一項工作。
- 使用
append_agent_result(..., visibility="private")處理不應進入正式聊天歷史的暫存工作。 - 使用
append_assistant_message(reply)處理使用者可見的回應,以便下一輪擁有助理上下文。 - 不要建立一個擁有所有工具的「聊天代理」。先路由,然後為選定的路由呼叫一個聚焦的代理。
請參閱 Flow 生命週期的入門參考:skills/getting-started/references/conversational-flows.md。
5. 專家型 vs 通才型代理
注意: 在確定你確實需要多個代理後(請參閱第 0 節),再應用本節。如果你只需要一個代理,「專家 vs 通才」就不是問題——問題只在於如何設計那個代理。
當你需要多個代理時,偏好專家型。 一個專注做好一件事的代理,勝過一個能做好多件事但表現平平的代理。
何時使用專家型
- 任務需要深厚的領域知識
- 輸出品質比速度更重要
- 任務足夠複雜,能從專注的專業知識中受益
何時通才型可以接受
- 簡單任務且有明確指示
- 原型開發,之後再專業化
- 任務確實平均涵蓋多個領域
專家型設計模式
與其建立一個「內容寫作者」代理,不如建立:
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 伺服器,以解決這些技能未涵蓋的問題






