🤖 第十一章:CrewAI マルチエージェントクローラー:台湾全土のキャンプ場自動収集

基礎編では、地図上にマーカーを配置する方法を学びました。 しかし、もしあなたが「台湾で最も充実したキャンピングカーマップ」を作ろうとする起業家だとしたら、毎日「キャンプ場」をGoogle検索して、手作業で住所や緯度経度、価格をコピー&ペーストするようなことはできません。 そんなことをしていたら、500件のデータを入力し終わる前に、競合他社に市場を奪われてしまいます!

AI時代において、最も貴重な資源は「プログラミングスキル」ではなく、「クリーンで構造化された高品質データ」です。 この章では、Vibe Codingの最深部である自動データ収集の世界へご案内します。 従来のPythonクローラー(BeautifulSoup/Scrapy)は書きません - それらはサイトの改訂で簡単に壊れてしまいます。 代わりに、現在世界で最も注目されているマルチエージェントフレームワークCrewAIを導入します。

6000字に及ぶこの深い解析章では、原理からPrompt設計、フェイルセーフ機構、最終的な構造化出力まで、「疲れ知らずのAIボット軍団」を構築する方法をステップバイステップで解説します。


🏭 CrewAIとは?なぜ従来のクローラーは時代遅れなのか?

従来のクローリングロジックは「このURLへ行く -> titleというIDのHTMLタグを見つける -> 中のテキストを抽出する」というものでした。 しかし、現代のウェブサイトはほとんどがReact/Vueで書かれた動的ページで、Cloudflareなどのアンチクローリング対策も施されています。サイトのHTMLがclass名を少し変更しただけで、あなたのクローラーはすぐにエラーを吐いて停止し、バグ修正に一日を費やすことになります。

CrewAIの革命的思考: 私たちはもはや硬直したHTMLタグに依存しません。代わりに「AI従業員チーム(Crew)」を構築します。

  • Agent A (検索専門家): 「南投 おすすめキャンプ場」をGoogle検索し、上位10記事を読む
  • Agent B (データ抽出専門家): Agent Aが読んだ長文記事をLLMで理解し、「施設名、住所、価格帯」を正確に抽出
  • Agent C (座標変換専門家): 住所をGoogle Maps APIに渡し、正確な緯度経度(Latitude/Longitude)に変換

あなたは高レベルの「人間の指示」を出すだけで、これらのAIは自分たちで会議を開き、仕事を分配し、例外を処理し、最終的に完璧なJSONファイルを提出してくれます!


🛠️ 実践1: 最初のAI従業員(Agent)を作成

CrewAIを使用する前に、各AI従業員の役割(Role)、目標(Goal)、バックストーリー(Backstory)を明確に定義する必要があります。こ���は従業員を面接するようなもので、設定が詳細であればあるほど、出力は正確になります。

💡 Vibe Prompt 実践1: 「データ検索専門家」の定義

Agentの設定方法がわからない場合は、大規模言語モデルに書かせましょう!

[!IMPORTANT] 以下のPromptをAI(Claude 3.5やChatGPT-4oなど)に送信してください:

私はPythonのCrewAIフレームワークを使ってキャンプ場データ自動収集システムを開発しています。 最初のAgent(AI従業員)を定義するのを手伝ってください。 彼の任務は: 台湾の主要掲示板(PTT、Dcard、Mobile01)やブログからキャンプ場推薦記事を検索することです。 このAgentのPythonコードを以下を含めて提供してください: 1. role (役割名) 2. goal (最終目標) 3. backstory (背景ストーリー、AIに性格と専門性を与えるため) 4. verbose=True (詳細ログを有効化) 5. allow_delegation=False (仕事を他人に委任しない) backstoryは専門的で生き生きとした文体で書いてください。

🤖 AIが生成した完璧なAgent設定:

from crewai import Agent

# データ検索専門家��作成
search_expert = Agent(
    role='シニアキャンプ場情報スペシャリスト',
    goal='広大なインターネットから、台湾全土の最新・人気・隠れたキャンプ場情報、特に「車中泊」に適した穴場を発掘すること',
    backstory=(
        'あなたはアウトドア活動を愛し、15年のキャンプ経験を持つベテランであり、トップクラスのネット情報アナリストです。'
        '主要なアウトドア掲示板の専門用語に精通し、PTTのCamping板、Dcardの旅行板、数多くのキャンプブロガーの記事から、'
        '宣伝記事と本当に質の高いキャンプ場を見分けることができます。'
        '鋭い直感を持ち、美しい星空や清潔なシャワー施設を備えたキャンプ場を見逃すことはありません。'
    ),
    verbose=True,
    allow_delegation=False,
    # ここではネット検索可能なTool(SerperDevToolなど)を設定していると仮定
    # tools=[search_tool] 
)

🔍 深い解析: これがCrewAIの強力な点です。「if/else」ロジックを書くのではなく、「魂を吹き込む」のです。 このようなバックストーリーを与えると、検索やウェブページの閲覧時に、大規模言語モデルは自動的に「ベテランキャンパー」の視点に切り替わり、詐欺や低品質な情報を自動的にフィルタリングします。これは従来のPythonクローラーでは絶対に不可能なことです!


🎯 実践2: AIに具体的なタスク(Task)を割り当て

従業員ができたら、具体的な仕事(Task)を割り当てます。 Taskの定義も極めて正確で、「何をするか」「期待される出力形式」を明確にする必要があります。

💡 Vibe Prompt 実践2: 精密なクローリングタスクの定義

[!IMPORTANT] 以下のPromptをAIに送信してください:

search_expertという名前のAgentが既にあります。 CrewAIのTaskクラスを使って、具体的なタスクを割り当ててください。 タスク要件: 1. description (説明): 「新竹県」で評価の高い車中泊キャンプ場5つを検索。詳細な住所、価格、最大の特徴(雲海、蛍など)を含める 2. expected_output (期待出力): 構造化されたリストを出力 3. agent: search_expertに紐付け Pythonコードを提供し、expected_outputの重要性をコメントで説明してください

🤖 AIによる高度なタスク設計:

from crewai import Task

# タスク割り当て: 新竹県車中泊徹底調査
hsinchu_camping_task = Task(
    description=(
        '検索ツールを使用し、「新竹県」で現在最も人気があり評価の高い5つの車中泊/キャンプ場を特定してください。'
        '関連するブログ記事や掲示板評価を読み、以下の情報を抽出してください:'
        '1. キャンプ場の正式名称'
        '2. 完全な住所(少なくとも市区町村レベル)'
        '3. 価格帯(���: 1サイト1000~1200元)'
        '4. キャンプ場の最大の特徴(20字以内、例: 秋冬限定の絶景雲海)'
    ),
    expected_output=(
        '5つの新竹キャンプ場を含むMarkdown箇条書きリスト。'
        '各キャンプ場は以下の形式に厳密に従うこと:'
        '- 名称: [キャンプ場名]'
        '- 住所: [完全な住所]'
        '- 価格: [価格]'
        '- 特徴: [特徴]'
    ),
    agent=search_expert
)

/*
【AIによるexpected_outputの重要性の深い解析】:
大規模言語モデル(LLM)でdescriptionだけを与えると、AIは数千字にも及ぶ「散文」を返す可能性があります。
`expected_output`はCrewAIフレームワークがAI出力を「制約」するための緊箍呪です。
形式(MarkdownリストやJSON形式など)を厳密に定義すると、AIはこの条件を満たすために、長い思考結果を収束・整形します。
これは後でデータベースにデータを投入する際に生死を分けるステップです!
*/

🔄 実践3: 最も困難なステップ - 座標変換(Geocoding)

地図システムでは「住所」だけでは役に立ちません。Leaflet.js地図は緯度経度(Latitude, Longitude)しか理解しません。 「新竹県五峰郷桃山村...」のような住所を地図に渡しても、地図はエラーを吐くだけです。

そこで、第二のAI従業員地理座標変換専門家が必要です。 ただし、LLMの脳だけで緯度経度を推測させることはできません(これは幻覚(Hallucination)と呼ばれ、AIが推測した緯度経度は太平洋の真ん中を指すことがよくあります)。 彼は実際のツール(Tool)を使ってGoogle Maps APIを呼び出す方法を学ぶ必要があります。

💡 Vibe Prompt 実践3: AIに外部APIツールの使用方法を教える

これが上級Vibe Codingの核心です: API呼び出しを自分で書くのではなく、ツールボックスを作成し、AIに呼び出させます!

[!IMPORTANT] 以下のPromptをAIに送信してください:

CrewAIでカスタムツール(Custom Tool)を作成し、Agentが住所を緯度経度に変換できるようにしたいです。 Pythonの@toolデコレータを使ってgeocode_address(address: str)関数を書いてください。 ロジック: 1. requestsパッケージでGoogle Maps Geocoding APIを呼び出す 2. APIキーは環境変数GOOGLE_MAPS_API_KEYに格納 3. 変換成功時はlatとlngを含む辞書文字列を返す 4. 失敗時または住所が見つからない場合はエラーメッセージを返す 完全なコードとコメントを提供し、docstringの重要性を強調してください

🤖 AIが生成した最強ツール関数:

import os
import requests
from langchain.tools import tool

# 🔥 これが魂です!docstring(関数の下のコメント)はAIのために書かれます!
# AIはこのdocstringを読み、いつこのツールを使い、どんなパラメータを渡すべきかを決定します
@tool("住所緯度経度変換ツール")
def geocode_address(address: str) -> str:
    """
    これは強力なジオコーディングツールです。
    場所や住所の正確な緯度経度(LatitudeとLongitude)が必要な時に使用してください。
    パラメータは明確な文字列住所である必要があります(例: "台北市信義区市府路1号" または "武陵農場")。
    """
    api_key = os.environ.get("GOOGLE_MAPS_API_KEY")
    if not api_key:
        return "エラー: GOOGLE_MAPS_API_KEY環境変数が見つかりません"

    # Google Maps Geocoding APIエンドポイント
    url = f"https://maps.googleapis.com/maps/api/geocode/json?address={address}&key={api_key}&language=zh-TW"
    
    try:
        response = requests.get(url)
        data = response.json()
        
        # APIが正常にデータを返したか確認
        if data['status'] == 'OK':
            location = data['results'][0]['geometry']['location']
            lat = location['lat']
            lng = location['lng']
            # AI従業員に明確な文字列結果を返す
            return f"住所 '{address}' の緯度経度: 緯度 {lat}, 経度 {lng}"
        else:
            return f"'{address}' の緯度経度が見つかりません。API応答ステータス: {data['status']}"
            
    except Exception as e:
        return f"API呼び出し中にネットワークエラーが発生: {str(e)}"

🔍 深い解析: LangChainやCrewAIの@toolを使用する際、ほとんどの初心者は"""docstring"""を書くのを忘れます。 AI Agentの世界では、関数のコメントは人間のためではなく、AIのための説明書です! このツールが「緯度経度変換」のためにあることをAIに伝えないと、AIは住所を見てもこの強力なツールを使うべきだと気づきません。 これで、この@toolを第二のAgentに割り当てることができます。


🧩 実践4: 上流と下流を接続 - AI従業員に連携作業させる

「検索専門家(データ取得)」と「座標変換専門家(ツールで緯度経度変換)」ができました。 実際の会社では、これをパイプラインと呼びます。Agent Aがレポートを作成し、Agent Bに処理を渡します。

💡 Vibe Prompt 実践4: CrewチームとJSON構造出力の作成

最終的に必要なのはMarkdown文書���はなく、Supabaseデータベースに直接投入できるJSON形式(例: [{"name": "...", "lat": 24.1, "lng": 121.2}])です。

[!IMPORTANT] 以下のPromptをAIに送信してください:

上記のプロセスを統合し、完全なCrewAI実行スクリプトを作成してください。 1. geocode_expert(座標変換専門家)を作成し、先ほどのgeocode_addressツールを割り当て 2. 第二のタスク(format_to_json_task)を作成: geocode_expertが最初のタスク(hsinchu_camping_task)で整理された5つのキャンプ場リストを受け取る 3. ツールを使って各キャンプ場の緯度経度を調べ、最終的に標準JSON Array形式に統合 4. Crewを作成し、これら2つのタスクを順番に実行 5. 完全なPythonコードを提供し、このチームを起動する方法を示してください

🤖 AI自動化チームの完成形:

from crewai import Agent, Task, Crew, Process
import json

# 1. 第二の従業員: 座標変換とデータクリーニング専門家
geocode_expert = Agent(
    role='シニア地理データエンジニア',
    goal='人間が読む住所を、地図システムが必要とする緯度経度に正確に変換し、完璧なJSON構造を出力する',
    backstory='あなたは重度の完璧主義者で、括弧や引���符の誤りを許しません。JSONデータフォーマットの達人です。',
    verbose=True,
    tools=[geocode_address] # 先ほど作成したツールを割り当て!
)

# 2. 第二のタスク: 変換とフォーマット
format_to_json_task = Task(
    description=(
        '前のタスクで整理された5つのキャンプ場リストを受け取ってください。'
        '各キャンプ場について、[住所緯度経度変換ツール]を使用して正確なlatとlngを調べてください。'
        'ツールが住所を見つけられない場合、緯度経度は0とします。'
        '最後に、これら5つのキャンプ場の全情報をJSON Array形式に変換してください。'
    ),
    expected_output=(
        '完全に有効なJSON文字列で、Markdownマークアップ(```jsonなど)を含んではいけません。形式例:\n'
        '[\n'
        '  {\n'
        '    "name": "キャンプ場名",\n'
        '    "address": "住所",

完全なチュートリアルをロック解除

このチャプターは有料コンテンツです。プロジェクトに参加して、10以上の神レベルのPromptや実際のソースコード例を含む、5000字以上の深い分析をロック解除してください!