AIエージェントの構築方法:開発者向けステップバイステップチュートリアル

開発者向けステップバイステップチュートリアル: AIエージェントをアーキテクチャから本番まで構築する方法を、実際に動作するコードと実世界で使える適切なツール選択とともに学びます。

by AnyCap

AIエージェントループアーキテクチャ図:Think、Act、Observe、Repeatのサイクル

AIエージェントの構築は複雑に聞こえます。すべてをゼロから構築しようとすれば、実際に複雑になり得ます。しかし、コアパターンはシンプルです。言語モデルにツールへのアクセスを与え、どのツールをいつ使うかを自分で判断させ、目標に到達するまで繰り返すだけです。

このガイドでは、アーキテクチャから動作するコードまで、AIエージェントをステップバイステップで構築する方法を解説します。最後には、ウェブ検索、画像生成、結果の配信までを単一のCLIで実行できる実用的なエージェントが完成します。


AIエージェントとは?

コードを書く前に、何を構築するのかを定義しましょう。

AIエージェントとは、目標を受け取り、一連のアクションを計画し、ツールを使ってそれらのアクションを実行し、結果を観察して適応するシステムです。単一のプロンプトに応答するチャットボットとは異なり、エージェントは数十回のツール呼び出しにわたって自律的に目標に向かって作業します。

チャットボット: 「この記事を要約して。」 → 要約を返す。 AIエージェント: 「このトピックを調査し、最良の情報源を見つけ、レポートを作成して公開して。」 → 計画、検索、読解、執筆、公開。

エージェントの力はツール、つまり呼び出せる能力から生まれます。ツールがなければ、エージェントは長いプロンプトを持つ言語モデルに過ぎません。ツールがあれば、世界と対話できます。


AIエージェントのアーキテクチャ

すべてのエージェントは同じ基本ループに従います。

┌─────────────────────────────────────────┐
│             エージェントループ             │
│                                          │
│  1. 目標を受け取る                        │
│  2. 考える: 次に何をすべきか?             │
│  3. 行動する: ツールを選択して呼び出す      │
│  4. 観察する: 結果はどうだったか?          │
│  5. 判断する: 目標は達成されたか?          │
│     → いいえ?ステップ2に戻る。             │
│     → はい?結果を返す。                   │
└─────────────────────────────────────────┘

これはReActパターン(推論+行動)と呼ばれます。LangChain、CrewAI、AutoGen、OpenAI Agents SDKなど、すべてのエージェントフレームワークはこのループの何らかのバージョンを実装しています。

必要な3つのコンポーネント:

  1. 言語モデル — 推論エンジン(Claude、GPT-4o、Gemini)
  2. ツールセット — エージェントができること(検索、クロール、生成、保存、公開)
  3. オーケストレーター — 次にどのツールを呼び出すかを決定するループ

ステップ1: ツールを選ぶ

ツールはエージェントが達成できることを定義します。「エージェントは現実世界で何をする必要があるか?」という質問から始めましょう。

一般的なエージェントツール:

機能 重要な理由
ウェブ検索 調査、事実確認、競合分析
ウェブクローリング 特定ページの深い読み取り、データ抽出
画像生成 ビジュアル、図、アセットの作成
ファイル保存 永続的な出力、共有、アセット管理
ウェブ公開 完成した作業をライブページとして配信
コード実行 スクリプト実行、データ処理、自動化

初心者が最もよく犯すミス: エージェントにツールが少なすぎて、なぜ何も達成できないのか不思議に思うこと。検索のみのエージェントはリンクしか返せません。検索+クロール+保存+公開できるエージェントは、完成した成果物を生み出せます。

ツールをプロビジョニングする最もシンプルな方法: 検索、クロール、画像生成、保存、公開を1つのインターフェースの背後にバンドルした統合機能レイヤーを使用すること。5つの別々のAPIを設定し、5つの認証情報セットを管理する代わりに、エージェントは1つの認証フローで1つのCLIを呼び出すだけです。これにより、エージェントループはシンプルに保たれ、トークンオーバーヘッドも低くなります。


ステップ2: エージェントのシステムプロンプトを定義する

システムプロンプトはエージェントの「取扱説明書」です。モデルに自分が何者で、どんなツールを持ち、それらをどう使うべきかを伝えます。

良いシステムプロンプトには4つの部分があります:

  1. アイデンティティ: エージェントが何であるか
  2. 目標: 何を達成すべきか
  3. ツール: 何をいつ使えるか
  4. 制約: やってはいけないこと

例:

あなたはリサーチエージェントです。与えられたトピックを徹底的に調査し、
包括的なレポートを作成することが目標です。

以下のツールにアクセスできます:
- search: ウェブで情報を見つけます。広範な調査に使用します。
- crawl: 特定のウェブページを全文読み取ります。有望な情報源を
  見つけた後に使用します。
- drive upload: レポートとアセットを永続的に保存します。
- page deploy: 最終レポートをウェブページとして公開します。

ワークフロー:
1. 全体像を理解するために広範な検索クエリから始めます。
2. 最も権威のある情報源を特定し、クロールします。
3. 調査結果を構造化されたレポートにまとめます。
4. 安全に保管するためにレポートをDriveにアップロードします。
5. レポートを公開ページとしてデプロイします。

制約:
- 常に情報源を引用してください。
- ある情報源が別の情報源と矛盾する場合は、さらに調査してください。
- 決して情報を捏造しないでください。

ステップ3: エージェントループを実装する

以下はPythonによる最小限のエージェントループです。このパターンは本番環境でそのまま使えます — 考え、行動し、観察し、繰り返す:

import subprocess
import json

def call_tool(tool_name, **params):
    """ツールを実行して結果を返します。"""
    if tool_name == "search":
        result = subprocess.run(
            ["anycap", "search", "--prompt", params["query"]],
            capture_output=True, text=True
        )
        return json.loads(result.stdout)
    elif tool_name == "crawl":
        result = subprocess.run(
            ["anycap", "crawl", params["url"]],
            capture_output=True, text=True
        )
        return result.stdout
    elif tool_name == "drive_upload":
        subprocess.run(
            ["anycap", "drive", "upload", params["file"]],
            capture_output=True
        )
        return {"status": "uploaded", "file": params["file"]}
    elif tool_name == "page_deploy":
        result = subprocess.run(
            ["anycap", "page", "deploy", params["file"]],
            capture_output=True, text=True
        )
        return json.loads(result.stdout)

# エージェントループ
def agent_loop(goal, tools, max_steps=20):
    memory = [{"role": "system", "content": SYSTEM_PROMPT}]
    memory.append({"role": "user", "content": goal})

    for step in range(max_steps):
        response = llm_call(memory, tools)

        if response.get("done"):
            return response["result"]

        tool_name = response["tool"]
        tool_params = response["params"]
        result = call_tool(tool_name, **tool_params)

        memory.append({"role": "assistant", "content": str(response)})
        memory.append({"role": "tool", "content": str(result)})

    return "エージェントが目標を完了せずに最大ステップ数に達しました。"

ステップ4: 失敗を処理する

エージェントは失敗します。重要なのはその対処法です。以下の安全策を最初から組み込んでください:

タイムアウト保護

エージェントを無限にループさせないでください。最大ステップ数と時間制限を設定します。エージェントがいずれかを超えた場合、静かにクラッシュするのではなく、それまでに得た結果を返すべきです。

ツール失敗時の回復

ツール呼び出しが失敗した場合 — URLに到達できない、APIがエラーを返す — エージェントはエラーメッセージを受け取り、次に何をすべきか判断すべきです。エージェントからエラーを隠さないでください。何かがうまくいかなかったことを知る必要があります。

try:
    result = call_tool(tool_name, **tool_params)
except Exception as e:
    result = {"error": str(e), "suggestion": "別のアプローチを試してください"}

コスト意識

すべての検索、すべてのクロール、すべての画像生成にはクレジットがかかります。エージェントに予算を与え、コストを意識させてください。簡単な質問に答えるために100回の検索を消費するエージェントは、設計が悪いと言えます。


ステップ5: デモと本番の違い

デモエージェントと有用なエージェントの違いは現実世界のツールアクセスです。デモエージェントはテキストを返します。有用なエージェントは公開されたレポート、生成された画像、デプロイされたウェブページを返します。

本番エージェントには5つの能力が必要です: ウェブ検索、特定ページの読み取り、ビジュアル生成、出力の永続的保存、完成した作業の公開。エージェントのコードはシンプルに保たれます — 必要なツールを呼び出すだけです。API統合、認証、エラー処理の複雑さはランタイムにあり、エージェントループにはありません。


エージェント構築時のよくあるミス

ミス1: 終了条件がない

明確な「完了」シグナルのないエージェントは永遠にループします。成功を明示的に定義してください: エージェントは特定の出力(レポート、デプロイされたページ)を生成したとき、または目標が到達不可能であることを確認したときに完了です。

ミス2: ツールが少なすぎる

「検索のみ」のエージェントは、ただの検索エンジンに過ぎません。エージェントに完全なパイプラインを与えてください: 見つける → 読む → 作る → 保存する → 届ける。

ミス3: ツール結果を無視する

エージェントは時にツールを呼び出して出力を無視し、結果がどうだったかを想定して進めてしまいます。すべてのツール結果を次の判断に必ず組み込むように強制してください。

ミス4: ループの過剰設計

ほとんどのユースケースでは、カスタムオーケストレーションフレームワークは必要ありません。良いシステムプロンプトと有能なツールを備えたシンプルなReActループが、タスクの80%で複雑なマルチエージェント構成を上回ります。


チュートリアルから本番へ

ここで構築したエージェントは出発点です。本番-readyにするために:

  • ログを追加: デバッグのために、すべてのツール呼び出し、その結果、エージェントの推論を記録します。
  • ヒューマンインザループを追加: 公開やメール送信などの高リスクなアクションには、人間の承認を必要とします。
  • モニタリングを追加: 成功率、タスクあたりの平均ステップ数、ツール呼び出しの分布を追跡し、ボトルネックを特定します。
  • システムプロンプトを反復改善: プロンプトはエージェントの頭脳です。実際の使用パターンに基づいて調整します。

AIエージェントの構築は複雑なアーキテクチャが目的ではありません。推論エンジンに適切なツールと明確な目標を与えることです。シンプルに始めましょう: 1つのモデル、3〜5のツール、基本ループ。シンプルなバージョンが破綻したときに初めて、複雑さを追加してください。