<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>LLM on AI2CORE - AI技術ブログ</title>
    <link>https://www.ai2core.com/tags/llm/</link>
    <description>Recent content in LLM on AI2CORE - AI技術ブログ</description>
    <generator>Hugo -- 0.146.4</generator>
    <language>ja</language>
    <lastBuildDate>Sat, 28 Feb 2026 17:00:00 +0900</lastBuildDate>
    <atom:link href="https://www.ai2core.com/tags/llm/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>RAG評価基盤の作り方：精度・再現率・運用コストを同時に最適化する実践手順</title>
      <link>https://www.ai2core.com/posts/2026-02-28-rag-evaluation-pipeline-practical/</link>
      <pubDate>Sat, 28 Feb 2026 17:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-28-rag-evaluation-pipeline-practical/</guid>
      <description>RAGシステムの品質評価を自動化し、検索・生成・運用コストをバランスさせる評価パイプラインの実装方法を解説。</description>
      <content:encoded><![CDATA[<h1 id="rag評価基盤の作り方精度再現率運用コストを同時に最適化する実践手順">RAG評価基盤の作り方：精度・再現率・運用コストを同時に最適化する実践手順</h1>
<p>RAG（Retrieval Augmented Generation）は導入が進んでいますが、運用で最も難しいのは「改善したつもり」が頻発する点です。embedding モデルを変えた、chunk サイズを変えた、reranker を追加した。どれも良さそうに見えるのに、ユーザー満足は上がらない。このギャップを埋めるのが評価基盤です。</p>
<p>本記事では、RAG を継続改善するための評価パイプラインを、データセット設計から CI 統合まで具体的に解説します。</p>
<h2 id="rag評価で見るべき3層">RAG評価で見るべき3層</h2>
<p>RAG の品質は 1 指標では測れません。最低でも次の3層を分けて評価します。</p>
<ol>
<li><strong>Retrieval層</strong>: 正しい文書を取れているか</li>
<li><strong>Generation層</strong>: 回答が正確で有用か</li>
<li><strong>System層</strong>: レイテンシ・コスト・安定性</li>
</ol>
<p>この分離がないと、生成品質低下の原因が retrieval なのか prompt なのか判別できません。</p>
<h2 id="ステップ1評価データセットを設計する">ステップ1：評価データセットを設計する</h2>
<h3 id="1-1-問い合わせカテゴリを分割">1-1. 問い合わせカテゴリを分割</h3>
<p>例として次の5カテゴリに分けます。</p>
<ul>
<li>定義確認（用語説明）</li>
<li>手順質問（How-to）</li>
<li>例外対応（エラー解決）</li>
<li>比較検討（A vs B）</li>
<li>根拠提示（出典必須）</li>
</ul>
<p>カテゴリごとに難易度と重要度を持たせ、偏りを防ぎます。</p>
<h3 id="1-2-正解の持ち方">1-2. 正解の持ち方</h3>
<p>正解は「理想回答1つ」では不十分です。RAGでは表現揺れが自然なので、次を保存します。</p>
<ul>
<li>期待要素（必須ポイント）</li>
<li>禁止要素（誤情報、過剰断定）</li>
<li>参照すべき文書ID</li>
</ul>
<p>この形式にすると、自動評価と人手レビューを両立できます。</p>
<h2 id="ステップ2retrieval評価を自動化">ステップ2：Retrieval評価を自動化</h2>
<p>代表指標:</p>
<ul>
<li>Recall@k</li>
<li>MRR</li>
<li>nDCG</li>
</ul>
<p>例えば、正解文書IDを持つ場合は次のように計算します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">recall_at_k</span>(retrieved_ids, gold_ids, k<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span>):
</span></span><span style="display:flex;"><span>    topk <span style="color:#f92672">=</span> set(retrieved_ids[:k])
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">1.0</span> <span style="color:#66d9ef">if</span> len(topk<span style="color:#f92672">.</span>intersection(gold_ids)) <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span> <span style="color:#66d9ef">else</span> <span style="color:#ae81ff">0.0</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>運用では平均値だけでなく、カテゴリ別分布を見ることが重要です。手順質問だけ recall が低い場合、chunk 戦略や見出し抽出に問題がある可能性が高いです。</p>
<h2 id="ステップ3generation評価の設計">ステップ3：Generation評価の設計</h2>
<p>自動評価では次を推奨します。</p>
<ul>
<li>Faithfulness（出典との整合）</li>
<li>Answer Relevance（質問への適合）</li>
<li>Completeness（必要要素網羅）</li>
<li>Safety（禁止事項違反）</li>
</ul>
<p>LLM-as-a-judge を使う場合、判定プロンプトを固定し、temperature=0 で再現性を確保します。さらに、週次で人手サンプル監査を入れて判定ドリフトを検出します。</p>
<h2 id="ステップ4system評価遅延コスト">ステップ4：System評価（遅延・コスト）</h2>
<p>品質改善がコスト爆増を招くと継続できません。次を同時に計測します。</p>
<ul>
<li>P50/P95 latency</li>
<li>平均 input/output token</li>
<li>1回答あたり推定コスト</li>
<li>timeout率、fallback率</li>
</ul>
<p>この4指標を CI レポートに含めると、精度改善の副作用を早期に発見できます。</p>
<h2 id="ステップ5ciへの組み込み">ステップ5：CIへの組み込み</h2>
<p>PR ごとに評価ジョブを実行し、閾値を満たさない変更をブロックします。</p>
<p>判定例:</p>
<ul>
<li>Recall@5: 0.82 以上</li>
<li>Faithfulness: 0.90 以上</li>
<li>P95 latency: 2500ms 以下</li>
<li>Cost/answer: $0.005 以下</li>
</ul>
<p>疑似フロー:</p>
<ol>
<li>変更ブランチでインデックス再構築</li>
<li>評価データセット100件で推論</li>
<li>指標を計算して前回基準と比較</li>
<li>差分レポートをPRコメントに投稿</li>
</ol>
<p>これで「なんとなく改善」を排除できます。</p>
<h2 id="ステップ6オンライン評価との接続">ステップ6：オンライン評価との接続</h2>
<p>オフライン評価だけでは実利用の多様性を拾えません。オンライン指標を接続します。</p>
<ul>
<li>ユーザー評価（👍/👎）</li>
<li>再質問率（同一セッションで再問い合わせ）</li>
<li>人間オペレータ転送率</li>
</ul>
<p>重要なのは trace_id でオフライン指標と紐づけることです。これにより「オフラインは良いのに本番満足が低い」差分を原因追跡できます。</p>
<h2 id="改善ループの実例">改善ループの実例</h2>
<p>ある社内ヘルプデスクRAGでの改善例:</p>
<ul>
<li>問題: 手順質問で誤回答が多い</li>
<li>原因: chunk が短すぎ、手順文脈が分断</li>
<li>対策: section-aware chunking + reranker導入</li>
</ul>
<p>結果:</p>
<ul>
<li>Recall@5: 0.74 → 0.88</li>
<li>Faithfulness: 0.81 → 0.93</li>
<li>P95 latency: +180ms（許容内）</li>
</ul>
<p>このように、どの変更がどの指標に効いたかを記録すると、次回改善の再現性が高まります。</p>
<h2 id="よくある失敗">よくある失敗</h2>
<ol>
<li><strong>評価データが少なすぎる</strong>
<ul>
<li>20件程度では統計的に不安定。最低100件、理想300件。</li>
</ul>
</li>
<li><strong>単一スコアで判定する</strong>
<ul>
<li>精度だけでコストを見ないと運用破綻。</li>
</ul>
</li>
<li><strong>判定プロンプトを頻繁に変える</strong>
<ul>
<li>指標比較の連続性が失われる。</li>
</ul>
</li>
<li><strong>失敗事例をデータセットへ反映しない</strong>
<ul>
<li>同じ不具合を繰り返す。</li>
</ul>
</li>
</ol>
<h2 id="90日ロードマップ">90日ロードマップ</h2>
<ul>
<li><strong>0-30日</strong>: 評価データセット整備、retrieval指標導入</li>
<li><strong>31-60日</strong>: generation指標 + CIゲート導入</li>
<li><strong>61-90日</strong>: オンライン評価統合、週次改善会の定着</li>
</ul>
<p>この順序なら、運用負荷を抑えつつ確実に品質を上げられます。</p>
<h2 id="まとめ">まとめ</h2>
<p>RAG の実力は、モデル選定より評価基盤で決まります。retrieval、generation、system の3層を分離し、CI に組み込むことで、改善の再現性が生まれます。</p>
<p>まずは小さく始めて、失敗ケースを評価データセットに反映し続けてください。評価が回り始めると、RAG は「当たるかどうかの賭け」から「制御可能なプロダクト」へ変わります。</p>
<h2 id="実装例評価結果をprコメントに自動投稿する">実装例：評価結果をPRコメントに自動投稿する</h2>
<p>運用で効くのは、評価結果を開発者が日常的に見る導線を作ることです。GitHub Actions で評価スクリプトを実行し、結果を PR コメントへ投稿します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">name</span>: <span style="color:#ae81ff">rag-eval</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">on</span>: [<span style="color:#ae81ff">pull_request]</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">evaluate</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v4</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">uv sync</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">uv run python scripts/run_rag_eval.py --dataset evalset_v3.json --out report.json</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">uv run python scripts/post_pr_comment.py report.json</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>この仕組みがあると、レビュー段階で「この変更は Faithfulness を 0.04 落とすが latency は改善」という会話ができ、意思決定が定量化されます。</p>
<h2 id="評価データセットの更新運用">評価データセットの更新運用</h2>
<p>評価セットを固定しすぎると、現実の問い合わせ変化に追従できません。次のルールを推奨します。</p>
<ul>
<li>月1回、実ユーザー失敗ケースを20件追加</li>
<li>四半期ごとに古いケースを棚卸し</li>
<li>重要カテゴリ比率を維持（例: 手順質問30%以上）</li>
</ul>
<p>この更新を怠ると、指標が良くても体感品質が落ちる「評価腐敗」が起きます。</p>
<h2 id="abテストとの接続">A/Bテストとの接続</h2>
<p>大きな変更（embedding刷新、reranker導入）は、オフライン評価だけでなくオンライン A/B を併用します。</p>
<ul>
<li>A群: 現行パイプライン</li>
<li>B群: 新パイプライン</li>
<li>比較指標: 👍率、再質問率、回答時間、コスト</li>
</ul>
<p>2週間程度の観測で統計差が出るケースが多く、主観ベースの議論を減らせます。</p>
<h2 id="まとめ定着のポイント">まとめ（定着のポイント）</h2>
<p>RAG 改善を継続する鍵は、評価を「一回の検証」ではなく「開発フローの標準」にすることです。CI コメント、データセット更新、A/B テストを回すことで、品質向上が偶然ではなく再現可能な活動になります。</p>
<h3 id="補足">補足</h3>
<p>評価結果は経営指標とも接続できます。問い合わせ解決率やサポート工数削減と紐づけることで、RAG 改善が事業価値にどう効いたかまで説明可能になります。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>RAG</category>
      <category>LLM</category>
      <category>Evaluation</category>
      <category>MLOps</category>
    </item>
    <item>
      <title>LLM運用の可観測性を実装する：OpenTelemetryでつくるPrompt/Token/Latency監視の実践</title>
      <link>https://www.ai2core.com/posts/2026-02-27-llm-observability-opentelemetry/</link>
      <pubDate>Fri, 27 Feb 2026 09:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-27-llm-observability-opentelemetry/</guid>
      <description>OpenTelemetryを使ってLLMアプリのレイテンシ、トークン、品質劣化を追跡する実装手順を具体例付きで解説。</description>
      <content:encoded><![CDATA[<h1 id="llm運用の可観測性を実装するopentelemetryでつくるprompttokenlatency監視の実践">LLM運用の可観測性を実装する：OpenTelemetryでつくるPrompt/Token/Latency監視の実践</h1>
<p>LLMアプリは「動く」だけでは本番品質になりません。運用を始めると、次のような問題が必ず発生します。</p>
<ul>
<li>昨日まで 1.2 秒だった応答が突然 4 秒台になる</li>
<li>コストが月末に急増したが、どの機能が原因かわからない</li>
<li>回答品質が落ちたと言われるが、どのプロンプト変更が影響したか追えない</li>
<li>リトライ回数や外部API待ちの偏りが可視化されていない</li>
</ul>
<p>この課題を解く鍵が「可観測性（Observability）」です。本記事では OpenTelemetry を軸に、LLM アプリの監視をゼロから構築する実装を、実際に運用で使える粒度で説明します。</p>
<h2 id="なぜ-apm-だけでは-llm-を見切れないのか">なぜ APM だけでは LLM を見切れないのか</h2>
<p>従来の Web アプリ監視（CPU、HTTP レイテンシ、エラーレート）だけでは、LLM 特有の故障点が見えません。理由は、LLM の品質とコストが「入力テキスト」と「推論設定」に強く依存するためです。</p>
<p>少なくとも次の軸が必要です。</p>
<ol>
<li><strong>Prompt 可視化</strong>: システム/ユーザー/ツール呼び出しの構成</li>
<li><strong>Token 可視化</strong>: input/output token、モデル別単価、キャッシュヒット率</li>
<li><strong>推論経路可視化</strong>: retrieval → rerank → generation の各ステップ時間</li>
<li><strong>品質シグナル</strong>: hallucination 率、参照文書一致率、ユーザー評価</li>
</ol>
<p>つまり、HTTP 1 本のログでは不十分で、<strong>トレース単位で LLM 実行を分解</strong>する必要があります。</p>
<h2 id="アーキテクチャの全体像">アーキテクチャの全体像</h2>
<p>最初に、実装対象を次の構成とします。</p>
<ul>
<li>API: FastAPI</li>
<li>LLM: OpenAI / Azure OpenAI（抽象化）</li>
<li>RAG: pgvector + reranker</li>
<li>Observability: OpenTelemetry SDK + OTLP Exporter + Grafana Tempo/Loki/Prometheus</li>
</ul>
<p>処理フローは次の通りです。</p>
<ol>
<li>リクエスト受信時に <code>trace_id</code> を生成</li>
<li>Retrieval、Rerank、Generate をそれぞれ span 化</li>
<li>各 span に token、model、temperature、cache_hit を attribute として記録</li>
<li>失敗時は exception をイベントとして保存</li>
<li>レスポンス時にコスト推定を metrics として送信</li>
</ol>
<h2 id="ステップ1opentelemetryの初期設定">ステップ1：OpenTelemetryの初期設定</h2>
<p>まずは Python で最小セットを導入します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>uv add opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-instrumentation-fastapi
</span></span></code></pre></td></tr></table>
</div>
</div><p>次に初期化コードを用意します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># observability.py</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> opentelemetry <span style="color:#f92672">import</span> trace, metrics
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> opentelemetry.sdk.trace <span style="color:#f92672">import</span> TracerProvider
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> opentelemetry.sdk.trace.export <span style="color:#f92672">import</span> BatchSpanProcessor
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> opentelemetry.exporter.otlp.proto.grpc.trace_exporter <span style="color:#f92672">import</span> OTLPSpanExporter
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> opentelemetry.sdk.resources <span style="color:#f92672">import</span> Resource
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>resource <span style="color:#f92672">=</span> Resource<span style="color:#f92672">.</span>create({
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;service.name&#34;</span>: <span style="color:#e6db74">&#34;tech-blog-autopilot-api&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;service.version&#34;</span>: <span style="color:#e6db74">&#34;1.3.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;deployment.environment&#34;</span>: <span style="color:#e6db74">&#34;production&#34;</span>,
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>provider <span style="color:#f92672">=</span> TracerProvider(resource<span style="color:#f92672">=</span>resource)
</span></span><span style="display:flex;"><span>provider<span style="color:#f92672">.</span>add_span_processor(
</span></span><span style="display:flex;"><span>    BatchSpanProcessor(OTLPSpanExporter(endpoint<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http://otel-collector:4317&#34;</span>, insecure<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>))
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>trace<span style="color:#f92672">.</span>set_tracer_provider(provider)
</span></span><span style="display:flex;"><span>tracer <span style="color:#f92672">=</span> trace<span style="color:#f92672">.</span>get_tracer(<span style="color:#e6db74">&#34;llm-pipeline&#34;</span>)
</span></span></code></pre></td></tr></table>
</div>
</div><p>ここで重要なのは、<strong>service.name を固定すること</strong>です。デプロイごとに揺れるとダッシュボードが分断され、比較分析ができません。</p>
<h2 id="ステップ2llm処理を-span-で分割する">ステップ2：LLM処理を span で分割する</h2>
<p>実運用では「遅い」の原因が retrieval なのか generation なのかで対応が変わります。そこで、処理を細かく span 化します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> opentelemetry <span style="color:#f92672">import</span> trace
</span></span><span style="display:flex;"><span>tracer <span style="color:#f92672">=</span> trace<span style="color:#f92672">.</span>get_tracer(<span style="color:#e6db74">&#34;llm-pipeline&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">generate_answer</span>(query: str, user_id: str):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">with</span> tracer<span style="color:#f92672">.</span>start_as_current_span(<span style="color:#e6db74">&#34;rag.pipeline&#34;</span>) <span style="color:#66d9ef">as</span> root:
</span></span><span style="display:flex;"><span>        root<span style="color:#f92672">.</span>set_attribute(<span style="color:#e6db74">&#34;user.id&#34;</span>, user_id)
</span></span><span style="display:flex;"><span>        root<span style="color:#f92672">.</span>set_attribute(<span style="color:#e6db74">&#34;feature&#34;</span>, <span style="color:#e6db74">&#34;support-chat&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">with</span> tracer<span style="color:#f92672">.</span>start_as_current_span(<span style="color:#e6db74">&#34;rag.retrieve&#34;</span>) <span style="color:#66d9ef">as</span> span_retrieve:
</span></span><span style="display:flex;"><span>            docs <span style="color:#f92672">=</span> retrieve_docs(query)
</span></span><span style="display:flex;"><span>            span_retrieve<span style="color:#f92672">.</span>set_attribute(<span style="color:#e6db74">&#34;retrieved.count&#34;</span>, len(docs))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">with</span> tracer<span style="color:#f92672">.</span>start_as_current_span(<span style="color:#e6db74">&#34;rag.rerank&#34;</span>) <span style="color:#66d9ef">as</span> span_rerank:
</span></span><span style="display:flex;"><span>            ranked <span style="color:#f92672">=</span> rerank_docs(query, docs)
</span></span><span style="display:flex;"><span>            span_rerank<span style="color:#f92672">.</span>set_attribute(<span style="color:#e6db74">&#34;rerank.top_k&#34;</span>, <span style="color:#ae81ff">5</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">with</span> tracer<span style="color:#f92672">.</span>start_as_current_span(<span style="color:#e6db74">&#34;llm.generate&#34;</span>) <span style="color:#66d9ef">as</span> span_gen:
</span></span><span style="display:flex;"><span>            response <span style="color:#f92672">=</span> call_llm(query, ranked)
</span></span><span style="display:flex;"><span>            span_gen<span style="color:#f92672">.</span>set_attribute(<span style="color:#e6db74">&#34;llm.model&#34;</span>, response<span style="color:#f92672">.</span>model)
</span></span><span style="display:flex;"><span>            span_gen<span style="color:#f92672">.</span>set_attribute(<span style="color:#e6db74">&#34;llm.input_tokens&#34;</span>, response<span style="color:#f92672">.</span>usage<span style="color:#f92672">.</span>input_tokens)
</span></span><span style="display:flex;"><span>            span_gen<span style="color:#f92672">.</span>set_attribute(<span style="color:#e6db74">&#34;llm.output_tokens&#34;</span>, response<span style="color:#f92672">.</span>usage<span style="color:#f92672">.</span>output_tokens)
</span></span><span style="display:flex;"><span>            span_gen<span style="color:#f92672">.</span>set_attribute(<span style="color:#e6db74">&#34;llm.temperature&#34;</span>, <span style="color:#ae81ff">0.2</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> response<span style="color:#f92672">.</span>text
</span></span></code></pre></td></tr></table>
</div>
</div><p>この分割で、「retrieval が中央値 70ms → 280ms に悪化」「特定モデルだけ output token が急増」など、運用判断に直結する情報が取得できます。</p>
<h2 id="ステップ3コストをメトリクス化する">ステップ3：コストをメトリクス化する</h2>
<p>運用現場で最も効くのは、<strong>推定コストをリアルタイムに可視化</strong>することです。モデル単価表をコードに持ち、1リクエストごとに計算して metrics に送ります。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>MODEL_PRICE <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;gpt-4.1-mini&#34;</span>: {<span style="color:#e6db74">&#34;in&#34;</span>: <span style="color:#ae81ff">0.0000003</span>, <span style="color:#e6db74">&#34;out&#34;</span>: <span style="color:#ae81ff">0.0000012</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;gpt-4.1&#34;</span>: {<span style="color:#e6db74">&#34;in&#34;</span>: <span style="color:#ae81ff">0.000003</span>, <span style="color:#e6db74">&#34;out&#34;</span>: <span style="color:#ae81ff">0.000012</span>},
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">estimate_cost</span>(model: str, in_tokens: int, out_tokens: int) <span style="color:#f92672">-&gt;</span> float:
</span></span><span style="display:flex;"><span>    p <span style="color:#f92672">=</span> MODEL_PRICE[model]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> in_tokens <span style="color:#f92672">*</span> p[<span style="color:#e6db74">&#34;in&#34;</span>] <span style="color:#f92672">+</span> out_tokens <span style="color:#f92672">*</span> p[<span style="color:#e6db74">&#34;out&#34;</span>]
</span></span></code></pre></td></tr></table>
</div>
</div><p>推奨は次の3指標です。</p>
<ul>
<li><code>llm_cost_usd_total</code>（counter）</li>
<li><code>llm_tokens_input_total</code> / <code>llm_tokens_output_total</code>（counter）</li>
<li><code>llm_latency_ms</code>（histogram）</li>
</ul>
<p>これを feature、tenant、model のラベルで集計すると、予算統制が一気に楽になります。</p>
<h2 id="ステップ4品質低下を検知する仕組みを入れる">ステップ4：品質低下を検知する仕組みを入れる</h2>
<p>レイテンシとコストだけでは不十分です。品質監視を最低限でも導入します。</p>
<h3 id="4-1-自動評価ジョブ">4-1. 自動評価ジョブ</h3>
<p>夜間バッチで固定データセット（100問程度）を流し、次を記録します。</p>
<ul>
<li>正答率（正解文との semantic similarity）</li>
<li>出典一致率（回答が引用した文書IDの妥当性）</li>
<li>禁止事項違反率（PII、コンプラNG）</li>
</ul>
<h3 id="4-2-本番フィードバック">4-2. 本番フィードバック</h3>
<p>UI で 👍 / 👎 を取り、trace_id と紐づけます。こうすると「悪評の大半が temperature=0.9 の実験フラグ経由」など、根因分析が可能です。</p>
<h2 id="ステップ5運用で効くダッシュボードを作る">ステップ5：運用で効くダッシュボードを作る</h2>
<p>実際に使われるダッシュボードは、項目を欲張らない方が強いです。最初は次の 6 つに絞ってください。</p>
<ol>
<li>P50/P95 レイテンシ（全体 + モデル別）</li>
<li>リクエスト数とエラー率（HTTP + LLM例外）</li>
<li>日次コスト（全体 + feature別）</li>
<li>input/output token 推移</li>
<li>retrieval 件数と空振り率</li>
<li>ユーザー評価（👍率）</li>
</ol>
<p>特に P95 とコストは同一画面に置くのがポイントです。高速化で品質が落ちた、または品質改善でコストが跳ねた、というトレードオフが即時に見えます。</p>
<h2 id="よくある失敗と回避策">よくある失敗と回避策</h2>
<h3 id="失敗1prompt全文を生で保存して個人情報を漏らす">失敗1：Prompt全文を生で保存して個人情報を漏らす</h3>
<p>対策は、PII マスキングを export 前に必ず実行することです。メール、電話番号、住所は正規表現だけでなく、NER ベースで二重防御すると安全です。</p>
<h3 id="失敗2span属性の命名がバラバラ">失敗2：span属性の命名がバラバラ</h3>
<p><code>llm.input_tokens</code> と <code>input_token_count</code> が混在すると集計不能になります。命名規約をリポジトリに固定し、CI で lint してください。</p>
<h3 id="失敗3高カーディナリティ地獄">失敗3：高カーディナリティ地獄</h3>
<p><code>user_id</code> をそのままメトリクスラベルに入れると TSDB が破綻します。ユーザー軸は trace/log に置き、metrics は tenant や plan 程度に抑えます。</p>
<h2 id="導入ロードマップ2週間">導入ロードマップ（2週間）</h2>
<ul>
<li><strong>Day 1-2</strong>: FastAPI + LLM呼び出しに trace 埋め込み</li>
<li><strong>Day 3-4</strong>: token/cost メトリクス送信</li>
<li><strong>Day 5-6</strong>: Grafana ダッシュボード構築</li>
<li><strong>Day 7-9</strong>: しきい値アラート設計（P95、error、cost）</li>
<li><strong>Day 10-12</strong>: 品質評価バッチ導入</li>
<li><strong>Day 13-14</strong>: インシデント演習（意図的劣化を検知できるか）</li>
</ul>
<p>2週間で「見える化」は十分達成できます。完璧を目指すより、まず計測可能にすることが重要です。</p>
<h2 id="まとめ">まとめ</h2>
<p>LLM運用で本当に困るのは、失敗そのものではなく「失敗の理由が見えない」状態です。OpenTelemetry を使って retrieval、generation、token、cost、品質を一貫して観測できるようにすると、改善サイクルが回り始めます。</p>
<p>可観測性は守りではなく、開発速度を上げるための攻めの基盤です。まずは span を3つに分けるところから始めてください。それだけで、LLM運用の景色が大きく変わります。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>LLM</category>
      <category>OpenTelemetry</category>
      <category>Observability</category>
      <category>Prompt Engineering</category>
    </item>
    <item>
      <title>2026年のAIエージェント進化論：シングルプロンプトからマルチエージェント協調へ</title>
      <link>https://www.ai2core.com/posts/2026-02-24-ai-agents-evolution/</link>
      <pubDate>Tue, 24 Feb 2026 18:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-24-ai-agents-evolution/</guid>
      <description>LangGraphやAutoGenを活用したマルチエージェントシステムのアーキテクチャと実装のポイント。</description>
      <content:encoded><![CDATA[<h1 id="2026年のaiエージェント進化論シングルプロンプトからマルチエージェント協調へ">2026年のAIエージェント進化論：シングルプロンプトからマルチエージェント協調へ</h1>
<h2 id="はじめに">はじめに</h2>
<p>「この複雑なレポート作成、AIに丸投げできないだろうか？」
「ユーザーからの曖昧な指示を解釈して、コードを書き、テストし、デプロイまで自動化したい。」</p>
<p>AI、特に大規模言語モデル（LLM）の進化に触れたエンジニアなら、一度はこんな夢を描いたことがあるのではないでしょうか。しかし、ChatGPTのような単一のプロンプトで対話するモデルに複雑なタスクを依頼すると、途中で文脈を見失ったり、期待とは異なるアウトプットが出てきたりと、その限界に直面することも少なくありません。</p>
<p>ReAct（Reasoning and Acting）のようなフレームワークを用いてツールを使わせる「シングルエージェント」は大きな進歩でしたが、それでもなお、複雑で多段階のタスクを自律的にこなすには力不足でした。まるで、一人の優秀な新入社員に、いきなり会社の全業務を任せるようなものです。</p>
<p>もし、AIが一人ではなく、「専門家チーム」として協調して働いてくれたらどうでしょう？リサーチ担当、コーディング担当、レビュー担当、そしてプロジェクト全体を管理するマネージャー。それぞれが専門知識を持ち、互いにコミュニケーションを取りながら、一つの大きな目標に向かって自律的にタスクを遂行する。</p>
<p>本記事では、そんな未来を実現する技術として注目を集める**「マルチエージェント・システム」**について、その概念から具体的な実装方法までを深く掘り下げます。特に、この分野を牽引する2大フレームワーク、**Microsoftの「AutoGen」<strong>と</strong>LangChainの「LangGraph」**に焦点を当て、そのアーキテクチャ、実装のポイント、そして現場で活かすための実践的なTipsを、豊富なコード例とともに解説していきます。</p>
<p>この記事を読み終える頃には、あなたはシングルプロンプトの呪縛から解き放たれ、自律的なAIエージェントチームを編成するための確かな知識とインスピレーションを得ているはずです。</p>
<h2 id="なぜ今マルチエージェントシステムなのか">なぜ今、マルチエージェント・システムなのか？</h2>
<p>LLMの能力が飛躍的に向上し、GPT-4oのようなマルチモーダル対応モデルが登場する中で、なぜわざわざ複数のエージェントを協調させる必要があるのでしょうか。その理由は、**「シングルエージェントの限界」<strong>と</strong>「タスクの複雑性への対応」**にあります。</p>
<h3 id="シングルエージェントの限界">シングルエージェントの限界</h3>
<p>従来のシングルエージェントのアーキテクチャは、基本的に一つの「思考の連鎖（Chain of Thought）」に依存しています。これは、直線的な思考プロセスには強いものの、以下のような課題を抱えています。</p>
<ol>
<li><strong>思考の硬直性</strong>: 一つの計画に固執し、途中で問題が発生しても柔軟に軌道修正するのが苦手です。複数の選択肢を並行して検討したり、第三者の視点でレビューしたりといった、人間が行うような複雑な意思決定が困難です。</li>
<li><strong>コンテキストの肥大化</strong>: タスクが複雑になるほど、プロンプトに含めるべき情報（過去のやり取り、ツールの使用履歴、中間生成物）が増大します。これはAPIコストの増加、処理速度の低下、そしてLLMが重要な情報を見失う「Lost in the Middle」問題を引き起こします。</li>
<li><strong>責任範囲の曖昧さ</strong>: 一つのエージェントにあらゆる役割（計画、実行、検証、修正）を詰め込もうとすると、プロンプトが極めて複雑になり、かえって性能が低下します。各ステップで何をすべきかが曖昧になり、幻覚（ハルシネーション）のリスクも高まります。</li>
</ol>
<h3 id="人間の組織に学ぶ専門化と協調">人間の組織に学ぶ「専門化」と「協調」</h3>
<p>これらの課題を解決するヒントは、私たち自身の社会、つまり「組織」にあります。優れた企業は、一人の天才が全てをこなすのではなく、営業、開発、マーケティング、品質管理といった専門部署が互いに連携・協調することで、複雑で大きな目標を達成します。</p>
<p>マルチエージェント・システムは、この組織論をAIの世界に持ち込むアプローチです。</p>
<ul>
<li><strong>専門化 (Specialization)</strong>: 各エージェントに特定の役割と専門知識を与えます。「コードを書くのが得意なエージェント」「書かれたコードを厳しくレビューするエージェント」「ユーザーとの対話を受け持つエージェント」といったように、責任範囲を限定することで、各エージェントのプロンプトをシンプルかつ高性能に保てます。</li>
<li><strong>協調 (Collaboration)</strong>: エージェント同士がメッセージを交換し、対話することで、問題を解決します。例えば、コーディングエージェントが書いたコードをレビューエージェントがチェックし、修正点をフィードバックする。この対話のループを通じて、生成物の品質をスパイラル状に向上させることができます。</li>
<li><strong>自律性 (Autonomy)</strong>: 全体の目標が与えられると、エージェントチームは自律的にタスクを分解し、役割を分担し、協調してタスクを遂行します。これにより、人間がマイクロマネジメントする必要がなくなります。</li>
</ul>
<p>このパラダイムシフトは、単なるAIの性能向上ではなく、<strong>AIによる問題解決の「方法論」そのものの進化</strong>と言えるでしょう。</p>
<h2 id="具体的な解決策autogenとlanggraphによる実装">具体的な解決策：AutoGenとLangGraphによる実装</h2>
<p>それでは、実際にマルチエージェント・システムを構築するためのフレームワークを見ていきましょう。ここでは、特に人気の高いAutoGenとLangGraphを取り上げ、それぞれの思想と実装方法を解説します。</p>
<h3 id="1-autogen対話による自律的タスク解決">1. AutoGen：対話による自律的タスク解決</h3>
<p>AutoGenは、Microsoft Researchが開発したフレームワークで、<strong>エージェント間の対話</strong>を中心に据えた設計が特徴です。複数のエージェント（<code>ConversableAgent</code>）を定義し、それらが互いにチャットを繰り返すことで、タスクが進行していきます。</p>
<h4 id="autogenのアーキテクチャ">AutoGenのアーキテクチャ</h4>
<p>AutoGenの基本的な登場人物は以下の通りです。</p>
<ul>
<li><strong><code>AssistantAgent</code></strong>: LLMを搭載した標準的なAIエージェント。与えられた役割（例：「あなたはPythonの専門家です」）に基づいて発言やコード生成を行います。</li>
<li><strong><code>UserProxyAgent</code></strong>: 人間の代理人として振る舞う特殊なエージェント。他のエージェントからコードを受け取ると、それを<strong>実際に実行</strong>しようと試みます。実行結果（成功、失敗、エラーメッセージ）を次のメッセージとして相手に返すことで、対話のループが生まれます。また、人間の入力を促し、介入（Human-in-the-Loop）を可能にします。</li>
<li><strong><code>GroupChatManager</code></strong>: 3体以上のエージェントが参加するグループチャットを管理し、次に誰が発言するかを制御します。</li>
</ul>
<h4 id="実装例コード生成実行タスク">実装例：コード生成＆実行タスク</h4>
<p>ここでは、「あるURLから株価データを取得し、それをプロットして画像ファイルとして保存する」というタスクを、2体のエージェントで解決する例を見てみましょう。</p>
<p><strong>1. セットアップ</strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pip install <span style="color:#e6db74">&#34;pyautogen[retrievechat]&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>2. 設定ファイルの準備</strong></p>
<p>プロジェクトのルートに <code>OAI_CONFIG_LIST</code> という名前でJSONファイルを作成し、APIキーを設定します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;model&#34;</span>: <span style="color:#e6db74">&#34;gpt-4o&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;api_key&#34;</span>: <span style="color:#e6db74">&#34;sk-...&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>3. Pythonコード</strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> autogen
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># LLMの設定を読み込む</span>
</span></span><span style="display:flex;"><span>config_list <span style="color:#f92672">=</span> autogen<span style="color:#f92672">.</span>config_list_from_json(<span style="color:#e6db74">&#34;OAI_CONFIG_LIST&#34;</span>)
</span></span><span style="display:flex;"><span>llm_config <span style="color:#f92672">=</span> {<span style="color:#e6db74">&#34;config_list&#34;</span>: config_list}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 1. アシスタントエージェント（コーダー）の定義</span>
</span></span><span style="display:flex;"><span>coder <span style="color:#f92672">=</span> autogen<span style="color:#f92672">.</span>AssistantAgent(
</span></span><span style="display:flex;"><span>    name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;Coder&#34;</span>,
</span></span><span style="display:flex;"><span>    llm_config<span style="color:#f92672">=</span>llm_config,
</span></span><span style="display:flex;"><span>    system_message<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;あなたは優秀なPythonプログラマーです。Pythonコードを生成し、問題を解決します。コードは ```python ... ``` の中に記述してください。&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 2. ユーザープロキシエージェント（コード実行者・人間の代理）の定義</span>
</span></span><span style="display:flex;"><span>user_proxy <span style="color:#f92672">=</span> autogen<span style="color:#f92672">.</span>UserProxyAgent(
</span></span><span style="display:flex;"><span>    name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;UserProxy&#34;</span>,
</span></span><span style="display:flex;"><span>    human_input_mode<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;NEVER&#34;</span>,  <span style="color:#75715e"># 人間の入力を介さず自動で進行</span>
</span></span><span style="display:flex;"><span>    max_consecutive_auto_reply<span style="color:#f92672">=</span><span style="color:#ae81ff">10</span>,
</span></span><span style="display:flex;"><span>    is_termination_msg<span style="color:#f92672">=</span><span style="color:#66d9ef">lambda</span> x: x<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#34;content&#34;</span>, <span style="color:#e6db74">&#34;&#34;</span>)<span style="color:#f92672">.</span>rstrip()<span style="color:#f92672">.</span>endswith(<span style="color:#e6db74">&#34;TERMINATE&#34;</span>),
</span></span><span style="display:flex;"><span>    code_execution_config<span style="color:#f92672">=</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;work_dir&#34;</span>: <span style="color:#e6db74">&#34;coding&#34;</span>,  <span style="color:#75715e"># コードを実行する作業ディレクトリ</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;use_docker&#34;</span>: <span style="color:#66d9ef">False</span>,  <span style="color:#75715e"># Dockerを使わない場合はFalse (True推奨)</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    system_message<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;あなたはコードの実行者です。Coderから提案されたコードを実行し、その結果を報告します。問題があればエラーを伝えてください。&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># タスクの定義と対話の開始</span>
</span></span><span style="display:flex;"><span>task <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">yfinanceとmatplotlibを使って、過去1年間のテスラ(TSLA)の株価を取得し、
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">終値を折れ線グラフでプロットしてください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">グラフは &#39;tsla_stock_price.png&#39; という名前でファイルに保存してください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>user_proxy<span style="color:#f92672">.</span>initiate_chat(
</span></span><span style="display:flex;"><span>    coder,
</span></span><span style="display:flex;"><span>    message<span style="color:#f92672">=</span>task
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="実行プロセスの解説">実行プロセスの解説</h4>
<p>このコードを実行すると、<code>user_proxy</code>が最初のタスクを<code>coder</code>に投げます。</p>
<ol>
<li><strong><code>coder</code></strong>: タスクを理解し、<code>yfinance</code>と<code>matplotlib</code>をインストールする必要があると考え、それらを使ったPythonコードを生成して返信します。</li>
<li><strong><code>user_proxy</code></strong>: <code>coder</code>から受け取ったコードブロックを検出し、<code>coding</code>ディレクトリ内でそのコードを実行します。</li>
<li><strong>（成功した場合）</strong>: コードが正常に実行され、<code>tsla_stock_price.png</code>が生成されます。<code>user_proxy</code>は実行結果（標準出力など）を<code>coder</code>に報告します。</li>
<li><strong><code>coder</code></strong>: 成功報告を受け、タスクが完了したと判断し、「TERMINATE」という終了キーワードを含むメッセージを返します。</li>
<li><strong><code>user_proxy</code></strong>: 「TERMINATE」を検知し、対話を終了します。</li>
</ol>
<p>もし途中でエラー（例：ライブラリがインストールされていない）が発生すれば、<code>user_proxy</code>はそのエラーメッセージを<code>coder</code>に伝えます。すると<code>coder</code>は「ライブラリをインストールしてください」といった修正案や、エラーを解決するための新しいコードを提案し、対話が続行されます。この<strong>試行錯誤のループ</strong>こそが、AutoGenの強みです。</p>
<h3 id="2-langgraphグラフによる状態遷移ワークフローの制御">2. LangGraph：グラフによる状態遷移ワークフローの制御</h3>
<p>LangGraphは、人気のLLMフレームワークLangChainから派生したライブラリで、<strong>状態遷移グラフ（Stateful Graphs）<strong>としてエージェントのワークフローを定義します。対話の自律性に重きを置くAutoGenとは対照的に、LangGraphは</strong>ワークフローの制御性</strong>に優れています。</p>
<h4 id="langgraphのアーキテクチャ">LangGraphのアーキテクチャ</h4>
<p>LangGraphの中心的な概念は以下の通りです。</p>
<ul>
<li><strong>State</strong>: グラフ全体で共有される状態オブジェクト。辞書やPydanticモデルで定義し、各ステップの出力がこのStateに蓄積されていきます。</li>
<li><strong>Nodes</strong>: グラフのノード（節点）。Python関数として定義され、それぞれが特定の処理（エージェントの呼び出し、ツールの実行など）を担当します。各ノードは現在の<code>State</code>を受け取り、更新した<code>State</code>の一部を返します。</li>
<li><strong>Edges</strong>: ノード間の繋がり（辺）。どのノードの次にどのノードを実行するかを定義します。</li>
<li><strong>Conditional Edges</strong>: 条件付きの辺。現在の<code>State</code>に基づいて、次に進むべきノードを動的に決定します。これにより、ループや分岐を持つ複雑なワークフローが実現できます。</li>
</ul>
<p><img alt="LangGraphの概念図" loading="lazy" src="https://blog.langchain.dev/content/images/2024/04/image-1.png">
<em>(出典: LangChain Blog)</em></p>
<h4 id="実装例リサーチタスクのワークフロー">実装例：リサーチタスクのワークフロー</h4>
<p>ここでは、「あるテーマについてWebでリサーチし、複数の視点から記事を作成し、それをレビューして最終的なレポートを生成する」というワークフローをLangGraphで構築してみましょう。</p>
<p><strong>1. セットアップ</strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pip install langgraph langchain langchain_openai duckduckgo-search
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>2. Pythonコード</strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">  9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 32
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 33
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 34
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 35
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 36
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 37
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 38
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 39
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 40
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 41
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 42
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 43
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 44
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 45
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 46
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 47
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 48
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 49
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 50
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 51
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 52
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 53
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 54
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 55
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 56
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 57
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 58
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 59
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 60
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 61
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 62
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 63
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 64
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 65
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 66
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 67
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 68
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 69
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 70
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 71
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 72
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 73
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 74
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 75
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 76
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 77
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 78
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 79
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 80
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 81
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 82
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 83
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 84
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 85
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 86
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 87
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 88
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 89
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 90
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 91
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 92
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 93
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 94
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 95
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 96
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 97
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 98
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 99
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">100
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">101
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">102
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">103
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">104
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">105
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">106
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">107
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">108
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">109
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">110
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">111
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">112
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">113
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">114
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">115
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">116
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">117
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">118
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">119
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> typing <span style="color:#f92672">import</span> TypedDict, Annotated, List
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain_core.messages <span style="color:#f92672">import</span> BaseMessage
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain_openai <span style="color:#f92672">import</span> ChatOpenAI
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> langgraph.graph <span style="color:#f92672">import</span> StateGraph, END
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain_community.tools <span style="color:#f92672">import</span> DuckDuckGoSearchRun
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 環境変数にAPIキーを設定</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># os.environ[&#34;OPENAI_API_KEY&#34;] = &#34;YOUR_API_KEY&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ツール（Web検索）の準備</span>
</span></span><span style="display:flex;"><span>search_tool <span style="color:#f92672">=</span> DuckDuckGoSearchRun()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># LLMモデルの定義</span>
</span></span><span style="display:flex;"><span>model <span style="color:#f92672">=</span> ChatOpenAI(temperature<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>, model<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;gpt-4o&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># --- 1. グラフの状態 (State) を定義 ---</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AgentState</span>(TypedDict):
</span></span><span style="display:flex;"><span>    topic: str
</span></span><span style="display:flex;"><span>    search_results: str
</span></span><span style="display:flex;"><span>    draft: str
</span></span><span style="display:flex;"><span>    review: str
</span></span><span style="display:flex;"><span>    revision_count: int
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># --- 2. グラフのノード (Nodes) を定義 ---</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># リサーチャーエージェント</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">researcher_node</span>(state: AgentState):
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;--- ノード: Researcher ---&#34;</span>)
</span></span><span style="display:flex;"><span>    topic <span style="color:#f92672">=</span> state[<span style="color:#e6db74">&#34;topic&#34;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># LLMに検索クエリを考えさせる</span>
</span></span><span style="display:flex;"><span>    query_generation_prompt <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;「</span><span style="color:#e6db74">{</span>topic<span style="color:#e6db74">}</span><span style="color:#e6db74">」について調査するための、効果的な検索クエリを3つ考えてください。&#34;</span>
</span></span><span style="display:flex;"><span>    query_response <span style="color:#f92672">=</span> model<span style="color:#f92672">.</span>invoke(query_generation_prompt)
</span></span><span style="display:flex;"><span>    queries <span style="color:#f92672">=</span> query_response<span style="color:#f92672">.</span>content<span style="color:#f92672">.</span>strip()<span style="color:#f92672">.</span>split(<span style="color:#e6db74">&#39;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#39;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    results <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> query <span style="color:#f92672">in</span> queries:
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;検索中: </span><span style="color:#e6db74">{</span>query<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>        results <span style="color:#f92672">+=</span> search_tool<span style="color:#f92672">.</span>run(query) <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n\n</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#34;search_results&#34;</span>: results}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ライターエージェント</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">writer_node</span>(state: AgentState):
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;--- ノード: Writer ---&#34;</span>)
</span></span><span style="display:flex;"><span>    topic <span style="color:#f92672">=</span> state[<span style="color:#e6db74">&#34;topic&#34;</span>]
</span></span><span style="display:flex;"><span>    search_results <span style="color:#f92672">=</span> state[<span style="color:#e6db74">&#34;search_results&#34;</span>]
</span></span><span style="display:flex;"><span>    prompt <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    以下の検索結果を基に、「</span><span style="color:#e6db74">{</span>topic<span style="color:#e6db74">}</span><span style="color:#e6db74">」に関するブログ記事のドラフトを作成してください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    検索結果:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    </span><span style="color:#e6db74">{</span>search_results<span style="color:#e6db74">}</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    draft <span style="color:#f92672">=</span> model<span style="color:#f92672">.</span>invoke(prompt)<span style="color:#f92672">.</span>content
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#34;draft&#34;</span>: draft}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># レビューアーエージェント</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">reviewer_node</span>(state: AgentState):
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;--- ノード: Reviewer ---&#34;</span>)
</span></span><span style="display:flex;"><span>    topic <span style="color:#f92672">=</span> state[<span style="color:#e6db74">&#34;topic&#34;</span>]
</span></span><span style="display:flex;"><span>    draft <span style="color:#f92672">=</span> state[<span style="color:#e6db74">&#34;draft&#34;</span>]
</span></span><span style="display:flex;"><span>    prompt <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    あなたは優秀な編集者です。以下の「</span><span style="color:#e6db74">{</span>topic<span style="color:#e6db74">}</span><span style="color:#e6db74">」に関するブログ記事のドラフトをレビューしてください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    改善点があれば具体的に指摘し、問題がなければ「PERFECT」とだけ回答してください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    ドラフト:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    </span><span style="color:#e6db74">{</span>draft<span style="color:#e6db74">}</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    review <span style="color:#f92672">=</span> model<span style="color:#f92672">.</span>invoke(prompt)<span style="color:#f92672">.</span>content
</span></span><span style="display:flex;"><span>    revision_count <span style="color:#f92672">=</span> state<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#34;revision_count&#34;</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#34;review&#34;</span>: review, <span style="color:#e6db74">&#34;revision_count&#34;</span>: revision_count}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># --- 3. 条件付きの辺 (Conditional Edge) を定義 ---</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">should_continue</span>(state: AgentState):
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;--- 条件分岐 ---&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> state[<span style="color:#e6db74">&#34;revision_count&#34;</span>] <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">3</span>:
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;最大修正回数に達しました。&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;end&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#e6db74">&#34;PERFECT&#34;</span> <span style="color:#f92672">in</span> state[<span style="color:#e6db74">&#34;review&#34;</span>]:
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;レビューをパスしました。&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;end&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;修正が必要です。&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;continue&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># --- 4. グラフを構築 ---</span>
</span></span><span style="display:flex;"><span>workflow <span style="color:#f92672">=</span> StateGraph(AgentState)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ノードを追加</span>
</span></span><span style="display:flex;"><span>workflow<span style="color:#f92672">.</span>add_node(<span style="color:#e6db74">&#34;researcher&#34;</span>, researcher_node)
</span></span><span style="display:flex;"><span>workflow<span style="color:#f92672">.</span>add_node(<span style="color:#e6db74">&#34;writer&#34;</span>, writer_node)
</span></span><span style="display:flex;"><span>workflow<span style="color:#f92672">.</span>add_node(<span style="color:#e6db74">&#34;reviewer&#34;</span>, reviewer_node)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># エッジを追加</span>
</span></span><span style="display:flex;"><span>workflow<span style="color:#f92672">.</span>set_entry_point(<span style="color:#e6db74">&#34;researcher&#34;</span>)
</span></span><span style="display:flex;"><span>workflow<span style="color:#f92672">.</span>add_edge(<span style="color:#e6db74">&#34;researcher&#34;</span>, <span style="color:#e6db74">&#34;writer&#34;</span>)
</span></span><span style="display:flex;"><span>workflow<span style="color:#f92672">.</span>add_edge(<span style="color:#e6db74">&#34;writer&#34;</span>, <span style="color:#e6db74">&#34;reviewer&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 条件付きエッジを追加</span>
</span></span><span style="display:flex;"><span>workflow<span style="color:#f92672">.</span>add_conditional_edges(
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;reviewer&#34;</span>,
</span></span><span style="display:flex;"><span>    should_continue,
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;continue&#34;</span>: <span style="color:#e6db74">&#34;writer&#34;</span>, <span style="color:#75715e"># 修正が必要ならライターに戻る</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;end&#34;</span>: END
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># グラフをコンパイル</span>
</span></span><span style="display:flex;"><span>app <span style="color:#f92672">=</span> workflow<span style="color:#f92672">.</span>compile()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># --- 5. グラフを実行 ---</span>
</span></span><span style="display:flex;"><span>inputs <span style="color:#f92672">=</span> {<span style="color:#e6db74">&#34;topic&#34;</span>: <span style="color:#e6db74">&#34;2024年の生成AIのトレンド&#34;</span>, <span style="color:#e6db74">&#34;revision_count&#34;</span>: <span style="color:#ae81ff">0</span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> output <span style="color:#f92672">in</span> app<span style="color:#f92672">.</span>stream(inputs):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> key, value <span style="color:#f92672">in</span> output<span style="color:#f92672">.</span>items():
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;ノード &#39;</span><span style="color:#e6db74">{</span>key<span style="color:#e6db74">}</span><span style="color:#e6db74">&#39; の出力:&#34;</span>)
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;---&#34;</span>)
</span></span><span style="display:flex;"><span>        print(value)
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;=&#34;</span><span style="color:#f92672">*</span><span style="color:#ae81ff">30</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>)
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="実行プロセスの解説-1">実行プロセスの解説</h4>
<p>このコードは、以下のような明確なワークフローを実行します。</p>
<ol>
<li><strong><code>researcher</code></strong>: 与えられたトピックに基づいてWeb検索を実行し、結果を<code>State</code>に保存します。</li>
<li><strong><code>writer</code></strong>: <code>researcher</code>が収集した情報をもとに、記事のドラフトを作成し、<code>State</code>に保存します。</li>
<li><strong><code>reviewer</code></strong>: <code>writer</code>が書いたドラフトをレビューします。</li>
<li><strong><code>should_continue</code> (条件分岐)</strong>:
<ul>
<li>レビュー結果が「PERFECT」なら、ワークフローは終了（<code>END</code>）します。</li>
<li>修正点があれば、<code>writer</code>ノードに処理を戻し、ドラフトの修正を促します（ループ）。</li>
<li>ループが3回を超えた場合も、無限ループを避けるために処理を終了します。</li>
</ul>
</li>
</ol>
<p>このように、LangGraphは処理の流れを明示的にグラフとして定義するため、デバッグが容易で、ビジネスロジックのような複雑なフローを堅牢に実装するのに適しています。</p>
<h2 id="メリットとデメリットそしてツールの比較">メリットとデメリット、そしてツールの比較</h2>
<p>マルチエージェント・システムは強力ですが、銀の弾丸ではありません。導入にあたっては、その利点と課題を理解することが重要です。</p>
<h3 id="マルチエージェントシステムのメリット">マルチエージェント・システムのメリット</h3>
<ul>
<li><strong>高度な問題解決能力</strong>: 複雑なタスクを専門家チームのように分業・協調して解決できる。</li>
<li><strong>堅牢性と自己修正</strong>: レビューやフィードバックのループを組み込むことで、生成物の品質を向上させ、エラーから自律的に回復できる。</li>
<li><strong>モジュール性と拡張性</strong>: 新しい役割を持つエージェントをノードや対話者として追加するのが比較的容易。</li>
<li><strong>プロセスの透明性</strong>: エージェント間の対話ログや状態遷移を追跡することで、AIが「どのように」その結論に至ったのかを理解しやすくなる。</li>
</ul>
<h3 id="マルチエージェントシステムのデメリットと課題">マルチエージェント・システムのデメリットと課題</h3>
<ul>
<li><strong>設計の複雑性</strong>: どのような役割のエージェントが必要か、どのようなワークフローや対話プロトコルを設計するかが成功の鍵となり、高度な設計能力が求められる。</li>
<li><strong>制御の難しさ</strong>: 特に自律性の高いシステムでは、エージェントが無限ループに陥ったり、意図しない方向にタスクを進めたりするリスクがある。</li>
<li><strong>コストの増加</strong>: 複数のエージェントが何度もLLM APIを呼び出すため、シングルエージェントに比べてトークン消費量とコストが大幅に増加する可能性がある。</li>
<li><strong>レイテンシーの増大</strong>: エージェント間の通信やLLMの呼び出しが重なるため、最終的な結果を得るまでの時間が長くなる傾向がある。</li>
</ul>
<h3 id="langgraph-vs-autogenどちらを選ぶべきか">LangGraph vs AutoGen：どちらを選ぶべきか？</h3>
<table>
  <thead>
      <tr>
          <th style="text-align: left">特徴</th>
          <th style="text-align: left">LangGraph (by LangChain)</th>
          <th style="text-align: left">AutoGen (by Microsoft)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>思想</strong></td>
          <td style="text-align: left"><strong>状態遷移グラフ</strong>によるワークフロー制御</td>
          <td style="text-align: left"><strong>対話</strong>による自律的な協調</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>制御性</strong></td>
          <td style="text-align: left"><strong>高い</strong>。処理の流れを明示的にグラフで定義するため、予測可能でデバッグしやすい。</td>
          <td style="text-align: left"><strong>中程度</strong>。エージェント間の対話に依存するため、創発的な挙動を示すが、制御は難しい。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>柔軟性</strong></td>
          <td style="text-align: left"><strong>非常に高い</strong>。ノードはただのPython関数なので、任意のロジックを自由に組み込める。</td>
          <td style="text-align: left"><strong>高い</strong>。Agentクラスを継承してカスタマイズ可能だが、対話の枠組みに従う必要がある。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>学習コスト</strong></td>
          <td style="text-align: left">やや高い。グラフ理論や状態管理の概念を理解する必要がある。</td>
          <td style="text-align: left">比較的低い。<code>initiate_chat</code>で始められ、直感的に理解しやすい。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>ベストな用途</strong></td>
          <td style="text-align: left">複雑なビジネスプロセス、ETLパイプライン、自己修正ループなど、<strong>手順が明確なタスク</strong>。</td>
          <td style="text-align: left">研究開発、コード生成、ブレーンストーミングなど、<strong>解決策が未知で探索的なタスク</strong>。</td>
      </tr>
  </tbody>
</table>
<p>結論として、「厳密なワークフローを構築したいならLangGraph」「エージェントの自律的な協調に任せてみたいならAutoGen」という使い分けが考えられます。</p>
<h2 id="現場で使える実践的なtips">現場で使える実践的なTips</h2>
<p>マルチエージェント・システムを本番環境で運用するには、いくつかの工夫が必要です。</p>
<ol>
<li><strong>スモールスタートを心がける</strong>: 最初から10体のエージェントチームを作るのではなく、まずは2〜3体のコアな役割のエージェントから始め、徐々に拡張していきましょう。</li>
<li><strong>役割（Role）のプロンプトを磨き込む</strong>: 各エージェントの<code>system_message</code>は、その性能を決定づける最も重要な要素です。「あなたは何者で、何が得意で、何をしてはいけないのか」を可能な限り明確に定義してください。</li>
<li><strong>強力なマネージャー/オーケストレーターを置く</strong>: LangGraphのグラフ定義そのものや、複数のエージェントを統括するマネージャーエージェントの設計は非常に重要です。タスクの分解、進行管理、最終的な成果物の統合といった役割を担わせましょう。</li>
<li><strong>コスト管理戦略を立てる</strong>:
<ul>
<li><strong>モデルの使い分け</strong>: 簡単なタスク（要約、分類など）には安価なモデル（例: GPT-3.5 Turbo, Claude 3 Sonnet）を使い、高度な推論やコーディングが必要な場面では高性能モデル（例: GPT-4o, Claude 3 Opus）を使うハイブリッド構成を検討します。</li>
<li><strong>サーキットブレーカー</strong>: APIコールの回数や対話のターン数に上限を設け、無限ループによるコスト増大を防ぎます。LangGraphの例で示した<code>revision_count</code>がこれにあたります。</li>
</ul>
</li>
<li><strong>人間参加のループ (Human-in-the-Loop) を組み込む</strong>: 全てを自動化するのではなく、重要な意思決定ポイント（例：生成したコードの実行前、顧客へのメール送信前）では、必ず人間の承認を求めるステップをワークフローに組み込みましょう。AutoGenの<code>UserProxyAgent</code>は、このための優れた仕組みを提供しています。</li>
<li><strong>ロギングとトレーサビリティ</strong>: エージェント間の全てのやり取りや状態の変化を詳細にログとして記録します。LangSmithのようなツールを使うと、複雑なエージェントの挙動を可視化し、デバッグを大幅に効率化できます。</li>
</ol>
<h2 id="まとめ">まとめ</h2>
<p>私たちは今、AI開発における大きな転換点に立っています。単一のLLMに完璧な答えを求める「シングルプロンプトの時代」は終わりを告げ、多様な能力を持つAIエージェントが協調して複雑な問題を解決する**「マルチエージェント協調の時代」**が幕を開けようとしています。</p>
<p>この記事では、その中核技術であるマルチエージェント・システムの概念と、それを実現するAutoGenとLangGraphという二つの強力なフレームワークについて解説しました。</p>
<ul>
<li><strong>AutoGen</strong>は、エージェント間の「対話」を通じて、自己修正的なループを生み出し、探索的なタスクを自律的に解決します。</li>
<li><strong>LangGraph</strong>は、「状態遷移グラフ」としてワークフローを明示的に定義することで、複雑なビジネスプロセスを堅牢かつ制御可能に実装します。</li>
</ul>
<p>これらの技術は、まだ発展途上であり、コストや制御性の面で課題も残されています。しかし、そのポテンシャルは計り知れません。もはや私たちの仕事は、単に賢いAIを一つ作ることではなく、<strong>いかにして「優秀なAIチーム」を設計し、編成し、マネジメントするか</strong>という、より高度な次元へとシフトしています。</p>
<p>2026年に向けて、この流れはさらに加速していくでしょう。ぜひ、この記事をきっかけに、まずは簡単な2エージェントシステムから、あなたの身の回りの課題解決に挑戦してみてください。そこに、次世代のAIアプリケーション開発の未来が広がっているはずです。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>AI Agent</category>
      <category>LangChain</category>
      <category>LLM</category>
    </item>
    <item>
      <title>【速報】Google Gemini 3.1 Pro登場！新機能と使い方を徹底解説</title>
      <link>https://www.ai2core.com/posts/2026-02-21-gemini-3-1-pro/</link>
      <pubDate>Sat, 21 Feb 2026 10:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-21-gemini-3-1-pro/</guid>
      <description>Google最新のGemini 3.1 Proモデルの概要、新機能、APIの使い方を初心者エンジニア向けに分かりやすく解説します。</description>
      <content:encoded><![CDATA[<h2 id="はじめに">はじめに</h2>
<p>皆さん、こんにちは！テクノロジーの進化は本当に早いもので、Googleから最新のAIモデル「<strong>Gemini 3.1 Pro</strong>」が正式に発表されました。</p>
<p>このニュースは世界中のエンジニアを驚かせており、テック系コミュニティの聖地とも言える<strong>Hacker Newsでは、投稿からわずか数時間で882ポイントという異例の高評価を獲得</strong>しました。これほどまでに注目されているのは、単なるスペックアップを超えた「実用性の進化」があるからです。</p>
<p>「AIの進化が早すぎて追いつけない……」と感じている初心者エンジニアの方も多いかもしれませんが、安心してください。この記事では、Gemini 3.1 Proの何がすごいのか、そして今日からどうやって使いこなすのかを、どこよりも噛み砕いて解説します！</p>
<hr>
<h2 id="gemini-31-proとは">Gemini 3.1 Proとは？</h2>
<p>Gemini 3.1 Proは、Googleが開発した「Gemini」シリーズの最新鋭モデルです。従来のGemini 3の長所を引き継ぎつつ、特に「推論（考える力）」と「文脈の理解（記憶力）」が大幅に強化されています。</p>
<p>エンジニアにとってのGemini 3.1 Proは、例えるなら**「プロジェクトの全コードを記憶し、複雑なバグの修正案を即座に提案してくれる、超優秀な先輩エンジニア」**のような存在です。</p>
<h3 id="なぜproなのか">なぜ「Pro」なのか？</h3>
<p>Googleのモデルには「Ultra」「Pro」「Flash」などのラインナップがありますが、Proモデルは「性能」と「コスト・速度」のバランスが最も優れています。開発者がAPIを使ってアプリケーションに組み込む際、最も選ばれているのがこのProシリーズなのです。</p>
<hr>
<h2 id="ここがすごいgemini-31-proの3つの進化点">ここがすごい！Gemini 3.1 Proの3つの進化点</h2>
<p>従来のモデルと比べて、具体的にどこが変わったのでしょうか？注目すべき3つのポイントを挙げます。</p>
<h3 id="1-熟考型の推論プロセス">1. 「熟考型」の推論プロセス</h3>
<p>Gemini 3.1 Proには、人間が難しい問題を解くときにじっくり考えるような「System 2 Thinking」に近い仕組みが導入されました。これにより、これまでは間違えやすかった複雑な数学の問題や、高度な論理パズル、さらには大規模なシステムのデバッグにおいて、圧倒的に正確な回答を返せるようになっています。</p>
<h3 id="2-200万トークンの超長大コンテキスト">2. 200万トークンの超長大コンテキスト</h3>
<p>「トークン」とは、AIが一度に扱える情報の単位です。Gemini 3.1 Proは、最大で<strong>200万トークン</strong>という驚異的な量を一度に読み込むことができます。
これは、<strong>「厚辞苑数冊分のテキスト」や「数万行のソースコード全体」を丸ごとAIに読み込ませて、その内容について質問できる</strong>ことを意味します。「あの関数の定義、どこにあったっけ？」と探す手間は、もう過去のものになるかもしれません。</p>
<h3 id="3-ハルシネーションもっともらしい嘘の劇的な減少">3. ハルシネーション（もっともらしい嘘）の劇的な減少</h3>
<p>AIが自信満々に嘘をつく現象「ハルシネーション」が、Gemini 3.1 Proでは大幅に抑えられています。特に関数呼び出し（Function Calling）の正確性が増しており、外部ツールやデータベースと連携させた際の信頼性が向上しました。</p>
<hr>
<h2 id="実践pythonでgemini-31-proを動かしてみよう">【実践】PythonでGemini 3.1 Proを動かしてみよう</h2>
<p>それでは、実際にAPIを使ってGemini 3.1 Proを操作してみましょう。初心者の方でも、以下の3ステップで簡単に始められます。</p>
<h3 id="1-ライブラリの準備">1. ライブラリの準備</h3>
<p>ターミナルで以下のコマンドを実行し、最新のSDKをインストールします。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pip install -U google-generativeai
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
      <category>Generative AI</category>
      <category>Google Cloud</category>
      <category>Gemini 3.1 Pro</category>
      <category>Google</category>
      <category>LLM</category>
      <category>Python</category>
    </item>
    <item>
      <title>AIエージェント開発の必須知識：RAGとVector DBの基礎</title>
      <link>https://www.ai2core.com/posts/2026-02-13-rag-basics/</link>
      <pubDate>Fri, 13 Feb 2026 19:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-13-rag-basics/</guid>
      <description>自社データをAIに学習させずに活用する技術、RAG（検索拡張生成）の仕組みを解説。</description>
      <content:encoded><![CDATA[<h1 id="aiエージェント開発の必須知識ragとvector-dbの基礎">AIエージェント開発の必須知識：RAGとVector DBの基礎</h1>
<h2 id="はじめに">はじめに</h2>
<p>「自社の膨大なマニュアルやナレッジベースの内容を、ChatGPTのように対話形式で手軽に引き出したい」
「開発中のAIチャットボットに、社内規定や顧客との過去のやり取りを正確に回答させたい」
「でも、機密情報を含む自社データを、外部のAIサービスに学習データとして渡すのはセキュリティ的に絶対に避けたい」</p>
<p>AI、特に大規模言語モデル（LLM）の活用を検討する多くのエンジニアや開発担当者が、このような課題に直面しているのではないでしょうか。LLMは非常に強力ですが、その知識は特定の時点までのものであり、自社の独自データについては何も知りません。</p>
<p>この課題を解決するために「ファインチューニング」を検討するかもしれません。しかし、ファインチューニングには大量の教師データと高い計算コストが必要な上、情報の更新があるたびにモデルを再学習させるのは現実的ではありません。さらに、AIがもっともらしい嘘をつく「ハルシネーション」という問題も依然として残ります。</p>
<p>本記事では、これらの課題をエレガントに解決する技術として、今、AIエージェント開発の現場でデファクトスタンダードとなりつつある**RAG（Retrieval-Augmented Generation：検索拡張生成）**というアプローチを徹底的に解説します。</p>
<p>RAGは、LLMに自社データを「学習」させるのではなく、必要な情報を「検索」して外部から与えることで、LLMの能力を最大限に引き出す画期的な手法です。そして、その中核を担うのが**Vector DB（ベクトルデータベース）**です。</p>
<p>この記事を読み終える頃には、あなたは以下のことを理解し、自社のAIエージェント開発に活かすための一歩を踏み出せるようになっているはずです。</p>
<ul>
<li>LLMが抱える根本的な課題（知識のカットオフ、ハルシネーション）</li>
<li>なぜファインチューニングだけでは不十分なのか</li>
<li>RAGがどのようにしてこれらの課題を解決するのか、その具体的な仕組み</li>
<li>RAGの心臓部であるEmbeddingとVector DBの役割</li>
<li>PythonとLangChainを使ったRAGの基本的な実装方法</li>
</ul>
<p>それでは、AIエージェント開発の新たな扉を開く、RAGとVector DBの世界へご案内します。</p>
<h2 id="なぜragとvector-dbが重要なのか-llmの限界と従来の課題">なぜRAGとVector DBが重要なのか？ LLMの限界と従来の課題</h2>
<p>RAGの重要性を理解するためには、まずLLMが単体で抱える限界を知る必要があります。</p>
<h3 id="llmが抱える3つの大きな壁">LLMが抱える3つの大きな壁</h3>
<ol>
<li>
<p><strong>知識のカットオフ（Knowledge Cut-off）</strong>
GPT-4のような最先端のLLMでさえ、その知識は学習データが収集された特定の日時で止まっています。例えば、GPT-4の初期モデルは2021年9月までの情報しか持っていません。そのため、それ以降の出来事や、新製品の情報、最新の社内規定について質問しても、答えることができません。ビジネスの世界では情報の鮮度が命であり、この「知識の壁」は致命的な欠点となります。</p>
</li>
<li>
<p><strong>ハルシネーション（Hallucination：幻覚）</strong>
LLMは、事実に基づかない情報を、あたかも真実であるかのように生成することがあります。これをハルシネーションと呼びます。特に、学習データに含まれていない専門的な内容や、社内情報のようなクローズドなドメインについて質問された場合に、この現象は顕著になります。顧客サポート用のAIが誤った製品情報を伝えたり、社内アシスタントが架空の規定を案内したりする事態は、企業の信頼を著しく損なうリスクをはらんでいます。</p>
</li>
<li>
<p><strong>情報セキュリティとプライバシー</strong>
自社の機密情報や顧客の個人情報を扱う場合、それらを外部のLLM提供企業のサーバーに学習データとしてアップロードすることには、非常に大きなセキュリティリスクが伴います。一度学習データとして取り込まれてしまうと、他のユーザーへの回答に利用されてしまう可能性もゼロではなく、データのコントロールを失うことになります。</p>
</li>
</ol>
<h3 id="従来の解決策ファインチューニングとその限界">従来の解決策「ファインチューニング」とその限界</h3>
<p>これらの課題を解決するアプローチとして、以前は「ファインチューニング」が主流でした。これは、既存の学習済みモデルに対して、自社データを含む追加の教師データセットを与えて再学習させる手法です。</p>
<p>ファインチューニングは、LLMに特定の文体や口調を真似させたり、特定のタスク（例えば、要約や感情分析）への性能を特化させたりするのには有効です。しかし、「知識を注入する」という目的においては、いくつかの大きな課題があります。</p>
<ul>
<li><strong>高いコスト</strong>: ファインチューニングには、大量の高品質な教師データ（質問と回答のペアなど）の準備と、モデルの学習を実行するための高価な計算リソース（GPU）が必要です。</li>
<li><strong>知識の更新が困難</strong>: 新しい情報（例えば、週次レポートや新しいマニュアル）を追加したい場合、その都度ファインチューニングをやり直す必要があります。これは時間的にも金銭的にも非効率です。</li>
<li><strong>透明性の欠如</strong>: ファインチューニングされたモデルが、なぜその回答を生成したのか、どの情報を根拠にしているのかを追跡することは非常に困難です。ハルシネーションが起きた場合の原因究明も難しくなります。</li>
</ul>
<p>そこで登場したのが、**RAG（検索拡張生成）**です。RAGは「学習」ではなく「検索」というアプローチで、これらの問題を根本から解決します。</p>
<h2 id="具体的な解決策ragの仕組みとvector-dbの役割">具体的な解決策：RAGの仕組みとVector DBの役割</h2>
<p>RAGは、その名の通り「検索（Retrieval）」でLLMの知識を「拡張（Augmented）」し、回答を「生成（Generation）」するアーキテクチャです。LLMを「非常に優秀だが記憶喪失のコンサルタント」、Vector DBを「完璧な記憶力を持つ外部の専門図書館」に例えると分かりやすいでしょう。</p>
<p>コンサルタント（LLM）は、質問を受けるたびに、まず図書館（Vector DB）へ行って関連資料を調べ（検索）、その資料を読み込みながら（コンテキストとしてプロンプトに含める）、質問に対する的確な回答を生成します。</p>
<p>この仕組みにより、LLMは常に最新かつ正確な情報に基づいて回答できるようになり、ハルシネーションを劇的に抑制できます。</p>
<h3 id="ragの全体像と処理フロー">RAGの全体像と処理フロー</h3>
<p>RAGのシステムは、大きく2つのフェーズに分かれています。</p>
<ol>
<li><strong>データ準備フェーズ（Indexing）</strong>: 事前に自社ドキュメントを検索可能な状態にして、Vector DBに保存しておくフェーズ。</li>
<li><strong>実行フェーズ（Retrieval &amp; Generation）</strong>: ユーザーからの質問を受け取り、Vector DBから関連情報を検索して、LLMが回答を生成するフェーズ。</li>
</ol>
<p>この流れを図で示すと以下のようになります。</p>
<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    subgraph &#34;データ準備フェーズ（Indexing）&#34;
        A[ドキュメント群&lt;br&gt;PDF, TXT, Markdown, etc.] --&gt; B(Load&lt;br&gt;ドキュメント読み込み);
        B --&gt; C(Split&lt;br&gt;テキストを適切なサイズに分割);
        C --&gt; D(Embed&lt;br&gt;分割したテキストをベクトル化);
        D --&gt; E[Vector DB&lt;br&gt;ベクトルと元のテキストを保存];
    end

    subgraph &#34;実行フェーズ（Retrieval &amp; Generation）&#34;
        F[ユーザーからの質問] --&gt; G(Embed&lt;br&gt;質問をベクトル化);
        G --&gt; H{Vector DB&lt;br&gt;類似ベクトル検索};
        E --&gt; H;
        H --&gt; I[関連性の高い&lt;br&gt;テキストチャンク（コンテキスト）];
        F &amp; I --&gt; J(Prompt&lt;br&gt;質問とコンテキストを結合し&lt;br&gt;プロンプトを作成);
        J --&gt; K[LLM (e.g., GPT-4)&lt;br&gt;回答生成];
        K --&gt; L[ユーザーへの回答];
    end
</code></pre><p>それでは、このフローの各要素を詳しく見ていきましょう。</p>
<h3 id="構成要素-embedding---テキストを意味のベクトルに変換する魔法">構成要素①: Embedding - テキストを「意味」のベクトルに変換する魔法</h3>
<p>RAGを理解する上で最も重要な概念が**Embedding（エンベディング、埋め込み）**です。</p>
<p>人間は「犬」と「猫」が似ていて、「犬」と「車」はあまり似ていないことを直感的に理解できます。しかし、コンピュータは単なる文字の羅列としてしか認識できません。Embeddingは、この「意味の近さ」をコンピュータが扱えるように、<strong>テキストを高次元のベクトル（数値の配列）に変換する</strong>技術です。</p>
<p>例えば、以下のように変換されます（次元数は簡略化しています）。</p>
<ul>
<li><code>犬</code>: <code>[0.8, 0.1, 0.3, ...]</code></li>
<li><code>猫</code>: <code>[0.7, 0.2, 0.4, ...]</code></li>
<li><code>車</code>: <code>[-0.5, 0.9, -0.1, ...]</code></li>
</ul>
<p>このベクトル空間上では、意味的に近い単語や文章は、その距離も近くなります。OpenAIの<code>text-embedding-ada-002</code>（1536次元）や、様々なオープンソースのモデルがこの変換を行うために利用されます。</p>
<p>RAGでは、事前にドキュメントの各部分（チャンク）をベクトル化しておき、ユーザーの質問も同じモデルでベクトル化します。そして、<strong>質問のベクトルと最も近いベクトルを持つドキュメントチャンクを探す</strong>ことで、質問に最も関連性の高い情報を特定するのです。</p>
<h3 id="構成要素-vector-db---意味で検索する次世代のデータベース">構成要素②: Vector DB - 意味で検索する次世代のデータベース</h3>
<p>Embeddingによって得られた大量のベクトルデータを効率的に保存し、高速に「意味の近さ（類似度）」で検索するための専用データベースが<strong>Vector DB</strong>です。</p>
<p>従来のRDB（リレーショナルデータベース）が<code>WHERE user_id = 123</code>のように完全一致でデータを検索するのに対し、Vector DBは「このベクトルに最も近いベクトルを探して」という**近似最近傍探索（Approximate Nearest Neighbor, ANN）**を得意とします。</p>
<p>代表的なVector DBには以下のようなものがあります。</p>
<ul>
<li><strong>Chroma</strong>: ローカル環境で手軽に試せるオープンソースのVector DB。プロトタイピングに最適。</li>
<li><strong>FAISS</strong>: Facebook (Meta) AIが開発した、ベクトル類似検索に特化したライブラリ。</li>
<li><strong>Pinecone, Weaviate, Qdrant</strong>: クラウドネイティブなマネージドサービスとして提供されることが多く、スケーラビリティや高度な機能（メタデータフィルタリングなど）が特徴。</li>
</ul>
<p>Vector DBは、ベクトル化されたドキュメントチャンクと、その元となったテキスト本文のペアを保存します。検索時には、質問ベクトルに類似したベクトルを持つチャンクを複数個見つけ出し、その元テキストをLLMへのコンテキストとして渡します。</p>
<h3 id="ragの実装ステップpythonとlangchainによるコード例">RAGの実装ステップ（PythonとLangChainによるコード例）</h3>
<p>それでは、実際にPythonのフレームワーク<code>LangChain</code>を使って、簡単なRAGシステムを構築してみましょう。<code>LangChain</code>は、LLMアプリケーション開発における一連の流れを抽象化し、簡単に実装できるようにしてくれる便利なツールです。</p>
<p>ここでは、架空の「社内副業規定.pdf」というドキュメントの内容について回答するAIエージェントを作成します。</p>
<p><strong>前提</strong>: PDFファイル<code>internal_rules.pdf</code>が手元にあるとします。内容は以下のようなものです。</p>
<blockquote>
<p><strong>第5条（副業・兼業）</strong></p>
<ol>
<li>社員は、会社の許可を得た上で、副業または兼業を行うことができる。</li>
<li>副業を希望する社員は、所定の申請書を人事部に提出し、事前の承認を得なければならない。</li>
<li>会社の競合他社での業務や、会社の信用を損なう可能性のある業務は許可されない。</li>
</ol></blockquote>
<h4 id="ステップ1-環境構築と準備">ステップ1: 環境構築と準備</h4>
<p>まず、必要なライブラリをインストールし、OpenAIのAPIキーを設定します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pip install langchain openai chromadb pypdf tiktoken
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 環境変数にOPENAI_API_KEYを設定</span>
</span></span><span style="display:flex;"><span>os<span style="color:#f92672">.</span>environ[<span style="color:#e6db74">&#34;OPENAI_API_KEY&#34;</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;YOUR_OPENAI_API_KEY&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="ステップ2-ドキュメントの読み込み-load">ステップ2: ドキュメントの読み込み (Load)</h4>
<p><code>LangChain</code>の<code>PyPDFLoader</code>を使って、PDFファイルを読み込みます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain.document_loaders <span style="color:#f92672">import</span> PyPDFLoader
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>loader <span style="color:#f92672">=</span> PyPDFLoader(<span style="color:#e6db74">&#34;internal_rules.pdf&#34;</span>)
</span></span><span style="display:flex;"><span>documents <span style="color:#f92672">=</span> loader<span style="color:#f92672">.</span>load()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;ドキュメントを </span><span style="color:#e6db74">{</span>len(documents)<span style="color:#e6db74">}</span><span style="color:#e6db74"> ページ読み込みました。&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 出力例: ドキュメントを 1 ページ読み込みました。</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="ステップ3-テキストの分割-splitchunking">ステップ3: テキストの分割 (Split/Chunking)</h4>
<p>LLMが一度に処理できるテキスト量（コンテキストウィンドウ）には限りがあるため、また、検索精度を向上させるために、読み込んだドキュメントを小さなチャンクに分割します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain.text_splitter <span style="color:#f92672">import</span> RecursiveCharacterTextSplitter
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># テキスト分割の設定</span>
</span></span><span style="display:flex;"><span>text_splitter <span style="color:#f92672">=</span> RecursiveCharacterTextSplitter(
</span></span><span style="display:flex;"><span>    chunk_size<span style="color:#f92672">=</span><span style="color:#ae81ff">500</span>,  <span style="color:#75715e"># チャンクの最大文字数</span>
</span></span><span style="display:flex;"><span>    chunk_overlap<span style="color:#f92672">=</span><span style="color:#ae81ff">50</span>   <span style="color:#75715e"># チャンク間のオーバーラップ文字数</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 分割の実行</span>
</span></span><span style="display:flex;"><span>split_docs <span style="color:#f92672">=</span> text_splitter<span style="color:#f92672">.</span>split_documents(documents)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;ドキュメントを </span><span style="color:#e6db74">{</span>len(split_docs)<span style="color:#e6db74">}</span><span style="color:#e6db74"> 個のチャンクに分割しました。&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 出力例: ドキュメントを 3 個のチャンクに分割しました。</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>chunk_size</code>と<code>chunk_overlap</code>はRAGの性能を左右する重要なパラメータです。<code>chunk_overlap</code>を設けることで、文の途中でチャンクが分断され、文脈が失われるのを防ぎます。</p>
<h4 id="ステップ4-embeddingとvector-dbへの保存-embed--store">ステップ4: EmbeddingとVector DBへの保存 (Embed &amp; Store)</h4>
<p>分割したチャンクをEmbeddingモデルでベクトル化し、Chroma DBに保存します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain.embeddings.openai <span style="color:#f92672">import</span> OpenAIEmbeddings
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain.vectorstores <span style="color:#f92672">import</span> Chroma
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Embeddingモデルのインスタンス化</span>
</span></span><span style="display:flex;"><span>embeddings <span style="color:#f92672">=</span> OpenAIEmbeddings()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Chroma DBにドキュメントを読み込み、ベクトル化して保存</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># persist_directoryを指定すると、DBがディスクに永続化される</span>
</span></span><span style="display:flex;"><span>vectordb <span style="color:#f92672">=</span> Chroma<span style="color:#f92672">.</span>from_documents(
</span></span><span style="display:flex;"><span>    documents<span style="color:#f92672">=</span>split_docs,
</span></span><span style="display:flex;"><span>    embedding<span style="color:#f92672">=</span>embeddings,
</span></span><span style="display:flex;"><span>    persist_directory<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;./chroma_db&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;Vector DBの準備が完了しました。&#34;</span>)
</span></span></code></pre></td></tr></table>
</div>
</div><p>このコードを実行すると、<code>./chroma_db</code>というディレクトリが作成され、ベクトルデータが保存されます。</p>
<h4 id="ステップ5-検索と生成-retrieve--generate">ステップ5: 検索と生成 (Retrieve &amp; Generate)</h4>
<p>いよいよ、作成したVector DBを使って質問応答チェーンを構築し、実際に質問をしてみます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain.chat_models <span style="color:#f92672">import</span> ChatOpenAI
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> langchain.chains <span style="color:#f92672">import</span> RetrievalQA
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># LLMモデルのインスタンス化</span>
</span></span><span style="display:flex;"><span>llm <span style="color:#f92672">=</span> ChatOpenAI(model_name<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;gpt-4&#34;</span>, temperature<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Vector DBをRetriever（検索機）として使用するQAチェーンを作成</span>
</span></span><span style="display:flex;"><span>qa_chain <span style="color:#f92672">=</span> RetrievalQA<span style="color:#f92672">.</span>from_chain_type(
</span></span><span style="display:flex;"><span>    llm<span style="color:#f92672">=</span>llm,
</span></span><span style="display:flex;"><span>    chain_type<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stuff&#34;</span>,  <span style="color:#75715e"># 最もシンプルなチェーンタイプ</span>
</span></span><span style="display:flex;"><span>    retriever<span style="color:#f92672">=</span>vectordb<span style="color:#f92672">.</span>as_retriever()
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 質問を実行</span>
</span></span><span style="display:flex;"><span>question <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;社員が副業を始めるには、どのような手続きが必要ですか？&#34;</span>
</span></span><span style="display:flex;"><span>response <span style="color:#f92672">=</span> qa_chain<span style="color:#f92672">.</span>run(question)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;質問: </span><span style="color:#e6db74">{</span>question<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;回答: </span><span style="color:#e6db74">{</span>response<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>実行結果の例:</strong></p>
<pre tabindex="0"><code>質問: 社員が副業を始めるには、どのような手続きが必要ですか？
回答: 社員が副業を始めるには、所定の申請書を人事部に提出し、事前の承認を得る必要があります。
</code></pre><p>見事に、PDFの内容に基づいた正確な回答が生成されました。これはLLMが元々持っていた知識ではなく、私たちがVector DB経由で提供した情報に基づいています。これがRAGの力です。</p>
<h2 id="ragのメリットとデメリット">RAGのメリットとデメリット</h2>
<p>RAGは非常に強力な技術ですが、万能ではありません。そのメリットとデメリットを正しく理解し、ファインチューニングとの使い分けを考えることが重要です。</p>
<h3 id="ragのメリット">RAGのメリット</h3>
<ol>
<li><strong>ハルシネーションの劇的な抑制</strong>: LLMは与えられたコンテキスト（検索結果）に基づいて回答を生成するため、事実に基づかない情報を捏造する可能性が大幅に低下します。</li>
<li><strong>知識の更新が容易かつ低コスト</strong>: 新しい情報やドキュメントの更新があった場合、モデル全体を再学習する必要はありません。Vector DB内の該当データを追加・更新するだけで、即座に知識を最新の状態に保てます。</li>
<li><strong>透明性と解釈可能性</strong>: RAGシステムでは、LLMがどのドキュメントチャンクを参考にして回答を生成したのかを追跡できます。これにより、ユーザーに出典を提示することが可能となり、回答の信頼性が向上します。</li>
<li><strong>高いセキュリティ</strong>: 自社の機密情報を外部のLLMに学習させる必要がありません。データは自社で管理するVector DB内に保持し、実行時に必要な情報だけをプロンプトの一部としてLLMに渡すため、データ漏洩のリスクを最小限に抑えられます。</li>
</ol>
<h3 id="ragのデメリットと課題">RAGのデメリットと課題</h3>
<ol>
<li><strong>検索精度への依存</strong>: RAGの性能は、検索コンポーネントの精度に大きく依存します。ユーザーの質問に対して関連性の低いドキュメントしか検索できなかった場合、当然ながら回答の質も低くなります（Garbage In, Garbage Out）。</li>
<li><strong>Chunking戦略の難しさ</strong>: テキストをどのように分割するか（<code>chunk_size</code>、<code>chunk_overlap</code>、分割単位など）は、試行錯誤が必要な職人芸的な側面があります。ドキュメントの構造を無視した不適切なChunkingは、検索精度を著しく低下させます。</li>
<li><strong>システム構成の複雑化</strong>: LLM単体で完結せず、データローダー、テキストスプリッター、Embeddingモデル、Vector DBなど、複数のコンポーネントを組み合わせたパイプラインを構築・運用する必要があります。</li>
<li><strong>レイテンシの増加</strong>: ユーザーからのリクエストごとに「検索」というステップが挟まるため、LLM APIを直接呼び出す場合に比べて、応答に時間がかかる可能性があります。</li>
</ol>
<h3 id="rag-vs-ファインチューニングどちらを選ぶべきか">RAG vs ファインチューニング：どちらを選ぶべきか？</h3>
<p>RAGとファインチューニングは対立するものではなく、補完関係にあります。目的によって使い分けるのが賢明です。</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">観点</th>
          <th style="text-align: left">RAG（検索拡張生成）</th>
          <th style="text-align: left">ファインチューニング</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>主な目的</strong></td>
          <td style="text-align: left"><strong>外部知識の参照</strong>、事実ベースの回答、ハルシネーション抑制</td>
          <td style="text-align: left"><strong>特定のスタイル・口調の模倣</strong>、特定タスクへの性能特化</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>知識の更新</strong></td>
          <td style="text-align: left"><strong>容易</strong>（Vector DBのデータを更新するだけ）</td>
          <td style="text-align: left"><strong>困難</strong>（モデルの再学習が必要で高コスト）</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>ハルシネーション</strong></td>
          <td style="text-align: left"><strong>抑制しやすい</strong>（根拠となる情報が与えられるため）</td>
          <td style="text-align: left">抑制しにくい（モデル内部の知識に依存するため）</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>出典の明示</strong></td>
          <td style="text-align: left"><strong>可能</strong></td>
          <td style="text-align: left">不可能</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>適した用途</strong></td>
          <td style="text-align: left">社内ナレッジQA、マニュアル検索、最新情報に基づく回答生成</td>
          <td style="text-align: left">特定のキャラクター模倣、メール自動作成、要約タスクの精度向上</td>
      </tr>
  </tbody>
</table>
<p><strong>使い分けの指針</strong>:</p>
<ul>
<li><strong>事実に基づいた正確な知識</strong>を扱いたい場合は、まず<strong>RAG</strong>を検討します。</li>
<li>LLMの**振る舞い（口調、文体、思考プロセス）**を特定の形に変えたい場合は、<strong>ファインチューニング</strong>が有効です。</li>
<li>両方を組み合わせる、つまりファインチューニングしたモデルをRAGの生成器（Generator）として使用することで、特定のスタイルで、かつ正確な情報に基づいた回答を生成する、という高度なアプローチも可能です。</li>
</ul>
<h2 id="現場で使える実践的なtips">現場で使える実践的なTips</h2>
<p>基本的なRAGの実装は比較的簡単ですが、実運用で高い性能を出すためにはいくつかの工夫が必要です。</p>
<ol>
<li>
<p><strong>高度なChunking戦略</strong>:
単純な固定長分割ではなく、ドキュメントの構造を活かしましょう。Markdownであれば<code>MarkdownHeaderTextSplitter</code>、ソースコードであれば<code>CodeSplitter</code>など、<code>LangChain</code>には様々なスプリッターが用意されています。これにより、意味のあるまとまりでテキストを分割でき、検索精度が向上します。</p>
</li>
<li>
<p><strong>Embeddingモデルの選定</strong>:
OpenAIのモデルは高性能ですが、コストがかかります。Hugging Faceで公開されているオープンソースのモデル（例: <code>intfloat/multilingual-e5-large</code>など日本語性能が高いもの）をセルフホストすることで、コストを抑えつつ、特定のドメインに特化した性能を得られる場合があります。</p>
</li>
<li>
<p><strong>ハイブリッド検索（Hybrid Search）</strong>:
Vector Search（意味検索）は万能ではありません。特定の製品名や型番、人名といった固有名詞を含むクエリには、従来のキーワード検索（BM25アルゴリズムなど）の方が強い場合があります。この2つを組み合わせたハイブリッド検索を実装することで、検索の網羅性と精度を両立させることができます。多くのマネージドVector DBサービスがこの機能を提供しています。</p>
</li>
<li>
<p><strong>Retrieverのチューニング</strong>:</p>
<ul>
<li><strong>検索ドキュメント数（k）の調整</strong>: <code>retriever=vectordb.as_retriever(search_kwargs={&quot;k&quot;: 5})</code> のように、一度に検索するチャンク数を調整します。多すぎるとノイズが増え、少なすぎると必要な情報が欠落します。</li>
<li><strong>Re-ranking</strong>: 最初に多めにチャンクを検索（例: k=20）し、その後、より軽量で高速なCross-Encoderモデルなどを使って、質問との関連性を再計算し、上位のチャンク（例: top 5）だけをLLMに渡す手法です。ノイズを減らし、コンテキストの質を高めるのに非常に有効です。</li>
</ul>
</li>
<li>
<p><strong>メタデータフィルタリング</strong>:
ドキュメントをVector DBに保存する際に、作成日、カテゴリ、著者などのメタデータを一緒に格納します。これにより、「2024年以降に作成された、&ldquo;技術部&quot;カテゴリのドキュメントの中から検索する」といった、より高度な絞り込み検索が可能になります。これは実用的なアプリケーションを構築する上で必須の機能です。</p>
</li>
</ol>
<h2 id="まとめ">まとめ</h2>
<p>本記事では、AIエージェント開発における必須知識として、RAG（検索拡張生成）と、その中核をなすVector DBの基礎について、仕組みから具体的な実装例、実践的なTipsまでを網羅的に解説しました。</p>
<p><strong>RAGは、LLMに自社のデータを「学習」させるのではなく、必要な情報をリアルタイムに「検索」して与えることで、LLMの持つハルシネーションや知識の陳腐化といった課題を解決し、ビジネスの現場で安全かつ効果的に活用するための強力なパラダイムです。</strong></p>
<p>その心臓部であるEmbeddingとVector DBは、非構造化データであるテキストを「意味」で扱えるようにする革新的な技術であり、これからのAIアプリケーション開発においてますます重要性を増していくでしょう。</p>
<p>今日学んだことをまとめると、以下のようになります。</p>
<ul>
<li>LLMの限界を克服するため、RAGは「検索」と「生成」を組み合わせる。</li>
<li>Embeddingがテキストを意味的ベクトルに変換し、Vector DBがそれを高速に検索する。</li>
<li><code>LangChain</code>のようなフレームワークを使えば、RAGパイプラインを効率的に構築できる。</li>
<li>RAGは知識の注入に、ファインチューニングは振る舞いの調整に適している。</li>
<li>実用的な性能を出すには、Chunking、Hybrid Search、Re-rankingなどの高度なテクニックが鍵となる。</li>
</ul>
<p>RAGはまだ発展途上の技術であり、日々新しい手法が提案されています。しかし、本記事で解説した基礎をしっかりと理解していれば、その進化にキャッチアップしていくことは十分に可能です。</p>
<p>まずは、あなた自身のPCで、身近なドキュメントを使って小さなRAGシステムを構築してみてください。自分のデータに基づいてAIが的確な回答を生成する体験は、きっと新たなインスピレーションを与えてくれるはずです。この記事が、あなたのAIエージェント開発の第一歩となることを願っています。</p>
]]></content:encoded>
      <category>AI Technology</category>
      <category>RAG</category>
      <category>Vector DB</category>
      <category>LLM</category>
    </item>
  </channel>
</rss>
