mcpField
B
build-flow

build-flow

github.com/crewaiincエージェントワークフロー48748 回インストール3 週間前に更新
リポジトリ

build-flow について

CrewAI Flowプロジェクトで、ステップの接続、型付き状態、永続化、crewやagentの呼び出しを実装するための実践ガイドです。現行APIを対象とし、CrewAI 1.15.22および1.15.23で検証したバージョン依存の注意点も扱います。

主な機能

  1. 01Flowプロジェクトの作成と実行
  2. 02型付き状態と辞書形式の状態の解説
  3. 03start、listen、routerの接続方法
  4. 04永続化と状態復元の説明
  5. 05Flow内からcrewやagentを呼び出す方法

活用シーン

  1. 01CrewAI CLIを使ったFlowの作成
  2. 02状態検証やルーティング問題の調査
  3. 03Flow実行の永続化と再開
  4. 04同期または非同期ステップからのcrew呼び出し

MCP 対応

非同期FlowメソッドでMCPサーバーに接続するには、`crewai.mcp`の`MCPServerHTTP`を使うよう説明しています。この状況では、`mcps`の文字列参照からツールが解決されない場合があります。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/crewaiinc/skills --skill build-flow

インストール後、Claude Code で/build-flowを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
conversational-flows.md
persistence-and-checkpoints.md
routing-patterns.md

詳細

CrewAI Flow を構築する

正しく接続し、型付きの状態を保持し、永続化して再開でき、クルーやエージェントを呼び出せる Flow を、現行 API で記述する方法です。

2026-10-01 に crewai 1.15.22 および 1.15.23 で検証済みです。 2026-10-01 に CrewAI AMP と実際の LLM を使ってライブテスト済みです。 まず crewai version を実行してください。メジャーまたはマイナーバージョンが 1.15 と異なる場合は、バージョン依存の項目を信頼する前に、ask-docs スキルで再検証してください。


1. Flow プロジェクトをスキャフォールドして実行する

crewai create flow lead-router      # the package is still lead_router
cd lead_router
crewai install                      # uv sync; writes uv.lock
crewai run                          # runs the `kickoff` script in pyproject.toml
crewai flow plot                    # runs the `plot` script

スキャフォールドで生成される内容:

ファイル内容
pyproject.toml[project.scripts] の kickoff、run_crew(どちらも <pkg>.main:kickoff)、plot、run_with_trigger;[tool.crewai] type = "flow"
src/<pkg>/main.pyFlow[ContentState] のサブクラスと、kickoff()、plot() 関数;from crewai.flow import Flow, listen, start
src/<pkg>/crews/content_crew/config/agents.yaml と config/tasks.yaml を持つ従来型の @CrewBase クルー。エージェントには llm が設定されていないため、デフォルトの OpenAI モデルが使われます
.envOPENAI_API_KEY=YOUR_API_KEY — プレースホルダーです。この値で実行すると 401 ... Incorrect API key provided エラーになります。シェルでエクスポートした OPENAI_API_KEY は .env より優先されます。有効なキーをエクスポートすると、変更していないスキャフォールドの crewai run は gpt-4.1-mini で 3 つのエージェントを実行し、output/post.md に書き込みました
AGENTS.md、CLAUDE.md、GEMINI.md(1.15.23 以降は CURSOR.md も)、README.md、tests/コーディングアシスタント向けの指示と、空のテストフォルダー
.git/crewai create は git init を実行します(コミットなし、リモートなし)

別のプロバイダーを使うには、agents.yaml の各エージェントに llm: anthropic/claude-haiku-4-5(例)を設定し、uv add "crewai[anthropic]" を実行して、.env に ANTHROPIC_API_KEY=<your-key> を記述します。シェルで何もエクスポートしていない状態でも、crewai run は .env からキーを読み取りました。

スキャフォールドから読み取れるルール:

  • [tool.crewai] type = "flow" はそのままにしてください。CLI はこれを使って、crewai run が uv run kickoff を意味するかどうかを判断します。
  • テンプレートの plan_content() は、トリガーペイロードがない場合、self.state.topic を "AI Agents" で上書きします。そのため、kickoff(inputs={"topic": ...})(デプロイされた kickoff の inputs も同様)は無視されます。入力に依存する前に、この代入を削除してください。
  • テンプレートの plot() はデフォルトの show=True で plot() を呼び出すため、crewai flow plot はブラウザーを開きます。ヘッドレス環境で使うには、plot("<name>.html", show=False) に変更してください。
  • テンプレートと同様に、絶対パッケージパスでインポートしてください(from lead_router.crews.content_crew.content_crew import ContentCrew)。
  • main.py の kickoff() は None を返す必要があります。生成されたコンソールスクリプトは sys.exit(kickoff()) を実行するため、Flow の結果(たとえば dict)を返すと、プロセスはステータス 1 で終了し、値を stderr に出力します。また、crewai run は「An error occurred while running the flow」と出力します。ただし、crewai run 自体は終了コード 0 のままなので、終了コードだけを確認する CI ステップは成功してしまいます。
  • Python Flow では crewai run --inputs '{...}' は拒否されます。表示されるエラーは Error: --inputs requires a declarative flow definition ([tool.crewai].definition) or --definition です。コード内で入力を渡す(flow.kickoff(inputs=...))か、kickoff() で sys.argv から読み取り、uv run kickoff '<json>' を呼び出してください。
  • crewai flow kickoff は引き続き動作しますが、「deprecated. Use 'crewai run' instead.」と表示されます。
  • プレースホルダーまたは無効なキーを使うと Flow は失敗しますが、crewai run は終了コード 0 のままです(An error occurred while running the flow: Command '['uv', 'run', 'kickoff']' returned non-zero exit status 1. と出力されます)。終了コードではなく、この行があるかを確認してください。

2. 状態:構造化または辞書

構造化(推奨)非構造化
宣言class MyFlow(Flow[MyState]) と MyState(BaseModel)class MyFlow(Flow)
アクセスself.state.topicself.state["topic"]
id自動的に追加されます(self.state.id、UUID文字列)self.state["id"]
検証kickoff(inputs=...) の値は型チェックされますなし
from pydantic import BaseModel
from crewai.flow import Flow, listen, start


class ReportState(BaseModel):
    topic: str = ""          # give every field a default
    draft: str = ""
    notes: list[str] = []


class ReportFlow(Flow[ReportState]):
    @start()
    def plan(self):
        self.state.notes.append(f"planning {self.state.topic}")
        return self.state.topic.upper()

    @listen(plan)
    def write(self, plan_output):          # receives plan()'s return value
        self.state.draft = f"Draft about {plan_output}"
        return self.state.draft


flow = ReportFlow()
result = flow.kickoff(inputs={"topic": "bees"})   # inputs set state BEFORE @start runs
print(result)           # "Draft about BEES" - kickoff returns the last method's output
print(flow.state.id)    # UUID string, generated per run

状態のルール(それぞれ、実際に起こりうる障害を防ぎます):

ルール違反した場合に起こること
モデルに id を宣言しないでください。Flow[MyState] が追加します宣言された id のデフォルト値は、生成されたUUIDで暗黙に置き換えられます
initial_state = MyState ではなく、Flow[MyState] を使います通常の BaseModel を使った initial_state:フローの状態モデルに「id」フィールドが必要です、という ValidationError ... Flow state model must have an 'id' field
すべてのフィールドにデフォルト値を指定します必須フィールドがある場合:kickoff の前、フローのインスタンス化時に ValidationError が発生します
構造化状態では属性アクセスを使いますself.state["topic"]:TypeError: 'StateWithId' object is not subscriptable
inputs のキーはモデルのフィールド名と完全に一致させます構造化状態では、不明なキーは暗黙に破棄されます
正しい型の inputs を渡しますinputs={"topic": 5}:ValueError: Invalid inputs for structured state
self.state.id を手動で設定しないでくださいこれは永続化キーです。restore_from_state_id を使ってください(セクション6)

デプロイ済みフロー(2026-10-01にCrewAI AMPでテスト済み)では、GET /inputs は状態モデルで宣言されたフィールドを一覧表示します(生成された id は一覧に含まれません。モデル自体が id を宣言している場合は、それも一覧に含まれます)。また、POST /kickoff の inputs オブジェクトは、kickoff(inputs=...) とまったく同じように状態に適用されます。inputs ラッパーのない本文({"text": "..."})も受け付けられ(200 と kickoff_id が返され)、デフォルトの状態で実行は成功しましたが、その値はフローに渡りませんでした。実行時の state.id は kickoff_id と同じでした。呼び出しパターンについては、call-deployed-crew スキルを参照してください。


3. 配線:@start、@listen、@router

from typing import Literal
from pydantic import BaseModel
from crewai.flow import Flow, listen, router, start


class TicketState(BaseModel):
    text: str = ""
    queue: str = ""


class TicketFlow(Flow[TicketState]):
    @start()
    def receive(self):
        return self.state.text

    @router(receive)
    def triage(self) -> Literal["urgent", "normal"]:   # labels, not method names
        return "urgent" if "outage" in self.state.text else "normal"

    @listen("urgent")
    def page_oncall(self):
        self.state.queue = "oncall"

    @listen("normal")
    def file_ticket(self):
        self.state.queue = "backlog"


flow = TicketFlow()
flow.kickoff(inputs={"text": "outage in eu-west"})
print(flow.state.queue)   # oncall
ルール理由
ルーターのラベルを、それをリッスンするメソッドの名前と同じにしてはいけません@listen("go") def go はインスタンス化時に次のエラーになります:Invalid flow definition for ...: methods.go.listen listen condition 'go' references the handler name 'go'. A listener triggered by its own completion creates an infinite loop. ラベルとメソッド名は同じ名前空間を共有します
どのメソッド名とも一致しないラベルを選びますラベルはイベント名であり、すべてのメソッド名も同様です。ルーターが自身をトリガーするメソッド名を返すと、その分岐が再実行され、RecursionError: Method 'r' has been called 100 times in this flow execution が発生します
ルーターが返す可能性のあるすべてのラベルにリスナーがあることを確認します一致するラベルがないと、フローは何も通知せずに終了し、kickoff() はラベル文字列そのものを返します
Literal[...] の戻り値型または @router(x, emit=[...]) でラベルを宣言しますplot() のエッジと静的チェックにのみ使われます。emit= は実行時には強制されません(emit に含まれないラベルを返すルーターも実行されました)
@listen("method_name") よりも @listen(method_ref) のほうが安全です文字列のタイプミスは、どのイベントからも発行されないイベントになるだけです

4. or_ と and_

from crewai.flow import Flow, and_, listen, or_, start

calls = []


class FanIn(Flow):
    @start()
    def fetch_a(self):
        return "A"

    @start()
    def fetch_b(self):
        return "B"

    @listen(or_(fetch_a, fetch_b))
    def first_result(self, value):
        calls.append(("or", value))

    @listen(and_(fetch_a, fetch_b))
    def all_results(self, value):
        calls.append(("and", value))


FanIn().kickoff()
print(calls)   # e.g. [('or', 'A'), ('and', 'B')] - the start methods run concurrently, so the values (and order) can vary
  • or_ はキックオフごとに一度だけ、最初に完了した条件で実行されます。2つ目の条件が完了しても再実行されないため、「処理済み」フラグは必要ありません。
  • ルーターがいずれかのラベルを再び出力すると、or_ は再度有効になります。これにより、改訂ループが機能します(@listen(or_(write_draft, "needs_revision")) は最初のドラフトで実行され、その後は needs_revision のたびに実行されます。routing-patterns を参照してください)。
  • レビューステップと同じラベルを、別の改訂ステップでリッスンしないでください。2つの @listen("needs_revision") メソッドは、そのラベルで並行実行されるため、レビューでは改訂前のドラフトが再確認されます(1.15.22 および 1.15.23 で検証済み。3回のレビューすべてで v1 が確認されました)。改訂ステップを新しいラベルを返すルーターにし(@router("needs_revision") def revise(...): ...; return "revised")、レビューでは or_(write_draft, "revised") をリッスンするようにしてください。
  • and_ はすべての条件が完了した後に一度だけ実行され、最後に完了した条件の出力を受け取ります。それ以外の出力は self.state から取得してください。
  • ルーターの後の一般的な合流処理では、@listen(or_(page_oncall, file_ticket)) が、どちらの分岐が実行された場合でも一度だけ実行されます。

5. フローメソッドからクルーやエージェントを呼び出す

from crewai import Agent, Crew, Task
from crewai.flow import Flow, listen, start


class ResearchFlow(Flow):
    @start()
    def research(self):
        researcher = Agent(role="Researcher", goal="Research {topic}",
                           backstory="You find facts.", llm="openai/gpt-4o-mini")
        task = Task(description="List three facts about {topic}.",
                    expected_output="Three bullets.", agent=researcher)
        out = Crew(agents=[researcher], tasks=[task]).kickoff(inputs={"topic": "bees"})
        return out.raw                       # CrewOutput: .raw .pydantic .json_dict

    @listen(research)
    def summarize(self, facts):
        writer = Agent(role="Writer", goal="Summarize", backstory="Concise.",
                       llm="openai/gpt-4o-mini")
        return writer.kickoff(f"One sentence summary: {facts}").raw   # LiteAgentOutput
メソッドの種類呼び出し方誤った形式
defcrew.kickoff(inputs=...)、agent.kickoff(...)-
async defawait crew.akickoff(inputs=...)async def 内の crew.kickoff():RuntimeError: Agent execution was invoked synchronously from within a running event loop
  • await crew.akickoff() の場合、エージェントの mcps=["https://..."] 文字列参照は、エラーなしでツールが0個に解決されます(独自のイベントループを開始する必要があるためです)。非同期メソッドでは crewai.mcp の MCPServerHTTP(url=...) を使用してください。詳細は connect-tools-and-mcp スキルを参照してください。
  • クルーの {placeholders} は、フローの状態ではなく、クルー自身の kickoff(inputs=...) から値が設定されます。入力なしでキックオフしたクルーは、リテラルの {topic} を LLM に送信します。必要な状態の値は明示的に渡してください。
  • クルーはメソッド内で構築するか(または @CrewBase クラスから毎回 MyCrew().crew() を呼び出し)、実行ごとに新しいオブジェクトが使われるようにしてください。
  • フローメソッドは通常のメソッドです。ユニットテストでは ReportFlow().plan() を直接呼び出せます。

6. @persist による永続化

from pydantic import BaseModel
from crewai.flow import Flow, persist, start   # or: from crewai.flow.persistence import persist


class CounterState(BaseModel):
    count: int = 0


@persist()                      # class level: state saved after every method
class CounterFlow(Flow[CounterState]):
    @start()
    def bump(self):
        self.state.count += 1
        return self.state.count


first = CounterFlow()
first.kickoff()                                        # count == 1
second = CounterFlow()
second.kickoff(restore_from_state_id=first.state.id)   # count == 2
print(second.state.id != first.state.id)               # True: a fork with a new id
目的実行すること避けること
インポートfrom crewai.flow import persist または from crewai.flow.persistence import persistfrom crewai.flow.flow import persist(ImportError)
デコレート@persist() - 必ず括弧を付けますクラスに括弧なしの @persist を付けること:クラスが関数に置き換わり、インスタンス化すると TypeError: persist.<locals>.decorator() missing 1 required positional argument: 'target' が発生します。メソッドに括弧なしの @persist を付けること:エラーはまったく発生しません - メソッド本体は実行されず、kickoff() はフローオブジェクトを返し、何も保存されません
保存済み状態から新しい実行を開始するkickoff(restore_from_state_id=sid) - 状態を復元してから、新しい state.id で書き込みますkickoff(inputs={"id": sid}) - 非推奨です。同じ ID で再開し、警告も表示されません
チェックポイントとして適切なステップだけで保存するそのメソッドに @persist() を付けます(@listen の上に重ねます)最終状態だけが必要なのに、クラスレベルで指定すること
復元した実行のフィールドを変更するrestore_from_state_id とともに inputs に渡します。入力は状態の復元後に適用されますSQLite ファイルを編集すること

状態の保存先と、何も通知されないケース:

  • デフォルトのバックエンド:SQLiteFlowPersistence。ファイルは appdirs.user_data_dir(<CREWAI_STORAGE_DIR or current dir name>, "CrewAI") 内の flow_states.db です。macOS では ~/Library/Application Support/<name>/、Linux では ~/.local/share/<name>/ になります。CREWAI_STORAGE_DIR に絶対パスを設定すると、ファイルはそのディレクトリ直下に置かれます。または、パスを渡します:@persist(SQLiteFlowPersistence("/abs/path/flows.db"))。
  • 存在しない restore_from_state_id を指定してもエラーにはなりません。「No flow state found ... proceeding without hydration」とログに記録され、デフォルト値から実行されます。
  • @persist のないフローに対する restore_from_state_id は、通知なく無視されます。
  • ローカルでは、@persist は SQLite ファイルに保存します。自分で管理する永続的な状態が必要な場合は、独自のデータベースを使って FlowPersistence を実装してください(persistence-and-checkpoints)。

CrewAI AMP(ZIP アップロードでデプロイしたクラスレベルの @persist() フローを 2026-10-01 にテスト)では、POST /kickoff の inputs と並ぶトップレベルの restoreFromStateId として ID を送信します:

Kickoff のボディ確認された動作
{"inputs": {...}, "restoreFromStateId": "<state_id>"}状態が復元されます(カウンターとリストは引き継がれ、inputs に含まれないフィールドも保持されます)。その上に inputs が適用され、新しい state.id(= 新しい kickoff_id)になります
同じ内容で、crewai deploy push による再デプロイを 2 回行った後引き続き復元されます - 保存された状態は再デプロイ後も保持されました
何にも一致しない restoreFromStateIdエラーは発生せず、デフォルト値から実行が開始されます
{"inputs": {"id": "<state_id>", ...}}(非推奨)状態は復元されますが、古い state.id が保持され、その後 kickoff_id と異なる値になります

呼び出し元が復元に使う ID を取得できるよう、最後のメソッドから self.state.id を返すか、kickoff_id を読み取ってください。


7. チェックポイント(クラッシュ後の再開)

チェックポイントは@persistとは別の仕組みです。実行(状態、完了したメソッド、出力)をファイルにスナップショットとして保存するため、失敗した実行を続行できます。

from crewai import CheckpointConfig
from crewai.flow import Flow, listen, start


class EtlFlow(Flow):
    @start()
    def extract(self):
        return "rows"

    @listen(extract)
    def load(self, rows):
        return f"loaded {rows}"


flow = EtlFlow(checkpoint=CheckpointConfig(
    location="./.checkpoints",
    on_events=["method_execution_finished"],   # required for flows, see below
))
flow.kickoff()

再開するには、EtlFlow().kickoff(from_checkpoint=CheckpointConfig(restore_from="./.checkpoints/<branch>/<file>.json"))を使います。完了済みのメソッドはスキップされます。続行前に状態を変更する場合(たとえば、失敗の原因となったフラグをクリアする場合)は、flow = EtlFlow.from_checkpoint(CheckpointConfig(restore_from=path))を使い、flow.stateを編集してからflow.kickoff()を呼び出します。

  • Flowでは、on_events=["method_execution_finished"]を設定してください。 デフォルトの["task_completed"](およびcheckpoint=True)では、メソッドがCrewを実行した場合でも、Flowのチェックポイントは一切書き込まれませんでした。
  • ファイルはlocation/<branch>/の下に、完了したメソッドごとに1つずつ、<timestamp>_<id>_p-<parent>.jsonという名前で書き込まれます。タイムスタンプは秒単位で、IDはランダムです。そのため、複数のメソッドが同じ秒に完了した場合、ファイル名をソートしても最新のファイルは見つかりません(ソートしたリストでは、最後から2番目のファイルが選ばれました)。max(files, key=os.path.getmtime)を使うか、crewai checkpoint list <location>を実行してください(新しい順に表示されます)。
  • 再開してもルーターの先へは続行されません。 @router(またはemitを持つ@human_feedbackメソッド)の完了後に取得したチェックポイントから再開すると、何も実行されず、ルーターのラベル(またはNone)がエラーなしで返されます。そのラベルのリスナーは実行されません。そのため、ルーターの下流でクラッシュした場合、最新のチェックポイントからは再開できません。ルーターの完了前に取得したチェックポイントからのみ、ルーターを通過して続行できます。1.15.22と1.15.23で確認済みです。クラッシュしやすい処理はルーターより前に置くか、下流のステップを冪等にしてFlowを再実行してください。
  • kickoff(from_checkpoint=..., restore_from_state_id=...)はValueError: Cannot combine ...を発生させます。いずれか一方のシステムを選んでください。

8. 人間によるフィードバック(@human_feedback)

from crewai.flow import Flow, HumanFeedbackResult, human_feedback, listen, start


class ReviewFlow(Flow):
    @start()
    @human_feedback(
        message="Approve this draft?",
        emit=["needs_revision", "approved"],
        llm="openai/gpt-4o-mini",            # maps free-text feedback to one label
        default_outcome="needs_revision",    # used when feedback is empty
    )
    def draft(self):
        return "Draft v1"

    @listen("approved")
    def publish(self, result: HumanFeedbackResult):
        return f"published: {result.output} ({result.feedback})"

    @listen("needs_revision")
    def revise(self, result: HumanFeedbackResult):
        return f"revise: {result.feedback}"
  • HumanFeedbackResultのフィールド:output、feedback、outcome、timestamp、method_name、metadata。テキストは**result.feedback**です(feedback_textはAttributeErrorを発生させます)。Flow上では、self.last_human_feedbackとself.human_feedback_historyも利用できます。
  • フィードバックが空の場合は、default_outcomeが使われます。未設定の場合はemit[0]が使われます。安全な結果を先頭に置き、default_outcomeにも設定してください。そうすれば、Enterキーを押しても承認されません。
  • 空ではないフィードバックをLLMがラベルに対応付けられない場合は、HumanFeedbackCollapseErrorが発生し、フォールバックは行われません。
  • 空ではないフィードバックはllmによって対応付けられます。anthropic/claude-haiku-4-5では、「Looks good to me, ship it.」はapprovedに、「No, the tone is wrong - please rewrite it more politely.」はneeds_revisionになりました。元のテキストはresult.feedbackに保持されます。
  • emitを指定せずにdefault_outcomeを指定すると、ValueError: default_outcome requires emit to be specified.が発生します。
  • emitがない場合、次のリスナーにはHumanFeedbackResultが渡され、ルーティングは行われません。
  • デフォルトのプロバイダーはコンソールから入力を読み取ります。テストやUIで使う場合は、provider=(request_feedback(context, flow) -> strを持つオブジェクト)を渡してください。プロバイダーからHumanFeedbackPendingを発生させて一時停止し、後でMyFlow.from_pending(flow_id, persistence).resume(feedback)を呼び出します(persistence-and-checkpoints)。stdinがない場合(CI、< /dev/null、バックグラウンドジョブ)、コンソールプロバイダーはEOFError: EOF when reading a lineを発生させ、Flowは失敗します。デプロイ済みのFlowではinput()を決して使わないでください。

9. plot()

path = TicketFlow().plot("ticket_flow.html", show=False)
print(path)   # /tmp/.../crewai_flow_xxxx/ticket_flow.html
  • plot(filename="crewai_flow.html", show=True) は、インタラクティブな HTML(および .js と .css)を新しい一時ディレクトリに書き込み、絶対パスを返します。現在のディレクトリには何も書き込まれず、PNG もありません。保存する必要がある場合は、ファイルをコピーしてください。
  • ファイル名は指定どおりに使用されます。plot("my_flow") は .html 拡張子のない my_flow という名前のファイルを書き込みます。
  • show=True はブラウザーを開きます。CI やヘッドレス環境で実行する場合は show=False を使用してください。
  • Literal[...] の戻り値型も emit=[...] もないルーターは、「Router events for 'r' are dynamic or not statically inferable」とログに記録し、グラフからエッジが省略されることがあります。グラフのデータは .html ではなく .js ファイルにあります。

10. 会話フロー

複数ターンのチャットでは、crewai.flow(ConversationConfig、ConversationState、RouterConfig)からインポートしてください。crewai.experimental.conversational は同じモジュールの非推奨エイリアスです。ターンは kickoff(user_message=...)(TypeError)ではなく、flow.handle_turn(message, session_id=...) で進めてください。ChatSession クラスはありません。詳細とテスト済みの例については、conversational-flows を参照してください。


11. よくある間違い

症状原因解決方法
ImportError: cannot import name 'persist'from crewai.flow.flow import persistfrom crewai.flow import persist
TypeError: persist.<locals>.decorator() missing 1 required positional argument: 'target'、またはメソッドが実行されない括弧なしの @persist@persist()
listen condition 'x' references the handler name 'x'ルーターのラベルがリスナーのメソッド名と同じラベルまたはメソッド名を変更する
RecursionError: Method 'r' has been called 100 timesルーターがメソッド名と同じラベル(多くの場合、自身のトリガー)を返したメソッド名ではないラベルを使用する
ルーターの後にフローが停止し、エラーもないルーターが @listen のないラベルを返したすべてのラベルをリッスンし、ルーターの型を -> Literal[...] にする
上流の両方が完了したのに、リスナーが一度しか実行されないそれは or_ ですすべてを待つには and_ を使用する
入力値が無視されるキーのスペルミス。構造化状態は未知のキーを破棄するフィールド名を完全に一致させる
'StateWithId' object is not subscriptable構造化状態に対する辞書アクセスself.state.field
Flow state model must have an 'id' fieldinitial_state = MyModelclass F(Flow[MyModel])
crewai run でフローがエラーになったと表示されるが、出力は正しいmain.py の kickoff() が値を返したNone を返す
--inputs requires a declarative flow definitionPython フローに対して crewai run --inputs を実行したコード内で入力を渡すか、uv run kickoff '<json>' を使用する
invoked synchronously from within a running event loopasync def 内で crew.kickoff() を呼び出したawait crew.akickoff() を使用するか、メソッドを同期型にする
再開した実行がデフォルト値から始まるID が不明、またはクラスに @persist がないID とデコレーターを確認する。どちらの場合もエラーは表示されない
実行 ID が複数の実行で再利用されるinputs={"id": ...}restore_from_state_id=
フローのチェックポイントファイルがないデフォルトの on_events=["task_completed"]on_events=["method_execution_finished"]
チェックポイントからの再開でラベル(または None)が返され、何も実行されないルーターの完了後にチェックポイントが取得されたルーターより前のチェックポイントから再開するか、再実行する
@human_feedback で EOFError: EOF when reading a line標準入力がないコンソールプロバイダーprovider= を渡す
雛形から生成されたフローが topic 入力を無視するテンプレートの plan_content() が "AI Agents" をハードコードしている代入を削除する
AttributeError: ... 'feedback_text'古いフィールド名result.feedback
HumanFeedbackCollapseErrorフィードバックがどの emit ラベルにも一致しなかったラベルをクリアし、対応可能な llm を使い、エラーを捕捉する
plot() のファイルがプロジェクト内にない一時ディレクトリに書き込まれる返されたパスを使用する

12. チェックリスト

  • 状態は Flow[MyState] で、すべてのフィールドにデフォルト値が設定されていること。id を手動で設定しないこと
  • すべてのルーターラベルがすべてのメソッド名と異なり、リスナーを持つこと
  • ルーターの戻り値の型が Literal[...](または emit=[...])であり、plot() にエッジが表示されること
  • or_(最初に一致したものを一度だけ実行)または and_(すべて実行)を意図的に使って結合すること
  • async def メソッドでは crew.akickoff() を await し、同期メソッドでは kickoff() を呼び出すこと
  • persist を crewai.flow からインポートし、@persist() として記述すること。再開には restore_from_state_id(HTTP では restoreFromStateId)を使用すること
  • 状態を再び見つける必要がある場所に、絶対パスを CREWAI_STORAGE_DIR に設定すること
  • チェックポイントを使用するフローでは on_events=["method_execution_finished"] を使用すること。最新のファイルは mtime で選択され、クラッシュしやすいステップはルーターの前に配置すること
  • @human_feedback では安全な結果を最初に指定し、default_outcome にも設定すること。コードでは .feedback を読み取ること
  • main.kickoff() が None を返し、[tool.crewai] type = "flow" が存在すること
  • crewai flow plot(または plot(show=False))を実行し、グラフを確認したこと

参考資料

  • ルーティングパターン - or_ + ルーターによる修正ループ、ネストされた条件、複数のルーター、ルーター/or_/persist を使った完全なサンプルフロー
  • 永続化とチェックポイント - ストレージパス、フォークと再開の違い、カスタムの FlowPersistence、チェックポイントからの再開と編集、一時停止された人間によるフィードバック
  • 会話型フロー - handle_turn、ConversationConfig、ターンのルーティング

公開ドキュメント:https://docs.crewai.com/en/concepts/flows、https://docs.crewai.com/en/concepts/checkpointing、https://docs.crewai.com/en/learn/human-feedback-in-flows、https://docs.crewai.com/en/guides/flows/inputs-id-deprecation

関連スキル:

  • getting-started - クルーとフローのどちらを選ぶか、プロジェクトの構成
  • check-crewai-api - 現在の Agent/Task/Crew/LLM API と、記憶にある 0.x 形式との比較
  • design-task - フローが呼び出すクルーのタスク説明、構造化出力、ガードレール
  • test-crewai-project - スタブ LLM を使ってフローをオフラインで実行し、状態を検証する
  • deploy-to-amp - type = "flow" に対してホスト型ビルドが想定するプロジェクト構成とエントリーポイント
  • call-deployed-crew - HTTP 経由でデプロイ済みフローをキックオフする
  • ask-docs - ここに記載された内容が使用中のバージョンと一致しない場合に、最新のドキュメントを検索する

コメント