CrewAI Flow 入門:用狀態機把多個 Crew 串成可控流程

6 min read

當你的流程從「研究 + 寫報告」演變成「研究 → 產文 → 品質審核 → 通過就發布、不通過就重跑」,單一 Crew 就不夠了。

不是 Crew 不好,而是它本來就不是設計來處理「有條件分支」的情境。單一 Crew 的任務流程是線性的,而真實業務邏輯通常長這樣:「做完 A,判斷結果,決定要跑 B 還是跑 C」。

Flow 就是 CrewAI 用來處理這種情境的工具——它像一個總控台,負責協調多個 Crew 的執行順序和條件邏輯。

Flow 在解決什麼問題?

三個核心能力:

  • 管理跨步驟狀態:資料可以從第一個步驟一路傳到最後一個步驟,不用靠全域變數
  • 條件分支:根據某個條件決定走哪條路,而不是全部跑完
  • 多 Crew 協調:把幾個獨立的 Crew 組合成一條有邏輯的流程

最小可用的 Flow 範例

from pydantic import BaseModel
from crewai.flow.flow import Flow, start, listen, router
 
class BlogState(BaseModel):
    topic: str = ""
    draft: str = ""
    word_count: int = 0
    approved: bool = False
 
class BlogFlow(Flow[BlogState]):
 
    @start()
    def init_topic(self) -> str:
        self.state.topic = "CrewAI for beginners"
        return self.state.topic
 
    @listen(init_topic)
    def write_draft(self, topic: str) -> str:
        # 這裡可以呼叫一個 WritingCrew
        draft = f"Draft content about {topic}..."
        self.state.draft = draft
        self.state.word_count = len(draft.split())
        return draft
 
    @router(write_draft)
    def quality_gate(self) -> str:
        if self.state.word_count > 100:
            return "approved"
        return "retry"
 
    @listen("approved")
    def publish(self) -> str:
        return f"Published: {self.state.draft[:50]}..."
 
    @listen("retry")
    def revise(self) -> str:
        return "Draft too short, revising..."

@start() 是流程入口,@listen() 監聽前一步的完成,@router() 根據條件決定走哪條分支。

Flow[BlogState] 裡的 BlogState 是整個流程的共享狀態——每個方法都可以讀取和修改它,這樣資料就可以在步驟之間流動,不需要全域變數。

狀態管理:Flow 最重要的設計概念

Flow 的狀態是整個架構的核心。把它用 Pydantic BaseModel 定義,有幾個好處:

  • 有型別提示,IDE 能幫你抓錯
  • 所有方法共享同一份狀態,資料流動清楚
  • 容易序列化和儲存,方便 debug

不要用裸 dict 當狀態——你半年後回來看,會花很多時間猜每個 key 是什麼意思。

什麼時候才需要用 Flow?

不是所有情境都需要 Flow。判斷標準很簡單:

  • 流程超過 3 個步驟,且步驟之間有條件判斷 → 考慮 Flow
  • 需要多個 Crew 協作,且執行順序不是全部都跑 → 考慮 Flow
  • 有「重試」或「審核不通過回頭重做」的邏輯 → 一定要用 Flow

如果你只是「研究員找資料 + 編輯寫報告」,繼續用單一 Crew 就好,不需要 Flow。引入 Flow 的成本是增加程式碼複雜度,只有在你真的需要那些能力的時候才值得。

Flow 的 3 個設計原則

一個方法一件事:每個 @listen 方法只負責一個步驟。如果一個方法在做三件事,把它拆開——Flow 的可讀性和 debug 難度都會變好。

條件邏輯集中在 @router:不要把 if/else 散落在各個 @listen 方法裡。@router 就是設計來集中管理分支邏輯的,用它。

狀態用 BaseModel 定型:裸 dict 最省事,但你一個月後就不知道自己在幹嘛了。

常見問題

Q:Flow 和 Crew 可以混用嗎?
可以,這正是 Flow 的設計用途。在 @listen 方法裡建立並執行 Crew,把 Crew 的輸出存到 self.state,後續步驟再讀取。

Q:@router 的回傳值有什麼限制?
回傳值是字串,會對應到 @listen 裡監聽的字串。確保 router 的每個可能回傳值都有對應的 listener,不然流程會在那個分支卡死。

Q:Flow 可以暫停執行等待人工確認嗎?
可以,透過 pause_for_input 機制。這個功能適合需要「人工審核後才繼續」的業務流程,但需要搭配持久化狀態使用,設定稍微複雜一點。

下一步

流程能跑了,但你可能注意到一件事:Agent 每次執行都是全新的,不記得上次說了什麼。下一篇來解決這個問題:
👉 Memory 與 Knowledge