mcpField
T
test-crewai-project

test-crewai-project

github.com/crewaiincテスト・品質48735 回インストール3 週間前に更新
リポジトリ

test-crewai-project について

CrewAIのcrewとFlowをテストするためのガイドです。APIキーなしで決定論的なテストを行い、実際の実行を調査したい開発者向けに、スタブLLM、プロンプトやルーティングの検証、ガードレールテスト、`crewai test`の動作を説明します。

主な機能

  1. 01スタブLLMを使ったcrewのテスト
  2. 02プロンプト内容とツール実行の検証
  3. 03ガードレールとFlowルーティングのテスト
  4. 04`crewai test`の評価動作の解説
  5. 05イベントとトレースに関する注意点

活用シーン

  1. 01CIでのオフライン単体テスト
  2. 02プロンプト入力とタスクコンテキストの確認
  3. 03ガードレールの合格判定と再試行のテスト
  4. 04CrewAI評価実行の調査

インストール

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

$npx skills add https://github.com/crewaiinc/skills --skill test-crewai-project

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

同梱ファイルreferences/example-tests.mdreferences/stub_llm.py

詳細

CrewAI プロジェクトをテストする

API キーなしでクルーとフローを決定論的にテストする方法と、イベントやトレースを使って実際の実行をデバッグする方法です。

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


1. テストにスタブ LLM が必要な理由

実際の LLM は実行のたびに異なるテキストを返し、費用がかかるうえ、キーも必要です。明示的な llm が設定されていない Agent はすべて gpt-4.1-mini を解決先とするため、キーが設定されていない場合はキックオフ時に ValueError: OPENAI_API_KEY is required が発生します。そのため、シークレットのない CI 実行は開始することさえできません。予測できない出力をアサートすることはできません。

スタブ LLM を使うと応答が固定されるため、正確なアサーションが可能になります。プロンプトの構築、{placeholder} の補間、タスクコンテキスト、ツールの実行、ガードレールの再試行、pydantic への変換、フローのルーティングといった、実際の crewai のコードパスはすべてそのまま使われます。また、すべての呼び出しが記録されるため、crewai が構築したプロンプトをアサートできます。多くのバグは、入力の欠落、コンテキストの脱落、ツールの未登録など、ここで発生します。

段階的にテストします。

Tier必要なもの検証できること
単体テスト:スタブ LLM、通常の pytestなし配線、プロンプト、ルーティング、解析、ガードレールのロジック
ガードレールの真理値表なし各ガードレールが正しい出力を受け入れ、拒否すること
実モデルでの実行(crewai test、スモークキックオフ)プロバイダーのキー、費用プロンプトの品質。@pytest.mark.credentialed を付け、オフラインスイートは pytest -m "not credentialed" で実行します

2. 1.15 におけるカスタム BaseLLM の要件

from crewai import BaseLLM。抽象メソッドは call だけです。crewai は from_task、from_agent、および(構造化タスクの場合)response_model をキーワード引数として渡すため、call はこれらを受け取れる必要があります。

call のシグネチャ結果
不適切(公開されているカスタム LLM ドキュメントの例)def call(self, messages, tools=None, callbacks=None, available_functions=None)キックオフ時に TypeError: MyLLM.call() got an unexpected keyword argument 'from_task'
適切def call(self, messages, tools=None, callbacks=None, available_functions=None, from_task=None, from_agent=None, response_model=None, **kwargs)実行される
要件省略した場合の動作
from_task、from_agent、response_model(または **kwargs)を受け取る最初の LLM 呼び出し時に TypeError
空でない model=(例:MyLLM(model="my-model"))を渡す構築時に ValidationError
with llm_call_context(): 内で self._emit_call_started_event(...) / self._emit_call_completed_event(...)(crewai.llms.base_llm から)を呼び出すイベントリスナーとトレースに LLM 呼び出しが表示されない(セクション 9 を参照)
messages をプロバイダー SDK に渡す場合は、role と content だけを転送するcrewai は各メッセージに cache_breakpoint: True を追加します。Anthropic API はこれを 400 ... messages.0.cache_breakpoint: Extra inputs are not permitted で拒否します
supports_function_calling() が False を返すcrewai はツールに ReAct のテキストループ(Thought / Action / Action Input)を使用します
response_model が渡された場合、検証済みのモデルインスタンスを返すネイティブプロバイダーはこのように動作します。テキストを返しても機能しますが、その場合にガードレールから output.pydantic がどう見えるかについては、セクション 6 を参照してください

references/stub_llm.py にはこれらすべてが実装されています。独自に書く必要がない限り、これをコピーして使ってください。

3. テストハーネスをセットアップする

  1. references/stub_llm.py をプロジェクト内の tests/stub_llm.py にコピーします。
  2. uv add --dev pytest を実行し、uv run pytest でテストを実行します。
  3. pytest の意味で uv run test を実行しないでください。スキャフォールドされた crew プロジェクトでは、test は crewai test(セクション 8)を実行するプロジェクトスクリプトであり、実際のモデルを呼び出します。
  4. tests/.crewai-test-storage/ を .gitignore に追加します。
# tests/conftest.py
import os
import socket
from pathlib import Path

# Optional, test-only switches. The AGENTS.md that `crewai create` writes leaves
# turning observability off to the user, so ask before uncommenting them.
# Without them the socket guard below does not cover everything:
# - crewai telemetry is still sent to telemetry.crewai.com when the test process
#   exits, after the guard has been undone;
# - if tracing is on (CREWAI_TRACING_ENABLED=true or tracing=True) and a CrewAI
#   credential exists (CREWAI_USER_PAT, or just a saved `crewai login` on the
#   machine), kickoff requests a trace grant, the guard refuses it, and the test
#   fails with TraceGrantError.
# If uncommented, they must run before crewai is imported; never put them in the project's .env.
# os.environ["CREWAI_DISABLE_TELEMETRY"] = "true"
# os.environ["CREWAI_TRACING_ENABLED"] = "false"
# os.environ["OTEL_SDK_DISABLED"] = "true"
# Absolute path: crewai's SQLite files and memory store go here instead of your user data dir.
os.environ["CREWAI_STORAGE_DIR"] = str(Path(__file__).parent / ".crewai-test-storage")

import pytest


@pytest.fixture(autouse=True)
def offline(monkeypatch, tmp_path):
    def refuse(*args, **kwargs):
        raise RuntimeError("test tried to use the network")

    monkeypatch.setattr(socket.socket, "connect", refuse)
    monkeypatch.chdir(tmp_path)  # output_file= and flow file writes land in a temp dir

各行の理由:

  • import crewai だけで <platform user data dir>/<cwd name>/ が作成されます。macOS では ~/Library/Application Support/<cwd name>/ です。Kickoff によってそこに SQLite ファイルが書き込まれます。インポート前に絶対パスの CREWAI_STORAGE_DIR を設定すると、すべての保存先が変更されます。
  • ソケットガードにより、誤って実際の LLM を呼び出すと、ハングしたり料金が発生したりする代わりに、即座に失敗します。
  • crewai の内部処理は、kickoff のたびに DeprecationWarning を出力します。これを抑制するには:
# pyproject.toml
[tool.pytest.ini_options]
filterwarnings = ["ignore::DeprecationWarning:crewai.*"]

4. スタブを使った実行

StubLLM は、次の 3 つのソースからこの順番で各応答を選びます。

ソース用途
responses=[...]LLM 呼び出しごとに、呼び出し順に 1 項目ずつ使用します。項目は str、dict/list(JSON として送信)、pydantic モデル、または f(messages, response_model) です
responder=f(messages, response_model)キューが空になった後に使用します。呼び出し順ではなく、プロンプトの内容やスキーマに基づいて判断できます
default_response="..."上記の両方を使用した後に使われます。これを設定せず、crewai が response_model を要求した場合、スタブはそのモデルのスケルトン JSON を返します

ツール呼び出しは ReAct ループを経由します。tool_call(name, args) は Thought/Action/Action Input 形式のテキストを作成します。実際のツールの _run が実行され、その結果は Observation: ... として次の LLM 呼び出しに返されます。

# tests/test_structured_and_tools.py
from pydantic import BaseModel

from crewai import Agent, Crew, Task
from crewai.tools import tool
from stub_llm import StubLLM, prompt_text, tool_call


class Brief(BaseModel):
    title: str
    bullets: list[str]


tool_inputs = []


@tool("Word Counter")
def word_counter(text: str) -> int:
    """Count the words in a piece of text."""
    tool_inputs.append(text)
    return len(text.split())


def test_tool_call_then_structured_answer():
    llm = StubLLM(responses=[
        tool_call("Word Counter", {"text": "bees make honey"}),  # researcher: use the tool
        "Final Answer: 3 words",                                 # researcher: answer
        {"title": "Bees", "bullets": ["honey"]},                 # writer: JSON for Brief
    ])
    researcher = Agent(role="Researcher", goal="Count words", backstory="Careful.",
                       tools=[word_counter], llm=llm)
    writer = Agent(role="Writer", goal="Write briefs", backstory="Concise.", llm=llm)
    count = Task(description="Count the words", expected_output="A count", agent=researcher)
    brief = Task(description="Write a brief", expected_output="Title and bullets",
                 agent=writer, output_pydantic=Brief)

    out = Crew(agents=[researcher, writer], tasks=[count, brief]).kickoff()

    assert tool_inputs == ["bees make honey"]               # the real tool ran
    assert "Observation: 3" in prompt_text(llm.calls[1])    # its result went back to the LLM
    assert llm.calls[0]["response_model"] is None           # agents WITH tools get no response_model
    assert llm.calls[2]["response_model"] is Brief
    assert out.pydantic == Brief(title="Bees", bullets=["honey"])

スタブテストを安定させるためのルール:

ルール理由
output_pydantic / output_json タスクごとに、有効な JSON(dict)をキューに追加します解析できない回答があると、crewai はコンバーターによる LLM 呼び出しを追加で行います。その呼び出しが後続のキュー項目を消費し、最終的に ConverterError が発生します
out.pydantic に値を設定するのは output_pydantic ですTask(response_model=X) だけではスキーマが LLM に送られるだけで、out.pydantic は None のままです
呼び出し回数は out.token_usage ではなく len(llm.calls) で数えますCrew の使用量は、各 LLM オブジェクトの存続期間にわたり、エージェントごとに合算されます。2 つのエージェントで 1 つのスタブを共有すると 2 回カウントされ、2 つ目の crew でスタブを再利用すると、1 つ目の crew のトークンも引き継がれます
呼び出し順の予測が難しい場合は、responder を使い、response_model またはプロンプトのテキストに基づいて判断しますキューは、正確な呼び出し順がわかっている場合にのみ機能します
kickoff_for_each では、新しい crew を作成しますすでに kickoff した crew では、各項目で前回の kickoff の入力が暗黙的に再利用されます

5. crew をテストし、プロンプトを検証する

LLM を注入できるようにします。ファクトリー関数では llm=None を受け取り、すべての Agent に渡します(None の場合、本番環境では crewai のデフォルトモデルが使われます)。スキャフォールドされた @CrewBase クラスでは、.crew() の後に LLM を差し替えます。

# tests/test_crew.py
from pathlib import Path

from acme_research.crew import AcmeResearch
from stub_llm import StubLLM, prompt_text


def crew_with(llm):
    crew = AcmeResearch().crew()
    for agent in crew.agents:
        agent.llm = llm
    return crew


def test_research_crew_output_and_prompts():
    llm = StubLLM(responses=["- bees pollinate crops", "# Bees report"])
    out = crew_with(llm).kickoff(inputs={"topic": "bees", "current_year": "2026"})

    assert out.raw == "# Bees report"
    assert [t.raw for t in out.tasks_output] == ["- bees pollinate crops", "# Bees report"]
    assert len(llm.calls) == 2

    first = prompt_text(llm.calls[0])
    assert "bees Senior Data Researcher" in first                   # {topic} filled in agents.yaml
    assert "the current year is 2026" in first                      # {current_year} in tasks.yaml
    assert "- bees pollinate crops" in prompt_text(llm.calls[1])    # task 1 output is task 2 context
    assert Path("report.md").read_text() == "# Bees report"         # output_file, in tmp cwd

llm.calls[i] には、messages(crewai が送信した system メッセージと user メッセージのスナップショット)、response_model、tools、response が格納されています。prompt_text(call) はメッセージの内容を結合します。次のような項目を検証すると便利です。

検証項目検出できる問題
入力値がプロンプトに含まれているinputs を指定しない kickoff(文字列の {topic} がプロンプトに残ります。キーが 1 つでも不足している dict を渡した場合は、代わりに ValueError が発生します)
前のタスクの出力が次のプロンプトに含まれているcontext= の不具合
Tool Name: <snake_case name> が含まれているツールが紐付けられていないこと。@tool("Word Counter") は word_counter として出力されますが、Action: Word Counter でも引き続き解決されます
再試行プロンプトにガードレールのエラーが含まれているフィードバックがモデルに届かないガードレール
分岐で llm.calls == [] であるモデルを呼び出すべきではないフロー分岐で、モデルが呼び出されていること

6. ガードレールのテスト

関数型ガードレールは、受け入れる場合は (True, value) を、拒否する場合は (False, "error for the LLM") を返します。2 通りの方法でテストします。単純な関数として出力の真理値表を使ってテストする方法と、クルー内でリトライを確認する方法です。ガードレールが失敗し続けると、guardrail_max_retries 回のリトライ後(デフォルトは 3 回)に Task failed guardrail validation after N retries が発生します。真理値表と諦める場合のテストは、example-tests.md のセクション 1 にあります。

# tests/test_guardrails.py
from pydantic import BaseModel

from crewai import Agent, Crew, Task
from crewai.tasks.task_output import TaskOutput
from stub_llm import StubLLM, prompt_text


class Brief(BaseModel):
    title: str
    bullets: list[str]


def max_three_bullets(output: TaskOutput):
    try:  # output.pydantic can be None here, so fall back to parsing raw
        brief = output.pydantic or Brief.model_validate_json(output.raw)
    except ValueError:
        return (False, "Return JSON matching the Brief schema.")
    if len(brief.bullets) > 3:
        return (False, f"Use at most 3 bullets, you used {len(brief.bullets)}.")
    return (True, output.raw)  # a string: crewai re-runs the pydantic conversion


def brief_crew(llm):
    writer = Agent(role="Writer", goal="Write briefs", backstory="Concise.", llm=llm)
    task = Task(description="Write a brief", expected_output="Title and up to 3 bullets",
                agent=writer, output_pydantic=Brief,
                guardrail=max_three_bullets, guardrail_max_retries=2)
    return Crew(agents=[writer], tasks=[task])


def test_retry_feeds_the_error_back():
    llm = StubLLM(responses=[{"title": "T", "bullets": ["a", "b", "c", "d"]},
                             {"title": "T", "bullets": ["a"]}])
    out = brief_crew(llm).kickoff()
    assert out.pydantic == Brief(title="T", bullets=["a"])
    assert "Use at most 3 bullets, you used 4." in prompt_text(llm.calls[1])

注意すべき点が 2 つあります。

  • ガードレール内では output.pydantic が None の場合があります。 タスクにガードレールがあり、LLM がテキストを返した場合、crewai は最初のガードレール呼び出し前に pydantic 変換をスキップします。エージェントにツールがある場合や、文字列を返すカスタム LLM の場合、LLM はテキストを返します。output.raw を自分で解析してください。
  • return (True, output) では out.pydantic がひそかに失われる場合があります。 output.pydantic が None だった場合、crewai はその TaskOutput をそのまま保持します。(True, output.raw) を返すと、crewai が文字列を変換します。その後、境界で out.pydantic is not None をアサートしてください。

7. フローのテスト

フローメソッドは通常のメソッドなので、1 つのステップをテストするには直接呼び出します。ルーティングをエンドツーエンドでテストするには、kickoff(inputs=...) を使用します。kickoff は最後に実行されたメソッドの戻り値を返し、inputs のキーは @start の実行前に state フィールドを上書きします。

# tests/test_flow_routing.py
from pydantic import BaseModel

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


class State(BaseModel):
    topic: str = ""
    log: list[str] = []


class TopicFlow(Flow[State]):
    @start()
    def normalize(self):
        self.state.topic = self.state.topic.strip().lower()

    @router(normalize)
    def choose(self):
        return "has_topic" if self.state.topic else "no_topic"  # labels differ from method names

    @listen("has_topic")
    def research(self):
        self.state.log.append("research")

    @listen("no_topic")
    def skip(self):
        self.state.log.append("skip")

    @listen(or_(research, skip))
    def finish(self):
        return f"done:{self.state.topic or '-'}"


def test_step_directly():
    flow = TopicFlow()
    flow.state.topic = "  Bees "
    flow.normalize()
    assert flow.state.topic == "bees"
    assert flow.choose() == "has_topic"


def test_both_branches_via_kickoff():
    flow = TopicFlow()
    assert flow.kickoff(inputs={"topic": " Bees "}) == "done:bees"
    assert flow.state.log == ["research"]

    flow = TopicFlow()
    assert flow.kickoff(inputs={"topic": "   "}) == "done:-"
    assert flow.state.log == ["skip"]

crewai create flow のスキャフォールドが ContentCrew().crew() で行うように、フローのステップがそれ自体でクルーを構築する場合、スタブを渡すパラメーターはありません。monkeypatch.setattr でクルークラスの crew メソッドをラップする関数に置き換え、元のメソッドを呼び出した後、すべてのエージェントで agent.llm = stub を設定します。完全なテストは example-tests.md のセクション 2 にあります。

Flow は pydantic モデルです。フローコードを管理している場合、フロークラスに llm: Any = None を宣言し、MyFlow(llm=stub) を渡す方が、パッチを当てるよりすっきりした接続点になります。x という名前のメソッドに @listen("x") を付けると、フローのインスタンス化時にエラーが発生するため、フローを構築するだけのテストでも検出できます。


8. crewai test が実際に行うこと

crewai test はモデル評価による評価であり、ユニットテストランナーではありません。

事実詳細
実行内容uv run test <n> <model>。つまり、プロジェクトの main.test() を実行し、これは Crew.test(n_iterations, eval_llm=model, inputs=...) を呼び出します
デフォルト-n 3、-m gpt-5.4-mini(古いドキュメントでは 2 と gpt-4o-mini)
評価モデル--help では OpenAI モデルのみと記載されていますが、-m は任意のプロバイダー文字列を指定できます。プロジェクトにプロバイダーの追加パッケージがある場合(uv add "crewai[anthropic]")、crewai test -m anthropic/claude-haiku-4-5 が動作します
各イテレーション独自の LLM を使ったクルーの完全な実際の kickoff と、タスクごとに 1 回の評価呼び出し
スコアリング-m モデルを使用する評価エージェントが各タスクを 1 から 10 で採点し、Tasks/Crew/Agents テーブルを表示します。テーブルにはタスクごとに 1 行、Crew 行、Execution Time (s)、実行ごとに 1 列、Avg. Total が含まれます
必要なものクルーのモデルと評価モデルの両方のキーが必要です。評価モデルは最初のタスクの後に実行されるため、評価モデルのキーが不足していると判明する時点では、そのタスクはすでに実行され、課金されています。エラーは An error occurred while testing the crew: OPENAI_API_KEY is required、または無効なキーの場合は Error code: 401 です
終了コード実行に失敗しても 0 なので、CI ではこれを信頼できません
副作用実行するクルーのコピー上で各タスクの callback を置き換えます。タスクのスコアごとに Sync handler error in on_crew_test_result をログに記録しますが、実行は失敗しません
フロープロジェクトフローのスキャフォールドには test スクリプトがないため、uv run test はシェルの test 組み込みコマンドに到達し、test: 1: unexpected operator が発生します

実際のモデルを使い、プロンプト品質を定期的にチェックする用途には使用してください。費用がかかり、スコアも決定的ではないため、CI には使用しないでください。Crew.test は eval_llm として BaseLLM を受け取るため、クルーと評価モデルの両方にスタブを使えば、オフラインで実行できます。これは配線のみをチェックします(example-tests.md のセクション 4)。

9. デバッグ用イベントリスナー

実行のたびに、crew、task、agent、LLM 呼び出し、tool、flow、memory の型付きイベントが crewai_event_bus に送信されます。これらは crewai.events からインポートしてください。crewai.utilities.events は存在しなくなりました。

BaseEventListener をサブクラス化し、setup_listeners(self, bus) を実装して、その中で @bus.on(EventClass) を使ってハンドラーを登録します。各ハンドラーのシグネチャは handler(source, event) です。リスナーをインスタンス化すると、ハンドラーが登録されます。example-tests.md のセクション 3 には、デバッグ用リスナー(ToolUsageFinishedEvent、LLMCallCompletedEvent、TaskFailedEvent)とレコーダーのテストがあります。

テストでは、with crewai_event_bus.scoped_handlers(): の中でリスナーを登録し、処理後にリスナーが解除されるようにしてください。ハンドラーはワーカースレッドで実行されるため、アサーションの前に crewai_event_bus.flush() を呼び出してください。

  • 実際の実行をデバッグするには、リスナーを専用のモジュールに記述し、main.py からインポートして一度だけインスタンス化してください。その他に役立つイベントは、LLMCallFailedEvent、MemorySaveFailedEvent、CrewKickoffStartedEvent、FlowFinishedEvent です。
  • LLMCallCompletedEvent.response はテキストです。ネイティブのツール呼び出しの場合は、プロバイダーのツール使用ブロックのリストです。event.model と event.usage には、モデルとトークン数が含まれます。
  • 独自の BaseLLM は、LLM 呼び出しイベントを自ら送信する必要があります。 組み込みプロバイダーは LLMCallStartedEvent と LLMCallCompletedEvent を送信します。テキストを返すだけのサブクラスは何も送信しないため、トレースリスナーも同じイベントを購読していることから、リスナーもトレースもすべての LLM 呼び出しを検出できません。

10. トレーシングとテレメトリーの切り替え

これらは別々のシステムです。テレメトリーは、CrewAI に送信される匿名の使用状況統計です。トレーシングは、CrewAI AMP で確認する実行トレース(プロンプト、入力、出力、ツール呼び出し)です。

変数使用できる値(1.15.22-1.15.23)効果
CREWAI_DISABLE_TELEMETRYtrue、1、yes、on(大文字・小文字を問わず、前後の空白を除去)は無効化します。false、0、no、off、空の場合は有効のままです。それ以外は警告を出して無視されますCrewAI のテレメトリーを無効にします
CREWAI_DISABLE_TRACKING上記と同じCrewAI のテレメトリーを無効にします
OTEL_SDK_DISABLED同じ値で CrewAI のテレメトリーを無効化します。true(大文字・小文字を問わず)のみ、実行トレーシングもブロックし、OpenTelemetry SDK 自体を無効にします1 は完全な停止スイッチではありません
CREWAI_TRACING_ENABLEDtrue / 1 はトレーシングを強制的に有効にします。false / 0 は強制的に無効にします。yes、on、no、off は無視され、保存された同意設定に従いますプロセスごとのトレーシングゲート
Crew(...) または Flow の tracing=True/Falsebool環境変数より優先されます

トレーシングの優先順位は、tracing= 引数、CREWAI_TRACING_ENABLED、crewai traces enable|disable で保存された同意設定の順です。保存された同意設定はプロジェクトディレクトリ名ごと(または CREWAI_STORAGE_DIR ごと)に保存されるため、チェックアウトの名前を変更するとリセットされます。crewai traces status には、ログイン済みかどうかではなく、環境変数(未設定の場合は false)と保存された同意設定が表示されます。

ルール:

  • オブザーバビリティを無効にするのは、テスト時かつユーザーが選択した場合に限ってください。 切り替え設定は tests/conftest.py または CI テストジョブの環境に記述してください。本番環境のデバッグができなくなるため、プロジェクトの .env、main.py、デプロイ環境の環境変数には絶対に記述しないでください。
  • 値は true / false と記述してください。すべての無効化スイッチで完全に認識される表記は true のみです。また、CREWAI_TRACING_ENABLED は yes / no / on / off を無視します。
  • 1 回の実行をデバッグするには、CREWAI_TRACING_ENABLED=true crewai run または tracing=True を使用してください。トレースの送信先は、CrewAI の認証情報(CREWAI_USER_PAT または保存済みの crewai login。PAT は不要)があるかどうかによって異なります。
    • 認証情報がある場合: kickoff は最初にトレース許可を要求し、その後、ターミナルの有無にかかわらず、そのログインアカウントで CrewAI AMP にトレースをアップロードします。1.15.23 では View traces: リンクが表示されますが、1.15.22 では表示されません。許可の要求に失敗すると、クルーの実行前に kickoff が TraceGrantError を発生させます。
    • 認証情報がなく、1.15.23 で対話型ターミナルを使用する場合: トレーシングを有効にすると、同意したものとみなされます。トレースはプロンプトなしでエフェメラルトレースとしてアップロードされ、Ephemeral Execution Traces リンクが表示されます。同意設定は保存されません。
    • 認証情報がなく、1.15.22 で対話型ターミナルを使用する場合: 実行後に crewai が Share this execution trace with CrewAI? [y/N] と尋ねます。
    • 認証情報がなく、ターミナルを使用しない場合(CI、pytest、< /dev/null): どちらのリリースでもトレースは破棄されます。
    • スイッチを設定せずに初めて実行する場合: crewai はローカルで自動的に収集し、ターミナルで Share this execution trace with CrewAI? [y/N] (20s timeout) と尋ね、回答を同意設定として保存します。
  • 1.15.23 以降、crewai はすべての BaseLLM サブクラスの call をレート制限リトライでラップします。「rate limit」または 429 に言及するエラーを発生させるスタブは、エラーが表面化するまでに約 3 秒かけて 3 回呼び出されます。独自のレート制限処理をテストするには、別のエラー型を発生させるか、リトライ後の呼び出し回数をアサートしてください。
  • トレースにはタスクのテキスト、入力、出力が含まれます。データがマシンの外部に送信されても問題ない場合を除き、トレース付き実行では合成データを使用してください。

よくある間違い

症状原因対処
TypeError: ... call() got an unexpected keyword argument 'from_task'ドキュメントの例のシグネチャをそのまま使ってカスタム LLM を実装しているfrom_task、from_agent、response_model、**kwargs を受け取るようにします
CI で ValueError: OPENAI_API_KEY is required が発生するエージェントがデフォルトの gpt-4.1-mini を使い続けているすべてのエージェントにスタブを注入します(セクション 5)
テストが停止する、またはアカウントに料金が発生する実際の LLM 呼び出しが紛れ込んでいるconftest.py のソケットガードを使います
uv run test が OpenAI を呼び出すtest はスキャフォールドの crewai test スクリプトですuv run pytest を使います
キューの回答が別のタスクに割り当てられる追加のコンバーター呼び出しやガードレールの再試行がキューの項目を消費した有効な JSON をキューに入れ、response_model をキーにした responder を使います
out.pydantic is None なのに実行が「成功」するoutput_pydantic= を指定せずに response_model= を使っている、またはガードレールが (True, output) を返したoutput_pydantic を使い、ガードレールは (True, output.raw) を返すようにし、None でないことをアサートします
ガードレールが有効な JSON を初回で拒否するoutput.pydantic を読み取っていたが、これは None だったoutput.raw を解析します
リスナーまたはトレースに LLM 呼び出しが表示されないカスタム LLM がイベントを出力しないllm_call_context() 内でイベントを出力します(スタブは出力します)
テスト間でリスナーのカウントが増えるハンドラーがグローバルに登録されているcrewai_event_bus.scoped_handlers() と flush() を使います
token_usage が呼び出し回数の 2 倍になる、またはテストごとに増える1 つの LLM インスタンスを複数のエージェントで共有している、または複数の crew で再利用しているcrew ごとに新しいスタブを用意し、len(llm.calls) をアサートします
テスト後にユーザーデータディレクトリ内に新しいディレクトリができるインポート時に CREWAI_STORAGE_DIR が設定されていないconftest.py の先頭で絶対パスに設定します
pytest で TraceGrantError: AMP trace grant request failed (RuntimeError) が発生するトレーシングが有効で、保存済みの crewai login または CREWAI_USER_PAT があり、ソケットガードも有効になっているconftest.py で CREWAI_TRACING_ENABLED=false を設定します(テストのみ)
CI で crewai test が「成功」したのに、スコアがない実行が失敗しても終了コード 0 を返す出力にスコア表があるか確認します
OTEL_SDK_DISABLED=1 でもトレーシングが実行される1 はテレメトリーのみを無効にし、トレーシングもブロックするのは true のみCREWAI_TRACING_ENABLED=false を設定します(テストのみ)

チェックリスト

  • tests/stub_llm.py を references/stub_llm.py からコピーし、pytest を開発用依存関係に追加する
  • conftest.py で crewai をインポートする前に絶対パスの CREWAI_STORAGE_DIR を設定してネットワークをブロックする。可観測性の切り替え設定のコメントを外すのは、ユーザーが選択した場合のみ
  • すべてのエージェントの LLM を注入できるようにする(ファクトリーパラメーター、.crew() の後に差し替え、または crew クラスへのパッチ)
  • 各構造化タスクでスタブから有効な JSON を受け取り、テストで out.pydantic is not None をアサートする
  • 少なくとも 1 つのテストでプロンプトテキスト(入力、コンテキスト、ツール一覧)をアサートする
  • 各ガードレールに真理値表のテストと再試行テストを用意する
  • 各フロールーターの分岐にキックオフテストを用意し、主要なステップも直接呼び出す
  • 実際のモデルを使うテストに credentialed マークを付け、デフォルトの CI から除外する
  • 可観測性の切り替え設定はテスト設定にのみ記述し、.env やデプロイ環境変数には記述しない

リファレンス

関連スキル:

  • check-crewai-api — 現行の Agent/Task/Crew API、構造化出力フィールド、ガードレールの仕様
  • build-flow — フローの状態、ルーター、or_/and_、永続化
  • connect-tools-and-mcp — スタブが操作するツールの構築
  • design-task — テストする価値のあるガードレールと expected_output の記述
  • ask-docs — バージョンによって変わる可能性のある項目を、最新のドキュメントで確認

コメント