CrewAI 入門実践ガイド

open-sourcebeginner4分で読める2026/6/4

CrewAI入門:実践ガイド

私は3時間かけて、APIエンドポイントを幻覚し続けるCrewAIエージェントのデバッグを行い、そこで重要なことを学びました。CrewAIの強力さは、同時に最大の落とし穴でもあるということです。LangChainから移行してきた方、あるいはマルチエージェントシステムを始めたばかりの方なら、複数のAIエージェントを調整することが、単にプロンプトをつなげるだけではないこと、つまり依存関係、コンテキスト、障害モードの管理が重要であることにすぐに気づくでしょう。このガイドでは、自動ブログコンテンツ生成のための実際のCrewAIシステムを構築する中で学んだこと、具体的には壊れたコードとその修正方法について説明します。

課題:なぜ単一エージェントではダメなのか?

おそらく、単一のLLM呼び出しでブログ記事を生成しようとしたことがあるでしょう。それはうまくいきます。ただし、ファクトチェック、SEO最適化、フォーマットが必要になるまでは。単一エージェントはコンテキストを忘れたり、自己矛盾を起こしたり、無意味な内容を生成したりします。CrewAIは、人間のチームのように、専門化されたエージェントを定義してタスクを相互に渡すことで、この問題を解決します。しかし、ここに落とし穴があります。CrewAIのシンプルさは複雑さを隠しています。エージェントの役割とタスクを慎重に設計しないと、循環依存、無限ループ、あるいは作業の引き継ぎを拒否するエージェントが発生します。

ステップ1:CrewAIのインストール(そして落とし穴)

pip install crewai

これで必要なものがすべてインストールされると思っていました。間違いでした。 CrewAIはlangchainopenaiに依存していますが、最新バージョンではありません。Python 3.12を使用している場合、pydanticの競合が発生します。以下が私が使用した正確な修正方法です。

pip install crewai langchain==0.1.0 openai==1.6.1 pydantic==2.5.0

これらのバージョンを固定しないと、ImportError: cannot import name 'BaseModel' from 'pydantic'というエラーが発生します。これで30分無駄にしました。

ステップ2:エージェントを定義する(具体的にしないと苦労する)

CrewAIのエージェントは、rolegoalbackstoryを持つPythonクラスとして定義されます。backstoryはオプションですが、エージェントのトーンと動作を制御するため重要です。以下が私が最初に使用したものです。

from crewai import Agent

class Researcher(Agent):
    role = "リサーチャー"
    goal = "医療におけるAIに関する最近のニュースを見つける"
    backstory = "あなたは情報源を検証する几帳面な研究者です。"

これは動作しますが、漠然としすぎています。エージェントは一般的な応答を生成します。テスト後、制約を追加することを学びました。

class Researcher(Agent):
    role = "シニアヘルスケアAIリサーチャー"
    goal = "腫瘍学におけるAIに関する最近(2024年)の査読付き論文を3件見つける"
    backstory = """あなたは医療AI分野で10年の経験があります。
    常に具体的なPMIDまたはDOIリンクを引用します。
    情報源を捏造することは決してありません。"""

情報源を引用する明示的な指示年の制約に注目してください。これらがないと、私のエージェントは偽の論文を作り出しました。CrewAIは事実を検証しません。LLMを信頼するのです。

ステップ3:正しく連鎖するタスクを作成する

タスクは、ほとんどの人が失敗する部分です。タスクにはdescriptionexpected_outputagentがあります。ポイントは、タスクを以前の出力に依存させることです。私は2つのエージェントからなるパイプラインを構築しました。

from crewai import Task

research_task = Task(
    description="AIヘルスケアに関する最近の論文を3件見つける。タイトルとリンクのリストを出力する。",
    expected_output="タイトル、年、URLを含む3件の論文の箇条書きリスト",
    agent=researcher
)

writing_task = Task(
    description="""リサーチの出力に基づいて、調査結果を要約した500語のブログ記事を書く。
    提供された論文への引用を含めること。""",
    expected_output="見出しと引用元を含むマークダウン形式のブログ記事",
    agent=writer
)

ここにバグがあります。writing_taskresearch_taskの出力を明示的に参照していません。CrewAIはエージェントのメモリを通じて暗黙的にコンテキストを渡しますが、これは信頼性が低いです。私はタスク依存関係を使用して修正しました。

writing_task = Task(
    description="""前のタスクのリサーチ出力に基づいて、調査結果を要約した500語のブログ記事を書く。
    リサーチ出力は次の通りです:{research_output}""",
    expected_output="見出しと引用元を含むマークダウン形式のブログ記事",
    agent=writer,
    context=[research_task]  # 明示的な依存関係
)

contextパラメータは、出力が説明に注入されるタスクのリストです。これがないと、私のライターエージェントは独自のリサーチを幻覚していました。

ステップ4:Crewを実行する(そして障害に対処する)

次にCrewを作成して実行します。

from crewai import Crew

crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, writing_task],
    verbose=True  # デバッグに不可欠
)

result = crew.kickoff()
print(result)

初めて実行したときは動作しましたが、45秒かかり、API呼び出しで0.12ドルの費用がかかりました。詳細出力を見ると、リサーチャーエージェントが論文を見つけるために3回のAPI呼び出しを行い、その後ライターエージェントが記事を書くためにさらに2回の呼び出しを行っていることがわかりました。CrewAIのデフォルトは逐次実行で、各タスクは前のタスクが完了するのを待ちます。

欠点: 最初のタスクが失敗した場合(例:APIがエラーを返す)、Crew全体がクラッシュします。CrewAIには組み込みのリトライロジックがありません。私は簡単なリトライラッパーを追加しました。

import time

def safe_kickoff(crew, max_retries=3):
    for attempt in range(max_retries):
        try:
            return crew.kickoff()
        except Exception as e:
            print(f"試行 {attempt+1} が失敗しました:{e}")
            time.sleep(2 ** attempt)  # 指数バックオフ
    raise Exception("3回試行後もCrewは失敗しました")

ステップ5:実践的な最適化のヒント

1週間のテストの後、実際に信頼性を向上させたものを紹介します。

  1. エージェントのメモリを制限する:デフォルトでは、エージェントは会話全体を記憶します。長いタスクでは、コンテキストウィンドウが大きくなりすぎます。履歴が必要ないエージェントにはmemory=Falseを設定します。

    researcher = Researcher(memory=False)
    
  2. allow_delegation=Falseを使用する:デフォルトでは、エージェントは他のエージェントにタスクを委任できます。これによりループが発生します。複雑な階層を構築していない限り、無効にします。

    researcher = Researcher(allow_delegation=False)
    
  3. 結果をキャッシュする:CrewAIはデフォルトでLLM呼び出しをキャッシュしますが、エージェントごとです。同じCrewを2回実行すると、応答を再利用します。これはデバッグには便利ですが、本番環境では危険です。古いデータを提供する可能性があります。キャッシュを無効にするには:

    crew = Crew(agents=[...], tasks=[...], cache=False)
    
  4. トークン使用量を監視する:CrewAIはトークン数を公開しません。私は簡単なコールバックを追加しました。

    from langchain.callbacks import get_openai_callback
    with get_openai_callback() as cb:
        result = crew.kickoff()
        print(f"総トークン数:{cb.total_tokens}、費用:${cb.total_cost}")
    

CrewAIが教えてくれない最大の制限

コンテンツ生成のための5エージェントシステムを構築した後、壁にぶつかりました。CrewAIにはエージェント障害に対する組み込みのエラー回復機能がありません。 リサーチャーエージェントが無意味な出力を返した場合でも、ライターエージェントはそれを使用しようとします。唯一の修正方法は、タスクの説明で出力を検証することです。

research_task = Task(
    description="""3件の論文を見つける。3件見つけられない場合は、'NO_RESULTS'を出力し、
    理由を説明する。論文を捏造しないこと。""",
    ...
)

その後、ライティングタスクでこのセンチネル値をチェックします。ハッキーな方法ですが、機能します。

次のステップ:実際のプロジェクトを構築する

理論から始めないでください。私の壊れたサンプルをgithub.com/your-repo/crewai-blog-generatorからクローンし、意図的なバグを修正してください。READMEには、私が意図的に壊した3つのことが書かれています。

  1. ライタータスクのcontextパラメータの欠落
  2. API障害に対するリトライロジックの欠如
  3. allow_delegation=Trueによるエージェントの無限ループ

これらを修正したら、ライターの引用を検証するFactCheckerエージェントを追加してシステムを拡張してください。ドキュメントを1時間読むよりも、30分のデバッグでより多くを学べます。そして、何かを壊してしまったときは、詳細出力が最良の友であることを忘れないでください。Trueに設定して、エージェントのすべての決定を確認しましょう。

関連エージェント

L

LangChain

Framework for developing applications powered by language models.

続きを読む →