はじめに
最近、「AIエージェント」という言葉をよく耳にするようになりました。ChatGPTのようなLLM(大規模言語モデル)に「道具(ツール)」を持たせて、自律的にタスクを実行させる仕組みです。
「エージェントってなんだか難しそう...」と思っていましたが、実際に手を動かして作ってみると、意外とシンプルな仕組みで動いていることがわかりました。
この記事では、AIエージェント初心者の私が、LangChain/LangGraphを使ってWikipedia検索エージェントを作った過程を共有します。
この記事で学べること
- AIエージェントの基本的な仕組み
- ReAct(Reasoning + Acting)パターンとは何か
- LangChainでのツール(Tool)の作り方
- LangGraphでのエージェント構築方法
完成イメージ
最終的に、こんな感じで動くエージェントを作ります。
質問: Pythonとは何ですか?
エージェントの動き:
1. 思考: 「Pythonについて調べよう」
2. 行動: Wikipedia検索を実行
3. 観察: 検索結果を確認
4. 思考: 「もう少し詳しい情報が必要だな」
5. 行動: Wikipediaページの詳細を取得
6. 観察: 詳細情報を確認
7. 最終回答: 収集した情報をまとめて回答
回答: Pythonは、1991年にGuido van Rossumによって開発された
汎用プログラミング言語です...
前提知識
- Python基礎(関数、クラス、デコレータ)
- 環境変数の基本(
.envファイル) - OpenAI APIの基本的な使い方
環境構築
1. プロジェクト作成
mkdir research-agent
cd research-agent
2. Python仮想環境の作成(uvを使用)
uv venv
source .venv/bin/activate # macOS/Linux
# .venv\Scripts\activate # Windows
3. 依存パッケージのインストール
pyproject.tomlを作成します。
[project]
name = "research-agent"
version = "0.1.0"
description = "LangGraph/LangChainを使用したリサーチエージェント"
requires-python = ">=3.10"
dependencies = [
"langgraph>=1.0.0",
"langchain>=0.3.0",
"langchain-openai>=0.3.0",
"wikipedia>=1.4.0",
"pydantic>=2.0",
"python-dotenv>=1.0.0",
"rich>=13.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"ruff>=0.5.0",
]
インストール実行:
uv pip install -e ".[dev]"
4. 環境変数の設定
.envファイルを作成します。
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx # OpenAI APIキー
LANGCHAIN_TRACING_V2=true # LangSmith有効化(任意)
LANGCHAIN_API_KEY=lsv2_pt_xxxxx # LangSmith APIキー(任意)
LANGCHAIN_PROJECT=research-agent # プロジェクト名
ReActパターンとは?
エージェントの実装に入る前に、ReActパターンについて理解しておきましょう。
ReAct = Reasoning + Acting
ReActは、LLMに「思考」と「行動」を交互に繰り返させるパターンです。
Thought(思考): 何をすべきか考える
↓
Action(行動): ツールを使って情報収集
↓
Observation(観察): ツールの結果を確認
↓
Thought(思考): 次に何をすべきか考える
↓
... 繰り返し ...
↓
Final Answer(最終回答): 十分な情報が集まったら回答
なぜReActが有効なのか?
従来のLLMは、学習データに含まれる情報しか回答できませんでした。ReActパターンを使うと:
- 最新情報へのアクセス: 外部ツール(Wikipedia、検索エンジン等)で最新情報を取得
- 正確性の向上: 「思う」だけでなく「調べてから答える」
- 透明性: 思考過程が見えるので、なぜその回答になったか追跡可能
実装開始
ディレクトリ構成
research-agent/
├── src/
│ ├── __init__.py
│ ├── main.py # エントリーポイント
│ ├── config.py # 設定管理
│ ├── tools/
│ │ ├── __init__.py
│ │ └── wikipedia.py # Wikipediaツール
│ ├── agents/
│ │ ├── __init__.py
│ │ └── researcher.py # Researcherエージェント
│ └── graph/
│ ├── __init__.py
│ └── workflow.py # ワークフロー定義
├── tests/
│ └── test_tools/
│ └── test_wikipedia.py
├── pyproject.toml
└── .env
Step 1: 設定管理(config.py)
まず、環境変数を管理する設定クラスを作ります。
# src/config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
"""アプリケーション設定"""
# OpenAI Configuration
openai_api_key: str = ""
openai_model_main: str = "gpt-4o-mini" # コスト効率の良いモデル
# LangSmith Configuration(トレーシング用・任意)
langchain_tracing_v2: bool = True
langchain_api_key: str = ""
langchain_project: str = "research-agent"
class Config:
env_file = ".env"
settings = Settings()
ポイント:
-
pydantic_settingsのBaseSettingsを使うと、環境変数を自動で読み込んでくれます -
gpt-4o-miniはコストパフォーマンスが良く、学習用途に最適です
Step 2: Wikipediaツールの実装(wikipedia.py)
エージェントが使う「道具」を作ります。LangChainでは@toolデコレータを使って簡単にツールを定義できます。
# src/tools/wikipedia.py
import wikipedia
from langchain_core.tools import tool
@tool
def search_wikipedia(query: str, lang: str = "ja") -> str:
"""Wikipediaで情報を検索する
Args:
query: 検索クエリ
lang: 言語コード("ja"=日本語, "en"=英語)
Returns:
検索結果のサマリー
"""
wikipedia.set_lang(lang)
try:
# 検索を実行(最大3件)
search_results = wikipedia.search(query, results=3)
if not search_results:
return f"'{query}' に関するWikipediaページが見つかりませんでした。"
# 最初の結果のサマリーを取得
page = wikipedia.page(search_results[0], auto_suggest=False)
summary = wikipedia.summary(
search_results[0],
sentences=5, # 5文まで
auto_suggest=False
)
return f"## {page.title}\n\n{summary}\n\nURL: {page.url}"
except wikipedia.DisambiguationError as e:
# 曖昧さ回避ページの場合
if e.options:
summary = wikipedia.summary(
e.options[0],
sentences=5,
auto_suggest=False
)
return f"## {e.options[0]}\n\n{summary}"
return f"曖昧さ回避: {', '.join(e.options[:5])}"
except Exception as e:
return f"Wikipedia検索中にエラーが発生しました: {e}"
@tool
def get_wikipedia_page(title: str, lang: str = "ja") -> str:
"""Wikipediaページの詳細な内容を取得する
Args:
title: ページタイトル
lang: 言語コード("ja"=日本語, "en"=英語)
Returns:
ページの詳細内容
"""
wikipedia.set_lang(lang)
try:
page = wikipedia.page(title, auto_suggest=False)
# 長すぎる場合は最初の3000文字だけ
content = page.content[:3000] if len(page.content) > 3000 else page.content
return f"## {page.title}\n\n{content}\n\n...\n\nURL: {page.url}"
except wikipedia.DisambiguationError as e:
return f"曖昧さ回避ページです。以下から選択してください: {', '.join(e.options[:10])}"
except wikipedia.PageError:
return f"'{title}' のWikipediaページが見つかりませんでした。"
except Exception as e:
return f"Wikipedia取得中にエラーが発生しました: {e}"
ポイント:
-
@toolデコレータを付けると、LangChainがツールとして認識してくれます - docstringがLLMへの説明文になります。ここを丁寧に書くと、LLMが適切なタイミングでツールを選んでくれます
- エラーハンドリングは重要!曖昧さ回避ページ(DisambiguationError)への対応を忘れずに
Step 3: Researcherエージェントの実装(researcher.py)
ツールを使う「エージェント」を作ります。
# src/agents/researcher.py
from typing import Any
from langchain.agents import create_agent
from langgraph.graph.state import CompiledStateGraph
from src.config import settings
from src.tools.wikipedia import get_wikipedia_page, search_wikipedia
# エージェントへの指示(システムプロンプト)
RESEARCHER_SYSTEM_PROMPT = """あなたは優秀なリサーチアシスタントです。
ユーザーの質問に対して、Wikipediaを使って正確な情報を収集します。
## 行動指針
1. まず search_wikipedia で関連する情報を検索する
2. 必要に応じて get_wikipedia_page で詳細情報を取得する
3. 複数の関連トピックを調査して、包括的な情報を集める
4. 収集した情報を整理して、要点をまとめる
## 注意事項
- 事実に基づいた情報のみを提供する
- 情報源が不明な場合は、その旨を明記する
- 専門用語は必要に応じて説明を加える
"""
def create_researcher_agent() -> CompiledStateGraph[Any, Any, Any, Any]:
"""Researcherエージェントを作成する"""
# エージェントが使えるツールのリスト
tools = [search_wikipedia, get_wikipedia_page]
# エージェントを作成
agent = create_agent(
model=f"openai:{settings.openai_model_main}", # "openai:gpt-4o-mini"
tools=tools,
system_prompt=RESEARCHER_SYSTEM_PROMPT,
)
return agent
ポイント:
-
create_agent()は、LangChain 1.0で推奨される方法です - モデルは
"openai:gpt-4o-mini"のように文字列で指定します - システムプロンプトでエージェントの「性格」や「行動指針」を定義します
Step 4: ワークフローの実装(workflow.py)
エージェントを実行するワークフローを定義します。
# src/graph/workflow.py
from typing import Any
from langchain_core.messages import HumanMessage
from langgraph.graph.state import CompiledStateGraph
from src.agents.researcher import create_researcher_agent
def create_research_workflow() -> CompiledStateGraph[Any, Any, Any, Any]:
"""リサーチワークフローを作成する"""
return create_researcher_agent()
def run_research_sync(query: str) -> str:
"""リサーチを同期的に実行する
Args:
query: ユーザーの質問
Returns:
リサーチ結果
"""
# ワークフローを作成
workflow = create_research_workflow()
# 実行(HumanMessageとしてクエリを渡す)
result = workflow.invoke({"messages": [HumanMessage(content=query)]})
# 最後のAIメッセージを取得
ai_messages = [
m for m in result["messages"]
if hasattr(m, "content") and m.type == "ai"
]
if ai_messages:
return str(ai_messages[-1].content)
return "リサーチ結果を取得できませんでした。"
ポイント:
-
HumanMessageでユーザーの質問をエージェントに渡します - エージェントの応答は
messagesリストに蓄積されます - 最後の
AIMessageが最終回答になります
Step 5: CLIの実装(main.py)
最後に、コマンドラインから実行できるようにします。
# src/main.py
from dotenv import load_dotenv
from rich.console import Console
from rich.panel import Panel
# 環境変数を読み込み(最初に実行)
load_dotenv()
from src.graph.workflow import run_research_sync
console = Console()
def main():
"""メイン関数"""
console.print(
Panel.fit(
"[bold blue]Research Agent[/bold blue]\n"
"Wikipediaを使ったリサーチエージェント",
border_style="blue",
)
)
while True:
# ユーザー入力を取得
console.print("\n[bold green]質問を入力してください[/bold green]")
console.print("[dim](終了: quit または exit)[/dim]")
query = console.input("[bold]> [/bold]").strip()
if query.lower() in ["quit", "exit", "q"]:
console.print("\n[yellow]終了します。[/yellow]")
break
if not query:
continue
# リサーチ実行
console.print("\n[dim]リサーチ中...[/dim]\n")
try:
result = run_research_sync(query)
console.print(Panel(result, title="回答", border_style="green"))
except Exception as e:
console.print(f"[red]エラーが発生しました: {e}[/red]")
if __name__ == "__main__":
main()
ポイント:
-
load_dotenv()は他のimportより前に実行する必要があります -
richライブラリで見やすいCLI表示を実現しています
実行してみよう
python -m src.main
╭──────────────────────────────────────╮
│ Research Agent │
│ Wikipediaを使ったリサーチエージェント │
╰──────────────────────────────────────╯
質問を入力してください
(終了: quit または exit)
> Pythonとは何ですか?
リサーチ中...
╭───────────────── 回答 ─────────────────╮
│ Pythonは、1991年にオランダ人の │
│ グイド・ヴァン・ロッサムによって │
│ 開発された汎用プログラミング言語です。│
│ │
│ ## 特徴 │
│ - 読みやすく書きやすい文法 │
│ - 豊富な標準ライブラリ │
│ - オブジェクト指向プログラミング対応 │
│ - インタプリタ型言語 │
│ │
│ 情報源: https://ja.wikipedia.org/... │
╰────────────────────────────────────────╯
テストを書く
ツールが正しく動作するか、テストを書いて確認しましょう。
# tests/test_tools/test_wikipedia.py
import pytest
from src.tools.wikipedia import search_wikipedia, get_wikipedia_page
class TestSearchWikipedia:
"""search_wikipedia のテスト"""
def test_search_existing_topic(self):
"""存在するトピックの検索"""
result = search_wikipedia.invoke({"query": "Python"})
assert "Python" in result
assert "エラー" not in result
def test_search_nonexistent_topic(self):
"""存在しないトピックの検索"""
result = search_wikipedia.invoke(
{"query": "xyznonexistenttopic12345"}
)
assert "見つかりませんでした" in result or "エラー" in result
def test_search_with_english(self):
"""英語での検索"""
result = search_wikipedia.invoke(
{"query": "Quantum computing", "lang": "en"}
)
assert "quantum" in result.lower() or "Quantum" in result
class TestGetWikipediaPage:
"""get_wikipedia_page のテスト"""
def test_get_existing_page(self):
"""存在するページの取得"""
result = get_wikipedia_page.invoke(
{"title": "Python", "lang": "en"}
)
assert "Python" in result
assert "エラー" not in result
テスト実行:
pytest tests/test_tools/test_wikipedia.py -v
LangSmithでトレーシング
環境変数を設定していれば、LangSmithでエージェントの動作を可視化できます。
- LangSmithにアクセス
- プロジェクト「research-agent」を確認
- エージェントの思考過程がすべて記録されています
これにより、エージェントが「なぜそのツールを選んだか」「どんな思考をしたか」が分かります。デバッグに非常に便利です。
学んだこと・つまずいたポイント
学んだこと
-
AIエージェントは意外とシンプル
- LLM + ツール + ループ、これだけで動く
- LangChainが複雑な部分を隠蔽してくれる
-
システムプロンプトが重要
- エージェントの「性格」はプロンプトで決まる
- 行動指針を明確に書くと、期待通りに動く
-
ツールのdocstringが命
- LLMはdocstringを読んでツールを選ぶ
- 説明が曖昧だと、意図しないタイミングで呼ばれる
つまずいたポイント
-
LangChain 1.0の変更
- 古いチュートリアルは
langchain.agents.initialize_agentを使っている - 現在は
langchain.agents.create_agentを使う
- 古いチュートリアルは
-
環境変数の読み込み順序
-
load_dotenv()は他のimportより先に実行する - そうしないと、設定クラスが空の環境変数を読んでしまう
-
-
Wikipedia APIの例外
- 曖昧さ回避ページで
DisambiguationErrorが発生する - 適切にハンドリングしないとエージェントが止まる
- 曖昧さ回避ページで
次のステップ
このPhase 1のエージェントには、いくつかの課題があります:
- 単一エージェント: すべてを1つのエージェントが担当
- レビューなし: 生成内容のチェック機能がない
- 人間の介入なし: 完全自動で進んでしまう
これらを解決するために、
- マルチエージェント化(Supervisor/Writer/Reviewer)
- Human-in-the-Loop(人間がレビュー・介入)
などの方法が考えられます。
まとめ
この記事では、LangChain/LangGraphを使ってシンプルなAIエージェントを実装しました。
作ったもの:
- Wikipedia検索ツール(2つ)
- ReActパターンのResearcherエージェント
- シンプルなCLI
学んだこと:
- ReActパターン(Reasoning + Acting)
-
@toolデコレータでのツール定義 -
create_agent()でのエージェント作成 - LangSmithでのトレーシング
AIエージェントは難しそうに見えますが、基本的な仕組みはシンプルです。ぜひ自分でも作ってみてください!