免責事項: 本章は2026年初頭時点の実践的なLLMアプリケーションパターンを解説しています。エコシステムは急速に進化しており、現在のベストプラクティスとAPI仕様は常に公式ドキュメントで確認してください。
1. RAG(検索拡張生成)
RAG(Retrieval-Augmented Generation)は、外部知識でLLMを強化し、プライベートデータ、最新情報、ドメイン固有コンテンツについて正確な応答を可能にします。LLMの根本的な制限である、古くなる静的な訓練データの問題に対処します。
1.1 RAGアーキテクチャ
flowchart LR
subgraph "インデックスパイプライン"
D[ドキュメント] --> C[チャンキング]
C --> E[埋め込み]
E --> V[(ベクトルDB)]
end
subgraph "クエリパイプライン"
Q[ユーザークエリ] --> QE[クエリ埋め込み]
QE --> R[検索]
V --> R
R --> Context[取得コンテキスト]
Context --> LLM[LLM生成]
Q --> LLM
LLM --> A[回答]
end
style V fill:#e3f2fd
style LLM fill:#fff3e0
1.2 RAGシステムの構築
from typing import List, Optional
import numpy as np
from dataclasses import dataclass
@dataclass
class Document:
"""メタデータ付きドキュメントチャンクを表す"""
content: str
metadata: dict
embedding: Optional[np.ndarray] = None
class RAGSystem:
"""
現代のベストプラクティスを取り入れた本番対応RAG実装
"""
def __init__(
self,
embedding_model: str = "text-embedding-3-large",
llm_model: str = "gpt-4o",
chunk_size: int = 512,
chunk_overlap: int = 50,
top_k: int = 5
):
self.embedding_model = embedding_model
self.llm_model = llm_model
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
self.top_k = top_k
self.documents: List[Document] = []
def retrieve(
self,
query: str,
top_k: Optional[int] = None
) -> List[Document]:
"""
コサイン類似度を使用して関連ドキュメントを検索
"""
from openai import OpenAI
client = OpenAI()
top_k = top_k or self.top_k
# クエリを埋め込み
response = client.embeddings.create(
model=self.embedding_model,
input=[query]
)
query_embedding = np.array(response.data[0].embedding)
# 類似度を計算
similarities = []
for doc in self.documents:
if doc.embedding is not None:
sim = np.dot(query_embedding, doc.embedding) / (
np.linalg.norm(query_embedding) * np.linalg.norm(doc.embedding)
)
similarities.append((sim, doc))
# ソートしてtop-kを返す
similarities.sort(key=lambda x: x[0], reverse=True)
return [doc for _, doc in similarities[:top_k]]
def generate(
self,
query: str,
context_documents: List[Document]
) -> str:
"""
検索されたコンテキストを使用して回答を生成
"""
from openai import OpenAI
client = OpenAI()
# コンテキスト文字列を構築
context = "\n\n---\n\n".join([
f"[ソース: {doc.metadata.get('source', '不明')}]\n{doc.content}"
for doc in context_documents
])
system = """あなたは提供されたコンテキストに基づいて質問に答える有用なアシスタントです。
回答がコンテキスト内に見つからない場合は、明確にそう述べてください。
回答で使用したソースを常に引用してください。"""
messages = [
{"role": "system", "content": system},
{"role": "user", "content": f"""コンテキスト:
{context}
質問: {query}
上記のコンテキストに基づいて回答してください。"""}
]
response = client.chat.completions.create(
model=self.llm_model,
messages=messages,
temperature=0.7
)
return response.choices[0].message.content
def query(self, question: str) -> dict:
"""
エンドツーエンドのRAGクエリパイプライン
"""
retrieved_docs = self.retrieve(question)
answer = self.generate(question, retrieved_docs)
return {
"question": question,
"answer": answer,
"sources": [
{"content": doc.content[:200] + "...", "metadata": doc.metadata}
for doc in retrieved_docs
]
}
1.3 高度なRAG技術
RAGの進化(2024-2026)
| 技術 | 説明 | ユースケース |
|---|---|---|
| ナイーブRAG | 基本的な検索→生成 | シンプルなQ&A |
| ハイブリッド検索 | ベクトル + キーワード(BM25)融合 | 複合クエリ |
| リランキング | クロスエンコーダーで結果を再順位付け | 高精度が必要な場合 |
| エージェント型RAG | 推論を伴う多段階検索 | 複雑なクエリ |
| GraphRAG | 知識グラフ + ベクトル検索 | 関係性クエリ |
2. Model Context Protocol(MCP)
MCPは、Anthropicが開発したLLMを外部ツールやデータソースに接続するためのオープン標準です。標準化されたインターフェースを通じて、AIモデルが訓練データを超えた機能にアクセスできるようにする統一プロトコルを提供します。
2.1 MCPアーキテクチャ
flowchart TB
subgraph "MCPホスト(クライアント)"
App[アプリケーション]
Client[MCPクライアント]
App --> Client
end
subgraph "MCPサーバー"
Server[MCPサーバー]
subgraph "機能"
T[ツール]
R[リソース]
P[プロンプト]
end
Server --> T
Server --> R
Server --> P
end
Client <-->|JSON-RPC| Server
subgraph "外部システム"
DB[(データベース)]
API[API]
FS[ファイルシステム]
end
T --> DB
T --> API
R --> FS
MCPの主要概念
- ツール: LLMが呼び出せる関数(検索、計算、データベースクエリなど)
- リソース: LLMが読み取れるデータ(ファイル、データベースレコードなど)
- プロンプト: 引数付きの再利用可能なプロンプトテンプレート
- サンプリング: サーバーからクライアントへのLLM補完リクエスト
2.2 MCPサーバーの構築
"""
MCPサーバー実装例
ファイル操作とウェブ検索のツールを提供し、
MCPプロトコルのパターンを示す
"""
from mcp.server import Server
from mcp.types import Tool, TextContent
import asyncio
from pathlib import Path
server = Server("example-mcp-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
"""利用可能なツールを定義"""
return [
Tool(
name="read_file",
description="ファイルシステムからファイルの内容を読み取る",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "読み取るファイルのパス"
}
},
"required": ["path"]
}
),
Tool(
name="web_search",
description="ウェブで情報を検索する",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "検索クエリ"
}
},
"required": ["query"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""ツールの実行を処理"""
if name == "read_file":
path = Path(arguments["path"])
if not path.exists():
return [TextContent(type="text", text=f"エラー: ファイルが見つかりません: {path}")]
content = path.read_text(encoding="utf-8")
return [TextContent(type="text", text=content)]
elif name == "web_search":
query = arguments["query"]
# 検索APIを実装
return [TextContent(type="text", text=f"'{query}'の検索結果...")]
return [TextContent(type="text", text=f"不明なツール: {name}")]
3. AIエージェント
AIエージェントは、推論、計画、行動を行って目標を達成するLLM駆動システムです。LLMの推論能力とツール使用、メモリ、多段階実行を組み合わせます。
3.1 エージェントアーキテクチャ
flowchart TB
subgraph "エージェントコア"
LLM[LLM頭脳]
P[プランナー]
E[実行者]
R[推論者]
end
subgraph "メモリ"
WM[ワーキングメモリ]
LTM[長期メモリ]
end
subgraph "ツール"
T1[ウェブ検索]
T2[コード実行]
T3[ファイル操作]
T4[API呼び出し]
end
User[ユーザー目標] --> P
P --> LLM
LLM --> R
R --> E
E --> T1 & T2 & T3 & T4
E --> WM
WM --> LTM
LTM --> LLM
3.2 ReActエージェントパターン
"""
ReActエージェント: 推論 + 行動
ReActパターンは推論トレースと行動を交互に行い、
モデルが反復的に計画、行動、観察できるようにする
"""
from typing import List, Dict, Any
from dataclasses import dataclass, field
import json
@dataclass
class AgentState:
"""エージェント実行状態"""
goal: str
history: List[Dict[str, Any]] = field(default_factory=list)
final_answer: str = None
iterations: int = 0
max_iterations: int = 10
class ReActAgent:
"""
ReActエージェント実装
パターン: 思考 -> 行動 -> 観察 -> 繰り返し
"""
def __init__(
self,
llm_client,
tools: Dict[str, callable],
model: str = "claude-sonnet-4-20250514"
):
self.llm = llm_client
self.tools = tools
self.model = model
self.system_prompt = """あなたは問題を段階的に解決する有用なAIエージェントです。
各ステップで以下の形式で出力してください:
思考: <次に何をすべきかの推論>
行動: <ツール名>
行動入力: <ツールへのJSON入力>
または最終回答がある場合:
思考: <推論>
最終回答: <完全な回答>
利用可能なツール:
{tool_descriptions}
ルール:
1. 行動の前に必ず考える
2. 観察を次のステップに活用する
3. ツールが失敗したら別のアプローチを試す
4. 十分な情報が得られたら最終回答を提供する"""
async def run(self, goal: str) -> str:
"""目標達成のためにエージェントを実行"""
state = AgentState(goal=goal)
messages = [{"role": "user", "content": f"目標: {goal}"}]
while state.iterations < state.max_iterations:
state.iterations += 1
response = self.llm.messages.create(
model=self.model,
system=self.system_prompt.format(
tool_descriptions=self._format_tools()
),
max_tokens=2048,
messages=messages
)
parsed = self._parse_response(response.content[0].text)
if 'final_answer' in parsed:
return parsed['final_answer']
if 'action' in parsed:
observation = self._execute_tool(
parsed['action'],
parsed.get('action_input', '{}')
)
messages.append({"role": "assistant", "content": response.content[0].text})
messages.append({"role": "user", "content": f"観察: {observation}"})
return "エージェントは回答を見つけられませんでした。"
def _format_tools(self) -> str:
return "\n".join([f"- {name}: {tool.__doc__}" for name, tool in self.tools.items()])
def _parse_response(self, text: str) -> dict:
# 応答を構造化形式にパース
result = {}
for line in text.split('\n'):
if line.startswith('思考:'):
result['thought'] = line[3:].strip()
elif line.startswith('行動:'):
result['action'] = line[3:].strip()
elif line.startswith('行動入力:'):
result['action_input'] = line[5:].strip()
elif line.startswith('最終回答:'):
result['final_answer'] = line[5:].strip()
return result
def _execute_tool(self, name: str, input_str: str) -> str:
if name not in self.tools:
return f"エラー: 不明なツール '{name}'"
try:
return str(self.tools[name](**json.loads(input_str)))
except Exception as e:
return f"エラー: {e}"
4. マルチモーダルLLM
マルチモーダルLLMは、テキスト、画像、音声、動画など複数のタイプのコンテンツを処理・生成します。これにより、視覚的な質問応答、テキストからの画像生成、ドキュメント理解などの強力なアプリケーションが可能になります。
4.1 ビジョン言語モデル
| モデル | 機能 | コンテキスト | プロバイダー |
|---|---|---|---|
| GPT-4o | テキスト、画像、音声の入出力 | 128K | OpenAI |
| Claude 3.5 Sonnet | テキスト、画像入力 | 200K | Anthropic |
| Gemini 2.0 Flash | テキスト、画像、音声、動画 | 1M |
4.2 画像の操作
"""
画像を使用したマルチモーダルLLMの使用例
"""
import anthropic
import base64
from pathlib import Path
def encode_image(image_path: str) -> tuple[str, str]:
"""画像をbase64エンコードしメディアタイプを検出"""
path = Path(image_path)
suffix = path.suffix.lower()
media_types = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.webp': 'image/webp'
}
media_type = media_types.get(suffix, 'image/png')
with open(path, 'rb') as f:
data = base64.standard_b64encode(f.read()).decode('utf-8')
return data, media_type
def analyze_image(image_path: str, question: str) -> str:
"""Claude Visionで画像を分析"""
client = anthropic.Anthropic()
image_data, media_type = encode_image(image_path)
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": media_type,
"data": image_data
}
},
{
"type": "text",
"text": question
}
]
}
]
)
return message.content[0].text
# 使用例
result = analyze_image(
"screenshot.png",
"このスクリーンショットにはどのようなUI問題がありますか?"
)
5. 本番デプロイメント
5.1 コスト最適化
LLM API価格比較(2026年)
| モデル | 入力($/100万トークン) | 出力($/100万トークン) | 最適用途 |
|---|---|---|---|
| GPT-4o | $2.50 | $10.00 | 複雑な推論 |
| GPT-4o-mini | $0.15 | $0.60 | 一般タスク |
| Claude Sonnet | $3.00 | $15.00 | コード、分析 |
| Claude Haiku | $0.25 | $1.25 | シンプルなタスク |
*価格は概算です。現在の料金はプロバイダーのドキュメントを確認してください
5.2 本番チェックリスト
LLMアプリケーション本番チェックリスト
セキュリティ:
- APIキーをセキュアなボールトに保存(コード内ではない)
- プロンプトインジェクション防止のための入力サニタイズ
- 機密データの出力フィルタリング
- ユーザー/APIキーごとのレート制限
- コンプライアンス用の監査ログ
信頼性:
- 指数バックオフ付きリトライロジック
- プロバイダー障害時のサーキットブレーカー
- フォールバックモデル(例:GPT-4o → GPT-4o-mini)
- タイムアウト設定
- グレースフルデグラデーション
パフォーマンス:
- 適切な場合のレスポンスキャッシュ
- トークン効率のためのプロンプト最適化
- バルク操作のバッチ処理
- ノンブロッキング操作の非同期処理
監視:
- リクエスト/レスポンスのログ
- ユーザー/機能ごとのコスト追跡
- アラート付きレイテンシ監視
- エラーレートダッシュボード
まとめ
第5章の重要ポイント
- RAG: 外部知識でLLMを強化;ハイブリッド検索 + リランキングで最良の結果
- MCP: ツール統合の標準化プロトコル;ポータブルで構成可能なAIアプリケーションを実現
- エージェント: LLM + 推論 + ツール + メモリ;ReActパターンで段階的問題解決
- マルチモーダル: ビジョン言語モデルが画像理解、ドキュメント処理、視覚的Q&Aを実現
- 本番: コスト最適化、監視、セキュリティ、信頼性に焦点を当てた実世界デプロイメント