<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[エージェント型AI - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[エージェント型AI - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/jp/search-labs/blog/category/agentic-ai</link>
    </image>
    <link>https://www.elastic.co/jp/search-labs/blog/category/agentic-ai</link>
    <atom:link href="https://www.elastic.co/jp/search-labs/rss/category/agentic-ai.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[jp]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 11:51:59 GMT</lastBuildDate>
  <item>
    <title><![CDATA[137,000人、人間の判断はゼロ：Elasticsearchによるエージェンティックな災害対応]]></title>
    <description><![CDATA[ハリケーンの襲来時に、ディスパッチャーを必要とせず、Kibana検出ルール、ワークフロー、AIエージェントを使用して、7か所の施設にいる137,000人の軍人を自動的に移動させた方法をご覧ください。]]></description>
    <content:encoded><![CDATA[<p>Elastic社は、7つの基地にまたがる13万7000人の軍関係者の自動避難を、人間の介入なしに調整しました。カテゴリー4のハリケーンがハンプトン・ローズの海岸線を直撃します。Elasticsearchの地理空間エンリッチメントにより、インデックス作成時に影響区域内のすべての施設が特定され、Kibanaの検出ルールがトリガーされ、ワークフローがAIエージェントの会話を開始します。エージェントは収容能力、距離、拠点の適合性を分析した上で、1回の処理で16件の避難および受け入れ通知を送信します。未加工のGDACSイベントから連携アクションまで、すべて自動で実行されます。</p><p>毎年、自然災害によって、緊急事態管理者、軍司令官、公安当局者は、限られた時間の中で重大な決断を迫られることになります。従来、こうした意思決定は、緊急連絡網やスプレッドシート、そして何十人もの人々に分散した組織的知識に依存しています。調整にかかる手間だけでも、貴重な時間が奪われます。</p><p>本記事では、脅威を検知し、ロジスティクスを推論して自動的にアクションを実行する災害対応向けの即応性に優れたエージェント型連携システムをElasticがどのように提供できるかをご紹介します。具体例として、シミュレーションを構築しました。ハンプトン・ローズの海岸線を脅かす架空のカテゴリー4のハリケーンによって、7つの軍事施設にわたる137,000人以上の要員の自動再配置がトリガーされるというものです。</p><p><strong>免責事項：</strong><strong>これはデモンストレーション目的で作成された完全に架空のシナリオです。 </strong>ハリケーンELARA-26は実在しません。施設の所在地は、公開されている実際の地理データ（米国国防総省［DoD］のMilitary Installations, Ranges, and Training Areas［MIRTA］データセット）に基づいたものですが、人員数、収容可能人数、資産、連絡先メールアドレス、ミッションプロファイルなどの運用データはすべて完全に架空のものです。このデモに含まれる内容は、実際の軍事的な即応態勢、能力、または運用手順を反映したものではありません。</p><h2>自動化された災害対応に地理空間とエージェント型の連携が必要な理由</h2><p>自然災害によって重要なインフラが脅かされると、次のような調整の課題が直ちに発生します。</p><ul><li><p>影響ゾーンにある施設はどれですか？</p></li><li><p>何人の人員を移動させる必要があるか？</p></li><li><p>どこに行けばいいのか、そしてそれらの施設には収容能力があるか？</p></li><li><p>今すぐ連絡する必要があるのは誰か？</p></li></ul><p>これらの質問は待ってくれません。 その答えも同様です。</p><h2>パイプラインのデプロイ：要件とセットアップ</h2><p><a href="https://github.com/tehbooom/elastic_natural_disaster/blob/main/README.md">サンプルリポジトリのこちら</a>にある手順に従って、<a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/connect-self-managed-cluster-to-eis#set-up-eis-with-cloud-connect">Cloud Connect</a>を介してElastic Inference Service（EIS）を使用するローカルElasticクラスターをデプロイします。</p><h2>Elasticsearchのエージェント型災害対応パイプラインの仕組み</h2><p>このパイプラインには、エンドツーエンドで連携して機能する7つのレイヤーがあります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf09bfae87ab35bec/6a4693ef31bdbbe3ef8b33ae/61814cddea0409162fb057c2113e0a496c105238-1999x275.png" alt="Pipeline flowchart Alt text: Horizontal flowchart with seven labeled boxes connected by arrows: GDACS feed, ingest pipeline, enrich (geo_shape), detection rule, workflow, AI agent, and email." /><ol><li><p><strong>データインジェスト：</strong>Elasticsearchに送信されたGlobal Disaster Alert and Coordination System（GDACS）の災害イベント。</p></li><li><p><strong>インジェストパイプライン</strong>：GeoJSONがインジェストされ、Elastic Common Schema（ECS）に正規化されます。</p></li><li><p><strong>地理空間エンリッチメント：</strong>イベントの影響エリアのポリゴンがインデックス登録された軍事施設の境界と照合されます。</p></li><li><p><strong>アラート：</strong>災害がいずれかのインストール環境と交差すると、Kibanaの検出ルールが作動します。</p></li><li><p><strong>ワークフローの自動化：</strong>アラートによってKibanaワークフローがトリガーされ、AIエージェントの会話が開始されます。</p></li><li><p><strong>AI推論：</strong>エージェントは、影響を受ける施設、その資産、および最寄りの支援施設をもとに推論を行い、すべての資産と人員の再配置を決定します。</p></li><li><p><strong>メール通知</strong>：エージェントは、出入りする人員や資産について、すべての受信者にメールを送信します。</p></li></ol><p>各レイヤーを見てみましょう。</p><h2>ステップ1：地理的境界に基づいて軍事施設をインデックス化</h2><p>基盤となるのは、<a href="https://source.coop/seerai/hifld/military-installations-ranges-and-training-areas-mirta-dod-sites---boundaries">source.coop/seerai/hifld</a>のDoD MIRTAデータセットです。このデータセットは、各施設に対してPointタイプのgeo_shapeを提供します。これは完全な境界ポリゴンではなく、重心座標となります。</p><p>mitra-facilitiesインデックス内の各インストールドキュメントは、MIRTAが提供する内容に加えて、運用プロファイルデータ（すべて架空のもの）でエンリッチされています。</p>{
  "entity_name": "Naval Station Norfolk",
  "branch_of_service": "Navy",
  "mission_function_type": "fleet_support",
  "personnel_count": 50000,
  "housing_capacity": 55000,
  "temporary_housing_capacity": 10000,
  "logistics_capabilities": ["fuel", "airlift", "sealift", "medical"],
  "available_assets": [
    { "type": "helicopters", "count": 24 },
    { "type": "transport_vehicles", "count": 150 }
  ],
  "contact_email": "norfolk.ops@navy.mil.gov.fake",
  "operational_status": "act",
  "is_joint_base": false,
  "entity_geo_location": { "type": "polygon", "coordinates": [...] }
}<p>このリッチなインデックスによって、AIエージェントは単に「近くに基地がある」というだけでなく、「収容能力があり、任務の種類が適合し、到着する資産を受け入れるための物流を備えた基地がある」という、インテリジェントな割り当て決定を下すことが可能になります。</p><h2>ステップ2：GDACSイベントの取り込みと正規化</h2><p>GDACSは、地震、熱帯低気圧、洪水、山火事、火山、干ばつに関するリアルタイムのGeoJSONを公開しています。このフィードをデータストリーム（logs-gdacs.events-*）に取り込み、生のGeoJSONをECSフィールドに正規化するカスタム取り込みパイプラインを使用します。</p><p>GDACS取り込みパイプラインは、注目すべきいくつかの処理を実行します。</p><p><strong>ジオメトリ抽出：</strong>重心はマップ表示用にgeo_pointとして格納され、影響ポリゴンはgdacs.affected_areaにgeo_shapeとして格納されます。これは、後で交差クエリに使用されるフィールドです。</p><p><strong>重大度の正規化：</strong>災害の種類ごとに、重大度のスケールが異なります。熱帯低気圧は風速（km/h）で、地震はリヒター・マグニチュードで測定されます。パイプラインはそれらすべてを正規化された0～100のスコアにマッピングします。</p>// 取り込みパイプラインからの簡単なスニペット
if (type == 'TC') {
  norm = Math.min(100.0, Math.max(0.0, (val - 40.0) / 2.6));
} else if (type == 'EQ') {
  norm = Math.min(100.0, Math.max(0.0, (val - 4.0) * 20.0));
}<p>正規化された深刻度スコアはその後、検出ルールのアラート深刻度マッピングで使用されるseverity_levelラベル（low、medium、high、critical）にマッピングされます。</p><p><strong>ECSとの整合性：</strong>event.kind: alert、event.category: threat、event.start/event.endにマッピングされたタイムスタンプ、重複排除のための安定したフィンガープリントベースの_id。</p><h2>ステップ3：地理空間エンリッチメント：インデックス作成時に影響を受ける施設を特定する</h2><p>Elasticsearchのgeo_matchエンリッチポリシーは、インデックス時に災害ポリゴンを各施設の境界と照合するため、クエリ時の結合は不要です。検索時にクエリを実行する代わりに、取り込みパイプラインで<strong>エンリッチプロセッサー</strong>を使用して、<em>ドキュメントのインデックス時に</em>災害の影響ポリゴンを各施設の境界と照合します。</p><p>エンリッチポリシーはgeo_matchポリシーです。</p>{
  "geo_match": {
    "indices": "mitra-facilities",
    "match_field": "entity_geo_location",
    "enrich_fields": [
      "entity_name",
      "entity_type",
      "entity_station_number",
      "entity_geo_city_name",
      "entity_geo_region_name"
    ]
  }
}<p>プロセッサーは取り込みパイプラインの最後で実行されます。</p>{
  "enrich": {
    "policy_name": "facilities-geo",
    "field": "gdacs.affected_area",
    "target_field": "affected_facilities",
    "shape_relation": "INTERSECTS",
    "max_matches": 128
  }
}<p>INTERSECTSは、境界線が災害ポリゴンに接しているか重複しているすべての施設や、部分的な交差さえも検出します。その結果、すべてのGDACSイベントドキュメントは、どの施設が影響区域内にあるかを正確に示すaffected_facilitiesネスト配列とともに格納されます。結合クエリは不要です。</p><h2>ステップ4：検出ルール：施設への影響に関するアラート</h2><p>Kibanaの検出ルールは、logs-gdacs.events-*データストリームを監視し、GDACSイベントが少なくとも1つの影響を受けた施設でエンリッチされたときにトリガーされます。</p>クエリ：affected_facilities: { entity_name: * }<p>このルールは1時間ごとのスケジュールで実行され（現在から1時間前までの期間を対象）、動的な重大度マッピングを使用します。取り込みパイプラインによって計算されるgdacs.severity_levelフィールドが、アラートの重大度を自動的に決定します。</p><p>アラートの深刻度も、フィールドマッピングを通じてリスクスコアに影響を与えます。</p>"risk_score_mapping": [
  {
    "field": "gdacs.normalized_severity",
    "operator": "equals",
    "value": ""
  }
]<p>ルールがトリガーされると、設置名、タイプ、場所を含むエンリッチされたaffected_facilities配列を含む完全なアラートコンテキストが、ダウンストリームのKibanaワークフローに渡されます。</p><h2>ステップ5：ワークフローの自動化：アラートからエージェントへの橋渡し</h2><p>Kibanaワークフローは、検出から対応への引き継ぎを処理します。自然災害対応ワークフローは、以下のアラートによって開始されます。</p>triggers:
  - type: alert
steps:
  - name: start_convo
    type: kibana.request
    with:
      method: "POST"
      path: "/api/agent_builder/converse"
      body:
        agent_id: "mitra.response"
        input: "New Natural Disaster Alert: {{ event.alerts | json }}"<p>アラートのペイロード全体（災害の種類、重大度、被災地域、影響を受けた設備の一覧）が、初期コンテキストとしてAIエージェントに転送されます。そこから先はエージェントが処理を引き継ぎます。</p><h2>ステップ6：AIエージェント：データから連携したアクションへ</h2><p>mitra.responseエージェントは、アラートペイロード全体を受け取り、単一のエージェントループで対象範囲を評価し、受け入れ施設を特定し、人員を割り当て、避難通知と受け入れ通知を送信します。これらはすべて、人間の介入なしで行われます。</p><p>エージェントには2つのツールが用意されています。</p><ul><li><p><strong>mitra.nearest_facility</strong>は、geo_shapeクエリを使用してmitra-facilitiesインデックスにクエリを実行し、指定された座標からの距離で並べ替えて、利用可能な収容能力を持つ周辺のアクティブな施設を最大50件返します。</p></li><li><p><strong>mitra.send_email</strong>は、施設オブジェクトのJSON配列を反復処理し、フォーマットされた避難通知または受入通知を送信します。</p></li></ul><p>エージェントの指示セットは、明確なワークフローを定義します。</p><ol><li><p><strong>状況を評価する。</strong>アラートをパースし、影響を受ける施設を特定して、災害の範囲を判断します。</p></li><li><p><strong>移行が必要な対象のインベントリを作成する。</strong>施設ごとの人員数、重要資産、住宅要件。</p></li><li><p><strong>再配置先施設を見つける。</strong>影響を受ける各施設に対してmitra.nearest_facilityを呼び出し、危険区域内にある施設を除外します。</p></li><li><p><strong>配分に関する決定を行う。</strong>単一施設ソリューションと複数施設ソリューションの比較検討、支店間の互換性、収容能力、資産サポートなどを考慮します。</p></li><li><p><strong>調整メールを送信する。</strong>避難元施設に避難指示を、受入施設に受入通知を送信します。</p></li><li><p><strong>サマリーレポートを作成する。</strong>影響を受けるすべての施設、総人員、移動した資産、再配置先施設、懸念事項の簡単なサマリーを作成し、確認のためにチャットに出力します。</p></li></ol><p>エージェントの割り当てロジックは、現実世界の制約に従います。住宅収容能力を超えないこと、可能な場合は同一部隊内での移転を優先すること、複数部隊にまたがる移転の場合は共同拠点を使用すること、そして移動時間を最小限に抑えるために距離を優先することなどが含まれます。</p><h3>最寄りの施設ツール</h3><p>基盤となるワークフロークエリでは、circleフィルター付きのgeo_shapeと_geo_distanceソートを使用します。</p>"query": {
  "bool": {
    "filter": [
      {
        "geo_shape": {
          "entity_geo_location": {
            "shape": {
              "type": "circle",
              "coordinates": [{{ inputs.lon }}, {{ inputs.lat }}],
              "radius": "5000km"
            },
            "relation": "intersects"
          }
        }
      },
      { "term": { "operational_status.keyword": "act" } }
    ]
  }
},
"sort": [
  {
    "_geo_distance": {
      "entity_geo_point": { "lat": {{ inputs.lat }}, "lon": {{ inputs.lon }} },
      "order": "asc",
      "unit": "km"
    }
  }
],
"script_fields": {
  "available_capacity": {
    "script": {
      "source": "Math.max(0, doc['housing_capacity'].value - doc['personnel_count'].value)"
    }
  }
}<p>利用可能な収容人数は、収容可能人数から現在の人員数を差し引くスクリプトフィールドを介して、クエリ実行時に計算されます。エージェントは、上限を超えないように、これを使用してデスティネーション間で人員を割り当てます。</p><h2>ハリケーンELARA-26：137,000人の人員をエンドツーエンドでエージェント的に調整</h2><p>ハリケーン「ELARA-26」は、バージニア州のハンプトン・ローズ地域に上陸すると予測されているカテゴリー4の暴風雨（最大風速213 km/h）です。GDACSイベントが取り込まれると、影響を受けるエリアのポリゴンは、この地域にある7つの主要な軍事施設と交差します。検出ルールがトリガーされます。ワークフローによってエージェントの会話が開始されます。</p><p>単一のエージェントループ内で、エージェントは以下を行いました。</p><ul><li><p>影響エリア内の7つの施設（合計137,372名の人員）を特定しました。</p></li><li><p>嵐の進路外にある受け入れ施設を見つけるために、mitra.nearest_facilityを呼び出しました。</p></li><li><p>利用可能な収容能力と距離に基づいて、9つの受入施設に人員を分散配置しました。</p></li><li><p>影響を受けた7つの施設すべてに避難指示を生成して送信しました。</p></li><li><p>受入通知を作成し、9件の受入施設すべてに送信した。</p></li><li><p>以下のような、調整に関する詳細な概要を作成しました。</p></li></ul><p><strong>避難対象施設：</strong></p><p>施設</p><p>人件費</p><p>ノーフォーク海軍基地</p><p>50,000</p><p>統合遠征基地リトルクリーク・フォートストーリー</p><p>18,000</p><p>オシアナ海軍航空基地</p><p>15,355</p><p>オシアナ海軍航空基地ダムネック別館</p><p>17,509</p><p>NG州立軍事基地キャンプ・ペンドルトン</p><p>9,707</p><p>ラングレー・ユースティス統合基地</p><p>15,000</p><p>ヨークタウン海軍兵器基地</p><p>11,801</p><p><strong>受入施設：</strong></p><p>施設</p><p>距離</p><p>避難受入人員</p><p>フォート・グレッグ・アダムズ</p><p>97 km</p><p>約40,000</p><p>クワンティコ海兵隊基地</p><p>148 km</p><p>約30,000</p><p>インディアン・ヘッド海軍支援施設</p><p>151 km</p><p>約30,000</p><p>アンドルーズ統合基地</p><p>180 km</p><p>約30,000</p><p>パタクセント・リバー海軍航空基地</p><p>141 km</p><p>約10,000</p><p>NG MTA キャンプ・バトナー</p><p>174 km</p><p>約5,000</p><p>NG Bethany Beach トレーニングサイト</p><p>209 km</p><p>約4,707</p><p>リバナステーション</p><p>140 km</p><p>約7,500</p><p>国防総合補給センター</p><p>22 km</p><p>約6,000</p><p>再配置された資産には、輸送車両、ヘリコプター、パトロール艇、医療ユニット、工作車両、発電機、給水トレーラー、シェルターキット、通信システムが含まれます。</p><h3>自動メール通知</h3><p>エージェントが割り当て計画を確定すると、mitra.send_emailを呼び出し、1回の処理で16通のメールを送信しました。つまり、影響を受けた7つの施設すべてへの避難指示と、受け入れ先となる9つの施設すべてへの受け入れ通知です。各メッセージには、受入先施設、到着する人員数、移動する資産、調整担当の連絡先が含まれていました。以前であれば電話連絡網に何時間もかかっていた作業が、エージェントによる推論が完了した瞬間に自動的に完了しました。</p><h3>RAGとポリシーグラウンディングによるエージェント型災害対応の拡張</h3><p>このデモは、収容能力の数値、距離、稼働状況などの構造化データのみに基づいています。Elasticのセマンティック検索およびRetrieval-Augmented Generation（RAG）機能に2つの要素を追加することで、エージェントを大幅にスマートにすることができます。</p><p><strong>過去の対応検索：</strong>過去の事後レポート、連邦緊急事態管理庁（FEMA）のインシデント概要、災害対応記録をベクトル埋め込みとしてインデックス化します。新しいイベントが発生すると、エージェントは類似のイベントがどのように対応されたかをセマンティックに検索し、単なるキャパシティの計算だけでなく組織の知識を活用して割り当ての意思決定を行うことができます。</p><p><strong>ポリシーとドクトリンの根拠付け：</strong>国防総省（DoD）の緊急事態管理指令、施設の業務継続計画（COOP）、司令官のガイダンスをインデックス化します。エージェントは対応を規定する実際のポリシーを取得して引用できるため、すべての決定が推論ではなくドクトリンに基づいていることを保証できます。</p><p>どちらも同じElasticネイティブのアプローチに従います。推論パイプラインがインデックス作成時に埋め込みを生成し、セマンティック検索ツールがエージェントに公開されます。 調整パイプラインは同じままです。エージェントはさらに賢くなります。</p><h2>公共部門のエージェント型対応にElasticsearchが最適なプラットフォームである理由</h2><p>これはチャットボットではなく、ダッシュボードでもありません。脅威を検知し、複雑なロジスティクス問題を推論して、人間の介在なしに137,000人の移転を調整した、応答性の高いエージェント型ワークフローシステムです。このような成果が可能になるのは、依存するすべての機能が単一の統合プラットフォームに集約されているからです。</p><p>Elasticsearchの地理空間サポート（geo_point、geo_shape、エンリッチメントポリシー、距離ベースの並べ替え）は、交差検出や施設検索の大規模な実行を可能にする空間推論を処理します。セマンティック検索とベクトル埋め込みによりエージェントを事実にグラウンディングさせ、AIの推論がハルシネーションによる推測ではなく、データに実際に存在する情報に基づくようにします。Kibanaの検出エンジン、Workflows、Agent Builder、Agent Builderツールにより、外部の連携コードを必要とすることなく、未加工のイベントから連携したアクションへと至るパイプラインとしてこれらすべてが統合されます。</p><p>Elasticのようにこれらを統合できるプラットフォームは他にありません。リアルタイムのインデキシング、地理空間の精度、セマンティック検索、エージェンティックオーケストレーションのすべてを1つのスタックに統合し、エンタープライズグレードのセキュリティとオブザーバビリティが組み込まれていることこそが、これらのうち1つには優れていても残りは自分たちでつなぎ合わせる必要があるツールと、Elasticを一線を画す存在にしている理由です。</p><h2>緊急事態管理、消防、法執行、公衆衛生のためのエージェント型地理空間対応</h2><p>人、施設、リアルタイムイベントが交差するあらゆる場所に、同じアーキテクチャーが適用されます。特定のデータは変更され、パイプラインは変わりません。</p><p><strong>緊急事態管理：</strong>FEMAや州の緊急事態管理局は、避難所の位置、集結地域、脆弱な人々を、到来する米国国立気象局（NWS）の悪天候ポリゴンと照らし合わせて地図化し、嵐が上陸する前にリソースの事前配置を自動的に開始できます。</p><p><strong>消防・救急医療サービス：</strong>消防署は、部隊の位置や対応ゾーンを山火事の境界線や建物火災のクラスターに重ね合わせ、適切な装備を備えた最も近くの対応可能な部隊へ相互応援要請を自動的にルーティングできます。</p><p><strong>法執行機関：</strong>機関は、進行中のインシデントの位置を学校区域、重要インフラ、警察官の位置と関連付け、手動でのトリアージを待たずに、位置情報を考慮したロックダウン通知やリソースの派遣をトリガーできます。</p><p><strong>公立学校の安全対策：</strong>学区は、キャンパスの敷地境界に対するリアルタイムの脅威フィードを監視できます。脅威が学校の敷地境界に差し掛かった場合、ディスパッチャーが電話を取る前に、エージェントは直ちに管理者に通知し、ロックダウンの連絡を開始し、法執行機関の対応を調整できます。</p><p><strong>公衆衛生：</strong>保健機関は、疾病監視データや環境有害区域を診療所の所在地、人口密度レイヤー、供給拠点の在庫と照合し、最も必要とされている場所にリソースを配分できます。</p><p>セクター</p><p>ユースケース</p><p>Elasticの機能</p><p>緊急管理</p><p>避難所の場所をNWSの悪天候ポリゴンと照合する</p><p>geo_shapeエンリッチメント+Kibanaワークフロー</p><p>消防およびEMS</p><p>山火事の境界線に部隊の位置を重ね合わせる</p><p>地理空間ルーティング+最寄り施設クエリ</p><p>法執行機関</p><p>事件と学校区域および警察官の配置を関連付ける</p><p>位置情報に基づくアラートルール + 担当者の派遣</p><p>公立学校の安全</p><p>キャンパス境界に対する脅威フィードを監視する</p><p>検出ルール+自動通知</p><p>公衆衛生</p><p>危険区域を診療所の場所や物資拠点と照合する</p><p>セマンティック検索 + 地理空間エンリッチメント</p><p>データはシナリオごとに異なります。取り込み、インデックス作成時のエンリッチメント、交差の検出、自律的な対応のトリガー、そしてアクションという根本的なパターンはすべて同じです。Elasticは、公共機関が一度構築すればどこでも適用できるプラットフォームを提供します。</p><p><em>本記事に記述されているあらゆる機能ないし性能のリリースおよびタイミングは、Elasticの単独裁量に委ねられます。現時点で提供されていないあらゆる機能ないし性能は、すみやかに提供されない可能性、または一切の提供が行われない可能性があります。</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-agentic-disaster-response</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-agentic-disaster-response</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[Kibana]]></category>
    <dc:creator><![CDATA[Alec Carpenter]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt969cad2694920de4/6a4693f37746672ad42675b5/cb292a501835472598dee30bef25c77afc54db6c-720x420.png" length="0" type="image/png"/>
    <pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[MastraとElasticsearchを使用してエージェント型AIアプリケーションを構築する方法]]></title>
    <description><![CDATA[MastraとElasticsearchを使用してエージェント型AIアプリケーションを構築する方法を実例を通じて学びましょう。]]></description>
    <content:encoded><![CDATA[<p>この記事では、<a href="https://mastra.ai/">Mastra</a> TypeScriptフレームワークを使用して、<a href="https://www.elastic.co/elasticsearch">Elasticsearch</a>と連携するエージェント型アプリケーションを構築する方法をご紹介します。</p><p>最近、<a href="https://github.com/mastra-ai/mastra">mastra-ai/mastra</a>オープンソースプロジェクトに、ベクトルデータベースとしてのElasticsearchのサポートを追加しました。この新機能により、MastraでElasticsearchをネイティブに使用して埋め込みを保存できます。ベクターに加えて、Elasticsearchはコンテキストエンジニアリングの要件を満たすための高度な機能群を提供します（例：<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">ハイブリッド検索と再ランク付け</a>）。</p><p>この記事では、Elasticsearchを使用したRetrieval-Augmented Generation（RAG）アーキテクチャを実装するエージェントの作成について詳しく説明します。エージェント的なアプローチを使用して、Elasticsearchに保存されているSF映画データのコーパスを操作するデモプロジェクトを紹介します。このプロジェクトは<a href="https://github.com/elastic/mastra-elasticsearch-example">elastic/mastra-elasticsearch-example</a>で入手できます。</p><h2>Mastra</h2><p>Mastraは、エージェント型AIアプリケーションを作成するためのTypeScriptフレームワークです。</p><p>Mastraのプロジェクト構造は以下のようになります：</p>src/
├── mastra/
│   ├── agents/
│   │   └── weather-agent.ts
│   ├── tools/
│   │   └── weather-tool.ts
│   ├── workflows/
│   │   └── weather-workflow.ts
│   ├── scorers/
│   │   └── weather-scorer.ts
│   └── index.ts
├── .env.example
├── package.json
└── tsconfig.json<p>Mastraでは、<a href="https://mastra.ai/docs/agents/overview">エージェント</a>、<a href="https://mastra.ai/docs/agents/using-tools">ツール</a>、<a href="https://mastra.ai/docs/workflows/overview">ワークフロー</a>、<a href="https://mastra.ai/docs/evals/overview">スコア</a>を作成できます。</p><p><strong>エージェント</strong>は、インプットとしてメッセージを受け取り、アウトプットとして回答を生成するクラスです。エージェントは、ツール、大規模言語モデル（LLM）、およびメモリを使用できます（図1）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f7484f7501997dc/6a170417cdacbffb5a7d28f6/f6aca2dcc7fcc45d25e06681649be1b2b7eb6781-706x721.png" alt="Mastraでエージェントがどのように動作するかを示す図です。" /><p>エージェントの<strong>ツール</strong>は、「外部の世界」と対話することを可能にします。たとえば、ウェブAPIと通信したり、Elasticsearchにクエリを実行するような内部操作を行ったりします。<strong>メモリ</strong>コンポーネントは、過去の入力と出力を含む会話の履歴を格納するために非常に重要です。この格納されたコンテキストにより、エージェントは過去の対話を活用して、将来の質問に対してより情報に基づいた関連性の高い回答を提供できます。</p><p><strong>ワークフロー</strong>を使用すると、単一のエージェントの推論に頼るのではなく、明確で構造化されたステップを使用して複雑なタスクのシーケンスを定義できます（図2）。タスクをどのように分割し、データをどのように移動させ、何をいつ実行するかを網羅的に制御できます。ワークフローは、デフォルトで組み込みの実行エンジンを使用して実行されますが、<a href="https://mastra.ai/docs/deployment/workflow-runners">ワークフローランナー</a>にデプロイすることもできます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd82ea661569e1018/6a170419dc55de3adde00cc9/0dce161cf7891207015dc87532b5b90df1822432-880x252.png" alt="Mastraのワークフローの例。" /><p>Mastraでは、スコアを定義することもできます。スコアは、モデル評価、ルールベース、統計的な方法を用いてエージェントの出力を評価する自動テストです。スコアラーは<em>スコア</em>を返します。これは、出力が評価基準をどれだけ満たしているかを定量化する数値（通常0から1の間）です。これらのスコアにより、パフォーマンスを客観的に追跡し、さまざまなアプローチを比較し、AIシステムの改善すべき分野を特定することができます。スコアラーは、独自のプロンプトやスコアリング関数でカスタマイズできます。</p><h2>Elasticsearch</h2><p>デモプロジェクトを実行するには、Elasticsearchインスタンスを実行する必要があります。<a href="https://www.elastic.co/cloud">Elastic Cloud</a>で無料トライアルを有効化するか、<a href="https://github.com/elastic/start-local"><code>start-local</code></a>スクリプトを使ってローカルにインストールできます。</p>curl -fsSL https://elastic.co/start-local | sh<p>これにより、ElasticsearchとKibanaがコンピュータにインストールされ、Mastraインテグレーションの設定に使用するAPIキーが生成されます。</p><p>APIキーは前のコマンドの出力として表示され、elastic-start-localフォルダー内の<strong>.env</strong>ファイルに保存されます。</p><h2>デモをインストールして設定</h2><p>デモプロジェクトのソースコードを含む <a href="https://github.com/elastic/mastra-elasticsearch-example">elastic/mastra-elasticsearch-example</a> リポジトリを作成しました。リポジトリに記載されている例は、Elasticsearchからドキュメントを取得するためのRAGアーキテクチャを実装したエージェントをMastraで作成する方法を示しています。</p><p>SF映画のデモ用データセットを提供しました。<a href="https://www.kaggle.com/datasets/rajugc/imdb-movies-dataset-based-on-genre/versions/2?select=scifi.csv">Kaggle</a>上のIMDbデータセットから500本の映画を抽出しました。</p><p>最初のステップは、次のコマンドを使用してnpmでプロジェクトの依存関係をインストールします：</p>npm install<p>次に、設定内容を格納する<strong>.env</strong>ファイルを構成する必要があります。以下のコマンドを使用して、 <strong>.env.example</strong>ファイルの構造をコピーすることで、このファイルを生成できます。</p>cp .env.example .env<p>これで、.envを編集できるようになりました。不足している情報を追加します：</p>OPENAI_API_KEY=
ELASTICSEARCH_URL=
ELASTICSEARCH_API_KEY=
ELASTICSEARCH_INDEX_NAME=scifi-movies<p>Elasticsearchインデックスの名称は <strong><code>scifi-movies</code></strong>です。必要であれば、環境変数 <code>ELASTICSEARCH_INDEX_NAME</code>を使って変更できます。</p><p>OpenAIを埋め込みサービスとして使用しました。これは、<code>OPENAI_API_KEY</code>環境変数にOpenAIのAPIキーを提供する必要があることを意味します。</p><p>例で使用されている埋め込みモデルは <a href="https://developers.openai.com/api/docs/models/text-embedding-3-small">openai/text-embedding-3-small</a> であり、埋め込み次元は1,536です。</p><p>最終的な答えを導き出すために、<a href="https://developers.openai.com/api/docs/models/gpt-5-nano">openai/gpt-5-nano</a> モデルを使用してコストを削減しました。</p><p>RAGアーキテクチャでは、回答の妥当性を検証するという重労働は検索コンポーネント（この場合は Elasticsearch）が行うため、それほど強力ではない（そして一般的に安価な）最終的なLLMモデルを使用することができます。</p><p>小規模なLLMは主に2つのタスクを担当します：</p><ul><li><p><strong>クエリの言い換え/埋め込み：</strong>ユーザーの自然言語の質問をセマンティック検索用のベクトル埋め込みに変換します。</p></li><li><p><strong>回答の合成：</strong>取得された関連性の高いコンテキストの断片（文書/動画）を取り出し、提示された指示に従って、首尾一貫した最終的な人間が読める回答に合成します。</p></li></ul><p>RAGプロセスは回答に必要とされる<strong>正確な事実的文脈を提供する</strong>ため、最終的なLLMは大規模または高度に複雑である必要はなく、必要な知識をすべて自らのパラメータ内に持つ必要もありません（それは大規模で高価なモデルが優れている領域です）。本質的には、本格的なナレッジベースとしてではなく、Elasticsearchが提供するコンテキストの洗練されたテキストサマライザーおよびフォーマッターとして機能します。これにより、コストやレイテンシーの最適化に<code>gpt-5-nano</code>のようなモデルを活用できます。</p><p>.envファイルの設定後、次のコマンドで映画をElasticsearchに取り込むことができます。</p>npx tsx src/utility/store.ts<p>次のような出力が表示されるはずです。</p>🚀 Starting ingestion of 500 movies from 500_scifi_movies.jsonl...
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 1/500 (0%) | ok:1 | fail:0 | chunks:1 | eta:19m 33s | current:Capricorn One
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 2/500 (0%) | ok:2 | fail:0 | chunks:2 | eta:10m 32s | current:Doghouse
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 3/500 (1%) | ok:3 | fail:0 | chunks:3 | eta:7m 33s | current:Dinocroc
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 4/500 (1%) | ok:4 | fail:0 | chunks:7 | eta:6m 10s | current:Back to the Future           
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 5/500 (1%) | ok:5 | fail:0 | chunks:9 | eta:5m 14s | current:The Projected Man            
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 6/500 (1%) | ok:6 | fail:0 | chunks:11 | eta:4m 41s | current:I, Robot
...
✅ Ingestion complete in 1m 46s. Success: 500, Failed: 0, Chunks: 693.<p>scifi-moviesインデックスのマッピングには、次のフィールドが含まれています。</p><ul><li><p><strong>埋め込み</strong>、1536次元のdense_vector、コサイン類似度。</p></li><li><p><strong>説明</strong>、映画の説明を含むテキスト。</p></li><li><p><strong>監督</strong>、監督の名前を含むテキスト。</p></li><li><p><strong>タイトル</strong>、映画のタイトルを含むテキスト。</p></li></ul><p>タイトルとディスクリプションを使用して埋め込みを生成しました。タイトルとディスクリプションは2つの別個のフィールドであるため、両者を連結することで、結果として得られる埋め込みベクトルが、映画の具体的で独自の特徴（タイトル）と豊かな説明的コンテキスト（内容説明）の両方を捉えることができます。これにより、より正確で包括的なセマンティック検索結果が得られます。この結合されたインプットにより、埋め込みモデルは類似度マッチングに向けて、ドキュメントのコンテンツをより適切に単一のベクトル表現として捉えることができます。</p><h2>デモを実行</h2><p>次のコマンドでデモを実行できます：</p>npm run dev<p>このコマンドを実行すると<strong>localhost:4111</strong>でWebアプリケーションが起動し、Mastra Studioにアクセスできるようになります（図3）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a539df677a33b36/6a17041a47d49c36842d88a0/1567e309df21a12bcf1dfef4429f82342549956c-1705x1079.png" alt="Elasticsearch Agentの例を使用したMastra Studioのスクリーンショット。" /><p><a href="https://mastra.ai/docs/getting-started/studio">Mastra Studio</a> はエージェントの構築とテストのためのインタラクティブなUIを提供し、さらにMastraアプリケーションをローカルサービスとして公開するREST APIも備えています。これにより、統合を気にせずにすぐに構築を始められます。</p><p>Elasticsearchを使ったセマンティック検索を実行するツールとして、Mastraの<a href="https://mastra.ai/reference/tools/vector-query-tool">createVectorQueryTool</a>を使った<strong>Elasticsearch Agent</strong>を提供しました。このエージェントは、RAGアプローチを使用して関連文書（つまり映画）を検索し、ユーザーの質問に答えます。</p><p>このエージェントは以下のプロンプトを使用します：</p>You are a helpful assistant that answers questions based on the provided context.
Follow these steps for each response:

1. First, carefully analyze the retrieved context chunks and identify key information.
2. Break down your thinking process about how the retrieved information relates to the query.
3. Draw conclusions based only on the evidence in the retrieved context.
4. If the retrieved chunks don't contain enough information, explicitly state what's missing.

Format your response as:
THOUGHT PROCESS:
- Step 1: [Initial analysis of retrieved chunks]
- Step 2: [Reasoning based on chunks]

FINAL ANSWER:
[Your concise answer based on the retrieved context]

Important: When asked to answer a question, please base your answer only on the context provided in the tool. 
If the context doesn't contain enough information to fully answer the question, please state that explicitly and stop it.
Do not add more information than what is present in the retrieved chunks.
Remember: Explain how you're using the retrieved information to reach your conclusions.<p><code>Mastra Studio &gt; Agents</code>メニューをクリックして<strong>Elasticsearch Agent</strong>を選択すると、チャットシステムを使用してエージェントをテストできます。たとえば、SF映画に関する情報を次のような質問で尋ねることができます。</p><p><em>UFOを題材にした映画またはテレビシリーズを5つ見つけてください</em>。</p><p>エージェントがvectorQueryToolを実行することがわかります。起動したツールをクリックすると、入力と出力を確認できます。実行の最後に、LLMはElasticsearchのscifi-moviesインデックスから得られたコンテキストに基づいて、あなたの質問に回答します（図4）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda92a9b6e56528d3/6a17041c2b835f9724f4b0d6/d9998d4f687984de98845dae52d1288166abf448-1344x1071.png" alt=" Elasticsearch Agentを使用したLLMからの対応。" /><p>Mastraは内部で以下の手順を実行します：</p><ol><li><p><strong>ベクトル変換：</strong>ユーザーの質問「<em>UFOに関する映画やテレビシリーズを5つ探して</em>」は、OpenAIの<code>openai/text-embedding-3-small</code>モデルを使ってベクトル埋め込みに変換されます。</p></li><li><p><strong>ベクトル検索：</strong>この埋め込みはその後、Elasticsearchに対してベクトル検索を用いてクエリを実行するために使用されます。</p></li><li><p><strong>結果の取得：</strong>Elasticsearchは、クエリに非常に関連性の高い10本の映画（つまり、ユーザーのクエリベクトルに最も近いベクトルを持つ映画）を返します。</p></li><li><p><strong>回答生成：</strong>取得された映画と元のユーザーの質問は、具体的には<code>openai/gpt-5-nano</code> LLM に送信されます。LLMはこの情報を処理し、最終的な回答を生成することで、ユーザーの5つの結果への要望を確実に満たします。</p></li></ol><h2>Elasticsearch エージェント</h2><p>ここではElasticsearch Agentのソースコードを報告しました。</p>import { Agent } from "@mastra/core/agent";
import { ElasticSearchVector } from '@mastra/elasticsearch';
import { createVectorQueryTool } from '@mastra/rag';
import { ModelRouterEmbeddingModel } from "@mastra/core/llm";
import { Memory } from "@mastra/memory";

const es_url = process.env.ELASTICSEARCH_URL;
const es_apikey = process.env.ELASTICSEARCH_API_KEY;
const es_index_name = process.env.ELASTICSEARCH_INDEX_NAME;
const prompt = 'insert here the previous prompt';

const esVector = new ElasticSearchVector({
  id: 'elasticsearch-vector',
  url: es_url,
  auth: {
    apiKey : es_apikey
  }
});

const vectorQueryTool = createVectorQueryTool({
  vectorStore: esVector,
  indexName: es_index_name,
  model: new ModelRouterEmbeddingModel("openai/text-embedding-3-small")
});

export const elasticsearchAgent = new Agent({
  id: "elasticsearch-agent",
  name: "Elasticsearch Agent",
  instructions: prompt,
  model: 'openai/gpt-5-nano',
  tools: { vectorQueryTool },
  memory: new Memory(),
});<p><strong>vectorQueryTool</strong>は、RAGの例における取得部分を実装するために呼び出されるツールです。MastraにElasticが提供した<a href="https://mastra.ai/reference/vectors/elasticsearch">ElasticSearchVector</a>実装を使用しています。</p><p>エージェントは、vectorQueryTool、プロンプト、およびメモリを消費するエージェントクラスのオブジェクトです。ご覧のように、Elasticsearchをエージェントに接続するために必要なコードはごくわずかです。</p><h2>まとめ</h2><p>この記事では、ElasticsearchとMastraフレームワークを統合し、洗練されたエージェント型AIアプリケーションを構築することのシンプルさとパワーについて実証しました。具体的には、Elasticsearchでインデックス化されたSF映画データのコーパスに対してセマンティック検索を実行できるRAGエージェントの作成について説明しました。</p><p>重要なポイントは、ElasticがMastraのオープンソースプロジェクトに直接貢献し、ベクターストアとしてのElasticsearchをネイティブサポートしていることです。この統合により、導入時の障壁が大幅に低下します。これは、 <strong>Elasticsearch Agent</strong>のソースコードを見れば明らかです。<code>ElasticSearchVector</code>と<code>createVectorQueryTool</code>を使用することで、Elasticsearchをエージェントに接続するための総合的なセットアップが、最小限の設定コード行数で済みます。</p><p>Elasticsearchは、結果の関連性を高めるためのいくつかの高度な機能を提供しています。例として、<a href="https://www.elastic.co/elasticsearch/hybrid-search">ハイブリッド検索</a>は、語彙検索とベクトル検索を組み合わせることで、精度を大幅に向上させます。もう一つの興味深い機能は、ハイブリッド検索の最後に適用できる最新の<a href="https://www.elastic.co/search-labs/tutorials/jina-tutorial/jina-reranker-v3">Jinaモデル</a>を使用した再ランク付けです。これらの技術についてさらに詳しく知るには、Elasticsearch Labsの以下の記事を参照してください。</p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/hybrid-search-elasticsearch">Elasticsearchのハイブリッド探索</a> by ヴァレンティン・クレっタス</p></li><li><p><a href="https://www.elastic.co/search-labs/blog/jina-models-elasticsearch-guide">Jinaモデル入門、その機能、Elasticsearchでの利用</a> by スコット・マーテンス</p></li></ul><p>また、提供されている例を参考に、MastraとElasticsearchを使って独自のデータ駆動型エージェントを構築し始めることをお勧めします。Mastraについての詳細は、 <a href="https://mastra.ai/docs">こちら</a>のオフィシャルドキュメントをご覧ください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/build-agentic-ai-applications-mastra-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-agentic-ai-applications-mastra-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Enrico Zimuel]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt083181bab7c0e2d0/6a17041eacf0880b70be99f5/ab30baf2f908534840c5d71a46705773807baf54-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[TypeScriptを使用したElasticsearch MCPサーバーの作成]]></title>
    <description><![CDATA[TypeScriptとClaude Desktopを使用してElasticsearch MCPサーバーを作成する方法を学びます。]]></description>
    <content:encoded><![CDATA[<p>Elasticsearchで大規模なナレッジベースを扱う場合、情報を見つけるだけでは片手落ちです。エンジニアは複数の文書から結果を統合し、要約を作成し、回答を情報源にたどる必要があることが多いです。モデルコンテキストプロトコル（MCP）は、Elasticsearchと大規模言語モデル（LLM）アプリケーションを接続するための標準化された方法を提供します。ElasticはElastic Agent Builder（<a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">MCPエンドポイントを</a>機能の一つに含む）のような公式ソリューションを提供していますが、カスタムMCPサーバーを構築することで、検索ロジック、結果のフォーマット、取得したコンテンツをLLMに渡して合成、要約、引用を行う方法などを完全に制御できます。</p><p>この記事では、カスタムElasticsearch MCPサーバーを構築するメリットを探り、ElasticsearchをLLM対応アプリケーションに接続するサーバーをTypeScriptで作成する方法を紹介します。</p><h2>カスタムのElasticsearch MCPサーバーを構築する理由</h2><p>Elasticは<a href="https://www.elastic.co/docs/solutions/search/mcp">MCPサーバー</a>のいくつかの代替手段を提供しています。</p><ul><li><p><a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">Elasticsearch 9.2+用Elastic Agent Builder MCPサーバー</a></p></li><li><p><a href="https://github.com/elastic/mcp-server-elasticsearch?tab=readme-ov-file#elasticsearch-mcp-server">旧バージョン向けのElasticsearch MCPサーバー（Python）</a></p></li></ul><p>MCPサーバーとElasticsearchの連携方法をより細かく制御したい場合は、独自のカスタムサーバーを構築することで、ニーズに合わせて柔軟にカスタマイズできます。例えば、Agent BuilderのMCPエンドポイントはElasticsearchクエリ言語（ES|QL）クエリに限定されていますが、カスタムサーバーでは完全なクエリDSLを使用できます。また、LLMに渡される前に結果をどのようにフォーマットするかを制御でき、このチュートリアルで実装するOpenAIを利用した要約など、追加の処理ステップを統合することもできます。</p><p>この記事を読み終える頃には、Elasticsearchインデックスに保存されている情報を検索し、要約し、引用を提供するTypeScriptで記述されたMCPサーバーが完成しているでしょう。Elasticsearchを使用して情報を検索し、OpenAIの<code>gpt-4o-mini</code>モデルを用いて要約と引用を生成し、Claude DesktopをMCPクライアントおよびUIとして活用してユーザーのクエリを受け取り、応答を提供します。最終的には、エンジニアが組織内の技術文書全体からベストプラクティスを発見し、統合するのに役立つ内部ナレッジアシスタントが完成します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltad9133cb083ad352/6a170c19b0367d411e72bd5b/ec5771a874cf9740d4cac6888622cbe8cd6aede7-1999x1133.png" alt="TypeScriptとClaude Desktopを使用してElastic MCPサーバーを作成します。" /><h2>要件：</h2><ul><li><p>Node.js 20 +</p></li><li><p>Elasticsearch</p></li><li><p>OpenAI APIキー</p></li><li><p>Claude Desktop</p></li></ul><h3>MCPとは何ですか？</h3><p><a href="https://www.elastic.co/what-is/mcp">MCP</a>は<a href="https://www.anthropic.com/news/model-context-protocol">Anthropic</a>によって作成されたオープンスタンダードで、LLMとElasticsearchのような外部システムとの間で安全かつ双方向の接続を提供します。MCP の現状については<a href="https://www.elastic.co/search-labs/blog/mcp-current-state">この記事</a>で詳しく読むことができます。</p><p>MCPの環境は<a href="https://www.elastic.co/search-labs/blog/mcp-current-state#mcp-project-updates:-transport,-elicitation,-and-structured-tooling">日々進化</a>しており、多様なユースケースに対応したサーバーが利用可能です。さらに、この記事でご紹介するように、独自のカスタムMCPサーバーを簡単に構築することもできます。</p><h3>MCPクライアント</h3><p><a href="https://modelcontextprotocol.io/clients">利用可能なMCPクライアント</a>は多数あり、それぞれに特徴や制限があります。簡便性と普及度を考慮し、今回はMCPクライアントとして<a href="https://claude.ai/download">Claude Desktop</a>を使用します。これは、ユーザーが自然言語で質問できるチャットインターフェースとして機能し、MCPサーバーが公開しているツールを自動的に呼び出して、文書を検索し、要約を生成します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06fd7a02042094e1/6a170c1b14b2700024e3c651/66eb0b11473347b6cf2d85718251eeac38d6249d-1999x1491.png" alt="「Coffee and Claude time? How can I help you today?」というメモが表示されたClaude 4.5 Sonnetのページ" /><h2>Elasticsearch MCPサーバーの作成</h2><p><a href="https://github.com/modelcontextprotocol/typescript-sdk">TypeScript SDK</a>を使えば、ユーザーのクエリ入力に基づいてElasticsearchデータのクエリ方法を理解するサーバーを簡単に作成できます。</p><p>この記事では、Elasticsearch MCPサーバーとClaude Desktopクライアントを統合するための手順を説明します。</p><ol><li><p><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#configure-mcp-server-for-elasticsearch">Elasticsearch 用の MCP サーバーを設定してください。</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#load-the-mcp-server-into-claude-desktop">MCPサーバーをClaude Desktopにロード</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#test-it-out">試してみてください。</a></p></li></ol><h3>Elasticsearch MCPサーバーを設定してください</h3><p>まず、Nodeアプリケーションを初期化します。</p>npm init -y<p>これで <code>package.json</code>ファイルが作成され、このアプリケーションに必要な依存関係のインストールを開始できます。</p>npm install @elastic/elasticsearch @modelcontextprotocol/sdk openai zod &amp;&amp; npm install --save-dev ts-node @types/node typescript<ul><li><p><strong>@elastic/elasticsearch</strong> はElasticsearchのNode.jsライブラリにアクセスするためのものです。</p></li><li><p><strong>@modelcontextprotocol/sdk</strong>は、MCPサーバーの作成と管理、ツールの登録、MCPクライアントとの通信処理を行うためのコアツールを提供します。</p></li><li><p><strong>openai</strong>は、OpenAIのモデルと対話し、要約や自然言語による対応を生成することができます。</p></li><li><p><a href="https://zod.dev/"><strong>zod</strong></a>は、各ツールの入出力データの構造化スキーマの定義と検証に役立ちます。</p></li></ul><p><code>ts-node</code>、<code>@types/node</code>、 <code>typescript</code>は開発中にコードの入力やスクリプトのコンパイルに使用されます。</p><h4>データセットを設定</h4><p>Claude DesktopがMCPサーバーを使用してクエリできるデータを提供するために、<a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/dataset.json">内部の模擬ナレッジベースデータセット</a>を使用します。このデータセットから作成される文書は以下のような形式になります。</p>{
    "id": 5,
    "title": "Logging Standards for Microservices",
    "content": "Consistent logging across microservices helps with debugging and tracing. Use structured JSON logs and include request IDs and timestamps. Avoid logging sensitive information. Centralize logs in Elasticsearch or a similar system. Configure log rotation to prevent storage issues and ensure logs are searchable for at least 30 days.",
    "tags": ["logging", "microservices", "standards"]
}<p>データを取り込むために、Elasticsearchにインデックスを作成し、そこにデータセットをロードするスクリプトを用意しました。<a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/setup.ts">こちらで</a>ご覧いただけます。</p><h4>MCPサーバー</h4><p><a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/index.ts"><code>index.ts</code></a>というファイルを作成し、依存関係をインポートして環境変数を処理するための以下のコードを追加します。</p>// index.ts
import { z } from "zod";
import { Client } from "@elastic/elasticsearch";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import OpenAI from "openai";

const ELASTICSEARCH_ENDPOINT =
  process.env.ELASTICSEARCH_ENDPOINT ?? "http://localhost:9200";
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY ?? "";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "";
const INDEX = "documents";<p>また、ElasticsearchとOpenAIの呼び出しを処理するようにクライアントを初期化します。</p>const openai = new OpenAI({
  apiKey: OPENAI_API_KEY,
});

const _client = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
});<p>実装をより堅牢にし、構造化された入出力を保証するために、 <a href="https://zod.dev/"><code>zod</code></a>を使用してスキーマを定義します。これにより、ランタイムでデータを検証し、エラーを早期に捕捉し、ツールの対応をプログラムで処理しやすくすることができます。</p>const DocumentSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
});

const SearchResultSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
  score: z.number(),
});

type Document = z.infer&lt;typeof DocumentSchema&gt;;
type SearchResult = z.infer&lt;typeof SearchResultSchema&gt;;<p>構造化出力の詳細については、<a href="https://www.elastic.co/search-labs/blog/structured-outputs-elasticsearch-guide">こちらを</a>ご覧ください。</p><p>それでは、MCPサーバーを初期化しましょう。</p>const server = new McpServer({
  name: "Elasticsearch RAG MCP",
  description:
    "A RAG server using Elasticsearch. Provides tools for document search, result summarization, and source citation.",
  version: "1.0.0",
});<h4>MCPツールの定義</h4><p>すべての設定が完了したので、MCPサーバーによって公開されるツールの作成を開始できます。このサーバーは2つのツールを公開します。</p><ul><li><p><strong><code>search_docs</code></strong><strong>：</strong>Elasticsearchで文書を全文検索で検索します。</p></li><li><p><strong><code>summarize_and_cite</code></strong><strong>：</strong>以前に取得した文書から情報を要約・統合し、ユーザーの質問に答えます。このツールは、出典となる文書を参照する引用も追加します。</p></li></ul><p>これらのツールを組み合わせることで、シンプルな「検索してから要約」ワークフローが構築されます。一方のツールが関連文書を取得し、もう一方のツールがその文書を使用して要約と引用を含む回答を生成します。</p><h4>ツールの応答形式</h4><p>各ツールは任意の入力パラメータを受け入れることができますが、以下の構造で応答する必要があります。</p><ul><li><p><strong>Content：</strong> これは非構造化形式でのツールの応答です。このフィールドは通常、テキスト、画像、音声、リンク、または埋め込みを返すために使用されます。この用途では、ツールによって生成された情報を含む整形済みテキストを返すために使用されます。</p></li><li><p><strong>structuredContent：</strong>これは、各ツールの結果を構造化された形式で提供するために使用されるオプションの戻り値です。これはプログラム上の目的に役立ちます。このMCPサーバーでは使用されていませんが、他のツールを開発したり、結果をプログラムで処理したりする場合に便利です。</p></li></ul><p>その構造を念頭に置いて、各ツールについて詳しく見ていきましょう。</p><h4>Search_docsツール</h4><p>このツールは、Elasticsearchインデックスで<a href="https://www.elastic.co/docs/solutions/search/full-text">全文検索</a>を実行し、ユーザークエリに基づいて最も関連性の高いドキュメントを取得します。主要な一致をハイライトし、関連性スコアを素早くまとめてくれます。</p>server.registerTool(
  "search_docs",
  {
    title: "Search Documents",
    description:
      "Search for documents in Elasticsearch using full-text search. Returns the most relevant documents with their content, title, tags, and relevance score.",
    inputSchema: {
      query: z
        .string()
        .describe("The search query terms to find relevant documents"),
      max_results: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of results to return"),
    },
    outputSchema: {
      results: z.array(SearchResultSchema),
      total: z.number(),
    },
  },
  async ({ query, max_results }) =&gt; {
    if (!query) {
      return {
        content: [
          {
            type: "text",
            text: "Query parameter is required",
          },
        ],
        isError: true,
      };
    }

    try {
      const response = await _client.search({
        index: INDEX,
        size: max_results,
        query: {
          bool: {
            must: [
              {
                multi_match: {
                  query: query,
                  fields: ["title^2", "content", "tags"],
                  fuzziness: "AUTO",
                },
              },
            ],
            should: [
              {
                match_phrase: {
                  title: {
                    query: query,
                    boost: 2,
                  },
                },
              },
            ],
          },
        },
        highlight: {
          fields: {
            title: {},
            content: {},
          },
        },
      });

      const results: SearchResult[] = response.hits.hits.map((hit: any) =&gt; {
        const source = hit._source as Document;

        return {
          id: source.id,
          title: source.title,
          content: source.content,
          tags: source.tags,
          score: hit._score ?? 0,
        };
      });

      const contentText = results
        .map(
          (r, i) =&gt;
            `[${i + 1}] ${r.title} (score: ${r.score.toFixed(
              2,
            )})\n${r.content.substring(0, 200)}...`,
        )
        .join("\n\n");

      const totalHits =
        typeof response.hits.total === "number"
          ? response.hits.total
          : (response.hits.total?.value ?? 0);

      return {
        content: [
          {
            type: "text",
            text: `Found ${results.length} relevant documents:\n\n${contentText}`,
          },
        ],
        structuredContent: {
          results: results,
          total: totalHits,
        },
      };
    } catch (error: any) {
      console.log("Error during search:", error);

      return {
        content: [
          {
            type: "text",
            text: `Error searching documents: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);<p><a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-fuzzy-query"><em><code>fuzziness</code></em></a><em><code>: “AUTO”</code></em> は <em> 分析対象のトークンの長さに応じて誤字許容度を調整するように設定しています。また </em>、<em> タイトルフィールドで一致が発生したドキュメントのスコアを上げるtitle^2も設定しました。</em>。</p><h4>summarize_and_citeツール</h4><p>このツールは、前回の検索で取得したドキュメントに基づいて要約を生成します。OpenAIの <code>gpt-4o-mini</code>モデルを使用して、ユーザーの質問に答えるために最も関連性の高い情報を統合し、検索結果に直接基づく回答を提供します。要約に加えて、使用したソースドキュメントの引用情報（メタデータ）も返します。</p>server.registerTool(
  "summarize_and_cite",
  {
    title: "Summarize and Cite",
    description:
      "Summarize the provided search results to answer a question and return citation metadata for the sources used.",
    inputSchema: {
      results: z
        .array(SearchResultSchema)
        .describe("Array of search results from search_docs"),
      question: z.string().describe("The question to answer"),
      max_length: z
        .number()
        .optional()
        .default(500)
        .describe("Maximum length of the summary in characters"),
      max_docs: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of documents to include in the context"),
    },
    outputSchema: {
      summary: z.string(),
      sources_used: z.number(),
      citations: z.array(
        z.object({
          id: z.number(),
          title: z.string(),
          tags: z.array(z.string()),
          relevance_score: z.number(),
        })
      ),
    },
  },
  async ({ results, question, max_length, max_docs }) =&gt; {
    if (!results || results.length === 0 || !question) {
      return {
        content: [
          {
            type: "text",
            text: "Both results and question parameters are required, and results must not be empty",
          },
        ],
        isError: true,
      };
    }

    try {
      const used = results.slice(0, max_docs);

      const context = used
        .map(
          (r: SearchResult, i: number) =&gt;
            `[Document ${i + 1}: ${r.title}]\\n${r.content}`
        )
        .join("\n\n---\n\n");

      // Generate summary with OpenAI
      const completion = await openai.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [
          {
            role: "system",
            content:
              "You are a helpful assistant that answers questions based on provided documents. Synthesize information from the documents to answer the user's question accurately and concisely. If the documents don't contain relevant information, say so.",
          },
          {
            role: "user",
            content: `Question: ${question}\\n\\nRelevant Documents:\\n${context}`,
          },
        ],
        max_tokens: Math.min(Math.ceil(max_length / 4), 1000),
        temperature: 0.3,
      });

      const summaryText =
        completion.choices[0]?.message?.content ?? "No summary generated.";

      const citations = used.map((r: SearchResult) =&gt; ({
        id: r.id,
        title: r.title,
        tags: r.tags,
        relevance_score: r.score,
      }));

      const citationText = citations
        .map(
          (c: any, i: number) =&gt;
            `[${i + 1}] ID: ${c.id}, Title: "${c.title}", Tags: ${c.tags.join(
              ", ",
            )}, Score: ${c.relevance_score.toFixed(2)}`,
        )
        .join("\n");

      const combinedText = `Summary:\\n\\n${summaryText}\\n\\nSources used (${citations.length}):\\n\\n${citationText}`;

      return {
        content: [
          {
            type: "text",
            text: combinedText,
          },
        ],
        structuredContent: {
          summary: summaryText,
          sources_used: citations.length,
          citations: citations,
        },
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error generating summary and citations: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);<p>最後に、<a href="https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#stdio">stdio</a>を使用してサーバーを起動する必要があります。つまり、MCPクライアントは、標準の入出力ストリームを読み書きすることでサーバーと通信します。stdioは最もシンプルな転送オプションで、クライアントによってサブプロセスとして立ち上げられるローカルMCPサーバーに適しています。ファイルの最後に以下のコードを追加します。</p>const transport = new StdioServerTransport();
server.connect(transport);<p>次に、以下のコマンドを使用してプロジェクトをコンパイルします。</p>npx tsc index.ts --target ES2022 --module node16 --moduleResolution node16 --outDir ./dist --strict --esModuleInterop<p>これにより<code>dist</code>フォルダが作成され、その中に<code>index.js</code>ファイルが作成されます。</p><h3>MCPサーバーをClaude Desktopにロード</h3><p>Claude DesktopでMCPサーバーを設定するには、<a href="https://modelcontextprotocol.io/docs/develop/connect-local-servers">このガイド</a>に従ってください。Claudeの設定ファイルでは、以下の値を設定する必要があります:</p>{
  "mcpServers": {
    "elasticsearch-rag-mcp": {
      "command": "node",
      "args": [   "/Users/user-name/app-dir/dist/index.js"
      ],
      "env": {
        "ELASTICSEARCH_ENDPOINT": "your-endpoint-here",
        "ELASTICSEARCH_API_KEY": "your-api-key-here",
        "OPENAI_API_KEY": "your-openai-key-here"
      }
    }
  }
}<p><code>args</code>値は、 <code>dist</code>フォルダ内のコンパイル済みファイルを指す必要があります。また、設定ファイル内の環境変数も、コード内で定義されているものと全く同じ名前で設定する必要があります。</p><h3>試してみる</h3><p>各ツールを実行する前に、「<strong>検索とツール</strong>」をクリックして、ツールが有効になっていることを確認します。ここでは、それぞれを有効または無効にすることもできます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt395a7337021f9820/6a170c1c67045bb74d45c228/172981c2a54adabc70d5819013c3007670935605-1999x1002.png" alt="「Good afternoon, Jeff. How can I help you today?」というメモが表示されたClaude 4.5 Sonnetのページ" /><p>最後に、Claude DesktopのチャットからMCPサーバーをテストして、質問を始めましょう。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf4ac458dc0206271/6a170c1e66c4f91328f8c072/03654c0f8c53c714f801fba8b25747071179209b-1999x1353.png" alt="ユーザーがClaude Desktopチャットで、認証方法と役割ベースのアクセス制御に関するドキュメントを検索するようリクエストし、それに対するClaudeの回答。" /><p>「<strong>認証方法とRBACに関する文書を検索する</strong>」という質問に対して、<code>search_docs</code> ツールが実行され、以下の結果が返されます。</p>Most Relevant Documents:
Access Control and Role Management (highest relevance) - This document covers role-based access control (RBAC) principles, including ensuring users only have necessary permissions, regular auditing of user roles, revoking inactive accounts, and implementing just-in-time access for sensitive operations.
User Authentication with OAuth 2.0 - This document explains OAuth 2.0 authentication, which enables secure delegated access without credential sharing. It covers configuring identity providers, token management with limited scope and lifetime, and secure storage of refresh tokens.
Container Security Guidelines - While primarily about container security, this document touches on access control aspects like running containers as non-root users and avoiding embedded credentials.
Incident Response Playbook - This mentions role assignment during incidents (incident commander, communications lead, etc.), which relates to access control in emergency scenarios.
Logging Standards for Microservices - This document includes guidance on avoiding logging sensitive information, which is relevant to authentication security.<p>回答は「素晴らしい！認証方法とロールベースのアクセス制御に関する関連文書を5件見つけました。見つかった内容は以下のとおりです。」</p><p>ツール呼び出しは、対応ペイロードの一部としてソース文書を返します。これらの文書は、後に引用を生成するために使用されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbaf48a9468770ce2/6a170c21509168bffee1bb14/25ff4c7e9563d99752f95540dafdc7fd211a66e3-800x530.gif" alt="Claude 4.5 Sonnetのページ。認証方法とRBACに関する5つの関連文書がスクロールして表示されます。" /><p>また、複数のツールを一度のインタラクションで連結することも可能です。この場合、Claude Desktopはユーザーの質問を分析し、まず<code>search_docs</code>を呼び出して関連文書を取得し、次にその結果を<code>summarize_and_cite</code>に渡して最終的な回答を生成する必要があると判断します。これらすべては、ユーザーからの個別のプロンプトを必要とせずに実行されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta46ff45274e64192/6a170c230c4857a91501aac1/e6a8a46acb4236e77058f18bcd2f0737b5882c05-1999x1101.png" alt="Claude Desktopチャットに、「Jeff returns」というメモと、新しいユーザーからの質問「What are the main recommendations to improve authentication and access control across our systems? Include references.」が添えられている。" /><p>この場合、「<strong>システム全体の認証とアクセス制御を改善するための主な推奨事項は何ですか？参考文献を含めてください。</strong>」というクエリに対して、以下の結果が得られました。</p>Based on the documentation, here are the main recommendations to improve authentication and access control across your systems:

Key Recommendations

1. Implement Role-Based Access Control (RBAC)
Ensure users have only the permissions necessary for their job functions. This principle of least privilege minimizes security risks by limiting access to what's truly needed. [1]

2. Regular Access Audits
Conduct regular audits of user roles and promptly revoke access for inactive accounts. This helps prevent unauthorized access from dormant or compromised accounts. [1]

3. Just-in-Time (JIT) Access
Implement just-in-time access for sensitive operations, where permissions are granted only when needed and for a limited duration. This reduces the attack surface by minimizing standing privileges. [1]

4. OAuth 2.0 for Secure Authentication
Use OAuth 2.0 to enable secure delegated access without sharing user credentials. This protocol allows third-party applications to access resources on behalf of users without exposing passwords. [2]

5. Token Security and Management
Configure your identity provider to issue tokens with limited scope and lifetime

Store refresh tokens securely
Validate access tokens consistently to maintain security [2]
References

Access Control and Role Management (Tags: security, access-control)
User Authentication with OAuth 2.0 (Tags: authentication, oauth)
These recommendations work together to create a defense-in-depth approach, where multiple security layers protect your systems from unauthorized access.<p>前のステップと同様に、この質問に対する各ツールの回答を確認できます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8f633c518e708a99/6a170c25ab7f082991db9ed6/cb606d356b2f7d5e4878a5eff71bc881869ac0ee-800x585.gif" alt="Claude Desktopチャットページ、スクロールテキストには各ツールからの対応が含まれ、システム全体の認証とアクセス制御を改善するための主な推奨事項に関する質問への回答が含まれている。" /><p><em>注：各ツールの使用を承認するかを確認するサブメニューが表示された場合は、</em><em><strong>「常に許可」</strong></em><em>または</em><em><strong>「一度だけ許可」</strong></em><em>を選択します。</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6627ee0bff1862df/6a170c266f7f040f6f91488c/aea942ba9b0037526ea215bec65690f1a5c3099c-1522x250.png" alt="ユーザーが選択できるClaude Desktop「常に許可」と「一度だけ許可」のオプション。" /><h2>まとめ</h2><p>MCPサーバーは、ローカルとリモートの両方のアプリケーションのLLMツールの標準化に向けた重要な一歩です。完全な互換性の実現にはまだ取り組んでいますが、その方向へ急速に進んでいます。</p><p>この記事では、ElasticsearchをLLM搭載アプリケーションに接続するカスタムMCPサーバーをTypeScriptで構築する方法を学びました。当サーバーは2つのツールを提供しています。1つはQuery DSLを使用して関連文書を取得するためのツール<code>search_docs</code>、もう1つはOpenAIモデルとクライアントUIとしてのClaude Desktopを使用して引用付きの要約を生成するためのツール<code>summarize_and_cite</code>です。</p><p>異なるクライアントとサーバープロバイダー間の互換性の将来は有望に見えます。次のステップは、エージェントにより多くの機能と柔軟性を加えることです。実用的な<a href="https://www.elastic.co/search-labs/blog/llm-functions-elasticsearch-intelligent-query">記事</a>で、検索テンプレートを使用してクエリをパラメーター化し、精度と柔軟性を得る方法を学ぶことができます。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[統合]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5600198cb47666a5/6a170c28509168ce3ae1bb18/0bb24c05fff391f42070c2883182ea6fe9cb9680-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[シェルツールはコンテキストエンジニアリングの万能薬ではありません]]></title>
    <description><![CDATA[コンテキストエンジニアリングに利用できるコンテキスト検索ツールにはどのようなものがあるのか、それらがどのように機能するのか、そしてそれぞれのトレードオフについて学びましょう。]]></description>
    <content:encoded><![CDATA[<p>エージェントが備える最も重要なツールは、自前でコンテキストを構築するために使用できる検索ツールです。<a href="https://www.llamaindex.ai/blog/files-are-all-you-need">LlamaIndex</a>と<a href="https://x.com/hwchase17/status/2011814697889316930">LangChain</a>による最近の投稿により、<em>エージェントがコンテキストエンジニアリングに必要なのは、シェルツールとファイルシステムだけなのか</em>という議論が巻き起こりました。残念ながら、この議論はすぐにファイルシステム対データベースという間違った方向に逸れてしまいました。</p><p>この投稿は、<em>エージェントが独自のコンテキストを構築するために必要な、適切な検索インターフェースとは何か</em>という問いに改めて焦点を当てるものです。まず、シェルツールと専用データベースツールのトレードオフについて論じます。それを基に、エージェントのニーズに合った適切なインターフェースを見つけるための実用的なフレームワークを提示します。</p><h2>エージェントにとって「コンテキスト構築」が具体的に意味するもの</h2><p>初期の<a href="https://www.elastic.co/what-is/retrieval-augmented-generation">Retrieval-Augmented Generation（RAG）パイプライン</a>では、開発者が肯定的な検索パイプラインを設計し、大規模言語モデル（LLM）はコンテキストを受動的に受け取るだけの存在でした。そしてこれが、根本的な制限となっていました。コンテキストは、必要かどうかにかかわらず、すべてのクエリで取得され、それが実際に役立つかどうかも確認されなかったのです。</p><p>エージェント型RAGへの移行により、エージェントは独自のコンテキストを構築するための一連の検索ツールを利用できるようになりました。たとえば、Claude Code [1]とCursor [2]は、タスクの必要に応じて、エージェントが異なる検索ツールを選択し、さらにはそれらを組み合わせてクエリーを連鎖的に実行することもできます。</p><h2>コンテキストエンジニアリングに用いる検索インターフェースの種類</h2><p>コンテキストは、ウェブ上、ローカルファイルシステム、データベースなど、さまざまな場所に存在します。エージェントは、これらのコンテキスト外の各データソースと、次のようなツールを通じてやり取りできます。</p><ul><li><p><strong>シェルツール</strong>はシェルコマンドを実行でき、ローカルファイルシステムにアクセスできます。組み込みシェルツールの例としては、<a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool">Claude APIのbashツール</a>、<a href="https://docs.openclaw.ai/tools/exec">OpenClawのExecツール</a>、<a href="https://docs.langchain.com/oss/python/integrations/tools/bash">LangChainのシェルツール</a>があります。</p></li><li><p><strong>専用のデータベースツール、</strong>たとえばモデルコンテキストプロトコル（MCP）サーバー（例：<a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Elastic Agent Builder MCPサーバー）</a>やカスタムツール（例：<code>run_esql(query)</code>、<code>db_list_index()</code>）などは、データベースをクエリできます。</p></li><li><p><strong>専用のファイル検索ツール</strong>は、ローカル（またはアップロードされた）ファイルを検索し、読み取ることができます（ただし、シェルに完全にアクセスする権限はありません）。組み込みファイル検索ツールの例としては、 <a href="https://ai.google.dev/gemini-api/docs/file-search">Gemini APIのファイル検索ツール</a> や <a href="https://developers.openai.com/api/docs/guides/tools-file-search">OpenAIのファイル検索ツール</a>があります。</p></li><li><p><strong>ウェブ検索ツール</strong>は、ウェブから情報を取得できます。</p></li><li><p><strong>メモリツール</strong>は、長期記憶に保存し、長期記憶から呼び出します（保存方法は任意です）。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2c5d083815149773/6a170acb964cea61a108bb80/115f20c8ded259e508f51524b2c06bdc702d70ab-1999x1050.png" alt="エージェントがさまざまなコンテキスト取得ツールを使用して、ローカルファイル、独自のデータ、ウェブ、長期記憶にアクセスする方法を示す図。" /><p>ご覧のとおり、シェルツールは用途が多彩で、以下のようなさまざまなデータソースからコンテキストを取得するために使用できます。</p><ul><li><p><strong>ファイルシステム：</strong>エージェントはディレクトリ構造を探索（ls、find）、関連コンテンツを検索（grep、cat）し、十分なコンテキストが構築されるまで繰り返します。</p></li><li><p><strong>データベース：</strong>エージェントは、データベース用のコマンドラインインターフェース（CLI）ツール（例：<a href="https://www.elastic.co/docs/reference/query-languages/sql/sql-cli"><code>elasticsearch-sql-cli</code></a>）を使用したり、curl経由でのHTTP APIを呼び出たり、スクリプトを実行したりできます。これは、再利用可能な文書化した例をエージェントのコンテキストに挿入して、ツールを適切に使用できるようにするようなエージェントスキル（例：<a href="https://github.com/elastic/agent-skills">Elastic Agent Skills for Elasticsearch</a>）と組み合わせると特に役立ちます。</p></li><li><p><strong>ウェブ：</strong>エージェントは、検索プロバイダーのAPIを通じてcurlコマンドでウェブ検索を実行できます。</p></li></ul><p>ただし、シェルツールはシステムに直接アクセスできるため、隔離されたサンドボックス環境での実行や、実行されたすべてのコマンドのログを取得するなどの安全対策が必要です。</p><h2>いつ、どの検索インターフェースを使用すべきか</h2><p>最適な検索インターフェースは、データ、クエリパターン、ユースケースによって異なります。このセクションは、実際に取りかかる際の出発点として役立ちます。</p><h3>ファイルシステムによってデータベースが時代遅れになったわけではない</h3><p>ファイルシステムかデータベースかという議論は、ストレージ層自体の話ではありません。たとえば、LangChainの説明によると、<a href="https://x.com/hwchase17/status/2011814697889316930">LangChainのメモリシステム</a>では、実のところファイルシステムにメモリを保存しているわけではありません。代わりに、メモリをデータベースに格納し、それをエージェントに対して一連のファイルとして<em>提示</em>します[3]。</p><p>ファイルシステムは、コーディングエージェントなど、ファイルを中心としたユースケースに最適です。また、一時的なスクラッチパッドや作業メモリとしても、同時実行が問題とならない単一ユーザーや単一エージェントのシナリオにも適しています。こうした場合、物理的なファイルシステム、またはデータをファイルシステムとして提示する方法により、目的に合わせてインターフェースを構築する前の段階において柔軟性を確保できます。</p><p>しかし、ファイルシステムストレージには、同時実行性、手動によるスキーマ適用、アトミックトランザクションなどの弱点があります。これらは、アプリケーションをスケールしたり、マルチエージェントシナリオに移行したりする必要がある場合に、いっそう顕著になります。これらの弱点を無視するならば、本番用データベースがすでに備えているような、トランザクションの安全性やアクセス制御を支える何十年もの技術的蓄積のない、<a href="https://dx.tips/oops-database">劣化版のデータベースを苦労して再発明する</a>羽目に陥ります。さらに、ほとんどのエンタープライズ環境では、データベースを使用するかどうかを選ぶ余地はありません。なぜなら、ビジネスクリティカルなデータを格納するデータベースはすでに存在しているからです。</p><h3>シェルツール+ファイルシステム</h3><p>ファイルシステム検索の出発点として、シェルツールは最適な選択肢です。現在、この分野において多くの発展を牽引しているのは、コーディングエージェントです。コーディングエージェントはローカルファイル内のコードを扱うため、必然的にファイル依存度の高いユースケースとなります。したがって、LLMはコーディングタスク用に、トレーニング後の段階で微調整されます。そのため、多くのLLMはコードを書くことだけでなく、シェルコマンドの使用やファイルシステム内の移動も得意です。</p><p><code>ls</code>や<code>grep</code>のような、組み込みCLIを備えたシェルツールでファイルを検索することは効果的です。grepでは「<code>matplotlib</code>をインポートしているすべてのファイルを検索」といったクエリを、高速かつ高精度に、しかも低コストで実行できます。しかし、エージェントが「アプリは失敗した認証をどのように処理しているか」といった概念的なクエリを処理する必要がある場合、grepによるパターンマッチングはすぐに限界に突き当たります。このギャップを埋めるために、<a href="https://github.com/jina-ai/jina-grep-cli"><code>jina-grep</code></a>のような、コマンドラインにセマンティック検索機能をもたらす代替手段もいくつか登場しています。</p><p>ただしgrepと、その代替手段であるセマンティック検索の多くは、コーパス全体に対してO(n)で実行されます。コードベースを対象とするユースケースなら、これで問題ないかもしれません。しかし、データが増えるとレイテンシーが目立つようになります。この場合、パフォーマンスを維持するにはインデックス化されたデータストアが必要となります。</p><h3>シェルツール+データベース</h3><p>セマンティック検索やハイブリッド検索など、データに対する検索機能を追加する別の方法は、たとえばCursorのように、機能をデータベースに格納することです。さらに、データに複雑なリレーショナル結合や集計が必要な場合、データベースインターフェースは不可欠です。</p><p>データがファイルシステム上ではなくデータベース内にある場合、特定のユースケースでは、シェルツールを軽量なデータベースインターフェースとして利用できます。クエリがCLIやcurlコマンドで十分に実行できるほどシンプルな場合、専用のデータベースツールを使うとかえって不要な複雑さが増えることがあります。</p><p>このアプローチは、エージェントが実際にどのようなクエリパターンを生成するかまだわからない、初期の探索段階にも適しています。この場合、Agent Skillsは、目的に合わせて構築されたツールに頼ることなく、正しくクエリを実行するための十分な構造を提供できます。ただし、反復的なタスクについてデータベースへの適切なクエリを見つけ出すためにエージェントが何度も試行錯誤しなければならない場合、インターフェースとしてシェルツールを使うことによって生じる、トークンオーバーヘッドによるデメリットが、追加のツールを避けられるという単純性のメリットを上回ってしまいます。</p><h3>専用のデータベースツール</h3><p>特に、繰り返し現れるクエリパターンが構造化されていたり分析的なものであったりする場合は、専用のデータベースツールが必要になります。<a href="https://vercel.com/blog/testing-if-bash-is-all-you-need">VercelとBraintrustのブログ記事</a>では、カスタマーサポートチケットや営業電話の書き起こしなどの半構造化データに関する実際の検索タスクで、さまざまな検索ツールセットを持つエージェントを比較しました（「『セキュリティ』に言及している未解決の問題はいくつありますか？」や「バグが報告され、後に誰かがそれを修正したと主張するPRが提出された問題を検索してください」など）[4]。</p><p>その結果、専用のデータベースツールを使用するエージェントは、シェルツールとファイルシステムのみを使用するエージェントに比べて、トークンの使用数が少なく、処理速度が速く、ミスも少ないことが判明しました。ここから得られる教訓は、クエリが半構造化データに対する分析推論を必要とする場合、データベースを直接扱うツールが正しい選択肢であるということです。</p><h3>検索インターフェースを組み合わせる</h3><p>すべてのクエリを適切に処理できる単一の検索インターフェースはありません。たとえば、Cursorはシェルツール（grepによる検索用）とセマンティック検索ツールを組み合わせて、エージェントがユーザーのプロンプトに基づいて適切なツールを選択できるようにしています。Cursorによると、エージェントは特定のシンボルや文字列を照合するためにgrepを選択し、概念的または行動に関する質問にはセマンティック検索を選択し、探索的なタスクには両方を使用するとのことです。</p><p>Vercelの実験レポートでも同じ結果が報告されています。シェルツールと専用データベースツールの両方にアクセスできるハイブリッドエージェントが、まず専用のデータベースツールを使用し、次にファイルシステムをgrepで検索して結果を確認するという方法を用いることにより、テストした全エージェントの中で最高のパフォーマンスを達成しました。しかし、このアプローチでは、ツールの選択と検証について検討するためにより多くのトークンと時間がかかります。</p><p>どちらの例でもパターンは同じです。インターフェースを組み合わせればどんな単一のインターフェースにも勝りますが、コストと遅延の増加というトレードオフも伴うということです。</p><h2>適切なツールセットを見つけるための実践的な推奨事項</h2><p>適切な検索インターフェースの組み合わせとは、小さく、目的が明確で、エージェントの実際のクエリパターンに即したものです。現在のベストプラクティスは、エージェントが何百ものMCPツールを備えるのではなく、備えるツールの数を最低限に抑えるということです。これは、利用可能なすべてのツールを事前に開示すると、コンテキストウィンドウが肥大化し、エージェントが実際にどのツールを使用すべきか混乱してしまうからです。たとえば、Claude Codeが備えているツールは約20個にとどまると報告されています。</p><p>その代わりに、段階的開示の考え方では、最小限のツールセットから始め、必要になったときにのみエージェントが追加機能を探すようにします。Anthropic [5]とCursor [6]の研究によると、このアプローチで47%–85%のトークンを節約できます。たとえば、Claude Codeはこの方法を直接実装しているため、エージェントはLLMを呼び出すたびにコンテキストを消費することなく、APIやデータベースにクエリする方法を段階的に発見できます。</p><p>エージェントのクエリパターンを把握できたら、エージェントがデフォルトでアクセス可能な検索ツールセットを見直すと良いでしょう。採用すべきツールを決定するにあたり、のトレードオフを考えるのに便利なのが、<a href="https://www.elastic.co/search-labs/blog/database-retrieval-tools-context-engineering#building-the-right-database-retrieval-tools-%5C(%E2%80%9Clow-floor,-high-ceiling%E2%80%9D%5C">「敷居を低く、限界を高く」という原則</a>です。限界の高いツールはエージェントの可能性を制限しません。たとえば、汎用的なシェルツールを使用すると、エージェントは曖昧なものを含めて完全なデータベースクエリを作成できますが、推論オーバーヘッド、遅延の増加、そして信頼性の低下を伴います。</p><p>敷居の低いツールはその逆です。これは、特定のクエリをラップし、最小限の推論オーバーヘッドでエージェントに即座にアクセスできる専用のツールであり、低コストと高い信頼性を実現します。ただし、事前のエンジニアリングが必要で、あらゆるクエリをカバーできるわけではなく、エージェントが適切なツールを選びにくくなる可能性もあります。</p><p>それぞれのツールは一長一短です。敷居の低いツールは、エージェントが正しく使用するのは簡単ですが、適用範囲は限定的です。限界の高いツールは多用途ですが、使いこなすにはより多くの推論が必要となります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72deecc6781e3499/6a170acd5091682f4fe1baba/e6d1b973be4b0a0a25c99c74f02a47e98395a3f7-1200x630.png" alt="エージェント設計アプローチ（敷居が高い/限界が高い、敷居が低い/限界が低い、敷居が低い/限界が高い）の比較図。異なるツール戦略がエージェントが曖昧、多目的、および予測可能なクエリを処理する方法にどのように影響するかを示しています。" /><p>ほとんどのエージェントは、さまざまな検索ツールを組み合わせる必要があります。ただし、どのツールも、追加するに見合うだけの価値が求められます。まずは汎用的な検索ツール（たとえば <code>search_database()</code>ツールやシェルツール）から始めることをお勧めします。そして、セキュリティ目的で既に保持しているコマンドログを活用して、ツールの呼び出し、再試行、およびユーザークエリごとの呼び出し回数などを含め、エージェントが実際に何をしているかを追跡しましょう。そして、あるクエリパターンが繰り返されたり失敗したりすることを把握できたなら、それが専用ツールを作るべき合図です。</p><h2>まとめ</h2><p>ファイルシステム対データベースという議論は、エンジニアが問うべき実際の問題から目をそらしています。「<em>エージェントが独自のコンテキストを構築するために必要な、適切な検索インターフェースとは何か</em>」という問いの答えはおそらく、「<em>単一のものではない</em>」でしょう。</p><p>シェルツールは、さまざまな文脈外の情報源とやり取りするための汎用性の高いツールであり、良い出発点となります。しかし、構造化された分析クエリを使用するユースケースでは、専用のデータベースツールほど効率的で正確ではありません。</p><p>目標は、エージェントの実際のクエリパターンをうまく処理できる最小限の検索ツールを見つけることです。まずはシェルツールから始めて、エージェントが実際に何をしているかをログに記録しましょう。繰り返している、または失敗しているクエリパターンを把握できたら、専用なツールを設計すべき時です。</p><h2>参照資料</h2><p>1. Thariq（Anthropic）。<a href="https://x.com/trq212/status/2027463795355095314">Lessons from Building Claude Code: Seeing like an Agen</a>（2026年）。</p><p>2. Cursor：Documentation。<a href="https://cursor.com/docs/agent/tools/search">セマンティック検索とエージェント検索</a>（2026年）。</p><p>3. Harrison Chase (LangChain)。<a href="https://x.com/hwchase17/status/2011814697889316930">How we built Agent Builder’s memory system</a>（2026年）。</p><p>4. Ankur Goyal（Braintrust）とAndrew Qu（Vercel）。<a href="https://vercel.com/blog/testing-if-bash-is-all-you-need">Testing if "bash is all you need"</a>（2026年）。</p><p>5. Anthropic。<a href="https://www.anthropic.com/engineering/advanced-tool-use">Introducing advanced tool use on the Claude Developer Platform</a>（2025年）。</p><p>6. Cursor。<a href="https://cursor.com/blog/dynamic-context-discovery">動的コンテキスト検出</a>（2026年）。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/search-tools-context-engineering</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/search-tools-context-engineering</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Leonie Monigatti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b9bbbff55c09fa4/6a170acecdacbff1167d29fd/f91e4d07915ba7bf3b7abf15fac8fab3350f7df2-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch Inference APIとHugging Faceモデルを組み合わせて使用]]></title>
    <description><![CDATA[推論エンドポイントを使用してElasticsearchをHugging Faceモデルに接続する方法と、セマンティック検索とチャット補完機能を備えた多言語ブログ推奨システムを構築する方法を学びましょう。]]></description>
    <content:encoded><![CDATA[<p>最近のアップデートで、Elasticsearchは<a href="https://endpoints.huggingface.co/">Hugging Face Inference Service</a>でホストされているモデルに接続するためのネイティブ統合機能を導入しました。この記事では、この統合を構成し、大規模言語モデル（LLM）を使用して簡単なAPI呼び出しを通じて推論を実行する方法を探ります。リソース使用量と解答品質のバランスが取れた軽量汎用モデルである<a href="https://huggingface.co/HuggingFaceTB/SmolLM3-3B">SmolLM3-3B</a>を使用します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9094997548bd70f8/6a170d6a839dfa0ad6dcff54/7ddadf1976421a860a7d62087239adb9150d808b-1999x1388.png" alt="複数の小規模言語モデルを、x軸にモデルサイズ（数十億個のパラメータ）、y軸に勝率（パーセント）でプロットした散布図。SmolLM3-3Bは効率性の傾向において上位に位置し、同規模の他のモデルよりも高い勝率を示しています。" /><h2>要件</h2><ul><li><p><strong>Elasticsearch 9.3またはElastic Cloud Serverless：</strong><a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">これらの指示に従って</a>クラウド導入を作成することもできますし、<a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart#local-dev-quick-start"><code>start-local</code></a>クイックスタートを使うこともできます。</p></li><li><p><strong>Python 3.12：</strong>Pythonは<a href="https://www.python.org/">こちら</a>からダウンロードしてください。</p></li><li><p><strong>Hugging Face</strong><a href="https://huggingface.co/docs/hub/en/security-tokens">アクセストークン</a>。</p></li></ul><h2>Hugging Face推論エンドポイントを使用したチャットの完了</h2><p>まず、ElasticsearchをHugging Faceの<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put">推論エンドポイント</a>に接続し、ブログ記事のコレクションからAIを活用したレコメンデーションを生成する実践的な例を作成します。アプリのナレッジベースには、会社のブログ記事のデータセットを使用します。これには価値のある情報が含まれていますが、多くの場合、見つけるのが困難です。</p><p>このエンドポイントでは、<a href="https://www.elastic.co/docs/solutions/search/semantic-search">セマンティック検索</a>が指定されたクエリに対して最も関連性の高い記事を取得し、Hugging Face LLMがそれらの結果に基づいて短いコンテキスト推奨を生成します。</p><p>これから構築する情報フローの概要を見ていきましょう。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf217b7b7db4e1e6c/6a170d6ca929cf8022ae0a3b/1dfbc2323438feaaa42e13ab242dd1f7166f74aa-1200x676.png" alt="Elasticsearchインデックスがセマンティック検索結果を推論エンドポイントに送り込み、そこから記事のレコメンデーション結果が返されるフロー図。" /><p>この記事では、コンパクトなサイズと強力な多言語推論能力・ツール呼び出し能力を組み合わせた<strong>SmolLM3-3B</strong>の性能を検証します。検索クエリに基づいて、一致するすべてのコンテンツ（英語とスペイン語）をLLMに送信し、検索クエリと結果に基づいたカスタムメイドの説明を含むおすすめ記事のリストを生成します。</p><p>AIによる推奨生成システムを備えた記事サイトのUIは次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20e69b9a06fecd65/6a170d6e839dfa6f97dcff58/8d3b86b212f28ff279f2da67a33e6134039f0e4e-1999x949.png" alt="AIによるおすすめ生成システムを備えた記事サイトのUI。3つの例がリストされており、テキストは英語、タイトルは英語またはスペイン語のいずれかで表示される。" /><p>このアプリケーションの完全な実装は、リンク先の<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/notebook.ipynb">ノートブック</a>で確認できます。</p><h3>Elasticsearch推論エンドポイントの構成</h3><p>Elasticsearch <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-hugging-face">Hugging Face推論エンドポイント</a>を使用するには、2つの重要な要素（Hugging Face APIキーと実行中のHugging FaceエンドポイントURL）が必要です。下の画像のように表示されるはずです。</p>PUT _inference/chat_completions/hugging-face-smollm3-3b
{
    "service": "hugging_face",
    "service_settings": {
        "api_key": "hugging-face-access-token", 
        "url": "url-endpoint" 
    }
}<p>Hugging FaceのElasticsearchにおける推論エンドポイントは、 <code>text_embedding</code>, <code>completion</code>, <code>chat_completion</code>, と <code>rerank</code>の異なるタスクタイプをサポートしています。このブログ記事では、検索結果とシステムプロンプトに基づいて会話形式のレコメンデーションをモデルに生成させる必要があるため、<code>chat_completion</code> を使用します。このエンドポイントを使用すると、Elasticsearch APIを使用してElasticsearchから直接チャットの完了を簡単に実行できます。</p>POST _inference/chat_completion/hugging-face-smollm3-3b/_stream
{
  "messages": [
      { "role": "user", "content": "&lt;user prompt&gt;" }
  ]
}<p>これはアプリケーションのコアとして機能し、モデルを通過するプロンプトと検索結果を受け取ります。理論について説明したので、アプリケーションの実装を始めましょう。</p><h4>Hugging Faceでの推論エンドポイントの設定</h4><p>Hugging Faceモデルをデプロイするために、モデルのエンドポイントをデプロイするための簡単で高速なサービス<a href="https://huggingface.co/inference-endpoints/dedicated">Hugging Faceワンクリック導入</a>を使用します。これは有料サービスであり、利用には追加料金が発生する可能性があることにご注意ください。このステップでは、記事の推奨を生成するために使うモデルインスタンスが作成されます。</p><p>ワンクリックカタログからモデルを選択できます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta7bdfa43d6766324/6a170d6fb339d59e5476a039/b816e9fba1fe172687bf58f5143fb1f838c1077f-549x331.png" alt="「smoll3」にフィルタリングされたモデルカタログのインターフェースビュー。テキスト生成「smollm3‑3b」という名前の1つのモデル、vLLM、GPU 1× NVIDIA L4、定価$ 0.8 と、すべての Hugging Faceモデルに検索を拡張する提案のメモが表示。" /><p><strong>SmolLM3-3B</strong>モデルを選択します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb0a2e6ffd7deb20/6a170d710c48574b7401aafc/610d3aba0429f3666c2df3616d513eb6a4397c0c-502x478.png" alt="SmolLM3-3Bモデルのエンドポイントを作成するためのインターフェース。モデル名、「Hugging Faceによって検証済み」という注記、エンドポイント名フィールド、実行中のレプリカ1つあたり1時間0.80ドルのコスト、cURLオプション、および「エンドポイントの作成」ボタンが表示。" /><p>ここから、Hugging FaceのエンドポイントURLを取得します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt25714021711ed6ff/6a170d72c1e8a54853f88336/025094ddb2cfbd1f0f216a5ec4e119b0f4fa2c42-646x328.png" alt="「smollm3-3b-pnz」という名前のHugging Face推論エンドポイントのダッシュボードビュー。緑色の実行ステータス、アクティブなレプリカ1つ、過去1時間のリクエスト数0、ナビゲーションタブ、表示されているエンドポイントURLが表示。" /><p>Elasticsearch <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-hugging-face">Hugging Faceの推論エンドポイントのドキュメント</a>で述べられているように、テキスト生成にはOpenAI APIと互換性のあるモデルが必要です。そのため、<code>/v1/chat/completions</code>のサブパスをHugging FaceのエンドポイントURLに追加する必要があります。最終的な結果は次のようになります。</p>https://j2g31h0futopfkli.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions<p>これで準備が整いましたので、Pythonノートブックでコーディングを開始できます。</p><h4>Hugging Face APIキーの生成</h4><p><a href="https://huggingface.co/join">Hugging Faceアカウント</a>を作成し、<a href="https://huggingface.co/docs/hub/en/security-tokens#user-access-tokens">以下の指示</a>に従ってAPIトークンを取得してください。トークンの種類は、<em>fine-grained</em>（本番環境に推奨。特定のリソースへのアクセスのみを提供）、<em>read</em>（読み取り専用アクセス用）、<em>write</em>（読み取りおよび書き込みアクセス用）の3つから選択できます。このチュートリアルでは、推論エンドポイントを呼び出すだけでよいので、readトークンで十分です。次のステップのために、このキーを保存しておいてください。</p><h4>Elasticsearch推論エンドポイントの設定</h4><p>まず、Elasticsearch Pythonクライアントを宣言します。</p>os.environ["ELASTICSEARCH_API_KEY"] = "your-elasticsearch-api-key"
os.environ["ELASTICSEARCH_URL"] = "https://xxxx.us-central1.gcp.cloud.es.io:443"

es_client = Elasticsearch(
    os.environ["ELASTICSEARCH_URL"], api_key=os.environ["ELASTICSEARCH_API_KEY"]
)<p>次に、Hugging Faceモデルを使用するElasticsearch推論エンドポイントを作成します。このエンドポイントを使用すると、ブログ記事とモデルに渡されたプロンプトに基づいて応答を生成できます。</p>INFERENCE_ENDPOINT_ID = "smollm3-3b-pnz"

os.environ["HUGGING_FACE_INFERENCE_ENDPOINT_URL"] = (
 "https://j2g31h0futopfkli.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions"
)
os.environ["HUGGING_FACE_API_KEY"] = "hf_xxxxx"

resp = es_client.inference.put(
        task_type="chat_completion",
        inference_id=INFERENCE_ENDPOINT_ID,
        body={
            "service": "hugging_face",
            "service_settings": {
                "api_key": os.environ["HUGGING_FACE_API_KEY"],
                "url": os.environ["HUGGING_FACE_INFERENCE_ENDPOINT_URL"],
            },
        },
    )<h3>データセット</h3><p>このデータセットには、クエリの対象となる<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/dataset.json">ブログ記事</a>が含まれており、ワークフロー全体で使用される多言語コンテンツセットを表しています。</p>// Articles dataset document example: 
{
    "id": "6",
    "title": "Complete guide to the new API: Endpoints and examples",
    "author": "Tomas Hernandez",
    "date": "2025-11-06",
    "category": "tutorial",
    "content": "This guide describes in detail all endpoints of the new API v2. It includes code examples in Python, JavaScript, and cURL for each endpoint. We cover authentication, resource creation, queries, updates, and deletion. We also explain error handling, rate limiting, and best practices. Complete documentation is available on our developer portal."
  }<h4>Elasticsearch マッピング</h4><p>データセットが定義されたので、ブログ記事の構造に適切にフィットするデータスキーマを作成する必要があります。Elasticsearchにデータを格納するために以下の<a href="https://www.elastic.co/docs/manage-data/data-store/mapping">インデックスマッピング</a>が使用されます。</p>INDEX_NAME = "blog-posts"

mapping = {
    "mappings": {
        "properties": {
            "id": {"type": "keyword"},
            "title": {
                "type": "object",
                "properties": {
                    "original": {
                        "type": "text",
                        "copy_to": "semantic_field",
                        "fields": {"keyword": {"type": "keyword"}},
                    },
                    "translated_title": {
                        "type": "text",
                        "fields": {"keyword": {"type": "keyword"}},
                    },
                },
            },
            "author": {"type": "keyword", "copy_to": "semantic_field"},
            "category": {"type": "keyword", "copy_to": "semantic_field"},
            "content": {"type": "text", "copy_to": "semantic_field"},
            "date": {"type": "date"},
            "semantic_field": {"type": "semantic_text"},
        }
    }
}


es_client.indices.create(index=INDEX_NAME, body=mapping)<p>ここで、データがどのように構造化されているかをより明確に見ることができます。セマンティック検索を使用して自然言語に基づいて結果を取得し、<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/copy-to"><code>copy_to</code></a>プロパティを使用してフィールドの内容を<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><code>semantic_text</code></a>フィールドにコピーします。さらに、<code>title</code>フィールドには2つのサブフィールドが含まれています。<code>original</code>サブフィールドは、記事の元の言語に応じて英語またはスペイン語でタイトルを格納し、<code>translated_title</code>サブフィールドはスペイン語の記事にのみ存在し、元のタイトルの英語訳が含まれています。</p><h3>データの取り込み</h3><p>以下のコードスニペットは<a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript/bulk_examples">bulk API</a>を使用してブログ投稿データセットをElasticsearchに取り込みます。</p>def build_data(json_file, index_name):
    with open(json_file, "r") as f:
        data = json.load(f)

    for doc in data:
        action = {"_index": index_name, "_source": doc}
        yield action


try:
    success, failed = helpers.bulk(
        es_client,
        build_data("dataset.json", INDEX_NAME),
    )
    print(f"{success} documents indexed successfully")

    if failed:
        print(f"Errors: {failed}")
except Exception as e:
    print(f"Error: {str(e)}")<p>Elasticsearchに記事を取り込んだので、次に<code>semantic_text</code>フィールドに対して検索できる関数を作成する必要があります:</p>def perform_semantic_search(query_text, index_name=INDEX_NAME, size=5):
    try:
        query = {
            "query": {
                "match": {
                    "semantic_field": {
                        "query": query_text,
                    }
                }
            },
            "size": size,
        }

        response = es_client.search(index=index_name, body=query)
        hits = response["hits"]["hits"]

        return hits
    except Exception as e:
        print(f"Semantic search error: {str(e)}")
        return []<p>推論エンドポイントを呼び出す関数も必要です。この場合、<strong><code>chat_completion</code></strong>タスクタイプを使用してエンドポイントを呼び出し、ストリーミング応答を取得します。</p>def stream_chat_completion(messages: list, inference_id: str = INFERENCE_ENDPOINT_ID):
    url = f"{ELASTICSEARCH_URL}/_inference/chat_completion/{inference_id}/_stream"
    payload = {"messages": messages}
    headers = {
        "Authorization": f"ApiKey {ELASTICSEARCH_API_KEY}",
        "Content-Type": "application/json",
    }

    try:
        response = requests.post(url, json=payload, headers=headers, stream=True)
        response.raise_for_status()

        for line in response.iter_lines(decode_unicode=True):
            if line:
                line = line.strip()

                if line.startswith("event:"):
                    continue

                if line.startswith("data: "):
                    data_content = line[6:]

                    if not data_content.strip() or data_content.strip() == "[DONE]":
                        continue

                    try:
                        chunk_data = json.loads(data_content)

                        if "choices" in chunk_data and len(chunk_data["choices"]) &gt; 0:
                            choice = chunk_data["choices"][0]
                            if "delta" in choice and "content" in choice["delta"]:
                                content = choice["delta"]["content"]
                                if content:
                                    yield content

                    except json.JSONDecodeError as json_err:
                        print(f"\nJSON decode error: {json_err}")
                        print(f"Problematic data: {data_content}")
                        continue

    except requests.exceptions.RequestException as e:
        yield f"Error: {str(e)}"<p>ここで、 <code>chat_completions</code> 推論エンドポイントと推薦エンドポイントを合わせてセマンティック検索関数を呼び出し、カードに割り当てられるデータを生成する関数を書くことができます。</p>def recommend_articles(search_query, index_name=INDEX_NAME, max_articles=5):
    print(f"\n{'='*80}")
    print(f"🔍 Search Query: {search_query}")
    print(f"{'='*80}\n")

    articles = perform_semantic_search(search_query, index_name, size=max_articles)

    if not articles:
        print("❌ No relevant articles found.")
        return None, None

    print(f"✅ Found {len(articles)} relevant articles\n")

    # Build context with found articles
    context = "Available blog articles:\n\n"
    for i, article in enumerate(articles, 1):
        source = article.get("_source", article)
        context += f"Article {i}:\n"
        context += f"- Title: {source.get('title', 'N/A')}\n"
        context += f"- Author: {source.get('author', 'N/A')}\n"
        context += f"- Category: {source.get('category', 'N/A')}\n"
        context += f"- Date: {source.get('date', 'N/A')}\n"
        context += f"- Content: {source.get('content', 'N/A')}\n\n"

    system_prompt = """You are an expert content curator that recommends blog articles.

    Write recommendations in a conversational style starting with phrases like:
    - "If you're interested in [topic], this article..."
    - "This post complements your search with..."
    - "For those looking into [topic], this article provides..."


    FORMAT REQUIREMENTS:
    - Return ONLY a JSON array
    - Each element must have EXACTLY these three fields: "article_number", "title", "recommendation"
    - If the original title is in spanish, use the "translated_title" subfield in the "title" field

    Keep each recommendation concise (2-3 sentences max) and focused on VALUE to the reader.

    EXAMPLE OF CORRECT FORMAT:
    [
        {"article_number": 1, "title": "Article title in english", "recommendation": "If you are interested in [topic], this article provides..."},
        {"article_number": 2, "title": "Article title in english", "recommendation": " for those looking into [topic], this article provides..."}
    ]

    Return ONLY the JSON array following this exact structure."""

    user_prompt = f"""Search query: "{search_query}"

    Generate recommendations for the following articles: {context}
    """

    messages = [
        {"role": "system", "content": "/no_think"},
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt},
    ]

    # LLM generation
    print(f"{'='*80}")
    print("🤖 Generating personalized recommendations...\n")

    full_response = ""

    for chunk in stream_chat_completion(messages):
        print(chunk, end="", flush=True)
        full_response += chunk

    return context, articles, full_response<p>最後に、情報を抽出して出力できるようにフォーマットする必要があります。</p>def display_recommendation_cards(articles, recommendations_text):
    print("\n" + "=" * 100)
    print("📇 RECOMMENDED ARTICLES".center(100))
    print("=" * 100 + "\n")

    # Parse JSON recommendations - clean tags and extract JSON
    recommendations_list = []
    try:

        # Clean up &lt;think&gt; tags
        cleaned_text = re.sub(
            r"&lt;think&gt;.*?&lt;/think&gt;", "", recommendations_text, flags=re.DOTALL
        )
        # Remove markdown code blocks ( ... ``` or ``` ... ```)
        cleaned_text = re.sub(r"```(?:json)?", "", cleaned_text)
        cleaned_text = cleaned_text.strip()

        parsed = json.loads(cleaned_text)

        # Extract recommendations from list format
        for item in parsed:
            article_number = item.get("article_number")
            title = item.get("title", "")
            rec_text = item.get("recommendation", "")

            if article_number and rec_text:
                recommendations_list.append(
                    {
                        "article_number": article_number,
                        "title": title,
                        "recommendation": rec_text,
                    }
                )
    except json.JSONDecodeError as e:
        print(f"⚠️  Could not parse recommendations as JSON: {e}")
        return

    for i, article in enumerate(articles, 1):
        source = article.get("_source", article)

        # Card border
        print("┌" + "─" * 98 + "┐")

        # Find recommendation and title for this article number
        recommendation = None
        title = None
        for rec in recommendations_list:
            if rec.get("article_number") == i:
                recommendation = rec.get("recommendation")
                title = rec.get("title")
                break

        # Print title
        title_lines = textwrap.wrap(f"📌 {title}", width=94)
        for line in title_lines:
            print(f"│  {line}".ljust(99) + "│")

        # Card border
        print("├" + "─" * 98 + "┤")

        # Print recommendation
        if recommendation:
            recommendation_lines = textwrap.wrap(recommendation, width=94)
            for line in recommendation_lines:
                print(f"│  {line}".ljust(99) + "│")

        # Card bottom
        print("└" + "─" * 98 + "┘")<p>セキュリティブログの投稿について質問して、これをテストしてみましょう。</p>search_query = "Security and vulnerabilities"

context, articles, recommendations = recommend_articles(search_query)

print("\nElasticsearch context:\n", context)

# Display visual cards
display_recommendation_cards(articles, recommendations)<p>ここでは、ワークフローによって生成されたコンソール内のカードを確認できます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4aa221a08a51aeb3/6a170d7460084be1413c45d6/730d35212594bb3db30447c3ea7e2a92857287b7-1999x1515.png" alt="セクションタイトル「Recommended Articles」。認証システムの脆弱性、移行リスク、REST API v2のパフォーマンスと認証の改善、通知システムの変更、新しいAPIの完全ガイドなど、5つの記事の要約がボックスで表示。" /><p>すべてのヒットとLLMの対応を含む完全な結果を<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/results.md">このファイル</a>でご覧いただけます。</p><p>「Security and vulnerabilities」に関連する記事をクエリしています。この質問は、Elasticsearchに保存されているドキュメントに対する検索クエリとして使用されます。取得された結果はモデルに渡され、モデルはその内容に基づいてレコメンデーションを生成します。ご覧の通り、このモデルは読者がクリックする動機付けとなる魅力的な短いテキストを非常にうまく生成しています。</p><h2>まとめ</h2><p>この例では、ElasticsearchとHugging Faceを組み合わせて、AIアプリケーション向けの高速で効率的な集中型システムを構築する方法を示します。Hugging Faceの豊富なモデルカタログにより、このアプローチでは手作業を削減し、柔軟性を確保できます。特にSmolLM3-3Bを使用すると、コンパクトな多言語モデルでも、セマンティック検索と組み合わせることで有意義な推論とコンテンツ生成を実現できることがわかります。これらのツールを組み合わせることで、インテリジェントなコンテンツ分析と多言語アプリケーションを構築するための、拡張性が高く効果的な基盤を提供できます。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hugging-face-elasticsearch-inference-api</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hugging-face-elasticsearch-inference-api</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[統合]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f961af4cb26ec97/6a170d767d8d6790c770e790/1417d6ff033712206c9bd4bcc22074ee3437ce96-1999x1125.png" length="0" type="image/png"/>
    <pubDate>Mon, 23 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ElasticsearchのGemini CLI拡張機能（ツールとスキル付き）]]></title>
    <description><![CDATA[GoogleのGemini CLIでElasticsearchのデータを検索、取得、分析するためのElasticの拡張機能（開発者およびエージェントのワークフロー向け）をご紹介します。
]]></description>
    <content:encoded><![CDATA[<p>GoogleのGemini CLI用のElastic拡張機能のリリースを発表できることを嬉しく思います。これにより、<a href="https://www.elastic.co/elasticsearch">Elasticsearch</a>と<a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a>のパワーを、AI開発ワークフローに直接組み込むことができます。この拡張機能には、Elasticsearchを操作するための最近開発されたエージェントスキルもいくつか用意されています。</p><p>この拡張機能はオープンソースプロジェクトとして<a href="https://github.com/elastic/gemini-cli-elasticsearch">こちら</a>から利用できます。</p><h2>Gemini CLIの概要とインストール方法</h2><p><a href="https://geminicli.com/">Gemini CLI</a> は、GoogleのGeminiモデルを直接コマンドラインに取り込むオープンソースのAIエージェントです。ターミナルからAIと対話することで、コードの生成、ファイルの編集、シェルコマンドの実行、ウェブからの情報の取得などのタスクを実行できます。</p><p>一般的なチャットインターフェースとは異なり、Gemini CLIはローカル開発環境と統合されます。つまり、プロジェクトのコンテキストを理解し、ファイルを変更し、ビルドやテストを実行し、ワークフローをターミナル内で直接自動化することができます。開発者、サイト信頼性エンジニア（SRE）、コマンドラインのワークフローを離れることなくAI支援のコーディングと自動化を求めるエンジニアにとって役立ちます。</p><p>Gemini CLIは複数のパッケージマネージャーを使ってインストール可能です。最も一般的な方法はnpm経由です。</p>npm install -g @google/gemini-cli<p>その他のインストール方法については、<a href="https://geminicli.com/docs/get-started/installation/">公式のインストールページ</a>を参照してください。</p><p>インストール後、以下のコマンドを実行してCLIを起動します。</p>gemini<p>図1に示すような画面が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4ff43abe550941b4/6a17072ba29299fc2ad00fa4/6dfcec4a77b3dc83bf0d974417bf2e211abb1f4f-876x468.png" alt="Gemini CLIのスクリーンショット。" /><h2>Elasticsearchを構成</h2><p>Elasticsearchインスタンスを実行する必要があります。モデルコンテキストプロトコル（MCP）サーバーを使用するには、Kibana 9.3以降もインストールする必要があります。Elasticsearchクエリ言語 (ES|QL) スキル (<code>esql</code>) を使用するためにKibanaは必要ありません。</p><p><a href="https://www.elastic.co/cloud">Elastic Cloud</a>で無料トライアルを有効化するか、<a href="https://github.com/elastic/start-local"><code>start-local</code></a>スクリプトを使ってローカルにインストールできます。</p>curl -fsSL https://elastic.co/start-local | sh<p>これにより、ElasticsearchとKibanaがコンピュータにインストールされ、Gemini CLIの設定に使用するAPIキーが生成されます。</p><p>APIキーは前のコマンドの出力として表示され、 <strong><code>elastic-start-local</code></strong>フォルダ内の<strong>.env</strong>ファイルに保存されます。</p><p>オンプレミスのElasticsearchを使用している場合（例えば、<code>start-local</code>）、MCPでElastic Agent Builderを使用するには、大規模言語モデル（LLM）を接続する必要があります。さまざまなオプションを理解するには、<a href="https://www.elastic.co/docs/explore-analyze/ai-features/llm-guides/llm-connectors">このドキュメントページ</a>をご覧ください。</p><p>Elastic Cloud（またはサーバーレス）を使用している場合は、LLM接続が事前構築されています。</p><h2>Elasticsearch拡張機能をインストールしてください</h2><p>次のコマンドを使用して、Gemini CLI用のElasticsearch拡張機能をインストールできます。</p>gemini extensions install https://github.com/elastic/gemini-cli-elasticsearch<p>Geminiを開き、以下のコマンドを実行することで、拡張機能が正常にインストールされたことを確認できます。</p>/extensions list<p>Elasticsearch拡張機能が利用可能になっているはずです。</p><p>MCP統合を使用するには、Elasticsearch 9.3以降のバージョンがインストールされている必要があります。<a href="https://www.elastic.co/kibana">Kibana</a>からMCPサーバーのURLを取得する必要があります。</p><ul><li><p>MCPサーバーのURLは、[エージェント] &gt; [すべてのツールを表示] &gt; [MCPの管理] &gt; [MCPサーバーのURLをコピー] から取得できます。</p></li><li><p>URLは次のようになります：https://your-kibana-instance/api/agent_builder/mcp</p></li></ul><p>ElasticsearchエンドポイントのURLが必要です。これは通常、Kibana Elasticsearchページの最上部に表示されます。Elasticsearchを<code>start-local</code>で実行している場合、 <code>start-local</code> .envファイルの<code>ES_LOCAL_URL</code>キーにエンドポイントが既に存在します。</p><p>APIキーも必要です。Elasticsearchを<code>start-local</code>で実行している場合、 <code>start-local</code> .envファイルには既に<code>ES_LOCAL_API_KEY</code>が含まれています。それ以外の場合は、<a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">こちらに</a>記載されているように、Kibanaインターフェースを使用してAPIキーを作成できます。</p><ul><li><p>Kibanaでは、[スタック管理] &gt; [セキュリティ] &gt; [APIキー] &gt; [APIキーの作成] の順に操作します。</p></li><li><p>API キーには読み取り権限のみを設定し、<a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/permissions#grant-access-with-roles">ここに</a>記載されているように<code>feature_agentBuilder.read</code>権限を有効にすることをお勧めします。</p></li><li><p>エンコードされたAPIキーの値をコピーしてください。</p></li></ul><p>シェルで必要な環境変数を設定してください。</p>export ELASTIC_URL="your-elasticsearch-url"
export ELASTIC_MCP_URL="your-elasticsearch-mcp-url"
export ELASTIC_API_KEY="your-encoded-api-key"<h2>サンプルデータセットをインストールする</h2><p>Kibanaから入手可能な<strong>eCommerce orders</strong>データをインストールできます。このデータベースには、eコマースWebサイトからの4,675件の注文に関する情報を含む<strong><code>kibana_sample_data_ecommerce</code></strong>という単一のインデックスが含まれています。各注文について、次の情報があります。</p><ul><li><p>顧客情報（名前、ID、生年月日、メールなど）。</p></li><li><p>注文日。</p></li><li><p>注文ID。</p></li><li><p>商品（価格、数量、ID、カテゴリー、割引、その他の詳細を含む全商品のリスト）</p></li><li><p>SKU。</p></li><li><p>合計金額（税抜、税込）。</p></li><li><p>合計数量。</p></li><li><p>地理情報（都市、国、大陸、場所、地域）。</p></li></ul><p>サンプルデータをインストールするには、Kibanaの<strong>統合</strong>ページを開き（検索トップバーで「Integration」を検索）、<strong>Sample Data</strong>をインストールしてください。詳細については、<a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">こちらの</a>ドキュメントを参照してください。</p><p>この記事の目的は、Gemini CLIをElasticsearchに接続し、<strong><code>kibana_sample_data_ecommerce</code></strong>インデックスとやり取りするのがいかに簡単かを示すことです。</p><h2>Elasticsearch MCPの使用方法</h2><p>Geminiで以下のコマンドを使用して接続状況を確認できます。</p>/mcp list<p>図2に示すように、 <strong><code>elastic-agent-builder</code></strong>が有効になっているはずです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt52b85e7255360f3b/6a17072da929cf33d3ae08f5/1508423bc1d1bc3c04a1cb01e2d59495a3516ed1-1465x844.png" alt="ツールのリストを備えた「elastic-agent-builder」MCPサーバー。" /><p>Elasticsearchはデフォルトのツールセットを提供しています。詳細は<a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools/builtin-tools-reference">こちらを</a>ご覧ください。</p><p>これらのツールを使用して、Elasticsearchと対話し、次のような質問をすることができます。</p><ul><li><p><code>Give me the list of all the indexes available in Elasticsearch.</code></p></li><li><p><code>How many customers are based in the USA in the kibana_sample_data_ecommerce index of Elasticsearch?</code></p></li></ul><p>質問に応じて、Geminiは利用可能なツールの一つ以上を使って回答を試みます。</p><h2>/elasticコマンド</h2><p>Gemini CLIのElasticsearch拡張機能では、さらに<strong><code>/elastic</code></strong>コマンドを追加しました。</p><p><strong><code>/help</code></strong>コマンドを実行すると、利用可能なすべての<code>/elastic</code>オプション（図3）が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt741c7451ecab10d2/6a17072ea6c2b9ccd6e79643/5b2a0727ce7a04354878dd048253d3f4d062324b-1983x230.png" alt="利用可能な `/elastic` コマンド。" /><p>これらのコマンドは、<code>elastic-agent-builder</code> MCPサーバーの特定のツールを直接実行したい場合に便利です。例えば、以下のコマンドを使用すると、 <code>kibana_sample_data_ecommerce</code>のマッピングを取得できます。</p>/elastic:get-mapping kibana_sample_data_ecommerce<p>これらのコマンドは、どのツールを呼び出すかをGeminiモデルに頼るのではなく、基本的に特定のツールを実行するためのショートカットです。</p><h2>Elasticsearchスキルの使用方法</h2><p>この拡張機能には、Elasticsearchで利用可能な<a href="https://www.elastic.co/docs/explore-analyze/discover/try-esql">Elasticsearchクエリ言語</a>である<a href="https://github.com/elastic/gemini-cli-elasticsearch/tree/main/skills/esql">ES|QL用のエージェントスキル</a>も付属しています。<a href="https://agentskills.io/home">エージェントスキル</a> は、Gemini CLIのようなAIコーディングエージェントに特定のタスクに合わせたカスタム指示を提供するオープンフォーマットです。<em>段階的開示</em>と呼ばれる概念を採用しており、最初のシステムプロンプトにスキルの簡単な説明のみを追加します。エージェントにElasticsearchへのクエリなどのタスクを実行するように依頼すると、リクエストが関連するスキルと照合され、詳細な指示が動的に読み込まれます。これは、トークン予算を効率的に管理すると同時に、AIが必要とする正確なコンテキストを提供する方法です。</p><p><strong><code>esql</code></strong><strong>スキル</strong>は、Gemini CLIがES|QLクエリを直接クラスターに対して書き込み、実行するように設計されています。ES|QLは強力なパイプクエリ言語で、データ調査、ログ分析、アグリゲーションを非常に直感的に行うことができます。このスキルを有効にすると、ES|QLの構文を調べる必要はなくなり、Gemini CLIにデータについて自然言語で質問するだけであとはエージェントが処理します。</p><p>実行は、ターミナルで実行されるシンプルな<a href="https://curl.se/">curl</a>コマンドを使用して行われます。これは、Elasticsearchが豊富なREST APIを提供し、システムをあらゆるアーキテクチャに容易に統合できるためです。</p><p><strong><code>esql</code></strong><strong>スキルが提供するもの：</strong></p><ul><li><p><strong>インデックスとスキーマの検出：</strong>エージェントは、スキルに搭載されたツールを使用して、利用可能なインデックスを一覧表示し、フィールドマッピングを取得できます。例えば、eCommerce データセットのクエリを書く前に、エージェントは <strong><code>kibana_sample_data_ecommerce</code></strong> でスキーマチェックを実行して、<strong><code>taxful_total_price</code></strong> や <strong><code>category</code></strong> のような利用可能なフィールドを理解することができます。</p></li><li><p><strong>シームレスな自然言語翻訳：</strong>スキルはエージェントに単なるリファレンスマニュアルにとどまらず、ユーザーの意図を解釈するための具体的なガイドを提供します。「サービス別にグループ化された平均応答時間を表示して」といった自然言語によるリクエストを入力すると、エージェントはスキルのバンドルされたパターンマッチングを使用して、入力された言葉を即座に正しいES|QLアグリゲーション、フィルタ、コマンドに変換します。</p></li><li><p><strong>自動修正：</strong> クエリが失敗した場合（例：タイプミスマッチや構文エラーなど）、スキルは生成されたクエリとElasticsearchのエラーを正確に返します。これにより、エージェントは即座にクエリを修正して再試行でき、介入する必要がありません。</p></li></ul><p><code>esql</code>スキルは<code>elastic-agent-builder</code> MCPサーバーのツールとしても利用できるため、このサーバーを一時的に無効にする必要があります。以下のコマンドを使用して無効にすることができます。</p>/mcp disable elastic-agent-builder<p>そうすれば、Gemini CLIに次のようなプロンプトを入力するだけで済みます。</p>Find the top 5 product categories by total sales revenue in the kibana_sample_data_ecommerce index<p>エージェントは以下を実行します：</p><ul><li><p><code>esql</code> スキルの必要性を認識します。</p></li><li><p><strong><code>kibana_sample_data_ecommerce</code></strong>のスキーマを確認してください。</p></li><li><p><code>FROM kibana_sample_data_ecommerce | STATS total_revenue = SUM(taxful_total_price) BY category.keyword | SORT total_revenue DESC | LIMIT 5</code> のようなES|QLクエリを構築します。</p></li><li><p>Elasticsearch APIに対してクエリを実行します。</p></li><li><p>最終的な答えを端末に直接表示します。</p></li></ul><p>ここでは、前回の質問に対するGeminiからの回答例を報告します。</p>───────────────────────────────────────────────────────────
&gt; Find the top 5 product categories by total sales revenue in the kibana_sample_data_ecommerce index   ───────────────────────────────────────────────────────────
✓  Activate Skill "esql": Interact with Elasticsearch using ES|QL and curl. Use when querying, managing indices,  …
Skill esql activated. Resources loaded from /home/user/.gemini/extensions/elasticsearch/skills/esql: 
Showing up to 200 items (files + folders).                                                                     /home/user/.gemini/extensions/elasticsearch/skills/esql/                                                            ├───SKILL.md 
└───references/
├───esql-reference.md 
├───generation-tips.md 
└───query-patterns.md

✦ I will start by exploring the mapping of the kibana_sample_data_ecommerce index to identify the fields for product category and sales revenue. Then, I will construct and execute an ES|QL query to determine the top 5 product categories by total sales revenue.

✓  Shell curl -s "${ELASTIC_URL%/}/kibana_sample_data_ecommerce/_mapping" -H "Authorization: ApiKey $(printenv ELAS…                                                                                                                       {  
   "kibana_sample_data_ecommerce": { 
     "mappings": { 
       "properties": {
         "category": { 
           "type": "text", 
           "fields": {
             "keyword": {
               "type": "keyword"
             }
           }
         }, 
         "currency": {
           "type": "keyword"
         }, 

✦ Okay, I have the mapping. The category is products.category.keyword and sales revenue is taxful_total_price. I'll now execute the ES|QL query to get the top 5 categories by revenue.

✓  Shell curl -s -X POST "${ELASTIC_URL%/}/_query" \  -H "Authorization: ApiKey $(printenv ELASTIC_API_KEY)" \  -H …
{ 
   "took": 62,
   "is_partial": false, 
   "completion_time_in_millis": 1772805644164,
   "documents_found": 4675,
   "values_loaded": 12084, 
   "start_time_in_millis": 1772805644102, 
   "expiration_time_in_millis": 1773237644087,  
   "columns": [ 
     { 
       "name": "total_revenue",    
       "type": "double"  
     }, 
     {   
       "name": "products.category.keyword",

✦ The query for top 5 product categories by revenue was successful. I'll format the returned JSON values into a   user-friendly list.

 ✓  Shell 
                             
 Top 5 product categories by total sales revenue: 
 1. Men's Clothing: 149393.91
 2. Women's Clothing: 135099.91
 3. Women's Shoes: 105479.17
 4. Men's Shoes: 91797.92
 5. Women's Accessories: 60830.31 

✦ I've found the answer; now I'll format it for clarity.

 ✓  Shell 

 Here are the top 5 product categories by total sales revenue:                                                        
                                                                      
 | Rank | Category              | Total Revenue | 
 |------|-----------------------|---------------| 
 | 1    | Men's Clothing        | 149,393.91    |
 | 2    | Women's Clothing      | 135,099.91    |
 | 3    | Women's Shoes         | 105,479.17    |
 | 4    | Men's Shoes           | 91,797.92     | 
 | 5    | Women's Accessories   | 60,830.31     | 

✦ I've formatted and presented the answer. I'm finished.
 ───────────────────────────────────────────────────────────<p>Geminiモデルが最終的な応答を生成する際にどのような手順を踏むかは非常に興味深い点です。ここでは、モデルの推論プロセスにおけるスキルの影響が明確に見て取れます。モデルがスキルを使用する必要がある、またはシェルコマンドを実行する必要があると初めて認識したとき、ヒューマン・イン・ザ・ループ方式を使用して許可を要求します。</p><p>スキーマの発見、クエリの生成、実行といった面倒な作業を<code>esql</code>スキルが処理することで、回答を得るための仕組みではなく、回答そのものに集中できるようになります。必要なデータが、適切な形式でターミナルに直接表示されます。構文を記述したり、別のアプリケーションに切り替えたりする必要は一切ありません。</p><h2>まとめ</h2><p>この記事では、最近リリースしたGemini CLI用のElasticsearch拡張機能を紹介しました。この拡張機能を使用すると、GeminiおよびElastic Agent Builderが提供するElasticsearch MCPサーバー（バージョン 9.3.0 以降で利用可能）と<code>/elastic</code>コマンドを使用してElasticsearchインスタンスとやり取りできます。</p><p>さらに、この拡張機能には、ユーザーの自然言語からのリクエストをES|QLに変換する <code>esql</code>スキルも含まれています。このスキルは、MCPサーバーが使用できない場合に特に役立ちます。なぜなら、基本的な通信はターミナルで実行されるシンプルなcurlコマンドによって行われるためです。Elasticsearchは、あらゆるプロジェクトに簡単に統合できる豊富なREST APIセットを提供します。これは特にエージェント型AIアプリケーションの開発時に有用です。</p><p>Gemini CLI拡張機能の詳細については<a href="https://github.com/elastic/gemini-cli-elasticsearch">こちら</a>のプロジェクトリポジトリをご覧ください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/gemini-cli-extension-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/gemini-cli-extension-elasticsearch</guid>
    <category><![CDATA[統合]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Walter Rafelsberger,Enrico Zimuel]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4ff43abe550941b4/6a17072ba29299fc2ad00fa4/6dfcec4a77b3dc83bf0d974417bf2e211abb1f4f-876x468.png" length="0" type="image/png"/>
    <pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ElasticのAgent Skills：AIエージェントをElasticのエキスパートに変換]]></title>
    <description><![CDATA[Elastic Agent Skillsを使用して、AIコーディングエージェントにクエリ、可視化、保護、自動化の知識を提供します。]]></description>
    <content:encoded><![CDATA[<p>専用プラットフォームでAIコーディングエージェントを使用しようとした開発者、サイト信頼性エンジニア（SRE）、アナリストは皆、同じ壁にぶつかっています。エージェントにクエリの作成、アラートの設定、または調査を依頼すると、惜しいところまではいくものの、正しくない結果になるのです。Elasticにはこの点で優位性があります。10年以上にわたるドキュメント、ブログ記事、コミュニティからの回答によって、AIエージェントは既にほとんどのデータプラットフォームよりもElasticをよく理解しているのです。しかし、その深みにはノイズが伴います。非推奨のAPIが現行のAPIと並んで表示され、古いパターンがベストプラクティスと同じくらい高い評価を受けます。エージェントは、3バージョン前にうまくいったアプローチを自信を持って再現します。なぜなら、そのトレーニングデータでは成功したからです。その結果、修正のコストが発生します。ユーザーは手動でドキュメントをコンテキストにフィードし、ハルシネーションによる構文を修正し、エージェントと一緒に作業するのではなく、エージェントを回避して作業するようになるのです。さらに悪いことに、高度な機能が全く使用されていません。これは、ユーザーがこうした機能を必要としていないからではなく、エージェントがその存在を認識していないためです。</p><p>だからこそ、当社は、Elasticsearch、Kibana、Elastic Observability、Elastic Securityといったプラットフォームに関するネイティブな専門知識である<a href="https://github.com/elastic/agent-skills">Elastic Agent Skills</a>をオープンソースとして提供しています。これらのスキルをすでに使用しているエージェントランタイムにドロップすることで、エージェントを多くの構文を推測する「ジェネラリスト」から、Elasticのエンジニアリングチームが使用する多くのアーキテクチャ標準を活用できる「スペシャリスト」へと改善します。この最初のテクニカルプレビューリリースでは<a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a>との最大限の互換性を備えたスキルに焦点を当てていますが、古いスタックリリースのサポートを強化するなど、急速に進化させる予定です。</p><p>さらに、Elasticはこの問題を両面から解決しようとしています。Elastic Platform上のエージェント向けに<a href="https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga">Elastic Agent Builder</a>（現在一般公開中）を利用して、データのアクセス制御を継承し、搭載の検索・分析ツールを使用し、ダッシュボード、アラート、調査と共にコンテキストで作業するAIエージェントを作成し、チャットすることができます。Elastic Platformで素晴らしいエージェント体験を実現するために懸命に取り組んでいますが、すべてのエージェントがElasticの中に存在するわけではありません。すでにCursor、Claude Codeなどのランタイムを使用されていると思いますが、それらのエージェントでもElasticが正しく動作する必要があります。そこで役立つのがAgent Skillsです。</p><h2>エージェントが専門プラットフォームで苦労する理由</h2><p>大規模言語モデル（LLM）は、非常に有能なジェネラリストです。豊富な事例を含むトレーニングデータを持つため、Pythonを記述したり、Kubernetesのマニフェストを説明したり、Reactコンポーネントをリファクタリングしたりすることができます。しかし、独自のクエリ言語、高度なAPIサーフェス、ドメイン固有のベストプラクティスなどを伴うプラットフォーム固有の作業となると、予測可能な形で不十分さを示します。</p><p>Elasticsearchの場合、そのギャップは具体的に現れます。</p><ul><li><p><strong>Elasticsearchクエリ言語（ES|QL）は新しい領域です。</strong>LLMはSQLに関するトレーニングを重点的に受けていますが、ES|QLは構文、機能、セマンティクスが異なるパイプ型クエリ言語です。エージェントは、一見もっともらしく見えるものの、解析できないクエリを頻繁に作成します。彼<code>WHERE</code> を <code>| WHERE</code>と混同し、存在しない関数を発明し、パイプベースの合成モデルを完全に見落としています。</p></li><li><p><strong>APIサーフェスは広く深いものです。</strong>Elasticsearch、Kibana、Elastic Securityは、検索、インジェスト、アラート、検出ルール、ケース管理、ダッシュボードなど、何百ものAPIを公開しています。エージェントは、一般的なトレーニングデータのみを武器に、どのエンドポイントを呼び出すか、リクエスト本文がどのようなものか、そして応答をどのように処理するかを推測しなければなりません。予測ミスが頻繁に起こるため、信頼が損われることになります。</p></li><li><p><strong>ベストプラクティスはトレーニングデータには含まれません。</strong><code>semantic_text</code>とカスタム埋め込みパイプラインは、それぞれどのような場合に使い分けるべきでしょうか？10GBのCSVの取り込みパイプラインはどのように構築すべきでしょうか？<a href="https://www.elastic.co/docs/solutions/security/detect-and-alert/mitre-attandckr-coverage">MITRE ATT&amp;CK</a>技術の適切な検出ルール構文はどれですか？汎用エージェントには、デフォルトで厳選され、信頼性の高い構造化されたElastic固有の知識がロードされていません。エージェントはそうした知識を探し出さなければならず、たとえ見つけたとしても、生の文書には熟練した専門家が持つ判断やベストプラクティスが必ずしも反映されているとは限りません。</p></li></ul><p>その結果、開発者は自分でコードを書くよりも、エージェントの出力を修正することに多くの時間を費やすことになります。予測していた結果とは言えません。</p><h2>Agent Skills：Platformの知識をエージェント向けにパッケージ化</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2099e0ccdf446fee/6a17074bd7c022e3e1de63d4/8d16ec00d16e70a916c5eef0aaa23fcc735b7186-1067x1280.png" alt="npx skills add elastic/agent-skills" /><p>Agent Skillsとは、エージェントランタイムが動的に読み込むことができる、指示、スクリプト、およ参照資料を含む自己完結型のディレクトリです。スキルがアクティブな時、エージェントは適切なタイミングで適切なコンテキストにアクセスできます。クエリ構文、APIパターン、検証ロジック、実践例などを活用し、一回の試行でタスクを正しく完了できます。</p><p>各スキルは、オープンな<a href="https://agentskills.io">agentskills.io</a>仕様に準拠しています。メタデータと構造化された手順を含む<code>SKILL.md</code>ファイルを含むフォルダです。独自のフォーマットもロックインもありません。スキルは、Cursor、Claude Code、GitHub Copilot、Windsurf、Gemini CLI、Cline、Codexなど、<a href="https://agentskills.io">さまざまな</a>エージェントランタイムで動作します。</p><h3>初期バージョンv0.1.0に含まれる内容</h3><p>最初のスキルセットは、Elastic Stackの5つの分野にまたがっています。</p><ul><li><p>Elasticsearch APIとのやり取り（検索、インデキシング、クラスター管理）</p></li><li><p>ダッシュボード、アラート、コネクターなどのKibanaコンテンツの構築と管理</p></li><li><p>Elastic Observabilityの専門知識</p></li><li><p>Elastic Securityの専門知識</p></li><li><p>Agent Builderで効果的なエージェントを作成</p></li></ul><h3>スキルは組み合わせ可能です</h3><p>スキルはモノリシックではなく、モジュール式の設計になっています。エージェントは、目の前のタスクに関連するスキルのみを読み込みます。ES|QLクエリを作成中なら、ES|QLスキルが発動します。その結果からダッシュボードを作る必要があるなら、ダッシュボードのスキルを習得します。アプリケーションの健全性を評価したいなら、サービス健全性スキルが対応します。セキュリティアラートの調査の場合は、トリアージスキルが調査が進むにつれてケース管理と対応スキルに連鎖します。</p><p>こうしたこの構成可能性のおかげで、すべてを網羅しようとする単一の巨大なプロンプトは必要なくなります。それぞれのスキルは、その分野に必要な文脈を正確に保持しており、それ以上でもそれ以下でもありません。</p><h2>検索やAIアプリケーションを開発する開発者向け</h2><p>Elasticsearchにデータをロードしたり、クエリを作成したり、インデックスを移行したりする際に、十分なスキルがあれば、コードを生成してエラーが発生し、その原因をドキュメントで調べるという繰り返しのサイクルを短縮できます。</p><p>エージェントにCSVファイルの読み込みを依頼すると、バックプレッシャーを処理し、データからマッピングを推測するストリーミングインジェストツールが使用されます。これは、最初の大きなファイルでメモリ不足になるような、手作業で作成した_bulkループではありません。ES|QLでクエリを実行するように依頼すると、実際のインデックス名とフィールドスキーマを検出し、その後、正しい構文、適切なアグリゲーション、バージョン対応の機能選択を備えた有効なパイプ付きクエリを作成します。これは、3回ものデバッグが必要なSQL風の推測ではなく、正確な処理です。クラスター間での再インデックスを指示すると、完全な運用ワークフローに従います。明示的なマッピングで送信先を作成し、スループットに合わせて設定を調整し、ジョブを非同期で実行し、完了時には本番環境の設定を復元します。これは、経験豊富なオペレーターが従う半分のステップをスキップする単なる_reindex呼び出しではありません。</p><p>修正が必須となるそれらしい出発点を示すエージェントの代わりに、出力が実際に機能するための運用規律を組み込んだエージェントが手に入ります。</p><p><strong>Elastic Agent Skillsの使用によるインパクトの例</strong></p><p>Eval</p><p>スキルによる変更内容</p><p>es-監査-クエリ-失敗したログイン</p><p>一般的な検索の代わりにスキルの監査ログクエリパターンを使用</p><p>es-authz-role-mapping-ldap</p><p>正しいロールマッピングAPI呼び出し構造を出力</p><p>esql-basic-query</p><p>ES|QLパイプ構文をクエリDSL上に記述</p><p>esql-error-handling</p><p>フィールド名を推測する代わりにスキーマを先に設定</p><p>esql-schema-discovery</p><p>インデックス名を推測したことは一度もなし</p><p>es-ingest-csv-with-infer</p><p>--infer-mappingsのみを使用し、--source-format csvとの組み合わせは回避（組み合わせると空のインデックスがマッピングされるため）</p><p>es-ingest-json-file</p><p>大容量ファイルを処理できる堅牢なインジェスト方式を採用</p><p>es-reindex-local-async</p><p>まずレプリカ数0、更新間隔「-1」で宛先インデックスを作成し、その後非同期で再インデックスを実行、ベースラインはあらゆる準備をスキップ</p><p>es-security-403-privileges</p><p>一般的なアドバイスではなく、スキルの診断ワークフローに従って特権エラーを解決</p><h2>セキュリティチーム向け</h2><p>セキュリティチームは、アラートのトリアージ、検出ルールの調整、ケースの管理といった、同じ運用ワークフローを毎日繰り返しています。Agent Skillsは、AIエージェントがこれらのワークフローを正しく実行し、適切な順序で適切なフィールド名で適切なAPIを呼び出せるように、その手順知識をエンコードします。IDEを離れることなく、ゼロから完全に機能するElastic Security環境を構築するまでの手順を実際に確認するには、<a href="https://www.elastic.co/security-labs/agent-skills-elastic-security">「AIエージェントからElastic Securityを使い始める」</a>を参照してください。</p><h2>オブザーバビリティおよび運用チーム向け</h2><p>Elastic Observabilityのための新しいAgent Skillsは、複雑なシステムのインスツルメンテーション、SLOの管理、複雑なデータの選別、サービスの健全性の評価といった運用の手間を軽減します。ネイティブのElastic専門知識をAIエージェントに直接組み込むことで、チームはシンプルな自然言語を使用して複雑なオブザーバビリティワークフローを実行できます。これにより、SREと運用チームはインシデントをより迅速に解決し、信頼性の高いシステムをより簡単に保守できます。詳しくは<a href="https://www.elastic.co/observability-labs/blog/elastic-agent-skills-observability-workflows">こちらのブログ記事</a>をご覧ください。</p><h2>オープンソース、オープン仕様、コミュニティ主導</h2><p>エージェントの知識はオープンであるべきだと当社は考えているため、Agent SkillsをApache 2.0ライセンスで公開しています。スキルが準拠する<a href="https://agentskills.io">agentskills.io</a>の仕様はオープンスタンダードであり、Elastic独自のフォーマットではありません。スキルが閉鎖的な環境ではなく、コミュニティ全体の取り組みとなることを望んでいます。</p><h2>より大きな全体像の一部</h2><p>Agent Skillsは、Elasticsearchを最もエージェントにとって使いやすいデータプラットフォームにするための広範な取り組みの一環です。Elasticsearch Platform上で動作するエージェントの場合、<a href="https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga">Agent Builder</a>は、データのアクセス制御と権限を継承し、検索と分析のための組み込みツールとカスタムツールを提供し、ユーザーがダッシュボード、アラート、調査と共にコンテキスト内でエージェントと対話できるようにすることで、さらに進化します。最後に、Agent Builderでのスキルのサポートが間もなく開始されます。これにより、開発者はElastic Agent Skillsやその他のソースのスキルを柔軟に活用して、Elasticsearchプラットフォームで安全でコンテキストが強化されたチャットと自動化を実現できます。</p><p>他の領域で動作するエージェントのために、以下のようにオープンエコシステムに投資しています。</p><ul><li><p><strong>モデルコンテキストプロトコル（MCP）サーバーの拡張：</strong>Agent Builderの<a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">MCPエンドポイント</a>を、現在の検索、ES|QL、インデックス操作以外のより多くのツールで拡張します。</p></li><li><p><strong>認証機能の改善：</strong>エージェントが安全に接続しやすくすることで、APIキーの手動コピー＆ペーストを排除することを目指します。</p></li><li><p><strong>LLMで読み取り可能なドキュメント：</strong>エージェントがElastic APIを独自に発見して理解できるように、 <code>llms.txt</code>ファイルと<code>AGENTS.md</code>ファイルを公開します。</p></li><li><p><strong>エージェントワークフロー用のコマンドラインインターフェース（CLI）：</strong>接続管理や一般的な操作をエージェントにとって使いやすいものにするコマンドラインツール。</p></li></ul><p>スキルは本日からご利用いただけるレイヤーです。残りはこれから提供されます。</p><h2>使用を開始</h2><p><strong>始める前に：</strong>AIコーディングエージェントは、実際の認証情報、実際のシェルアクセス、そして多くの場合、実行しているユーザーの完全な権限を使用して動作します。エージェントがセキュリティワークフローに向けられる場合、自動化されたシステムに検出ロジック、対応アクション、機密テレメトリへのアクセスを委ねることになり、リスクはより高くなります。すべての組織のリスクプロファイルは異なります。AIを活用したセキュリティワークフローを有効にする前に、<strong>エージェントがアクセスできるデータ、実行できるアクション、予期しない動作が発生した場合に何が起こるかを評価してください</strong>。</p><p>Elastic Agent Skillsをエージェントランタイムにインストールする：</p><p><code>npx skills add elastic/agent-skills</code></p><p>これにより、インストールされているエージェントランタイムが自動的に検出され、スキルが適切な構成ディレクトリに配置されます。そこからエージェントが自動的にそれらを拾います。</p><p>また、<a href="https://github.com/elastic/agent-skills">スキルカタログ</a>を直接ブラウズし、スキルフォルダをエージェントの設定ディレクトリにコピーして、個別に手動でスキルをインストールすることもできます。</p><p>まだElasticsearchクラスターをお持ちではありませんか？<a href="https://cloud.elastic.co/registration">Elastic Cloudの無料トライアル</a>を始めましょう。1分ほどで完全に構成された環境を構築できます。</p><p><strong>プロジェクトを探索：</strong></p><ul><li><p><a href="https://github.com/elastic/agent-skills">Agent Skillsリポジトリ</a></p></li><li><p><a href="https://agentskills.io">agentskills.io 仕様</a></p></li><li><p><a href="https://www.elastic.co/docs">Elasticsearchドキュメント</a></p></li><li><p><a href="https://cloud.elastic.co/registration">Elastic Cloudの無料トライアル</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-skills-elastic</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-skills-elastic</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AIツール ]]></category>
    <dc:creator><![CDATA[Graham Hudgins,Matt Ryan]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbd233e8cf5c66c88/6a17074dc1e8a59502f8822a/09e64953819083168a9ecef0888c7f8bde1a43bd-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Common Expression Language（CEL）：CEL入力がElastic Agent統合におけるデータ収集を改善する方法]]></title>
    <description><![CDATA[Common Expression Languageと他のプログラミング言語との違い、FilebeatのCEL入力用の拡張方法、Elastic Agent統合でデータ収集ロジックを表現する上での柔軟性についてご紹介します。]]></description>
    <content:encoded><![CDATA[<p>Elastic Agent<a href="https://www.elastic.co/integrations">統合</a>により、ユーザーは幅広いソースからデータをElasticsearchに取り込むことができます。コレクションロジック、取り込みパイプライン、ダッシュボード、その他のアーティファクトをパッケージにまとめ、Kibana Webインターフェースからインストールおよび管理できます。</p><p>統合では、データ収集を行うために<a href="https://www.elastic.co/docs/reference/beats/filebeat/configuration-filebeat-options">Filebeat入力</a>を設定します。HTTP APIからデータを収集するために、私たちはしばしば<a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-httpjson">HTTP JSON入力</a>を使用してきました。しかし、基本的なリスティングAPIでさえ、細部において大きく異なることがあり、HTTP JSON入力のYAMLで構成された変換のモデルでは必要なコレクションロジックを表現するのが難しく、場合によっては不可能になることがあります。</p><p><a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-cel">Common Expression Language（CEL）入力</a>は、HTTP APIとのより柔軟な相互作用を可能にするために導入されました。<a href="https://cel.dev/">CEL</a>は、条件やデータ変換を高速、安全、かつ拡張性のある方法で表現するアプリケーションに組み込めるよう設計された言語です。CEL入力を使用すると、統合ビルダーは設定を読み取り、自身の状態を追跡し、リクエストを作成し、応答を処理し、最終的に取り込む準備が整ったイベントを返すことができる1つの式を記述できます。</p><p>この記事では、CELが他のプログラミング言語との違い、CEL入力用の拡張方法、そしてデータ収集ロジックを表現する上で提供するその柔軟性とパワーについて見ていきます。</p><h2>CELと入力での動作の仕組み</h2><p>CELは式言語で、ステートメントはありません。CELを記述する場合、ステートメントを記述して何を実行するかを指示するのではなく、式を記述してどのような値を生成するかを指示します。すべてのCEL式は値を生成し、小さな式を組み合わせて大きな式にすることで、より複雑なルールに従った結果を生成することができます。後ほど、他の言語のステートメントで記述できる内容に対して式を使用する方法について説明します。</p><p>CELは意図的に非チューリング完全言語であり、無限ループは許可されません。後ほど、マクロを使用してリストやマップを処理する方法を見ていきますが、無限ループを避けることで、この言語は個々の式に対して予測可能で制限された実行時間を保証します。</p><p>CEL入力は、CELプログラム（式）といくつかの初期状態で設定されます。状態はプログラムの入力として提供され、プログラムは出力状態を生成するために評価されます。出力状態にイベントのリストが含まれている場合、それらは削除されて公開されます。残りの出力状態は、次の評価の入力として使用されます。出力状態に1つ以上のイベントとフラグ<code>want_more: true</code>が含まれている場合、次の評価はすぐに実行されます。それ以外の場合、設定された間隔の残りの時間スリープしてから続行します。以下は入力の制御フローの簡略的な図です。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ec4ea57bfc2a2ff/6a17059f2b835f7d58f4b115/42671541f97e2dba808fd53969fe12f517917f9a-1600x529.png" alt="Common Expression Language（CEL）入力制御フロー" /><p>各評価の出力は、入力が実行されている限り、次の評価への入力として順に渡されます。キー「<code>cursor</code>」下の出力データはディスクに永続化され、入力の再起動後に再ロードされますが、その他の状態は再起動をまたいで保存されません。</p><p>CEL言語自体は機能が制限されており、副作用を回避しますが、拡張可能です。<a href="https://github.com/google/cel-go">cel-go</a>の実装では、オプションの構文や型などの機能が追加されています。<a href="https://github.com/elastic/mito">Mito</a>ライブラリはcel-goを基盤とし、HTTPリクエストの機能を含むより多くの機能を追加しています。CEL入力はMito版のCELを使用しています。</p><h2>Mitoの操作</h2><p>CEL入力を使用して統合を構築またはデバッグする際、最も重要なことは、与えられた入力状態に対してCELプログラムがどのような出力状態を生成するかを理解することです。開発中は、完全なElasticスタックに囲まれた入力でCELプログラムを実行するのは面倒な場合があります。より高速なフィードバックループを実現する1つの方法は、Mitoのコマンドラインツールを使用することです。このツールを使用すると、CELプログラムを直接実行し、特定の入力に対して生成される出力を確認できます。</p><p>MitoはGoで記述されており、以下のようにインストールできます。</p>go install github.com/elastic/mito/cmd/mito@latest<p>MitoでCELプログラムを実行する場合、通常は2つのファイルを指定します。初期入力状態を含むJSONファイルと、CELプログラムのソースコードを含む別のファイルです。</p>mito -data state.json src.cel<p>コピー＆ペーストを容易にするため、この記事の例は、シェルが<code>&lt;(echo '...content...')</code>で各ファイルの内容をラップして、その場で一時ファイルを作成する単一のコマンドとして記述されています。独自の開発では、実際のファイルを操作する方が簡単になります。</p><h2>GitHubからイシューデータを取得</h2><p>以下の例には、<a href="https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues">GitHub API</a>からイシューに関するデータを取得する完全なCELプログラムが含まれています。その初期入力状態には、APIエンドポイントのURLと、ページネーションをどのように処理すべきかについての情報が含まれています。CELプログラムは、入力状態のデータを使用してリクエストを生成します。対応をデコードし、そこからイベントを生成し、出力状態の一部として返します。</p>mito -data &lt;(echo '
  {
    "url": "https://api.github.com/repos/elastic/integrations/issues",
    "per_page": 3,
    "max_pages": 3
  }
') &lt;(echo '
  int(state.?cursor.page.orValue(1)).as(page,
    (
      state.url + "?" + {
        "state": ["all"],
        "sort": ["created"],
        "direction": ["asc"],
        "per_page": [string(state.per_page)],
        "page": [string(page)],
      }.format_query()
    ).as(full_url,
      request("GET", full_url).with({
        "Header": {
          "Accept": ["application/vnd.github+json"],
          "X-GitHub-Api-Version": ["2022-11-28"],
        }
      }).do_request().as(resp,
        resp.Body.decode_json().as(data,
          state.with({
            "events": data.map(i, {
              "html_url": i.html_url,
              "title": i.title,
              "created_at": i.created_at,
            }),
            "cursor": { "page": page + 1 },
            "want_more": size(data) == state.per_page &amp;&amp; page &lt; state.max_pages,
          })
        )
      )
    )
  )
')<p>その最初の評価は次の出力を生成します。</p>{
  "cursor": {
    "page": 2
  },
  "events": [
    {
      "created_at": "2018-09-14T09:47:35Z",
      "html_url": "https://github.com/elastic/integrations/issues/3250",
      "title": "Increase support of log formats in haproxy filebeat module"
    },
    {
      "created_at": "2019-02-06T12:37:37Z",
      "html_url": "https://github.com/elastic/integrations/issues/487",
      "title": "ETCD Metricbeat module needs polishing and grooming"
    },
    {
      "created_at": "2019-08-13T11:33:11Z",
      "html_url": "https://github.com/elastic/integrations/pull/1",
      "title": "Initial structure"
    }
  ],
  "max_pages": 3,
  "per_page": 3,
  "url": "https://api.github.com/repos/elastic/integrations/issues",
  "want_more": true
}<p>イベントは削除され、CEL入力で実行されると、インジェストのために公開されます。残りの出力は、次のCELプログラム評価に入力状態として提供されます。</p><p></p><p>そのCELプログラムの仕組みを理解するために、いくつかの小さなCELの例で、CEL入力の仕組みについて詳しく説明します。</p><h2>CELの基本</h2><p>CEL言語にはステートメントはなく、式のみが存在します。成功したすべてのCEL式は最終値まで評価されます。以下は、記述できる最も小さなCEL式の1つと、その出力です。</p>mito &lt;(echo '
  "hello" + " " + "world"
')"hello world"<p>シンプルな表現は通常、直感的です。数学演算は同じタイプの値でのみサポートされます（例：<code>int</code> と <code>int</code> など）。そのため、必要に応じてタイプを変換します（ここでは <code>int</code> から <code>double</code> へ）。</p>mito &lt;(echo '
  double((1 + 2) * (3 + 4)) / 2.0
')10.5<p>CEL言語には変数はありませんが、Mitoの<a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-As__Macro_-Collections"><code>as</code></a>マクロを使用して、式に名前を付け、より大きな式で使用することができます。この例では、式<code>(1 + 1)</code>は値<code>2</code>に評価され、<code>.as(n, ...)</code>がその値に式<code>"one plus one is "+string(n)</code>で使用するための名前<code>n</code>を付けます。</p>mito &lt;(echo '
  (1 + 1).as(n, "one plus one is "+string(n))
')"one plus one is 2"<p>また、<a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-With-Collections"><code>with</code></a>を使用して示したように、マップに情報を蓄積し、それを後で式の中で使用することも可能です。</p>mito &lt;(echo '
  { "key": "value" }.with({ "key2": "value2" }).as(data,
    {
      "data": data,
      "size": size(data),
    }
  )
'){
  "data": {
    "key": "value",
    "key2": "value2"
  },
  "size": 2
}<p>その例をもう一度見てみましょう。ネストされた部分<code>({ "data": data, "size": size(data), })</code>が最終値の形状を示すことに注意してください。これは<code>"data"</code>と<code>"size"</code>をキーとするマップです。これらのキーの値は<code>data</code>に依存し、式の外側部分によって定義されます。CEL式を内側から外側まで読み取ると、何が返されるかをすぐに把握できるようになります。</p><p>CELには<code>if</code>のような制御フロー文はありませんが、条件分岐は三項演算子で実行可能です。</p>mito &lt;(echo '
  1 + 1 &lt; 12 ? "few" : "many"
')"few"<p>CELはチューリング完全言語ではないため、無制限のループと再帰はサポートされていません。これにより、実行時間が予測可能になり、入力データのサイズと式の複雑さに比例するようになります。</p><p>個別のCEL式では無限ループはできませんが、<a href="https://github.com/google/cel-spec/blob/master/doc/langdef.md#macros"><code>map</code></a>のようなマクロを使ってリストやマップを処理できます。</p>mito &lt;(echo '
  [1, 2, 3].map(x, x * 2)
')[2, 4, 6]<p>このセクションでは、次の内容を説明しました。</p><ul><li><p>文字列、数値、リスト、マップ。</p></li><li><p>文字列の連結。</p></li><li><p>数学演算。</p></li><li><p>タイプキャスティング。</p></li><li><p>条件文。</p></li><li><p>部分式の命名。</p></li><li><p>コレクションの処理。</p></li></ul><p>次に、HTTPリクエストを行う方法を見ていきます。</p><h2>リクエスト</h2><p>MitoはCELを拡張して<a href="https://pkg.go.dev/github.com/elastic/mito/lib#HTTP">HTTPリクエスト</a>を行う機能を提供します。</p>mito &lt;(echo '
  get("https://example.com").as(resp, string(resp.Body))
')"&lt;!doctype html&gt;&lt;html lang=\"en\"&gt;&lt;head&gt;&lt;title&gt;Example Domain&lt;/title&gt;..."<p>リクエストは実行前に明示的に構築することができます。これにより、さまざまなHTTPメソッドを使用したり、ヘッダーや本文を追加したりできるようになります。</p><p>この例では、 <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-Format_Query-HTTP"><code>format_query</code></a>を使用してURLを構築し、リクエストにヘッダーを追加し、 <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-Decode_JSON-JSON"><code>decode_json</code></a>を使用してレスポンス本文を解析します。<code>-log_requests</code>オプションを指定すると、Mitoは各リクエストと対応に関する詳細情報をJSON形式でログに記録します。</p>mito -log_requests &lt;(echo '
  request("GET",
    "https://postman-echo.com/get?" + {
        "q": ["query value"]
     }.format_query()
  ).with({
    "Header": { "Accept": ["application/json"] }
  }).do_request().as(resp, {
    "status": resp.StatusCode,
    "data": resp.Body.decode_json(),
  })
'){"time":"...","level":"INFO","msg":"HTTP request",...}
{"time":"...","level":"INFO","msg":"HTTP response",...}
{
  "data": {
    "args": {
      "q": "query value"
    },
    "headers": {
      "accept": "application/json",
      "accept-encoding": "gzip, br",
      "host": "postman-echo.com",
      "user-agent": "Go-http-client/2.0",
      "x-forwarded-proto": "https"
    },
    "url": "https://postman-echo.com/get?q=query+value"
  },
  "status": 200
}<h2>状態と評価の管理</h2><p>ここまで、リクエストの作成方法と、目的の出力状態を生成するために必要なCELの基本について説明しました。次は、出力状態に何を入れるべきか、そしてそれによって後続の処理をどのように指示できるかについて詳しく見ていきましょう。</p><p>統合のCELプログラムでは、その出力状態が次の評価の入力として使用するのに適していることを確認する必要があります。構成では初期状態を設定し、適切な変更を加えて出力でそれを繰り返す必要があります。簡単な方法は <code>state.with({ ... })</code> を使って、状態マップをオーバーライドして繰り返し表示することです。小規模プログラムの一般的なパターンは、<code>state.with()</code>でプログラム全体をラップすることです。これにより、出力データを生成する各分岐（例えば、成功、エラー）で状態の伝播を繰り返す必要がなくなります。</p><p>初期入力状態にハードコードされているのではなく、評価によって初期化される状態値がある場合、プログラムは初期値を設定する前に既存の値を確認する必要があります。これには<a href="https://pkg.go.dev/github.com/google/cel-go/cel#OptionalTypes">オプションの構文と型</a>のサポートが役立ちます。マップキーのフィールド名の前に疑問符を使用すると、アクセスはオプションになります。値に解決される場合とされない場合がありますが、さらにオプションのアクセスが可能であり、値が存在しない場合にデフォルトを簡単に提供できます。
</p>mito -data &lt;(echo '{}') &lt;(echo '
  int(state.?counter.orValue(0)).as(counter,
    state.with({
      "counter": counter + 1,
      "want_more": counter + 1 &lt; 3,
    })
  )
'){ "counter": 1, "want_more": true }
{ "counter": 2, "want_more": true }
{ "counter": 3, "want_more": false }<p>その例では、状態から読み取られたカウンター値は<code>int</code>にキャストされます。これは、JSONとJavaScriptの<code>Number</code>型によって確立された規則に従って、状態内のすべての数値が浮動小数点数としてシリアライズされるためです。また、<code>"want_more": true</code>はここでMitoによって尊重されますが、CEL入力で実行される場合、出力にもイベントが含まれている場合にのみ評価が繰り返されます。</p><p>CEL入力によって実行されるCELプログラムでは、出力マップに<code>"events"</code>キーを返すことが要件となります。その値はイベントマップのリスト、空のリスト、または単一のイベントマップである場合があります。単一イベントケースは通常、エラーに使用されます。イベントは入力によって公開されますが、その値もログに記録されます。<code>error.message</code>値が設定されている場合、その値は統合のFleetのヘルスステータスを更新するために使用されます。プログラムが単一の非エラーイベントを生成する場合は、それをリストにラップするのが最適です。</p><p>先ほどのGitHubイシュープログラムの出力をもう一度見てみましょう。</p>{
  "url": "https://api.github.com/repos/elastic/integrations/issues",
  "per_page": 3,
  "max_pages": 3,
  "cursor": {
    "page": 2
  },
  "events": [
    { ... },
    { ... },
    { ... }
  ],
  "want_more": true
}<p>プログラムは次のようにして状態を効果的に管理しました。</p><ul><li><p><code>url</code>、<code>per_page</code>、<code>max_pages</code>で初期状態値を繰り返します。</p></li><li><p><code>cursor.page</code>の再起動時に永続化されるべき状態を追加します。</p></li><li><p><code>events</code>リストで公開する準備ができたイベントを返します。</p></li><li><p><code>want_more: true</code>で即時の再評価をリクエストしています。</p></li></ul><p>オプションのアクセスと状態管理、CELの基本とHTTPリクエストを理解できたので、GitHubのイシュープログラム全体が読み取れるようになります。Mitoで実行し、いくつか変更を加えて実験してみてください。</p><h2>レビューとリソース</h2><p>この記事では、CEL言語とは何か、そしてそれがMitoライブラリでどのように拡張され、CEL入力で使用されるかを調べました。GitHub APIからイシュー情報を取得するサンプルプログラムでCELの柔軟性を確認し、初期状態での設定へのアクセス、HTTP APIとのやりとり、取り込むべきイベントのリターン、後のプログラム実行のための状態管理など、そのプログラムを理解するために必要なすべての詳細を説明しました。</p><p>CEL入力を使用してさらに学び、統合を構築ために役立つリソースは多数あります。</p><ul><li><p><a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-cel">CEL インプット - Filebeat ドキュメント</a></p></li><li><p><a href="https://pkg.go.dev/github.com/elastic/mito">Mito ドキュメント</a></p></li><li><p><a href="https://cel.dev/">Common Expression Language - cel.devウェブサイト</a></p></li><li><p><a href="https://www.elastic.co/docs/extend/integrations">統合を作成する - Elastic ドキュメント</a></p></li></ul><p>CEL入力を使用した統合を構築するための最も貴重なリソースは、GitHubで入手できる既存のElastic 統合のCELコードです。</p><p><a href="https://github.com/search?q=repo%3Aelastic%2Fintegrations+path%3A**%2Fcel.yml.hbs&amp;type=code"><code>cel.yml.hbs</code></a><a href="https://github.com/search?q=repo%3Aelastic%2Fintegrations+path%3A**%2Fcel.yml.hbs&amp;type=code"> Elastic統合リポジトリ内のファイル - GitHub</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/common-expression-language-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/common-expression-language-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Chris Berkhout]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt330db607ffb818f9/6a1705a08b73cb8502189f4c/985c50bfabee3348494eb4307f0b3375a97a0644-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 27 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[チャットボックスを超えたエージェントビルダー：Augmented Infrastructureの導入]]></title>
    <description><![CDATA[拡張運用、拡張開発、拡張合成を可能にするAIエージェントである、Elastic Agent Builder with Augmented Infrastructureについてご紹介します。]]></description>
    <content:encoded><![CDATA[<p><strong>これは机上の空論ではなく、私たちはすでに行動しています。</strong></p><p>私たちは皆、AIエージェントの台頭を見てきました。テキストを要約したり、コードスニペットを書いたり、ドキュメントに基づいて質問に答えたりするのが得意です。しかし、DevOpsやサイト信頼性エンジニアリング（SRE）に携わる者にとっては、もどかしい制限がありました。ほとんどのエージェントは、コールセンターのパラダイムに囚われています。つまり、読んだり、考えたり、チャットしたりすることはできても、手を伸ばして本来管理すべきインフラに触れることはできないのです。</p><p>最新のハッカソンプロジェクトでは、その制限を打ち破ることを目指しました。</p><p>私たちは、インフラのコパイロットである<strong>Augmented Infrastructure</strong>を開発しました。これは、アドバイスを提供するだけでなく、稼働中の環境の構築、デプロイ、監視、および修正も行います。</p><h2><strong>問題点：コピー、再フォーマット、貼り付け</strong></h2><p>標準的なエージェントは、密閉空間で活動しています。アプリがダウンして会社に500万ドルの損害をもたらした場合、標準的なエージェントは、修正方法についての手順書を読み上げることができます。しかし、その作業を行うのが<em>あなた</em>であることは変わりません。コードをコピーして、自分の環境に合わせてフォーマットし直し、ターミナルに貼り付ける作業が残っています。</p><p>私たちは、Kubernetesについて<em>話すこと</em>と、Kubernetesを<em>設定すること</em>の違いを理解するエージェントが欲しいと考えていました。</p><h2><strong>エンジン：Elastic Agent Builderとは？</strong></h2><p>これを構築するにあたって、私たちはゼロから始めたわけではありません。<a href="https://www.elastic.co/jp/elasticsearch/agent-builder"><strong>Elastic Agent Builder</strong></a>を基盤として構築しました。Elastic Agent Builderをご存知ない方のために説明すると、これはエージェントを迅速に開発するために設計されたフレームワークであり、大規模言語モデル（LLM）（今回のデモではGoogle Geminiを使用）とElasticsearchに保存されているプライベートデータとの間の橋渡し役を果たします。</p><p>Agent Builderは、ドキュメントやログなどの内部データを基盤として、会話型AIに活用できます。しかし、最も強力な機能は<strong>ツール</strong>を割り当てる機能です。これらのツールにより、LLMはチャットインターフェースに留まらず、特定のタスクを実行できます。この機能を可能な限り活用すれば、Agent Builderを自動化の強力なツールに変えることができることに気付きました。</p><h2><strong>成功のために：初期バージョンの構築</strong></h2><p>プロジェクトを開始した当初から、エージェントが外の世界を変えられるようにしたいと考えていました。私たちはあるアイデアを思いつきました。エージェントがホスト上で考えられる任意のコマンドを実行する「ランナー」ソフトウェアを構築したらどうなるでしょうか？そして、ランナーであるElastic Agent Builderとユーザーが三者通話をしていたらどうなるでしょうか？</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltec9d20da8c41a898/6a170704dc55debd4ce00d43/8dc8317c1301b8eb7b89438529e8d8d17411c95a-1024x559.png" alt="Agent Builder with Augmented Infrastructure architecture" /><p>まず、Augmented Infrastructure RunnersというPythonプロジェクトを構築しました。これは本質的に、Elastic Agent Builderの会話APIを毎秒クエリし、当社が作成した特別な構文があるかどうかを確認するwhile(true)ループでした。</p>{
"tool_name": "my_tool",
       "tool_arguments": "\{stringified json arguments\}"
}<p>次に、新しいツール呼び出し構文を認識させるために、プロンプトを更新しました。ビルは<a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>のメンテナーです。FastMCPは、PythonでModel Context Protocol（MCP）サーバーを構築する目的において最も人気のあるフレームワークです。彼は、この新しいランナーソフトウェアとFastMCPクライアントを使用して、MCPサーバーをマウントし、ランナーがそのツールを利用できるようにするための作業に着手しました。エージェントがこれを確認するとツール呼び出しを実行し、結果をユーザーが送信したかのように会話にPOSTします。これがきっかけでLLMは結果に反応し、私たちの取り組みが加速したのです。</p><p>これは素晴らしい考えでしたが、主に2つの問題がありました。</p><ol><li><p>エージェントは、このJSONすべてをユーザーとの会話に直接吐き出します。</p></li><li><p>メッセージが会話APIを通じて表示される最も早い時点は、会話ラウンドが完了したとき（つまり、LLMが応答したとき）でした。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0596217e962f8464/6a17070647d49c3fef2d890c/7b3755aeae17722ff1bb9677712293e9195f96a0-1058x1034.png" alt="Issue when building agent with augment infrastructure" /><p>そこで、これをバックグラウンドに移動させる方法を模索することにしました。</p><p>次に、エージェントにcall_external_toolというツールを与え、tool_nameと文字列化されたJSONツール引数の2つの引数を持たせるように変更しました。この外部ツール呼び出しは何も返しませんが、重要なのは会話APIへのGETリクエストで確認できることです。その後、ランナーにElasticsearchに直接ドキュメントを書き込む許可を与えました。Elastic Agent Builderのエージェントは必要に応じてそれを取得できました。エージェントは常にユーザーのメッセージに対応して動作しているため、結果を検索し、処理を続行するようにエージェントをユーザーのメッセージで起動させる必要があります。そこで、会話を再開するために、エージェントにチャットに短いメッセージを挿入させました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta22be3c67ad2ff1f/6a170708cdacbf0ae87d295b/61ff59a57c68ed5fad492d19c0580644113a507d-1600x1321.png" alt="Agent Builder with Augmented Infrastructure demostration" /><p>これで、外部ツールの呼び出しが行われました。しかし、前述の2つ目の問題のため、最後のキックスタート部分を削除せざるを得ませんでした。それをしなければ、外部ツールを呼び出すたびに、結果を取得するためにもう一度会話をすべてやり直す必要があったためです。</p><h2><strong>優れたものにするために：ワークフローの導入</strong></h2><p>Elasticsearch Query Language（ES|QL）とインデックス検索ツールの呼び出しに加え、Agent BuilderエージェントはElasticのワークフローベースのツールを呼び出すことができます。Elasticのワークフローは、任意のアクションのシーケンスとロジックを実行するための柔軟で管理しやすい方法を提供します。私たちの目的では、ワークフローに必要なのは、Elasticsearchに外部ツールのリクエストを格納することと、結果をポーリングするためのIDを返すことだけです。これにより、以下の簡単なワークフロー定義が得られます。</p>name: ai-tool-call
enabled: true
triggers:
  - type: manual
inputs:
  - name: runner_id
    type: string
  - name: tool_calls
    type: string

steps:
  - name: store_request
    type: elasticsearch.create
    with:
      index: distributed-tool-requests
      id: "{{inputs.runner_id}}_{{ execution.id }}"
      document:
        request_id: "{{ execution.id }}"
        runner_id: "{{inputs.runner_id}}"
        tool_call: "{{inputs.tool_calls}}"
        status: "unhandled"

  - name: output_result
    type: console
    with:
      message: "ツールが呼び出されました。実行ID: {{ execution.id }}. このIDを使用して結果をポーリングしてください。"<p>それにより、会話に書き込まれるツール呼び出しリクエストに依存するのではなく、ランナーはElasticsearchのdistributed-tool-requestsインデックスをポーリングして新しい外部ツールリクエストを検索し、その結果を指定されたexecution.idを使用して別のElasticsearchインデックスにレポートすることができます。</p><p>これにより、上記の2つの主な問題が解消されます。</p><ol><li><p>会話履歴に外部ツール呼び出しのペイロードが散乱することはなくなりました。</p></li><li><p>ランナーは会話履歴ではなくElasticsearchインデックスをポーリングしているため、外部ツールのリクエストが表示されるようになるまで、会話のラウンドが完了するのを待つ必要がなく、ブロックされることはありません。</p></li></ol><p>2つ目の点には、外部ツール呼び出しの処理が（会話ラウンドが完了した後ではなく）エージェントの思考フェーズ内で開始されるという大きな利点があります。これにより、システムプロンプトでLLMに外部ツールの結果が利用可能になるまでポーリングするように指示できレガシ、キックスタートメッセージが不要になります。全体として、これにより会話がより自然に感じられるという良い効果があります。LLMは（ツールリクエストごとに1回の会話ラウンドを必要とするのではなく）1回の会話ラウンドで複数の外部ツールリクエストを処理できるため、より複雑なユーザーリクエストを一度に達成できます。</p><h2><strong>すべてを集約</strong></h2><p>LLMとサーバーラックの間のギャップを埋めるために、Agent Builderのツール機能を使用してある特定のアーキテクチャを開発しました。</p><ol><li><p><strong>Augmented Infrastructureのランナー：</strong>ターゲット環境（サーバー、Kubernetesクラスター、クラウドアカウント）内に軽量ランナーをデプロイしました。これらのランナーは、各ランナーだけが利用できる安全なエンドポイントとシークレットを使用して、Elasticに直接接続されています。</p></li><li><p><strong>ES|QL検索</strong>：コパイロットはElasticの<strong>ES|QL</strong>を使用してハイブリッド検索を行います。単に知識を検索するだけではなく、<em>機能</em>を検索します。接続されたランナーに問い合わせて利用可能なツールを確認します（例：list_ec2_instances, install_helm_chart）。</p></li><li><p><strong>ワークフローの実行：</strong>エージェントが行動方針を決定すると、構造化されたワークフローを作成します。</p></li><li><p><strong>フィードバックループ：</strong>ランナーはローカルでコマンドを実行し、その結果をElasticsearchにレポートします。コパイロットはインデックスの結果を読み取り、次のステップを決定します。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9726199693a10c5c/6a17070ae8fbced43a39fb9a/76be256da722c1965971fc506502768bd890f0c4-1290x1076.png" alt="Architecture using Agent Builder’s tool capabilities with Augmented Infrastructure" /><h2><strong>デモ：停止からオブザーバビリティへ</strong></h2><p>動画では、このアーキテクチャの影響力を示す2つの異なるシナリオを紹介しました。</p><h3><strong>シナリオ1：DevOpsの救出</strong></h3><p>私たちは、Kubernetesクラスター内の死角によって引き起こされた500万ドルの障害に関連してパニックに陥ったユーザーから取り組み始めました。</p><ul><li><p><strong>リクエスト：</strong>「このようなことが二度と起こらないようにするにはどうすればよいでしょうか？」</p></li><li><p><strong>アクション：</strong>エージェントは単にチュートリアルを提供するだけではありませんでした。クラスターを識別し、必要な名前空間を作成し、Kubernetesシークレットを生成し、OpenTelemetry Operatorをインストールして、ライブAPMダッシュボードへのリンクを即座に提供しました。</p></li><li><p><strong>結果：</strong>ユーザーがYAMLコードを一行も記述することなく、Kubernetesの網羅的なオブザーバビリティとアプリケーションの洞察を実現しました。</p></li></ul><h3><strong>シナリオ2：セキュリティの引き継ぎ</strong></h3><p>インフラセキュリティの基本的なルールは、見えないものは守れないということです。DevOpsの救出を実行している際、エージェントは環境のセキュリティを向上させる機会を見出します。</p><p>前回のElastic Observability関連の調査から始まったアラートを受けて、セキュリティ担当者が自社のインフラストラクチャーと直接チャットする方法を示します。1つ目はクラウド環境内の資産とリソースを列挙すること、2つ目は環境のセキュリティを確保するために必要なツールをデプロイすることです。</p><ul><li><p><strong>発見：</strong>コパイロットはセキュリティ担当者のためにAWSリソースを列挙し、重要なギャップを特定しました。すなわち、Amazon Elastic Compute Cloud（EC2）インスタンスと、パブリックエンドポイントにエンドポイント保護がないAmazon Elastic Kubernetes Service（EKS）クラスターです。</p></li><li><p><strong>対策：</strong>簡単な承認手続きで、コパイロットは脆弱な資産に対して<strong>Elastic Security</strong>の<strong>拡張検出および対応（XDR）とクラウド検出と対応（CDR）</strong>を展開し、環境をリアルタイムで保護しました。</p></li><li><p><strong>結果：</strong>デプロイされたAWS資産とリソースを完全なランタイムセキュリティで保護します。</p></li></ul><h2><strong>未来：あらゆるものが拡張される</strong></h2><p>このプロジェクトは、Elastic Agent Builderが分散運用の中心的な頭脳になり得ることを証明しています。インフラだけに留まらず、私たちのランナー技術は以下の影響力を発揮します。</p><ul><li><p><strong>拡張合成：</strong>グローバルランナー全体にわたるTLSエラーの診断。</p></li><li><p><strong>拡張開発：</strong>プルリクエストの作成と、フロントエンドサービスへのCAPTCHAの実装。</p></li><li><p><strong>拡張オペレーション：</strong>障害時にDNSリゾルバを自動的に再構成。</p></li></ul><h2><strong>はじめましょう</strong></h2><p>私たちは、AIの未来は単なるチャットサポートだけではなく、<strong>拡張されたインフラストラクチャー</strong>にあると信じています。これは、ユーザーと共にデプロイ、修正、観察、そして保護できるパートナーを持つことです。</p><p>コードをご覧になり、<a href="https://github.com/strawgate/augmented-infrastructure">GitHub</a>の分散ランナーや<a href="https://cloud.elastic.co/">Elastic Cloud Serverless</a>のElastic Agent Builderをぜひ直接お試しください。</p><ul><li><p>Elastic Cloud上にサーバーレスプロジェクトを作成します。</p></li><li><p>コードをランナーにデプロイします。</p></li><li><p>ランナーをセットアップします。</p></li><li><p>mcp.jsonを設定します。</p></li><li><p>ランナーを起動すると、エージェントとそのツールが自動的に作成されます。</p></li><li><p>分散ランナーで推論、計画、およびアクションを実行できるエージェントとチャットしましょう。</p></li></ul><p><strong>チーム： </strong><em>アレックス、ビル、ギル、グラハム、ノーリー</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-augmented-infrastructure</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-augmented-infrastructure</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AIツール ]]></category>
    <dc:creator><![CDATA[Alexander Wert,Bill Easton,Gil Raphaelli,Graham Hudgins,Norrie Taylor]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6de9245ad57ccc00/6a17070cdc55deaa39e00d48/e08daf78f328e826f39d06329f6a5487f75d178d-1272x700.png" length="0" type="image/png"/>
    <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Agent Builderが一般提供開始：コンテキスト駆動型エージェントを数分で出荷]]></title>
    <description><![CDATA[Agent Builderが一般提供となりました。コンテキスト駆動型AIエージェントを迅速に開発する方法を学びましょう。]]></description>
    <content:encoded><![CDATA[<p>Elastic Cloud Serverlessおよび近日公開予定の9.3リリースで、Agent Builderが一般公開されることを発表できることを嬉しく思います。Agent Builderは、コンテキストエンジニアリングプラットフォームとしてElasticsearchの機能を活用し、コンテキストに基づいたデータ重視のAIエージェントを迅速に開発します。</p><p>エージェントは、効率性の向上と顧客体験の向上をもたらす可能性で注目を集めています。しかし実際には、乱雑で構造化されていないエンタープライズデータを扱う場合など、エージェントに適切なコンテキストを提供することは困難です。開発者は、ツール、プロンプト、状態、推論ロジック、モデルを管理し、ビジネスソースから関連するコンテキストを取得して正確な結果とアクションを提供する必要があります。Elastic Agent Builderは、これらのコアコンポーネントを提供して、安全で信頼性の高い、コンテキスト駆動型のエージェントを開発します。</p><h2>Agent Builderのコア機能</h2><p>Agent Builderは、検索の関連性とRetrieval-Augmented GenerationへのElasticの長期投資を活用し、Elasticsearchをコンテキストに応じたデータ重視のAIエージェントの開発を簡素化する最高のベクトルデータベースにすることを目指しています。</p><p>Agent Builderを使用すると、次のことが可能になります。</p><ul><li><p>質問に答え、分析を実行し、Elasticsearch内のあらゆるデータに関する調査を推進できる組み込みの会話エージェントをすぐに使い始めることができます。</p></li><li><p>複雑な非構造化データから、設定ベースの開発エクスペリエンスを用いてカスタムエージェントへ迅速に移行します。</p></li><li><p>組み込みのES|QLまたはカスタムツールを通じてクラス最高のハイブリッド検索関連性を活用し、コンテキストの品質とエージェントの信頼性を向上させます。</p></li><li><p>複雑なワークフロー（プレビュー）を再利用可能なツールとして実行し、データを充実させ、レコードを更新し、メッセージを送信するなど、ルールベースの自動化を実現します。</p></li><li><p>ワークフローとMCPを使用してElasticsearch外のデータソースに接続し、エージェントのコンテキストを関連付けたり組み合わせたりします。</p></li><li><p>搭載のまたはカスタムツールをMCP経由で公開して、任意のエージェントまたはアプリケーションフレームワークと統合し、外部MCPに接続する機能（プレビュー）、A2Aのサポート、完全なAPIサポートを提供します。</p></li><li><p>LlamaIndexを使用した複雑な文書処理や、Arcade.devを使用した安全で構造化されたツールアクセスなどのサードパーティソリューションと統合して、Agent Builderの機能を拡張します。</p></li></ul><p>Agent Builderの機能をさらに拡張するために、新しいルールベースの自動化機能であるElastic ワークフローを導入します。現在はテクニカルプレビュー段階です。組織のタスクでは、エージェントはルールベースのアクションの確実性と信頼性を必要とする場合があり、これは特定のビジネスロジックを実装するために不可欠となることがよくあります。Elastic Workflowsは、内部システムと外部システムを管理してアクションを実行し、データやコンテキストを収集して変換するためのシンプルで宣言的な方法をエージェントに提供します。ワークフローは完全にコンポーザブルで、イベント主導型かつ柔軟性があり、MCPを介してエージェントにツールとして公開できます。</p><h2>わずか数分でデータからエージェントへ</h2><p>エージェントの開発には、別々のデータストアを統合し、手動のパイプラインを構築し、クエリを調整し、複雑なオーケストレーションを管理するために、数週間の事前作業を要する場合があります。Agent Builderは、データストア、ベクトルデータベース、RAGパイプライン、検索レイヤー、クエリトランスレータ、ツールオーケストレータの必要性を排除することで、エージェントの開発時間を短縮し、エージェントのロジックとアプリケーションの提供に集中できるようにします。</p><p>Agent BuilderはElasticsearchプラットフォームのプリミティブをネイティブに統合して、エージェントの開発を迅速にします。</p><ul><li><p>インデックス付けされたデータとすぐにチャットして推論できる組み込みの会話エージェントから始めましょう。</p></li><li><p>Kibana、API、またはMCPやA2Aを介したインタラクティブなアクセスにより、エージェントをアプリケーション、ダッシュボード、CI/CDシステムに統合します。</p></li><li><p>デフォルトのツールを使用してデータ構造を理解し、適切なインデックスを選択し、最適化されたハイブリッド、セマンティック、構造化クエリを生成し、自然言語プロンプトに基づいてES|QLを使用した設定可能な可視化を作成します。</p></li></ul><p>さらに詳しく知りたい場合は、完全な<a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">ハンズオンウォークスルー</a>をお試しください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8def92028138672/6a17e086af47b60cd8cdde96/b55b63eae40f72952967cc8f3ea4df4cd62d7d70-1080x608.gif" alt="Elastic Agent Builder ウォークスルー" /><h2>コンテキストエンジニアリングのための完全なデータプラットフォームであるElasticsearch上に構築</h2><p>AIエージェントにとって、コンテキストの品質は効果的な推論を提供し、ハルシネーションのリスクを軽減するために不可欠です。多くの企業のAIエージェントにとって、タスクを実行するために必要なビジネスデータは、最も重要なコンテキストです。拡張性に優れたデータ格納、ベクトルデータベース、そして関連性におけるリーダーとして、Elasticsearchはすでに多くの強力なコンテキストエンジニアリングプリミティブを提供しています。コンテキストエンジニアリングは、単なるRetrieval-Augmented Generationを超えて、データの取得、ランキング、フィルタリング、エージェントへの提示方法をカスタマイズ・スケールできるようにすることで、ノイズと曖昧さを減らすのに役立ちます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4c10c1d09e9f81e/6a17e087577262feb31bcb4b/419b9b6f13739e0a8983249d8ac31478e73dac89-1600x901.png" alt="Agent Builderの図" /><p>Elasticsearchは、レキシカル検索、ベクトル検索、構造化フィルタリングを組み合わせたコンテキストエンジンを提供し、モデルが関連性のある正確なコンテキスト上で動作することを確実にすることで、<a href="https://www.elastic.co/search-labs/blog/context-engineering-relevance-ai-agents-elasticsearch">LLMのパフォーマンスを大幅に向上</a>させます。この機能は、エージェント検索、組み込みツール、適切なインデックスを自動的に選択し、自然言語をコンテキストに最適化されたクエリに変換する検索ロジックによってサポートされています。</p><p>Agent Builderでは、関連性とランキングを制御して、エージェントが最も役立つコンテキストを最初に受け取るようにして、スコアリング、ランキング、フィルタリングロジックを微調整できます。Elasticsearchを使用すると、不透明な検索動作に頼るのではなく、重要なこと、重要な理由、優先順位付け方法を制御できます。これらはすべて、テキスト、ベクトル、メタデータ、ログなどすべてのデータを1つのプラットフォームに保存・拡張できるスケーラブルなデータプラットフォームであるElasticsearchによって支えられており、エージェントのコンテキスト管理が容易になります。</p><h2>複雑なワークフローを再利用可能なツールとして実行</h2><p>AIエージェントは複雑なタスクの推論を可能にしますが、多くの自動化は、特定のビジネスロジックを強制するルールベースのアクションの確実な実行に依存しています。Elastic Workflowsは、内部および外部のシステムをオーケストレーションし、アクションを実行し、コンテキストやデータを収集し、エージェントの一部として統合するための、シンプルで宣言的な方法を提供します。YAMLで定義されているワークフローは完全にコンポーザブルで、ジョブに応じて単純にしたり複雑にしたりできます。これにより、エージェントはElasticsearchプラットフォームやソリューション、そしてサードパーティのアプリケーションに対して効率的にアクションを起こすことができます。</p><p>ワークフローをAgent Builderと統合するには、3つの手順を実行します（前提条件：<a href="https://github.com/elastic/workflows">ここに</a>記載されている詳細を使用してワークフローを有効にします）</p><p>1. シンプルなYAMLベースのエディターを使用して、組み込みの自動入力とテスト機能付きで新しいワークフローを作成して保存します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt00585158429a3395/6a17e089e317916b122d5740/308888bf3d2fa013f9391a55be6a6fbd458b6dac-1600x998.png" alt="Agent Builderのワークフロー" /><p>2. Agent Builderでタイプ「ワークフロー」の新しいツールを作成し、エージェントがワークフローツールをいつ使用するかを判断できるように説明を入力します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt874b6a1ce3a2ac34/6a17e08be9ea87b1dea9c4d9/c04810d30d226112c3610bd58e208607b213fc3d-1600x945.png" alt="Agent Builderで新しいツールを作成" /><p>3. ワークフローツールをカスタムエージェントに追加します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94a31cb60ef11ce6/6a17e08daf47b61f0dcdde9a/724cd4ac93c46efb0d339fd140e5caf138f8150f-1600x948.png" alt="ワークフローツールをカスタムエージェントに追加してください。" /><p>4. 以上です！エージェントが会話内からワークフローを呼び出せるようになりました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5143f401a06e8ba2/6a17e08fdbb4ffcfc6fb55de/8dfdd726ab89e31c48b79372650ce33946713dca-1600x929.png" alt="AIエージェントがElastic Agent Builderで作成されました" /><h2>ニーズに合わせてエージェントを構築</h2><p>Agent Builderは単一の開発パラダイムに限定されず、データ、関連性、モデル、相互運用性、セキュリティ、エージェント設計を完全に制御し、エージェントに対してオープンで柔軟な開発アプローチを可能にするように設計されています。</p><p>カスタムエージェント定義を使用すると、エージェントがアクセスできるツールを正確に選択したり、カスタムシステムプロンプトを埋め込んだり、エージェントの指示を調整したり、セキュリティ境界を定義したりできます。エージェントはモデルに依存しないため、単一のプロバイダーに縛られることなく、ネイティブとより広範なエコシステムの両方で、好みのLLMを柔軟に構成できます。</p><p>拡張可能なツールを構築し、ドメイン固有のロジック（例：特定のインデックスフィルター、ES|QL結合、分析パイプライン）をカプセル化し、それらを本番環境での安全な使用に制約します。APIの完全サポートで、モデルコンテキストプロトコル（MCP）のネイティブサポートにより、他のエージェントフレームワークとの相互運用が可能になります。A2A統合とは、Elastic Agentを他のフレームワークやサービス、クライアントアプリに公開し、同じデータやコンテキストエンジニアリングロジックを統合間で再利用できることを意味します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt309a0b3dd4cc367b/6a17e090ec0f8932045a6550/5e903ba24ffb3f40231e901f63bd494c89cb7757-1600x1004.png" alt="Elastic Agent Builderを使用したAIエージェントの構成" /><p>Agent Builderは、柔軟でオープンな開発をサポートし、一般的なエージェントフレームワークやPlatformと簡単に統合できるように設計されています。これらの統合は効果的なエージェントを提供するために不可欠です。<strong>Arcade.devの共同創設者であるSam Partee氏は</strong>次のように述べています。</p><p><em>「今日のエージェントシステムが機能しないのは、AIをツールやデータに接続するのが難しいためです。Arcade.devのElastic Agent Builderは、エージェントがコンテキストを取得し、推論し、行動する方法を扱うための構造化されたセキュアな方法を開発者に提供します。」</em></p><p>Agent Builderは、複雑なデータを処理するためにElasticsearchの拡張性も活用します。<strong>LlamaIndexのCEOであるJerry Liu氏</strong>は次のように述べています。</p><p><em>「非構造化データソースから企業のコンテキストを解き放つことが、効果的なエージェントを構築する鍵となります。Elastic Agent BuilderとLlamaIndexの複雑なドキュメント処理を組み合わせることで、重要なコンテキストレイヤーが強化され、チームがデータを取得、処理、準備できるようになるため、エージェントはより正確に推論し、より良い結果を提供できるようになります。」</em></p><h2>構築できるもの</h2><p>Agent Builderはすでにさまざまなユースケースで使用されています。以下に、エージェントの使用を開始するためのいくつかの例とリファレンスアーキテクチャを示します。</p><ul><li><p><strong>インフラストラクチャーの自動化：</strong>サポートシナリオでは、エージェントは読み取り、思考、チャットに使用されてきましたが、これまでは、管理する必要があるインフラストラクチャにアクセスして操作することはできませんでした。Elasticのエンジニアリングチームは、ハッカソンの一環として<a href="https://www.elastic.co/search-labs/blog/agent-builder-augmented-infrastructure">自動インフラ管理</a>エージェントを構築しました。このエージェントはアプリケーションインフラストラクチャの問題を積極的に調査し、自動アクションを実行します。インフラログをインテリジェントに理解し、ワークフローを使用して構成を最適化し、問題に対応し、リソースを拡張します。</p></li><li><p><strong>セキュリティ脅威分析：</strong>Elastic Agent Builder、MCP、Elasticsearchを使用してセキュリティ脆弱性エージェントが開発されました。内部のセキュリティデータと外部の脅威インテリジェンスを相関させることにより、脅威分析を自動化します。エージェントは過去のインシデントと設定に対してセマンティック検索を実行し、結果をライブインターネットデータで強化し、LLMの推論を適用して環境の関連性を評価し、リスクを優先順位付けし、実行可能な修復策を生成します。<a href="https://www.elastic.co/search-labs/blog/agent-builder-mcp-reference-architecture-elasticsearch">リファレンスアーキテクチャ</a><strong>を参照してください。</strong></p></li><li><p><strong>テクニカルカスタマーサポート：</strong>エージェントは、ケースの要約、問題の重複排除と作成、詳細な技術調査など、複数のサポートタスクを実行できます。Agent Builderを使用すると、多段階のハイブリッド検索が可能になり、最も関連性の高い問題、ソリューション、手順のみを見つけ、根本原因の仮説と改善計画を策定できます。Agent Builderは<a href="https://www.elastic.co/blog/generative-ai-customer-support-elastic-support-assistant">複雑なサポートシステムのアーキテクチャを簡素化し</a>、提供までの時間を短縮できます。</p></li><li><p><strong>製品とコンテンツの検出：</strong>Agent Builderは、<a href="https://www.elastic.co/search-labs/blog/build-voice-agents-elastic-agent-builder">会話型エクスペリエンスのための複雑な製品カタログを公開する</a>プロセスを簡素化すると同時に、組織が独自のビジネスロジックと要件を組み込む柔軟性を維持できるようにします。</p></li><li><p><strong>自分で構築：</strong>2026年1月22日から2月27日まで開催される<a href="https://elasticsearch.devpost.com/">Agent Builder Hackathon</a>に参加しましょう。コミュニティと協力して、検索、ワークフロー、ツール、推論を組み合わせた、コンテキスト駆動型のマルチステップAIエージェントを構築し、実世界のタスクを自動化できます。*</p></li></ul><h2>今すぐカスタムエージェントの構築を開始</h2><p>まずは<a href="https://cloud.elastic.co/registration?onboarding_token=search&amp;pg=en-enterprise-search-page">Elastic Cloudトライアル</a>から始めて、<a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">こちら</a>のドキュメントをご覧ください。既存のお客様の場合、Agent BuilderはCloud Serverless、Elastic Cloud Hosted、セルフマネージドのエンタープライズティアでご利用いただけます。</p><p>* ハッカソンの利用規約と参加資格の詳細については<a href="https://elasticsearch.devpost.com/rules">こちらをクリックしてください</a>。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[Elastic Cloud Serverless]]></category>
    <dc:creator><![CDATA[Anish Mathur,Evan Castle]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5ffa581514d8b8c/6a17e092dbb4fff61afb55e2/6840eb7dbb884055ab0e965dcfd614fec54936af-2210x1440.png" length="0" type="image/png"/>
    <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Agent Builderで音声エージェントを構築]]></title>
    <description><![CDATA[音声エージェントの仕組みと、Elastic Agent BuilderとLiveKitを使用して音声エージェントを構築する方法について説明します。]]></description>
    <content:encoded><![CDATA[<p>AIはガラスの箱に閉じ込められています。コマンドを入力すると、テキストで応答が返されます。役には立ちますが、画面の向こうで誰かが動いているのを見ているような距離感があります。今年、2026年は、ビジネスがこのガラスの壁を打ち破り、AIエージェントを製品に導入し、真の価値を提供する年となるでしょう。</p><p>ガラスを砕く方法の1つは、人間の音声を認識し、コンピューター生成の音声を合成するAIエージェントである<em>音声エージェント</em>の採用によるものです。低遅延の文字起こし、高速な大規模言語モデル（LLM）、人間のように聞こえるテキスト読み上げモデルの登場により、これが可能になりました。</p><p>音声エージェントが真に価値あるものになるためには、ビジネスデータへのアクセスも必要です。このブログでは、音声エージェントの仕組みを学び、<a href="https://livekit.io/">LiveKit</a>と<a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a>を使って、架空のアウトドアスポーツ用品店であるElasticSport向けに音声エージェントを構築します。当社の音声エージェントはコンテキストを認識し、自前のデータを活用します。</p><h2>プログラム概要</h2><p>音声エージェントの世界には2つのパラダイムがあります。1つ目は音声合成（speech-to-speech）モデルを使用し、2つ目は音声テキスト変換、LLM、テキスト読み上げからなる音声パイプラインを使用します。音声合成には独自の利点がありますが、音声パイプラインは使用される技術やコンテキストの管理方法、エージェントの動作に対する制御をより柔軟にカスタマイズできます。ここでは音声パイプラインモデルに焦点を当てます。</p><h3>主要コンポーネント</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7dbeb09a3743f38d/6a17de9caf47b67330cdde7d/b237501903f9c3a71fe1b7755c3990e40c5495c8-1600x653.png" alt="Elastic Agent BuilderでAI音声エージェントを構築するためのアーキテクチャ" /><h4>文字起こし（音声テキスト変換）</h4><p>文字起こしは音声パイプラインの入口です。文字起こしコンポーネントは、生のオーディオフレームを入力として受け取り、音声をテキストに書き起こして、そのテキストを出力します。文字起こしされたテキストは、システムがユーザーの音声が終了したことを検出するまでバッファリングされます。その時点で、LLM生成が開始されます。さまざまなサードパーティプロバイダーが低遅延の文字起こしを提供しています。選択する際には、遅延と文字起こしの精度を考慮し、ストリーミング文字起こしをサポートしていることを確認してください。</p><p></p><p>サードパーティAPIの例：<a href="https://www.assemblyai.com/">AssemblyAI</a>、<a href="https://deepgram.com/product/speech-to-text">Deepgram</a>、<a href="https://platform.openai.com/docs/guides/realtime-transcription">OpenAI</a>、<a href="https://elevenlabs.io/speech-to-text">ElevenLabs</a></p><h4>ターン検出</h4><p>ターン検出は、話者が話し終わり、生成が開始されるべき時を検出するパイプラインのコンポーネントです。これを行う一般的な方法の一つは、<a href="https://github.com/snakers4/silero-vad">Silero VAD</a>のような音声アクティビティ検出（VAD）モデルを利用することです。VADは、音声のエネルギーレベルを使用して、音声に発話が含まれるタイミングと発話が終了したタイミングを検出します。しかし、VADだけでは、一時停止と発話の終了の違いを識別することはできません。このため、中間トランスクリプトまたは生の音声に基づいて話者が話し終えたかどうかを予測する発話終了モデルと組み合わせられることがよくあります。</p><p>例 (Hugging Face): <a href="https://huggingface.co/livekit/turn-detector">livekit/turn-detector</a>, <a href="https://huggingface.co/pipecat-ai/smart-turn-v3">pipecat-ai/smart-turn-v3</a></p><h4>エージェント</h4><p>エージェントは音声パイプラインのコアです。意図を理解し、適切なコンテキストを収集し、テキスト形式で返信を作成する役割を担います。<a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a>は、組み込まれた推論機能、ツールライブラリ、ワークフロー統合により、データ上で動作し、外部サービスとやり取りできるエージェントとなります。</p><h4>LLM（テキストからテキスト）</h4><p>Elastic Agent Builder用のLLMを選択する際には、主に、LLMの推論ベンチマークと、最初のトークンまでの時間（TTFT）の2つの特性を考慮する必要があります。</p><p>推論ベンチマークは、LLMがどの程度正しい応答を生成できるかを示します。検討すべきベンチマークは、MT-BenchやHumanity's Last Examデータセットなど、マルチターン会話の遵守とインテリジェンスベンチマークを評価するものです。</p><p>TTFTベンチマークは、モデルが最初の出力トークンを生成する速度を評価します。レイテンシのベンチマークには他にも種類がありますが、TTFTは音声エージェントにとって特に重要です。最初のトークンを受信するとすぐにオーディオ合成を開始できるため、ターン間のレイテンシが短縮され、自然な会話が実現します。</p><p>通常、高速モデルは推論ベンチマークでパフォーマンスが低下することが多いため、これら2つの特性は互いにトレードオフとなります。</p><p>例 (Hugging Face): <a href="https://huggingface.co/openai/gpt-oss-20b">openai/gpt-oss-20b</a>, <a href="https://huggingface.co/openai/gpt-oss-120b">openai/gpt-oss-120b</a></p><h4>合成（テキスト読み上げ）</h4><p>パイプラインの最後の部分はテキスト読み上げモデルです。このコンポーネントは、LLMから出力されたテキストを可聴音声に変換する役割を担います。LLMと同様に、遅延はテキスト読み上げプロバイダーを選択する際に注意すべき特性です。テキスト読み上げの遅延は、最初のバイトまでの時間（TTFB）で測定されます。これは、最初のオーディオバイトが受信されるまでにかかる時間です。TTFBが低いほど、ターンの待ち時間も短縮されます。</p><p>例：<a href="https://elevenlabs.io/text-to-speech-api">ElevenLabs</a>、<a href="https://cartesia.ai/sonic">Cartesia</a>、<a href="https://www.rime.ai/">Rime</a></p><h4>音声パイプラインの構築</h4><p>Elastic Agent Builderは、音声パイプラインに複数のレベルで統合できます。</p><ol><li><p>Agent Builderツールのみ：音声テキスト変換 → LLM（Agent Builderツール使用）→ テキスト読み上げ</p></li><li><p>MCPとしてのAgent Builder：音声テキスト変換 → LLM（MCP 経由でAgent Builderにアクセス）→ テキスト読み上げ</p></li><li><p>Agent Builderをコアとして使用：音声テキスト変換 → Agent Builder →テキスト読み上げ</p></li></ol><p>このプロジェクトでは、コアアプローチとしてAgent Builderを選択しました。このアプローチにより、Agent Builderとワークフローの全機能を使用できます。このプロジェクトでは、LiveKitを使用して音声テキスト変換、ターン検出、テキスト読み上げを管理する、Agent Builderと直接統合するカスタムLLMノードを実装しています。</p><h2>Elasticサポート音声エージェント</h2><p>ElasticSportという架空のスポーツショップ向けにカスタムサポート音声エージェントを構築します。顧客はヘルプラインに電話をかけ、製品の推奨を尋ねたり、製品の詳細を見つけたり、注文状況を確認したり、注文情報をテキストで送信してもらったりできるようになります。これを達成するために、まずカスタムエージェントを設定し、Elasticsearch Query Language（ES|QL）クエリとワークフローを実行するためのツールを作成する必要があります。</p><h3>エージェントの構成</h3><h4>プロンプト</h4><p>プロンプトは、エージェントがどのような性格を持ち、どのように応答するかを指示します。重要なのは、応答が適切に音声に合成され、誤解が適切に修復されることを保証する音声固有のプロンプトがいくつかあることです。</p>You are a Sales Assistant at ElasticSport, an outdoor sport shop specialized in hiking and winter equipment. 

[Profile]
- name: Iva
- company: ElasticSport
- role: Sales Assistant
- language: en-GB
- description: ElasticSport virtual sales assistant

[Context]
- Ask clarifying questions to understand the context.
- Use available tools to answer the user's question.
- Use the knowledge base to retrieve general information

[Style]
- Be informative and comprehensive.
- Maintain a professional, friendly and polite tone.
- Mimic human behavior and speech patterns.
- Be concise. Do not over explain initially

[Response Guideline]
- Present dates in spelled-out month date format (e.g., January fifteenth, two thousand and twenty-four).
- Avoid the use of unpronounceable punctuation such as bullet points, tables, emojis.
- Respond in plain text, avoid any formatting.
- Spell out numbers as words for more natural-sounding speech.
- Respond in short and concise sentences. Responses should be 1 or 2 sentences long.

[ERROR RECOVERY]
### Misunderstanding Protocol
1. Acknowledge potential misunderstanding
2. Request specific clarification<h4>ワークフロー</h4><p>TwilioのメッセージングAPIを介してSMSを送信するための小さなワークフローを追加します。ワークフローはカスタムエージェントに対してツールとして公開され、エージェントが通話中に発信者にSMSを送信できるユーザーエクスペリエンスが実現されます。これにより、発信者は、「 <em>X</em>についての詳細をテキストで送信できますか？」などと尋ねることができます。</p>name: send sms
enabled: true
triggers:
  - type: manual
inputs:
  - name: message
    type: string
    description: The message to send to the phone number.

  - name: phone_number
    type: string
    description: The phone number to send the message to.

consts:
  TWILIO_ACCOUNT: "****"
  BASIC_AUTH: "****"
  FROM_PHONE_NNUMBER: "****"
steps:
  - name: http_step
    type: http
    with:
      url: https://api.twilio.com/2010-04-01/Accounts/{{consts.TWILIO_ACCOUNT}}/Messages.json
      method: POST
      headers:
        Content-Type: application/x-www-form-urlencoded
        Authorization: Basic {{consts.BASIC_AUTH | base64_encode}}
      body: From={{consts.FROM_PHONE_NNUMBER}}&amp;To={{inputs.phone_number}}&amp;Body={{inputs.message}}
      timeout: 30s<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt960a9395fb0985bf/6a17de9e4b055d1dff4320f0/b057e71b0a7c50eb3da47cd4f95e77ec7b4c6126-1600x1245.png" alt="Elastic Agent Builderを使用してAI音声エージェント用の新しいツールを作成" /><h4>ES|QLツール</h4><p>次のツールを使用すると、エージェントは実際のデータに基づいた適切な応答を提供できます。サンプルリポジトリには、製品、注文、ナレッジベースのデータセットでKibanaを初期化するためのセットアップスクリプトが含まれています。</p><ul><li><p><strong>Product.search</strong></p></li></ul><p>製品データセットには65種類の架空の製品が含まれています。これはサンプルドキュメントです。</p>{
      "sku": "ort3M7k",
      "name": "Ortovox Free Rider 26 Backpack",
      "price": 189,
      "currency": "USD",
      "image": "https://via.placeholder.com/150",
      "description": "The Ortovox Free Rider 26 is a technical freeride backpack with a dedicated safety compartment and diagonal ski carry system. Perfect for backcountry missions.\n\nKey Features:\n- 26L capacity\n- Diagonal ski carry system\n- Safety equipment compartment\n- Helmet holder\n- Hydration system compatible",
      "category": "Accessories",
      "subCategory": "Backpacks",
      "brand": "Ortovox",
      "sizes": ["One Size"],
      "colors": ["Black", "Blue", "Orange"],
      "materials": ["Nylon", "Polyester"]
    }<p>名前と説明のフィールドは <code>semantic_text</code>としてマップされており、LLMがES|QLを介してセマンティック検索を実行して関連製品を取得できるようになっています。ハイブリッド検索クエリは、両方のフィールド間でセマンティックマッチングを実行し、ブーストを使用して名前フィールドの一致によりわずかに高い重みを適用します。</p><p>クエリはまず、初期関連性スコアでランク付けされた上位20件の結果を取得します。これらの結果は、<code>.rerank-v1-elasticsearch</code> 推論モデルを使用して説明フィールドに基づいて再ランク付けされ、最後に最も関連性の高い上位 5 つの製品に絞り込まれます。</p>type: ES|QL
toolId: products.search
description: Use this tool to search through the product catalogue by keywords.
query: |
    FROM products
        METADATA _score
      | WHERE
          MATCH(name, ?query, {"boost": 0.6}) OR
            MATCH(description, ?query, {"boost": 0.4})
      | SORT _score DESC
      | LIMIT 20
      | RERANK ?query
            ON description
            WITH {"inference_id": ".rerank-v1-elasticsearch"}
      | LIMIT 5

parameters:
    query: space separated keywords to search for in catalogue<ul><li><p><strong>Knowledgebase.search</strong></p></li></ul><p>ナレッジベースのデータセットには、以下の形式のドキュメントが含まれており、タイトルとコンテンツのフィールドはセマンティックテキストとして格納されます。</p>{
        id: "8273645",
        createdAt: "2025-11-14",
        title: "International Orders",
        content: `International orders are processed through our international shipping partner. Below are the countries we ship to and average delivery times.
        Germany: 3-5 working days
        France: 3-5 working days
        Italy: 3-5 working days
        Spain: 3-5 working days
        United Kingdom: 3-5 working days
        United States: 3-5 working days
        Canada: 3-5 working days
        Australia: 3-5 working days
        New Zealand: 3-5 working days
        `
}<p>このツールは、 <code>product.search</code>ツールと同様のクエリを使用します。</p>type: "ES|QL"
toolId: knowledgebase.search
description: Use this tool to search the knowledgebase.
query: |
  FROM knowledge_base
    METADATA _score
  | WHERE
      MATCH(title, ?query, {"boost": 0.6}) OR
      MATCH(content, ?query, {"boost": 0.4})
  | SORT _score DESC
  | LIMIT 20
  | RERANK ?query
      ON content
      WITH {"inference_id": ".rerank-v1-elasticsearch"}
  | LIMIT 5

parameters:
  query: space separated keywords or natural language phrase to semantically search for in the knowledge base<ul><li><p><strong>Orders.search</strong></p></li></ul><p>最後に追加するツールは、 <code>order_id</code>による注文を取得するために使用されるツールです。</p>type: "ES|QL"
toolId: order.search
description: Use this tool to retrieve an order by its ID.
query: |
  FROM orders
    METADATA _score
  | WHERE order_id == ?order_id
  | SORT _score DESC
  | LIMIT 1

parameters:
  order_id: "the ID of the order"<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfaaf9634f27f70d7/6a17dea07f6f15b8d2c09a3d/d22bdd540a95b5a9c2bd5f308620835e8e6f7ecb-1600x1361.png" alt="音声エージェントの設定" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8f4c704ab96c294a/6a17dea23e03d74af14f2b7e/d91709a50fb5391876b714885242d998b2b21027-1600x1443.png" alt="音声エージェントツール" /><p>エージェントを設定し、これらのワークフローとES|QLツールをエージェントにアタッチした後、エージェントはKibana内でテストできます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbfd2934a9582c04/6a17dea463baff1532741b5e/8691f41624247a6b1352d158c970031e1426ce5e-1600x1056.png" alt="音声エージェントのテスト" /><p>ElasticSportサポートエージェントの構築以外にも、エージェント、ワークフロー、ツールを、リード資格を審査する営業エージェント、住宅修理のサービスエージェント、レストランの予約、予約スケジュールエージェントなど、他のユースケースに合わせてカスタマイズできます。</p><p></p><p>最後の部分は、先ほど作成したエージェントをLiveKit、テキスト読み上げ、音声テキスト変換モデルと連携することです。このブログの最後にリンクされているリポジトリには、LiveKitで使用できるカスタムElastic Agent Builder LLMノードが含まれています。<code>AGENT_ID</code>を独自のものに置き換えて、Kibanaインスタンスにリンクするだけです。</p><h2>はじめに</h2><p><a href="https://github.com/KDKHD/elastic_agent_builder_livekit">ここで</a>コードをチェックして試してみてください。 </p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/build-voice-agents-elastic-agent-builder</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-voice-agents-elastic-agent-builder</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Kenneth Kreindler]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2732d87a324baa78/6a17dea6e9ea873632a9c4cc/43ceabb9e2c0966261c188bd40e03178d5a91e5c-1280x720.png" length="0" type="image/png"/>
    <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearchによるエージェントメモリの管理]]></title>
    <description><![CDATA[Elasticsearchを使用してメモリを管理することで、よりコンテキストを意識した効率的なエージェントを作成できます。]]></description>
    <content:encoded><![CDATA[<p>新興の分野である<strong>コンテキストエンジニアリング</strong>では、AIエージェントに適切なタイミングで適切な情報を提供することが極めて重要です。コンテキストエンジニアリングの最も重要な側面の1つは、AIの<strong>メモリ</strong>を管理することです。人間と同じように、AIシステムは情報を思い出すために短期記憶と長期記憶の両方に依存しています。大規模言語モデル（LLM）エージェントに論理的な会話をさせたり、ユーザーの好みを覚えたり、以前の結果や対応に基づいて構築させたりしたい場合は、効果的な記憶メカニズムをエージェントに装備する必要があります。</p><p>結局のところ、コンテキスト内のすべてがAIの応答に影響を与えます。「<em>ゴミを入れたらゴミが出てくる</em>」は真実です。</p><p>この記事では、AIエージェントにとって短期記憶と長期記憶が何を意味するのか、具体的に紹介します。</p><ul><li><p>短期記憶と長期記憶の違い。</p></li><li><p>Elasticsearchのようなベクトルデータベースを使用したRetrieval-Augmented Generation（RAG）手法との関係、そして慎重なメモリ管理が必要な理由。</p></li><li><p>コンテキストオーバーフローやコンテキストポイズニングなど、メモリを軽視することによるリスク。</p></li><li><p>エージェントのメモリを有用かつ安全に保つベストプラクティス（コンテキストのプルーニング、要約、関連するもののみの取得など）</p></li><li><p>最後に、Elasticsearchを使用してエージェントが混乱することなく協力できるように、マルチエージェントシステムでメモリを共有および伝播する方法について説明します。</p></li></ul><h2>AIエージェントにおける短期記憶と長期記憶の違い</h2><p><em><strong>AIエージェントの短期記憶</strong></em>は通常、直前の会話のコンテキストや状態を指します。本質的には、アクティブなセッションにおける現在のチャット履歴や最近のメッセージです。ユーザーの最新のクエリと最近のやり取りが含まれ、人が会話中に心に留めている情報と非常に似ています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb714ce810d1c472/6a170f321949f782cbe7aaf6/4fbcc6f68055b2bccefc4176297a4ca50056dc0d-764x498.png" alt="エージェントの短期記憶と長期記憶" /><p>AIフレームワークは、エージェントの状態の一部としてこの一時的なメモリを保持することがよくあります（例えば、<a href="https://docs.langchain.com/oss/python/langgraph/persistence#checkpoints">LangGraphのこの例</a>のように、会話の状態を格納するためにチェックポインタを使用します）。短期記憶は<em><strong>セッション範囲に限定されます</strong></em>。つまり、短期記憶は単一の会話またはタスク内に存在し、明示的に他の場所に保存されない限り、そのセッションが終了するとリセットまたはクリアされます。セッションに縛られた短期記憶の例としては、ChatGPTで利用可能な<a href="https://help.openai.com/en/articles/8914046-temporary-chat-faq"><strong>一時的なチャット</strong></a>が挙げられます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4b8680e22d4e1185/6a170f341949f78bbae7aafa/150bdf209cda5ed20b59cddf34e624ad1a8016aa-1100x577.png" alt="AIフレームワークのメモリ" /><p>一方、<em><strong>長期記憶</strong></em>とは、<strong>会話やセッションを超えて</strong>持続する情報を指します。これは、エージェントが長期にわたって保持する知識、以前に学習した事実、ユーザーの選好、または永続的に覚えておくように指示されたデータです。</p><p>長期記憶は通常、ファイルやベクトルデータベースなどの即時コンテキストウィンドウの外部にある外部ソースに格納・取得することで実装されます。短期的なチャット履歴とは異なり、長期記憶はすべてのプロンプトに自動的に含まれるわけではありません。代わりに、特定のシナリオに基づいて、エージェントは関連するツールが呼び出されたときにそれを<strong>想起したり</strong>取得したりする必要があります。実際には、長期記憶にはユーザーのプロフィール情報、エージェントが生成した過去の回答や分析、またはエージェントがクエリを実行できるナレッジベースが含まれる可能性があります。</p><p>例えば、旅行プランナーエージェントの場合、<em>短期メモリには</em>現在の旅行の問い合わせの詳細（日付、目的地、予算）とそのチャットでのフォローアップの質問が格納され、<em>長期メモリには</em>ユーザーの一般的な旅行の好み、過去の旅程、および以前のセッションで共有されたその他の事実が格納されます。ユーザーが後で戻ってきたときに、エージェントはこの長期格納場所から情報を引き出すことができます（そのユーザーはビーチや山が好きで、平均予算はINR 100,000で、行きたい場所のリストがあり、子供向けのアトラクションよりも歴史や文化を体験することを好むなど）。そのため、エージェントは毎回ユーザーを白紙の状態として扱うことはありません。</p><p>短期記憶（チャット履歴）は即時のコンテキストと継続性を提供し、長期記憶はエージェントが必要なときに利用できるより広範なコンテキストを提供します。ほとんどの先進的なAIエージェントフレームワークは、その両方を可能にします。コンテキストを保守するために最近の対話を追跡し、<em>かつ</em>より長期的なリポジトリに情報を検索または格納するメカニズムを提供します。短期記憶を管理することで、コンテキストウィンドウ内に留まることを確保し、長期記憶を管理することで、エージェントは過去のやり取りやペルソナに基づいて回答を裏付けることができます。</p><h2>コンテキストエンジニアリングにおけるメモリとRAG</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt98c1514741bea460/6a170f36509168083ce1bbae/46635aa11ceff89b8d6a26ac3e22da52407d82f3-1600x900.png" alt="コンテキストエンジニアリングにおけるメモリとRAG" /><p><em><strong>実際にAIエージェントに有用な長期記憶を与えるにはどうすればよいでしょうか？</strong></em></p><p>長期記憶のための一つの顕著なアプローチは<em><strong>セマンティック記憶</strong></em>であり、しばしば<strong>検索拡張生成（RAG）</strong>を介して実装されます。これには、LLMを外部のナレッジストアやElasticsearchなどのベクトル対応データストアと結合することが含まれます。LLMは、プロンプトや組み込みのトレーニングの内容以外の情報を必要とする場合、Elasticsearchに対してセマンティック検索を実行し、最も関連性の高い結果をコンテキストとしてプロンプトに注入します。このように、モデルの効果的なコンテキストには、最近の会話（短期記憶）だけでなく、その場で得られた関連する長期的事実も含まれます。その後、LLMは、独自の推論と取得した情報の両方に基づいて回答を決定し、短期記憶と長期記憶を効果的に組み合わせて、より正確でコンテキストを意識した応答を生成します。</p><p><strong>Elasticsearch</strong>はAIエージェントの長期記憶実装にも利用できます。長期記憶のためにElasticsearchからコンテキストを取得する方法の高次的な例を示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt44f5a6887b0bca32/6a170f37a6c2b9c735e797be/41ccbc7b5171e8170ac300139a963c0708816ba6-1600x900.png" alt="RAGの実行" /><p>このように、エージェントは<strong>さまざまなリスクにつながる</strong>限られたプロンプト内にすべてを格納するのではなく、関連するデータを検索することによって「思い出し」ます。</p><p><strong>RAGをElasticsearchや任意のベクトルストアで使用することで、多くの利点が得られます。</strong></p><p>第一に、モデルのトレーニングのカットオフを超えて<strong>知識を拡張</strong>できます。エージェントは、LLMが知らない可能性のある最新の情報やドメイン固有のデータを取得できます。これは最近の出来事や専門的なトピックに関する質問に非常に重要です。</p><p>第二に、オンデマンドでコンテキストを取得することで、ハルシネーションを減らすことができます。特に、LLMはニッチなユースケースに関連する独自データや高度に専門化されたデータでトレーニングされていないため、ハルシネーションが発生する可能性が非常に高いです。LLMが評価によって推測や新しい情報を作り出すのではなく、最近のOpenAIの論文（<a href="https://arxiv.org/pdf/2509.04664">Why Language Models Hallucinate</a>）で指摘されているように、このモデルはElasticsearchの事実に基づく参照に基づいています。当然ながら、LLMはベクトルストアのデータの信頼性に依存しており、誤情報を確実に防御するために、コアの関連性指標に基づいて関連データが取得されます。</p><p>第三に、RAGを使用すると、エージェントはプロンプトに収めることができるものよりもはるかに大きなナレッジベースを扱うことができます。長い研究論文や政策文書のように、文書全体をコンテキストウィンドウに押し込んで過剰負荷や無関係な情報コンテキストがモデルの推論を損なう<a href="https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch#context-poisoning">コンテキストポイズニング</a>を招くことなく、RAGは<a href="https://www.elastic.co/search-labs/blog/chunking-strategies-elasticsearch">チャンク化</a>に依存します。大きな文書は意味的に意味のある小さな部分に分割され、システムはクエリに最も関連性の高い少数のチャンクのみを取得します。この方法では、モデルが知識豊富であることを示すために100万トークンのコンテキストを必要とせず、はるかに大規模なコーパスの適切なチャンクにアクセスするだけで済みます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4c90f81a56db0a33/6a170f3960084be7ba3c462e/e6897356c9f0940e35a63d005e9cd20bc33e5dd7-1600x931.png" alt="LLMコンテキストエンジニアリングの進化" /><p>注目すべきは、LLMのコンテキストウィンドウが拡大し（<a href="https://www.anthropic.com/news/1m-context">一部のモデルは現在、数十万または数百万のトークンをサポート</a><em>）</em>、RAGの「終焉」についての議論が起きたことです。なぜすべてのデータをプロンプトに押し込まないのでしょうか。同じようにお考えなら、同僚のJeffrey RengifoとEduard Martinによるこの素晴らしい記事<a href="https://www.elastic.co/search-labs/blog/rag-vs-long-context-model-llm">「コンテキストは長ければよいわけではない：RAGが引き続き重要な理由」</a>を参照してください。これにより、「ゴミを入れればゴミが出てくる」問題を回避できます。LLMは、ノイズを処理するのではなく、重要な少数のチャンクに焦点を合わせ続けます。</p><p>とはいえ、Elasticsearchやその他のベクトルストアをAIエージェントのアーキテクチャに統合することで、<strong>長期記憶が</strong>可能になります。エージェントは知識を外部に格納し、必要に応じてメモリコンテキストとして取り込みます。これは、各ユーザークエリの後、エージェントがElasticsearchで関連情報を検索し、LLMを呼び出す前にプロンプトに上位の結果を追加する<em>アーキテクチャとして</em>実装することができます。応答に新しい有用な情報が含まれている場合、その応答は長期保存されることもあります（学習のフィードバックループが作成されます）。そのような検索ベースのメモリを使用することで、エージェントは、コンテキストウィンドウが<em>100万トークン</em>をサポートしている場合であっても、常に情報を得て最新の状態を保ちながら、すべての知識をすべてのプロンプトに詰め込む必要がなくなります。この技術は、情報検索と生成AIの強みを組み合わせたコンテキストエンジニアリングの基礎となるものです。 </p><p>これは、セッション中の短期記憶にLangGraphのチェックポイントシステムを使用して管理されたメモリ内会話状態の例です。（当社の<a href="https://github.com/someshwaranM/elastic-context-engineering-short-term-long-term-memory">コンテキストエンジニアリングをサポートするアプリ</a>を参照してください。）</p># Initialize chat memory (Note: This is in-memory only, not persistent)
memory = MemorySaver()

# Create a LangGraph agent
langgraph_agent = create_react_agent(model=llm, tools=tools, checkpointer=memory)

...
...
# Only process and display checkpoints if verbose mode is enabled
if args.verbose:
    # List all checkpoints that match a given configuration
    checkpoints = memory.list({"configurable": {"thread_id": "1"}})
    # Process the checkpoints
    process_checkpoints(checkpoints)<p><strong>checkpoint</strong>の格納方法は以下の通りです。</p>Checkpoint:
Timestamp: 2025-12-30T09:19:41.691087+00:00
Checkpoint ID: 1f0e560a-c2fa-69ec-8001-14ee5373f9cf
User: Hi I'm Som, how are you? (Message ID: ad0a8415-5392-4a58-85ad-84154875bbf2)
Agent: Hi Som! I'm doing well, thank you! How about you? (Message ID: 
56d31efb-14e3-4148-806e-24a839799ece)
Agent:  (Message ID: lc_run--019b6e8e-553f-7b52-8796-a8b1fbb206a4-0)

Checkpoint:
Timestamp: 2025-12-30T09:19:40.350507+00:00
Checkpoint ID: 1f0e560a-b631-6a08-8000-7796d108109a
User: Hi I'm Som, how are you? (Message ID: ad0a8415-5392-4a58-85ad-84154875bbf2)
Agent: Hi Som! I'm doing well, thank you! How about you? (Message ID: 
56d31efb-14e3-4148-806e-24a839799ece)

Checkpoint:
Timestamp: 2025-12-30T09:19:40.349027+00:00
Checkpoint ID: 1f0e560a-b62e-6010-bfff-cbebe1d865f6<p>長期記憶については、Elasticsearchでセマンティック検索を実行し、チェックポイントを要約して Elasticsearchにインデックス付けした後、ベクトル埋め込みを使用して関連する以前の会話を取得する方法を次に示します。</p>Functions: 
retrieve_from_elasticsearch() 

# Enhanced Elasticsearch retrieval with rank_window and verbose display
def retrieve_from_elasticsearch(query: str, k: int = 5, rank_window: int = None) -&gt; tuple[List[Dict[str, Any]], str]:
    """
    Retrieve context from Elasticsearch with score-based ranking
    
    Args:
        query: Search query
        k: Number of results to return
        rank_window: Number of candidates to retrieve before ranking (default: args.rank_window)
        
    Returns:
        Tuple of (retrieved_documents, formatted_context_string)
    """
    if not es_client or not es_index_name:
        return [], "Elasticsearch is not available. Cannot search long-term memory."
    
    if rank_window is None:
        rank_window = args.rank_window
    
    try:
        # Check if index exists and has documents
        if not es_client.indices.exists(index=es_index_name):
            return [], "No previous conversations stored in long-term memory yet."
        
        # Get document count
        try:
            doc_count = es_client.count(index=es_index_name)["count"]
            if doc_count == 0:
                return [], "Long-term memory is empty. No previous conversations to search."
        except Exception as e:
            return [], f"Error checking memory: {str(e)}"
        
        # Generate embedding for the query
        try:
            query_embedding = embeddings.embed_query(query)
        except Exception as e:
            return [], f"Error generating embedding: {str(e)}"
        
        # Perform semantic search using kNN with rank_window
        try:
            search_body = {
                "knn": {
                    "field": "vector",
                    "query_vector": query_embedding,
                    "k": k,
                    "num_candidates": rank_window  # Retrieve more candidates, then rank top k
                },
                "_source": ["text", "content", "message_type", "timestamp", "thread_id"],
                "size": k
            }
            
            response = es_client.search(index=es_index_name, body=search_body)
            
            if not response.get("hits") or len(response["hits"]["hits"]) == 0:
                return [], "No relevant previous conversations found in long-term memory."
            
            # Extract documents with scores
            retrieved_docs = []
            for hit in response["hits"]["hits"]:
                source = hit["_source"]
                score = hit["_score"]
                retrieved_docs.append({
                    "content": source.get("content", source.get("text", "")),
                    "message_type": source.get("message_type", "unknown"),
                    "timestamp": source.get("timestamp", "unknown"),
                    "thread_id": source.get("thread_id", "unknown"),
                    "score": score
                })
            
            # Format context string
            context_parts = []
            for i, doc in enumerate(retrieved_docs, 1):
                context_parts.append(doc["content"])
            
            context_string = "\n\n".join(context_parts)
            
            # Verbose display
            if args.verbose:
                rich.print(f"\n[bold yellow]🔍 RETRIEVAL ANALYSIS[/bold yellow]")
                rich.print("="*80)
                rich.print(f"[blue]Query:[/blue] {query}")
                rich.print(f"[blue]Retrieved:[/blue] {len(retrieved_docs)} documents (from {rank_window} candidates)")
                rich.print(f"[blue]Total context length:[/blue] {len(context_string)} characters\n")
                
                for i, doc in enumerate(retrieved_docs, 1):
                    rich.print(f"[cyan]📄 Document {i} | Score: {doc['score']:.4f} | Type: {doc['message_type']}[/cyan]")
                    rich.print(f"[cyan]   Timestamp: {doc['timestamp']} | Thread: {doc['thread_id']}[/cyan]")
                    content_preview = doc['content'][:200] + "..." if len(doc['content']) &gt; 200 else doc['content']
                    rich.print(f"[cyan]   Content: {content_preview}[/cyan]")
                    rich.print("-" * 80)
            
            return retrieved_docs, context_string
            
        except Exception as e:
            return [], f"Error searching memory: {str(e)}"
            
    except Exception as e:
        return [], f"Error accessing long-term memory: {str(e)}"<p>ElasticsearchのLangGraphのチェックポイントを使用して、短期記憶と長期記憶がどのようにインデックス化され、取得されるかを調べてきました。さて、少し時間を取って、会話全体をインデキシングしてダンプすることがなぜ危険なのかを理解しましょう。</p><h2>コンテキストメモリを管理しないことのリスク</h2><p>コンテキストエンジニアリング、短期記憶、長期記憶について詳しく説明しているので、エージェントのメモリとコンテキストを適切に管理しないと何が起こるかを理解しましょう。</p><p>残念ながら、AIのコンテキストが非常に長くなったり、悪い情報が含まれたりすると、多くの問題が発生する可能性があります。コンテキストウィンドウが大きくなるにつれて、次のような<strong>新しい障害モード</strong>が発生します。</p><ul><li><p><strong>コンテキストポイズニング</strong></p></li><li><p><strong>コンテキストの逸脱</strong></p></li><li><p><strong>コンテキストの混乱</strong></p></li><li><p><strong>コンテキストの衝突</strong></p></li><li><p><strong>コンテキスト漏洩と知識対立</strong></p></li><li><p><strong>ハルシネーションと誤情報</strong></p></li></ul><p>これらの問題や、不適切なコンテキスト管理から生じるその他のリスクを分解して見ていきましょう。</p><h3>コンテキストポイズニング</h3><p><em>コンテキストポイズニング</em>とは、不正確または有害な情報がコンテキストに入り込み、モデルのその後の出力を「汚染」してしまうことを指します。よくある例としては、モデルによるハルシネーションが事実として扱われ、会話履歴に挿入されることがあります。モデルはその後、そのエラーを元に対応を構築し、誤りを複合化させることがあります。反復的なエージェントループでは、共有コンテキスト（エージェントの作業メモの要約など）に誤った情報が入ると、その情報が何度も強化されることがあります。 </p><p><a href="https://storage.googleapis.com/deepmind-media/gemini/gemini_v2_5_report.pdf">DeepMindの研究者は、Gemini 2.5レポート</a>（概略は<a href="https://www.dbreunig.com/2025/06/17/an-agentic-case-study-playing-pok%C3%A9mon-with-gemini.html">ここをチェック</a>）のリリース時に長年<em>ポケモン</em>をプレイしているエージェントでこれを観察しました。エージェントが間違ったゲーム状態のハルシネーションを起こし、それがその<em>コンテキスト</em>（ゴールの記憶）に記録された場合、エージェントは不可能な目標に関する<strong>無意味な戦略</strong>を立て、行き詰まってしまいます。言い換えれば、汚染された記憶はエージェントを永久に誤った道へと導く可能性があるのです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd56e9e0681f32239/6a170f3b4a531bd79536aa21/3f2facf5aad67613ad557422e09ec23a66adc0ed-1600x1388.png" alt="コンテキストポイズニング" /><p>コンテキストポイズニングは、無意識のうちに（誤って）発生する可能性があるだけでなく、悪意を伴ってを発生する可能性もあります。例えば、プロンプトインジェクション攻撃では、ユーザーや第三者が隠れた指示や虚偽の事実を忍び込ませ、エージェントがそれを記憶し、従うようにします。</p><p><strong>推奨される対策：</strong></p><p><a href="https://www.wiz.io/academy/data-poisoning">Wiz</a>、<a href="https://zerlo.net/en/blog/what-is-llm-data-poisoning">Zerlo</a>、<a href="https://www.anthropic.com/research/small-samples-poison">Anthropic</a>からの洞察に基づき、コンテキストポイズニングの対策は、LLMのプロンプト、コンテキストウィンドウ、検索パイプラインに悪い情報や誤解を招く情報が入らないようにすることに重点を置いています。主な手順は次のとおりです。</p><ul><li><p>常にコンテキストを確認する：開始プロンプトだけでなく、不審な点や有害な点がないか、会話や検索されたテキストを監視します。</p></li><li><p>信頼できるソースを使用する：信頼性に基づいて文書にスコアを付けたりラベルを付けたりすることで、システムは信頼性の高い情報を優先し、スコアの低いデータを無視します。</p></li><li><p>異常なデータを見つける：奇妙なもの、場違いなもの、または操作されたコンテンツを検出するツールを使用し、モデルが使用する前に削除します。</p></li><li><p>入力と出力をフィルターする: 有害または誤解を招くテキストがシステムに簡単に入力されたり、モデルによって繰り返されたりしないようにガードレールを追加します。</p></li><li><p>モデルをクリーンなデータで最新の状態に保つ：検証済みの情報で定期的にシステムを更新して、流出してしまった不良データに対処します。</p></li><li><p>人間が関与する：重要な出力を人間が確認したり、既知の信頼できるソースと比較したりします。</p></li></ul><p>長いチャットをリセットし、関連情報のみを共有し、複雑なタスクを小さなステップに分割し、モデル外で明確なメモを維持するなど、使用時のシンプルな習慣も役立ちます。</p><p>これらの対策を組み合わせることで、コンテキストポイズニングからLLMを保護し、出力の正確性と信頼性を維持する階層化された防御が実現します。</p><p>ここに記載されている対策を講じないと、エージェントは以前のガイドラインや攻撃者が挿入した些細な事実を無視するなどの指示を覚えてしまい、有害な出力につながることがあります。</p><h3>コンテキストの逸脱</h3><p><em>コンテキストの逸脱</em>とは、コンテキストが長くなりすぎて、モデルがコンテキストに過度に集中し、トレーニング中に学習した内容を無視してしまうことです。極端な場合、これは<a href="https://en.wikipedia.org/wiki/Catastrophic_interference"><em>壊滅的な忘却</em></a>のようになります。この場合、モデルは基本的な知識を「忘れ」、目の前にある情報に過度に依存するようになります。過去の研究では、プロンプトが非常に長いとLLMが焦点を失うことが多いことが示されています。</p><p>例えば、Gemini 2.5エージェントは100万トークンのウィンドウをサポートしていましたが、そのコンテキストが特定のポイント（実験では約10万トークン）を超えると、新しいソリューションを思いつく代わりに、<strong>過去の行動を繰り返すことに固執</strong>し始めました。ある意味、エージェントはその長い歴史の囚人となったのです。基礎となるトレーニング知識を活用して斬新な戦略を考案するのではなく、以前の動作の長いログ（コンテキスト）を見てそれを真似し続けました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91ea0056bbda6e2d/6a170f3d2b835fdd2bf4b2db/e08e5b6d2e8ec7e3511d455985eed3d7fa6241e0-1352x636.png" alt="コンテキストの逸脱 " /><p>これでは逆効果です。私たちは、モデルが思考能力を無効にするのではなく、推論を助けるために関連コンテキストを使用することを望んでいます。注目すべきは、巨大なウィンドウを持つモデルでさえ、トークンが追加されるにつれて、パフォーマンスが不均一に低下するという<a href="https://research.trychroma.com/context-rot"><em>コンテキスト腐敗</em></a>を示すことです。ここには<em>注意予算</em>が見られるようです。つまり、人間の作業記憶が限られているように、LLMがトークンに注意を払う能力も有限であり、その予算が限界に達すると、その精度と焦点が低下します。</p><p>緩和策として、チャンキング、適切な情報のエンジニアリング、定期的なコンテキストの要約、応答の正確性を測定するための評価とモニタリング技術を使用して、コンテキストの逸脱を防止できます。</p><p>これらの方法により、モデルは関連するコンテキストとその基礎となるトレーニングの両方に基盤を置くようになり、逸脱のリスクが軽減され、全体的な推論品質が向上します。</p><h3>コンテキストの混乱</h3><p><em>コンテキストの混乱</em>とは、コンテキスト内の余分なコンテンツがモデルによって使用され、低品質の対応が生成されることです。代表的な例としては、エージェントが使用する可能性のある大量のツールやAPI定義をエージェントに提供することが挙げられます。それらのツールの多くが現在のタスクと無関係であっても、モデルはコンテキスト内に存在するという理由だけで、それらを不適切に使用しようとする可能性があります。実験によると、必要でないツールやドキュメントを<em>多く</em>提供すると、パフォーマンスが<em>低下</em>することがわかっています。エージェントは、間違った関数を呼び出したり、無関係なテキストを参照したりするなどの間違いを犯し始めます。 </p><p>あるケースでは、小型の<strong>Llama 3.1 8B</strong>モデルは、検討すべきツールが46個与えられたときにはタスクに失敗しましたが、19個のツールしか与えられなかったときには成功しました。コンテキストが長さ制限内であったにもかかわらず、追加のツールによって混乱が生じました。根本的な問題は、プロンプト内のすべての情報がモデルに<em>処理</em>されることです。何かを無視するべきことを認識していない場合、その何かが望ましくない方法で出力に影響を及ぼす可能性があります。無関係な部分がモデルの注意の一部を「奪い」、誤った方向に導くことがあります（無関係な文書によってエージェントが尋ねられた質問とは異なる質問に答えるなど）。コンテキストの混乱は、しばしばモデルが無関係なコンテキストを統合した低品質の応答を生成することとして現れます。研究論文「<a href="https://arxiv.org/pdf/2411.15399">Less is More: Optimizing Function Calling for LLM Execution on Edge Devices.</a>」を参照してください。</p><p>これは、特に関連性を重視して<strong>厳選</strong>されていない場合、コンテキストが多ければ多いほど良いとは限らないことを想起させます。</p><h3>コンテキストの衝突</h3><p><em>コンテキストの衝突</em>は、<strong>コンテキストの部分が互いに矛盾</strong>し、モデルの推論を脱線させる内部不整合が発生するときに起こります。エージェントが矛盾する複数の情報を蓄積すると、衝突が発生することがあります。 </p><p>例えば、あるエージェントが2つのソースからデータを取得したとします。1つは<em>午後5時にA便が出発</em>するというもので、もう1つは<em>午後6時にA便が出発</em>するというものです。両方の事実がコンテキスト内に存在する場合、貧弱なモデルではどちらが正しいのかを判断できず、混乱したり、不正確な回答や類似しない回答を生成したりする可能性があります。</p><p>コンテキストの衝突は、モデルの<strong>過去の回答の試み</strong>が、その後の洗練された情報とともにコンテキスト内に残っている、マルチターンの会話でも頻繁に発生します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd86976266867c0ed/6a170f3e66c4f9c785f8c105/500d7a80dc8db1923f9b5ca84728eed64fa296f7-1316x580.png" alt="コンテキストの衝突" /><p>MicrosoftとSalesforceの<a href="https://arxiv.org/pdf/2505.06120">研究</a>によると、複雑なクエリを複数のチャットボットターンに分割して詳細を徐々に追加すると、単一のプロンプトですべての詳細を伝える場合と比べて、最終的な精度が大幅に低下することが示されています。なぜでしょうか。初期のターンにモデルからの部分的なまたは不正確な中間回答が含まれており、それらはコンテキスト内に残るためです。モデルが後ですべての情報を使用して回答しようとすると、その<em>メモリ</em>にはまだそれらの誤った試行が含まれており、修正された情報と矛盾して軌道から外れてしまいます。本質的に、会話のコンテキストが自己衝突しているのです。このモデルは、新しい情報が追加された後には適用されない古いコンテキスト（以前のターンからの）を誤って使用してしまう可能性があります。</p><p>エージェントが異なるツールやサブエージェントからの出力を組み合わせる可能性があるエージェントシステムでは、コンテキストの衝突は特に危険です。これらの出力が一致しない場合、集約された文脈は一貫性がありません。すると、エージェントは行き詰まったり、矛盾を調整しようとして無意味な結果を生成する可能性があります。コンテキストの衝突を防ぐには、コンテキストが<strong>新鮮で一貫している</strong>こと、例えば、古い情報をクリアまたは更新し、一貫性がないソースを混ぜないことが重要です。</p><h3>コンテキスト漏洩と知識対立</h3><p>システムで複数のエージェントやユーザーがメモリストアを共有する場合、コンテキスト間で情報が漏れるリスクがあります。</p><p>例えば、適切なアクセス制御がないまま、2人の異なるユーザーのデータ埋め込みが同じベクトルデータベースに存在する場合、ユーザーAのクエリに応答するエージェントが、誤ってユーザーBのメモリの一部を取得する可能性があります。この <em><strong>クロスコンテキスト漏洩</strong></em> は、個人情報を漏洩させたり、対応に混乱を生じさせたりする可能性があります。</p><p><a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/">OWASP Top 10 for LLM Applications</a>によれば、マルチテナントベクトルデータベースは次のような漏洩を防ぐ必要があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte433216805a66d29/6a170f404a531b2c4e36aa25/8f0ccf0b2f7bd6715c14aceee2deffb213d50bd9-1600x936.png" alt="コンテキストの漏洩" /><p><a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/">LLM08:2025 Vector and Embedding Weaknesses</a><em>によると、</em>一般的なリスクの1つはコンテキストの漏洩です。</p><em>マルチテナント環境では、複数のクラスのユーザーやアプリケーションが同じベクトルデータベースを共有するため、ユーザーやクエリ間でコンテキストが漏洩するリスクがあります。データフェデレーション知識の矛盾エラーは、複数のソースからのデータが矛盾し合う場合に発生します。これは、LLMがトレーニング中に学習した古い知識を検索拡張からの新しいデータで置き換えることができない場合にも発生する可能性があります。</em><p>もう一つの側面は、LLMがメモリからの新しい情報で<strong>組み込まれた知識</strong>を上書きするのに苦労する可能性があることです。モデルが何らかの事実に基づいてトレーニングされていて、取得したコンテキストが逆の場合、モデルはどちらを信頼すべきか混乱する可能性があります。適切な設計がないと、エージェントがコンテキストを混同したり、古い知識を新しい証拠で更新できなかったりして、古くなった回答や間違った回答につながる可能性があります。</p><h3><strong>ハルシネーションと誤情報</strong></h3><p><em>ハルシネーション</em>（LLM がもっともらしく聞こえるが誤った情報を生成すること）は、コンテキストが長くなくても既知の問題ですが、メモリ管理が不十分だと増幅される可能性があります。 </p><p>エージェントのメモリに重要な事実が欠けている場合、モデルは単に<strong>その空白を推測で埋める</strong>可能性があり、その推測がコンテキストに入り込む（汚染）と、エラーが持続します。 </p><p>OWASPのLLMセキュリティレポート<a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/"><strong>（LLM09:2025 Misinformation）</strong></a>では、誤情報が主な脆弱性として強調されています。LLMは自信を持って虚偽の回答を生成する可能性があり、ユーザーはそれらを過度に信頼する可能性があります。長期記憶が不良または古くなっているエージェントは、その記憶が最新の状態に保たれていない限り、昨年は真実であったが現在は間違っていることを自信を持って引用する可能性があります。 </p><p>（ループ内のユーザーまたはエージェント自体による）AIの出力への過度の依存は、この問題を悪化させる可能性があります。誰もメモリの情報を確認しなければ、エージェントは虚偽を蓄積してしまう可能性があります。RAGがハルシネーションを抑えるためによく使われるのはこのためです。信頼できる情報源を検索することで、モデルは事実を捏造する必要がなくなります。しかし、検索によって間違った文書（誤った情報が含まれている文書など）が取得されたり、初期のハルシネーションが除去されなかったりすると、システムはその誤った情報をその動作全体に伝播してしまう可能性があります。 </p><p>肝心なことは、メモリの管理に失敗すると、<strong>不正確で誤解を招く出力</strong>につながる可能性があり、特にリスクが高い場合（金融や医療分野での誤ったアドバイスなど）には損害を与える可能性があります。エージェントには、コンテキスト内のあらゆるものを無条件に信頼するだけでなく、メモリの内容を検証または修正するためのメカニズムが必要です。</p><p>まとめると、成功の秘訣は、AIエージェントに無限に長いメモリを与えたり、ありとあらゆるものをそのコンテキストに放り込んだりすることでは<em>ありません</em>。</p><h2>LLMアプリケーションにおけるメモリ管理のベストプラクティス</h2><p>上記の陥穽を避けるために、開発者と研究者はAIシステムで<strong>コンテキストとメモリを管理するためのベストプラクティス</strong>をいくつか考案しました。これらのプラクティスは、AIの動作コンテキストを無駄なく、関連性があり、最新の状態に保つことを目的としています。ここでは、いくつかの重要な戦略と、それがどのように役立つかの例を示します。</p><h3>RAG：ターゲットを絞ったコンテキストを使用する</h3><p>RAGの大部分についてはすでに前のセクションで説明しましたので、このセクションは実用的な注意事項を簡潔にまとめたものです。</p><ul><li><p>ターゲットを絞った検索を使用し、一括読み込みは避ける：全文書や会話の履歴全体をプロンプトにプッシュするのではなく、最も関連性の高い部分のみを取得します。</p></li><li><p>RAGをジャストインタイムのメモリ呼び出しとして扱う：ターンを超えてすべてを転送するのではなく、必要なときにのみコンテキストを取得します。</p></li><li><p>関連性に注意した検索戦略を優先する：トップkセマンティック検索、逆順位融合、ツールロードアウトフィルタリングなどのアプローチは、ノイズを減らし、グラウンディングを改善するのに役立ちます。</p></li><li><p>コンテキストウィンドウが大きくなってもRAGは必要：2つの非常に関連性の高い段落は、20の緩く関連したページよりもほとんど常に効果的です。</p></li></ul><p>つまり、RAGではコンテキストを増やすことではなく、適切なコンテキストを追加することが重要なのです。</p><h3>ツールのロードアウト</h3><p><em>ツールのロードアウト</em>とは、モデルにタスクに実際に必要なツールのみを与えることです。この用語はゲームに由来し、状況に合った装備を選ぶというものです。ツールが多すぎると作業が遅くなり、間違ったツールを使用すると失敗の原因になります。研究論文「<a href="https://arxiv.org/abs/2411.15399">Less is more</a>」によると、LLMも同様に動作します。ツールが30個を超えると、説明が重複し始め、モデルが混乱します。ツールの数が約100個を超えると、失敗はほぼ確実です。これはコンテキストウィンドウの問題ではなく、コンテキストの混乱です。</p><p>シンプルで効果的な解決策となるのが<a href="https://arxiv.org/abs/2505.03275"><strong>RAG-MCP</strong></a>です。すべてのツールをプロンプトにダンプする代わりに、ツールの説明はベクトルデータベースに保存され、リクエストごとに最も関連性の高いものだけが取得されます。実際には、これにより、ロードアウトが小さく集中したものになり、プロンプトが大幅に短縮され、ツール選択の精度が最大3倍向上します。</p><p>小型モデルでは、この壁にぶつかるのがさらに早くなります。調査によると、8Bモデルは数十個のツールでは失敗しますが、ロードアウトを調整すると成功します。場合によっては最初にLLMを使用して、必要と思われるツールを動的に選択すると、パフォーマンスが44%向上し、電力使用量とレイテンシも削減されます。重要な点は、ほとんどのエージェントはわずかなツールしか必要としないものの、システムが成長するにつれて、ツールのロードアウトとRAG-MCPが第一の設計上の決定事項になるということです。</p><h3>コンテキストのプルーニング：チャット履歴の長さを制限する</h3><p>会話が何ターンも続くと、蓄積されたチャット履歴が大きすぎて収まらなくなり、コンテキストがオーバーフローしたり、モデルの注意が散漫になったりします。 </p><p><em>トリミングと</em>は、会話が長くなるにつれて、重要度の低い部分をプログラムで削除または短縮することを指します。単純な形式としては、一定の制限に達したときに会話の最も古いターンを削除し、最新の<em>N</em>件のメッセージのみを保持する方法があります。より高度なプルーニングにより、無関係な余談や不要になった以前の指示を削除することもあります。目標は、<strong>コンテキストウィンドウが古いニュースで煩雑にならないよう</strong>にすることです。 </p><p>例えば、エージェントが10ターン前にサブ問題を解決し、その後先に進んだ場合、コンテキストから履歴のその部分を（それ以上必要ないという前提で）削除することがあります。多くのチャットベースの実装では、最近のメッセージのローリングウィンドウを維持することでこれを実行します。 </p><p>トリミングは、会話の最初の部分を、要約したり、無関係だと判断したりした後に「忘れる」という単純な作業である場合もあります。そうすることで、コンテキストオーバーフローエラーのリスクが軽減され、<a href="https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch#context-distraction"><strong>コンテキストの逸脱</strong></a>も軽減されるため、モデルが古いコンテンツやトピック外のコンテンツを見つけて脇道に逸れることがなくなります。このアプローチは、人間が1時間の講演のすべての単語を覚えているわけではないものの、重要な部分は覚えているということと非常によく似ています。 </p><p>作者のDrew Breunigが<a href="https://www.dbreunig.com/2025/06/26/how-to-fix-your-context.html#tool-loadout:~:text=Provence%20is%20fast%2C%20accurate%2C%20simple%20to%20use%2C%20and%20relatively%20small%20%E2%80%93%20only%201.75%20GB.%20You%20can%20call%20it%20in%20a%20few%20lines%2C%20like%20so%3A">ここ</a>で強調しているように、コンテキストプルーニングについて混乱している場合は、質問応答用の軽量（1.75 GB）、効率的で正確なコンテキストプルーナーであるProvence（`<a href="https://huggingface.co/naver/provence-reranker-debertav3-v1">naver/provence-reranker-debertav3-v1</a>`）モデルを使用すると違いが出る可能性があります。大きな文書を特定のクエリに最も関連性の高いテキストだけに絞り、特定の間隔で呼び出すことができます。</p><p>コード内で`provence-reranker`モデルを呼び出してコンテキストをプルーニングする方法は次のとおりです。</p># Context pruning with Provence
def prune_with_provence(query: str, context: str, threshold: Optional[float] = None) -&gt; str:
    """
    Prune context using Provence reranker model
    
    Args:
        query: User's query/question
        context: Original context to prune
        threshold: Relevance threshold (0-1) for Provence reranker.
                   If None, uses args.pruning_threshold.
                   0.1 = conservative (recommended, no performance drop)
                   0.3-0.5 = moderate to aggressive pruning
    
    Returns:
        Pruned context with only relevant sentences
    """
    if provence_model is None:
        return context
    
    if threshold is None:
        threshold = args.pruning_threshold
    
    try:
        # Use Provence's process method
        provence_output = provence_model.process(
            question=query,
            context=context,
            threshold=threshold,
            always_select_title=False,
            enable_warnings=False
        )
        
        # Extract pruned context from output
        pruned_context = provence_output.get('pruned_context', context)
        reranking_score = provence_output.get('reranking_score', 0.0)
        
        # Log statistics
        original_length = len(context)
        pruned_length = len(pruned_context)
        reduction_pct = ((original_length - pruned_length) / original_length * 100) if original_length &gt; 0 else 0
        
        if args.verbose:
            rich.print(f"[cyan]📊 Pruning stats: {pruned_length}/{original_length} chars ({reduction_pct:.1f}% reduction, threshold={threshold:.2f}, rerank_score={reranking_score:.3f})[/cyan]")
        
        return pruned_context if pruned_context else context
        
    except Exception as e:
        rich.print(f"[yellow]⚠️ Error in Provence pruning: {str(e)}[/yellow]")
        rich.print(f"[yellow]⚠️ Falling back to original context[/yellow]")
        return context<p>文の関連性のスコア付けにはProvenceリランカーモデル（`naver/provence-reranker-debertav3-v1`）を使用します。しきい値ベースのフィルタリングにより、関連性しきい値を超える文が保持されます。また、プルーニングが失敗した場合に元のコンテキストに戻るフォールバックメカニズムも導入します。最後に、統計ログが詳細モードで削減率を追跡します。</p><h3>コンテキストの要約：古い情報を完全に削除するのではなく、要約する</h3><p><em>要約</em>はトリミングの相棒です。履歴やナレッジベースが大きくなりすぎた場合は、LLMを使用して重要なポイントの簡単な要約を作成し、上記のコードで実行したように、今後のコンテンツ全体の代わりにその要約を使用できます。</p><p>例えば、AIアシスタントが50ターンの会話を行った場合、51ターン目に50ターンすべてをモデルに送信するのではなく（おそらく収まりません）、システムは1ターン目から40ターン目までを取得し、モデルにそれらを段落に要約させてから、次のプロンプトでその要約と最後の10ターンのみを提供する可能性があります。こうすることで、モデルはすべての詳細を必要とせずに議論内容を把握できます。初期のチャットボットのユーザーは、「これまでの話を要約してくれますか？」と尋ね、要約した内容を新しいセッションで継続することで、これを手動で行っていました。今ではこれを自動化できます。要約はコンテキストウィンドウのスペースを節約するだけでなく、余分な詳細を削除して重要な事実だけを保持することで<strong>コンテキストの逸脱や混乱を</strong>減らすこともできます。</p><p>OpenAIのモデル（どんなLLMでも使用可能）を使って、すべての関連情報を保持しながらコンテキストを凝縮し、冗長性や重複を排除する方法を紹介します。
</p># Context summarization
def summarize_context(query: str, context: str) -&gt; str:
    """
    Summarize context using LLM to reduce duplication and focus on relevant information
    
    Args:
        query: User's query/question
        context: Context to summarize
        
    Returns:
        Summarized context
    """
    try:
        summary_prompt = f"""You are an expert at summarizing conversation context.

Your task: Analyze the provided conversation context and produce a condensed summary that fully answers or supports the user's specific question.

The summary must:
1. Preserve every fact, detail, and information that directly relates to the question
2. Eliminate redundancy and duplicate information
3. Maintain chronological flow when relevant
4. Focus on information that helps answer: "{query}"

Context to summarize:
{context}

Provide a concise summary that preserves all relevant information:"""

        summary = llm.invoke(summary_prompt).content
        
        if args.verbose:
            original_length = len(context)
            summary_length = len(summary)
            reduction_pct = ((original_length - summary_length) / original_length * 100) if original_length &gt; 0 else 0
            rich.print(f"[cyan]📝 Summarization stats: {summary_length}/{original_length} chars ({reduction_pct:.1f}% reduction)[/cyan]")
        
        return summary
        
    except Exception as e:
        rich.print(f"[yellow]⚠️ Error in context summarization: {str(e)}[/yellow]")
        rich.print(f"[yellow]⚠️ Falling back to original context[/yellow]")
        return context<p>重要なのは、コンテキストを要約すると、モデルが些細な詳細や過去のエラーに圧倒されにくくなることです（要約が正確であれば）。 </p><p>しかし、要約は慎重に行わなければなりません。悪い要約は重要な部分を省略したり、エラーを生じさせたりすることがあります。これは本質的にはモデルに対する別のプロンプト（「これを要約してください」）なので、ハルシネーションを起こしたり、ニュアンスが失われたりする可能性があります。ベストプラクティスとしては、段階的に要約し、一部の標準的な事実は要約しないままにしておくことが挙げられます。</p><p>それでも非常に有用であることが証明されています。<a href="https://storage.googleapis.com/deepmind-media/gemini/gemini_v2_5_report.pdf">Geminiエージェントのシナリオでは、</a>約10万トークンごとにコンテキストを要約することが、モデルの繰り返し傾向に対抗する方法でした。要約は会話やデータの圧縮された記憶のように機能します。開発者としては、エージェントに会話履歴や長いドキュメントの要約機能（おそらく小さなLLMまたは専用ルーティン）を定期的に呼び出させることでこれを実装できます。結果の要約はプロンプト内の元のコンテンツに置き換えられます。この戦術は、コンテキストを制限内に保ち、情報を精査するために広く使用されています。</p><h3>コンテキストの隔離：可能な限りコンテキストを隔離する</h3><p>これは複雑なエージェントシステムや多段階のワークフローでより重要です。コンテキストセグメンテーションの考え方は、大きなタスクを、それぞれ独自のコンテキストを持つ小さな独立したタスクに分割し、すべてが含まれる1つの巨大なコンテキストが蓄積されないようにすることです。各サブエージェントまたはサブタスクは、焦点を絞ったコンテキストで問題の一部に取り組み、その後、上位レベルのエージェント、スーパーバイザー、またはコーディネーターが結果を統合します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09d1eac7442aea2b/6a170f42dc55deb10de00ea7/f2de68c3339883d7658e633af3948f29f427e6cf-1600x900.png" alt="コンテキストの隔離" /><p><a href="https://www.anthropic.com/engineering/multi-agent-research-system">Anthropicの研究戦略はそれぞれが異なる質問の側面を調査し、それぞれのコンテキストウィンドウを持つ複数のサブエージェント</a>と、そのサブエージェントから抽出された結果を読み取るリードエージェントを用います。この並列のモジュール方式のアプローチにより、単一のコンテキストウィンドウが肥大化することはありません。また、無関係な情報が混ざる可能性も減り、各スレッドはトピックに沿って進み（コンテキストの混乱がなく）、特定のサブ質問に答えるときに不必要な負担がかかりません。ある意味、それは思考プロセス全体ではなく、結果だけを共有する別々の思考スレッドを実行するようなものです。</p><p>マルチエージェントシステムでは、このアプローチは不可欠です。エージェントAがタスクAを処理し、エージェントBがタスクBを処理する場合、本当に必要な場合を除き、どちらのエージェントも他方のエージェントの完全なコンテキストを使用する理由はありません。代わりに、エージェントは必要な情報だけを交換できます。例えば、エージェントAは、その調査結果の統合された要約をスーパーバイザーエージェントを介してエージェントBに渡すことができますが、各サブエージェントは独自の専用コンテキストスレッドを維持します。この設定では、人間による介入は必要ありません。最小限かつ制御されたコンテキスト共有を備えたツールが有効になっているスーパーバイザーエージェントに依存します。</p><p>にもかかわらず、エージェントやツールが最小限の必要なコンテキストオーバーラップで動作するようにシステムを設計することで、明確さとパフォーマンスを大幅に向上させることができます。これを<strong>AI用のマイクロサービス</strong>と考えると、各コンポーネントがそれぞれのコンテキストを処理し、1 つのモノリシックなコンテキストではなく、制御された方法でコンポーネント間でメッセージを渡すことができます。これらのベストプラクティスは、多くの場合、組み合わせて使用されます。また、これにより、些細な履歴をトリミングしたり、重要な古いメッセージや会話を要約したり、長期的なコンテキストのために詳細なログをElasticsearchにオフロードしたり、必要なときに関連するものを取得して戻したりする柔軟性が得られます。</p><p><a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=While%20some%20models,to%20the%20LLM">ここで</a>述べたように、コンテキストは限られた貴重なリソースであるというのが基本原則です。プロンプト内のすべてのトークンがその価値を生むようにし、出力の品質に貢献させる必要があります。メモリ内の何かが役に立たない場合（さらに悪いことに、積極的に混乱を引き起こしている場合も）、そのメモリは削除、要約、削除する必要があります。</p><p>開発者として、現在の私たちは、コードをプログラムするのと同じようにコンテキストをプログラムし、含める情報、そのフォーマット方法、および省略または更新するタイミングを決定できます。これらのプラクティスに従うことで、LLMエージェントに、前述の障害モードの被害に遭うことなくタスクを実行するために必要なコンテキストを提供できます。その結果、エージェントは必要なことを記憶し、不要なことを忘れ、必要な情報を必要なタイミングで取得できるようになります。</p><h2>まとめ</h2><p>メモリはエージェントに追加するものではなく、設計するものです。短期メモリはエージェントの一時的な作業領域であり、長期メモリはその永続的な知識格納場所です。RAGは2つの間の架け橋であり、Elasticsearchのようなパッシブデータストアを、出力を接地してエージェントを最新の状態に保つことができるアクティブリコールメカニズムに変えます。</p><p>しかし、メモリは両刃の剣です。コンテキストを制御せずに放置すると、ポイズニング、逸脱、混乱、衝突を引き起こし、共有システムではデータ漏洩さえも招く可能性があります。だからこそ、メモリに関する最も重要な作業は「より多くを格納する」のではなく、「よりよくキュレーションする」ことです。選択的に取り出し、積極的にプルーニングし、慎重に要約し、タスクが真に要求する場合を除いて、無関係なコンテキストを混ぜ合わせることを避けます。</p><p>実際には、優れたコンテキストエンジニアリングは、優れたシステム設計のように見えます。具体的には、コンテキストが小さく十分であり、コンポーネント間のインターフェイスが制御され、モデルに実際に表示したい生の状態と精製された状態が明確に区別された状態です。適切に実行すれば、すべてを記憶するエージェントではなく、適切な理由で適切なタイミングで適切なことを記憶するエージェントが得られます。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Someshwaran Mohankumar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3bad6b045392e641/6a170f43a29299c189d010cc/80907fd072e72d6ec902470b449c9f337957a0d7-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Agent BuilderとStrands Agents SDKの使用を開始]]></title>
    <description><![CDATA[Elastic Agent Builderでエージェントを作成する方法を学び、次にStrands Agents SDKで管理されたA2Aプロトコルを介してエージェントを使用する方法を学びましょう。]]></description>
    <content:encoded><![CDATA[<p>AIエージェントのアイデアをお持ちですか？おそらく、データを使って何かを行うことが関係しているでしょう。エージェントが有用なアクションを開始するには、決定を下す必要があり、正しい決定を下すには正しいデータが必要だからです。</p><p>Elastic Agent Builderは、データ接続型AIエージェントを簡単に構築できるようにします。このブログ記事でその方法を説明します。まず、Elasticに格納されているデータにアクセスするMCPツールを使ってエージェントを作成するのに必要なすべてのステップを見ていきましょう。次に、Strands Agents SDKとそのAgent2Agent（A2A）機能を使用してエージェントを操作します。<a href="https://strandsagents.com/">Strands Agents SDK</a>は、望む結果を得るために十分なコードでエージェント向けアプリを構築するマルチエージェントAI開発プラットフォームです。</p><p>AIエージェントを構築しましょう。このエージェントは、RPS+というゲームをプレイします。これは古典的な「じゃんけん」に追加のひねりを加えたもので、プレイヤーにいくつかの追加の選択肢を与えます。</p><h2>要件</h2><p>こちらのブログ記事の手順に従うために必要なものは次のとおりです。</p><ul><li><p>ローカルコンピューターで実行されているテキストエディター</p><ul><li><p>このブログ記事の例では<a href="https://code.visualstudio.com/download">Visual Studio Code</a>を使用します。</p></li></ul></li><li><p>ローカルコンピューターで実行されている<a href="https://www.python.org/downloads/">Python 3.10以上</a></p></li></ul><h2>Serverlessプロジェクトを作成する</h2><p>最初に必要なのは、Elastic Agentビルダーを含むElasticsearch Serverlessプロジェクトです。</p><p><a href="http://cloud.elastic.co/">cloud.elastic.co</a>に移動して新しいElasticsearch Serverlessプロジェクトを作成します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3472edce39ec0b81/6a17060e66c4f93c17f8bf57/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" alt="" /><h2>インデックスを作成してデータを追加する</h2><p>次に、Elasticsearchプロジェクトにデータを追加します。開発者ツールを開き、コマンドを実行して新しいインデックスを作成し、そこにデータを挿入します。トップレベルのナビゲーションメニューから「開発者向けツール」を選択します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaedaa94068c07a17/6a17060f961e697558c4ce5f/f97d5af077504463155655a9e27c171a7f974f71-1600x879.jpg" alt="" /><p>コピーして、以下のPUTコマンドを開発者向けツールコンソールのリクエストインプットエリアに貼り付けてください。この文は「game-docs」という名前のElasticsearchインデックスを作成します。</p>PUT /game-docs
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "content": { 
        "type": "text"
      },
      "filename": { "type": "keyword" },
      "last_modified": { "type": "date" }
    }
  }
}<p>開発者ツールのステートメントの右側に表示される <strong>[リクエストの送信]</strong> ボタンをクリックします。開発者向けツールの対応エリアに<em>game-docs</em>インデックスが作成されたことを確認する通知が表示されるはずです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt430c357b479d93af/6a170611a6c2b98191e79624/be0555a1930e4d4f58b7ed8b669c9b702532ed17-1600x880.jpg" alt="" /><p><em>game-docs</em>という名前のインデックスは、作成中のゲームのデータを保存するのに最適な場所です。ゲームに必要なすべてのデータを含むこのインデックスに、<em>rps+-md</em>という名前のドキュメントを配置しましょう。次のPUTコマンドをコピーして、開発者向けツールコンソールに貼り付けます。</p>PUT /game-docs/_doc/rps+-md
{
  "title": "Rock Paper Scissors +",
  "content": "
# Game Name
RPS+

# Starting Prompt
Let's play RPS+ !
---
What do you choose?

# Game Objects
1. Rock 🪨 👊
2. Paper 📜 🖐
3. Scissors ✄ ✌️
4. Light ☼ 👍
5. Dark Energy ☄ 🫱

# Judgement of Victory
* Rock beats Scissors
  * because rocks break scissors
* Paper beats Rock
  * because paper covers rock
* Scissors beat Paper
  * because scissors cut paper
* Rock beats Light
  * because you can build a rock structure to block out light
* Paper beats Light
  * because knowledge stored in files and paper books helps us understand light
* Light beats Dark Energy
  * because light enables humans to lighten up and laugh in the face of dark energy as it causes the eventual heat death of the universe
* Light beats Scissors
  * because light is needed to use scissors safely
* Dark Energy beats Rock
  * because dark energy rocks more than rocks. It rocks rocks and everything else in its expansion of the universe
* Dark Energy beats Paper
  * because humans, with their knowledge stored in files and paper books, can't explain dark energy 
* Scissors beat Dark Energy
  * because a human running with scissors is darker than dark energy

# Invalid Input
I was hoping for an worthy opponent
  - but alas it appears that time has past
  - but alas there's little time for your todo list when [todo:fix this] is so vast

# Cancel Game
The future belongs to the bold. Goodbye..
",
  "filename": "RPS+.md",
  "last_modified": "2025-11-25T12:00:00Z"
}<p>ステートメントの横にある<strong> [リクエストの送信] </strong>ボタンをクリックして実行し、<em>rps+-md</em>ドキュメントをgame-docsのインデックスに追加してください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt64d49e13754d5b25/6a17061214b270524be3c55d/3c01d8a4602de5c33337457591a388a4a4e3fad3-1600x879.jpg" alt="" /><p>クエリを実行するためのデータが用意されているはずです。Agent Builderを使用すると、クエリはこれまで以上に簡単になります。</p><p>トップレベルのナビゲーションメニューから<strong> [エージェント]</strong> を選択します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4d374bf2ba9135c/6a1706147d8d67468570e63e/82dbd2e9a439cabd5a5eea3d0ce005b87df0c3ea-1600x879.jpg" alt="" /><p>あとは、デフォルトのElastic AI Agentに「どんなデータがありますか？」と聞くだけです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0f879cf28772718/6a1706161949f7f25ee7a92d/f7a2f39c9d1486bdf02d9e88a732b540ac2e2cd1-1600x872.gif" alt="" /><p>Elastic AI Agentはデータを評価し、保有するデータの簡潔な説明を返します。</p><h2>ツールを作成する</h2><p>さて、Elasticにデータがいくつか入ったので、それを活用してみましょう。Agent Builderには、エージェントが必要なデータにアクセスし、タスクに適したコンテキストを得られるように<a href="https://modelcontextprotocol.io/">MCP</a>ツールを作成するための組み込みサポートが含まれています。ゲームデータを取得できるシンプルなツールを作りましょう。</p><p>Agent Builderのアクションメニューをクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7802a6b94e81440c/6a170618ab7f085287db9db4/0e327c202674dda33bcc0e494d2b588fa8b32e4f-1600x879.png" alt="" /><p>メニューオプションから <strong>[すべてのツールを表示]</strong> を選択します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f52ffe114fb6ea7/6a17061a4a531b801b36a884/1ebf58650e9fb56750d3f0b1700fab50b44f9bdf-1600x879.png" alt="" /><p><strong>[+ 新しいツール] </strong>をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8090769f6c4d1899/6a17061c286714294093e219/6c03a7f28b99ac2d805f34f39948979893316a00-1600x879.png" alt="" /><p><strong>ツール作成</strong>フォームで<a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql"><strong>ES|QL</strong></a>を選択します。ツール<strong>タイプ</strong>として次の値を入力します。</p><p><strong>ツールID</strong>について：</p>example.get_game_docs<p><strong>説明</strong>について：</p>Get RPS+ doc from Elasticsearch game-docs index.<p><strong>構成</strong>については、以下のクエリを<strong>ES|QLクエリ</strong>テキスト領域にします。</p>FROM game-docs | WHERE filename == "RPS+.md"<p>完了した<strong>ツール作成</strong>フォームは次のようになります。ツールを作成するには、 <strong>[保存]</strong> をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77034c305198217a/6a17061e66c4f9e54ef8bf5e/b6c93e344600f319b9d2c3030020cf2d171ac1c4-1600x1312.png" alt="" /><p>ツールラックに新しいツールが追加されました。ツールはラックに掛けておけばよいというものではなく、有効に活用されるべきものです。新しいカスタムツールを使用できるエージェントを作成しましょう。</p><h2>エージェントを作成し、ツールを割り当てます。</h2><p>Agent Builderを使えば、エージェントの作成は驚くほど簡単です。いくつかの詳細を記載したエージェントの指示を入力するだけで十分です。それではエージェントを作成しましょう。</p><p><strong>[エージェントを管理]</strong> をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaa8a83fc2f3758a9/6a1706201949f71a10e7a931/53934b93db07187e251d4b321cb9ca647e2fd51b-1600x858.png" alt="" /><p><strong>[+ 新しいエージェント] </strong>をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3778403c5101a000/6a17062160084be12f3c449e/fae3ad8f31e71a6dfd044e1daa025a4e280b4e68-1600x490.png" alt="" /><p><strong>新しいエージェント</strong>フォームに次の情報を入力します。</p><p><strong>エージェントID</strong>には以下のテキストを入力します。</p>rps_plus_agent<p><strong>カスタム指示</strong>テキスト領域には次の指示を入力します。</p>When prompted, if the prompt contains an integer, then select the corresponding numbered item in the list of "Game Objects" from your documents. Otherwise select a random game object. This is your chosen game object for a single round of the game.

# General Game Rules
* 2 players
    - the user: the person playing the game
    - you: the agent playing the game and serving as the game master
* Each player chooses a game object which will be compared and cause them to tie, win or lose.

# Start the game
1. This is the way each new game always starts. You make the first line of your response only the name of your chosen game object. 

2. The remainder of your response should be the "Starting Prompt" text from your documents and generate a list of "Game Objects" for the person playing the game to choose a game object from.  

# End of Game: The game ends in one of the following three outcomes:
1. Invalid Input: If the player responds with an invalid game object choice, respond with variations of the "Invalid Input" text from your documents and then end the game.

2. Tie: The game ends in a tie if the user chooses the same game object as your game object choice.

3. Win or Lose: The game winner is decided based on the "Judgement of Victory" conditions from your documents. Compare the user's game object choice and your game object choice and determine who chose the winning game object.

# Game conclusion
Respond with a declaration of the winner of the game by outputting the corresponding text in the "Judgement of Victory" section of your documents.<p><strong>表示名</strong>には以下のテキストを入力します。</p>RPS+ Agent<p><strong>表示の説明</strong>には以下のテキストを入力します。</p>An agent that plays the game RPS+<p><strong>[ツール]</strong> タブをクリックして、以前に作成したカスタムツールをエージェントに提供します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5b0fe00abdde07c/6a17062314b2704bc4e3c563/1778f64bc3a1b4004998dc3668ef7f666788e193-1600x1390.png" alt="" /><p>先ほど作成した<em>example.get_game_docs</em>ツールのみを選択します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2210212e07e06104/6a170625a929cf3277ae08d1/7d734cd80161bcc058817482eb330ffcf1cb567b-1600x1363.png" alt="" /><p><strong>[保存]</strong> をクリックして新しいエージェントを作成します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e3afc1918e26f14/6a170627ab7f084746db9db8/c0014faf605ce50c03679ed0d073bd9f3ae7234d-1600x468.png" alt="" /><p>新しいエージェントをテストしてみましょう。エージェントのリストから任意のエージェントとチャットを開始するための便利なリンクがあります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb4b69dc5971d3a0/6a1706286f7f046840914743/b7d6943ad90a4f68691207caf66b81742e712145-1600x560.png" alt="" /><p>「start game」と入力すると、ゲームが始まります。うまくいきました！</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5b621d602223dff/6a17062ab339d568a1769ef8/984d008e4cc3f08cc1f101720673b0f7347c066c-1600x874.gif" alt="" /><p>エージェントが応答の上部にゲームオブジェクトの選択を表示することがわかります。これは、エージェントの選択を確認し、ゲームが期待どおりに機能していることを確認できる点で便利です。しかし、自分が選択する前に相手の選択がわかっていると、じゃんけんゲームはあまり楽しくありません。ゲームを最終形に磨き上げるために、コードでエージェントを制御できるエージェントオーケストレーションプラットフォームを使用できます。</p><p>Strands Agents SDKがチャットに参加します。</p><h2>Strands Agents SDK</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73901ec745a97fbf/6a17062c964cea23c808bab3/c195bba6ff2754f5d8fda174a0c1d247bc283710-456x156.png" alt="" /><p>新しいエージェント開発フレームワークを試してみたい場合は、<a href="https://strandsagents.com/latest/">Strands Agents SDK</a>がおすすめです。<a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/">Strands Agents SDKはAWSから2025年5月に</a>オープンソースの<a href="https://github.com/strands-agents/sdk-python">Python</a>実装としてリリースされ、現在は<a href="https://dev.to/aws/strands-agents-now-speaks-typescript-a-side-by-side-guide-12b3">Typescript</a>版もあります。</p><h2>PythonでStrands Agents SDKの使用を開始</h2><p>コーディングエンジンを起動して、Strandsエージェントを使用してA2Aプロトコル経由で<em>RPS+エージェント</em>を制御するサンプルアプリのクローン作成と実行のプロセスを早速実行してみましょう。RPS+ゲームの微調整バージョンを作成し、エージェントの選択がプレイヤーの選択後に明らかになるようにしてみましょう。結局のところ、じゃんけんのようなゲームを楽しいものにするのは推測と驚きの結果だからです。</p><p>ローカルコンピューターで<a href="https://code.visualstudio.com/download">Visual Studio Code</a>を開き、新しいターミナルを開きます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3de752025d62993f/6a17062d0c4857f16501a997/2339cc37c89a3524f2b2a21684bc61dae958e1cf-915x460.jpg" alt="" /><p>新しく開いたターミナルで、以下のコマンドを実行してElasticsearch Labsリポジトリをクローンします。</p>git clone https://github.com/elastic/elasticsearch-labs<p>次の<em>cd</em>コマンドを実行して、ディレクトリをelasticsearch-labsディレクトリに変更します。</p>cd elasticsearch-labs<p>次に、次のコマンドを実行して、Visual Studio Codeでリポジトリを開きます。</p>code .<p>Visual Studio File Explorerで、<em>supporting-blog-content</em>フォルダーと<em>agent-builder-a2a-strands-agents</em>フォルダーを展開し、<em>elastic_agent_builder_a2a_rps+.py</em>ファイルを開きます。Visual Studio Codeで開いたファイルは次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65ef8036a70bcaf1/6a17062f1949f7af36e7a935/d153b19e0e016c701576edb99ccab5af7c554f34-1484x1530.jpg" alt="" /><p>テキストエディターに表示される<em>elastic_agent_builder_a2a_rps+.py</em>の内容は次のとおりです。</p>import asyncio
from dotenv import load_dotenv
from uuid import uuid4
import httpx
import os
import random
from a2a.client import A2ACardResolver, ClientConfig, ClientFactory
from a2a.types import Message, Part, Role, TextPart

DEFAULT_TIMEOUT = 60  # set request timeout to 1 minute


def create_message(*, role: Role = Role.user, text: str, context_id=None) -&gt; Message:
    return Message(
        kind="message",
        role="user",
        parts=[Part(TextPart(kind="text", text=text))],
        message_id=uuid4().hex,
        context_id=context_id,
    )


async def main():
    load_dotenv()
    a2a_agent_host = os.getenv("ES_AGENT_URL")
    a2a_agent_key = os.getenv("ES_API_KEY")
    custom_headers = {"Authorization": f"ApiKey {a2a_agent_key}"}

    async with httpx.AsyncClient(
        timeout=DEFAULT_TIMEOUT, headers=custom_headers
    ) as httpx_client:
        # Get agent card
        resolver = A2ACardResolver(httpx_client=httpx_client, base_url=a2a_agent_host)
        agent_card = await resolver.get_agent_card(
            relative_card_path="/rps_plus_agent.json"
        )
        # Create client using factory
        config = ClientConfig(
            httpx_client=httpx_client,
            streaming=True,
        )
        factory = ClientFactory(config)
        client = factory.create(agent_card)
        # Use the client to communicate with the agent
        print("\nSending 'start game' message to Elastic A2A agent...")
        random_game_object = random.randint(1, 5)
        msg = create_message(text=f"start with game object {random_game_object}")
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                context_id = event.context_id
                response_complete = event.parts[0].root.text
                # Get agent choice from the first line of the response
                parsed_response = response_complete.split("\n", 1)
                agent_choice = parsed_response[0]
                print(parsed_response[1])
        # User choice sent for game results from the agent
        prompt = input("Your Choice  : ")
        msg = create_message(text=prompt, context_id=context_id)
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                print(f"Agent Choice : {agent_choice}")
                print(event.parts[0].root.text)


if __name__ == "__main__":
    asyncio.run(main())<p>このコードで何が起きているのか見てみましょう。<em><code>main()</code></em>メソッドから始めて、コードはエージェントのURLとAPIキーの環境変数にアクセスすることから始まります。その値を用いてエージェントカードを取得するための<em><code>httpx</code></em><code> client</code>を作成します。次に、クライアントはエージェントカードの詳細を使用して、「start game」リクエストをエージェントに送信します。ここで注目すべき興味深い点は、 <code>"start game"</code>リクエストの一部として<code>random_game_object</code>値が含まれていることです。この値は、Python の標準ライブラリの<em>random</em>モジュールで生成された乱数です。これを行う理由は、（AIエージェントを可能にする）強力なLLMがランダム性に関してはそれほど優れていないことが判明したためです。Pythonが助けてくれますので問題ありません。</p><p>コードの続きですが、エージェントが「start game」リクエストに応答すると、コードはエージェントのゲームオブジェクトセレクションを取り除き、<em>agent_choice</em>変数に保存します。対応の残りの部分は、エンドユーザーに対してテキストとして表示されます。次に、ユーザーはゲームオブジェクトの選択を入力するように求められ、それがエージェントに送信されます。次に、コードはエージェントのゲームオブジェクトの選択と、エージェントの最終的なゲーム結果の決定を表示します。</p><h2>エージェントのURLとAPIキーを環境変数として設定する</h2><p>サンプルアプリはローカルコンピュータ上で実行されるため、Agent Builderエージェントと通信するためには、Strands Agents SDKにエージェントのA2A URLとAPI Keyを提供する必要があります。この例のアプリは<em>`.env`</em>というファイルを使用してこれらの値を格納します。</p><p><em>env.example</em>ファイルのコピーを作成し、新しいファイル名を<em>.env</em>とします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta17961cbcb42985c/6a170631b0367dc5a072bc55/25ead5f15a17dedb777132a082097cffb06cae4d-1600x843.jpg" alt="" /><p>Elastic Agent Builderに戻りましょう。ここで必要な両方の値を取得できます。</p><p>ページの右上にあるAgent Builderアクションメニューから <strong>[すべてのツールを表示]</strong> を選択します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt140885d7ebfcb969/6a1706327d8d67b17670e646/9c4f4e4a3bd76e11e0a182fa007a2f6aec7777b4-1600x880.jpg" alt="" /><p>ツールページ上部の<strong>MCPサーバー</strong>ドロップダウンをクリックし、<strong>[MCPサーバーURLをコピー] </strong>を選択してください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc153c2caa27e949b/6a170634a292997793d00f6d/6cde0de678bb6f81bef8a59deffb110ad6c6ce26-1600x882.jpg" alt="" /><p><strong>MCPサーバーのURL</strong>を<em>.env</em>に貼り付けます。<strong>&lt;YOUR-ELASTIC-AGENT-BUILDER-URL&gt;</strong>プレースホルダー値の代わりにファイルを使用します。ここで、URLを1箇所更新する必要があります。つまり、末尾のテキスト「mcp」を「a2a」に置き換えます。これは、Agent Strands SDKがElastic Agent Builderで実行されているエージェントと通信するために使用するプロトコルが<a href="https://a2a-protocol.org/">A2Aプロトコル</a>であるためです。</p><p>編集したURLは次のようになるはずです。</p>https://rps-game-project-12345a.kb.us-east-1.aws.elastic.cloud/api/agent_builder/a2a<p>Elastic Cloudで取得する必要があるもう1つの値は、APIキーです。最上位ナビゲーションで<strong>Elasticsearch</strong>をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltada5de819f31d8ff/6a170635b339d55ae9769efc/651676b9be65178cdad50b5d24f26441c0bf3f97-1600x549.jpg" alt="" /><p><strong>[APIキーをコピー] ボタン</strong>をクリックして、APIキーをコピーします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta18f85790df00706/6a170637cf4f257145b2d0bd/17f1e2ed5c7682630c71e75b0b09ffb1d9036210-1600x879.jpg" alt="" /><p>次に、Visual Studio Codeに戻り、<em>.env</em>ファイルにAPIキーを貼り付けて、<strong>&lt;YOUR-ELASTIC-API-KEY&gt;</strong>プレースホルダーテキストを置き換えます。<em>.env</em>ファイルは次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92ab4b37cdcca85e/6a1706386f7f0472ed914747/a357947e07f29c8c03382e00c7baedf04a399297-1600x286.jpg" alt="" /><h2>サンプルアプリを実行してください</h2><p>Visual Studio Codeで新しいターミナルを開いてください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8702d826849755d0/6a17063a60084b45ca3c44a2/33e1174c68ea1ed47c7fe62ab6a6da657c606f56-1413x711.jpg" alt="" /><p>まず、ターミナルで次の<em>cd</em>コマンドを実行します。</p>cd elasticsearch-labs/supporting-blog-content/agent-builder-a2a-strands-agents<p>次のコマンドを実行して、Python仮想環境を作成します。</p>python -m venv .venv<p>お使いのローカルコンピューターのオペレーティングシステムに応じて、以下のコマンドを実行して仮想環境を有効にしてください。</p><ul><li><p>MacOS/Linux</p></li></ul>source .venv/bin/activate<ul><li><p>Windows</p></li></ul>.venv\Scripts\activate<p>サンプルアプリはStrands Agents SDKを使用するため、このチュートリアルではこれをインストールする必要があります。以下のコマンドを実行して、Strands Agents SDKとその必要なPythonライブラリの依存関係をインストールします。</p>pip install -r requirements.txt<p>発射台を片付けてカウントダウンを開始する時間です。アプリを起動する準備ができました。後ろに下がってください。次のコマンドを使用して実行しましょう：</p>python elastic_agent_builder_a2a_rps+.py<p>RPS+のゲームに挑戦してみましょう。幸運を祈ります！</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbb3715672995fcfa/6a17063c6234e07b76db195f/041df81fbf1776f09e1243af0a435c4c0af6aca1-1600x948.gif" alt="" /><h2>関連コンテキストでAIアプリを構築</h2><p>AIエージェントの構築のスキルを習得できました。また、Strands Agents SDKのようなエージェント開発フレームワークで、A2Aを介してElastic Agent Builderエージェントを使用することがいかに簡単であるかをお分かりいただけたと思います。カスタムデータの関連コンテキストに接続されたAIエージェントの構築には<a href="https://cloud.elastic.co/registration?utm_source=agentic-ai-category&amp;utm_medium=search-labs&amp;utm_campaign=agent-builder">Elasticをお試し</a>ください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-a2a-strands-agents-guide</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-a2a-strands-agents-guide</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3472edce39ec0b81/6a17060e66c4f93c17f8bf57/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" length="0" type="image/gif"/>
    <pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Google MCP Toolbox for DatabasesにElasticsearchサポートを導入]]></title>
    <description><![CDATA[Google MCP Toolbox for Databasesで利用可能になったElasticsearchサポートの詳細を確認し、ES|QLツールを活用してインデックスを任意の MCP クライアントと安全に統合します。]]></description>
    <content:encoded><![CDATA[<p>この記事では、Google MCP Toolboxと<a href="https://github.com/elastic/elasticsearch">Elasticsearch</a>を併用し、Elasticsearchインデックスから情報を抽出する簡単なツールを構築する方法を解説します。</p><p>当社は最近、<a href="https://github.com/googleapis/genai-toolbox">Google MCP Toolbox for Databases</a>のオープンソースプロジェクトに貢献し、Elasticsearchをデータベースとしてサポートしました。</p><p>この新しい機能により、Google MCP Toolboxを使用してElasticsearchに接続し、データと直接「会話」できるようになりました。</p><h2>Elasticsearch</h2><p>Elasticsearchインスタンスを実行する必要があります。<a href="https://www.elastic.co/cloud">Elastic Cloud</a>で無料トライアルを有効化するか、<a href="https://github.com/elastic/start-local">start-local</a>スクリプトを使ってローカルにインストールできます。</p>curl -fsSL https://elastic.co/start-local | sh<p>これにより、ElasticsearchとKibanaがコンピュータにインストールされ、Google MCP Toolboxの設定に使用するAPIキーが生成されます。</p><p>APIキーは前のコマンドの出力として表示され、elastic-start-localフォルダー内の.envファイルに保存されます。</p><h2>サンプルデータセットをインストールする</h2><p>インストール後、ユーザー名<em>elastic</em>とstart-localスクリプトによって生成されたパスワード（.envファイルに保存）を使用してKibanaにログインできます。</p><p>Kibanaから入手可能な<strong>eCommerce orders</strong>データをインストールできます。このデータベースには、eコマースWebサイトからの4,675件の注文に関する情報を含む<strong>kibana_sample_data_ecommerce</strong>という単一のインデックスが含まれています。各注文について、次の情報があります。</p><ul><li><p>顧客情報（氏名、ID、生年月日、メールアドレスなど）</p></li><li><p>注文日</p></li><li><p>注文ID</p></li><li><p>商品（価格、数量、ID、カテゴリー、割引などを含む全商品のリスト）</p></li><li><p>SKU</p></li><li><p>合計金額（税抜、税込）</p></li><li><p>合計数量</p></li><li><p>地理情報（都市、国、大陸、場所、地域）</p></li></ul><p>サンプルデータをインストールするには、Kibanaの<strong>統合</strong>ページを開き（検索トップバーで「Integration」を検索）、「Sample Data」をインストールしてください。詳細については、ドキュメント<a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana</a>を参照してください。</p><p>この記事の目的は、Google MCP ToolboxがElasticsearchに接続し、自然言語で<strong>kibana_sample_data_ecommerce</strong>インデックスとやり取りするのがいかに簡単かを示すことです。</p><h2>Google MCP Toolbox</h2><p>Google MCP ToolboxはオープンソースのMCPサーバーで、アプリケーションやAIエージェントが安全かつ効率的にデータベースとやり取りできるように設計されています。以前は「GenAI Toolbox for Databases」と呼ばれていたこのプロジェクトは、<a href="https://www.anthropic.com/news/model-context-protocol">モデルコンテキストプロトコル</a>（MCP）との完全な互換性を採用した後に改名されました。その目的は、エージェントをデータベースに接続する際に従来必要とされていた接続プーリング、認証、オブザーバビリティ、その他の運用上の懸念をバックエンドで処理することで、重労働を排除することです。</p><p>Toolboxの本質は、開発者がデータベースのやり取りをカプセル化する再利用可能な高レベルのツールを定義できるようにすることです。これらのツールは、AIエージェントなどのMCP互換クライアントならどれでも起動できます。クライアントが低レベルのSQLクエリを実装したり、データベース接続を管理したりする必要はありません。このアプローチにより、データベース対応エージェントの構築に必要な定型コードの量が大幅に削減され、わずか数行のアプリケーションロジックに高度なデータ操作を統合できるようになります。ツールが定義されると、複数のエージェント、フレームワーク、言語間で共有できます（図1）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte90070297ee83546/6a16fa29964cea694b08b972/137cea290bb70ad5da21853f9a6358cef4cf7451-1248x1056.png" alt="" /><p>Toolboxを使用する大きな利点は、組み込まれたセキュリティモデルです。OAuth2やOIDCなどの認証フローはネイティブにサポートされているため、開発者はデータベースの機密認証情報をエージェントで処理したり格納したりする必要がありません。このプラットフォームは、デバッグ、監視、本番環境への導入に不可欠な、OpenTelemetryによる指標やトレースなどの観測機能も提供します。全体として、MCP Toolboxは、あらゆるMCP対応システムのデータを操作するための、統一された安全で拡張可能なインターフェースとして機能します。</p><h2>MCP Toolboxのインストール方法</h2><p>MCP ToolboxサーバーをLinuxにインストールするには、次のコマンドを使用します。</p>export VERSION=0.21.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox<p>macOSまたはWindowsにインストールする場合は、<a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/#installing-the-server">ここに</a>記載されている手順に従ってください。</p><h2>Elasticsearch向けにToolboxを構成する</h2><p>Elasticsearch向けにMCP Toolboxを構成するには、次のように<strong>tools.yaml</strong>ファイルを作成する必要があります。</p>sources:
  my-cluster:
    kind: elasticsearch
    addresses:
      - http://localhost:9200
    apikey: &lt;insert-here-api-key&gt;

tools:
  customer-orders:
    kind: elasticsearch-esql
    source: my-cluster
    description: Get the orders made by a customer identified by name.
    query: |
    	FROM kibana_sample_data_ecommerce | WHERE MATCH(customer_full_name, ?name, {"operator": "AND"})
    parameters:
      - name: name
        type: string
        description: The customer name.

toolsets:
  elasticsearch-tools:
    - customer-orders<p><strong>&lt;insert-here-api-key&gt;</strong>値を有効なElasticsearch APIキーに置き換える必要があります。start-localを使用してElasticsearchをローカルで実行している場合は、start-localによって生成された.envファイルの<strong>ES_LOCAL_API_KEY</strong>変数の下にAPIキーがあります。Elastic Cloudを使用している場合は<a href="https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys">ここで</a>説明した手順に従うことでAPIキーを生成できます。</p><p>前のツールには、Elasticsearch用の次のES|QLクエリが含まれています。</p><p>ES|QLに慣れていない方のために説明すると、ES|QLはSQLと同様にElasticが開発したクエリ言語で、1つ以上のインデックスを検索するために使用できます。ES|QLの詳細については<a href="https://www.elastic.co/docs/reference/query-languages/esql">こちらの</a>公式ドキュメントをご覧ください。</p><p>上記のクエリは、<strong>kibana_sample_data_ecommerce</strong>インデックスに格納されている指定顧客名を含むすべての注文を<strong>?name</strong>パラメーター（疑問符はパラメーターを示します）を用いて検索します。</p><p>顧客名は、以前のYAML設定で文字列型と「顧客名」という記述で定義されています。</p><p>このツールを使用すると、顧客の注文に関する質問に答えることができます。たとえば、<em>「顧客Fooは2025年10月に何件の注文をしましたか？」</em></p><p>ツールとそのパラメーターの説明は、ユーザーの自然言語リクエストから関連情報を抽出するために不可欠です。この抽出は、大規模言語モデル（LLM）の<strong>関数呼び出し</strong>機能を使用して実行されます。実際には、LLMは、必要な情報を取得するためにどの機能（ツール）を実行する必要があるかを判断し、その機能に適したパラメーターも取得できます。</p><p>詳細については、<a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic">Elasticsearchを使用したOpenAIの関数呼び出し</a>に関するAshish Tiwariの記事を読むことをお勧めします。</p><h2>Toolboxサーバーを実行する</h2><p>次のコマンドで、以前のtools.yamlファイルを使用してMCPツールボックスを実行できます。</p>./toolbox --tools-file tools.yaml --ui<p><strong> –ui</strong>パラメーターは<a href="http://127.0.0.1:5000/ui">http://127.0.0.1:5000/ui</a>のウェブアプリケーションを実行します（図2）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0fb3953fae603572/6a16fa2aa6c2b92763e794fb/3caf2339b632bafd5847af1ed8b33b518a25b8a2-1600x314.png" alt="" /><p><strong>[ツール]</strong> &gt; <strong>[customer-orders]</strong> を選択し、パラメータ<strong>名</strong>に顧客名（例：Gwen Sanders）を挿入して <strong>[ツールを実行]</strong> ボタンをクリックします。図3に示すように、JSON応答が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca02df8c78cb39ff/6a16fa2c961e6909b5c4cd22/b167e0142afb8919d9cedf6d0fa431d33d0e55f8-1600x933.png" alt="" /><p>セットアップが完了すると、MCP Toolboxは<strong>customer-orders</strong>ツールを実行してElasticsearchと通信し、ES|QLクエリを実行できるようになります。</p><h2>Gemini CLIでのMCP Toolboxの使用</h2><p>任意のMCPクライアントを使用して、MCP Toolbox for Databasesと通信できます。例えば、<a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a>というコマンドラインツールを使ってGeminiを使うことができます。Gemini CLIのインストールは、<a href="https://geminicli.com/docs/get-started/installation/">こちら</a>の手順に従って行うことができます。</p><p>Gemini CLIは、MCP Toolbox用の事前設定された拡張機能を提供しており、<a href="https://github.com/gemini-cli-extensions/mcp-toolbox">gemini-cli-extensions/mcp-toolbox</a>で入手できます。この拡張機能は次のコマンドを実行してインストールできます。</p>gemini extensions install https://github.com/gemini-cli-extensions/mcp-toolbox<p>インストール後、MCP Toolbox用のtools.yaml設定ファイルを格納したディレクトリに移動し、以下のようにGemini CLIを実行する必要があります（この手順は、Gemini CLIをMCP Toolboxで自動的に設定するために必要です）。</p>gemini<p>図4に示すように出力広告が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf7245c10b9e32cd6/6a16fa2d964cea073208b976/0f22df6d3da13c1dc50dcb560414fa7c630eb9a7-1434x341.png" alt="" /><p>次のコマンドを使用して、MCP Toolboxが接続されているかどうかを確認できます。</p>/mcp list<p><strong>mcp_toolbox</strong>と<strong>customer-orders</strong> ツールが一覧に表示されているはずです（図5）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte857b42dbe604203/6a16fa2f8b73cb531b189e33/97edbc40de9e44f469f6f3a09427532be167de0e-493x155.png" alt="" /><p>MCP ToolboxがGemini CLI に接続されている場合は、「<em>顧客Gwen Sandersの注文を教えてください</em>」などの質問をいくつか試すことができます。Gemini CLIは、mcp_toolboxサーバーからcustomer-ordersツールを実行する許可を要求します（図6を参照）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfb7ea752c1a39da7/6a16fa30cdacbfdf937d27ff/c052f3b5e49436903b804280c0065f67ee02444b-1432x284.png" alt="" /><p>確認後、Gemini CLIはMCP Toolboxへのリクエストを実行し、結果としてJSON応答を取得し、それを使用して応答をフォーマットします（図7）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9f6e04987137a9f/6a16fa320811ae2297e9fef7/7ea5128f1705951c2757af6da4b456d394d4a080-1432x734.png" alt="" /><p>Gemini CLIからの応答で、Gwen Sandersが2つの製品を1回の注文で、合計132ユーロの価格で購入したことがレポートされます。</p><h2>MCP Toolbox SDKs</h2><p>Google MCP Toolboxは、Go、Python、Javascriptで書かれたプログラムからすべての機能にアクセスするためのSDKも提供しています。</p><p>例えば、Python SDKはGithubの次のページ<a href="https://github.com/googleapis/mcp-toolbox-sdk-python">https://github.com/googleapis/mcp-toolbox-sdk-python</a>で入手可能です。</p><p>MCP Toolboxに接続するための簡単なエージェントを作成する必要があります。次のパッケージをインストールする必要があります。</p>pip install toolbox-core
pip install google-adk<p>次のコマンドを使用して、新しいエージェントプロジェクトを作成します。</p>adk create my_agent<p>これにより、ファイル<strong>agent.py</strong>を持つ新しいディレクトリが<strong>my_agent</strong>として作成されます。</p><p>Toolboxに接続するには、次の内容で<strong>my_agent/agent.py</strong>を更新します。</p>from google.adk import Agent
from google.adk.apps import App
from toolbox_core import ToolboxSyncClient

client = ToolboxSyncClient("http://127.0.0.1:5000")

root_agent = Agent(
    name='root_agent',
    model='gemini-2.5-flash',
    instruction="You are a helpful AI assistant designed to search information about a dataset of ecommerce orders.",
    tools=client.load_toolset(),
)

app = App(root_agent=root_agent, name="my_agent")<p>Google APIキーを使用して<strong>.env</strong>ファイルを作成します。</p>echo 'GOOGLE_API_KEY="YOUR_API_KEY"' &gt; my_agent/.env<p>最後に、エージェントを実行して結果を確認します。エージェントを実行するには、次のコマンドを実行します。</p>adk run my_agent<p>または、Webインターフェース経由で提供することもできます。</p>adk web --port 8000<p>両方の場合において、Q&amp;Aインターフェースを使用してMCP Toolboxと対話することができます。たとえば、先程の質問「<em>顧客Gwen Sandersの注文を教えてください</em>」をすることができます。</p><p>さまざまなSDKの詳細については、<a href="https://googleapis.github.io/genai-toolbox/sdks/">このドキュメントページ</a>をご参照ください。</p><h2>まとめ</h2><p>この記事では、Google MCP Toolbox for DatabasesのElasticsearch統合について説明しました。シンプルなYAML設定ファイルを使用して、自然言語の質問をES|QL言語を使用してElasticsearchクエリに変換する一連のツールを定義できます。</p><p>eコマースWebサイトからの注文を含むkibana_sample_data_ecommerceデータセットとの対話方法を示しました。この設定ファイルを使用すると、MCP Toolboxサーバーを簡単に実行し、任意のMCPクライアントから接続できます。</p><p>最後に、Gemini CLIをクライアントとして使用してMCP Toolbox for Databasesに接続し、Elasticsearchに保存されているeコマースデータをクエリする方法を示しました。特定の顧客の名前で識別された注文情報を取得するために自然言語クエリを実行しました。</p><p>MCPエコシステムが成長し続けるにつれて、このパターン（安全で本番環境ですぐに使えるインフラストラクチャーに裏打ちされた軽量なツール定義）は、最小限の労力で、ますます有能でデータを認識するエージェントを構築する新しい機会を生み出します。MCP Toolboxは、Elasticのサンプルデータセットを使ってローカルで実験する場合でも、大規模なアプリケーションに検索機能を統合する場合でも、自然言語を使ってElasticsearchのデータを操作するための、信頼性と拡張性に優れた基盤を提供します。</p><p>エージェントAIアプリケーションの開発の詳細については、Anish MathurとDana Juratoniによる記事<a href="https://search-labs-redesign.vercel.app/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">「Elasticsearchを使用したAI エージェントワークフローの構築」</a>をお読みください。</p><p>Google MCP Toolboxの詳細については、<a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/">https://googleapis.github.io/genai-toolbox/getting-started/introduction/</a>をご覧ください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/google-mcp-toolbox-elasticsearch-support</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/google-mcp-toolbox-elasticsearch-support</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Enrico Zimuel,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72d49893c51407cf/6a16fa33cf4f2502bab2cf7e/425a48691f436ed47c9bdfaf5d561ac122b2c472-1062x668.png" length="0" type="image/png"/>
    <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LangGraph.jsとElasticsearchを使用して金融AI検索ワークフローを構築]]></title>
    <description><![CDATA[LangGraph.jsとElasticsearchを使用して、自然言語クエリを投資や市場分析のための動的な条件付きフィルターに変換するAIを活用した金融検索ワークフローを構築する方法を学びます。]]></description>
    <content:encoded><![CDATA[<p>AI検索アプリケーションの構築では、多くの場合、複数のタスク、データ取得、データ抽出をシームレスなワークフローに調整する必要があります。LangGraphは、開発者がnodeベースの構造を使用してAIエージェントを管理することで、このプロセスを簡素化します。この記事では、<a href="https://langchain-ai.github.io/langgraphjs/">LangGraph.js</a>を使用して金融ソリューションを構築します。</p><h2>LangGraphの概要</h2><p><a href="https://langchain-ai.github.io/langgraphjs/">LangGraph</a>は、AIエージェントを構築し、ワークフロー内で管理してAI支援アプリケーションを作成するためのフレームワークです。LangGraphには、タスクを表す関数を宣言し、それらをワークフローのノードとして割り当てることができるノードアーキテクチャがあります。複数のノードが相互作用した結果がグラフになります。LangGraphは、モジュール式かつ構成可能なAIシステムを構築するためのツールを提供する、より広範な<a href="https://js.langchain.com/docs/introduction/">LangChain</a>エコシステムの一部です。</p><p>LangGraphが有用である理由をより深く理解するために、LangGraphを使用して問題のある状況を解決してみましょう。</p><h2>ソリューションの概要</h2><p>ベンチャーキャピタル企業では、投資家は多くのフィルタリングオプションを備えた大規模なデータベースにアクセスできますが、基準を組み合わせたい場合には困難で時間がかかります。これにより、関連するスタートアップの一部が投資対象として見つからない可能性があります。その結果、最適な候補を見つけるために多くの時間を費やしたり、機会を逃したりすることになります。</p><p>LangGraphとElasticsearchを使用することで、自然言語を用いてフィルターで検索することが可能となり、ユーザーが手動で複雑なリクエストを何十ものフィルターで構築する必要がなくなります。柔軟性を高めるために、ワークフローはユーザーの入力に基づいて2つのクエリタイプを自動的に決定します。</p><ul><li><p><strong>投資に焦点を当てたクエリ</strong>：スタートアップ企業の財務および資金調達の側面を対象としており、<a href="https://www.investopedia.com/articles/personal-finance/102015/series-b-c-funding-what-it-all-means-and-how-it-works.asp">資金調達ラウンド</a>、バリュエーション、<a href="https://www.investopedia.com/terms/r/revenue.asp">収益</a>を含みます。<em>例：</em>「シリーズAまたはシリーズBの資金調達額が800万ドル～2,500万ドルで、月間収益が50万ドルを超えるスタートアップを探してください。」</p></li><li><p><strong>市場重視のクエリ</strong>：<a href="https://en.wikipedia.org/wiki/Vertical_market">業界分野</a>、<a href="https://en.wikipedia.org/wiki/Target_market">地理的市場</a>、<a href="https://www.investopedia.com/terms/b/businessmodel.asp">ビジネスモデル</a>に重点を置き、特定のセクターまたは地域での機会の特定に役立ちます。<em>例：</em>「サンフランシスコ、ニューヨーク、ボストンのフィンテックおよびヘルスケアのスタートアップ企業を探してください」</p></li></ul><p>クエリを強固に保つため、LLMに<a href="https://www.elastic.co/docs/solutions/search/search-templates">検索テンプレート</a>を構築させ、完全な<a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/querydsl">DSLクエリ</a>の代わりとします。このようにすれば、必要なクエリを常に取得でき、LLMは空白を埋めるだけで済み、毎回必要なクエリを構築する責任を負う必要がなくなります。</p><h2>始めるために必要なもの</h2><ul><li><p>Elasticsearch APIキー</p></li><li><p>OpenAPI APIキー</p></li><li><p>Node 18以降</p></li></ul><h2>ステップ別のガイド</h2><p>このセクションでは、アプリがどのように見えるかを見てみましょう。<a href="https://www.typescriptlang.org/">TypeScript</a>はJavaScriptのスーパーセットで、静的な型を追加することでコードの信頼性を高め、保守性を向上させ、エラーを早期に発見して安全性を高めます。既存のJavaScriptとの完全な互換性を保ちながら、これを実現します。</p><p>ノードのフローは次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt90db8f03f372608c/6a170986dc55de6e16e00d93/b47d7f238c4964a6febc0de7fe5e68b186f539c3-363x555.png" alt="" /><p>上記の画像はLangGraphによって生成されたもので、ノード間の実行順序と条件付きロジックを定義するワークフローを表しています。</p><ul><li><p><strong>decideStrategy：</strong>LLMを用いてユーザーのクエリを分析し、投資重視か市場重視の2つの専門的な検索戦略のどちらかを判断します。</p></li><li><p><strong>prepareInvestmentSearch：</strong>クエリからフィルター値を抽出し、財務および資金調達関連のパラメータを強調した定義済みテンプレートを構築します。</p></li><li><p><strong>prepareMarketSearch</strong> : フィルター値も抽出しますが、市場、業界、地理的コンテキストを重視したパラメータを動的に構築します。</p></li><li><p><strong>executeSearch：</strong>検索テンプレートを使用して構築されたクエリをElasticsearchに送信し、一致するスタートアップドキュメントを取得します。</p></li><li><p><strong>visualizeResults：</strong>最終結果を、資金、業界、収益などの主要なスタートアップ属性を示す明確で読みやすい要約にフォーマットします。</p></li></ul><p>このフローには「if」ステートメントとして機能する<a href="https://langchain-ai.github.io/langgraphjs/how-tos/branching/?h=conditional#how-to-create-branches-for-parallel-node-execution">条件分岐が</a>含まれており、ユーザーの入力に基づいて投資検索パスを使用するか、市場検索パスを使用するかを決定します。LLMにより駆動されるこの意思決定ロジックにより、ワークフローは適応的でコンテキストに応じたものになります。このメカニズムについては次のセクションで詳しく説明します。</p><h3>LangGraphの状態</h3><p>各ノードを個別に見る前に、ノードがどのように通信し、データを共有するかを理解する必要があります。そのために、LangGraphではワークフローの状態を定義することができます。これはノード間で共有される状態を定義します。</p><p>状態は、ワークフロー全体の中間データを保存する共有コンテナとして機能します。ユーザーの自然言語クエリから始まり、選択された検索戦略、Elasticsearch用に準備されたパラメータ、取得された検索結果、最後にフォーマットされた出力が保持されます。</p><p>この構造により、すべてのノードが状態を読み取って更新できるようになり、ユーザー入力から最終的な視覚化までの一貫した情報の流れが保証されます。</p>const VCState = Annotation.Root({
  input: Annotation&lt;string&gt;(), // User's natural language query
  searchStrategy: Annotation&lt;string&gt;(), // Search strategy chosen by LLM
  searchParams: Annotation&lt;any&gt;(), // Prepared search parameters
  results: Annotation&lt;any[]&gt;(), // Search results
  final: Annotation&lt;string&gt;(), // Final formatted response
});<h3>アプリケーションをセットアップする</h3><p>このセクションのすべてのコードは<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch">elasticsearch-labsリポジトリ</a>で見つけることができます。</p><p>アプリが置かれるフォルダーでターミナルを開き、以下のコマンドで Node.js アプリケーションを初期化します。</p>npm init -y<p>これで、このプロジェクトに必要な依存関係をインストールできます。</p>npm install @elastic/elasticsearch @langchain/langgraph @langchain/openai @langchain/core dotenv zod &amp;&amp; npm install --save-dev @types/node tsx typescript<ul><li><p><strong><code>@elastic/elasticsearch</code></strong>: Elasticsearchのデータインジェストや検索などのリクエストを処理するのに役立ちます。</p></li><li><p><strong><code>@langchain/langgraph</code></strong>: すべてのLangGraphツールを提供するためのJS依存関係。</p></li><li><p><strong><code>@langchain/openai</code></strong>: LangChain用のOpenAI LLMクライアント。</p></li><li><p>@langchain/core：プロンプトテンプレートなど、LangChainアプリのコアとなる基本的な構成要素を提供します。</p></li><li><p><strong><code>dotenv</code></strong>:JavaScriptで環境変数を使用するために必要な依存関係。</p></li><li><p><strong><code>zod</code></strong>：型データへの依存関係。</p></li></ul><p><code>@types/node</code> <code>tsx</code> <code>typescript</code> により、TypeScriptコードを記述して実行できるようになります。</p><p>次に、以下のファイルを作成します。</p><ul><li><p><code>elasticsearchSetup</code><a href="http://ingest.ts/"><code>.ts</code></a>: Elasticsearchのマッピングを作成し、JSONファイルからデータを取り込み、Elasticsearchにデータを取り込みます。</p></li><li><p><a href="http://main.ts/"><code>main.ts</code></a>: LangGraphアプリケーションが含まれます。</p></li><li><p><code>.env</code>：環境変数を格納するファイル</p></li></ul><p><code>.env</code>ファイルに以下の環境変数を追加します。</p>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"<p>OpenAPI APIKeyはコード上で直接使用されることはなく、ライブラリ<code>@langchain/openai</code>によって内部的に使用されます。</p><p>マッピングの作成、検索テンプレートの作成、データセットのインジェストに関するすべてのロジックは、<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>ファイルにあります。次のステップでは、<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/main.ts"><code>main.ts</code></a>ファイルに焦点を当てていきます。また、データセットをチェックして、 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/dataset.json"><code>dataset.json</code></a>でデータがどのように表示されるかをよりよく理解することもできます。</p><h3>LangGraphアプリ</h3><p><code>main.ts</code>ファイルで、LangGraphアプリを統合するために必要な依存関係をいくつかインポートしましょう。このファイルには、ノード関数と状態宣言も含める必要があります。グラフの宣言は、次のステップで <code>main</code> メソッドで行われます。<code>elasticsearchSetup.ts</code>ファイルには、以降のステップでノード内で使用する Elasticsearch ヘルパーが含まれます。</p>import { writeFileSync } from "node:fs";
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";
import {
  esClient,
  ingestDocuments,
  createSearchTemplates,
  INDEX_NAME,
  INVESTMENT_FOCUSED_TEMPLATE,
  MARKET_FOCUSED_TEMPLATE,
  createIndex,
} from "./elasticsearchSetup.js";

const llm = new ChatOpenAI({ model: "gpt-4o-mini" });<p>前述のように、LLMクライアントは、ユーザーの質問に基づいてElasticsearch検索テンプレートパラメーターを生成するために使用されます。</p>async function saveGraphImage(app: any): Promise&lt;void&gt; {
  try {
    const drawableGraph = app.getGraph();
    const image = await drawableGraph.drawMermaidPng();
    const arrayBuffer = await image.arrayBuffer();

    const filePath = "./workflow_graph.png";
    writeFileSync(filePath, new Uint8Array(arrayBuffer));
    console.log(`📊 Workflow graph saved as: ${filePath}`);
  } catch (error: any) {
    console.log("⚠️  Could not save graph image:", error.message);
  }
}<p>上記の方法はグラフ画像をpng形式で生成し、裏で<a href="https://mermaid.ink/">Mermaid.INK API</a>を利用しています。これは、スタイル設定された視覚化を使用してアプリノードがどのように相互作用するかを確認する場合に便利です。</p><h3>LangGraphノード</h3><p>次に、各ノードの詳細を見てみましょう。</p><h3>decideSearchStrategyノード</h3><p><code>decideSearchStrategy</code>ノードはユーザー入力を分析し、投資重視の検索を実行するか、市場重視の検索を実行するかを決定します。構造化された出力スキーマ（Zodで定義）を持つLLMを使用してクエリタイプを分類します。決定を下す前に、集計を使用してインデックスから利用可能なフィルターを取得し、モデルが業界、場所、資金調達データに関する最新のコンテキストを持っていることを確認します。</p><p>フィルタの可能な値を抽出してLLMに送信するために、<a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">集計</a>クエリを使ってElasticsearchインデックスから直接値を取得してみましょう。このロジックは<code>getAvailableFilters</code>というメソッドに割り当てられます。</p>async function getAvailableFilters() {
  try {
    const response = await esClient.search({
      index: INDEX_NAME,
      size: 0,
      aggs: {
        industries: {
          terms: { field: "industry", size: 100 },
        },
        locations: {
          terms: { field: "location", size: 100 },
        },
        funding_stages: {
          terms: { field: "funding_stage", size: 20 },
        },
        business_models: {
          terms: { field: "business_model", size: 10 },
        },
        lead_investors: {
          terms: { field: "lead_investor", size: 100 },
        },
        funding_amount_stats: {
          stats: { field: "funding_amount" },
        },
      },
    });

    return response.aggregations;
  } catch (error) {
    console.error("❌ Error getting available filters:", error);
    return {};
  }
}<p>上記の集約クエリを用いると、以下の結果が得られます。</p>{
  "industries": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "logistics",
        "doc_count": 5
      },
      ...
    ]
  },
  "locations": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "San Francisco, CA",
        "doc_count": 4
      },
      {
        "key": "New York, NY",
        "doc_count": 3
      },
      ...
    ]
  },
  "funding_stages": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "Series A",
        "doc_count": 8
      },
      ...
    ]
  },
  "business_models": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "B2B",
        "doc_count": 13
      },
      ...
    ]
  },
  "lead_investors": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "Battery Ventures",
        "doc_count": 1
      },
      {
        "key": "Benchmark Capital",
        "doc_count": 1
      },
      ...
    ]
  },
  "funding_amount_stats": {
    "count": 20,
    "min": 4500000,
    "max": 35000000,
    "avg": 14075000,
    "sum": 281500000
  }
}<p>すべての結果は<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/responses/aggregationsResponse.json">こちらで</a>ご覧いただけます。</p><p>両方の戦略において、ハイブリッド検索を行うことにより、質問の構造化された部分（フィルター）とより主観的な部分（セマンティック）の両方を検出します。以下は<a href="https://www.elastic.co/docs/solutions/search/search-templates">検索テンプレート</a>を使用した両方のクエリの例です。</p>await esClient.putScript({
      id: INVESTMENT_FOCUSED_TEMPLATE,
      script: {
        lang: "mustache",
        source: `{
          "size": 5,
          "retriever": {
            "rrf": {
              "retrievers": [
                {
                  "standard": {
                    "query": {
                      "semantic": {
                        "field": "semantic_field",
                        "query": "{{query_text}}"
                      }
                    }
                  }
                },
                {
                  "standard": {
                    "query": {
                      "bool": {
                        "filter": [
                          {"terms": {"funding_stage": {{#join}}{{#toJson}}funding_stage{{/toJson}}{{/join}}}},
                          {"range": {"funding_amount": {"gte": {{funding_amount_gte}}{{#funding_amount_lte}},"lte": {{funding_amount_lte}}{{/funding_amount_lte}}}}},
                          {"terms": {"lead_investor": {{#join}}{{#toJson}}lead_investor{{/toJson}}{{/join}}}},
                          {"range": {"monthly_revenue": {"gte": {{monthly_revenue_gte}}{{#monthly_revenue_lte}},"lte": {{monthly_revenue_lte}}{{/monthly_revenue_lte}}}}}
                        ]
                      }
                    }
                  }
                }
              ],
              "rank_window_size": 100,
              "rank_constant": 20
            }
          }
        }`,
      },
    });<p><a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts#L119"><code>elasticsearchSetup.ts</code></a>ファイルに詳細が記載されているクエリを確認します。次のノードでは、2つのクエリのどちらを使用するかが決定されます。</p>// Node 1: Decide search strategy using LLM
async function decideSearchStrategy(state: typeof VCState.State) {
  // Zod schema for specialized search strategy decision
  const SearchDecisionSchema = z.object({
    search_type: z
      .enum(["investment_focused", "market_focused"])
      .describe("Type of specialized search strategy to use"),
    reasoning: z
      .string()
      .describe("Brief explanation of why this search strategy was chosen"),
  });

  const decisionLLM = llm.withStructuredOutput(SearchDecisionSchema);

  // Get dynamic filters from Elasticsearch
  const availableFilters = await getAvailableFilters();

  const prompt = `Query: "${state.input}"
    Available filters: ${JSON.stringify(availableFilters, null, 2)}

    Choose between two specialized search strategies:
    
    - investment_focused: For queries about funding stages, funding amounts, monthly revenue, lead investors, financial performance
    
    - market_focused: For queries about industries, locations, business models, market segments, geographic markets
    
    Analyze the query intent and choose the most appropriate strategy.
  `;

  try {
    const result = await decisionLLM.invoke(prompt);
    console.log(
      `🤔 Search strategy: ${result.search_type} - ${result.reasoning}`
    );

    return {
      searchStrategy: result.search_type,
    };
  } catch (error: any) {
    console.error("❌ Error in decideSearchStrategy:", error.message);
    return {
      searchStrategy: "investment_focused",
    };
  }
}<h3>prepareInvestmentSearchノードとprepareMarketSearchノード</h3><p>どちらのノードも共有ヘルパー関数<code>extractFilterValues</code>を使用します。この関数はLLMを活用して、業界、場所、資金調達段階、ビジネスモデルなど、ユーザーの入力に記載されている関連フィルターを識別します。このスキーマを使用して<a href="https://www.elastic.co/docs/solutions/search/search-templates">検索テンプレート</a>を構築します。</p>// Extract all possible filter values from user input
async function extractFilterValues(input: string) {
  const FilterValuesSchema = z.object({
    // Investment-focused filters
    funding_stage: z
      .array(z.string())
      .default([])
      .describe("Funding stage values mentioned in query"),
    funding_amount_gte: z
      .number()
      .default(0)
      .describe("Minimum funding amount in USD"),
    funding_amount_lte: z
      .number()
      .default(100000000)
      .describe("Maximum funding amount in USD"),
    lead_investor: z
      .array(z.string())
      .default([])
      .describe("Lead investor values mentioned in query"),
    monthly_revenue_gte: z
      .number()
      .default(0)
      .describe("Minimum monthly revenue in USD"),
    monthly_revenue_lte: z
      .number()
      .default(10000000)
      .describe("Maximum monthly revenue in USD"),
    industry: z
      .array(z.string())
      .default([])
      .describe("Industry values mentioned in query"),
    location: z
      .array(z.string())
      .default([])
      .describe("Location values mentioned in query"),
    business_model: z
      .array(z.string())
      .default([])
      .describe("Business model values mentioned in query"),
  });

  const extractorLLM = llm.withStructuredOutput(FilterValuesSchema);
  const availableFilters = await getAvailableFilters();

  const extractPrompt = `Extract ALL relevant filter values from: "${input}"
    Available options: ${JSON.stringify(availableFilters, null, 2)}
    Extract only values explicitly mentioned in the query. Leave fields empty if not mentioned.`;

  return await extractorLLM.invoke(extractPrompt);
}<p>検出された意図に応じて、ワークフローは2つのパスのいずれかを選択します。</p><p><strong>prepareInvestmentSearch：</strong>資金調達段階、資金調達額、投資家、更新情報などの財務指向の検索パラメータを構築します。クエリ テンプレート全体は<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>ファイルで確認できます。</p>// Node 2A: Prepare Investment-Focused Search Parameters 
async function prepareInvestmentSearch(state: typeof VCState.State) {
  console.log(
    "💰 Preparing INVESTMENT-FOCUSED search parameters with financial emphasis..."
  );

  try {
    // Extract all filter values from input
    const values = await extractFilterValues(state.input);

    let searchParams: any = {
      template_id: INVESTMENT_FOCUSED_TEMPLATE,
      query_text: state.input,
      ...values,
    };

    return { searchParams };
  } catch (error) {
    console.error("❌ Error preparing investment-focused params:", error);
    return {
      searchParams: {},
    };
  }
}<p><strong>prepareMarketSearch：</strong>業界、地域、ビジネスモデルに重点を置いた市場主導のパラメータを作成します。クエリ全文は<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>ファイルをご覧ください。</p>// Node 2B: Prepare Market-Focused Search Parameters
async function prepareMarketSearch(state: typeof VCState.State) {
  console.log(
    "🔍 Preparing MARKET-FOCUSED search parameters with market emphasis..."
  );

  try {
    // Extract all filter values from input
    const values = await extractFilterValues(state.input);

    let searchParams: any = {
      template_id: MARKET_FOCUSED_TEMPLATE,
      query_text: state.input,
      ...values,
    };

    return { searchParams };
  } catch (error) {
    console.error("❌ Error preparing market-focused params:", error);
    return {};
  }
}<h3>executeSearchノード</h3><p>このノードは、生成された検索パラメータを状態から取得し、最初にElasticsearchに送信します。次に、<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-render-search-template">_render API</a>を使用してデバッグの目的でクエリを視覚化し、次に結果を取得するためのリクエストを送信します。</p>// Node 3: Execute Search
async function executeSearch(state: typeof VCState.State) {
  const { searchParams } = state;

  try {
    // getting formed query from template for debugging
    const renderedTemplate = await esClient.renderSearchTemplate({
      id: searchParams.template_id,
      params: searchParams,
    });

    console.log(
      "📋 Complete query:",
      JSON.stringify(renderedTemplate.template_output, null, 2)
    );

    const results = await esClient.searchTemplate({
      index: INDEX_NAME,
      id: searchParams.template_id,
      params: searchParams,
    });

    return {
      results: results.hits.hits.map((hit: any) =&gt; hit._source),
    };
  } catch (error: any) {
    console.error(`❌ ${state.searchParams.search_type} search error:`, error);
    return { results: [] };
  }
}<h3>visualizeResultsノード</h3><p>最後に、このnodeはElasticsearchの結果を表示します。</p>// Node 4: Visualize results
async function visualizeResults(state: typeof VCState.State) {
  const results = state.results || [];

  let formattedResults = `🎯 Found ${results.length} startups matching your criteria:\n\n`;

  results.forEach((startup: any, index: number) =&gt; {
    formattedResults += `${index + 1}. **${startup.company_name}**\n`;
    formattedResults += `   📍 ${startup.location} | 🏢 ${startup.industry} | 💼 ${startup.business_model}\n`;
    formattedResults += `   💰 ${startup.funding_stage} - $${(
      startup.funding_amount / 1000000
    ).toFixed(1)}M\n`;
    formattedResults += `   👥 ${startup.employee_count} employees | 📈 $${(
      startup.monthly_revenue / 1000
    ).toFixed(0)}K MRR\n`;
    formattedResults += `   🏦 Lead: ${startup.lead_investor}\n`;
    formattedResults += `   📝 ${startup.description}\n\n`;
  });

  return {
    final: formattedResults,
  };
}<p>プログラム的には、グラフ全体は次のようになります。</p>  const workflow = new StateGraph(VCState)
    // Register nodes - these are the processing functions
    .addNode("decideStrategy", decideSearchStrategy)
    .addNode("prepareInvestment", prepareInvestmentSearch)
    .addNode("prepareMarket", prepareMarketSearch)
    .addNode("executeSearch", executeSearch)
    .addNode("visualizeResults", visualizeResults)
    // Define execution flow with conditional branching
    .addEdge(START, "decideStrategy") // Start with strategy decision
    .addConditionalEdges(
      "decideStrategy",
      (state: typeof VCState.State) =&gt; state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -&gt; RRF template preparation
        market_focused: "prepareMarket", // If market focused -&gt; dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -&gt; execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -&gt; execute
    .addEdge("executeSearch", "visualizeResults") // Execute -&gt; visualize
    .addEdge("visualizeResults", END); // End workflow<p>ご覧のとおり、アプリが次にどの「パス」またはノードを実行するかを決定する条件付きエッジがあります。この特徴は、ワークフローに分岐ロジックが必要な場合、例えば複数のツールから選択する場合や、人間が関与するステップを含む場合に有用です。</p><p>LangGraph のコア機能を理解したら、コードが実行されるアプリケーションをセットアップできます。</p><p>すべてを<code>main</code>メソッドで組み合わせ、ここではすべての要素をワークフロー変数下のグラフとして宣言します。</p>async function main() {
  await createIndex();
  await createSearchTemplates();
  await ingestDocuments();

  // Create the workflow graph with shared state
  const workflow = new StateGraph(VCState)
    // Register nodes - these are the processing functions
    .addNode("decideStrategy", decideSearchStrategy)
    .addNode("prepareInvestment", prepareInvestmentSearch)
    .addNode("prepareMarket", prepareMarketSearch)
    .addNode("executeSearch", executeSearch)
    .addNode("visualizeResults", visualizeResults)
    // Define execution flow with conditional branching
    .addEdge(START, "decideStrategy") // Start with strategy decision
    .addConditionalEdges(
      "decideStrategy",
      (state: typeof VCState.State) =&gt; state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -&gt; RRF template preparation
        market_focused: "prepareMarket", // If market focused -&gt; dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -&gt; execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -&gt; execute
    .addEdge("executeSearch", "visualizeResults") // Execute -&gt; visualize
    .addEdge("visualizeResults", END); // End workflow


  const app = workflow.compile();

  await saveGraphImage(app);

  const query =
    "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K";

  const marketResult = await app.invoke({ input: query });
  console.log(marketResult.final);
}<p>クエリ変数は、仮想の検索バーに入力されたユーザー入力をシミュレートします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba7189d5f4e63403/6a1709880e2e49cc3041a076/e8d76909eb2bc1bb62f3ca9a8b3e4b85fcec2893-1600x164.png" alt="" /><p>「シリーズAまたはシリーズBの資金調達額が800万ドル～2,500万ドルで、月間収益が50万ドルを超えるスタートアップを探してください。」という自然言語フレーズから、すべてのフィルターが抽出されます。</p><p>最後にmainメソッドを呼び出します。</p>main().catch(console.error);<h3>成果</h3>🔍 Checking if index exists...
🏗️ Creating index...
✅ Index created successfully!
Ingesting documents...
✅ Documents ingested successfully!
✅ Investment-focused template created successfully!
✅ Market-focused template created successfully!

📊 Workflow graph saved as: ./workflow_graph.png

🔍 Query: "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K"

🤔 Search strategy: investment_focused - The query specifically seeks profitable fintech startups with defined funding amounts and high monthly revenue, which aligns closely with financial performance metrics and investment-related criteria.

💰 Preparing INVESTMENT-FOCUSED search parameters with financial emphasis...

📋 Complete query: {
  "size": 5,
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "semantic": {
                "field": "semantic_field",
                "query": "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "funding_stage": [
                        "Series A",
                        "Series B"
                      ]
                    }
                  },
                  {
                    "range": {
                      "funding_amount": {
                        "gte": 8000000,
                        "lte": 25000000
                      }
                    }
                  },
                  {
                    "terms": {
                      "lead_investor": []
                    }
                  },
                  {
                    "range": {
                      "monthly_revenue": {
                        "gte": 500000,
                        "lte": 0
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      ],
      "rank_window_size": 100,
      "rank_constant": 20
    }
  }
}
🎯 Found 5 startups matching your criteria:

1. **TechFlow**
   📍 San Francisco, CA | 🏢 logistics | 💼 B2B
   💰 Series A - $8.0M
   👥 45 employees | 📈 $500K MRR
   🏦 Lead: Sequoia Capital
   📝 TechFlow optimizes supply chain operations using AI-powered route optimization and real-time tracking. Founded in 2023, shows remarkable growth with $500K monthly revenue.

2. **DataViz**
   📍 New York, NY | 🏢 enterprise software | 💼 B2B
   💰 Series A - $10.0M
   👥 42 employees | 📈 $450K MRR
   🏦 Lead: Battery Ventures
   📝 DataViz creates intuitive data visualization tools for enterprise customers. No-code platform allows business users to create dashboards without technical expertise.

3. **FinanceAI**
   📍 San Francisco, CA | 🏢 fintech | 💼 B2C
   💰 Series C - $25.0M
   👥 120 employees | 📈 $1200K MRR
   🏦 Lead: Tiger Global Management
   📝 FinanceAI provides AI-powered investment advisory services to retail investors. Uses machine learning to analyze market trends with over 100,000 active users.

4. **UrbanMobility**
   📍 New York, NY | 🏢 logistics | 💼 B2B2C
   💰 Series B - $15.0M
   👥 78 employees | 📈 $750K MRR
   🏦 Lead: Kleiner Perkins
   📝 UrbanMobility revolutionizes urban transportation through autonomous delivery drones and smart logistics hubs. Partners with major retailers for same-day delivery across Manhattan and Brooklyn.

5. **HealthTech Solutions**
   📍 Boston, MA | 🏢 healthcare | 💼 B2B
   💰 Series B - $18.0M
   👥 95 employees | 📈 $900K MRR
   🏦 Lead: General Catalyst
   📝 HealthTech Solutions develops medical devices and software for remote patient monitoring. Comprehensive telehealth platform reducing hospital readmissions by 30%.

✨  Done in 18.80s.<p>送信された入力に対して、アプリケーションは<strong>投資に重点を置いた</strong>パスを選択し、その結果、ユーザー入力から値と範囲を抽出するLangGraphワークフローによって生成されたElasticsearchクエリを確認できます。また、抽出された値が適用された状態でElasticsearchに送信されたクエリと、最後に<code>visualizeResults</code>ノードによって結果がフォーマットされた結果も確認できます。</p><p>次に、<strong>市場重視</strong>のノードを、クエリ「サンフランシスコ、ニューヨーク、ボストンのフィンテックおよびヘルスケアのスタートアップ企業を探してください」を使用してテストしてみましょう。</p>...

🔍 Query: Find fintech and healthcare startups in San Francisco, New York, or Boston

🤔 Search strategy: market_focused - The query is focused on finding fintech startups in San Francisco that are disrupting traditional banking and payment systems, which pertains to specific industries (fintech) and locations (San Francisco). Thus, a market-focused strategy is more appropriate.

🔍 Preparing MARKET-FOCUSED search parameters with market emphasis...

📋 Complete query: {
  "size": 5,
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "semantic": {
                "field": "semantic_field",
                "query": "Find fintech and healthcare startups in San Francisco, New York, or Boston"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "industry": [
                        "fintech",
                        "healthcare"
                      ]
                    }
                  },
                  {
                    "terms": {
                      "location": [
                        "San Francisco, CA",
                        "New York, NY",
                        "Boston, MA"
                      ]
                    }
                  },
                  {
                    "terms": {
                      "business_model": []
                    }
                  }
                ]
              }
            }
          }
        }
      ],
      "rank_window_size": 50,
      "rank_constant": 10
    }
  }
}
🎯 Found 5 startups matching your criteria:

1. **FinanceAI**
   📍 San Francisco, CA | 🏢 fintech | 💼 B2C
   💰 Series C - $25.0M
   👥 120 employees | 📈 $1200K MRR
   🏦 Lead: Tiger Global Management
   📝 FinanceAI provides AI-powered investment advisory services to retail investors. Uses machine learning to analyze market trends with over 100,000 active users.

2. **CryptoWallet**
   📍 Miami, FL | 🏢 fintech | 💼 B2C
   💰 Series B - $16.0M
   👥 73 employees | 📈 $820K MRR
   🏦 Lead: Coinbase Ventures
   📝 CryptoWallet provides secure digital wallet solutions for cryptocurrency trading and storage. Multi-chain support with enterprise-grade security features.

...

✨  Done in 7.41s.<h2>学び</h2><p>執筆の過程で次のことを学びました。</p><ul><li><p>LLMにフィルターの正確な値を表示する必要があります。そうしないと、ユーザーが正確な値を入力することになります。カーディナリティが低い場合はこのアプローチで問題ありませんが、カーディナリティが高い場合は結果をフィルタリングする何らかのメカニズムが必要です。</p></li><li><p>検索テンプレートを使用すると、LLMにElasticsearchクエリを記述させるよりも結果の一貫性が大幅に向上し、速度も速くなります。</p></li><li><p>条件付きエッジは、複数のバリアントと分岐パスを持つアプリケーションを構築するための強力なメカニズムです。</p></li><li><p>構造化された出力は、予測可能でタイプセーフな応答を強制するため、LLMを使用して情報を生成する場合に非常に役立ちます。これにより、信頼性が向上し、プロンプトの誤解が減少します。</p></li></ul><p>ハイブリッド検索を通じてセマンティック検索と構造化検索を組み合わせることで、精度とコンテキスト理解のバランスを保ちながら、より適切で関連性の高い結果が生成されます。</p><h2>まとめ</h2><p>この例では、LangGraph.jsとElasticsearchを組み合わせて、自然言語クエリをElasticsearchで検索し、金融と市場のいずれかワークフローを焦点を当てた検索戦略をワークフローで決定できる動的なワークフローを作成します。このアプローチにより、手動クエリ作成の複雑さが軽減され、ベンチャーキャピタルアナリストの柔軟性と精度が向上します。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt013eba5d152f11f3/6a1709892b835f6784f4b1a6/12b6057d84c6356267cd178a3c6c1a5c61123ece-2000x1256.png" length="0" type="image/png"/>
    <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[GitHubのイシューをElasticsearchでクエリするChatGPTコネクターの構築]]></title>
    <description><![CDATA[カスタムChatGPTコネクターの構築方法と、ハイブリッド検索して内部のGitHubイシューをクエリするElasticsearch MCPサーバーをデプロイする方法を学びましょう。]]></description>
    <content:encoded><![CDATA[<p>最近、OpenAIはPro/Business/EnterpriseおよびEduプラン向けに<a href="https://help.openai.com/en/articles/11487775-connectors-in-chatgpt">ChatGPT向けのカスタムコネクター</a>機能を発表しました。これは、Gmail、GitHub、Dropboxなどのデータを活用するためのすぐに使えるコネクターへの追加となります。MCPサーバーを使用してカスタムコネクターを作成できます。</p><p>カスタムコネクターを使用すると、既存のChatGPTコネクターをElasticsearchなどの追加のデータソースと組み合わせて、包括的な回答を得ることができます。</p><p>この記事では、内部のGitHubの課題とプルリクエストに関する情報を含むElasticsearchインデックスにChatGPTを接続する<a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a>サーバーを構築します。これにより、Elasticsearchデータを使用して自然言語クエリに回答できるようになります。</p><p>Google Colabの<a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>とngrokを使ってMCPサーバーをデプロイし、ChatGPTが接続できる公開URLを取得し、複雑なインフラ構築の必要性を排除します。</p><p>MCPとそのエコシステムの包括的な概要については、<a href="https://www.elastic.co/search-labs/blog/mcp-current-state">MCPの現在の状態</a>をご参照ください。</p><h2>要件</h2><p>始める前に必要なものは次のとおりです。</p><ul><li><p>Elasticsearchクラスター（8.X以降）</p></li><li><p>インデックスへの読み取りアクセス権を持つ Elasticsearch APIキー</p></li><li><p>Googleアカウント（Google Colab用）</p></li><li><p>Ngrokアカウント（無料プランでも可）</p></li><li><p>Pro/Enterprise/BusinessまたはEduプランのChatGPTアカウント</p></li></ul><h2>ChatGPT MCPコネクターの要件を理解する</h2><p>ChatGPT MCPコネクターには、<code>search</code>と<code>fetch</code>の2つのツールを実装する必要があります。詳細については、<a href="https://platform.openai.com/docs/mcp#create-an-mcp-server">OpenAIドキュメント</a>をご覧ください。</p><h3><a href="https://platform.openai.com/docs/mcp#search-tool">検索ツール</a></h3><p>ユーザークエリに基づいて、Elasticsearchインデックスから関連する結果のリストを返します。</p><h4>受け取るもの：</h4><ul><li><p>ユーザーの自然言語クエリを含む単一の文字列。</p></li><li><p>例：「Elasticsearch移行に関連するイシューを見つけて」</p></li></ul><h4>返されるもの：</h4><ul><li><p>結果オブジェクトの配列を含む<code>result</code>キーを持つオブジェクト。各結果には以下が含まれます。</p><ul><li><p><code>id</code> - 一意の文書識別子</p></li><li><p><code>title</code> - イシューまたはPRタイトル</p></li><li><p><code>url</code> - イシュー/PRへのリンク</p></li></ul></li></ul><h4>実装内容：</h4>return {
    "results": [
        {
            "id": "PR-612",
            "title": "Fix memory leak in WebSocket notification service",
            "url": "https://internal-git.techcorp.com/pulls/612"
        },
        # ... more results
    ]
}<h3><a href="https://platform.openai.com/docs/mcp#fetch-tool">フェッチ・ツール</a></h3><p>特定の文書の完全な内容を取得します。</p><h4>受け取るもの：</h4><ul><li><p>検索結果からElasticsearch文書IDを入力する単一の文字列</p></li><li><p>例：「PR-578の詳細を教えてください。」</p></li></ul><h4>返されるもの：</h4><ul><li><p>以下を含む完全な文書オブジェクト：</p><ul><li><p><code>id</code> - 一意の文書識別子</p></li><li><p><code>title</code> - イシューまたはPRタイトル</p></li><li><p><code>text</code> - 完全なイシュー・PRの説明と詳細</p></li><li><p><code>url</code> - イシュー/PRへのリンク</p></li><li><p><code>type</code> - 文書の種類（issue, pull_request）</p></li><li><p><code>status</code> - 現在のステータス（open, in_progress, resolved）</p></li><li><p><code>priority</code> - 優先度レベル（low, medium, high, critical）</p></li><li><p><code>assignee</code> - イシュー/PRの担当者</p></li><li><p><code>created_date</code> - 作成された時期</p></li><li><p><code>resolved_date</code> - 解決された時期（該当する場合）</p></li><li><p><code>labels</code> 文書に関連するタグ</p></li><li><p><code>related_pr</code> - 関連するプルリクエストID</p></li></ul></li></ul>return {
    "id": "PR-578",
    "title": "Security hotfix: Patch SQL injection vulnerabilities",
    "text": "Description: CRITICAL SECURITY FIX for ISSUE-1889. Patches SQL...",
    "url": "https://internal-git.techcorp.com/pulls/578",
    "type": "pull_request",
    "status": "closed",
    "priority": "critical",
    "assignee": "sarah_dev",
    "created_date": "2025-09-19",
    "resolved_date": "2025-09-19",
    "labels": "security, hotfix, sql",
    "related_pr": null
}<p><strong>注</strong>：この例では、すべてのフィールドがルートレベルにあるフラット構造を使用しています。OpenAIの要件は柔軟で、ネストされたメタデータオブジェクトもサポートしています。</p><h2>GitHubのデータセットとプルリクエストデータセット</h2><p>このチュートリアルでは、イシューとプルリクエストを含む内部GitHubデータセットを使用します。これは、ChatGPTを通じてプライベートな内部データをクエリするシナリオを表しています。</p><p>データセットは<a href="https://gist.github.com/TomasMurua/4e7bbdf7a7ebbdffaa663c43578d934a">こちら</a>からご覧いただけます。そして、<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">Bulk APIを使って</a>データのインデックスを更新します。</p><p>このデータセットには以下が含まれます。</p><ul><li><p>説明、ステータス、優先順位、担当者に関する問題</p></li><li><p>コード変更、レビュー、導入情報を含むプルリクエスト</p></li><li><p>イシューとPRの関係（例：PR-578がISSUE-1889を修正）</p></li><li><p>ラベル、日付、その他のメタデータ</p></li></ul><h3>インデックスマッピング</h3><p>インデックスは、<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">ELSER</a>とのハイブリッド検索をサポートするために以下の<a href="https://www.elastic.co/docs/manage-data/data-store/mapping">マッピング</a>を使用します。<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">text_semantic</a>はセマンティック検索に使用され、他のフィールドはキーワード検索を可能にします。</p>{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "title": {
        "type": "text"
      },
      "text": {
        "type": "text"
      },
      "text_semantic": {
        "type": "semantic_text",
        "inference_id": ".elser-2-elasticsearch"
      },
      "url": {
        "type": "keyword"
      },
      "type": {
        "type": "keyword"
      },
      "status": {
        "type": "keyword"
      },
      "priority": {
        "type": "keyword"
      },
      "assignee": {
        "type": "keyword"
      },
      "created_date": {
        "type": "date",
        "format": "iso8601"
      },
      "resolved_date": {
        "type": "date",
        "format": "iso8601"
      },
      "labels": {
        "type": "keyword"
      },
      "related_pr": {
        "type": "keyword"
      }
    }
  }
}<h2>MCPサーバーを構築する</h2><p>当社のMCPサーバーは、OpenAI仕様に従って2つのツールを実装しています。ハイブリッド検索を使用してセマンティックマッチングとテキストマッチングを組み合わせることで、より良い結果が得られます。</p><h3>検索ツール</h3><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a>（相互ランク融合）を用いたハイブリッド検索を使用し、セマンティック検索とテキストマッチングを組み合わせています。</p>@mcp.tool()
    async def search(query: str) -&gt; Dict[str, List[Dict[str, Any]]]:
        """
        Search for internal issues and PRs using hybrid search (semantic + text with RRF).
        Returns list with id, title, and url per OpenAI spec.
        """
        if not query or not query.strip():
            return {"results": []}

        logger.info(f"Searching for: '{query}'")

        try:
            # Hybrid search with RRF (Reciprocal Rank Fusion)
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                size=10,
                source=["id", "title", "url", "type", "priority"],
                retriever={
                    "rrf": {
                        "retrievers": [
                            {
                                # Semantic search with ELSER
                                "standard": {
                                    "query": {
                                        "semantic": {
                                            "field": "text_semantic",
                                            "query": query
                                        }
                                    }
                                }
                            },
                            {
                                # Text search (BM25) for keyword matching
                                "standard": {
                                    "query": {
                                        "multi_match": {
                                            "query": query,
                                            "fields": [
                                                "title^3",
                                                "text^2",
                                                "assignee^2",
                                                "type",
                                                "labels",
                                                "priority"
                                            ],
                                            "type": "best_fields",
                                            "fuzziness": "AUTO"
                                        }
                                    }
                                }
                            }
                        ],
                        "rank_window_size": 50,
                        "rank_constant": 60
                    }
                }
            )

            results = []
            if response and 'hits' in response:
                for hit in response['hits']['hits']:
                    source = hit['_source']
                    results.append({
                        "id": source.get('id', hit['_id']),
                        "title": source.get('title', 'Unknown'),
                        "url": source.get('url', '')
                    })

            logger.info(f"Found {len(results)} results")
            return {"results": results}

        except Exception as e:
            logger.error(f"Search error: {e}")
            raise ValueError(f"Search failed: {str(e)}")<h3>主なポイント：</h3><ul><li><p><strong>RRFを用いたハイブリッド検索：</strong> より良い結果を得るために、セマンティック検索（ELSER）とテキスト検索（BM25）を組み合わせます。</p></li><li><p><strong>複数一致クエリ：</strong>ブースティングを使用して<a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query">複数のフィールドを検索します</a>（title^3, text^2, assignee^2）。キャレット記号（^）は関連性スコアを乗算し、コンテンツよりもタイトルの一致を優先します。</p></li><li><p><strong>あいまい一致：</strong> <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/common-options#fuzziness"><code>fuzziness: AUTO</code></a> は近似一致を許可することでタイプミスやスペルミスを処理します。</p></li><li><p><strong>RRFのパラメーター調整：</strong></p><ul><li><p><code>rank_window_size: 50</code> - マージする前に、各リトリーバー（セマンティックとテキスト）からの上位結果をいくつ考慮するかを指定します。</p></li><li><p><code>rank_constant: 60</code> - この値は、個々の結果セット内の文書が最終的なランク付け結果にどの程度影響を与えるかを決定します。</p></li></ul></li><li><p><strong>必須フィールドのみを返す：</strong> <code>id</code>、<code>title</code>、<code>url</code>はOpenAIの仕様に従い、追加のフィールドを不必要に公開しないようにします。</p></li></ul><h3>フェッチ・ツール</h3><p>文書IDが存在する場合は、そのIDで文書の詳細を取得します。</p>@mcp.tool()
    async def fetch(id: str) -&gt; Dict[str, Any]:
        """
        Retrieve complete issue/PR details by ID.
        Returns id, title, text, url.
        """
        if not id:
            raise ValueError("ID is required")

        logger.info(f"Fetching: {id}")

        try:
            # Search by the 'id' field (not _id) since IDs are stored as a field
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                body={
                    "query": {
                        "term": {
                            "id": id  # Search by your custom 'id' field
                        }
                    },
                    "size": 1
                }
            )

            if not response or not response['hits']['hits']:
                raise ValueError(f"Document with id '{id}' not found")

            hit = response['hits']['hits'][0]
            source = hit['_source']

            result = {
                "id": source.get('id', id),
                "title": source.get('title', 'Unknown'),
                "text": source.get('text', ''),
                "url": source.get('url', ''),
                "type": source.get('type', ''),
                "status": source.get('status', ''),
                "priority": source.get('priority', ''),
                "assignee": source.get('assignee', ''),
                "created_date": source.get('created_date', ''),
                "resolved_date": source.get('resolved_date', ''),
                "labels": source.get('labels', ''),
                "related_pr": source.get('related_pr', '')
            }

            logger.info(f"Fetched: {result['title']}")
            return result

        except Exception as e:
            logger.error(f"Fetch error: {e}")
            raise ValueError(f"Failed to fetch '{id}': {str(e)}")<h3>主なポイント：</h3><ul><li><p><strong>文書IDのフィールドで検索：</strong>カスタム <code>id</code>フィールドに用語クエリを使用します</p></li><li><p><strong>完全な文書を返す：</strong> すべてのコンテンツを含む完全な<code>text</code>フィールドが含まれます</p></li><li><p><strong>フラットな構造：</strong>すべてのフィールドがルートレベルにあり、Elasticsearchのドキュメント構造に一致します。</p></li></ul><h2>Google Colabにデプロイする</h2><p>Google Colabを使用してMCPサーバーを実行し、ngrokで公開することで、ChatGPTが接続できるようにします。</p><h3>ステップ1：Google Colabノートブックを開く</h3><p>事前設定されたノートブック<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/elasticsearch-chatgpt-connector">Elasticsearch MCP for ChatGPT</a>にアクセスします。</p><h3>ステップ2：認証情報を設定する</h3><p>次の3つの情報が必要になります。</p><ul><li><p><strong>Elasticsearch URL：</strong>お客様の<a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-enterprise/connect-elasticsearch">ElasticsearchクラスタリングURL</a>。</p></li><li><p><strong>Elasticsearch API キー：</strong>インデックスへの読み取りアクセス権を持つ<a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">APIキー</a>。</p></li><li><p><strong>Ngrok認証トークン：</strong><a href="https://ngrok.com/">ngrok</a>からの無料トークン。ngrokを使ってMCPのURLをインターネットに公開し、ChatGPTが接続できるようにします。</p></li></ul><h4>ngrokトークンの取得</h4><ol><li><p><a href="https://ngrok.com/">ngrok</a>で無料アカウントに登録します。</p></li><li><p><a href="https://dashboard.ngrok.com/">ngrok</a>ダッシュボードにアクセスします。</p></li><li><p>認証トークンをコピーします。</p></li></ol><h4>Google Colabにシークレットを追加する</h4><p>Google Colabノートブック内で：</p><ol><li><p>左側のサイドバーにある<strong>キーアイコン</strong>をクリックして、<strong>シークレット</strong>を開きます。</p></li><li><p>次の3つのシークレットを追加します。</p></li></ol>ELASTICSEARCH_URL=https://your-cluster.elastic.com:443
ELASTICSEARCH_API_KEY=your-api-key
NGROK_TOKEN=your-ngrok-token<p>3. 各シークレットのノートブックアクセスを有効にします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5acae97b386277f8/6a17f08f5ea30f74c964b6c2/d5dd6ac19fe816a562c6351fdb0f11369da0e877-609x321.jpg" alt="Google Colabへのシークレットの追加" /><h3>ステップ3：ノートブックを実行する</h3><ol><li><p><strong>ランタイム</strong>をクリックし、次に<strong>すべて実行</strong>をクリックして、すべてのセルを実行します。</p></li><li><p>サーバーの起動を待ちます（約30秒）。</p></li><li><p>公開ngrok URLを示す出力を探します。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd11aacf2deab67c/6a17f091e8fbce81f13a1a41/f185100e8869624bc9e1c7b2b4eb32785e2d89e7-1189x283.png" alt="" /><p>4. 出力は次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8891d917fdbaaf48/6a17f092abe0f208c7dfeaf6/e02e625e91ed9136454e4401b184575fb03a336e-1052x465.jpg" alt="Google Collabでノートブックを実行した結果" /><h2>ChatGPTに接続する</h2><p>次に、MCPサーバーをあなたのChatGPTアカウントに接続します。</p><ol><li><p>ChatGPTを開き、<strong>設定</strong>に移動します。</p></li><li><p><strong>コネクター</strong>に移動します。Proアカウントを使用している場合は、コネクタで<a href="https://platform.openai.com/docs/guides/developer-mode">開発者モード</a>をオンにする必要があります。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt95efdcb2c39307e7/6a17f094abe0f24d8edfeafa/32c02192912fc0e7e5a52e9399077ba7ae3b4901-739x715.png" alt="ChatGPTアカウントへのMPCサーバーの接続" /><p><em>ChatGPT EnterpriseまたはBusinessを使用している場合は、コネクターを職場に公開する必要があります。</em></p><p>3.  <strong>作成</strong>をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4c8fc8dd6033918/6a17f095631730de19585b7b/15c53e5ccc381108a9dc0052cca05bf0fc97679a-755x683.png" alt="ChatGPTへのコネクターの追加" /><p><em><strong>注</strong></em><em>：Business、Enterprise、Eduワークスペースでは、ワークスペースの所有者、管理者、およびそれぞれの設定が有効になっているユーザー（Enterprise/Eduの場合）のみがカスタムコネクターを追加できます。通常のメンバーロールのユーザーには、自分でカスタムコネクターを追加する権限がありません。</em></p><p><em>コネクターが所有者または管理者ユーザーによって追加され有効化されると、ワークスペースのすべてのメンバーが使用できるようになります。</em></p><p>4. 必要な情報と、<code>/sse/</code>で終わるngrokのURLを入力します。「sse」の後の「/」に注意してください。これがない場合、動作しません。</p><ul><li><p><strong>Name:</strong> Elasticsearch MCP</p></li><li><p><strong>Description: </strong>GitHubの内部情報を検索および取得するためのカスタムMCP。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd716ad0beeeb1d35/6a17f09714d90c11cc79b6d7/162a85705cc8ac48a3f2f665551d513e0719f93d-479x684.png" alt="Elastic MCPコネクターの作成 " /><p>5. <strong>作成</strong>を押してカスタムMCPを保存します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt857794237d7d3b5a/6a17f0983e03d729b74f2d54/97eb5fb0a32b86bfadfb35561f698616f217c049-913x629.png" alt="作成をクリックしてカスタムMCPコネクタを保存する" /><p>サーバーが稼働していれば、接続は瞬時に完了します。追加の認証は不要で、Elasticsearch APIキーはサーバー上で設定されています。</p><h2>MCPサーバーをテストする</h2><p>質問する前に、ChatGPTが使用するコネクターを選択する必要があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1602c48878dc7a9/6a17f09a6df731cca90a0fff/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif" alt="ChatGPTが使用するコネクターを選択する" /><h3>プロンプト1：イシューを検索する</h3><p><strong>「Elasticsearchの移行に関連するイシューを見つけて」</strong>と質問し、アクションツールの呼び出しを確認します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6c204ceacf897f61/6a17f09c9da390fb1de4657d/cfd781acbff8cd7c8095bbe29224f8b26d581f77-650x375.png" alt="ChatGPTに「Elasticsearchの移行に関連する問題を見つける」ように依頼し、アクションツールの呼び出しを確認します。" /><p>ChatGPT はクエリを使用して<code>search</code>ツールを呼び出します。利用可能なツールを検索し、Elasticsearchツールを呼び出す準備をし、ツールに対して何らかのアクションを実行する前にユーザーに確認していることがわかります。</p><h4>ツール呼び出しリクエスト：</h4>{
  "query": "Elasticsearch migration issues"
}<h4>ツールの応答：</h4>{
  "results": [
    {
      "id": "PR-598",
      "title": "Elasticsearch 8.x migration - Application code changes",
      "url": "https://internal-git.techcorp.com/pulls/598"
    },
    {
      "id": "ISSUE-1712",
      "title": "Migrate from Elasticsearch 7.x to 8.x",
      "url": "https://internal-git.techcorp.com/issues/1712"
    },
    {
      "id": "RFC-045",
      "title": "Design Proposal: Microservices Migration Architecture",
      "url": "https://internal-git.techcorp.com/rfcs/045"
    }
    // ... 7 more results
  ]
}<p>ChatGPTは結果を処理し、自然で会話的な形式で提示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b4378e7d26b4ad0/6a17f09ddbb4ff4de1fb57bf/9d5b6cff85c7e54ccc2584b8ae96d45495fae8c1-923x1352.png" alt="ChatGPTがツール呼び出しリクエストとツール呼び出しレスポンスの結果を処理する方法" /><h3>仕組み</h3><h4>プロンプト：「Elasticsearch移行に関連するイシューを見つけて」</h4><p>1. ChatGPTの呼び出し <code>search(“Elasticsearch migration”)</code></p><p>2. Elasticsearchがハイブリッド検索を実行する</p><ul><li><p><strong>セマンティック検索は</strong>「アップグレード」や「<em>バージョン互換性」などの概念を理解します。</em></p></li><li><p><strong>テキスト検索</strong>で「<em>Elasticsearch</em>」と「migration」の完全一致を見つけます。</p></li><li><p><strong>RRF</strong>は両方のアプローチの結果を組み合わせてランク付けします。</p></li></ul><p>3. <code>id</code>、<code>title</code>を含むトップ10のマッチングイベントを返します。 <code>url</code></p><p>4. ChatGPTは「<em>ISSUE-1712: migrate from Elasticsearch 7.x to 8.x</em>」を最も関連性の高い結果として特定します。</p><h3>プロンプト2：完全な詳細を取得する</h3><p>質問：<em><strong>「ISSUE-1889の詳細を教えて」</strong></em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8d1a53db8bfe8326/6a17f09f445de966104d021a/5c0db5245535ce67a36056e61e135bddc97ce496-934x629.png" alt="ChatGPTは、ユーザーが特定のイシューに関する詳細な情報を求めていることを認識し、フェッチツールを呼び出し、ツールに対して何らかのアクションを実行する前にユーザーに確認します。" /><p>ChatGPTは、あなたが特定のイシューに関する詳細な情報を求めていることを認識し、<code>fetch</code>ツールを呼び出し、ツールに対して何らかのアクションを起こす前にユーザーに確認します。</p><h4>ツール呼び出しリクエスト：</h4>{
  "id": "ISSUE-1889"
}<h4>ツールの応答：</h4>{
  "id": "ISSUE-1889",
  "title": "SQL injection vulnerability in search endpoint",
  "text": "Description: Security audit identified SQL injection vulnerability in /api/v1/search endpoint. User input from query parameter is not properly sanitized before being used in raw SQL query. Severity: HIGH - Immediate action required Affected Code: - File: services/search/query_builder.py - Line: 145-152 - Issue: String concatenation used instead of parameterized queries Investigation: - @security_team_alice: Confirmed exploitable with UNION-based injection - @sarah_dev: Checking all other endpoints for similar patterns - @john_backend: Found 3 more instances in legacy codebase Remediation: - Rewrite using SQLAlchemy ORM or parameterized queries - Add input validation and sanitization - Implement WAF rules as additional layer - Security regression tests Comments: - @tech_lead_mike: Stop all other work, this is P0 - @sarah_dev: PR-578 ready with fixes for all 4 vulnerable endpoints - @alex_devops: Deployed hotfix to production 2025-09-19 at 14:30 UTC - @security_team_alice: Verified fix, conducting full pentest next week Resolution: All vulnerable endpoints patched. Added pre-commit hooks to catch raw SQL queries. Security training scheduled for team.",
  "url": "https://internal-git.techcorp.com/issues/1889",
  "type": "issue",
  "status": "closed",
  "priority": "critical",
  "assignee": "sarah_dev",
  "created_date": "2025-09-18",
  "resolved_date": "2025-09-19",
  "labels": "security, vulnerability, bug, sql",
  "related_pr": "PR-578"
}<p>ChatGPTは情報を統合し、明確に提示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt560958fa3bd212d0/6a17f0a0faa91355ba93c974/410f19f213e94fc4e3c47eeef6e04b69e0c86159-602x462.png" alt="ChatGPTがどのように情報を統合して提示するか " /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcccf35a584e8373b/6a17f0a2505ac3471cad8c2e/54d8ffa117628a1e3afc317c3ab75d4f7731d7ab-767x1600.png" alt="ChatGPTがどのように情報を提示するか" /><h3>仕組み</h3><h4>プロンプト：「ISSUE-1889の詳細を教えて」</h4><ol><li><p>ChatGPT呼び出し <code>fetch(“ISSUE-1889”)</code></p></li><li><p>Elasticsearchが完全な文書を取得する</p></li><li><p>すべてのフィールドがルートレベルにある完全な文書を返す</p></li><li><p>ChatGPTは情報を統合し、適切な引用で回答する</p></li></ol><h2>まとめ</h2><p>この記事では、専用の<strong>検索</strong>および<strong>フェッチ</strong>MCPツールを使用してChatGPTをElasticsearchに接続するカスタムMCPサーバーを構築し、プライベートデータに対する自然言語クエリを可能にしました。</p><p>このMCPパターンは、自然言語を使用してクエリしたい任意のElasticsearchインデックス、ドキュメント、製品、ログ、またはその他のデータで機能します。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/chatgpt-connector-mcp-server-github-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/chatgpt-connector-mcp-server-github-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1602c48878dc7a9/6a17f09a6df731cca90a0fff/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif" length="0" type="image/gif"/>
    <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LangChainとElasticsearchを使用したエージェントRAGアシスタントの開発]]></title>
    <description><![CDATA[LangChain と Elasticsearch を使用して、適応型ルーティングで記事に関するクエリに回答するエージェント型ラグニュース アシスタントを構築する方法を学びます。]]></description>
    <content:encoded><![CDATA[<p>このブログ記事では、エージェント RAG ワークフローについて詳しく説明し、その主な機能と一般的な設計パターンについて説明します。さらに、Elasticsearch をベクター ストアとして使用し、LangChain を使用してエージェント RAG フレームワークを構築する実践的な例を通じて、これらのワークフローを実装する方法を示します。最後に、この記事では、このようなアーキテクチャの設計と実装に関連するベスト プラクティスと課題について簡単に説明します。この<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Jupyter ノートブック</a>に従って、シンプルなエージェント RAG パイプラインを作成できます。</p><h2>エージェントRAGの紹介</h2><p>検索拡張生成 ( <a href="https://www.elastic.co/docs/solutions/search/rag">RAG</a> ) は LLM ベースのアプリケーションの基礎となり、モデルがユーザーのクエリに基づいて関連するコンテキストを取得することで最適な回答を提供できるようになりました。RAG システムは、事前にトレーニングされた LLM 知識に限定されるのではなく、API またはデータ ストアからの外部情報を利用することで、LLM 応答の精度とコンテキストを強化します。一方、AI エージェントは自律的に動作し、指定された目的を達成するために意思決定とアクションを実行します。</p><p>Agentic RAG は、検索強化生成とエージェント推論の両方の長所を統合したフレームワークです。RAG をエージェントの意思決定プロセスに統合することで、システムはデータ ソースを動的に選択し、クエリを改良してコンテキスト検索を改善し、より正確な応答を生成し、フィードバック ループを適用して出力品質を継続的に向上できるようになります。</p><h2>エージェントRAGの主な特徴</h2><p>エージェント RAG フレームワークは、従来の RAG システムに比べて大きな進歩を遂げています。固定された検索プロセスに従うのではなく、結果をリアルタイムで計画、実行、最適化できる動的エージェントを活用します。</p><p>エージェント RAG パイプラインを区別する主な機能のいくつかを見てみましょう。</p><ul><li><p><strong>動的な意思決定</strong>: Agentic RAG は推論メカニズムを使用してユーザーの意図を理解し、各クエリを最も関連性の高いデータ ソースにルーティングして、正確でコンテキストに応じた応答を生成します。</p></li><li><p><strong>包括的なクエリ分析:</strong> Agentic RAG は、サブ質問とその全体的な意図を含むユーザークエリを詳細に分析します。クエリの複雑さを評価し、最も関連性の高いデータ ソースを動的に選択して情報を取得し、正確で完全な応答を保証します。</p></li><li><p><strong>多段階コラボレーション</strong>: このフレームワークは、専門エージェントのネットワークを通じて多段階コラボレーションを可能にします。各エージェントは、より大きな目標の特定の部分を担当し、一貫した結果を達成するために順次または同時に作業します。</p></li><li><p><strong>自己評価メカニズム</strong>: エージェント RAG パイプラインは、自己反映を使用して、取得したドキュメントと生成された応答を評価します。取得した情報がクエリに完全に対応しているかどうかを確認し、出力の正確性、完全性、事実の一貫性を確認できます。</p></li><li><p><strong>外部ツールとの統合</strong>: このワークフローは、外部 API、データベース、リアルタイム情報ソースと対話して、最新の情報を取り込み、進化するデータに動的に適応できます。</p></li></ul><h2>エージェントRAGのワークフローパターン</h2><p>ワークフロー パターンは、エージェント AI が LLM ベースのアプリケーションを信頼性と効率性をもって構築、管理、調整する方法を定義します。<a href="https://www.langchain.com/">LangChain</a> 、 <a href="https://www.langchain.com/langgraph">LangGraph</a> 、 <a href="https://www.crewai.com/">CrewAI</a> 、 <a href="https://www.llamaindex.ai/">LlamaIndex</a>などのいくつかのフレームワークとプラットフォームを使用して、これらのエージェント ワークフローを実装できます。</p><ol><li><p><strong>順次取得チェーン</strong>: 順次ワークフローは、複雑なタスクを単純な順序付けられたステップに分割します。各ステップで次のステップの入力が改善され、より良い結果が得られます。たとえば、顧客プロファイルを作成する場合、1 人のエージェントが CRM から基本的な詳細を取得し、別のエージェントがトランザクション データベースから購入履歴を取得し、最後のエージェントがこの情報を組み合わせて推奨事項やレポート用の完全なプロファイルを生成します。</p></li><li><p><strong>ルーティング取得チェーン</strong>: このワークフロー パターンでは、ルーター エージェントが入力を分析し、最も適切なプロセスまたはデータ ソースに送信します。このアプローチは、重複が最小限で複数の異なるデータ ソースが存在する場合に特に効果的です。たとえば、顧客サービス システムでは、ルータ エージェントが技術的な問題、払い戻し、苦情などの受信リクエストを分類し、適切な部門にルーティングして効率的に処理します。</p></li><li><p><strong>並列検索チェーン</strong>: このワークフロー パターンでは、複数の独立したサブタスクが同時に実行され、それらの出力が後で集約されて最終的な応答が生成されます。このアプローチにより、処理時間が大幅に短縮され、ワークフローの効率が向上します。たとえば、顧客サービスの並列ワークフローでは、1 人のエージェントが過去の同様のリクエストを取得し、別のエージェントが関連するナレッジベースの記事を参照します。アグリゲータはこれらの出力を組み合わせて包括的な解決策を生成します。</p></li><li><p><strong>オーケストレーター ワーカー チェーン</strong>: このワークフローは、独立したサブタスクを利用するため、並列化と類似点があります。ただし、重要な違いはオーケストレーター エージェントの統合にあります。このエージェントは、ユーザークエリを分析し、実行時にそれらをサブタスクに動的に分割し、正確な応答を作成するために必要な適切なプロセスまたはツールを識別する役割を担います。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1e2e634cf9c94e25/6a17ff81b1e113c9fc79f4e0/ece6fc2403f211556c93e99d5227bfb7053b0c31-1600x1047.png" alt="エージェントRAGのワークフローパターン" /><h2>エージェントRAGパイプラインをゼロから構築する</h2><p>エージェント RAG の原理を説明するために、LangChain と Elasticsearch を使用してワークフローを設計してみましょう。このワークフローはルーティングベースのアーキテクチャを採用しており、複数のエージェントが連携してクエリを分析し、関連情報を取得し、結果を評価し、一貫した応答を生成します。この例に従うには、この<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Jupyter ノートブック</a>を参照してください。</p><p>ワークフローはルータ エージェントから開始され、ルータ エージェントはユーザーのクエリを分析して最適な取得方法 ( <code>vectorstore</code> 、 <code>websearch</code> 、または<code>composite</code>のいずれかのアプローチ) を選択します。ベクターストアは従来の RAG ベースのドキュメント検索を処理し、Web 検索はベクターストアに保存されていない最新の情報を取得し、複合アプローチは複数のソースからの情報が必要な場合に両方を組み合わせます。</p><p>ドキュメントが適切であると判断された場合、要約エージェントは明確で文脈に適した応答を生成します。ただし、ドキュメントが不十分または無関係な場合、クエリ書き換えエージェントはクエリを再作成して検索を改善します。この修正されたクエリによりルーティング プロセスが再開され、システムは検索を絞り込み、最終出力を強化できるようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16020333cf6dda91/6a17ff82e8fbceb4d83a1c00/ed8701a7f15558fbf2e967a884b3e770eccb826b-1256x1092.png" alt="エージェントシステムがさまざまなクエリで出力を洗練させる方法" /><h3>要件</h3><p>このワークフローは、例を効果的に実行するために次のコア コンポーネントに依存しています。</p><ul><li><p>Python 3.10</p></li><li><p><a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Jupyterノートブック</a></p></li><li><p>Azure OpenAI</p></li><li><p>Elasticsearch</p></li><li><p>LangChain</p></li></ul><p>続行する前に、この例に必要な次の環境変数のセットを構成するように求められます。</p>AZURE_OPENAI_ENDPOINT="Add your azure openai endpoint"
AZURE_OPENAI_KEY="Add your azure openai key"
AZURE_OPENAI_DEPLOYMENT="gpt-4.1"
AZURE_OPENAI_API_VERSION="Add your azure openai api version"

ES_ENDPOINT = "Add your Elasticsearch ENDPOINT"
ES_API_KEY = "Add your Elasticsearch API KEY"<h3>データソース</h3><p>このワークフローは、AG ニュース データセットのサブセットを使用して説明されています。このデータセットは、国際、スポーツ、ビジネス、科学技術など、さまざまなカテゴリのニュース記事で構成されています。</p>dataset = load_dataset("ag_news", split="train[:1000]")
docs = [
    Document(
        page_content=sample["text"],
        metadata={"category": sample["label"]}
    )
    for sample in dataset
]<p><a href="https://python.langchain.com/docs/integrations/vectorstores/elasticsearch/">ElasticsearchStore モジュールは</a>、 <code>langchain_elasticsearch</code>からベクター ストアとして利用されます。検索には、Elastic 独自の埋め込みモデルである<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">ELSER</a>を採用した SparseVectorStrategy を実装します。ベクター ストアを開始する前に、ELSER モデルが Elasticsearch 環境に正しくインストールされ、デプロイされていることを確認することが重要です。</p>elastic_vectorstore = ElasticsearchStore.from_documents(
    docs,
    es_url=ES_ENDPOINT,
    es_api_key=ES_API_KEY,
    index_name=index_name,
    strategy=SparseVectorStrategy(model_id=".elser_model_2"),
)

elastic_vectorstore.client.indices.refresh(index=index_name)<p>Web 検索機能は、LangChain コミュニティ ツールの<a href="https://python.langchain.com/api_reference/community/tools/langchain_community.tools.ddg_search.tool.DuckDuckGoSearchRun.html">DuckDuckGoSearchRun</a>を使用して実装されており、これによりシステムは Web からライブ情報を効率的に取得できます。より関連性の高い結果を提供できる他の検索 API の使用も検討できます。このツールは、API キーを必要とせずに検索できるため選択されました。</p>duckduckgo = DuckDuckGoSearchRun(description= "A custom DuckDuckGo search tool for finding latest news stories.", verbose=True)
def websearch_retriever(query):
    results = duckduckgo.run(f"{query}")
    return results<p>複合リトリーバーは、ソースの組み合わせを必要とするクエリ用に設計されています。これは、Web からリアルタイム データを取得すると同時に、ベクター ストアから過去のニュースを参照することで、包括的かつ文脈的に正確な応答を提供するために使用されます。</p>def composite_retriever(query):
    related_docs = vectorstore_retriever(query)
    related_docs += websearch_retriever(query)
    return related_docs<h3>エージェントの設定</h3><p>次のステップでは、このワークフロー内で推論および意思決定機能を提供する LLM エージェントが定義されます。作成する LLM チェーンには、 <code>router_chain</code> 、 <code>grade_docs_chain</code> 、 <code>rewrite_query_chain</code> 、 <code>summary_chain</code>が含まれます。</p><p>ルータ エージェントは LLM アシスタントを使用して、実行時に特定のクエリに最適なデータ ソースを決定します。グレーディングエージェントは、取得したドキュメントの関連性を評価します。ドキュメントが関連していると判断された場合、それらは要約エージェントに渡され、要約が生成されます。それ以外の場合、書き換えクエリ エージェントはクエリを再作成し、別の取得を試行するためにルーティング プロセスに送り返します。すべてのエージェントの手順は、<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">ノートブック</a>の LLM チェーン セクションに記載されています。</p>class RouteQuery(BaseModel):
    datasource: Literal["vectorstore", "websearch", "composite"] = Field(
        ...,
        description="Choose to route the query to web search, vectorstore or composite."
    )

router_prompt = ChatPromptTemplate.from_template("""You are an assistant that decides the best data source for questions based on news articles.
Choose one of the following options:
- 'vectorstore': for general, background, or historical news articles.
- 'websearch': for recent discoveries, 'latest', 'current', or '2025' type queries.
- 'composite': when the question needs both historical and current knowledge on news articles.

Question: {query}

Return one word: 'vectorstore', 'websearch', or 'composite'.
""")
router_structured = llm.with_structured_output(RouteQuery)
router_chain: RunnableSequence = router_prompt | router_structured<p><code>llm.with_structured_output</code>は、モデルの出力を<code>RouteQuery</code>クラスの BaseModel によって定義された定義済みスキーマに従うように制限し、結果の一貫性を保証します。2 行目は、 <code>router_prompt</code>と<code>router_structured</code>を接続して<code>RunnableSequence</code>を構成し、入力プロンプトが言語モデルによって処理され、構造化されたスキーマ準拠の結果が生成されるパイプラインを形成します。</p><h3>グラフノードを定義する</h3><p>この部分では、システムのさまざまなコンポーネント間を流れるデータを表すグラフの状態を定義します。これらの状態を明確に指定することで、ワークフロー内の各ノードがアクセスおよび更新できる情報を確実に認識できるようになります。</p>class RAGState(TypedDict):
    query: str
    docs: List[Document]
    router: str
    summary: str
    self_reflection: bool
    retry_count: int = 0<p>状態が定義されたら、次のステップはグラフのノードを定義することです。ノードは、データに対して特定の操作を実行するグラフの機能単位のようなものです。パイプラインには 7 つの異なるノードがあります。</p>def router(state: RAGState):
   router = router_chain.invoke({'query': state["query"]})
   logger.info(f"Router selected the datasource: {router.datasource}")
   logger.info(f"User query: {state['query']}")
   return {"router": router.datasource}

def vectorstore(state: RAGState):
   return {"docs": vectorstore_retriever(state["query"])}

def websearch(state: RAGState):
   return {"docs": websearch_retriever(state["query"])}

def composite(state: RAGState):
   return {"docs": composite_retriever(state["query"])}

def self_reflection(state: RAGState):
   evaluation = grade_docs_chain.invoke(
       {"query": state["query"], "docs": state["docs"]}
   )
   if evaluation.binary_score:
       logger.info(f"Self-reflection passed -- binary_score={evaluation.binary_score}")
   else:
       logger.info(f"Self-reflection failed -- binary_score={evaluation.binary_score}")

   return {
       "self_reflection": evaluation.binary_score,
   }

def query_rewriter(state: RAGState):
   retry_count = state.get("retry_count", 0) + 1
   new_query = rewrite_query_chain.invoke({"query": state["query"]})
   logger.info(f"Query rewritten: {new_query}, retry_count: {retry_count}")
   return {
       "query": new_query,
       "retry_count": retry_count,
   }

def summarize(state: RAGState):
   summary = summarize_chain.run(
       query=state["query"],
       docs=state["docs"],
   )
   return {"summary": summary}<p><code>query_rewriter</code>ノードはワークフロー内で 2 つの目的を果たします。まず、自己反省エージェントによって評価された文書が不十分または無関係であると判断された場合に、 <code>rewrite_query_chain</code>を使用してユーザークエリを書き換え、検索を改善します。2 番目に、クエリが書き換えられた回数を追跡するカウンターとして機能します。</p><p>ノードが呼び出されるたびに、ワークフロー状態に格納されている<code>retry_count</code>が増加します。このメカニズムにより、ワークフローが無限ループに陥るのを防ぎます。<code>retry_count</code>が事前定義されたしきい値を超えた場合、システムはエラー状態、デフォルトの応答、または選択したその他の定義済み条件にフォールバックできます。</p><h3>グラフのコンパイル</h3><p>最後のステップは、グラフのエッジを定義し、コンパイルする前に必要な条件を追加することです。すべてのグラフは、ワークフローのエントリ ポイントとして機能する指定された開始ノードから開始する必要があります。グラフ内のエッジはノード間のデータの流れを表し、次の 2 つのタイプがあります。</p><ul><li><p>直線エッジ: 1 つのノードから別のノードへの直接的で無条件のフローを定義します。最初のノードがタスクを完了すると、ワークフローは直線に沿って次のノードに自動的に進みます。</p></li><li><p>条件付きエッジ: これにより、現在の状態またはノードの計算結果に基づいてワークフローを分岐できます。次のノードは、評価結果、ルーティングの決定、再試行回数などの条件に応じて動的に選択されます。</p></li></ul>graph.add_edge(START, "router")

def after_router(state: RAGState):
   route = state.get("router", None)
   if route == "vectorstore":
       return "vectorstore"
   elif route == "websearch":
       return "websearch"
   else:
       return "composite"

def after_self_reflection(state: RAGState):
   if state["self_reflection"]:
           return "summarize"
   return "query_rewriter"

def after_query_rewriter(state: RAGState):
   while state['retry_count'] &lt;= 3:
           return "router"
   raise RuntimeError("Maximum retries (3) reached -- evaluation failed.")

graph.add_conditional_edges(
   "router",
   after_router,
   {
       "vectorstore": "vectorstore",
       "websearch": "websearch",
       "composite": "composite"
   }
)

graph.add_edge("vectorstore", "self_reflection")
graph.add_edge("websearch", "self_reflection")
graph.add_edge("composite", "self_reflection")
graph.add_conditional_edges(
   "self_reflection",
   after_self_reflection,
   {
       "summarize": "summarize",
       "query_rewriter": "query_rewriter"
   }
)
graph.add_conditional_edges("query_rewriter", after_query_rewriter, {"router": "router"})
graph.add_edge("summarize", END)
agent=graph.compile()<p>これで、最初のエージェント RAG パイプラインの準備が整い、コンパイルされたエージェントを使用してテストできるようになります。</p>result = agent.invoke({"query": query1})
logger.info(f"\nFinal Summary:\n: {result['summary']}")<h3>エージェントRAGパイプラインのテスト</h3><p>次に、以下の 3 つの異なるタイプのクエリを使用してこのパイプラインをテストします。結果は異なる場合があり、以下に示す例は潜在的な結果の 1 つを示しているにすぎないことに注意してください。</p>query1="What are the latest AI models released this month?"
query2="What technological innovations are discussed in Sci/Tech news?"
query3="Compare a Sci/Tech article from the dataset with a current web article about AI trends."<p>最初のクエリでは、ルータはデータ ソースとして<code>websearch</code>を選択します。クエリは自己反映評価に失敗し、出力に示されているように、その後クエリ書き換えステージにリダイレクトされます。</p>INFO     | __main__:router:11 - Router selected the datasource: websearch
INFO     | __main__:router:12 - User query: What are the latest AI models released this month?
Latest Singapore news, including the city state's relationships with Malaysia and Mahathir, China and Xi Jinping, and the rest of Southeast Asia. 3 days ago · The latest military news, insights and analysis from China. All the latest news, opinions and analysis on Hong Kong, China, Asia and around the world Latest news, in-depth features and opinion on Malaysia, covering politics, economy, society and the Asean member-nation's relationships with China, Singapore, and other Southeast Asian ... Oct 12, 2025 · Brics (an acronym for Brazil, Russia, India, China and South Africa) refers to an association of 10 leading emerging markets. The other member states are Egypt, Ethiopia, ...
INFO     | __main__:self_reflection:31 - Self-reflection failed -- binary_score=False
INFO     | __main__:query_rewriter:40 - Query rewritten: query='Which AI models have been officially released in June 2024?', retry_count: 1
INFO     | __main__:router:11 - Router selected the datasource: websearch
INFO     | __main__:router:12 - User query: query='Which AI models have been officially released in June 2024?'
Dream Machine is a text-to-video model created by Luma Labs and launched in June 2024 . It generates video output based on user prompts or still images. Dream Machine has been noted for its ability to realistically capture motion... Released in June 2023. In June 2024 , Baidu announced Ernie 4.0 Turbo. In April 2025, Ernie 4.5 Turbo and X1 Turbo were released . These models are optimized for faster response times and lower operational costs.[28][29]. The meaning of QUERY is question, inquiry. How to use query in a sentence. Synonym Discussion of Query. QUERY definition: 1. a question, often expressing doubt about something or looking for an answer from an authority.... Learn more. Query definition: a question; an inquiry.. See examples of QUERY used in a sentence.
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:&lt;module&gt;:2 - 
Final Summary:
: In June 2024, two AI models were officially released: Dream Machine, a text-to-video model launched by Luma Labs, and Ernie 4.0 Turbo, announced by Baidu, which is optimized for faster response times and lower operational costs.<p>次に、2 番目のクエリで示されているように、 <code>vectorstore</code>検索が使用される例を調べます。</p>INFO     | __main__:router:11 - Router selected the datasource: vectorstore
INFO     | __main__:router:12 - User query: What technological innovations are discussed in Sci/Tech news?
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:&lt;module&gt;:2 - 
Final Summary:
: Recent Sci/Tech news highlights several technological innovations: NASA is collaborating with Silicon Valley firms to build a powerful Linux-based supercomputer to support theoretical research and shuttle engineering; new chromatin transfer techniques have enabled the cloning of cats; cybersecurity advancements are being discussed in relation to protecting personal technology; Princeton University scientists assert that existing technologies can be used immediately to stabilize global warming; and a set of GameBoy micro-games has been recognized for innovation in game design.<p>最後のクエリは、ベクターストアと Web 検索の両方を活用する複合検索に向けられます。</p>INFO     | __main__:router:11 - Router selected the datasource: composite
INFO     | __main__:router:12 - User query: Compare a Sci/Tech article from the dataset with a current web article about AI trends.
Atlas currently only available on macOS, built on Chromium with planned features like ad-blocking still in development. OpenAI's Atlas browser launched with bold promises of AI -powered web browsing, but early real-world testing reveals a different story. Career-long data are updated to end-of-2024 and single recent year data pertain to citations received during calendar year 2024. The selection is based on the top 100,000 scientists by c-score (with and without self-citations) or a percentile rank of 2% or above in the sub-field. In this article I list 45 AI tools across 21 different categories. After exploring all the available options in each category, I've carefully selected the best tools based on my personal experience. Reading a complex technical article ? Simply highlight confusing terminology and ask "what's this?" to receive instant explanations. compare browsers. Comparison showing traditional browser navigation versus OpenAI Atlas AI -powered workflows. After putting Gemini, ChatGPT, Grok, and DeepSeek through rigorous testing in October 2025, it's clear that there isn't one AI that reigns supreme across all categories.
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:&lt;module&gt;:2 - 
Final Summary:
: A Sci/Tech article from the dataset highlights NASA's development of robust artificial intelligence software for planetary rovers, aiming to make them more self-reliant and capable of decision-making during missions. In contrast, a current web article about AI trends focuses on the proliferation of AI-powered tools across various categories, including browsers like OpenAI Atlas, and compares leading models such as Gemini, ChatGPT, Grok, and DeepSeek, noting that no single AI currently excels in all areas. While the NASA article emphasizes specialized AI applications for autonomous robotics in space exploration, the current trends article showcases the broadening impact of AI across consumer and professional technologies, with ongoing competition and rapid innovation among major AI platforms.<p>上記のワークフローでは、エージェント RAG は、ユーザー クエリの情報を取得するときに使用するデータ ソースをインテリジェントに決定し、応答の精度と関連性を向上させます。追加の例を作成してエージェントをテストし、出力を確認して興味深い結果が得られるかどうかを確認できます。</p><h2>エージェント型 RAG ワークフローを構築するためのベストプラクティス</h2><p>エージェント RAG の仕組みがわかったので、次にこれらのワークフローを構築するためのベスト プラクティスをいくつか見てみましょう。これらのガイドラインに従うことで、システムを効率的に維持し、保守を容易にすることができます。</p><ul><li><p><strong>フォールバックの準備</strong>: ワークフローのいずれかのステップが失敗するシナリオに備えて、事前にフォールバック戦略を計画します。これらには、デフォルトの回答を返すこと、エラー状態をトリガーすること、代替ツールを使用することなどが含まれます。これにより、全体的なワークフローを中断することなく、システムが障害を適切に処理できるようになります。</p></li><li><p><strong>包括的なログ記録を実装する</strong>: 再試行、生成された出力、ルーティングの選択、クエリの書き換えなど、ワークフローの各段階でログ記録を実装してみます。これらのログは、透明性の向上、デバッグの容易化、プロンプト、エージェントの動作、および取得戦略の継続的な改善に役立ちます。</p></li><li><p><strong>適切なワークフロー パターンを選択する</strong>: ユースケースを調べて、ニーズに最適なワークフロー パターンを選択します。ステップごとの推論には順次ワークフローを使用し、独立したデータ ソースには並列ワークフローを使用し、複数のツールや複雑なクエリにはオーケストレーター ワーカー パターンを使用します。</p></li><li><p><strong>評価戦略を組み込む</strong>: ワークフローのさまざまな段階で評価メカニズムを統合します。これらには、自己反省エージェント、取得したドキュメントの採点、自動品質チェックなどが含まれます。評価は、取得されたドキュメントが関連性があり、応答が正確であり、複雑なクエリのすべての部分が処理されていることを確認するのに役立ちます。</p></li></ul><h2>課題</h2><p>エージェント RAG システムは、適応性、精度、動的推論の点で大きな利点を提供しますが、設計および実装の段階で対処する必要がある特定の課題も伴います。主な課題は次のとおりです。</p><ul><li><p><strong>複雑なワークフロー</strong>: エージェントと決定ポイントが追加されるにつれて、全体的なワークフローはますます複雑になります。これにより、実行時にエラーや障害が発生する可能性が高くなります。可能な限り、冗長なエージェントと不要な意思決定ポイントを排除して、合理化されたワークフローを優先します。</p></li><li><p><strong>スケーラビリティ</strong>: 大規模なデータセットと大量のクエリを処理するためにエージェント RAG システムを拡張するのは難しい場合があります。効率的なインデックス作成、キャッシュ、分散処理戦略を組み込んで、大規模なパフォーマンスを維持します。</p></li><li><p><strong>オーケストレーションと計算オーバーヘッド</strong>: 複数のエージェントによるワークフローの実行には、高度なオーケストレーションが必要です。これには、ボトルネックや競合を防ぐための慎重なスケジュール設定、依存関係の管理、エージェントの調整が含まれますが、これらはすべてシステム全体の複雑さを増大させます。</p></li><li><p><strong>評価の複雑さ</strong>: これらのワークフローの評価には、各段階で異なる評価戦略が必要になるため、固有の課題が伴います。たとえば、RAG ステージでは、取得したドキュメントの関連性と完全性を評価する必要があり、生成された要約については品質と正確性をチェックする必要があります。同様に、クエリの再定式化の有効性には、書き換えられたクエリによって検索結果が改善されるかどうかを判断するための別の評価ロジックが必要です。</p></li></ul><h2>まとめ</h2><p>このブログ投稿では、エージェント RAG の概念を紹介し、エージェント AI の自律機能を組み込むことで従来の RAG フレームワークをどのように強化するかについて説明しました。私たちは、エージェント RAG のコア機能を探り、Elasticsearch をベクター ストアとして使用し、LangChain を使用してエージェント フレームワークを作成するニュース アシスタントを構築するという実践的な例を通じて、これらの機能を実演しました。</p><p>さらに、エージェント RAG パイプラインを設計および実装する際に考慮すべきベスト プラクティスと主な課題についても説明しました。これらの洞察は、検索、推論、意思決定を効果的に組み合わせた、堅牢でスケーラブルかつ効率的なエージェント システムを開発者が作成できるようにするためのものです。</p><h2>次のステップ</h2><p>私たちが構築したワークフローはシンプルで、改善と実験の余地が十分にあります。さまざまな埋め込みモデルを試し、検索戦略を改良することで、これを強化できます。さらに、検索されたドキュメントに優先順位を付ける再ランク付けエージェントを統合すると有益となる可能性があります。もう一つの調査領域は、エージェント フレームワークの評価戦略の開発、具体的にはさまざまな種類のフレームワークに適用可能な共通かつ再利用可能なアプローチの特定です。最後に、これらのフレームワークを大規模でより複雑なデータセットで実験します。</p><p>その間、同様の実験を共有していただける場合は、ぜひお聞かせください。お気軽にフィードバックをお寄せください。また、<a href="https://ela.st/slack">コミュニティの Slack チャンネル</a>や<a href="https://discuss.elastic.co/c/security">ディスカッション フォーラム</a>を通じてご連絡いただくこともできます。</p><h2>各種資料</h2><ul><li><p><a href="https://arxiv.org/abs/2310.11511">セルフRAG：自己反省を通して、情報の検索、生成、批評を学ぶ</a></p></li><li><p><a href="https://arxiv.org/abs/2501.09136">エージェント検索拡張生成：エージェントRAGの調査</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agentic-rag-news-assistant-langchain-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-rag-news-assistant-langchain-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Kirti Sodhi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8c7a9f3b0d141d5d/6a17ff83fbc5f86686491d15/59dc0077f5dab00561d9f1b1e7dbf8ec3456259e-1600x1047.heif" length="0" type="image/*"/>
    <pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Agent Builder と GPT-OSS を使用した HR 向け AI エージェントの構築]]></title>
    <description><![CDATA[Elastic Agent Builder と GPT-OSS を使用して、従業員の HR データに関する自然言語クエリに回答できる AI エージェントを構築する方法を学びます。]]></description>
    <content:encoded><![CDATA[<h2>はじめに</h2><p>この記事では<a href="https://openai.com/index/introducing-gpt-oss/">、GPT-OSS</a>と Elastic Agent Builder を使用して HR 向けの AI エージェントを構築する方法を説明します。エージェントは、OpenAI、Anthropic、その他の外部サービスにデータを送信せずに質問に答えることができます。</p><p>LM Studio を使用して GPT-OSS をローカルで提供し、Elastic Agent Builder に接続します。</p><p>この記事を読み終える頃には、情報とモデルを完全に制御しながら、従業員データに関する自然言語の質問に答えることができるカスタム AI エージェントが完成しているはずです。</p><h2>要件</h2><p>この記事には以下が必要です:</p><ul><li><p><a href="https://www.elastic.co/cloud">Elastic Cloud</a>ホスト 9.2、サーバーレスまたは<a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">ローカル</a>展開</p></li><li><p>32GB RAM搭載マシンを推奨（GPT-OSS 20Bの場合は最低16GB）</p></li><li><p><a href="https://lmstudio.ai/">LM Studio</a>がインストール済み</p></li><li><p><a href="https://www.docker.com/products/docker-desktop/">Dockerデスクトップ</a>がインストール済み</p></li></ul><h2>GPT-OSS を使用する理由は何ですか?</h2><p>ローカル LLM を使用すると、独自のインフラストラクチャに LLM を展開し、独自のニーズに合わせて微調整することができます。モデルと共有するデータの制御を維持しながら、これらすべてを実行できます。もちろん、外部プロバイダーにライセンス料を支払う必要はありません。</p><p>OpenAI は、オープン モデル エコシステムへの取り組みの一環として、2025 年 8 月 5 日に<a href="https://openai.com/index/introducing-gpt-oss/">GPT-OSS をリリースしました</a>。</p><p>20B パラメータ モデルは以下を提供します。</p><ul><li><p><strong>ツール使用能力</strong></p></li><li><p><strong>効率的な推論</strong></p></li><li><p><strong>OpenAI SDK対応</strong></p></li><li><p><strong>エージェントワークフローと互換性あり</strong></p></li></ul><p>ベンチマーク比較:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt58fab956edb40412/6a170cfcb0367da43a72bd80/29160e3345352088e8213297630882f252b00c47-1600x680.png" alt="" /><h2>ソリューションアーキテクチャ</h2><p>アーキテクチャは完全にローカル マシン上で実行されます。Elastic (Docker で実行) は LM Studio を介してローカル LLM と直接通信し、Elastic Agent Builder はこの接続を使用して従業員データを照会できるカスタム AI エージェントを作成します。</p><p>詳細については、 こちらの<a href="https://www.elastic.co/docs/solutions/observability/connect-to-own-local-llm">ドキュメント</a>を参照してください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80db5bb0a797f51b/6a170cfd0e2e492f2c41a16f/a4a886750ff25fa8bb7aefc7448161e52cf73ed3-1600x896.png" alt="" /><h2>HR向けAIエージェントの構築：手順</h2><p>実装は 5 つのステップに分けられます。</p><ol><li><p>ローカルモデルでLMスタジオを構成する</p></li><li><p>DockerでローカルElasticをデプロイする</p></li><li><p>ElasticでOpenAIコネクタを作成する</p></li><li><p>従業員データをElasticsearchにアップロードする</p></li><li><p>AIエージェントを構築してテストする</p></li></ol><h2>ステップ1：LM StudioをGPT-OSS 20Bで構成する</h2><p>LM Studio は、大規模な言語モデルをコンピュータ上でローカルに実行できるユーザーフレンドリーなアプリケーションです。OpenAI 互換の API サーバーを提供するため、複雑なセットアップ プロセスなしで Elastic などのツールと簡単に統合できます。詳細については、 <a href="https://lmstudio.ai/docs/app">LM Studio ドキュメント</a>を参照してください。</p><p>まず、公式サイトからLM Studioをダウンロードしてインストールします。インストールしたら、アプリケーションを開きます。</p><h3>LM Studio インターフェースの場合:</h3><ol><li><p>検索タブに移動して「GPT-OSS」を検索します。</p></li><li><p>OpenAIから<code>openai/gpt-oss-20b</code>を選択してください</p></li><li><p>ダウンロードをクリック</p></li></ol><p>このモデルのサイズは約<strong>12.10 GB</strong>になります。インターネット接続によっては、ダウンロードに数分かかる場合があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2dc341a6625e34b7/6a170cff839dfa2eb4dcff44/5d01bc4dcb377b5259fc6b521fe2425a31b90ca4-1312x872.png" alt="" /><h4>モデルをダウンロードしたら:</h4><ol><li><p>ローカルサーバータブに移動します</p></li><li><p>openai/gpt-oss-20bを選択します</p></li><li><p>デフォルトのポート1234を使用する</p></li><li><p>右側のパネルで、 <strong>「ロード」</strong>に移動し、コンテキストの長さを<strong>40K</strong>以上に設定します。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3704ca1b28465cc4/6a170d00d7c022ed8fde64ef/e546033f916381647b876815b2c1f1ae2a08365f-326x337.png" alt="" /><p>5. サーバーの開始をクリック</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b9170a4945ff857/6a170d0266c4f9ffadf8c0a6/28ee78a3caa84d14e04db3d42f30acbe4d4d005a-1312x872.png" alt="" /><p>サーバーが実行中の場合はこれが表示されます。</p>[LM STUDIO SERVER] Success! HTTP server listening on port 1234
[LM STUDIO SERVER] Supported endpoints:
[LM STUDIO SERVER] -&gt;	GET  http://localhost:1234/v1/models
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/responses
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/chat/completions
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/completions
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/embeddings
Server started.<h2>ステップ2: DockerでローカルElasticをデプロイする</h2><p>ここで、Docker を使用して Elasticsearch と Kibana をローカルにセットアップします。Elastic は、セットアッププロセス全体を処理する便利なスクリプトを提供します。詳細については、<a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">公式ドキュメント</a>を参照してください。</p><h3>start-local スクリプトを実行する</h3><p>ターミナルで次のコマンドを実行します。</p>curl -fsSL https://elastic.co/start-local | sh<p>このスクリプトは次のことを行います。</p><ul><li><p>ElasticsearchとKibanaをダウンロードして設定する</p></li><li><p>Docker Composeを使用して両方のサービスを開始します</p></li><li><p>30日間のプラチナトライアルライセンスを自動的に有効化</p></li></ul><h3>期待される出力</h3><p>次のメッセージが表示されるまで待ち、表示されるパスワードと API キーを保存します。これらは Kibana にアクセスするために必要になります。</p>🎉 Congrats, Elasticsearch and Kibana are installed and running in Docker!
🌐 Open your browser at http://localhost:5601
   Username: elastic
   Password: KSUlOMNr
🔌 Elasticsearch API endpoint: http://localhost:9200
🔑 API key: cnJGX0pwb0JhOG00cmNJVklUNXg6cnNJdXZWMnM4bncwMllpQlFlUTlWdw==
Learn more at https://github.com/elastic/start-local<h3>Kibanaにアクセスする</h3><p>ブラウザを開いて次の場所に移動します:</p>http://localhost:5601<p>ターミナル出力で取得した資格情報を使用してログインします。</p><h3>エージェントビルダーを有効にする</h3><p>Kibana にログインしたら、 <strong>[Management]</strong> &gt; <strong>[AI]</strong> &gt; <strong>[Agent Builder]</strong>に移動して、Agent Builder をアクティブ化します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a934bd99fa6a0ce/6a170d046234e019c3db1a5a/92e104cb846c20d875865ded8a3d37f5c7daae9b-1491x1528.png" alt="" /><h2>ステップ3: ElasticでOpenAIコネクタを作成する</h2><p>ここで、ローカル LLM を使用するように Elastic を構成します。</p><h3>アクセスコネクタ</h3><ol><li><p>キバナで</p></li><li><p><strong>プロジェクト設定</strong>&gt;<strong>管理</strong>に移動します</p></li><li><p><strong>アラートとインサイトの</strong>下で、<strong>コネクタ</strong>を選択します。</p></li><li><p>コネクタの作成をクリック</p></li></ol><h3>コネクタを構成する</h3><p>コネクタのリストから<strong>OpenAI を</strong>選択します。LM Studio は OpenAI SDK を使用しているため、互換性があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt762023c39781eb78/6a170d06a29299a59ed01087/5ac87042e086c7a2bd47a8039e646ec831f0dcc6-923x974.png" alt="" /><p>次の値をフィールドに入力します。</p><ul><li><p><strong>コネクタ名:</strong> LM Studio - GPT-OSS 20B</p></li><li><p><strong>OpenAIプロバイダーを選択:</strong>その他 (OpenAI互換サービス)</p></li><li><p><strong>URL: </strong><code>http://host.docker.internal:1234/v1/chat/completions</code></p></li><li><p><strong>デフォルトモデル:</strong> openai/gpt-oss-20b</p></li><li><p><strong>API キー:</strong> testkey-123 (LM Studio Server では認証が不要なので、任意のテキストを使用できます。)</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt980e595f80e2be2e/6a170d086f7f0468a19148cc/2084ac32fcf1fb810c8b54ecab1c85a1e3e8905b-672x1302.png" alt="" /><p>設定を完了するには、 <strong>「保存してテスト」</strong>をクリックします。</p><p><strong>重要:</strong> 「<strong>ネイティブ関数の呼び出しを有効にする</strong>」をオンにします。これは、Agent Builder が正しく動作するために必要です。これを有効にしないと、 <strong><code>No tool calls found in the response</code></strong>エラーが発生します。</p><h3>接続をテストする</h3><p>Elastic は自動的に接続をテストするはずです。すべてが正しく構成されている場合、次のような成功メッセージが表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d2e815dd558f881/6a170d090e2e49076541a177/f567d767f1969c4730c1daa92f651789dc3742ac-1042x812.png" alt="" /><p>対応：</p>{
  "status": "ok",
  "data": {
    "id": "chatcmpl-flj9h0hy4wcx4bfson00an",
    "object": "chat.completion",
    "created": 1761189456,
    "model": "openai/gpt-oss-20b",
    "choices": [
      {
        "index": 0,
        "message": {
          "role": "assistant",
          "content": "Hello! 👋 How can I assist you today?",
          "reasoning": "Just greet.",
          "tool_calls": []
        },
        "logprobs": null,
        "finish_reason": "stop"
      }
    ],
    "usage": {
      "prompt_tokens": 69,
      "completion_tokens": 23,
      "total_tokens": 92
    },
    "stats": {},
    "system_fingerprint": "openai/gpt-oss-20b"
  },
  "actionId": "ee1c3aaf-bad0-4ada-8149-118f52dad757"
}<h2>ステップ4: 従業員データをElasticsearchにアップロードする</h2><p>ここで、 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">HR 従業員データセット</a>をアップロードして、エージェントが機密データをどのように処理するかを説明します。私はこの構造を持つ架空のデータセットを生成しました。</p><h3>データセットの構造</h3>{
  "employee_id": "0f4dce68-2a09-4cb1-b2af-6bcb4821539b",
  "full_name": "Daffi Stiebler",
  "email": "lscutchings0@huffingtonpost.com",
  "date_of_birth": "1975-06-20T15:39:36Z",
  "hire_date": "2025-07-28T00:10:45Z",
  "job_title": "Physical Therapy Assistant",
  "department": "HR",
  "salary": "108455",
  "performance_rating": "Needs Improvement",
  "years_of_experience": 2,
  "skills": "Java",
  "education_level": "Master's Degree",
  "manager": "Carl MacGibbon",
  "emergency_contact": "Leigha Scutchings",
  "home_address": "5571 6th Park"
}<h3>マッピングを使用してインデックスを作成する</h3><p>まず、適切なマッピングを使用してインデックスを作成します。一部のキー フィールドに<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a>フィールドを使用していることに注意してください。これにより、インデックスのセマンティック検索機能が有効になります。</p>​​PUT hr-employees
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "employee_id": {
        "type": "keyword"
      },
      "full_name": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "email": {
        "type": "keyword"
      },
      "date_of_birth": {
        "type": "date",
        "format": "iso8601"
      },
      "hire_date": {
        "type": "date",
        "format": "iso8601"
      },
      "job_title": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "department": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "salary": {
        "type": "double"
      },
      "performance_rating": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "years_of_experience": {
        "type": "long"
      },
      "skills": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "education_level": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "manager": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "emergency_contact": {
        "type": "keyword"
      },
      "home_address": {
        "type": "keyword"
      },
      "employee_semantic": {
        "type": "semantic_text"
      }
    }
  }
}<h3>Bulk APIを使用したインデックス</h3><p><a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">データセット</a>をコピーして Kibana の開発ツールに貼り付け、実行します。</p>POST hr-employees/_bulk
{"index": {}}
{"employee_id": "57728b91-e5d7-4fa8-954a-2384040d3886", "full_name": "Filide Gane", "email": "vhallahan1@booking.com", "job_title": "Business Systems Development Analyst", "department": "Marketing", "salary": "$52330.27", "performance_rating": "Meets Expectations", "years_of_experience": 12, "skills": "Java", "education_level": "Bachelor's Degree", "date_of_birth": "2000-02-07T16:49:32Z", "hire_date": "2023-11-07T13:03:16Z", "manager": "Freedman Kings", "emergency_contact": "Vilhelmina Hallahan", "home_address": "75 Dennis Junction"}
{"index": {}}
{"employee_id": "...", ...}<h3>データを検証する</h3><p>クエリを実行して確認します。</p>GET hr-employees/_search<h2>ステップ5: AIエージェントを構築してテストする</h2><p>すべての設定が完了したら、Elastic Agent Builder を使用してカスタム AI エージェントを構築します。詳細については、 <a href="https://www.elastic.co/docs/solutions/search/agent-builder/get-started">Elastic のドキュメント</a>を参照してください。</p><h3>コネクタを追加する</h3><p>新しいエージェントを作成する前に、デフォルトのコネクタは<a href="https://www.elastic.co/docs/reference/kibana/connectors-kibana/elastic-managed-llm">Elastic Managed LLM</a>であるため、 <code>LM Studio - GPT-OSS 20B</code>というカスタム コネクタを使用するようにエージェント ビルダーを設定する必要があります。そのためには、 <strong>「プロジェクト設定」</strong> &gt; <strong>「管理」</strong> &gt; <strong>「GenAI 設定」</strong>に移動し、作成した設定を選択して<strong>「保存」</strong>をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc42f079c5e756057/6a170d0acf4f2501d9b2d1c7/11e830c3e2fb4c298b020c928fa5422f3397ba08-1600x1152.png" alt="" /><h3>アクセスエージェントビルダー</h3><ol><li><p><strong>エージェント</strong>へ</p></li><li><p><strong>「新しいエージェントを作成」</strong>をクリックします</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8e734817c5a7c6a/6a170d0ca929cf867cae0a34/c1e60541563650163f972ac9088dc1ed1de759a7-1600x1054.png" alt="" /><h3>エージェントを構成する</h3><p>新しいエージェントを作成するには、<strong>エージェント ID</strong> 、<strong>表示名</strong>、および<strong>表示手順</strong>が必須フィールドです。</p><p>ただし、システム プロンプトに似ていますが、カスタム エージェント用の、エージェントの動作やツールとの対話方法をガイドするカスタム インストラクションなど、さらに多くのカスタマイズ オプションがあります。ラベルは、エージェント、アバターの色、アバター シンボルを整理するのに役立ちます。</p><p>データセットに基づいてエージェント用に選択したものは次のとおりです。

<strong>エージェントID:</strong> <code>hr_assistant</code></p><p><strong>カスタム指示:</strong></p>You are an HR Analytics Assistant that helps answer questions about employee data.
When responding to queries:
- Provide clear, concise answers
- Include relevant employee details (name, department, salary, skills)
- Format monetary values with currency symbols
- Be professional and maintain data confidentiality<p>
ラベル: <code>Human Resources</code>および <code>GPT-OSS</code></p><p>表示名： <code>HR Analytics Assistant</code></p><p>表示の説明:</p>A specialized AI assistant for Human Resources that helps analyze employee data, compensation, performance metrics, and talent management. Ask questions about employees, departments, salaries, or performance analytics.<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt23fb011e5b4f4d49/6a170d0e7d8d67f47a70e77f/f94bb2bf08497e5e756ca76b30a3a51f42927756-1424x1217.png" alt="" /><p>すべてのデータが入力されたら、新しいエージェントの<strong>「保存」</strong>をクリックします。</p><h3>エージェントをテストする</h3><p>従業員データについて自然言語で質問できるようになり、GPT-OSS 20B が意図を理解して適切な応答を生成します。</p><h4>プロンプト：</h4>Which employee is the one with the highest salary in the hr-employees index?<h4>答え：</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0c52faacf63b583/6a170d0f0e2e497bfd41a17b/94ad19f80b96304028a59f60beca51dfc9aecc8a-899x631.png" alt="" /><p>エージェントのプロセスは次のとおりです。</p><p>1. GPT-OSSコネクタを使用して質問を理解する</p><p>2. 適切なElasticsearchクエリを生成する（組み込みツールまたはカスタム<a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a>を使用）</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte32a8a7e6363c7f2/6a170d115091680077e1bb44/6f2961d0d1b97475f6dda300acee84da540938e6-844x466.png" alt="" /><p>3. 一致する従業員レコードを取得する</p><p>4. 適切なフォーマットで自然言語で結果を提示する</p><p>従来の語彙検索とは異なり、GPT-OSS を搭載したエージェントは意図とコンテキストを理解するため、正確なフィールド名やクエリ構文を知らなくても情報を簡単に見つけることができます。エージェントの思考プロセスの詳細については、こちらの<a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance">記事</a>を参照してください。</p><h2>まとめ</h2><p>この記事では、Elastic の Agent Builder を使用してカスタム AI エージェントを構築し、ローカルで実行されている OpenAI GPT-OSS モデルに接続しました。このアーキテクチャでは、Elastic と LLM の両方をローカルマシンにデプロイすることで、外部サービスに情報を送信することなく、データに対する完全な制御を維持しながら生成 AI 機能を活用できます。</p><p>実験としてはGPT-OSS 20Bを使用しましたが、Elastic Agent Builderの公式推奨モデルは<a href="https://www.elastic.co/docs/solutions/search/agent-builder/models#recommended-models">こちらを</a>参考にしています。より高度な推論機能が必要な場合は、複雑なシナリオでより優れたパフォーマンスを発揮する<a href="https://huggingface.co/openai/gpt-oss-120b">120B パラメータ バリアント</a>もありますが、ローカルで実行するにはより高性能なマシンが必要です。詳細については、 <a href="https://openai.com/open-models/">OpenAI の公式ドキュメント</a>を参照してください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt664f490053e46e6b/6a170d13b0367d2d7e72bd84/05d2d0513fff67d975f9223d75108aa9f50646bc-1600x914.png" length="0" type="image/png"/>
    <pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cal Hacks 12.0 で取り上げた Elastic Agent Builder のトッププロジェクトと学習内容]]></title>
    <description><![CDATA[Cal Hacks 12.0 のトップ Elastic Agent Builder プロジェクトを探索し、サーバーレス、ES|QL、エージェント アーキテクチャに関する技術的なポイントを詳しく調べます。]]></description>
    <content:encoded><![CDATA[<p>数週間前、私たちは、世界中から 2,000 人を超える参加者が集まる最大規模の対面ハッカソンの 1 つである<a href="https://cal-hacks-12-0.devpost.com/">Cal Hacks 12.0 を</a>スポンサーするという素晴らしい機会を得ました。Elastic Agent Builder on Serverless の最も優れた活用方法に専用の賞品トラックを設けましたが、反響は驚くほど大きかったです。わずか 36 時間で、山火事インテリジェンス ツールの構築から StackOverflow バリデーターまで、Agent Builder を独創的な方法で使用した 29 件の応募を受け取りました。</p><p>Cal Hacks 12.0 での経験は、印象的なプロジェクト以外にも、同様に貴重なものをもたらしてくれました。それは、初めて当社のスタックに遭遇した開発者からの、迅速でフィルターされていないフィードバックです。ハッカソンは、厳しい期限、事前の知識ゼロ、そして予測不可能な障害（悪名高い WiFi の停止など）を伴う、ユニークなプレッシャーテストです。開発者エクスペリエンスが優れている点と、まだ改善が必要な点が正確に明らかになります。開発者が LLM 主導のワークフローを通じて新しい方法で Elastic Stack を操作することが増えているため、これは現在さらに重要になっています。このブログ投稿では、参加者が Agent Builder を使用して構築したものと、そのプロセスで学んだことについてさらに詳しく説明します。</p><h2>受賞プロジェクト</h2><h3>1位: AgentOverflow</h3><p>LLM およびエージェント時代に合わせて再構築された Stack Overflow。</p><p>AgentOverflow の詳細については、<a href="https://devpost.com/software/agentoverflow">こちらを</a>ご覧ください。</p><p>AgentOverflow は、ほとんどの AI 開発者が遭遇する問題、つまり LLM が幻覚を起こし、チャット履歴が消え、開発者が同じ問題を再度解決するのに時間を無駄にする問題に対処します。</p><p>AgentOverflow は実際の問題と解決策のペアをキャプチャ、検証、再表示するため、開発者は幻覚スパイラルを打破し、より早く製品を出荷できます。</p><h4>仕組み：</h4><p><strong>1. JSON（「ソリューション スキーマ」）を共有します。</strong></p><p>Claude の共有から 1 回クリックすると、次の内容を含む構造化形式である Share Solution JSON がスクレイピング、抽出、組み立てられます。</p><ul><li><p>問題</p></li><li><p>コンテクスト</p></li><li><p>コード</p></li><li><p>タグ</p></li><li><p>検証済みの解決手順。</p></li></ul><p>バリデーター (LAVA) が構造をチェックして強制し、ユーザーが追加のコンテキストの行を追加すると、Elasticsearch 内に保存されてインデックスが作成されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7bc35b6d54921e8/6a17f0176df73162760a0fe6/45a3e96f4474050a855419628c2a7338bb12c706-1600x877.png" alt="「ソリューションを共有」をクリックすると、現在のセッションと関連するメタデータがスクレイピングされます。" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9967f52007fff99e/6a17f019ec0f8987c45a6701/2d65cb154d8ee32fc96ff17dfa5b0bf2636e3777-1600x1002.png" alt="ユーザーはWebフロントエンドを通じて追加のコンテキストを提供し、JSONはElasticsearchでインデックス化されます。" /><p><strong>2. 解決策を見つける</strong></p><p>行き詰まったら、 <code>Find Solution</code>をクリックすると、AgentOverflow が現在の会話をスクレイピングし、それを使用してクエリを作成し、ハイブリッド Elasticsearch 検索を実行して次の内容を表示します。</p><ul><li><p>ランク付けされたコミュニティ検証済みの修正</p></li><li><p>当初問題を解決した正確なプロンプト</p></li></ul><p>これにより、開発者は現在のセッションをすばやくコピー、貼り付け、ブロック解除できます。</p><p><strong>3. MCP - LLMのコンテキスト注入</strong></p><p>MCP (モデル コンテキスト プロトコル) を介して Elasticsearch 内に保存された構造化ソリューションに接続することにより、LLM には実行時に余分なノイズなしで高度な信号コンテキスト (コード、ログ、構成、以前の修正) が供給されます。</p><p>AgentOverflow は、関連するコンテキストを LLM に挿入する構造化メモリ レイヤーとして、Elasticsearch を備えた Agent Builder を使用します。これにより、受動的なチャットボットからコンテキストを認識した問題解決者へと変化します。</p><h3>準優勝：マーケットマインド</h3><p>6 つの Elastic Agent を活用した、市場エネルギーのリアルタイムの解釈可能なビュー。</p><p>MarketMindの詳細については、<a href="https://devpost.com/software/marketmind-b6cy2q">こちらを</a>ご覧ください。</p><p>MarketMind は、初心者トレーダーに、断片化された市場データを明確でリアルタイムなシグナルに変換するプラットフォームを提供することで、その地位を獲得しました。MarketMind は、さまざまなツール間で価格変動、ファンダメンタルズ、センチメント、ボラティリティを調整する代わりに、これらすべての情報を 1 つのプラットフォームに統合し、トレーダーが実用的な洞察を得られるよう支援します。このプロジェクトでは、エージェントの構築時に複雑な ES|QL クエリも使用しました。</p><h4>仕組み：</h4><p><strong>1. リアルタイムの市場データを収集する</strong></p><p>MarketMind は、Yahoo Finance から価格動向、ファンダメンタルズ、センチメント、ボラティリティ、リスク指標を取得します。このデータは複数の Elasticsearch インデックスに取り込まれ、整理されます。</p><p><strong>2. 6人の専門エージェントが市場を分析</strong></p><p>Agent Builder で構築された各エージェントは、市場の異なる層に焦点を当てています。これらは Elasticsearch インデックスから読み取り、独自のドメイン固有のメトリックを計算し、スコアと推論を含む標準化された JSON 出力を生成します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4ba582f9872b65b/6a17f01b7f6f15c2d8c09c1c/7d9716cca06a047a2b3584378b5c7e592a785ba1-1284x878.png" alt="市場を分析する6つの専門的GOOGL AIエージェント" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd86ed3bfe4b8bd2b/6a17f01c5ea30f868164b6ba/5aac6a833347c0d2e596c02049ec4b4d3aae5cd7-794x764.png" alt="GOOGL特化エージェントのボリューム異常と大惨事検出分析機能" /><p><strong>3. シグナルを統合した「市場エネルギー」モデルに集約する</strong></p><p>組み合わせた出力は各株の周囲に光るパルスとして表示され、勢いが高まっているのか、リスクが高まっているのか、感情が変化しているのかを示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5af7c7c838308275/6a17f01e42022917b629f6ca/46b3da8e3d528c5dd4e2829416c5446098acb3aa-744x718.png" alt="GOOGL特化エージェントの統一「市場エネルギー」モデル" /><p><strong>4. 洞察を視覚化する</strong></p><p>フロントエンドは、TypeScript、SVG 物理ベースのビジュアル、ライブ ローソク足チャート用の<a href="https://github.com/chartjs"> Chart.js</a> を使用して、React と<a href="https://github.com/vercel/next.js"> Next.js で構築されました。</a>これにより、生の分析がリアルタイムで実用的なフィードバックに変換されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt775e1880aa7afacc/6a17f01f1d1b83ce1f93e528/3f000c043117b77ed4127202be5a49c12e3682ba-1600x930.png" alt="GOOGL特化エージェント分析の洞察を視覚化する方法" /><h2>その他の興味深いプロジェクト:</h2><p>スタックのさまざまな部分で Elastic を使用した他の有力な候補を次に示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltffe292009e446a70/6a17f0216df731068c0a0fea/76c49a853426844f475cd6b2a74999e60af20e8c-926x1080.png" alt="" /><p>私たちのトラックに提出されたプロジェクトの完全なリストは、<a href="https://cal-hacks-12-0.devpost.com/submissions/search?utf8=%E2%9C%93&amp;prize_filter%5Bprizes%5D%5B%5D=91882">こちらで</a>ご覧ください。</p><h2>開発者から学んだこと</h2><ul><li><p><strong>Agent Builder はユーザーフレンドリーです:</strong></p></li></ul><p>ほとんどのチームはこれまで Elastic を使用したことがありませんでしたが、それでもほとんどサポートなしでエージェントを迅速に構築できました。さらに詳しい指導が必要な人向けにワークショップを開催しましたが、ほとんどの人はデータを取り込み、そのデータに基づいてアクションを実行するエージェントを構築することができました。</p><ul><li><p>LLM は<strong><code>kNN</code></strong>クエリ<strong>に優れています</strong><strong>が、ES|QL の生成には依然としてガイダンスが必要です。</strong></p></li></ul><p>ChatGPT-5 に ES|QL クエリの生成を依頼すると、ES|QL と SQL が混在するなど、誤った情報が返されることがよくありました。LLM にマークダウン ファイルでドキュメントを供給することは、実行可能な修正であるように思われました。</p><ul><li><p><strong>スナップショット専用の ES|QL 関数がドキュメントに漏洩しました:</strong></p></li></ul><p>今後登場する<code>FIRST</code>および<code>LAST</code>集計関数が、意図せず ES|QL ドキュメントに紛れ込んでしまいました。これらのドキュメントを ChatGPT に渡したため、Serverless ではまだ利用できないにもかかわらず、モデルはこれらの関数を忠実に使用しました。グループからのフィードバックのおかげで、エンジニアリングはすぐに修正を公開し、マージして、公開されたドキュメントから関数を削除しました ( <a href="https://github.com/elastic/elasticsearch/pull/137341">PR #137341</a> )。</p><ul><li><p><strong>サーバーレス固有のガイダンスが不足しています:</strong></p></li></ul><p>チームは、ルックアップ モードで作成されなかったインデックスで<code>LOOKUP JOIN</code>有効にしようとしました。エラー メッセージにより、Serverless に存在しないコマンドが追跡されました。私たちはこれを製品チームに伝え、製品チームはすぐに Serverless 固有の実用的なメッセージの修正を開始しました。長期的には、再インデックスの複雑さを完全に隠すことがビジョンです (<a href="https://github.com/elastic/elasticsearch-serverless/issues/4838">問題 #4838</a> )。</p><ul><li><p><strong>対面イベントの価値:</strong></p></li></ul><p>オンライン ハッカソンは素晴らしいですが、ビルダーと肩を並べてデバッグしているときに得られる迅速なフィードバック ループに匹敵するものはありません。私たちは、チームがさまざまなユースケースにわたって Agent Builder を統合する様子を観察し、ES|QL を使用した開発者エクスペリエンスを改善できる部分を見つけ、非同期チャネルで解決するよりもはるかに迅速に問題を修正しました。</p><h2>まとめ</h2><p>Cal Hacks 12.0 では、素晴らしいデモを週末にわたって披露するだけでなく、新しい開発者が Elastic Stack とどのように関わっているかについても理解することができました。わずか 36 時間で、チームは Agent Builder を導入し、Elasticsearch にデータを取り込み、マルチエージェント システムを設計し、さまざまな方法で機能をテストするようになりました。このイベントは、対面イベントがなぜ重要なのかを私たちに思い出させてくれました。迅速なフィードバック ループ、実際の会話、実践的なデバッグにより、現在の開発者のニーズを理解することができました。私たちが学んだことをエンジニアリング チームに還元できることを嬉しく思います。次回のハッカソンでお会いしましょう。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f079179be9832d4/6a17f023631730a69c585b6d/8ba034a6f19b50521f541b8131756a8acdb52975-1280x960.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch で A2A プロトコルと MCP を使用して LLM エージェント ニュースルームを作成する: パート II]]></title>
    <description><![CDATA[エージェントのコラボレーションに A2A プロトコルを使用し、Elasticsearch でのツール アクセスに MCP を使用して、特殊なハイブリッド LLM エージェント ニュースルームを構築する方法を説明します。]]></description>
    <content:encoded><![CDATA[<h2>A2AとMCP：コードの動作</h2><p>これは、記事「Elasticsearch で A2A プロトコルと MCP を使用して LLM エージェント ニュースルームを作成する」の補足記事です。この記事では、同じエージェント内に A2A と MCP の両方のアーキテクチャを実装して、両方のフレームワークの独自のメリットを最大限に活用するメリットについて説明しました。自分でデモを実行したい場合、<a href="https://github.com/justincastilla/elastic-newsroom">リポジトリ</a>が利用可能です。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt232e466d2153c764/6a17f15f631730042d585b8d/7196f004089127f83547b2e5dc3f663205cfcdce-1162x1600.png" alt="A2A &amp; MCP プロトコルエージェントワークフロー" /><p>ニュースルームのエージェントが A2A と MCP の両方を使用して協力し、ニュース記事を作成する方法を見ていきましょう。エージェントの動作を確認するための付属リポジトリは、<a href="https://github.com/justincastilla/elastic-newsroom">ここに</a>あります。</p><h3>ステップ1：ストーリーの割り当て</h3><p><strong>ニュースチーフ</strong>（クライアントとして行動）がストーリーを割り当てます。</p>{
  "message_type": "task_request",
  "sender": "news_chief",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "story_renewable_energy_2024",
    "assignment": {
      "topic": "Renewable Energy Adoption in Europe",
      "angle": "Policy changes driving solar and wind expansion",
      "target_length": 1200,
      "deadline": "2025-09-30T18:00:00Z"
    }
  }
}<h3>ステップ2: 記者が調査を依頼する</h3><p><strong>レポーター エージェントは</strong>背景情報が必要であることを認識し、A2A を介して<strong>リサーチャー エージェント</strong>に委任します。</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "researcher_agent",
  "payload": {
    "task_id": "research_eu_renewable_2024",
    "parent_task_id": "story_renewable_energy_2024",
    "capability": "fact_gathering",
    "parameters": {
      "queries": [
        "EU renewable energy capacity 2024",
        "Solar installations growth Europe",
        "Wind energy policy changes 2024"
      ],
      "depth": "comprehensive"
    }
  }
}<h3>ステップ3: 報告者がアーカイブエージェントに歴史的背景をリクエストする</h3><p><strong>レポーターエージェントは</strong>、歴史的背景が記事の内容を強めることを認識しています。A2A 経由で<strong>アーカイブエージェント</strong>( <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">Elastic の A2A エージェント</a>を搭載) に委任し、ニュースルームの Elasticsearch 搭載記事アーカイブを検索します。</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "archive_agent",
  "payload": {
    "task_id": "archive_search_renewable_2024",
    "parent_task_id": "story_renewable_energy_2024",
    "capability": "search_archive",
    "parameters": {
      "query": "European renewable energy policy changes and adoption trends over past 5 years",
      "focus_areas": ["solar", "wind", "policy", "Germany", "France"],
      "time_range": "2019-2024",
      "result_count": 10
    }
  }
}<h3>ステップ4: アーカイブエージェントはMCPでElastic A2Aエージェントを使用する</h3><p><strong>アーカイブ エージェントは</strong>Elastic の A2A エージェントを使用し、A2A エージェントは MCP を使用して Elasticsearch ツールにアクセスします。これは、A2A がエージェントのコラボレーションを可能にし、MCP がツール アクセスを提供するハイブリッド アーキテクチャを示しています。</p># Archive Agent using Elastic A2A Agent
async def search_historical_articles(self, query_params):
    # The Archive Agent sends a request to Elastic's A2A Agent
    elastic_response = await self.a2a_client.send_request(
        agent="elastic_agent",
        capability="search_and_analyze",
        parameters={
            "natural_language_query": query_params["query"],
            "index_pattern": "newsroom-articles-*",
            "filters": {
                "topics": query_params["focus_areas"],
                "date_range": query_params["time_range"]
            },
            "analysis_type": "trend_analysis"
        }
    )
    
    # Elastic's A2A Agent internally uses MCP tools:
    # - platform.core.search (to find relevant articles)
    # - platform.core.generate_esql (to analyze trends)
    # - platform.core.index_explorer (to identify relevant indices)
    
    return elastic_response<p><strong>アーカイブエージェントは</strong>Elastic の A2A エージェントから包括的な履歴データを受信し、それをレポーターに返します。</p>{
  "message_type": "task_response",
  "sender": "archive_agent",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "archive_search_renewable_2024",
    "status": "completed",
    "archive_data": {
      "historical_articles": [
        {
          "title": "Germany's Energiewende: Five Years of Solar Growth",
          "published": "2022-06-15",
          "key_points": [
            "Germany added 7 GW annually 2020-2022",
            "Policy subsidies drove 60% of growth"
          ],
          "relevance_score": 0.94
        },
        {
          "title": "France Balances Nuclear and Renewables",
          "published": "2023-03-20",
          "key_points": [
            "France increased renewable target to 40% by 2030",
            "Solar capacity doubled 2021-2023"
          ],
          "relevance_score": 0.89
        }
      ],
      "trend_analysis": {
        "coverage_frequency": "EU renewable stories increased 150% since 2019",
        "emerging_themes": ["policy incentives", "grid modernization", "battery storage"],
        "coverage_gaps": ["Small member states", "offshore wind permitting"]
      },
      "total_articles_found": 47,
      "search_confidence": 0.91
    }
  }
}<p>このステップでは、Elastic の A2A エージェントがニュースルームのワークフローにどのように統合されるかを示します。Archive Agent（ニュースルーム固有のエージェント）は、Elastic の A2A Agent（サードパーティの専門家）と連携して、Elasticsearch の強力な検索および分析機能を活用します。Elastic のエージェントは内部的に MCP を使用して Elasticsearch ツールにアクセスし、エージェント調整 (A2A) とツール アクセス (MCP) を明確に分離します。</p><h3>ステップ5: 研究者はMCPサーバーを使用する</h3><p><strong>研究者エージェントは</strong>複数の MCP サーバーにアクセスして情報を収集します。</p># Researcher Agent using MCP to access tools
async def gather_facts(self, queries):
    results = []
    
    # Use News API MCP Server
    news_data = await self.mcp_client.invoke_tool(
        server="news_api_mcp",
        tool="search_articles",
        parameters={
            "query": queries[0],
            "date_range": "2024-01-01 to 2024-09-30",
            "sources": ["reuters", "bloomberg", "euractiv"]
        }
    )
    
    # Use Statistics MCP Server
    stats_data = await self.mcp_client.invoke_tool(
        server="eurostat_mcp",
        tool="get_energy_statistics",
        parameters={
            "metrics": ["solar_capacity_mw", "wind_capacity_mw"],
            "countries": ["EU27"],
            "year": 2024
        }
    )
    
    # Use Fact-Checking MCP Server
    verified_facts = await self.mcp_client.invoke_tool(
        server="factcheck_mcp",
        tool="verify_claims",
        parameters={
            "claims": [
                "EU solar capacity increased 40% in 2024",
                "Germany leads in wind installations"
            ]
        }
    )
    
    return self._synthesize_research(news_data, stats_data, verified_facts)<h3>ステップ6: 研究者が報告者にデータを返す</h3><p><strong>研究者エージェントは、</strong>包括的な研究を A2A 経由で返送します。</p>{
  "message_type": "task_response",
  "sender": "researcher_agent",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "research_eu_renewable_2024",
    "status": "completed",
    "research_data": {
      "key_facts": [
        {
          "claim": "EU solar capacity reached 260 GW in 2024",
          "source": "Eurostat Q3 2024 Report",
          "confidence": 0.95,
          "verified": true
        },
        {
          "claim": "Germany installed 12 GW of wind capacity in 2024",
          "source": "German Federal Network Agency",
          "confidence": 0.92,
          "verified": true
        }
      ],
      "statistics": {
        "solar_growth_rate": "35%",
        "wind_growth_rate": "28%"
      },
      "sources_count": 15
    }
  }
}<h3>ステップ7：記者が記事を書く</h3><p><strong>Reporter Agent は、</strong>調査データと独自の LLM 機能を使用して記事を作成します。書き込み中、Reporter はスタイルとテンプレートに MCP サーバーを使用します。</p># Reporter Agent writing with MCP assistance
async def write_article(self, research_data, assignment):
    # Get style guidelines via MCP
    style_guide = await self.mcp_client.get_resource(
        server="newsroom_mcp",
        resource="style://editorial/ap_style_guide"
    )
    
    # Get article template via MCP
    template = await self.mcp_client.get_resource(
        server="newsroom_mcp",
        resource="template://articles/news_story"
    )
    
    # Generate article using LLM + research + style
    draft = await self.llm.generate(
        prompt=f"""
        Write a news article following these guidelines:
        {style_guide}
        
        Using this template:
        {template}
        
        Based on this research:
        {research_data}
        
        Assignment: {assignment}
        """
    )
    
    # Self-evaluate confidence in claims
    confidence_check = await self._evaluate_confidence(draft)
    
    return draft, confidence_check<h3>ステップ8：自信が低い場合は再調査を促します</h3><p><strong>レポーター エージェントは</strong>下書きを評価し、1 つの主張の信頼性が低いことを発見しました。<strong>研究者エージェント</strong>に別のリクエストを送信します:</p>{
  "message_type": "collaboration_request",
  "sender": "reporter_agent",
  "receiver": "researcher_agent",
  "payload": {
    "request_type": "fact_verification",
    "claims": [
      {
        "text": "France's nuclear phase-down contributed to 15% increase in renewable capacity",
        "context": "Discussing policy drivers for renewable growth",
        "current_confidence": 0.45,
        "required_confidence": 0.80
      }
    ],
    "urgency": "high"
  }
}<p><strong>研究者は</strong>ファクトチェックMCPサーバーを使用して主張を検証し、更新された情報を返します。</p>{
  "message_type": "collaboration_response",
  "sender": "researcher_agent",
  "receiver": "reporter_agent",
  "payload": {
    "verified_claims": [
      {
        "original_claim": "France's nuclear phase-down contributed to 15% increase...",
        "verified_claim": "France's renewable capacity increased 18% in 2024, partially offsetting reduced nuclear output",
        "confidence": 0.88,
        "corrections": "Percentage was 18%, not 15%; nuclear phase-down is gradual, not primary driver",
        "sources": ["RTE France", "French Energy Ministry Report 2024"]
      }
    ]
  }
}<h3>ステップ9: 記者が修正して編集者に提出する</h3><p><strong>記者は</strong>検証された事実を組み込み、完成した原稿を A2A 経由で<strong>編集者エージェント</strong>に送信します。</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "editor_agent",
  "payload": {
    "task_id": "edit_renewable_story",
    "parent_task_id": "story_renewable_energy_2024",
    "content": {
      "headline": "Europe's Renewable Revolution: Solar and Wind Surge 30% in 2024",
      "body": "[Full article text...]",
      "word_count": 1185,
      "sources": [/* array of sources */]
    },
    "editing_requirements": {
      "check_style": true,
      "check_facts": true,
      "check_seo": true
    }
  }
}<h3>ステップ10: MCPツールを使用した編集者のレビュー</h3><p><strong>エディター エージェントは</strong>複数の MCP サーバーを使用して記事をレビューします。</p># Editor Agent using MCP for quality checks
async def review_article(self, content):
    # Grammar and style check
    grammar_issues = await self.mcp_client.invoke_tool(
        server="grammarly_mcp",
        tool="check_document",
        parameters={"text": content["body"]}
    )
    
    # SEO optimization check
    seo_analysis = await self.mcp_client.invoke_tool(
        server="seo_mcp",
        tool="analyze_content",
        parameters={
            "headline": content["headline"],
            "body": content["body"],
            "target_keywords": ["renewable energy", "Europe", "solar", "wind"]
        }
    )
    
    # Plagiarism check
    originality = await self.mcp_client.invoke_tool(
        server="plagiarism_mcp",
        tool="check_originality",
        parameters={"text": content["body"]}
    )
    
    # Generate editorial feedback
    feedback = await self._generate_feedback(
        grammar_issues, 
        seo_analysis, 
        originality
    )
    
    return feedback<p><strong>編集者は</strong>記事を承認し、送信します。</p>{
  "message_type": "task_response",
  "sender": "editor_agent",
  "receiver": "reporter_agent",
  "payload": {
    "status": "approved",
    "quality_score": 9.2,
    "minor_edits": [
      "Changed 'surge' to 'increased' in paragraph 3 for AP style consistency",
      "Added Oxford comma in list of countries"
    ],
    "approved_content": "[Final edited article]"
  }
}<h3>ステップ11: パブリッシャーがCI/CD経由でパブリッシュする</h3><p>最後に、<strong>プリンター エージェントは</strong>、CMS および CI/CD パイプラインの MCP サーバーを使用して承認された記事を公開します。</p># Publisher Agent publishing via MCP
async def publish_article(self, content, metadata):
    # Upload to CMS via MCP
    cms_result = await self.mcp_client.invoke_tool(
        server="wordpress_mcp",
        tool="create_post",
        parameters={
            "title": content["headline"],
            "body": content["body"],
            "status": "draft",
            "categories": metadata["categories"],
            "tags": metadata["tags"],
            "featured_image_url": metadata["image_url"]
        }
    )
    
    post_id = cms_result["post_id"]
    
    # Trigger CI/CD deployment via MCP
    deploy_result = await self.mcp_client.invoke_tool(
        server="cicd_mcp",
        tool="trigger_deployment",
        parameters={
            "pipeline": "publish_article",
            "environment": "production",
            "post_id": post_id,
            "schedule": "immediate"
        }
    )
    
    # Track analytics
    await self.mcp_client.invoke_tool(
        server="analytics_mcp",
        tool="register_publication",
        parameters={
            "post_id": post_id,
            "publish_time": datetime.now().isoformat(),
            "story_id": metadata["story_id"]
        }
    )
    
    return {
        "status": "published",
        "post_id": post_id,
        "url": f"https://newsroom.example.com/articles/{post_id}",
        "deployment_id": deploy_result["deployment_id"]
    }<p><strong>出版社は</strong>A2Aを通じて出版を確認します。</p>{
  "message_type": "task_complete",
  "sender": "printer_agent",
  "receiver": "news_chief",
  "payload": {
    "task_id": "story_renewable_energy_2024",
    "status": "published",
    "publication": {
      "url": "https://newsroom.example.com/articles/renewable-europe-2024",
      "published_at": "2025-09-30T17:45:00Z",
      "post_id": "12345"
    },
    "workflow_metrics": {
      "total_time_minutes": 45,
      "agents_involved": ["reporter", "researcher", "archive", "editor", "printer"],
      "iterations": 2,
      "mcp_calls": 12
    }
  }
}<p>以下は、上記と同じエージェントを使用した付属のリポジトリ内の A2A ワークフローの完全なシーケンスです。</p><p>#</p><p>から</p><p>に</p><p>アクション</p><p>プロトコル</p><p>説明</p><p>1</p><p>ユーザー</p><p>ニュースチーフ</p><p>ストーリーの割り当て</p><p>HTTP ポスト</p><p>ユーザーがストーリーのトピックと角度を提出する</p><p>2</p><p>ニュースチーフ</p><p>内部</p><p>ストーリーを作成する</p><p>-</p><p>固有のIDを持つストーリーレコードを作成します</p><p>3</p><p>ニュースチーフ</p><p>記者</p><p>委任の割り当て</p><p>A2A</p><p>A2Aプロトコル経由でストーリー割り当てを送信します</p><p>4</p><p>記者</p><p>内部</p><p>割り当てを受け入れる</p><p>-</p><p>割り当てを内部に保存する</p><p>5</p><p>記者</p><p>MCP サーバー</p><p>アウトラインを生成</p><p>MCP/HTTP</p><p>記事のアウトラインと研究の質問を作成します</p><p>6a</p><p>記者</p><p>研究者</p><p>調査依頼</p><p>A2A</p><p>質問を送信します（6bと並行）</p><p>6b</p><p>記者</p><p>アーキビスト</p><p>アーカイブを検索</p><p>A2A JSONRPC</p><p>歴史的な記事を検索します（6aと並行）</p><p>7</p><p>研究者</p><p>MCP サーバー</p><p>研究上の質問</p><p>MCP/HTTP</p><p>MCP経由でAnthropicを使用して質問に答えます</p><p>8</p><p>研究者</p><p>記者</p><p>リターンリサーチ</p><p>A2A</p><p>調査の回答を返す</p><p>9</p><p>アーキビスト</p><p>Elasticsearch</p><p>検索インデックス</p><p>ES REST API</p><p>news_archiveインデックスをクエリ</p><p>10</p><p>アーキビスト</p><p>記者</p><p>アーカイブに戻る</p><p>A2A JSONRPC</p><p>過去の検索結果を返します</p><p>11</p><p>記者</p><p>MCP サーバー</p><p>記事を生成する</p><p>MCP/HTTP</p><p>研究/アーカイブの文脈で記事を作成する</p><p>12</p><p>記者</p><p>内部</p><p>ストアドラフト</p><p>-</p><p>下書きを内部に保存</p><p>13</p><p>記者</p><p>ニュースチーフ</p><p>下書きを送信</p><p>A2A</p><p>完成した草稿を提出する</p><p>14</p><p>ニュースチーフ</p><p>内部</p><p>ストーリーを更新</p><p>-</p><p>下書きを保存し、ステータスを「draft_submitted」に更新します</p><p>15</p><p>ニュースチーフ</p><p>エディタ</p><p>レビュー草稿</p><p>A2A</p><p>レビューのために編集者に自動ルーティング</p><p>16</p><p>エディタ</p><p>MCP サーバー</p><p>総説</p><p>MCP/HTTP</p><p>MCP経由でAnthropicを使用してコンテンツを分析します</p><p>17</p><p>エディタ</p><p>ニュースチーフ</p><p>返品レビュー</p><p>A2A</p><p>編集上のフィードバックと提案を送信します</p><p>18</p><p>ニュースチーフ</p><p>内部</p><p>ストアレビュー</p><p>-</p><p>編集者のフィードバックを保存</p><p>19</p><p>ニュースチーフ</p><p>記者</p><p>編集を適用</p><p>A2A</p><p>レビューのフィードバックをレポーターに転送する</p><p>20</p><p>記者</p><p>MCP サーバー</p><p>編集を適用</p><p>MCP/HTTP</p><p>フィードバックに基づいて記事を修正する</p><p>21</p><p>記者</p><p>内部</p><p>下書きの更新</p><p>-</p><p>修正を加えて下書きを更新する</p><p>22</p><p>記者</p><p>ニュースチーフ</p><p>返品修正</p><p>A2A</p><p>修正された記事を返す</p><p>23</p><p>ニュースチーフ</p><p>内部</p><p>ストーリーを更新</p><p>-</p><p>修正した下書きを保存し、ステータスを「修正済み」にする</p><p>24</p><p>ニュースチーフ</p><p>出版社</p><p>記事を公開する</p><p>A2A</p><p>パブリッシャーへの自動ルーティング</p><p>25</p><p>出版社</p><p>MCP サーバー</p><p>タグを生成する</p><p>MCP/HTTP</p><p>タグとカテゴリを作成する</p><p>26</p><p>出版社</p><p>Elasticsearch</p><p>インデックス記事</p><p>ES REST API</p><p>記事をnews_archiveインデックスにインデックスします</p><p>27</p><p>出版社</p><p>ファイルシステム</p><p>マークダウンを保存</p><p>ファイルI/O</p><p>記事を.mdとして保存します/articles内のファイル</p><p>28</p><p>出版社</p><p>ニュースチーフ</p><p>公開の確認</p><p>A2A</p><p>成功ステータスを返します</p><p>29</p><p>ニュースチーフ</p><p>内部</p><p>ストーリーを更新</p><p>-</p><p>ストーリーのステータスを「公開済み」に更新します</p><h2>まとめ</h2><p>A2A と MCP はどちらも、現代の拡張 LLM インフラストラクチャ パラダイムにおいて重要な役割を果たします。A2A は複雑なマルチエージェント システムに柔軟性を提供しますが、移植性が低くなり、運用が複雑になる可能性があります。MCP は、マルチエージェント オーケストレーションを処理するようには設計されていませんが、実装と保守がより簡単なツール統合のための標準化されたアプローチを提供します。</p><p>選択は二者択一ではありません。私たちのニュースルームの例で示されているように、最も洗練され効果的な LLM 対応システムは、多くの場合、両方のアプローチを組み合わせています。つまり、エージェントは A2A プロトコルを通じて調整と専門化を行いながら、MCP サーバーを通じてツールやリソースにアクセスします。このハイブリッド アーキテクチャは、MCP の標準化とエコシステムの利点に加えて、マルチエージェント システムの組織上の利点も提供します。これは、選択する必要が全くないかもしれないことを示唆している。単に両方を標準的なアプローチとして使うだけでよい。</p><p>開発者またはアーキテクトとして、両方のソリューションの最適な組み合わせをテストして決定し、特定のユースケースに適した結果を生み出すのはあなた次第です。それぞれのアプローチの長所、制限、適切な適用を理解することで、より効果的で保守性と拡張性に優れた AI システムを構築できるようになります。</p><p>デジタル ニュースルーム、顧客サービス プラットフォーム、リサーチ アシスタント、またはその他の LLM を利用したアプリケーションを構築する場合でも、調整ニーズ (A2A) とツール アクセス要件 (MCP) を慎重に検討することで、成功への道が開かれます。</p><h2>参考資料</h2><ul><li><p><strong>Elasticsearch エージェントビルダー:</strong> <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">https://www.elastic.co/docs/solutions/search/elastic-agent-builder</a></p></li><li><p><strong>A2A仕様</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></p></li><li><p><strong>A2A と MCP の統合</strong>: <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">https://a2a-protocol.org/latest/topics/a2a-and-mcp/</a></p></li><li><p><strong>モデルコンテキストプロトコル</strong>: <a href="https://modelcontextprotocol.io/">https://modelcontextprotocol.io</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b1f22cdc2130333/6a17f161ec0f8917fa5a6712/f87330e5d4ca961593b3cfb861ca850a4cc34186-1519x1173.png" length="0" type="image/png"/>
    <pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[コンテキストエンジニアリングにおけるハイブリッド検索の威力 - パート3]]></title>
    <description><![CDATA[コンテキスト エンジニアリングとハイブリッド検索を使用して、集計、RBAC、非コンテンツ シグナルによって AI 出力の精度を向上させる方法を説明します。]]></description>
    <content:encoded><![CDATA[<p>ハイブリッド検索 (<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">パート I</a> ) とコンテキスト エンジニアリング (<a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">パート II</a> ) の両方について説明しました。次に、RAG およびエージェント AI 操作にターゲットを絞ったコンテキストを提供する上で、これらがどのように連携して最大の効果を発揮するかについて詳しく見ていきましょう。</p><h2>検索は死んでいない、ただ移動しただけだ</h2><p>そのため、主にテキスト ボックスでコンテキストを検索し、返された情報 (コンテキスト) を使用して自分で回答を構築するという方法から、自然言語を使用してエージェントに必要なものを伝え、エージェントが自動的に回答を調査してまとめる方法へと移行しました。テクノロジー業界の多くの人々は、この変化を指摘し、「検索は死んだ」と主張しています（まあ、SEO とアドワーズの世界は<a href="https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/">確実に変化しています</a>。GEO<a href="https://www.wired.com/story/goodbye-seo-hello-geo-brandlight-openai/">は</a>どうですか？）。しかし、検索は依然として代理店の業務にとって絶対に不可欠です。ただ、現在では主にツールを介して目に見えない形で実行されているだけです。</p><p>以前は、主観的な関連性の主な判断者は人間でした。各ユーザーには検索を実行する独自の理由があり、個人的な経験が結果の相対的な正確性に影響を与えていました。エージェントが私たちと同じ（あるいはそれ以上の）結論に達することができると信頼するには、エージェントがアクセスできるコンテキスト情報が私たちの主観的な意図に可能な限り近いことを保証する必要があります。私たちはその目標に向けて、LLM に提供するコンテキストを設計する必要があります。</p><h2>ハイブリッド検索によるコンテキストの生成</h2><p>パート I でもう一度お伝えしましたが、Elastic のハイブリッド検索は、従来のキーワードベースの検索の強み (構文の柔軟性、キーワードの精度、関連性のスコアリング) とベクトル類似性検索の意味理解を組み合わせ、複数の再ランキング手法を提供します。この相乗効果（この言葉のより正確な使い方はこれまで見つかりませんでした！）クエリによってコンテンツをターゲットする方法がより細かく指定できるため、関連性の高い結果を得ることができます。主観的関連性を検索段階の<em>1 つ</em>として適用できるというだけでなく、実際には、第 1 段階の検索に関連性スコアリングを他のすべてのモードとともに一度に含めることができるのです。</p><h3>優れた精度と効率</h3><p>分散検索、取得、再ランク付け機能を備えたデータ プラットフォームを主要なコンテキスト検索エンジンとして使用することは、非常に理にかなっています。高度なクエリ構文を使用して、主観的な意図の欠落したコンポーネントを追加し、返されるコンテキスト情報の価値を損なったり不明瞭にしたりする可能性のあるコンテンツを除外できます。利用可能な個々の構文オプションから選択することも、モダリティを単一の検索に組み合わせて、各データの種類を最もよく理解できる方法でターゲットにし、それらを再ランク付けして組み合わせたり並べ替えたりすることもできます。不要なデータを除外し、必要なフィールド/値のみが含まれるように応答をフィルタリングできます。エージェントにとって、このターゲティングの柔軟性により、コンテキストを非常に正確に取得できるツールを構築できます。</p><h3>コンテキストの洗練（集約と非コンテンツシグナル）</h3><p>集約は、ツールがコンテキスト ウィンドウに配信するコンテンツを形成する際に特に役立ちます。集計により、返されるコンテキスト データの形状に関する数値ベースの事実が自然に提供されるため、LLM による推論がより容易かつ正確になります。集計は階層的にネストできるため、LLM に複数レベルの詳細を追加して、より微妙な理解を深めることが簡単にできます。集計はコンテキスト ウィンドウのサイズの管理にも役立ちます。10 万件のドキュメントのクエリ結果を、集約された分析情報の数百トークンに簡単に減らすことができます。</p><p>非コンテンツ シグナルは、データに内在する指標であり、見ているものの全体像を示します。つまり、人気、鮮度、地理的位置、カテゴリ、ホストの多様性、価格帯など、結果の追加特性です。これらの情報は、エージェントが受け取ったコンテキストの重要性をどのように評価するかをエージェントに通知するのに役立ちます。これを最もよく説明するために、いくつかの簡単な例を挙げます。</p><ul><li><p><strong>最近公開されたコンテンツや人気コンテンツの強化</strong>- 記事のナレッジ ベースがあると想像してください。ユーザーのクエリに関連する記事を見つけたいが、最近の記事であり、他のユーザーに役立つと判断された記事（「いいね」の数が多いなど）を優先したいとします。このシナリオでは、ハイブリッド検索を使用して関連する記事を見つけ、公開日と人気度の組み合わせに基づいて記事を再ランク付けすることができます。</p></li><li><p><strong>売上と在庫調整を伴う電子商取引の検索</strong>- 電子商取引の設定では、検索語に一致する製品を顧客に表示したいだけでなく、売れ行きがよく在庫がある製品を宣伝したいとも考えます。顧客の不満を避けるために、在庫が少ない商品のランクを下げることもできます。</p></li><li><p><strong>バグ トラッカーで重大度の高い問題を優先する</strong>- ソフトウェア開発チームにとって、問題を検索する際には、重大度が高く、優先度が高く、最近更新された問題を最初に表示することが重要です。「重要度」や「最も議論されている」などの非シグナルを使用して、さまざまな要素を個別に評価し、最も重要で活発に議論されている問題が最上位に表示されるようにすることができます。</p></li></ul><p>これらのサンプルクエリおよびその他の詳細は、付属の Elasticsearch Labs<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/you-know-for-context/">コンテンツ ページ</a>にあります。</p><h3>セキュリティ強化</h3><p>コンテキストエンジニアリングに Elastic のような検索を活用したスピードレイヤーを活用する重要な利点は、セキュリティ フレームワークが組み込まれていることです。Elastic のプラットフォームは、きめ細かなロールベースのアクセス制御 (RBAC) と属性ベースのアクセス制御 (ABAC) を通じて、エージェントおよび生成 AI オペレーションに提供されるコンテキストが機密性の高い非公開情報を尊重して保護することを保証します。これは、クエリが効率的に処理されるだけでなく、エージェントまたはリクエストを開始したユーザーの特定の権限に応じて結果がフィルタリングされることを意味します。</p><p>エージェントは認証されたユーザーとして実行されるため、プラットフォームに組み込まれたセキュリティ機能を通じてセキュリティが暗黙的に適用されます。</p><ul><li><p><strong>きめ細かな権限:</strong>ドキュメント、フィールド、さらには用語レベルでアクセスを定義し、AI エージェントが表示を許可されているデータのみを受信するようにします。</p></li><li><p><strong>ロールベースのアクセス制御 (RBAC):</strong>エージェントまたはユーザーにロールを割り当て、定義された責任に基づいて特定のデータセットまたは機能へのアクセスを許可します。</p></li><li><p><strong>属性ベースのアクセス制御 (ABAC):</strong>データ、ユーザー、または環境の属性に基づいて動的なアクセス ポリシーを実装し、適応性の高いコンテキスト認識型のセキュリティを実現します。</p></li><li><p><strong>ドキュメント レベルのセキュリティ (DLS) とフィールド レベルのセキュリティ (FLS):</strong>これらの機能により、取得したドキュメント内でも許可された部分のみが表示されるようになり、機密情報の漏洩を防止できます。</p></li><li><p><strong>エンタープライズ セキュリティとの統合:</strong>既存の ID 管理システム (LDAP、SAML、OIDC など) とシームレスに統合し、組織全体で一貫したセキュリティ ポリシーを適用します。</p></li></ul><p>これらのセキュリティ対策をコンテキスト取得メカニズムに直接統合することで、Elastic は安全なゲートキーパーとして機能し、AI エージェントが定義されたデータ境界内で動作し、不正なデータ公開を防ぎ、データプライバシー規制へのコンプライアンスを維持できるようにします。これは、機密情報や独自情報を扱うエージェント AI システムへの信頼を構築する上で非常に重要です。</p><p>追加のボーナスとして、エンタープライズ データ ソース上で統合されたデータ スピード レイヤーを使用することで、エージェント ツールによって作成されるリポジトリでの予期しないアドホック クエリ負荷を軽減できます。ほぼリアルタイムであらゆるものを検索できる単一の場所と、セキュリティとガバナンスの制御を適用できる単一の場所が提供されます。</p><h2>ハイブリッド検索ベースのツール</h2><p>Elastic プラットフォームには、コンテキスト エンジニアリングの追求を加速させるコア機能がいくつかあります (<a href="https://www.elastic.co/blog/whats-new-elastic-9-2-0">今後もさらに増える予定</a>です)。ここで重要なのは、このプラットフォームが、AI エコシステムの進化に合わせて方法を適応、変更、拡張できる柔軟性を備え、さまざまな達成方法を提供していることです。</p><h3>エージェントビルダーの紹介</h3><p>Elastic <a href="https://www.elastic.co/elasticsearch/agent-builder">Agent Builder は</a>、Elastic にすでに保存されているデータと対話するために構築されたエージェント AI ツールの領域への最初の進出です。Agent Builder は、ユーザーが Kibana 内で独自のエージェントとツールを作成および管理できるようにするチャット インターフェースを提供します。組み込みの MCP および A2A サーバー、プログラム API、Elasticsearch インデックスのクエリと探索、および自然言語からの ES|QL クエリの生成用の一連の構築済みシステム ツールが付属しています。Agent Builder を使用すると、表現力豊かな<a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a>クエリ構文を通じてエージェントに返されるコンテキスト データをターゲットにして整形するカスタム ツールを作成できます。</p><p>ES|QL はハイブリッド検索をどのように実行するのでしょうか?コア機能は、 <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a>フィールド タイプと<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork">FORK</a> / <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fuse">FUSE</a>コマンドの組み合わせによって実現されます (FUSE はデフォルトで<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a>を使用して各フォークの結果をマージします)。架空の製品検索の簡単な例を次に示します。</p>FROM products
| FORK
  (MATCH description "high performance gaming laptop" | EVAL search_type = "bm25"),
  (MATCH description_semantic "high performance gaming laptop" | EVAL search_type = "semantic")
| FUSE 
| LIMIT 20
| KEEP product_name, description, _score, search_type<p>上記の例の各 FORK ブランチに含まれる<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval">EVAL</a>句は厳密には必須ではありません。これは、特定の結果がどの検索モダリティから返されたかを追跡する方法を示すためだけに含まれています。</p><h3>検索テンプレート</h3><p>独自の外部エージェントツールを Elastic デプロイメントにポイントするとします。また、ES|QL の代わりに、マルチステージ リトリーバーを使用したり、開発した既存の DSL 構文を再利用したり、クエリが受け入れる入力、検索を実行するために使用される構文、および出力で返されるフィールドを制御できるようにしたいと考えています。<a href="https://www.elastic.co/docs/solutions/search/search-templates">検索テンプレートを</a>使用すると、ユーザーは一般的な検索パターンの定義済み構造を定義できるため、データ取得の効率と一貫性が向上します。これは、定型コードの標準化と検索ロジックの高速な反復処理を可能にするため、検索 API と対話するエージェント ツールにとって特に有益です。そして、これらの要素のいずれかを調整する必要がある場合は、検索テンプレートを更新するだけで、変更が実装されます。エージェントツールで実際に実行される検索テンプレートの例を探している場合は、Elasticsearch Labs のブログ「 <a href="https://www.elastic.co/search-labs/blog/mcp-intelligent-search">MCP for intelligent search</a> 」をご覧ください。このブログでは、外部 MCP サーバーからのツール呼び出しの背後で検索テンプレートが使用されています。</p><h3>統合ワークフロー (最高!)</h3><p>新しいエージェント AI の世界で最も扱いにくいことの 1 つは、半自律型で自己指向的な「推論」エージェントの非決定論的な性質です。コンテキスト エンジニアリングは、エージェント AI にとって非常に重要な分野です。これは、エージェントが生成できる可能性のある結論を、私たちが知っている事実に絞り込むのに役立つ手法です。非常に正確で関連性の高いコンテキスト ウィンドウがあっても、(数値的事実の領域から外れると) エージェントの応答が完全に再現可能で信頼できるという安心感がまだ少し欠けています。</p><p>エージェントに対して同じリクエストを複数回実行すると、応答に わずかな違いがあるだけ <em>で、回答は 基本的に</em><em> 同じになる可能性があります。</em>これは通常、単純なクエリでは問題なく、ほとんど気づかれない程度で、コンテキスト エンジニアリング手法を使用して出力を調整することができます。しかし、エージェントに要求するタスクが複雑になるにつれて、1 つ以上のサブタスクによって差異が生じ、最終結果がわずかに変わる可能性が高くなります。エージェント間のコミュニケーションにさらに依存するようになると、状況はさらに悪化し、差異が累積していくでしょう。これは、エージェントが対話するツールは、コンテキスト データを正確にターゲットにするために非常に柔軟かつ調整可能である必要があり、予期される出力形式で応答する必要があるという考えを再び示しています。また、多くのユースケースでは、エージェントとツールのやり取りを誘導する必要があることも示しています。ここでワークフローが登場します。</p><p>Elastic ではまもなく、プラットフォームの中核に完全にカスタマイズ可能なワークフローが組み込まれる予定です。これらのワークフローはエージェントやツールと双方向に操作できるため、ワークフローはエージェントやツールを呼び出すことができ、エージェントやツールはワークフローを呼び出すことができます。これらの機能が、すべてのデータが存在する同じ検索 AI プラットフォームに完全に統合されることで、ワークフローの可能性は大きく変化します。もうすぐ、もうすぐ登場です！</p><h3>統合メモリバンクとしてのElastic</h3><p>Elastic は、ほぼリアルタイムの検索向けに作られた分散データ プラットフォームであるため、エージェント AI システムの長期メモリ機能を自然に実行します。組み込みの Agent Builder チャット エクスペリエンスにより、短期記憶とチャット履歴の追跡と管理も行えます。また、プラットフォーム全体が API ファーストであるため、エージェントのコンテキスト ウィンドウを圧倒する可能性のあるツールのコンテキスト出力を永続化するためのプラットフォームとして Elastic を利用する（そして後で参照できるようにする）ことは非常に簡単です。この手法は、コンテキスト エンジニアリングの分野では「<a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=Agents%20can%20assemble%20understanding%20layer%20by%20layer%2C%20maintaining%20only%20what%27s%20necessary%20in%20working%20memory%20and%20leveraging%20note%2Dtaking%20strategies%20for%20additional%20persistence">メモを取る</a>」と呼ばれることもあります。</p><p>同じ検索プラットフォームに短期記憶と長期記憶の両方を持つことで、多くの本質的なメリットが生まれます。チャット履歴と永続的なコンテキスト応答を、将来のチャットのやり取りに対する意味的影響要因の一部として使用したり、脅威分析を実行したり、頻繁に繰り返されるツール呼び出しから自動的に生成される永続的なデータ製品を作成したりできるようになることを想像してみてください。可能性は無限です。</p><h2>まとめ</h2><p>大規模言語モデルの出現により、コンテンツを一致させる方法や、データを調査するために使用する手法が変化しました。私たちは、人間が自らの疑問に答えるために調査、状況の考慮、論理的推論を行う現在の世界から、それらのステップがエージェント AI によって大部分が自動化される世界へと急速に移行しつつあります。生成された回答を信頼するには、エージェントが応答を生成する際に<em>最も関連性の高い</em> 情報（主観的関連性の要素を含む） をすべて 考慮したという保証が必要です。エージェント AI を信頼できるものにするための主な方法は、RAG とコンテキスト エンジニアリング技術を通じて追加のコンテキストを取得するツールを基盤化することですが、それらのツールが<em>最初の取得を</em>どのように実行するかが応答の精度に非常に重要になる場合があります。</p><p>Elastic Search AI プラットフォームは、ハイブリッド検索の柔軟性と利点に加えて、エージェント AI の精度、パフォーマンス、スケーラビリティを向上させるいくつかの組み込み機能を提供します。つまり、Elastic はコンテキスト エンジニアリングのさまざまな側面に対応する素晴らしいプラットフォームを実現します。検索プラットフォームを介したコンテキスト検索の標準化により、エージェントツールの操作がいくつかの面で簡素化されます。「速度を落としてスピードを上げる」という矛盾した表現と同様に、コンテキスト生成レイヤーの簡素化は、より高速で信頼性の高いエージェントAIを意味します。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy</guid>
    <category><![CDATA[ハイブリッド検索]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt42a203a316f0e22e/6a170932b339d58ebc769f5f/b82ff25242e4229cc20b218d9cc91c60cfd680bc-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[コンテキストのためのYou Know - パートII：エージェントAIとコンテキストエンジニアリングの必要性]]></title>
    <description><![CDATA[LLM がエージェント AI へと進化するにつれ、RAG コンテキストの制限とメモリ管理を解決するためのコンテキスト エンジニアリングの必要性がどのように高まるのかを学びます。]]></description>
    <content:encoded><![CDATA[<p>LLM が情報検索の基本的なプロセスをどのように変えてきたかについての (かなり広範囲にわたる)<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">背景</a>を踏まえて、LLM がデータのクエリ方法をどのように変えてきたかを見てみましょう。</p><h2>データと対話する新しい方法</h2><p>ジェネレーティブ (genAI) AI とエージェント AI は、従来の検索とは異なる処理を行います。かつて私たちが情報を調べ始める方法は検索（「グーグルで検索してみます…」）でしたが、gen AI とエージェントの両方にとって、開始アクションは通常、チャット インターフェースに入力された自然言語を通じて行われます。チャット インターフェースは、意味理解を使用して質問を簡潔な回答、つまりあらゆる種類の情報に関する幅広い知識を持つ予言者から出されたような要約された応答に変換する LLM とのディスカッションです。本当に売れているのは、LLM が表面化した知識の断片をつなぎ合わせて首尾一貫した思慮深い文章を生成する能力です。たとえそれが不正確であったり完全に幻覚的であったりしても、そこには<a href="https://en.wikipedia.org/wiki/Truthiness">真実味</a>があります。</p><p>私たちが使い慣れている古い検索バーは<em><strong>、私たち自身が</strong></em>推論エージェントであったときに使用した RAG エンジンと考えることができます。現在では、インターネット検索エンジンでさえ、使い古された「ハント・アンド・ペック」という語彙検索エクスペリエンスを、クエリに対する結果の要約で答える AI 主導の概要へと変えつつあり、ユーザーがクリックして個々の結果を自分で評価する必要がないようにしています。</p><h2>生成AIとRAG</h2><p>生成 AI は、世界の意味理解を活用してチャット リクエストを通じて表明された主観的な意図を解析し、推論能力を使用して専門的な回答を即座に作成します。生成 AI インタラクションにはいくつかの部分があります。ユーザーの入力/クエリから始まり、チャット セッションでの以前の会話が追加のコンテキストとして使用でき、LLM に推論方法と応答の構築手順を指示する指示プロンプトがあります。プロンプトは、「5 歳児に説明するように説明してください」という単純なタイプのガイダンスから、リクエストを処理する方法の完全な詳細へと進化しました。これらの内訳には、AI のペルソナ/役割、生成前の推論/内部思考プロセス、客観的な基準、制約、出力形式、対象者、および期待される結果を示すのに役立つ例の詳細を説明する個別のセクションが含まれることがよくあります。</p><p>ユーザーのクエリとシステム プロンプトに加えて、検索拡張生成 (RAG) は、「コンテキスト ウィンドウ」と呼ばれる追加のコンテキスト情報を提供します。RAG はアーキテクチャへの重要な追加機能であり、世界の意味理解において欠落している部分を LLM に通知するために使用します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbfa000ccfdd9d184/6a17ddb57b54f955f38b37da/5b9671d5d07d4caefde372bb3188000754a91eed-1470x746.png" alt="LLMがユーザークエリを処理してコンテキストを作成する方法" /><p>コンテキスト ウィンドウは、何を、どこに、どれだけ与えるかという点では、かなり<a href="https://www.dbreunig.com/2025/06/22/how-contexts-fail-and-how-to-fix-them.html">細かい指定が</a>必要になる場合があります。もちろん、どのコンテキストが選択されるかは非常に重要ですが、提供されたコンテキストの信号対雑音比やウィンドウの長さも重要です。</p><h3>情報が少なすぎる</h3><p>クエリ、プロンプト、またはコンテキスト ウィンドウに提供される情報が少なすぎると、LLM が応答を生成するための正しいセマンティック コンテキストを正確に判断できないため、幻覚が発生する可能性があります。また、ドキュメント チャンク サイズのベクトル類似性にも問題があります。つまり、短くて単純な質問は、ベクトル化された知識ベースにある豊富で詳細なドキュメントと意味的に一致しない可能性があります。<a href="https://medium.com/data-science/how-to-use-hyde-for-better-llm-rag-retrieval-a0aa5d0e23e8">Hypothetical Document Embeddings (HyDE)</a>などのクエリ拡張手法が開発され、LLM を使用して、短いクエリよりも豊富で表現力豊かな仮説的な回答を生成します。もちろん、ここでの危険は、仮説文書自体が LLM を正しい文脈からさらに逸脱させる幻覚であるということです。</p><h3>情報が多すぎる</h3><p>私たち人間と同じように、コンテキスト ウィンドウに情報が多すぎると、LLM は重要な部分が何であるのかについて混乱し、圧倒されてしまう可能性があります。コンテキスト オーバーフロー (または「<a href="https://research.trychroma.com/context-rot">コンテキスト ロット</a>」) は、生成 AI 操作の品質とパフォーマンスに影響します。LLM の「注意予算」(作業メモリ) に大きな影響を与え、競合する多くのトークン間の関連性を薄めます。「コンテキスト腐敗」の概念には、LLM が<a href="https://alexandrabarr.beehiiv.com/p/context-windows">位置の偏り</a>を持つ傾向があるという観察も含まれます。つまり、LLM はコンテキスト ウィンドウの中央セクションのコンテンツよりも、コンテキスト ウィンドウの先頭または末尾のコンテンツを優先します。</p><h3>気が散ったり矛盾したりする情報</h3><p>コンテキスト ウィンドウが大きくなるほど、LLM が正しいコンテキストを選択して処理する妨げとなる余分な情報や矛盾した情報が含まれる可能性が高くなります。ある意味、これは「ガベージ イン/ガベージ アウト」の問題になります。つまり、ドキュメント結果セットをコンテキスト ウィンドウにダンプするだけで、LLM に処理すべき大量の情報が提供されます (多すぎる可能性があります)。ただし、コンテキストの選択方法によっては、矛盾した情報や無関係な情報が入り込む可能性が高くなります。</p><h2>エージェント型AI</h2><p>カバーすべき内容がたくさんあると言いましたが、ついにエージェント AI のトピックについて話すことができました。エージェント AI は、LLM チャット インターフェイスの非常にエキサイティングな新しい使用法であり、独自の知識とユーザーが提供するコンテキスト情報に基づいて応答を合成する生成 AI (すでに「レガシー」と呼んでもいいでしょうか?) の機能を拡張します。生成 AI が成熟するにつれて、当初は人間が簡単に確認/検証できる、面倒でリスクの低いアクティビティに限定されていた、一定レベルのタスク処理と自動化を LLM に実行させることができることに気付きました。短期間で、当初のスコープは拡大しました。LLM チャット ウィンドウは、AI エージェントが自律的に計画、実行し、指定された目標を達成するためにその計画を反復的に評価および適応させるきっかけとなることができるようになりました。エージェントは、LLM 自身の推論、チャット履歴、思考メモリ (現状のまま) にアクセスでき、その目標達成に向けて活用できる特定のツールも利用できます。また、トップレベルのエージェントが、それぞれ独自のロジック チェーン、命令セット、コンテキスト、ツールを持つ複数の<a href="https://www.philschmid.de/the-rise-of-subagents">サブエージェント</a>のオーケストレーターとして機能することを可能にするアーキテクチャも登場しています。</p><p>エージェントは、ほぼ自動化されたワークフローへのエントリ ポイントです。エージェントは自己主導型であり、ユーザーとチャットしてから「ロジック」を使用して、ユーザーの質問に答えるために使用できるツールを決定します。ツールは通常、エージェントに比べて受動的であると考えられており、1 種類のタスクを実行するために構築されています。ツールが実行できるタスクの<em>種類</em>はほぼ無限です (これは本当に素晴らしいことです!) が、ツールが実行する主なタスクは、エージェントがワークフローを実行する際に考慮するコンテキスト情報を収集することです。</p><p>技術としては、エージェント AI はまだ初期段階にあり、注意欠陥障害に相当する LLM になりがちです。つまり、指示されたことをすぐに忘れてしまい、指示にまったく含まれていない他の作業に走り出してしまうことがよくあります。一見魔法のように見えますが、LLM の「推論」機能は、シーケンス内で次に最も可能性の高いトークンを予測することに基づいています。推論（あるいは将来的には、汎用人工知能（AGI））が信頼できるものになるためには、正確で最新の情報が与えられたときに、私たちが期待する通りに推論してくれるか（そしておそらく、私たち自身では考えつかなかったようなちょっとした追加情報を提供してくれるか）を検証できなければなりません。これを実現するには、エージェント アーキテクチャに、明確に通信する機能 (プロトコル)、指定されたワークフローと制約を順守する機能 (ガードレール)、タスク内の位置を記憶する機能 (状態)、使用可能なメモリ領域を管理する機能、応答が正確でありタスクの基準を満たしていることを検証する機能が必要になります。</p><h2>私に理解できる言語で話してください</h2><p>新しい開発分野ではよくあることですが (特に LLM の世界ではそうです)、当初はエージェントとツール間の通信にはかなり多くのアプローチがありましたが、すぐに<a href="https://modelcontextprotocol.io/docs/getting-started/intro">モデル コンテキスト プロトコル (MCP) が</a>事実上の標準として採用されました。モデル コンテキスト プロトコルの定義はまさにその名前の通りで、<strong> モデルが</strong><strong> コンテキスト</strong> 情報を要求および受信するために使用する<strong> プロトコル</strong> です。MCP は、LLM エージェントが外部ツールやデータ ソースに接続するためのユニバーサル アダプタとして機能し、さまざまな LLM フレームワークやツールが簡単に相互運用できるように API を簡素化および標準化します。そのため、MCP は、エージェントが目的を達成するために自律的に実行するために与えられるオーケストレーション ロジックとシステム プロンプトと、より分離された形式 (少なくとも開始エージェントに関しては分離された形式) で実行するためにツールに送信される操作との間の、一種のピボット ポイントになります。</p><p>このエコシステムは非常に新しいため、あらゆる方向への拡大が新たなフロンティアのように感じられます。エージェント間のインタラクション（もちろん<a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/">Agent2Agent (A2A)</a> ）用の類似プロトコルのほか、エージェントの推論メモリを改善するプロジェクト（ <a href="https://venturebeat.com/ai/new-memory-framework-builds-ai-agents-that-can-handle-the-real-worlds">ReasoningBank</a> ）、手元のジョブに最適な MCP サーバーを選択するプロジェクト（ <a href="https://arxiv.org/abs/2505.03275">RAG-MCP</a> ）、ゼロショット分類や入力と出力のパターン検出などのセマンティック分析を<a href="https://openai.github.io/openai-guardrails-python/">ガードレール</a>として使用してエージェントが操作できる内容を制御するプロジェクトもあります。</p><p>これらの各プロジェクトの根本的な目的は、エージェント/genAI コンテキスト ウィンドウに返される情報の品質と制御を向上させることであることにお気づきでしょうか。エージェント AI エコシステムは、コンテキスト情報をより適切に処理する (制御、管理、操作する) 能力の開発を継続していますが、エージェントが処理するための<em>最も関連性の高い</em>コンテキスト情報を取得する必要性は常に存在します。</p><h2>コンテキストエンジニアリングへようこそ!</h2><p>生成 AI の用語に詳しい方なら、おそらく「プロンプト エンジニアリング」という言葉を聞いたことがあるでしょう。現時点では、プロンプト エンジニアリングはそれ自体がほぼ疑似科学となっています。プロンプト エンジニアリングは、LLM が応答を生成する際に使用する動作を積極的に記述するための最良かつ最も効率的な方法を見つけるために使用されます。「<a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">コンテキスト エンジニアリング</a>」は、「プロンプト エンジニアリング」の手法をエージェント側を超えて拡張し、MCP プロトコルのツール側で利用可能なコンテキスト ソースとシステムもカバーし、コンテキストの管理、処理、生成という幅広いトピックを扱います。</p><ul><li><p><strong>コンテキスト管理</strong>- 長時間実行される、またはより複雑なエージェント ワークフロー全体で状態とコンテキストの効率を維持することに関連します。エージェントの目標を達成するために、タスクとツールの呼び出しを繰り返し計画、追跡、オーケストレーションします。エージェントが動作しなければならない「注意予算」は限られているため、コンテキスト管理は主に、コンテキスト ウィンドウを絞り込んでコンテキストの最大限の範囲と最も重要な部分 (精度と再現率) の両方をキャプチャするのに役立つ手法に関係しています。技術には、圧縮、要約、前のステップまたはツール呼び出しからのコンテキストを永続化して、後続のステップで追加のコンテキストのために作業メモリ内にスペースを確保することが含まれます。</p></li><li><p><strong>コンテキスト処理</strong>- エージェントがすべてのコンテキストをある程度統一された方法で推論できるように、異なるソースから取得したコンテキストを統合、正規化、または調整するための論理的かつできればほとんどプログラム的な手順。基本的な作業は、すべてのソース (プロンプト、RAG、メモリなど) からのコンテキストを、エージェントが可能な限り効率的に使用できるようにすることです。 </p></li><li><p><strong>コンテキスト生成</strong>- コンテキスト処理が、取得したコンテキストをエージェントが使用できるようにすることであるならば、コンテキスト生成は、追加のコンテキスト情報を自由に、しかし制約付きで要求して受け取るための範囲をエージェントに提供します。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e1e68c08fe050bc/6a17ddb7414c645035945073/4a8240e1eb078b2294b8d981b9caa8593589cac4-1600x900.png" alt="LLMにおけるコンテキストエンジニアリング" /><p>LLM チャット アプリケーションのさまざまな一時的な機能は、コンテキスト エンジニアリングの高レベル機能に直接 (場合によっては重複して) マッピングされます。</p><ul><li><p><strong>指示 / システム プロンプト</strong>- プロンプトは、生成的 (またはエージェント的) AI アクティビティがユーザーの目標を達成するためにどのように思考を導くかを示す足場です。プロンプトはそれ自体がコンテキストです。単なる音声による指示ではなく、回答がユーザーの要求に完全に応えているかどうかを確認するために、応答する前に「段階的に考える」や「深呼吸する」などのタスク実行ロジックやルールも含まれることがよくあります。最近のテストでは、マークアップ言語はプロンプトのさまざまな部分を組み立てるのに非常に効果的であることが示されていますが、指示を曖昧になりすぎず、具体的になりすぎないように注意して調整する必要があります。LLM が適切なコンテキストを見つけるのに十分な指示を与える必要がありますが、予期しない洞察を見逃すほど規範的であってはなりません。</p></li><li><p><strong>短期記憶</strong>(状態/履歴) - 短期記憶は、基本的にユーザーと LLM 間のチャット セッションのやり取りです。これらはライブ セッションのコンテキストを絞り込むのに役立ち、将来の取得や続行のために保存できます。 </p></li><li><p><strong>長期記憶</strong>- 長期記憶は、複数のセッションにわたって役立つ情報で構成されている必要があります。また、RAG を通じてアクセスされるのはドメイン固有の知識ベースだけではありません。最近の研究では、以前のエージェント/生成 AI 要求の結果を使用して、現在のエージェントのやり取り内で学習および参照を行っています。長期記憶領域における最も興味深い革新のいくつかは、エージェントが中断したところから再開できるように、状態がどのように<a href="https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a">保存され、リンクされるかを</a>調整することに関係しています。 </p></li><li><p><strong>構造化された出力</strong>- 認知には努力が必要なので、推論能力があっても、LLM が (人間と同じように) 考えるときにあまり努力を費やしたくないのは当然です。また、定義された API やプロトコルがない場合、ツール呼び出しから返されたデータを読み取る方法のマップ (スキーマ) を持つことは非常に役立ちます。<a href="https://platform.openai.com/docs/guides/structured-outputs?lang=javascript">構造化出力</a>をエージェント フレームワークの一部として組み込むと、思考主導の解析の必要性が減り、マシン間のやり取りがより高速かつ信頼性が高くなるようになります。</p></li><li><p><strong>利用可能なツール</strong>- ツールは、追加情報の収集 (エンタープライズ データ リポジトリへの RAG クエリの発行、またはオンライン API 経由の RAG クエリの発行など) から、エージェントに代わって自動アクションを実行すること (エージェントからのリクエストの基準に基づいてホテルの部屋を予約するなど) まで、さまざまな処理を実行できます。ツールは、独自のエージェント処理チェーンを持つサブエージェントになることもできます。 </p></li><li><p><strong>検索拡張生成 (RAG)</strong> - RAG の「動的な知識統合」という説明がとても気に入っています。前述のように、RAG は LLM がトレーニング時にアクセスできなかった追加情報を提供するための手法であり、主観的なクエリに最も関連性の高い正しい答えを得るために最も重要だと考えられるアイデアを繰り返し述べたものです。</p></li></ul><h2>驚異的な宇宙のパワー、小さな居住空間！</h2><p>エージェント AI には、探索すべき魅力的でエキサイティングな新しい領域が数多くあります。解決すべき従来のデータ検索および処理の問題はまだたくさんありますが、LLM の新時代に初めて日の目を見るようになったまったく新しい種類の課題もあります。私たちが現在取り組んでいる差し迫った問題の多くは、コンテキスト エンジニアリング、つまり、LLM の限られた作業メモリ空間を圧迫することなく、必要な追加のコンテキスト情報を取得することに関係しています。</p><p>さまざまなツール (および他のエージェント) にアクセスできる半自律エージェントの柔軟性により、AI を実装するための非常に多くの新しいアイデアが生まれ、さまざまな方法でそれらを組み合わせることができるのかを推測するのは困難です。現在の研究のほとんどはコンテキスト エンジニアリングの分野に属し、大量のコンテキストを処理および追跡できるメモリ管理構造の構築に重点を置いています。これは、LLM に解決してほしい深い思考の問題には、記憶することが極めて重要となる、複雑さが増し、実行時間が長く、多段階の思考ステップが含まれるためです。</p><p>この分野で現在行われている多くの実験では、エージェントの口を満たすための最適なタスク管理とツール構成を見つけようとしています。エージェントの推論チェーンにおける各ツール呼び出しは、そのツールの機能を実行するための計算と、制限されたコンテキスト ウィンドウへの影響の両方の点で累積的なコストを発生させます。LLM<a href="https://venturebeat.com/ai/ace-prevents-context-collapse-with-evolving-playbooks-for-self-improving-ai"> </a>エージェントのコンテキストを管理する最新の技術の一部は、長時間実行されるタスクの蓄積されたコンテキストを圧縮/要約すると損失が<em> 大きくなりすぎる 「</em> コンテキストの崩壊 」などの意図しない連鎖効果を引き起こしています。望ましい結果は、貴重なコンテキスト ウィンドウのメモリ領域に余分な情報が漏れることなく、簡潔で正確なコンテキストを返すツールです。</p><h3>可能性が多すぎる</h3><p>私たちはツール/コンポーネントを再利用するための柔軟性を備えた職務の分離を望んでいるため、特定のデータ ソースに接続するための専用のエージェント ツールを作成することは完全に理にかなっています。各ツールは、1 つのタイプのリポジトリ、1 つのタイプのデータ ストリーム、または 1 つのユース ケースのクエリに特化できます。しかし、注意してください。時間や費用を節約し、何かが可能であると証明しようとすると、LLM をフェデレーション ツールとして使用する強い誘惑に駆られるでしょう... やめてください。私たちは以前にも<a href="https://www.elastic.co/pdf/elastic-distributed-not-federated-search.pdf">その道を歩ん</a>だことがあります。フェデレーション クエリは、受信したクエリをリモート リポジトリが理解できる構文に変換する「ユニバーサル トランスレータ」のように機能し、その後、複数のソースからの結果を何らかの方法で合理化して一貫した応答を生成する必要があります。技術としてのフェデレーションは小規模では <em>問題なく</em><em> 機能します が、大規模で、特にデータがマルチモーダルである場合、フェデレーションは大きすぎるギャップを埋めようとします。</em></p><p>エージェントの世界では、エージェントがフェデレーターとなり、ツール (MCP 経由) がさまざまなリソースへの手動で定義された接続となります。専用のツールを使用して接続されていないデータ ソースにアクセスすることは、クエリごとにさまざまなデータ ストリームを動的に統合する強力な新しい方法のように思えるかもしれませんが、ツールを使用して複数のソースに同じ質問をすると、解決するよりも多くの問題が発生する可能性があります。これらのデータ ソースはそれぞれ、その下にある異なるタイプのリポジトリである可能性があり、それぞれが内部のデータを取得、ランク付け、保護するための独自の機能を備えています。もちろん、リポジトリ間のこうした差異、つまり「インピーダンスの不一致」により、処理負荷が増加します。また、矛盾する情報やシグナルが生じる可能性があり、スコアの不一致のように一見無害に見えるものでも、返されたコンテキストの重要性が大きく損なわれ、最終的に生成された応答の関連性に影響する可能性があります。</p><h3>コンテキストスイッチはコンピュータにとっても難しい</h3><p>エージェントを任務に送り出す場合、多くの場合、最初の任務はエージェントがアクセスできるすべての関連データを見つけることです。人間の場合と同様に、エージェントが接続する各データ ソースが類似していない分散した応答を返すと、取得したコンテンツから重要なコンテキスト ビットを抽出することに関連する認知負荷 (まったく同じ種類ではありませんが) が発生します。これには時間と計算がかかり、エージェントのロジック チェーンでは少しずつ蓄積されていきます。このことから、 <a href="https://blog.cloudflare.com/code-mode/">MCP</a>について議論されているように、ほとんどのエージェント ツールは、API (既知の入力と出力を持つ分離された関数で、さまざまな種類のエージェントのニーズをサポートするように調整された) のように動作する必要があるという結論に至ります。実際、 <a href="https://arxiv.org/html/2501.12372v5">LLM にはコンテキストのためのコンテキストが必要である</a>ことにも気づき始めています。特に、自然言語を構造化構文に翻訳するようなタスクでは、参照できるスキーマがあれば、LLM は意味の点と点を結びつけるのがはるかに上手です (まさに RTFM!)。</p><h2>7回裏ストレッチ！</h2><p>ここでは、 <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">LLM がデータの取得とクエリに与えた影響</a>と、チャット ウィンドウがエージェント AI エクスペリエンスへとどのように成熟しているかについて説明しました。これら 2 つのトピックを組み合わせて、最新の検索機能と取得機能を使用してコンテキスト エンジニアリングの結果を改善する方法を見てみましょう。<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy">パート III へ進みます: コンテキスト エンジニアリングにおけるハイブリッド検索の威力</a>!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f98889141fba45b/6a17ddb80b0bed0822dd34a2/79c0378b68d74d9e018c35ee2c1fd17daeee9f2c-1080x608.webp" length="0" type="image/webp"/>
    <pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch で A2A プロトコルと MCP を使用して LLM エージェント ニュースルームを作成する: パート I]]></title>
    <description><![CDATA[専門の LLM エージェントが協力してニュース記事の調査、執筆、編集、公開を行う実践的なニュースルームの例で、A2A プロトコルと MCP の概念を探ります。]]></description>
    <content:encoded><![CDATA[<h2>はじめに</h2><p>現在の LLM 対応システムは、単一モデルのアプリケーションから、専門のエージェントが連携して、現代のコンピューティングではこれまで不可能と思われていたタスクを達成する複雑なネットワークへと急速に進化しています。これらのシステムの複雑さが増すにつれて、エージェントの通信とツールへのアクセスを可能にするインフラストラクチャが開発の主な焦点になります。これらのニーズに対応するために、マルチエージェント調整用の<strong>Agent2Agent (A2A)</strong>プロトコルと、標準化されたツールおよびリソース アクセス用の<strong>Model Context Protocol (MCP) という</strong>2 つの補完的なアプローチが登場しました。</p><p>それぞれの機能をいつ、またいつ単独で、調和して使用するかを理解することは、アプリケーションのスケーラビリティ、保守性、および有効性に大きな影響を与える可能性があります。この記事では、専門の LLM エージェントが協力してニュース記事の調査、執筆、編集、公開を行うデジタル ニュースルームの実際の例を通して、 <strong>A2A</strong>の概念と実装について説明します。</p><p>付属のリポジトリは<a href="https://github.com/justincastilla/elastic-newsroom/tree/main">ここに</a>あります。セクション 5 の最後の方で、A2A の実際の動作の具体的な例を検討します。</p><h3>要件</h3><p><a href="https://github.com/justincastilla/elastic-newsroom/tree/main">リポジトリは</a>、A2A エージェントの Python ベースの実装で構成されています。Flask には API サーバーが用意されているほか、ログ記録や UI 更新のメッセージをルーティングする Event Hub というカスタム Python メッセージング サービスも用意されています。最後に、ニュースルームの機能をスタンドアロンで使用するための React UI が提供されます。実装を容易にするために、すべてが Docker イメージ内に含まれています。マシンで直接サービスを実行する場合は、次のテクノロジがインストールされていることを確認してください。</p><p>言語とランタイム</p><ul><li><p>Python 13.12 - コアバックエンド言語</p></li><li><p>Node.js 18+ - オプションのReact UI</p></li></ul><p>コアフレームワークと SDK:</p><ul><li><p>A2A SDK 0.3.8 - エージェントの調整と通信</p></li><li><p>Anthropic SDK - AI生成のためのClaude統合</p></li><li><p>Uvicorn - エージェントを実行するためのASGIサーバー</p></li><li><p>FastMCP 2.12.5+ - MCP サーバーの実装</p></li><li><p>React 18.2 - フロントエンドUIフレームワーク</p></li></ul><p>データと検索</p><ul><li><p>Elasticsearch 9.1.1 以上- 記事のインデックス作成と検索</p></li></ul><p>Docker のデプロイメント (オプションですが推奨)</p><ul><li><p>Docker 28.5.1 以上</p></li></ul><h2>セクション 1: Agent2Agent (A2A) とは何ですか?</h2><h3>定義とコアコンセプト</h3><p>Agent2Agent (A2A) は、独立した LLM エージェント間の相互作用のための標準化されたプロトコルです。A2A は、すべてのタスクを処理する単一のモノリシック システムではなく、複数の専門エージェントが通信、調整、および連携して、単一のエージェントでは効率的に処理するのが困難、遅い、またはまったく不可能な複雑なワークフローを実現できるようにします。</p><p><strong>公式仕様</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></p><h3>起源と進化</h3><p>エージェント間通信、つまりマルチエージェント システムの概念は、<a href="https://en.wikipedia.org/wiki/Multi-agent_system">数十年</a>前に遡る分散システム、マイクロサービス、およびマルチエージェントの研究に根ざしています。分散型人工知能の初期の研究は、交渉、調整、共同作業ができるエージェントの基盤を築きました。これらの初期のシステムは、大規模な<a href="https://www.jasss.org/5/1/7.html">社会シミュレーション</a>、<a href="https://arxiv.org/html/2410.09403v1">学術研究</a>、<a href="https://www.researchgate.net/publication/334765661_Generation_Expansion_Planning_Considering_Investment_Dynamic_of_Market_Participants_Using_Multi-agent_System">電力網管理</a>に特化していました。</p><p>LLM が利用可能になり、運用コストが削減されたことで、Google や AI 研究コミュニティ全体の支援を受けて、マルチエージェント システムが「プロシューマー」市場で利用可能になりました。現在 Agent2Agent システムとして知られている A2A プロトコルの追加により、複数の大規模言語モデルが取り組みとタスクを調整する時代に合わせて特別に設計された最新の標準へと進化しました。</p><p>A2A プロトコルは、LLM が接続して通信するインタラクション ポイントに一貫した標準と原則を適用することで、エージェント間のシームレスな通信と調整を保証します。この標準化により、異なる開発者のエージェントが、異なる基盤モデルを使用して、効果的に連携できるようになります。</p><p>通信プロトコルは新しいものではなく、インターネット上で行われるほぼすべてのデジタル取引に広く定着しています。<a href="https://www.elastic.co/search-labs">https://www.elastic.co/search-labs</a>と入力した場合この記事にアクセスするためにブラウザにログインすると、TCP/IP、HTTP トランスポート、DNS ルックアップ プロトコルがすべて実行され、一貫したブラウジング エクスペリエンスが保証される可能性が高くなります。</p><h3>主な特徴</h3><p>A2A システムは、スムーズな通信を確保するためにいくつかの基本原則に基づいて構築されています。これらの原則に基づいて構築することで、異なる LLM、フレームワーク、プログラミング言語に基づくさまざまなエージェントがすべてシームレスに対話できるようになります。</p><p>主な原則は次の 4 つです。</p><ul><li><p><strong>メッセージパッシング</strong>: エージェントは、明確に定義されたプロパティとフォーマットを持つ構造化されたメッセージを通じて通信します。</p></li><li><p><strong>調整</strong>: エージェントは、他のエージェントをブロックすることなく、タスクを互いに委任し、依存関係を管理することで、複雑なワークフローを調整します。</p></li><li><p><strong>専門分野</strong>: 各エージェントは特定のドメインまたは機能に焦点を合わせ、その分野の専門家となり、そのスキルセットに基づいてタスクの完了を提供します。</p></li><li><p><strong>分散状態</strong>: 状態と知識は集中化されるのではなくエージェント間に分散され、エージェントはタスクの状態と部分的な戻り値(成果物)の進捗状況を相互に更新する機能を持ちます。</p></li></ul><h3>ニュースルーム：実例</h3><p>ジャーナリズムのさまざまな側面に特化した AI エージェントによって駆動されるデジタル ニュースルームを想像してみてください。</p><ul><li><p><strong>ニュースチーフ</strong>（コーディネーター/クライアント）：ストーリーを割り当て、ワークフローを監督する</p></li><li><p><strong>記者エージェント</strong>：調査やインタビューに基づいて記事を書く</p></li><li><p><strong>研究エージェント</strong>: 事実、統計、背景情報を収集します</p></li><li><p><strong>アーカイブエージェント</strong>: Elasticsearchを使用して過去の記事を検索し、傾向を特定します</p></li><li><p><strong>エディターエージェント</strong>: 記事の品質、スタイル、SEO最適化をレビューします</p></li><li><p><strong>パブリッシャーエージェント</strong>: 承認された記事をCI/CD経由でブログプラットフォームに公開します。</p></li></ul><p>これらのエージェントは単独では機能しません。ニュースチーフが<em>再生可能エネルギーの導入</em>についての記事を割り当てる場合、記者は統計を収集する研究者、草稿を確認する編集者、そして最終記事を公開する発行者を必要とします。この調整は A2A プロトコルを通じて行われます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6c7215a96326481/6a17f2dd445de953024d0243/cc0760dbd74c49b92fa00dafbb8c2e8740eb70b6-963x693.png" alt="" /><h2>セクション2: A2Aアーキテクチャの理解</h2><h3>クライアントエージェントとリモートエージェントの役割</h3><p>A2A アーキテクチャでは、エージェントは主に 2 つの役割を担います。<strong>クライアント エージェントは</strong>、タスクを策定し、システム内の他のエージェントに伝達する役割を担います。リモート エージェントとその機能を識別し、この情報を使用してタスクの委任について十分な情報に基づいた決定を下します。クライアント エージェントはワークフロー全体を調整し、タスクが適切に分散され、システムが目標に向かって進行することを保証します。</p><p>対照的に、<strong>リモート エージェントは</strong>、クライアントによって委任されたタスクを実行します。リクエストに応じて情報を提供したり特定のアクションを実行したりしますが、独自にアクションを開始することはありません。リモート エージェントは、割り当てられた責任を果たすために必要に応じて他のリモート エージェントと通信し、特殊な機能の共同ネットワークを作成することもできます。</p><p>私たちのニュースルームでは、ニュースチーフがクライアントエージェントとして機能し、レポーター、リサーチャー、エディター、パブリッシャーはリクエストに応答し、互いに調整するリモートエージェントとして機能します。</p><h3>コアA2A機能</h3><p>A2A プロトコルは、マルチエージェントのコラボレーションを可能にするいくつかの機能を定義します。</p><h4>1. 発見</h4><p>A2A サーバーは、クライアントが特定のタスクにいつどのようにサーバーを利用できるかがわかるように、その機能をアナウンスする必要があります。これは、エージェントの能力、入力、出力を記述する JSON ドキュメントであるエージェント カードを通じて実現されます。エージェント カードは、一貫性のあるよく知られたエンドポイント (推奨される<code>/.well-known/agent-card.json</code>エンドポイントなど) で利用できるようになり、クライアントはコラボレーションを開始する前にエージェントの機能を検出して照会できるようになります。</p><p>以下は、Elastic のカスタム アーカイブ エージェント「Archie Archivist」のエージェント カードの例です。Elastic などのソフトウェア プロバイダーは A2A エージェントをホストし、アクセス用の URL を提供していることに注意してください。</p>{
  "name": "Archie Archivist",
  "description": "Helps find historical news documents in the Elasticsearch Index of archived news articles and content.",
  "url": "https://xxxxxxxxxxxxx-abc123.kb.us-central1.gcp.elastic.cloud/api/agent_builder/a2a/archive-agent",
  "provider": {
    "organization": "Elastic",
    "url": "https://elastic.co"
  },
  "version": "0.1.0",
  "protocolVersion": "0.3.0",
  "preferred_transport": "JSONRPC",
  "documentationURL": "https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server"
  "capabilities": {
    "streaming": false,
    "pushNotifications": false,
    "stateTransitionHistory": false
  },
  "skills": [
    {
      "id": "platform.core.search",
      "name": "platform.core.search",
      "description": "A powerful tool for searching and analyzing data within your Elasticsearch cluster.",
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["text/plain", "application/json"]
    },
    {
      "id": "platform.core.index_explorer",
      "name": "platform.core.index_explorer",
      "description": "List relevant indices, aliases and datastreams based on a natural language query.",
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["text/plain", "application/json"]
    }
  ],
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"]
}<p>このエージェント カードでは、Elastic のアーカイブ エージェントのいくつかの重要な側面について説明します。エージェントは自身を「Archie Archivist」と名乗り、Elasticsearch インデックス内の過去のニュース文書の検索を支援するという目的を明確に述べています。カードはプロバイダー (Elastic) とプロトコル バージョン (0.3.0) を指定し、他の A2A 準拠エージェントとの互換性を確保します。最も重要なのは、 <code>skills</code>配列が、強力な検索機能やインテリジェントなインデックス探索など、このエージェントが提供する特定の機能を列挙していることです。各スキルはサポートする入力モードと出力モードを定義し、クライアントがこのエージェントと通信する方法を正確に理解できるようにします。このエージェントは Elastic の Agent Builder サービスから派生したもので、データ ストアからデータを取得するだけでなく、データ ストアと対話するためのネイティブ LLM 対応ツールと API エンドポイントのスイートを提供します。Elasticsearch の A2A エージェントへのアクセスについては、<a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server">こちらを</a>ご覧ください。</p><h4>2. 交渉</h4><p>クライアントとエージェントは、適切なユーザー インタラクションとデータ交換を確保するために、コミュニケーション方法 (インタラクションがテキスト、フォーム、iframe、またはオーディオ/ビデオを介して行われるかどうか) について合意する必要があります。このネゴシエーションはエージェントのコラボレーションの開始時に行われ、ワークフロー全体にわたるエージェントの相互作用を管理するプロトコルを確立します。たとえば、音声ベースのカスタマー サービス エージェントはオーディオ ストリーム経由での通信をネゴシエートする可能性がありますが、データ分析エージェントは構造化された JSON を好む可能性があります。交渉プロセスにより、両当事者がそれぞれの能力と現在のタスクの要件に適した形式で情報を効果的に交換できるようになります。</p><p>上記の JSON スニペットにリストされている機能にはすべて入力スキーマと出力スキーマがあり、これらによって、他のエージェントからこのエージェントと対話する方法の期待値が設定されます。</p><h4>3. タスクと状態の管理</h4><p>クライアントとエージェントには、タスク実行全体を通じてタスクのステータス、変更、依存関係を通信するためのメカニズムが必要です。これには、タスクの作成と割り当てから進捗状況の更新とステータスの変更までのタスクのライフサイクル全体の管理が含まれます。一般的なステータスには、保留中、進行中、完了、失敗などの状態が含まれます。また、システムは、依存タスクが開始する前に前提条件となる作業が完了していることを確認するために、タスク間の依存関係を追跡する必要があります。エラー処理と再試行ロジックも重要なコンポーネントであり、システムが障害から正常に回復し、主な目標に向かって前進し続けることを可能にします。</p><p>タスクメッセージの例:</p>{
  "message_id": "msg_789xyz",
  "message_type": "task_request",
  "sender": "news_chief",
  "receiver": "researcher_agent",
  "timestamp": "2025-09-30T10:15:00Z",
  "payload": {
    "task_id": "task_456abc",
    "capability": "fact_gathering",
    "parameters": {
      "query": "renewable energy adoption rates in Europe 2024",
      "sources": ["eurostat", "iea", "ember"],
      "depth": "comprehensive"
    },
    "context": {
      "story_id": "story_123",
      "deadline": "2025-09-30T18:00:00Z",
      "priority": "high"
    }
  }
}<p>このサンプル タスク メッセージは、A2A 通信のいくつかの重要な側面を示しています。</p><ul><li><p><strong>メッセージ</strong>構造には、一意のメッセージ識別子、送信されるメッセージの種類、送信者と受信者の識別、追跡およびデバッグ用のタイムスタンプなどのメタデータが含まれます。</p></li><li><p><strong>ペイロードには</strong>実際のタスク情報が含まれており、リモート エージェントで呼び出される機能を指定し、その機能を実行するために必要なパラメータを提供します。</p></li><li><p><strong>コンテキスト</strong>セクションでは、受信側エージェントが広範なワークフローを理解するのに役立つ追加情報が提供されます。これには、エージェントがリソースを割り当てて作業をスケジュールする方法を示す期限や優先度レベルなどが含まれます。</p></li></ul><h4>4. コラボレーション</h4><p>クライアントとエージェントは、動的かつ構造化されたインタラクションをサポートし、エージェントがクライアント、他のエージェント、またはユーザーに説明、情報、またはサブアクションを要求できるようにする<strong>必要があります</strong>。これにより、エージェントが最初の指示が曖昧な場合にフォローアップの質問をしたり、より適切な決定を下すために追加のコンテキストを要求したり、より適切な専門知識を持つ他のエージェントにサブタスクを委任したり、完全なタスクに進む前にフィードバック用の中間結果を提供したりできる共同作業環境が作成されます。この多方向のコミュニケーションにより、エージェントは孤立して作業するのではなく、継続的な対話に参加してより良い結果を得ることができます。</p><h3>分散型ピアツーピア通信</h3><p>A2A は、エージェントが異なる組織によってホストされ、一部のエージェントが社内で管理され、他のエージェントがサードパーティのサービスによって提供される分散通信を可能にします。これらのエージェントは、複数のクラウド プロバイダーまたはオンプレミスのデータ センターにまたがる可能性のある、さまざまなインフラストラクチャで実行できます。エージェントによっては、GPT モデルを活用したエージェント、Claude を活用したエージェント、オープンソースの代替手段を活用したエージェントなど、基盤となる LLM が異なる場合があります。エージェントは、データ主権の要件に準拠したり、待ち時間を削減したりするために、異なる地理的領域にまたがって動作する場合もあります。この多様性にもかかわらず、すべてのエージェントは情報を交換するための共通の通信プロトコルに同意し、実装の詳細に関係なく相互運用性を保証します。この分散アーキテクチャにより、システムの構築と展開に柔軟性が提供され、組織は特定のニーズに合わせて最適なエージェントとインフラストラクチャを組み合わせることができます。</p><p>これはニュースルーム アプリケーションの最終的なアーキテクチャです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74d59cd9267f54d8/6a17f2de505ac31129ad8c71/82e01a0d9746038eafd69d11177042b5390507ae-1600x838.png" alt="" /><h2>セクション3: モデルコンテキストプロトコル (MCP)</h2><h3>定義と目的</h3><p>モデル コンテキスト プロトコル (MCP) は、Anthropic によって開発された標準化されたプロトコルであり、ユーザー定義のツール、リソース、プロンプト、その他の補足的なコードベースの追加機能を使用して個々の LLM を強化および強化します。MCP は、言語モデルと、タスクを効果的に完了するために必要な外部リソースとの間のユニバーサル インターフェイスを提供します。この<a href="https://www.elastic.co/search-labs/blog/mcp-current-state">記事では</a>、ユースケース、新たなトレンド、Elastic 独自の実装の例を挙げて、MCP の現状を概説します。</p><h3>MCPのコアコンセプト</h3><p>MCP は、次の 3 つの主要コンポーネントを持つクライアント サーバー アーキテクチャで動作します。</p><ul><li><p><strong>クライアント:</strong> MCP サーバーに接続してその機能にアクセスするアプリケーション (Claude Desktop やカスタム AI アプリケーションなど)。</p></li><li><p><strong>サーバー</strong>: 言語モデルにリソース、ツール、プロンプトを公開するアプリケーション。各サーバーは、特定の機能またはデータ ソースへのアクセスを提供することに特化しています。</p><ul><li><p><strong>ツール</strong>: モデルがデータベースの検索、外部APIの呼び出し、データに対する変換の実行などのアクションを実行するために呼び出すことができるユーザー定義関数</p></li><li><p><strong>リソース:</strong>モデルが読み取り可能なデータ ソース。動的または静的データが提供され、URI パターン (REST ルートに類似) 経由でアクセスされます。</p></li><li><p><strong>プロンプト:</strong>特定のタスクを実行するためにモデルをガイドする変数を含む再利用可能なプロンプト テンプレート。</p></li></ul></li></ul><h3>リクエスト・レスポンスパターン</h3><p>MCP は、REST API に似た、使い慣れた要求と応答の相互作用パターンに従います。クライアント (LLM) がリソースを要求するかツールを呼び出すと、MCP サーバーが要求を処理して結果を返します。LLM はこれを使用してタスクを続行します。周辺サーバーを備えたこの集中型モデルは、ピアツーピアのエージェント通信に比べて、よりシンプルな統合パターンを提供します。</p><h3>ニュースルームのMCP</h3><p>私たちのニュースルームの例では、個々のエージェントが MCP サーバーを使用して必要なツールとデータにアクセスします。</p><ul><li><p><strong>研究者エージェントは</strong>以下を使用します:</p><ul><li><p>ニュース API MCP サーバー (ニュース データベースへのアクセス)</p></li><li><p>ファクトチェックMCPサーバー（信頼できる情報源との照合による主張の検証）</p></li><li><p>学術データベース MCP サーバー (学術論文と研究)</p></li></ul></li><li><p><strong>レポーターエージェントは</strong>以下を使用します:</p><ul><li><p>スタイルガイド MCP サーバー (ニュースルームの執筆基準)</p></li><li><p>テンプレート MCP サーバー (記事テンプレートとフォーマット)</p></li><li><p>画像ライブラリ MCP サーバー (ストック写真とグラフィック)</p></li></ul></li><li><p><strong>エディターエージェントは</strong>以下を使用します:</p><ul><li><p>文法チェッカーMCPサーバー（言語品質ツール）</p></li><li><p>盗作検出MCPサーバー（独創性検証）</p></li><li><p>SEO分析MCPサーバー（見出しとキーワードの最適化）</p></li></ul></li><li><p><strong>Publisher Agent は</strong>以下を使用します:</p><ul><li><p>CMS MCP サーバー (コンテンツ管理システム API)</p></li><li><p>CI/CD MCP サーバー (デプロイメント パイプライン)</p></li><li><p>Analytics MCP サーバー (追跡と監視)</p></li></ul></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt195fe0bd36d36a48/6a17f2e0b1e113afe479f36c/b67311e3b58b27f9eb1b42a7b1dbad47ef3be4ad-808x535.png" alt="" /><h2>
セクション4: アーキテクチャの比較</h2><h3>A2Aを使用する場合</h3><p>A2A アーキテクチャは<strong>、真のマルチエージェントコラボレーションを必要とするシナリオ</strong>に優れています。調整を必要とする複数ステップのワークフローでは、特にタスクに複数の順次または並列ステップが含まれる場合、反復と改良が必要なワークフロー、およびチェックポイントと検証のニーズがあるプロセスの場合に、A2A から大きなメリットが得られます。私たちのニュースルームの例では、ストーリーのワークフローでは記者が記事を書く必要がありますが、特定の事実に対する信頼性が低い場合は研究者に繰り返し報告し、その後編集者に進み、最終的に発行者に渡す必要がある場合があります。</p><p><strong>複数の領域にわたるドメイン固有の特化</strong>は、A2A のもう 1 つの強力な使用例です。より大きなタスクを達成するためにさまざまな分野の複数の専門家が必要であり、各エージェントがさまざまな側面に関する深いドメイン知識と専門的な推論機能を提供する場合、A2A はそれらの接続を行うために必要な調整フレームワークを提供します。ニュースルームはこれを完璧に例証しています。リサーチャーは情報収集、レポーターは執筆、編集者は品質管理を専門としており、それぞれが異なる専門知識を持っています。</p><p>自律的なエージェントの動作の必要性により、A2A は特に価値が高まります。<strong>独立した意思決定を行い、変化する状況に基づいて積極的な行動を示し、ワークフロー要件に動的に適応できる</strong>エージェントは、A2A アーキテクチャで成功します。特化された機能の水平スケーリングも重要な利点の 1 つです。単一の万能エージェントではなく、複数の特化エージェントが連携して動作し、同じエージェントの複数のインスタンスがサブタスクを非同期的に処理できます。たとえば、ニュースルームでニュース速報を取材しているとき、複数の記者エージェントが同時に同じニュースのさまざまな角度から取材することがあります。</p><p>最後に、真のマルチエージェントコラボレーションを必要とするタスクは A2A に最適です。これには<a href="https://arxiv.org/abs/2404.18796">、陪審員としての LLM 評価</a>メカニズム、合意形成および投票システム、および最善の結果に到達するために<strong>複数の視点が必要となる共同問題解決が</strong>含まれます。</p><h3>MCPを使用する場合</h3><p>モデル コンテキスト プロトコルは、単一の AI モデルの機能を拡張する場合に最適です。単一の AI モデルが複数のツールやデータ ソースにアクセスする必要がある場合、MCP は、集中型の推論と分散ツール、および簡単なツール統合を組み合わせた完璧なソリューションを提供します。私たちのニュースルームの例では、研究者エージェント (1 つのモデル) は、ニュース API、ファクトチェック サービス、学術データベースなど、標準化された MCP サーバーを介してアクセスされる複数のデータ ソースにアクセスする必要があります。</p><p>ツール統合の広範な共有と再利用性が重要になる場合は、標準化されたツール統合が優先されます。MCP は、一般的な統合の開発時間を大幅に短縮する、事前に構築された MCP サーバーのエコシステムを備えているため、この点で優れています。シンプルさと保守性が求められる場合、MCP の要求応答パターンは開発者に馴染みがあり、分散システムよりも理解やデバッグが容易で、運用上の複雑さも少なくなります。</p><p>最後に、MCP は、システムとのリモート通信を容易にするためにソフトウェア プロバイダーによって提供されることがよくあります。プロバイダーが提供するこれらの MCP サーバーは、独自のシステムへの標準化されたインターフェースを提供しながら、オンボーディングと開発時間を大幅に短縮し、カスタム API 開発よりも統合をはるかに簡単にします。</p><h3>両方を使用する場合 (A2A ❤️ の MCP)</h3><p><a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">MCP 統合に関する A2A ドキュメント</a>に記載されているように、多くの高度なシステムは A2A と MCP を組み合わせることでメリットを得られます。調整と標準化の両方を必要とするシステムは、ハイブリッド アプローチに最適です。A2A はエージェントの調整とワークフロー オーケストレーションを処理し、MCP は個々のエージェントにツール アクセスを提供します。私たちのニュースルームの例では、エージェントは A2A を介して調整し、ワークフローは記者から研究者、編集者、そして発行者へと移行します。ただし、各エージェントは専用のツール用に MCP サーバーを使用するため、アーキテクチャが明確に分離されます。</p><p>ツール アクセスにそれぞれ MCP を使用する複数の特殊エージェントは、A2A によって処理されるエージェント調整レイヤーと、MCP によって管理されるツール アクセス レイヤーがある一般的なパターンを表します。このように関心事を明確に分離することで、システムの理解と保守が容易になります。</p><p>両方のアプローチを組み合わせることによる利点は非常に大きいです。特殊化、自律性、並列処理などのマルチエージェント システムの組織的な利点が得られると同時に、ツールの統合やリソース アクセスなどの MCP の標準化とエコシステムの利点も享受できます。エージェント調整 (A2A) とリソース アクセス (MCP) は明確に区別されており、重要なのは、API アクセスなどの小規模なタスクのみには A2A は必要ないことです。MCP は、マルチエージェント オーケストレーションのオーバーヘッドなしで、これらのタスクを効率的に処理します。</p><p><strong>FAQ: A2A vs. MCP - ユースケース</strong></p><p>機能</p><p>エージェント2エージェント（A2A）</p><p>モデルコンテキストプロトコル（MCP）</p><p>ハイブリッド（A2A + MCP）</p><p>主な目標</p><p>マルチエージェント調整: 専門エージェントのチームが、複雑な複数ステップのワークフローで連携できるようにします。</p><p>単一エージェントの拡張: 外部ツール、リソース、およびデータを使用して、単一の LLM/エージェントの機能を拡張します。</p><p>組み合わせた強み: A2A がチームのワークフローを処理し、MCP が各チーム メンバーにツールを提供します。</p><p>ニュースルームチームの例</p><p>ワークフロー チェーン: ニュース チーフ → レポーター → リサーチャー → 編集者 → 発行者。これは調整レイヤーです。</p><p>個々のエージェントのツール: スタイル ガイド サーバーとテンプレート サーバーにアクセスする Reporter Agent (MCP 経由)。これはツール アクセス レイヤーです。</p><p>完全なシステム: 記者は編集者 (A2A) と連携し、画像ライブラリ MCP サーバーを使用して記事のグラフィックを検索します。</p><p>いつどれを使うか</p><p>真のコラボレーション、反復、改良、または専門知識を複数のエージェントに分割する必要がある場合。</p><p>1 つのエージェントが複数のツールやデータ ソースにアクセスする必要がある場合、または独自のシステムとの標準化された統合が必要な場合。</p><p>マルチエージェント システムの組織的利点と、MCP の標準化およびエコシステムの利点が必要な場合。</p><p>コアベネフィット</p><p>自律性とスケーリング: エージェントは独立して決定を下すことができ、システムは特殊な機能の水平スケーリングを可能にします。</p><p>シンプルさと標準化: 集中化された推論によりデバッグと保守が容易になり、リソースに対する汎用的なインターフェースが提供されます。</p><p>関心事の明確な分離: システムを理解しやすくなります: A2A = チームワーク、MCP = ツール アクセス。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1735ea5de41e10fd/6a17f2e26864a4125cb688c4/ddf6a29b1107ac6a63e94ecef703abc561a29e1e-986x656.png" alt="" /><h2>まとめ</h2><p>これは、データとツールへのサポートと外部アクセスを提供するために MCP サーバーで強化された A2A ベースのエージェントの実装を扱った 2 部構成の最初のセクションです。次の部分では、実際のコードを調べて、オンライン ニュースルームのアクティビティをエミュレートするためにそれらが連携して動作する様子を示します。どちらのフレームワークも、それ自体で非常に有能で柔軟性に優れていますが、連携して動作することで、どれだけ互いを補完し合うかがわかります。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2716d804698ec878/6a17f2e41480095fd7b48888/9f938d8e2f0fdf7509edf028816c48bdbc8b3fc7-1600x900.png" length="0" type="image/png"/>
    <pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[MastraとElasticsearchを使用してセマンティックリコールを備えた知識エージェントを構築する]]></title>
    <description><![CDATA[メモリと情報検索用のベクトル ストアとして Mastra と Elasticsearch を使用して、セマンティック リコールを備えたナレッジ エージェントを構築する方法を学びます。]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">コンテキスト エンジニアリングは</a>、信頼性の高い AI エージェントとアーキテクチャの構築においてますます重要になっています。モデルがどんどん良くなるにつれて、その有効性と信頼性はトレーニングされたデータに依存するのではなく、適切なコンテキストにどれだけ適切に基づいているかに依存するようになります。最も関連性の高い情報を適切なタイミングで取得して適用できるエージェントは、正確で信頼できる出力を生成する可能性がはるかに高くなります。</p><p>このブログでは、 <a href="https://mastra.ai/">Mastra</a>を使用して、Elasticsearch をメモリおよび検索バックエンドとして使用し、ユーザーの発言を記憶し、後で関連情報を思い出すことができるナレッジ エージェントを構築します。これと同じ概念を実際のユースケースに簡単に拡張できます。サポート エージェントが過去の会話や解決策を記憶し、特定のユーザーへの応答をカスタマイズしたり、以前のコンテキストに基づいてより迅速に解決策を提示したりできると考えてください。</p><p>ここから手順に従って、ステップごとに構築する方法を確認してください。迷ってしまったり、完成した例を実行したいだけの場合は、<a href="https://github.com/jdarmada/getting-started-mastra-elastic/tree/main">ここにある</a>リポジトリを確認してください。</p><h2>マストラとは何ですか？</h2><p>Mastra は、推論、メモリ、ツールの交換可能なパーツを備えた AI エージェントを構築するためのオープンソースの TypeScript フレームワークです。<a href="https://mastra.ai/docs/memory/semantic-recall">セマンティック リコール</a>機能により、エージェントはメッセージをベクター データベースに埋め込みとして保存することで、過去のやり取りを記憶して取り出すことができます。これにより、エージェントは長期的な会話のコンテキストと継続性を維持できます。Elasticsearch は効率的な高密度ベクトル検索をサポートしているため、この機能を有効にするのに最適なベクトル ストアです。セマンティックリコールがトリガーされると、エージェントは関連する過去のメッセージをモデルのコンテキストウィンドウに引き出し、モデルが取得したコンテキストを推論と応答の基礎として使用できるようにします。</p><h2>始めるために必要なもの</h2><ul><li><p>ノード v18+</p></li><li><p>Elasticsearch（バージョン8.15以降）</p></li><li><p>Elasticsearch APIキー</p></li><li><p><a href="https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key">OpenAI APIキー</a></p></li></ul><p>注: デモでは OpenAI プロバイダーを使用するため、これが必要になりますが、Mastra は他の AI SDK とコミュニティ モデル プロバイダーをサポートしているため、設定に応じて簡単に交換できます。</p><h2>Mastraプロジェクトの構築</h2><p>プロジェクトの足場を提供するために、Mastra の組み込み CLI を使用します。次のコマンドを実行します。</p>npm create mastra@latest<p>次のような一連のプロンプトが表示されます。</p><p>1. プロジェクトに名前を付けます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87f941f654d03827/6a16f7af67045b214d45bfa1/2b9fe559e0276140dd539e24f916a73c60870405-620x84.png" alt="Mastraアプリでプロンプトに名前を付ける" /><p>2. このデフォルト設定を維持することもできますし、空白のままにしておくこともできます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7dbb3d4f27435cac/6a16f7b0cdacbf29497d27de/e04729eb03bce8499e973e18c28642402340d0e5-852x68.png" alt="プロンプトファイルを保存する場所をmastraに伝える" /><p>3. このプロジェクトでは、OpenAI が提供するモデルを使用します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f654f6cb9397e94/6a16f7b2964cea899a08b942/a86596a469a71bdf8bd99cbaf528d0f0cf7272c0-436x222.png" alt="MastraでOpenAIが提供するモデルを選択する" /><p>4. すべての環境変数を、後のステップで設定する `.env` ファイルに保存するため、「今はスキップ」オプションを選択します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff106117521a3519/6a16f7b3c1e8a5031af880d8/02b19ccc34af0bdacf52fd94b519d036540ca2e6-426x114.png" alt="OpenAIキーについては今はスキップを選択" /><p>5. このオプションをスキップすることもできます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcda7d9c51c3878d7/6a16f7b450916809dbe1b892/b3fe63d19d270bc2e0de1dd92033bf8b26750819-990x208.png" alt="" /><p>初期化が完了したら、次のステップに進むことができます。</p><h3>依存関係のインストール</h3><p>次に、いくつかの依存関係をインストールする必要があります。</p>npm install ai @ai-sdk/openai @elastic/elasticsearch dotenv<ul><li><p><code>ai</code> - JavaScript/TypeScript で AI モデル、プロンプト、ワークフローを管理するためのツールを提供するコア AI SDK パッケージ。Mastra は Vercel の<a href="https://ai-sdk.dev/">AI SDK</a>上に構築されているため、エージェントとのモデルのインタラクションを有効にするにはこの依存関係が必要です。</p></li><li><p><code>@ai-sdk/openai</code> - AI SDK を OpenAI モデル (GPT-4、GPT-4o など) に接続し、OpenAI API キーを使用した API 呼び出しを可能にするプラグイン。</p></li><li><p><code>@elastic/elasticsearch</code> - <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript">Node.js 用の公式 Elasticsearch クライアント</a>、インデックス作成、検索、ベクター操作のために Elastic Cloud またはローカル クラスターに接続するために使用されます。</p></li><li><p><code>dotenv</code> - .envから環境変数を読み込みますファイルをprocess.envにコピーし、API キーや Elasticsearch エンドポイントなどの資格情報を安全に挿入できるようになります。</p></li></ul><h3>環境変数の設定</h3><p>プロジェクトのルート ディレクトリに<code>.env</code>ファイルが存在しない場合は作成します。あるいは、<a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/.env.example">リポジトリ</a>に提供されている例<code>.env</code>をコピーして名前を変更することもできます。このファイルでは、次の変数を追加できます。</p>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"<p>これで基本的な設定は完了です。ここから、エージェントの構築とオーケストレーションを開始できます。さらに一歩進んで、Elasticsearch をストアおよびベクター検索レイヤーとして追加します。</p><h2>ベクターストアとしてElasticsearchを追加する</h2><p><code>stores</code>という新しいフォルダーを作成し、その中にこの<a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/src/mastra/stores/elastic-store.ts">ファイル</a>を追加します。Mastra と Elastic が公式の Elasticsearch ベクター ストア統合を出荷する前に、 <a href="https://github.com/abhiaiyer91">Abhi Aiyer</a> (Mastra CTO) が<code>ElasticVector</code>と呼ばれるこの初期のプロトタイプ クラスを共有しました。簡単に言えば、Mastra のメモリ抽象化を Elasticsearch の高密度ベクトル機能に接続することで、開発者はエージェントのベクトル データベースとして Elasticsearch を導入できるようになります。</p><p>統合の重要な部分を詳しく見てみましょう。</p><h3>Elasticsearchクライアントの取り込み</h3><p>このセクションでは、 <code>ElasticVector</code>クラスを定義し、標準デプロイメントとサーバーレスデプロイメントの両方をサポートする Elasticsearch クライアント接続を設定します。</p>export interface ElasticVectorConfig extends ClientOptions {
    /**
     * Explicitly specify if connecting to Elasticsearch Serverless.
     * If not provided, will be auto-detected on first use.
     */
    isServerless?: boolean;
    
    /**
     * Maximum documents to count accurately when describing indices.
     * Higher values provide accurate counts but may impact performance on large indices.
     * 
     * @default 10000
     */
    maxCountAccuracy?: number;
}

export class ElasticVector extends MastraVector {
    private client: Client;
    private isServerless: boolean | undefined;
    private deploymentChecked: boolean = false;
    private readonly maxCountAccuracy: number;

    constructor(config: ElasticVectorConfig) {
        super();
        this.client = new Client(config);
        this.isServerless = config.isServerless;
        this.maxCountAccuracy = config.maxCountAccuracy ?? 10000;
    }
}<ul><li><p><code>ElasticVectorConfig extends ClientOptions</code>: これにより、すべての Elasticsearch クライアント オプション ( <code>node</code> 、 <code>auth</code> 、 <code>requestTimeout</code>など) を継承し、カスタム プロパティを追加する新しい構成インターフェースが作成されます。つまり、ユーザーは、サーバーレス固有のオプションとともに、有効な Elasticsearch 構成を渡すことができるということです。</p></li><li><p><code>extends MastraVector</code>: これにより、 <code>ElasticVector</code> 、すべてのベクター ストア統合が準拠する共通インターフェースである Mastra の基本<code>MastraVector</code>クラスから継承できるようになります。これにより、エージェントの観点から見ると、Elasticsearch は他の Mastra ベクター バックエンドと同じように動作するようになります。</p></li><li><p><code>private client: Client</code>: これは、Elasticsearch JavaScript クライアントのインスタンスを保持するプライベート プロパティです。これにより、クラスはクラスターと直接通信できるようになります。</p></li><li><p><code>isServerless</code> および<code>deploymentChecked</code> : これらのプロパティは連携して、サーバーレスまたは標準の Elasticsearch デプロイメントに接続されているかどうかを検出し、キャッシュします。この検出は最初の使用時に自動的に行われますが、明示的に構成することもできます。</p></li><li><p><code>constructor(config: ClientOptions)</code>: このコンストラクターは、構成オブジェクト (Elasticsearch の資格情報とオプションのサーバーレス設定を含む) を受け取り、それを使用して<code>this.client = new Client(config)</code>行でクライアントを初期化します。</p></li><li><p><code>super()</code>: これは Mastra の基本コンストラクターを呼び出すため、ログ記録、検証ヘルパー、およびその他の内部フックを継承します。</p></li></ul><p>この時点で、Mastraは新しいベクターストアがあることを知っています。 <code>ElasticVector</code></p><h3>展開タイプの検出</h3><p>インデックスを作成する前に、アダプターは標準の Elasticsearch を使用しているか Elasticsearch Serverless を使用しているかを自動的に検出します。サーバーレス デプロイメントでは手動でのシャード構成が許可されないため、これは重要です。</p>private async detectServerless(): Promise&lt;boolean&gt; {
    // Return cached result if already detected
    if (this.deploymentChecked) {
        return this.isServerless ?? false;
    }

    // Use explicit configuration if provided
    if (this.isServerless !== undefined) {
        this.deploymentChecked = true;
        this.logger?.info(
            `Using explicit deployment type: ${this.isServerless ? 'Serverless' : 'Standard'}`
        );
        return this.isServerless;
    }

    try {
        const info = await this.client.info();
        
        // Primary detection: build flavor (most reliable)
        const isBuildFlavorServerless = info.version?.build_flavor === 'serverless';
        
        // Secondary detection: tagline (fallback)
        const isTaglineServerless = info.tagline?.toLowerCase().includes('serverless') ?? false;
        
        this.isServerless = isBuildFlavorServerless || isTaglineServerless;
        this.deploymentChecked = true;
        
        this.logger?.info(
            `Auto-detected ${this.isServerless ? 'Serverless' : 'Standard'} Elasticsearch deployment`,
            { 
                buildFlavor: info.version?.build_flavor, 
                version: info.version?.number,
                detectionMethod: isBuildFlavorServerless ? 'build_flavor' : 'tagline'
            }
        );
        
        return this.isServerless;
    } catch (error) {
        this.logger?.warn(
            'Could not auto-detect deployment type, assuming Standard Elasticsearch. ' +
            'Set isServerless: true explicitly in config if using Serverless.',
            { error: error instanceof Error ? error.message : String(error) }
        );
        this.isServerless = false;
        this.deploymentChecked = true;
        return false;
    }
}<p>何が起こっていますか：</p><ul><li><p>まず、構成で明示的に<code>isServerless</code>が設定されているかどうかを確認します (自動検出をスキップします)</p></li><li><p>Elasticsearch の<code>info()</code> API を呼び出してクラスター情報を取得します</p></li><li><p><code>build_flavor field</code>をチェックします (サーバーレス デプロイメントは<code>serverless</code>を返します)</p></li><li><p>ビルドフレーバーが利用できない場合はタグラインをチェックする</p></li><li><p>結果をキャッシュして、API 呼び出しの繰り返しを回避します。</p></li><li><p>検出に失敗した場合は標準展開をデフォルトとする</p></li></ul><p> 使用例:</p>// Option 1: Auto-detect (recommended)
const vector = new ElasticVector({
    node: 'https://your-cluster.es.cloud',
    auth: { apiKey: 'your-api-key' }
});
// Detection happens automatically on first index operation

// Option 2: Explicit configuration (faster startup)
const vector = new ElasticVector({
    node: 'https://your-serverless.es.cloud',
    auth: { apiKey: 'your-api-key' },
    isServerless: true  // Skips auto-detection
});<h3>Elasticsearchに「メモリ」ストアを作成する</h3><p>以下の関数は、埋め込みを保存するための Elasticsearch インデックスを設定します。インデックスがすでに存在するかどうかを確認します。そうでない場合は、埋め込みとカスタム類似度メトリックを格納するための<code>dense_vector</code>フィールドを含む以下のマッピングを使用して作成します。</p><p>注意すべき点:</p><ul><li><p><code>dimension</code>パラメータは各埋め込みベクトルの長さであり、使用している埋め込みモデルによって異なります。私たちの場合、サイズ<code>1536</code>のベクトルを出力する OpenAI の<code>text-embedding-3-small</code>モデルを使用して埋め込みを生成します。これをデフォルト値として使用します。</p></li><li><p>以下のマッピングで使用される<code>similarity</code>変数は、ヘルパー関数 c <code>onst similarity = this.mapMetricToSimilarity(metric)</code>から定義されます。この関数は、 <code>metric</code>パラメータの値を受け取り、選択された距離メトリックの Elasticsearch 互換キーワードに変換します。</p><ul><li><p>たとえば、Mastra では、ベクトルの類似性を表すために<code>cosine</code> 、 <code>euclidean</code> 、 <code>dotproduct</code>などの一般的な用語を使用します。メトリック<code>euclidean</code> Elasticsearch マッピングに直接渡すと、Elasticsearch はキーワード<code>l2_norm</code>がユークリッド距離を表すと想定するため、エラーが発生します。</p></li></ul></li><li><p>サーバーレス互換性: サーバーレスデプロイメントのシャードとレプリカの設定は Elasticsearch Serverless によって自動的に管理されるため、コードでは自動的に省略されます。</p></li></ul>async createIndex(params: CreateIndexParams): Promise&lt;void&gt; {
    const { indexName, dimension = 1536, metric = 'cosine' } = params;

    try {
        const exists = await this.client.indices.exists({ index: indexName });

        if (exists) {
            try {
                await this.validateExistingIndex(indexName, dimension, metric);
                this.logger?.info(`Index "${indexName}" already exists and is valid`);
                return;
            } catch (validationError) {
                throw new Error(
                    `Index "${indexName}" exists but does not match the required configuration: ${
                        validationError instanceof Error ? validationError.message : String(validationError)
                    }`
                );
            }
        }

        const isServerless = await this.detectServerless();
        const similarity = this.mapMetricToSimilarity(metric);

        const indexConfig: any = {
            index: indexName,
            mappings: {
                properties: {
                    vector: {
                        type: 'dense_vector',
                        dims: dimension,
                        index: true,
                        similarity: similarity,
                    },
                    metadata: {
                        type: 'object',
                        enabled: true,
                        dynamic: true, // Allows flexible metadata structures
                    },
                },
            },
        };

        // Only configure shards/replicas for non-serverless deployments
        // Serverless manages infrastructure automatically
        if (!isServerless) {
            indexConfig.settings = {
                number_of_shards: 1,
                number_of_replicas: 0, // Increase for production HA deployments
            };
        }

        await this.client.indices.create(indexConfig);

        this.logger?.info(
            `Created ${isServerless ? 'Serverless' : 'Standard'} Elasticsearch index "${indexName}"`,
            { dimension, metric, similarity }
        );
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to create index "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to create index "${indexName}": ${errorMessage}`);
    }
}<h3>やりとりの後に新しい記憶やメモを保存する</h3><p>この関数は、各インタラクションの後に生成された新しい埋め込みをメタデータとともに取得し、Elastic の<code>bulk</code> API を使用してそれらをインデックスに挿入または更新します。<code>bulk</code> API は複数の書き込み操作を 1 つのリクエストにグループ化します。このインデックス作成パフォーマンスの向上により、エージェントのメモリが増加し続けても更新の効率が維持されます。</p>async upsert(params: UpsertVectorParams): Promise&lt;string[]&gt; {
    const { indexName, vectors, metadata = [], ids } = params;

    try {
        // Generate unique IDs if not provided
        const vectorIds = ids || vectors.map((_, i) =&gt; 
            `vec_${Date.now()}_${i}_${Math.random().toString(36).substr(2, 9)}`
        );

        const operations = vectors.flatMap((vec, index) =&gt; [
            { index: { _index: indexName, _id: vectorIds[index] } },
            {
                vector: vec,
                metadata: metadata[index] || {},
            },
        ]);

        const response = await this.client.bulk({
            refresh: true,
            operations,
        });

        if (response.errors) {
            const erroredItems = response.items.filter((item: any) =&gt; item.index?.error);
            const erroredIds = erroredItems.map((item: any) =&gt; item.index?._id);
            const errorDetails = erroredItems.slice(0, 3).map((item: any) =&gt; ({
                id: item.index?._id,
                error: item.index?.error?.reason || item.index?.error,
                type: item.index?.error?.type
            }));
            
            const errorMessage = `Failed to upsert ${erroredIds.length}/${vectors.length} vectors`;
            console.error(`${errorMessage}. Sample errors:`, JSON.stringify(errorDetails, null, 2));
            this.logger?.error(errorMessage, { 
                failedCount: erroredIds.length, 
                totalCount: vectors.length,
                sampleErrors: errorDetails 
            });
            
            // Still return successfully inserted IDs
            const successfulIds = vectorIds.filter((id, idx) =&gt; 
                !erroredIds.includes(id)
            );
            
            if (successfulIds.length === 0) {
                throw new Error(`${errorMessage}. All operations failed. See logs for details.`);
            }
            
            return successfulIds;
        }

        this.logger?.info(`Successfully upserted ${vectors.length} vectors to "${indexName}"`);
        return vectorIds;
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to upsert vectors to "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to upsert vectors to "${indexName}": ${errorMessage}`);
    }
}<h3>意味的想起のための類似ベクトルのクエリ</h3><p>この機能は、セマンティック リコール機能の中核です。エージェントはベクトル検索を使用して、インデックス内に保存されている類似の埋め込みを見つけます。</p>async query(params: QueryVectorParams&lt;any&gt;): Promise&lt;QueryResult[]&gt; {
    const { indexName, queryVector, topK = 10, filter, includeVector = false } = params;

    try {
        const knnQuery: any = {
            field: 'vector',
            query_vector: queryVector,
            k: topK,
            num_candidates: Math.max(topK * 10, 100), // Search more candidates for better recall
        };

        // Apply metadata filters if provided
        if (filter) {
            knnQuery.filter = this.buildElasticFilter(filter);
        }

        const sourceFields = ['metadata'];
        if (includeVector) {
            sourceFields.push('vector');
        }

        const response = await this.client.search({
            index: indexName,
            knn: knnQuery,
            size: topK,
            _source: sourceFields,
        });

        const results = response.hits.hits.map((hit: any) =&gt; ({
            id: hit._id,
            score: hit._score || 0,
            metadata: hit._source?.metadata || {},
            vector: includeVector ? hit._source?.vector : undefined,
        }));

        this.logger?.debug(`Query returned ${results.length} results from "${indexName}"`);
        return results;
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to query vectors from "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to query vectors from "${indexName}": ${errorMessage}`);
    }
}<p>内部構造:</p><ul><li><p>Elasticsearch の<code>knn</code> API を使用して<a href="https://www.elastic.co/docs/solutions/search/vector/knn">kNN</a> (k 近傍法) クエリを実行します。</p></li><li><p>入力クエリ ベクトルに類似する上位 K 個のベクトルを取得します。</p></li><li><p>オプションでメタデータ フィルターを適用して結果を絞り込む (例: 特定のカテゴリまたは時間範囲内のみを検索する)</p></li><li><p>ドキュメント ID、類似度スコア、保存されたメタデータを含む構造化された結果を返します。</p></li></ul><h2>知識エージェントの作成</h2><p><code>ElasticVector</code>統合を通じて Mastra と Elasticsearch の接続を確認したので、次は Knowledge Agent 自体を作成しましょう。</p><p>フォルダー<code>agents</code>内に、 <code>knowledge-agent.ts</code>というファイルを作成します。まず、環境変数を接続し、Elasticsearch クライアントを初期化します。</p>import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { openai } from '@ai-sdk/openai';
import { Client } from '@elastic/elasticsearch';
import { ElasticVector } from '../stores/elastic-store';
import dotenv from "dotenv";

dotenv.config();

const ELASTICSEARCH_ENDPOINT = process.env.ELASTICSEARCH_ENDPOINT;
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY;

//Error check for undefined credentials
if (!ELASTICSEARCH_ENDPOINT || !ELASTICSEARCH_API_KEY) {
  throw new Error('Missing Elasticsearch credentials');
}

//Check to see if a connection can be established
const testClient = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: { 
    apiKey: ELASTICSEARCH_API_KEY 
  },
});

try {
  await testClient.ping();
  console.log('Connected to Elasticsearch successfully');
} catch (error: unknown) {
  if (error instanceof Error) {
    console.error('Failed to connect to Elasticsearch:', error.message);
  } else {
    console.error('Failed to connect to Elasticsearch:', error);
  }
  process.exit(1);
}
//Initialize the Elasticsearch vector store
const vectorStore = new ElasticVector({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
//Optional: Explicitly set to true if using Elasticsearch Serverless to skip auto-detection and improve startup time
//isServerless: true,
});<p>ここでは、次の操作を行います。</p><ul><li><p><code>dotenv</code>を使用して、 <code>.env</code>ファイルから変数を読み込みます。</p></li><li><p>Elasticsearch の資格情報が正しく挿入されているかどうかを確認し、クライアントへの接続を正常に確立できるかどうかを確認します。</p></li><li><p>Elasticsearch エンドポイントと API キーを<code>ElasticVector</code>コンストラクターに渡して、先ほど定義したベクター ストアのインスタンスを作成します。</p></li><li><p>Elasticsearch Serverless を使用している場合は、オプションで<code>isServerless: true</code>を指定します。これにより、自動検出手順がスキップされ、起動時間が短縮されます。省略した場合、アダプタは最初の使用時にデプロイメント タイプを自動的に検出します。</p></li></ul><p>次に、Mastra の<code>Agent</code>クラスを使用してエージェントを定義します。</p>export const knowledgeAgent = new Agent({
    name: 'KnowledgeAgent',
    instructions: 'You are a helpful knowledge assistant.',
    model: openai('gpt-4o'),
    memory: new Memory({

        vector: vectorStore,

        //embedder used to create embeddings for each message
        embedder: 'openai/text-embedding-3-small',

        //set semantic recall options
        options: {
            semanticRecall: {
                topK: 3, // retrieve 3 similar messages
                messageRange: 2, // include 2 messages before/after each match
                scope: 'resource',
            },
        },
    }),
});<p>定義できるフィールドは次のとおりです。</p><ul><li><p><code>name</code> そして<code>instructions</code> : アイデンティティと主な機能を与えます。</p></li><li><p><code>model</code>: <code>@ai-sdk/openai</code>パッケージを通じて OpenAI の<code>gpt-4o</code>を使用しています。</p></li><li><p><code>memory</code>:</p><ul><li><p><code>vector</code>: Elasticsearch ストアを指すので、埋め込みはそこから保存され、取得されます。</p></li><li><p><code>embedder</code>: 埋め込みを生成するためにどのモデルを使用するか</p></li><li><p><code>semanticRecall</code> オプションによってリコールの動作が決まります。</p><ul><li><p><code>topK</code>: 意味的に類似したメッセージを取得する数。</p></li><li><p><code>messageRange</code>: 各マッチにどの程度の会話を含めるか。</p></li><li><p><code>scope</code>: メモリの境界を定義します。</p></li></ul></li></ul></li></ul><p>もうすぐ終わりです。新しく作成したエージェントを Mastra 構成に追加するだけです。<a href="http://index.ts/"><code>index.ts</code></a>というファイルで、ナレッジ エージェントをインポートし、 <code>agents</code>フィールドに挿入します。</p>export const mastra = new Mastra({
  agents: { knowledgeAgent },
  storage: new LibSQLStore({
    // stores observability, scores, ... into memory storage, if it needs to persist, change to file:../mastra.db
    url: ":memory:",
  }),
  logger: new PinoLogger({
    name: 'Mastra',
    level: 'info',
  }),
  telemetry: {
    // Telemetry is deprecated and will be removed in the Nov 4th release
    enabled: false, 
  },
  observability: {
    // Enables DefaultExporter and CloudExporter for AI tracing
    default: { enabled: true }, 
  },
});<p>その他のフィールドには以下が含まれます。</p><ul><li><p><code>storage</code>: これは、実行履歴、観測性メトリック、スコア、キャッシュのための Mastra の内部データ ストアです。Mastra ストレージの詳細については、<a href="https://mastra.ai/docs/server-db/storage">こちらを</a>ご覧ください。</p></li><li><p><code>logger</code>: Mastra は、軽量の構造化 JSON ロガーである<a href="https://github.com/pinojs/pino">Pino</a>を使用します。エージェントの開始と停止、ツールの呼び出しと結果、エラー、LLM 応答時間などのイベントをキャプチャします。</p></li><li><p><code>observability</code>: エージェントの AI トレースおよび実行の可視性を制御します。追跡対象:</p><ul><li><p>各推論ステップの開始/終了。</p></li><li><p>使用されたモデルまたはツール。</p></li><li><p>入力と出力。</p></li><li><p>スコアと評価</p></li></ul></li></ul><h3>Mastra Studioでエージェントをテストする</h3><p>おめでとうございます！ここまで到達したら、このエージェントを実行し、そのセマンティックリコール能力をテストする準備が整いました。幸いなことに、Mastra には組み込みのチャット UI が用意されているため、独自に構築する必要はありません。</p><p>Mastra 開発サーバーを起動するには、ターミナルを開いて次のコマンドを実行します。</p>npm run dev<p>サーバーの初期バンドルと起動が完了すると、Playground のアドレスが提供されるはずです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5f857fddc74ffc9/6a16f7b6a6c2b995d5e794c0/8b045f70008d26aec4d2e6b59d61085555b9c5b2-686x116.png" alt="プレイグラウンドのサーバーアドレス" /><p>このアドレスをブラウザに貼り付けると、Mastra Studio が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7fdda6ce46ce068/6a16f7b7b0367d4f7672bacf/69bc80fe8486edd9e0cf91d87b39f465aeb23111-1600x438.png" alt="プレイグラウンドのアドレスを貼り付けてMastra Studioにアクセスする" /><p><code>knowledgeAgent</code>のオプションを選択してチャットを開始してください。</p><p>すべてが正しく接続されているかどうかを確認するための簡単なテストでは、次のような情報を入力します。「チームは、主にエンタープライズ契約の更新により、10 月の売上実績が 12% 増加したと発表しました。次のステップは、中規模市場の顧客へのリーチを拡大することです。」次に、新しいチャットを開始して、「次に重点を置く必要があると言った顧客セグメントはどれですか?」などの質問をします。ナレッジエージェントは、最初のチャットで提供した情報を思い出せるはずです。次のような応答が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfec3266e81a7213b/6a16f7b92b835f6f70f4afe2/da8ebddad89874023ed440a8f1ad2cb04ed043f4-1070x288.png" alt="Mastra Studioでナレッジエージェントとチャット - エージェントは情報を思い出すことができます" /><p>このような応答が表示された場合、エージェントが以前のメッセージを Elasticsearch に埋め込みとして正常に保存し、後でベクトル検索を使用して取得したことを意味します。</p><h3>エージェントの長期記憶ストアの検査</h3><p>Mastra Studio のエージェント構成の<code>memory</code>タブに移動します。これにより、エージェントが時間の経過とともに何を学習したかを確認できます。Elasticsearch に埋め込まれて保存されるすべてのメッセージ、応答、およびやり取りは、この長期メモリの一部になります。過去のやり取りを意味的に検索して、エージェントが以前に学習した思い出の情報やコンテキストをすぐに見つけることができます。これは本質的には、エージェントがセマンティックリコール中に使用するメカニズムと同じものですが、ここではそれを直接検査できます。以下の例では、「sales」という用語を検索し、sales に関連する内容を含むすべてのインタラクションを取得しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte428134d7bf2a43a/6a16f7bbb0367d185872bad3/3decaa0c332d288c5ae0b11c25f592c7d50c2f0f-1104x1320.png" alt="知識エージェントの長期記憶ストアを検査する方法" /><h2>まとめ</h2><p>Mastra と Elasticsearch を接続することで、コンテキスト エンジニアリングの重要なレイヤーであるメモリをエージェントに付与できます。セマンティックリコールを使用すると、エージェントは時間の経過とともにコンテキストを構築し、学習した内容に基づいて応答することができます。つまり、より正確で信頼性が高く、自然なやりとりが可能になります。</p><p>この早期の統合は単なる出発点にすぎません。ここで同じパターンを使用すると、過去のチケットを記憶しているサポートエージェント、関連ドキュメントを取得する内部ボット、会話の途中で顧客の詳細を思い出すことができる AI アシスタントなどが可能になります。当社は公式の Mastra 統合にも取り組んでおり、近い将来この組み合わせがさらにシームレスになる予定です。</p><p>次に何を構築するのか楽しみにしています。ぜひ試してみて、 <a href="https://mastra.ai/">Mastra</a>とそのメモリ機能を調べ、発見したことをコミュニティと自由に共有してください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[開発者エクスペリエンス]]></category>
    <category><![CDATA[統合]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09afdbff05603865/6a16f7bd839dfabbf2dcfcb5/b8d51c2726d5573385c9246a7821d12ade4f1b0e-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic MCP サーバー: あらゆる AI エージェントに Agent Builder ツールを公開]]></title>
    <description><![CDATA[Agent Builder に組み込まれている Elastic MCP サーバーを使用して、プライベート データやカスタム ツールにアクセスできる AI エージェントを安全に拡張する方法を説明します。]]></description>
    <content:encoded><![CDATA[<p>Elastic Agent Builder は、Elasticsearch 内の独自のデータと深く統合されたツールとエージェントを作成するためのプラットフォームです。たとえば、内部ドキュメントに対してセマンティック検索を実行したり、観測ログを分析したり、セキュリティアラートを照会したりするツールを作成できます。</p><p>しかし、本当の魔法は、これらのカスタマイズされたデータ対応ツールを、ほとんどの時間を費やす環境に導入できたときに起こります。コード エディター エージェントが組織のプライベート ナレッジ ベースに安全にアクセスできたらどうなるでしょうか?</p><p>ここで、<strong>モデル コンテキスト プロトコル (MCP)</strong>が登場します。Elastic Agent Builder には、プラットフォーム内のツールへのアクセスを提供する組み込みの MCP サーバーが付属しています。</p><h2>Elastic Agent Builder MCP サーバーを使用する理由は何ですか?</h2><p>AI エージェントは非常に強力ですが、その知識は通常、トレーニングに使用されたデータとパブリック インターネット上でアクティブに検索できる情報に限定されます。彼らは、会社の内部設計ドキュメント、チーム固有のデプロイメント ランブック、またはアプリケーション ログの独自の構造については知りません。</p><p>課題は、AI アシスタントに必要な特殊なコンテキストを提供することです。これはまさに、MCP が解決するために設計された問題です。<strong>MCP は、AI モデルまたはエージェントが外部ツールを検出して使用できるようにするオープン スタンダードです。</strong></p><p>これを実現するために、Elastic Agent Builder は組み込みの MCP サーバーを通じてカスタム ツールをネイティブに公開します。つまり、 <strong>Cursor</strong> 、 <strong>VS Code</strong> 、 <strong>Claude Desktop</strong>などの MCP 対応クライアントを、Elastic Agent Builder で構築した特殊なデータ対応ツールに簡単に接続できるということです。</p><h2>MCP を使用する場合 (および使用しない場合)</h2><p>Elastic Agent Builder には、さまざまな統合パターンをサポートするためのいくつかのプロトコルが含まれています。適切なものを選択することが、効果的な AI ワークフローを構築する鍵となります。</p><ul><li><p><a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server"><strong>MCP を使用して</strong></a> 、専用のツールで AI エージェント (<strong> Cursor</strong> や<strong> VS Code</strong> など) を拡張します。これは「独自のツールを持ち込む」アプローチであり、すでに使用しているアシスタントを強化して、プライベート データに安全にアクセスできるようにします。MCP サーバーを通じて公開されるのはツールのみで、Elastic のエージェントはそれとは別です。</p></li><li><p><a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server"><strong>A2A プロトコルを</strong></a><strong> 使用すると</strong> 、完全なカスタム Elastic Agent が他の自律エージェント (<a href="https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise"><strong> Google の Gemini Enterprise</strong></a> など) と連携できるようになります。これはエージェント間の委任用であり、各エージェントは問題を解決するためにピアとして機能します。</p></li><li><p><a href="https://www.elastic.co/docs/solutions/search/agent-builder/kibana-api"><strong>カスタム</strong></a> アプリケーションを最初から構築するときに、完全なプログラム制御を行うには Agent Builder API を 使用します 。</p></li></ul><p>IDE を離れずに社内ドキュメントから回答を得たい開発者にとって、MCP は最適です。</p><h2>例: Agent Builder MCP サーバーを使用した Cursor のカスタム ツール</h2><p>私が日常的に使用している実際の例を見てみましょう。まず、社内のエンジニアリング ドキュメントをクロールして、 <code>elastic-dev-docs</code>という Elasticsearch インデックスにインデックス付けしました。Agent Builder で使用できる汎用の組み込みツールを使用することもできますが、この特定のナレッジベースを照会するための独自のカスタム ツールを作成します。</p><p>カスタム ツールを構築する理由はシンプルです。<strong>制御と精度です</strong>。このアプローチにより、 <code>elastic-dev-docs</code>インデックスに対して高速でセマンティックなクエリを直接実行できるようになります。どのインデックスをターゲットにするか、データをどのように取得するかを完全に制御できます。</p><p>ここで、このカスタム ナレッジ ベースを Cursor のような AI 搭載コード エディターで使用する方法を説明します。</p><h3>ステップ1: Agent Builderでカスタムナレッジベースツールを作成する</h3><p>まず、Agent Builder で新しいツールを作成します。明確で具体的なツールの説明は重要です。なぜなら、それが内部 Elastic Agent であれ、MCP 経由で接続する Cursor などの外部ツールであれ、あらゆる AI エージェントが適切なタスクのためにツールを検出し選択する方法だからです。</p><p>強力な説明は明確である必要があります。たとえば、「elastic-dev-docs インデックスでセマンティック検索を実行して、社内のエンジニアリング ドキュメント、ランブック、リリース手順を検索します。」</p><p>これで、ツールは特定のインデックスに対してセマンティック検索を実行するように構成されます。保存すると、すぐに利用できるようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt011118f0a9279185/6a17f367dbb4ffc4f3fb581a/1eea079908fdf7cc72dbe81abd07ff51601a43d4-1472x1600.png" alt="Agent Builder でカスタム ナレッジ ベース ツールを作成します。" /><p>外部に接続する前に、UI で直接テストできます。<strong>[テスト]</strong>ボタンをクリックするだけで、パラメータを手動で入力し、LLM の動作をエミュレートして、結果を検査し、すべてが正しく動作していることを確認します。</p><h3>ステップ2: CursorをElastic MCPサーバーに接続する</h3><p>Elastic Agent Builder は、安全な MCP エンドポイントを介して利用可能なすべてのツールを自動的に公開します。固有のサーバー URL は、Kibana 内のツール UI で見つけることができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd0e62ae0f394c3d/6a17f368e317916ec32d5933/ba137be30f0eaa7f028b96bd8af4e2779c3f8a33-1600x589.png" alt="Kibana のツール UI のカーソルを Elastic MCP サーバーに接続する方法。" /><p>Cursor に接続するには、この URL と認証用の Elastic API キー ( <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">ES API キーの作成方法を参照</a>) を構成ファイルに追加するだけです。認証には API キーを使用します。これにより、すべてのアクセス制御ルールを尊重し、ツールは付与した権限でのみ実行されるようになります。</p><p>カーソルの<code>~/.cursor/mcp.json</code>内の MCP 構成は次のようになります。</p>{
  "mcpServers": {
    "elastic-agent-builder": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-kibana.kb.company.io/api/agent_builder/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "ApiKey &lt;ELASTIC_API_KEY&gt;"
      }
    }
  }
}<p>設定が保存されると、Cursor で Elastic Agent Builder MCP サーバー ツールが利用可能になります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2837638263e628ed/6a17f36adbb4ffeb9cfb5820/d302c6d3609fbf14fd40e21b9e69e567bf12553f-1600x1002.png" alt="Cursor で使用できる Elastic Agent Builder MCP サーバー ツールのイメージ。" /><h3>ステップ 3: どんどん質問しましょう!</h3><p>接続が確立されると、カーソル エージェントはカスタム ツールを呼び出して質問に答えたり、コード生成プロセスをガイドしたりできるようになります。</p><p>具体的な質問をしてみましょう。</p><p><em>「Elastic Search org のエンジニアリング内部ドキュメントからクローラー サービスをリリースするための手順を参照する」</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt83fa357261b30e93/6a17f36c4b055d16d1432326/14f572730203c23615bb9dd38234bcb3b0f81155-1600x1468.png" alt="カスタム ツールを呼び出して質問に答え、コード生成プロセスをガイドするカーソル エージェント。" /><p>舞台裏では魔法が起こります:</p><ol><li><p>カーソルエージェントはあなたの質問に最もよく答える方法を決定し、 <code>engineering_documentation_internal_search</code></p></li><li><p>自然言語クエリでツールを呼び出す</p></li><li><p>このツールは、 <code>elastic-dev-docs</code>インデックスに対してセマンティック検索を実行し、最も関連性の高い最新の手順を返します。</p></li></ol><p>コード エディターを離れることなく、社内ドキュメントに基づいた正確で信頼できる回答が得られます。体験はシームレスかつ強力です。</p><h2>あなたの番です</h2><p>ここでは、Elastic Agent Builder に組み込まれている MCP サーバーを使用して、プライベート データへの安全なアクセスを備えた AI アシスタントを拡張する方法を説明しました。モデルを本当に役立つものにするためには、独自の情報に基づいてモデルを構築することが鍵となります。</p><p>要約すると、主要な手順について説明しました。</p><ul><li><p>ニーズに合った適切なプロトコルを選択する (MCP)。</p></li><li><p>カスタム ナレッジ ベース ツールを構築します。</p></li><li><p>そのツールを Cursor などの IDE アシスタントに接続します。</p></li></ul><p>エージェントとツールを最も重要なコンテキストから切り離す必要がなくなりました。このガイドがより効果的でデータを考慮したワークフローの作成に役立つことを願っています。楽しい建築を！</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[AIツール ]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5b61961b6269ab1/6a17f36ea29299d839d02db2/ef5153551a1d14833c7f512fede554d1dfb31553-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[AIエージェントの評価：Elasticによるエージェントフレームワークのテスト方法]]></title>
    <description><![CDATA[正確で検証可能な結果を確保するために、エージェントシステムへの変更を Elastic ユーザーにリリースする前に評価およびテストする方法を学びます。]]></description>
    <content:encoded><![CDATA[<h2>はじめに</h2><p>Elastic Stack には、 <a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Agent Builder</a>の近々リリースされる Elastic AI Agent (現在技術プレビュー) や<a href="https://www.elastic.co/docs/solutions/security/ai/attack-discovery">Attack Discovery</a> (8.18 および 9.0 以降で<a href="https://www.elastic.co/blog/whats-new-elastic-security-9-0-0">GA</a>提供) など、LLM を利用したエージェント アプリケーションが多数あり、さらに多くのアプリケーションが開発中です。開発中、そして展開後でも、次の質問に答えることが重要です。</p><ul><li><p>これらの AI アプリケーションの応答の品質をどのように評価するのでしょうか?</p></li><li><p>変更を加えた場合、その変更が本当に改善となり、ユーザー エクスペリエンスが低下しないことをどのように保証すればよいでしょうか。</p></li><li><p>これらの結果を繰り返し簡単にテストするにはどうすればよいでしょうか?</p></li></ul><p>従来のソフトウェア テストとは異なり、生成 AI アプリケーションの評価には、統計的手法、微妙な定性的なレビュー、ユーザーの目標の深い理解が必要になります。</p><p>この記事では、Elastic 開発チームが評価を実施し、展開前に変更の品質を確保し、システム パフォーマンスを監視するために採用しているプロセスについて詳しく説明します。私たちは、あらゆる変更が証拠によって裏付けられ、信頼できる検証可能な結果につながるようにすることを目指しています。このプロセスの一部は Kibana に直接統合されており、オープンソース精神の一環として透明性への取り組みを反映しています。評価データと指標の一部を公開することで、コミュニティの信頼を育み、AI エージェントを開発したり当社の製品を利用したりするすべての人にとって明確なフレームワークを提供することを目指しています。</p><h2>製品例</h2><p>このドキュメントで使用した方法は、Attack Discovery や Elastic AI Agent などのソリューションを反復して改善する方法の基礎となりました。それぞれ2つの簡単な紹介:</p><h3>Elastic Securityの攻撃検出</h3><p>Attack Discovery は LLM を使用して、Elastic 内の攻撃シーケンスを識別および要約します。特定の期間（デフォルトでは 24 時間）内の Elastic Security アラートに基づいて、Attack Discovery のエージェント ワークフローは、攻撃が発生したかどうかを自動的に検出するほか、どのホストまたはユーザーが侵害されたか、どのアラートが結論に寄与したかなどの重要な情報も検出します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb70932abe8d4de75/6a17f04ea292990c52d02d61/20fabb47642dad7b588daaaa8c3a98de860ad01d-1251x758.png" alt="" /><p></p><p>目標は、LLM ベースのソリューションが少なくとも人間と同等の出力を生成することです。</p><h3>エラスティックAIエージェント</h3><p><strong>Elastic Agent Builder は、</strong>すべての検索機能を活用するコンテキスト認識型 AI エージェントを構築するための新しいプラットフォームです。この製品には、会話形式のやりとりを通じてユーザーがデータを理解し、データから回答を得られるよう設計された、あらかじめ構築された汎用エージェントである<strong>Elastic AI Agent</strong>が付属しています。</p><p>エージェントは、Elasticsearch または接続されたナレッジベース内の関連情報を自動的に識別し、事前に構築された一連のツールを活用してそれらと対話することでこれを実現します。これにより、Elastic AI Agent は、単一のドキュメントに関する単純な Q&amp;A から、複数のインデックスにわたる集約や単一または複数ステップの検索を必要とする複雑なリクエストまで、さまざまなユーザー クエリに応答できるようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b9dbede85a56bd6/6a17f050e8fbce88943a1a30/d29dee100bb8a17bb623acd745773a5164a1df4f-1600x1014.png" alt="" /><h2>実験による改善の測定</h2><p>AI エージェントのコンテキストでは、実験とは、明確に定義された次元 (有用性、正確性、遅延など) のパフォーマンスを向上させるように設計された、システムに対する構造化されたテスト可能な変更です。目標は、「この変更をマージした場合、それが真の改善であり、ユーザー エクスペリエンスを低下させないことを保証できますか?」という質問に明確に答えることです。</p><p>私たちが実施するほとんどの実験には、一般的に次のようなものが含まれます。</p><ul><li><p><strong>仮説:</strong>特定の、反証可能な主張。<em>例:</em> 「攻撃検出ツールへのアクセスを追加すると、セキュリティ関連のクエリの正確性が向上します。」</p></li><li><p><strong>成功基準:</strong> 「成功」の意味を定義する明確なしきい値。<em>例:</em> 「セキュリティ データセットの正確性スコアが 5% 向上し、他の部分では低下は見られません。」</p></li><li><p><strong>評価計画:</strong>成功の測定方法 (指標、データセット、比較方法)</p></li></ul><p>成功した実験は体系的な調査プロセスです。小さなプロンプトの調整から大規模なアーキテクチャの変更まで、すべての変更は次の 7 つの手順に従い、結果が有意義かつ実用的なものになるようにします。</p><ul><li><p>手順1：問題を特定する</p></li><li><p>ステップ2: 指標を定義する</p></li><li><p>ステップ3：明確な仮説を立てる</p></li><li><p>ステップ4: 評価データセットの準備</p></li><li><p>ステップ5: 実験を実行する</p></li><li><p>ステップ6: 結果の分析と反復</p></li><li><p>ステップ7：決定を下し、文書化する</p></li></ul><p>これらのステップの例を<em>図 1</em>に示します。次のサブセクションでは各ステップについて説明します。各ステップの技術的な詳細については、今後のドキュメントで詳しく説明します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06bfe2f0e4205a18/6a17f052faa91358eb93c968/3a9f5a3e92dd4922a795a19104c6e4ad8c98958d-2400x1352.png" alt="" /><h2>実際の Elastic の例を使ったステップバイステップのウォークスルー</h2><h3>手順1：問題を特定する</h3><p><em>この変更が解決しようとしている問題は正確には何でしょうか?</em></p><p>攻撃検出の例: 概要が不完全な場合や、無害なアクティビティが誤って攻撃としてフラグ付けされる (誤検知) 場合があります。</p><p>Elastic AI エージェントの例: 特に分析クエリの場合、エージェントのツール選択は最適ではなく一貫性がなく、間違ったツールが選択されてしまうことがよくあります。これにより、トークンのコストとレイテンシが増加します。</p><h3>ステップ2: 指標を定義する</h3><p><em>問題を測定可能にして、変化を現在の状態と比較できるようにします。</em></p><p>一般的な指標には、<a href="https://developers.google.com/machine-learning/crash-course/classification/accuracy-precision-recall">精度と再現率</a>、<a href="https://en.wikipedia.org/wiki/Semantic_similarity">意味的類似性</a>、事実性などがあります。ユースケースに応じて、一致するアラート ID や正しく取得された URL などのメトリックを計算するためにコード チェックを使用したり、より自由形式の回答を得るために LLM-as-judge などの手法を使用したりします。</p><p>以下は、実験で使用されたメトリックの例です (<em>網羅的ではありません</em>)。</p><p><strong>攻撃の検出</strong></p><p>メトリック</p><p>説明</p><p>精度と再現率</p><p>実際の出力と予想される出力の間でアラート ID を一致させて、検出精度を測定します。</p><p>類似性</p><p>BERTScore を使用して、応答テキストの意味的類似性を比較します。</p><p>事実性</p><p>重要な IOC (侵害の兆候) は存在しますか?MITRE 戦術 (攻撃の業界分類) は正しく反映されていますか?</p><p>攻撃チェーンの一貫性</p><p>発見された数を比較して、攻撃の過剰報告または過少報告がないか確認します。</p><p><strong>エラスティックAIエージェント</strong></p><p>メトリック</p><p>説明</p><p>精度と再現率</p><p>ユーザーのクエリに回答するためにエージェントによって取得されたドキュメント/情報と、クエリに回答するために必要な実際の情報またはドキュメントを照合して、情報取得の精度を測定します。</p><p>事実性</p><p>ユーザーのクエリに回答するために必要な主要な事実は存在しますか?事実は手続き上のクエリに対して正しい順序になっていますか?</p><p>回答の関連性</p><p>応答には、ユーザーのクエリとは関連がない、または周辺的な情報が含まれていますか?</p><p>応答の完全性</p><p>応答はユーザークエリのすべての部分に答えていますか?応答にはグラウンドトゥルースに存在するすべての情報が含まれていますか?</p><p>ES|QL検証</p><p>生成された ES|QL は構文的に正しいですか?機能的にはグラウンドトゥルース ES|QL と同一ですか?</p><h3>ステップ3：明確な仮説を立てる</h3><p><em>上記で定義した問題と指標を使用して、明確な成功基準を確立します。</em></p><p>Elastic AI エージェントの例:</p><ol><li><p><strong>relevance_search および nl_search ツールの説明に変更を加え、それぞれの機能と使用例を明確に定義します</strong>。</p></li><li><p><strong>ツールの呼び出し精度が</strong> <strong>25% 向上 する</strong> と予測しています。</p></li><li><p>他の指標に悪影響が及ばないことを保証し、これが純粋にプラスであることを確認します。<strong>事実性と完全性</strong>。</p></li><li><p><strong>正確なツールの説明により、エージェントがさまざまなクエリタイプに最も適した検索ツールをより正確に選択して適用できるようになり、誤った適用が減り、全体的な検索の有効性が向上するため、この方法が効果的であると考えています</strong>。</p></li></ol><h3>ステップ4: 評価データセットの準備</h3><p><em>システムのパフォーマンスを測定するために、現実世界のシナリオをキャプチャしたデータセットを使用します。</em></p><p>実施する評価の種類に応じて、LLMに供給される生データ（例：攻撃検出のための攻撃シナリオと予想される出力。アプリケーションがチャットボットの場合、入力はユーザークエリであり、出力は正しいチャットボット応答、取得されるべき正しいリンクなどになります。</p><p>攻撃検出の例:</p><p>10の斬新な攻撃シナリオ</p><p>Oh My Malware のエピソード 8 つ (ohmymalware.com)</p><p>4 つのマルチ攻撃シナリオ (最初の 2 つのカテゴリの攻撃を組み合わせて作成)</p><p>3つの良性のシナリオ</p><p>Elastic AI エージェント評価データセットの例 ( <a href="https://github.com/elastic/kibana/blob/main/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/evals/kb/kb.spec.ts">Kibana データセット リンク</a>):</p><p>オープンソース データセットを使用して KB 内の複数のソースをシミュレートする 14 のインデックス。</p><p>5 つのクエリ タイプ (分析、テキスト検索、ハイブリッドなど)</p><p>7 つのクエリ意図タイプ（手続き型、事実型 - 分類型、調査型など）</p><h3>ステップ5: 実験を実行する</h3><p>評価データセットに対して既存のエージェントと修正バージョンの両方からの応答を生成して実験を実行します。事実性などの指標を計算します (手順 2 を参照)。</p><p>ステップ 2 で必要な指標に基づいて、さまざまな評価を組み合わせます。</p><ul><li><p>ルールベースの評価（例：Python/TypeScriptを使用して.jsonが有効かどうかを確認します)</p></li><li><p>LLM が裁判官となる（回答が原文と事実上一致しているかどうかを別の LLM に尋ねる）</p></li><li><p>ニュアンス品質チェックのための人間によるレビュー</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt17ec63af0850d8dd/6a17f054505ac3e508ad8c1e/8648e75818d3291f0ac66f069438a500d42b8225-1600x1099.png" alt="これは、当社の内部フレームワークによって生成された評価結果の例です。さまざまなデータセットにわたって実施された実験からのさまざまなメトリックを示します。" /><h3>ステップ6: 結果の分析と反復</h3><p>指標が得られたので、結果を分析します。<u><em>結果がステップ 3 で定義された成功基準を満たしている場合でも、変更を本番環境にマージする前に人間によるレビューが行われます</em></u>。結果が基準を満たしていない場合は、問題を反復して修正してから、新しい変更に対して評価を実行します。</p><p>マージする前に、最適な変更を見つけるために数回の反復が必要になると予想されます。コミットをプッシュする前にローカル ソフトウェア テストを実行するのと同様に、オフライン評価はローカルの変更または複数の提案された変更で実行できます。分析を効率化するために、実験結果、複合スコア、視覚化の保存を自動化すると便利です。</p><h3>ステップ7：決定を下し、文書化する</h3><p>意思決定フレームワークと受け入れ基準に基づいて、変更のマージを決定し、実験を文書化します。意思決定は多面的であり、他のデータセットでの回帰シナリオの確認や、提案された変更の費用対効果の検討など、評価データセット以外の要素を考慮する場合があります。</p><p>例: いくつかの反復をテストして比較した後、最高スコアの変更を選択し、製品マネージャーやその他の関連する関係者に送信して承認を得ます。意思決定を支援するために、前の手順の結果を添付します。攻撃検出に関するその他の例については、 <a href="https://www.elastic.co/blog/elastic-security-generative-ai-features">「Elastic Security の生成 AI 機能の舞台裏」を</a>ご覧ください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt62a466f3a0da114a/6a17f056faa91342c393c96c/74c80b8f34dce8ddd20873ecb2f553873587ed35-1600x618.png" alt="" /><h2>まとめ</h2><p>このブログでは、実験ワークフローのエンドツーエンドのプロセスについて説明し、エージェントシステムの変更を Elastic ユーザーにリリースする前に評価およびテストする方法を説明しました。また、Elastic でのエージェントベースのワークフローの改善例もいくつか紹介しました。今後のブログ投稿では、適切なデータセットを作成する方法、信頼性の高いメトリックを設計する方法、複数のメトリックが関係する場合に意思決定を行う方法など、さまざまな手順の詳細を詳しく説明します。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-evaluation-elastic</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-evaluation-elastic</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Susan Chang,Abhimanyu Anand]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte578b636637be6b1/6a17f057e8fbcebe9e3a1a36/ef3922076713872163e1aab47735361513b2c9ee-2400x1352.heif" length="0" type="image/*"/>
    <pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[A2Aプロトコルを介してElastic AgentsをGemini Enterpriseに接続する]]></title>
    <description><![CDATA[Agent Builder を使用して、A2A プロトコルを使用してカスタム Elastic Agent を Gemini Enterprise などの外部サービスに公開する方法を学習します。]]></description>
    <content:encoded><![CDATA[<p><strong>Elastic Agent Builder は、</strong> Elasticsearch で直接データ駆動型の AI エージェントを作成するための機能セットです。この<a href="https://www.elastic.co/search-labs/blog/series/context-aware-ai-agentic-workflows-with-elastic">シリーズ</a>の以前の投稿では、カスタム エージェントに複雑なタスクを実行するツールを装備し、エージェントの動作をガイドする一連のカスタム指示を提供する方法を説明しました。</p><p>しかし、すでに使用しているアプリケーションや生産性ツールでカスタムエージェントを使用したい場合はどうすればよいでしょうか?</p><p>ここで、<strong>エージェント間 (A2A) プロトコルが</strong>登場します。A2A は相互運用性のための<a href="https://github.com/a2aproject/A2A">オープン スタンダード</a>であり、異なるプラットフォームのエージェント間の通信と共同作業を可能にします。そして、これを Elastic Agent Builder に直接組み込みました。</p><p>今日は、構築したカスタム エージェントを他のサービス、具体的には<strong>Gemini Enterprise</strong> (旧称 Agentspace) に公開する方法を紹介します。</p><h2>オープンスタンダードの力：A2Aが重要な理由</h2><p>ブログ記事<a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">「初めての Elastic Agent」</a>では、市場データに安全にアクセスできる<em>Financial Assistant</em>エージェントなどのカスタムエージェントの構築方法を説明しました。しかし、作業を再構築せずに、Gemini Enterprise などの他の環境でその洞察を利用できない場合、その価値は限られます。</p><p>この相互運用性の課題が、エージェント AI の実現を妨げているのです。エージェントはプラットフォーム間で通信するために共通言語を必要としますが、これがまさに A2A プロトコルの役割です。標準の通信レイヤーを提供することで、エージェントと直接対話できるだけでなく、組織全体の専門エージェントが連携して洞察を共有できる未来が開かれます。</p><p>これを実現するために、Elastic Agent Builder は、すべてのエージェントに対して 2 つの標準エンドポイントを通じて A2A プロトコルをネイティブにサポートしています。</p><ol><li><p><strong>エージェント カード エンドポイント (</strong> <strong><code>GET {your-kibana-url}/api/agent_builder/a2a/{agentId}.json</code></strong> <strong>) -</strong>これはカスタム エージェントの名刺として機能します。エージェントに関するメタデータ (名前、説明、機能など) を A2A 互換サービスに提供します。</p></li><li><p><strong>A2A プロトコル エンドポイント (</strong> <strong><code>POST {your-kibana-url}/api/agent_builder/a2a/{agentId}</code></strong> <strong>)</strong> - これは通信チャネルです。他のエージェントはここにリクエストを送信し、エージェントはそれを処理して応答を返します。これらはすべて<a href="https://a2a-protocol.org/latest/specification/">A2A プロトコル仕様</a>に従って行われます。</p></li></ol><h2>A2Aインスペクターでエージェントをテストする</h2><p>エージェントを本番システムに接続する前に、正しく通信していることを確認することをお勧めします。これを行う最も簡単な方法は、A2A 統合のテストとデバッグ専用に設計されたツールである<strong>A2A Inspector を</strong>使用することです。</p><p>インスペクターを実行するのは簡単です。<a href="https://github.com/a2aproject/a2a-inspector">a2a-inspector</a>リポジトリのクローンを作成し、README の指示に従って<a href="https://github.com/a2aproject/a2a-inspector?tab=readme-ov-file#3-run-the-application">アプリケーションを実行でき</a>ます。起動すると、UI はデフォルトで<code>http://localhost:5001/</code>で使用できるようになります。</p><p>A2A インスペクターをエージェントに接続するには、次の 2 つの重要な情報を提供する必要があります。</p><ul><li><p>エージェント カード URL: これはエージェントを説明するエンドポイントです。<a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">前回の投稿の Financial Assistant エージェント</a>の場合、この URL は<code>{your-kibana-url}/api/agent_builder/a2a/financial_assistant.json</code>になります。</p></li><li><p>認証ヘッダー: 認証には標準の API キーを使用します。</p></li></ul><p>インスペクターの UI にこれらの詳細を入力すると、すぐにエージェントに接続してチャットを開始できます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6381135e3fb297df/6a17ef4bec0f898b0c5a66ea/7231c72bf30bed2a854f58658c1eca2843f43bfc-1600x1296.png" alt="A2Aエージェントカードとエージェントインスペクターの設定" /><p>この簡単な検証により、エージェントが正しく構成され、次のステップの準備ができていることが保証されます。</p><h2>ライブ配信しよう！Gemini Enterpriseのカスタムエージェント</h2><p>次は、エキサイティングな部分です。カスタム ファイナンシャル アドバイザー エージェントを Gemini Enterprise (旧 Agentspace) 内で実現します。この統合は<a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-ai-agent">、Google Cloud Marketplace で入手可能な Elastic AI Agent</a>によって実現されています。</p><p>接続されると、Gemini Enterprise は A2A プロトコルを使用してエージェントと直接通信します。ここで相互運用性の真の威力が発揮されます。ユーザーは使い慣れた環境を離れることなく、カスタム Elasticsearch エージェントから得られる詳細なデータ駆動型の分析情報にアクセスできるようになります。エージェント リストにカスタム Elastic Agent が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f54f0bb15216d8e/6a17ef4d6df73107d90a0fdb/37a39e92ebf3d72c6c8014397cd8e846336173a4-1600x834.png" alt="Google Agentspace リストでカスタム エージェントを表示する" /><p>Gemini Enterprise のユーザーが次のように質問していると想像してください。</p><p><em>「市場のセンチメントが心配です。悪いニュースによって最もリスクが高い顧客は誰でしょうか？</em> 」</p><p>バックグラウンドでは、Gemini Enterprise がこのクエリを A2A プロトコル経由でカスタム Elastic Agent にルーティングします。エージェントは専用のツールを使用してデータを照会し、回答を作成して返送します。エンドユーザーにとって、エクスペリエンスはシームレスです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte130c332ee0648a6/6a17ef4fe9ea874426a9c6bb/e5f126c1a27a51c6e69a767aa87c9f746b62e39c-1600x1044.png" alt="ユーザーがAgentspaceにクエリを尋ねると、そのクエリは舞台裏で何が起こるのか" /><p>そして、ここで終わりではありません!Elasticエージェントで取得した回答は、別の専門エージェントをトリガーする可能性のある次の質問のコンテキストとして使用できるようになりました（例：上場企業へのエクスポージャーを調整するには、投資プラットフォーム エージェントにご相談ください。検索バーを離れることなくすべて行えます。</p><p>A2A を搭載した Gemini Enterprise に Elastic エージェントをデプロイすると、ユーザーがデータやツールにコンテキスト内でアクセスできる単一の UI が提供されるため、AI、検索、エンタープライズ システム間の摩擦をなくし、アクセス、オーケストレーション、ワークフローを統合できます。ユーザーにとって、これはツールの切り替えが減り、より直感的で有能な AI アシスタントが利用できるようになることを意味します。組織にとって、これは一貫したガバナンス、スケーラビリティ、相互運用性が組み込まれていることを意味します。</p><h2>あなたの番です</h2><p>これで、Elastic Agent をどこからでも利用できるようにするツールが手に入りました。オープン A2A プロトコルを活用することで、カスタムのデータ対応エージェントの範囲を拡大できます。</p><p>この投稿では、重要な手順について説明しました。</p><ul><li><p>A2A エージェント カードとプロトコル エンドポイントを介してエージェントを公開します。</p></li><li><p>A2A Inspector を使用して接続をテストします。</p></li><li><p>エージェントを Google の Gemini Enterprise などの外部サービスにライブで統合します。</p></li></ul><p>エージェントを分離する必要がなくなりました。皆さんが作り上げる、強力で相互接続されたシステムを見るのが待ちきれません。楽しい建築を！</p><p>始める最も簡単な方法は、 <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud?pli=1">Google Cloud Marketplace</a>で Elastic Cloud の無料トライアルを利用することです。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Valerio Arvizzigno,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63d7675adc5bc211/6a17ef51ddf97d38e8910bdf/5be8a425fab55dca2f9717d2e50812b0450fa625-1440x840.png" length="0" type="image/png"/>
    <pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch向けAgentic AIツールの改善実験]]></title>
    <description><![CDATA[スケーラブルな RAG 最適化のために線形リトリーバー、ハイブリッド検索、および semantic_text を組み合わせることで、反復的な実験を通じて Elasticsearch の AI エージェント ワークフローをどのように改善したかを学びます。]]></description>
    <content:encoded><![CDATA[<p>最近の他社と同様に、Elastic ではチャット、エージェント、RAG に全力を注いでいます。検索部門では最近、エージェント ビルダーとツール レジストリに取り組んでおり、その目的は、Elasticsearch 内のデータとの「チャット」を簡単に行えるようにすることです。</p><p>この取り組みの「全体像」について詳しくは、<a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">ブログ「Elasticsearch を使用した AI エージェントワークフローの構築」</a>をお読みください。より実践的な入門書として<a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">、「初めての Elastic エージェント: 単一のクエリから AI を活用したチャットまで」もご覧ください</a>。</p><p>ただし、このブログでは、チャットを開始したときに最初に起こることの 1 つに焦点を絞り、最近行った改善点のいくつかについて説明します。</p><h2>ここで何が起こっているのですか?</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1331b1043612efe3/6a17f115505ac3dc41ad8c3c/25a24055a166d7d6ba81d80aa35cb97163662e23-1600x443.png" alt="" /><p>Elasticsearch データとチャットする場合、デフォルトの AI エージェントが次の標準フローを実行します。</p><ol><li><p>プロンプトを検査します。</p></li><li><p>どのインデックスにそのプロンプトの回答が含まれている可能性があるかを特定します。</p></li><li><p>プロンプトに基づいて、そのインデックスのクエリを生成します。</p></li><li><p>そのクエリでそのインデックスを検索します。</p></li><li><p>結果を統合します。</p></li><li><p>結果はプロンプトに対応できますか?はいの場合は応答してください。そうでない場合は、別の方法を試しながら繰り返します。</p></li></ol><p>これはあまり目新しいものではないはずです。これは単に Retrieval Augmented Generation (RAG) です。そして当然のことですが、応答の質は最初の検索結果の関連性に大きく左右されます。そのため、応答品質の向上に取り組む中で、ステップ 3 で生成してステップ 4 で実行するクエリに細心の注意を払ってきました。そして、私たちは興味深いパターンに気づきました。</p><p>多くの場合、最初の応答が「悪い」場合、それは実行したクエリが悪かったからではありません。クエリを実行するために<em>間違ったインデックスを選択した</em>ためです。通常、ステップ 3 と 4 は問題ではありません。問題はステップ 2 です。</p><h2>私たちは何をしていたのでしょうか?</h2><p>当初の実装はシンプルでした。私たちは、 <code>_cat/indices</code>を効果的に実行して利用可能なすべてのインデックスをリストし、これらのインデックスのうちどれがユーザーのメッセージ/質問/プロンプトに最も一致するかを LLM に識別させるツール (index_explorer と呼ばれる) を構築しました。この<a href="https://github.com/elastic/kibana/blob/0cc78184957fcd12110dabae50353392ea937508/x-pack/platform/packages/shared/onechat/onechat-genai-utils/tools/index_explorer.ts#L98-L113">オリジナルの実装はここで</a>見ることができます。</p>You are an AI assistant for the Elasticsearch company.
based on a natural language query from the user, your task is to select up to ${limit} most relevant indices from a list of indices.

*The natural language query is:* ${nlQuery}

*List of indices:*
${indices.map((index) =&gt; `- ${index.index}`).join('\n')}

Based on those information, please return most relevant indices with your reasoning.
Remember, you should select at maximum ${limit} indices.<p>これはどれくらいうまく機能しましたか?よく分かりませんでした！うまく機能して<em>いない</em>明確な例はありましたが、私たちにとっての本当の最初の課題は、現状を定量化することでした。</p><h2>ベースラインの確立</h2><h3>それはデータから始まる</h3><p>私たちが必要としていたのは、ユーザーのプロンプトと既存のインデックス セットに基づいて適切なインデックスを選択するツールの有効性を測定するためのゴールデン データ セットでした。そして、手元にそのようなデータセットがなかったので、それを生成しました。</p><p>謝辞: これは「ベスト プラクティス」ではないことは承知しています。しかし、時には、自転車を捨てるよりも前進する方が良いこともあります。<a href="https://www.elastic.co/about/our-source-code#progress-perfection">進歩、シンプルな完璧さ</a>。</p><p><a href="https://gist.github.com/seanstory/a08db2e149897da656db3a1ca72e17ac">このプロンプト</a>を使用して、いくつかの異なるドメインのシードのインデックスを生成しました。次に、生成されたドメインごとに、<a href="https://gist.github.com/seanstory/a280a85d067e61bfeb5911bf2654e6e2">このプロンプト</a>を使用してさらにいくつかのインデックスを生成しました (ここでの目標は、ハードネガティブと分類が難しい例を使用して LLM に混乱を引き起こすことです)。次に、生成された各インデックスとその説明を手動で編集しました。最後に、<a href="https://gist.github.com/seanstory/44291b666c05a383136f6e36bb9106fa">このプロンプト</a>を使用してテストクエリを生成しました。次のようなサンプルデータが得られました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1bd9cd78154195e3/6a17f117dbb4fff7b5fb57d2/9d96d87e286eddbc012402b1ecccd57419a99253-1600x782.png" alt="" /><p>そして次のようなテストケース:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltadf30a0aeafd56ef/6a17f1192f4a5c160ffa89eb/4c2e9ad941d98d7e66033bbc08c9b8060ec19097-1600x797.png" alt="" /><h3>テストハーネスの作成</h3><p>ここからのプロセスは非常に簡単でした。次の機能を備えたツールをスクリプト化します。</p><ol><li><p>ターゲット Elasticsearch クラスターを使用してクリーンな状態を確立します。</p></li><li><p>ターゲット データセットで定義されているすべてのインデックスを作成します。</p></li><li><p>各テスト シナリオに対して、 i <code>ndex_explorer</code>ツールを実行します (便利なことに、<a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-post-agent-builder-tools-execute">実行ツール API が</a>あります)。</p></li><li><p>結果のインデックスを予想インデックスと比較し、結果を取得します。</p></li><li><p>すべてのテストシナリオを終了したら、結果を表にまとめます。</p></li></ol><h3>調査によると…</h3><p>当初の結果は予想通り平凡なものでした。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73367741359e258d/6a17f11a505ac39749ad8c40/9c10679bcd6291edfa2a9ba42e7dd922aa483f0b-1216x806.png" alt="" /><p>全体として、正しいインデックスを識別する精度は 77.14% です。これは、すべてのインデックスに意味的に意味のある適切な名前が付けられている「最良のケース」のシナリオでした。`PUT test2/_doc/foo {...} ` を実行したことがある人なら、インデックスの名前が必ずしも意味のあるものではないことはご存じでしょう。</p><p>つまり、ベースラインがあり、改善の余地が十分にあることがわかります。さあ、科学の時間です！🧪</p><h2>実験</h2><h3>仮説1: マッピングは役立つ</h3><p>ここでの目標は、元のプロンプトに関連するデータが含まれるインデックスを識別することです。インデックスに含まれるデータを最もよく表す部分は、インデックスの<em>マッピング</em>です。インデックスの内容のサンプルを取得しなくても、インデックスに double 型の価格フィールドがあることがわかれば、そのデータは販売されるものを表していることがわかります。テキストタイプの著者フィールドは、何らかの非構造化言語データを意味します。これら 2 つを組み合わせると、データが書籍、物語、詩であることを意味する可能性があります。インデックスのプロパティを知るだけで、意味上の手がかりを数多く得ることができます。そこでローカルブランチで`.index_explorer`を調整しましたインデックスの完全なマッピング (およびその名前) を LLM に送信して決定を下すツール。 </p><p>結果（Kibana ログより）:</p>[2025-09-05T11:01:21.552-05:00][ERROR][plugins.onechat] Error: Error calling connector: event: error
data: {"error":{"code":"request_entity_too_large","message":"Received a content too large status code for request from inference entity id [.rainbow-sprinkles-elastic] status [413]","type":"error"}}


    at createInferenceProviderError (errors.ts:90:10)
    at convertUpstreamError (convert_upstream_error.ts:39:38)
    at handle_connector_response.ts:26:33
    at Observable.init [as _subscribe] (/Users/seanstory/Desktop/Dev/kibana/node_modules/rxjs/src/internal/observable/throwError.ts:123:68)...<p>ツールの最初の作成者はこれを予期していました。インデックスのマッピングは情報の宝庫ですが、非常に冗長な JSON ブロックでもあります。そして、多数のインデックス (評価データセットでは 20 個が定義されています) を比較する現実的なシナリオでは、これらの JSON BLOB が加算されます。したがって、LLM に、すべてのオプションのインデックス名だけでなく、それぞれの完全なマッピングほどではなく、決定のためのより多くのコンテキストを提供したいと考えています。</p><h3>仮説2: 妥協案としての「フラット化された」マッピング（フィールドリスト）</h3><p>私たちは、インデックス作成者が意味的に意味のあるインデックス名を使用するという前提から始めました。その仮定をフィールド名にも拡張するとどうなるでしょうか?前回の実験は、JSON のマッピングに大量の煩わしいメタデータと定型句が含まれているため失敗しました。</p>     "description_text": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          },
          "copy_to": [
            "description_semantic"
          ]
        },<p>たとえば、上記のブロックは 236 文字で、Elasticsearch マッピング内の 1 つのフィールドのみを定義します。一方、文字列「description_text」は 16 文字だけです。これは文字数が約 15 倍に増加していることを意味しますが、そのフィールドが利用可能なデータについて何を意味するかを説明する意味的な改善は見られません。すべてのインデックスのマッピングをフェッチしたが、それを LLM に送信する前に、フィールド名のリストだけに「フラット化」するとどうなるでしょうか?</p><p>試してみました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5eda7a79493ee81/6a17f11c9da390327fe46590/112c2f447c11f154b5082725cd49b51d0a3c8a65-1214x804.png" alt="" /><p>これは素晴らしいですね！全面的に改善されました。しかし、もっと良い方法はないでしょうか?</p><h3>仮説3: マッピング_meta内の説明</h3><p>追加のコンテキストのないフィールド名だけでこれほど大きな変化が生じたのであれば、実質的なコンテキストを追加すればさらに良くなると思われます。すべてのインデックスに説明を添付することが必ずしも慣例ではありませんが、マッピングの _meta オブジェクトにあらゆる種類のインデックス レベルのメタデータを追加することは可能です。生成されたインデックスに戻り、データセット内のすべてのインデックスに説明を追加しました。説明が極端に長くない限り、完全なマッピングよりも少ないトークンが使用され、インデックスに含まれるデータに関するはるかに優れた洞察が提供されるはずです。私たちの実験はこの仮説を検証しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt61b85cf40e0e6357/6a17f11dfbc5f82809491bbe/32d2692ad4479d0e52d8ee723dcc5710a6ec90f3-1208x806.png" alt="" /><p>若干の改善があり、現在では全体的に 90% を超える精度を実現しています。</p><h3>仮説4：全体は部分の合計よりも大きい</h3><p>フィールド名により結果が向上しました。説明により結果が向上しました。したがって、説明とフィールド名の<em>両方</em>を利用すると、さらに良い結果が得られるはずです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte6297c6aaf7db802/6a17f11e14d90c1bd779b6e6/114cbb408ff16b136251d2265416bd5270380fe5-1208x794.png" alt="" /><p>データは「いいえ」（前回の実験から変化なし）を示しました。ここでの主な理論は、説明はそもそもインデックス フィールド/マッピングから生成されたため、これら 2 つのコンテキストの間には、組み合わせたときに何か「新しい」ものを追加するのに十分な情報がないというものでした。さらに、20 個のテスト インデックスに送信するペイロードもかなり大きくなっています。これまで私たちが辿ってきた考え方はスケーラブルではありません。実際、これまでの私たちの実験は、数百または数千のインデックスから選択できる Elasticsearch クラスターでは機能しないと考えられる十分な理由があります。インデックスの合計数が増加するにつれて、LLM に送信されるメッセージ サイズが直線的に増加するアプローチは、おそらく一般化可能な戦略にはなりません。</p><p>私たちに本当に必要なのは、多数の候補から最も関連性の高い選択肢だけを絞り込むのに役立つアプローチです...</p><p>ここで問題となるのは検索の問題です。</p><h3>仮説5：意味検索による選択</h3><p>インデックスの名前に意味がある場合は、ベクトルとして保存し、意味的に検索することができます。</p><p>インデックスのフィールド名に意味がある場合は、それらをベクトルとして保存し、意味的に検索することができます。</p><p>インデックスに意味を持つ記述がある場合は、それもベクトルとして保存し、意味的に検索することができます。</p><p>現在、Elasticsearch インデックスではこの情報を検索可能にしていません (検索可能にすべきかもしれませんが) が、そのギャップを回避できる<a href="https://github.com/elastic/connectors/pull/3638">ものをハックする</a>のは非常に簡単でした。Elastic のコネクタ フレームワークを使用して、クラスター内のすべてのインデックスのドキュメントを出力するコネクタを構築しました。出力ドキュメントは次のようになります。</p> doc = {
                "_id": index_name,
                "index_name": index_name,
			"meta_description”: description,
"field_descriptions" = field_descriptions,
                "mapping": json.dumps(mapping),  
                "source_cluster": self.es_client.configured_host,
            }<p>これらのドキュメントを、次のように手動でマッピングを定義した新しいインデックスに送信しました。</p>{
   "mappings": {
       "properties": {
           "semantic_content": {
               "type": "semantic_text"
           },
           "index_name": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "mapping": {
               "type": "keyword",
               "copy_to": "semantic_content"
           },
           "source_cluster": {
               "type": "keyword"
           },
           "meta_description": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "field_descriptions": {
               "type": "text",
               "copy_to": "semantic_content"
           }
       }
   }
}<p>これにより、単一の semantic_content フィールドが作成され、セマンティックな意味を持つ他のすべてのフィールドがチャンク化され、インデックスが作成されます。このインデックスの検索は、次のようにするだけで簡単になります。</p>GET indexed-indices/_search
{
 "query": {
   "semantic": {
     "field": "semantic_content",
     "query": "$query"
   }
 }
}<p>修正された<code>index_explorer</code>ツールは、LLM へのリクエストを行う必要がなくなり、代わりに指定されたクエリに対して単一の埋め込みをリクエストして効率的なベクトル検索操作を実行できるため、<em>大幅に</em>高速化されました。トップヒットを選択したインデックスとして取得すると、次の結果が得られました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc27c302e6bef0b23/6a17f120577262d2f21bccdc/06ef5d78040d064d3444793f636d527d9e19a869-1214x800.png" alt="" /><p>このアプローチはスケーラブルです。このアプローチは効率的です。しかし、このアプローチはベースラインよりわずかに優れているだけです。しかし、これは驚くことではありません。ここでの検索アプローチは信じられないほど単純です。ニュアンスがない。インデックスの名前と説明は、インデックスに含まれる任意のフィールド名よりも重視されるべきであるという認識がありません。正確な語彙の一致を同義語の一致よりも重視するアフォーダンスはありません。ただし、非常に微妙なニュアンスのあるクエリを構築するには、手元のデータについて多くのことを想定する必要があります。これまで、インデックス名とフィールド名には意味があるという大きな仮定をすでに立ててきましたが、さらに一歩進んで、インデックス名とフィールド名が<em>どの程度の</em>意味を持ち、互いにどのように関連しているかを仮定する必要があります。そうしないと、最上位の結果として最適な一致を確実に特定することはできないかもしれませんが、最上位 N 個の結果のどこかに最上位の一致があると言える可能性が高くなります。意味情報をそれが存在するコンテキスト内で消費し、意味的に異なる方法で自身を表現する別のエンティティと比較し、それらを判断できるものが必要です。LLM のようなものです。</p><h3>仮説6: 候補セットの削減</h3><p>他にも簡単に触れる実験はいくつかありましたが、重要な突破口となったのは、純粋にセマンティック検索から最適な一致を選択したいという欲求を捨て、代わりにセマンティック検索をフィルターとして活用して、LLM の検討対象から無関係なインデックスを除外したことです。<a href="https://gist.github.com/seanstory/d704443120e20f6c844db10e30066860">検索</a>では、リニア リトリーバー、RRF を使用したハイブリッド検索、 <code>semantic_text</code>を組み合わせて、一致する上位 5 つのインデックスに結果を制限しました。</p><p>次に、一致ごとに、インデックスの名前、説明、フィールド名を LLM のメッセージに追加しました。結果は素晴らしかったです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7ac4cb8f7153fdf9/6a17f121af47b66d1dcde082/8fcabd78f591f90d6bc7c0e087d31317e4eef791-1206x804.png" alt="" /><p>これまでのどの実験よりも最高の精度です!また、このアプローチではインデックスの合計数に比例してメッセージ サイズが増加しないため、このアプローチははるかにスケーラブルです。</p><h2>成果</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8d66130d9fae6bea/6a17f123ddf97d7527910c19/04d630797213dbb8bf567da41d1cdd5c7b4586c9-1600x521.png" alt="" /><p>最初の明らかな結果は、ベースラインを改善<em>できる</em>ということでした。振り返ってみるとこれは明らかなようですが、実験が始まる前に、 <code>index_explorer</code>ツールを完全に放棄して、ユーザーからの明示的な構成に依存して検索空間を制限すべきかどうかについて真剣な議論がありました。これはまだ実行可能かつ有効なオプションですが、この調査では、そのようなユーザー入力が利用できない場合にインデックス選択を自動化するための有望な道筋があることが示されています。</p><p>次の明らかな結果は、問題に対して説明文字をさらに追加するだけでは、効果は減少するということです。この調査を行う前、Elasticsearch の<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-field-meta">フィールドレベルのメタデータ</a>保存機能を拡張することに投資すべきかどうかについて議論していました。現在、これらの<code>meta</code>値は 50 文字に制限されており、フィールドの意味を理解できるようにするにはこの値を増やす必要があると想定されていました。これは明らかに事実ではなく、LLM はフィールド名だけでかなりうまく機能しているようです。これについては後でさらに調査するかもしれませんが、もはや緊急の問題ではないように思われます。</p><p>逆に言えば、これは「検索可能な」インデックス メタデータを持つことの重要性を明確に示しています。これらの実験のために、インデックスのインデックスをハッキングしました。しかし、これを Elasticsearch に直接組み込むか、管理するための API を構築するか、少なくとも規則を確立することを調査することはできます。私たちは選択肢を検討し、社内で議論する予定ですので、お楽しみに。</p><p>最後に、この取り組みにより、時間をかけて実験し、データに基づいた意思決定を行うことの価値が確認されました。実際、これにより、Agent Builder 製品には強力な製品内評価機能が必要になることが再確認されました。インデックスを選択するツール専用のテスト ハーネス全体を構築する必要がある場合、お客様は反復的な調整を行う際にカスタム ツールを定性的に評価する方法が絶対に必要になります。</p><p>私たちが何を構築するのか楽しみにしています。皆さんも楽しみにしていただければ幸いです。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[Elastic内部の実情]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <dc:creator><![CDATA[Sean Story]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68d11a4c7fd11d4c/6a17f1257b54f9b6598b39d4/42903c869e034674b30bb36013345aaa97f6608b-1184x864.png" length="0" type="image/png"/>
    <pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[初めてのElastic Agent: 単一のクエリからAIを活用したチャットまで]]></title>
    <description><![CDATA[Elastic の AI エージェント ビルダーを使用して特殊な AI エージェントを作成する方法を学びます。このブログでは、金融 AI エージェントを構築します。]]></description>
    <content:encoded><![CDATA[<p>Elastic の新しい<a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Agent Builder を</a>使用すると、特定のビジネスドメインの専門家として機能する特殊な AI エージェントを作成できます。この機能により、単純なダッシュボードや検索バーを超えて、データを受動的なリソースから能動的な会話のパートナーへと変換できます。</p><p>顧客との会議の前に、状況を把握しておく必要がある財務マネージャーを想像してください。ニュース フィードを手動で調べたり、ポートフォリオ ダッシュボードを相互参照したりする代わりに、カスタム構築されたエージェントに直接質問するだけで済みます。これは「チャットファースト」アプローチの利点です。マネージャーはデータに直接、会話形式でアクセスし、「ACME Corp の最新ニュースは何ですか。また、それがクライアントの保有株にどのような影響を与えますか」などと質問します。数秒以内に専門家による総合的な回答が得られます。</p><p>私たちは現在、金融の専門家を構築していますが、そのアプリケーションはデータと同じくらい多様です。同じ力で、脅威を探すサイバーセキュリティアナリスト、機能停止を診断するサイト信頼性エンジニア、キャンペーンを最適化するマーケティングマネージャーを生み出すこともできます。分野に関係なく、中核となる使命は同じです。データを、チャットできる専門家に変換することです。</p><h2>ステップ0: データセット</h2><p>本日のデータセットは、金融口座、資産状況、ニュース、財務レポートで構成される合成的な金融ベースのデータセットです。これは合成ではありますが、実際の金融データセットの簡略化されたバージョンを複製したものです。</p><p><code>financial_accounts</code>: リスクプロファイル付き顧客ポートフォリオ</p><p><code>financial_holdings</code>: 購入履歴のある株式/ETF/債券のポジション</p><p><code>financial_asset_details</code>: 株式/ETF/債券の詳細</p><p><code>financial_news</code>: 感情分析によるAI生成の市場記事</p><p><code>financial_reports</code>: 企業収益とアナリストのコメント</p><p><a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">ここに</a>ある付属のノートブックに従って、このデータセットを自分でロードできます。</p><h2>ステップ1: 基盤 - ES|QLとしてのビジネスロジック</h2><p>すべての AI スキルは、確かなロジックから始まります。Financial Manager エージェントには、「市場のセンチメントが心配です。」というよくある質問に回答する方法を教える必要があります。悪いニュースによって最もリスクにさらされている顧客は誰なのか教えていただけますか？」この質問は単純な検索の範囲を超えています。市場の感情と顧客のポートフォリオを相関させる必要があります。</p><p>否定的な記事で言及されている資産を見つけ、それらの資産を保有しているすべての顧客を特定し、そのエクスポージャーの現在の市場価値を計算し、結果をランク付けして最も高いリスクを優先する必要があります。この複雑な複数結合の分析は、当社の高度な ES|QL ツールに最適です。</p><p>使用する完全なクエリは次のとおりです。見た目は印象的ですが、コンセプトは単純です。</p><h2>分解：接合部とガードレール</h2><p>このクエリでは、エージェント ビルダーを構成する 2 つの重要な概念が関係しています。</p><h3>1.ルックアップ結合</h3><p>長年にわたり、Elasticsearch で最も要望が多かった機能の 1 つは、共通キーに基づいて異なるインデックスのデータを結合する機能でした。ES|QL では、 <code>LOOKUP JOIN</code>でそれが可能になりました。</p><p>新しいクエリでは、3 つの<code>LOOKUP JOIN</code>のチェーンを実行します。最初に否定的なニュースを資産の詳細に関連付け、次にそれらの資産をクライアントの保有資産にリンクし、最後にクライアントのアカウント情報に結合します。これにより、単一の効率的なクエリで 4 つの異なるインデックスから非常に豊富な結果が作成されます。つまり、すべてのデータを事前に 1 つの巨大なインデックスに非正規化する必要がなく、異なるデータセットを組み合わせて単一の洞察に満ちた回答を作成できるということです。</p><h3>2. LLMガードレールとしてのパラメータ</h3><p>クエリでは<code>?time_duration</code>が使用されていることがわかります。これは単なる変数ではなく、AI のガードレールです。大規模言語モデル (LLM) はクエリの生成に優れていますが、データに対して LLM を自由に制御させると、非効率的なクエリや間違ったクエリが発生する可能性があります。</p><p>パラメータ化されたクエリを作成することで、LLM は、人間の専門家がすでに定義したテスト済みの効率的で正しいビジネス ロジック内で動作するように強制されます。これは、開発者が長年にわたり検索テンプレートを使用して、クエリ機能をアプリケーションに安全に公開してきた方法に似ています。エージェントは「今週」のようなユーザーのリクエストを解釈して<code>time_duration</code>パラメータを埋めることができますが、回答を取得するにはクエリ構造を使用する必要があります。これにより、柔軟性と制御の完璧なバランスが実現します。</p><p>最終的に、このクエリにより、データを理解している専門家は自分の知識をツールにカプセル化できるようになります。他の人や AI エージェントは、そのツールを使用して、基礎となる複雑さについて何も知らなくても、単一のパラメータを提供するだけで相関結果を得ることができます。</p><h2>ステップ2：スキル - クエリを再利用可能なツールに変える</h2><p>ES|QL クエリは、<strong>ツール</strong>として登録されるまでは単なるテキストです。エージェント ビルダーでは、ツールは単なる保存されたクエリではなく、AI エージェントが理解して使用することを選択できる「スキル」です。その魔法は、私たちが提供する<strong>自然言語による説明</strong>にあります。この説明は、ユーザーの質問と基礎となるクエリ ロジックを結び付ける橋渡しとなります。作成したクエリを登録しましょう。</p><h3>UIパス</h3><p>Kibana でツールを作成するのは簡単なプロセスです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte73e11c1d87593fa/6a17f2134202294dae29f6f2/a29c53a73b99af5972273c51218ea9004a9b0abb-1600x812.png" alt="Kibana でツールを作成する方法。" /><p>1.<strong>エージェント</strong>へ移動</p><ul><li><p><strong>[ツール]</strong>または<strong>[ツールの管理]</strong>をクリックし、 <strong>[新しいツール]</strong>ボタンをクリックします。</p></li></ul><p>2. フォームに以下の詳細を入力します。</p><ul><li><p><strong>ツールID:</strong> <code>find_client_exposure_to_negative_news</code></p></li></ul><p>             私。これはツールの一意のIDです</p><ul><li><p><strong>説明:</strong> 「クライアントのポートフォリオがネガティブなニュースにさらされているかどうかを調べます。」このツールは、最近のニュースやレポートをスキャンして否定的な感情を検出し、関連する資産を識別して、その資産を保有しているすべてのクライアントを見つけます。最も高い潜在的リスクを強調するために、ポジションの現在の市場価値でソートされたリストを返します。</p></li></ul><p>             私。これは、LLM が読んで、このツールが仕事に適しているかどうかを判断します。</p><ul><li><p><strong>ラベル</strong>: <code>retrieval</code>および <code>risk-analysis</code></p></li></ul><p>         ラベルは複数のツールをグループ化するのに役立ちます</p><ul><li><p><strong>設定:</strong>ステップ1の完全なES|QLクエリを貼り付けます</p></li></ul><p>            私。これはエージェントが使用する検索です</p><p>3.<strong>クエリからパラメータを推測するを</strong>クリックします。UI は自動的に<code>?time_duration</code>見つけて以下にリストします。エージェント (および他のユーザー) が目的を理解できるように、それぞれに簡単な説明を追加します。</p><ul><li><p><code>time_duration</code>: ネガティブなニュースを遡って検索する期間。フォーマットは「X時間」です。デフォルトは8760時間です。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7afbb0589c1828ad/6a17f2146864a44e7cb688a9/deb422d97863f78dbe08bfa2e3c708d1f75166ff-1600x938.png" alt="ESQL クエリを使用して、ロジックや必要なパラメータを含むツールを構成します。 " /><p>4. 試してみましょう!</p><ul><li><p>[保存してテスト]をクリックします。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd09afbef6e21a93/6a17f2162f4a5c73b1fa89fd/57e768b88327821e70bd616744822f98fa367362-732x136.png" alt="Kibana の同じ &amp; test ボタン。" /><ul><li><p>クエリが期待どおりに動作していることを確認できる新しいフライアウトが表示されます。</p></li></ul><p>             私。<code>time_duration</code>に希望の範囲を入力します。ここでは「8760時間」を使用します。</p><ul><li><p>「送信」をクリックすると、すべてがうまくいけば JSON レスポンスが表示されます。期待どおりに動作することを確認するには、下にスクロールして<code>values</code>オブジェクトを確認します。ここで、実際に一致するドキュメントが返されます。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bdc3f093363f2a/6a17f217be60861c9c00488a/7e0c5171a4f7ffdfc1830f1a05a9acb987870b75-1600x722.png" alt="送信をクリックした後に表示される JSON 応答。" /><p>5. 右上の「X」をクリックして、テストのフライアウトを閉じます。新しいツールがリストに表示され、エージェントに割り当てる準備が整います。</p><h3>APIパス</h3><p>自動化を好む開発者やツールをプログラムで管理する必要がある開発者は、1 回の API 呼び出しで同じ結果を得ることができます。ツールの定義を含む<code>POST</code>リクエストを<code>/api/agent_builder/tools</code>エンドポイントに送信するだけです。</p>POST kbn://api/agent_builder/tools
{
  "id": "find_client_exposure_to_negative_news",
  "type": "esql",
  "description": "Finds client portfolio exposure to negative news. This tool scans recent news and reports for negative sentiment, identifies the associated asset, and finds all clients holding that asset. It returns a list sorted by the current market value of the position to highlight the highest potential risk.",
  "configuration": {
    "query": """
        FROM financial_news, financial_reports METADATA _index
        | WHERE sentiment == "negative"
        | WHERE coalesce(published_date, report_date) &gt;= NOW() - TO_TIMEDURATION(?time_duration)
        | RENAME primary_symbol AS symbol
        | LOOKUP JOIN financial_asset_details ON symbol
        | LOOKUP JOIN financial_holdings ON symbol
        | LOOKUP JOIN financial_accounts ON account_id
        | WHERE account_holder_name IS NOT NULL
        | EVAL position_current_value = quantity * current_price.price
        | RENAME title AS news_title
        | KEEP
            account_holder_name, symbol, asset_name, news_title,
            sentiment, position_current_value, quantity, current_price.price,
            published_date, report_date
        | SORT position_current_value DESC
        | LIMIT 50
      """,
    "params": {
      "time_duration": {
        "type": "keyword",
        "description": """The timeframe to search back for negative news. Format is "X hours" DEFAULT TO 8760 hours """
      }
    }
  },
  "tags": [
    "retrieval",
    "risk-analysis"
  ]
}<h2>ステップ3：頭脳 - カスタムエージェントの作成</h2><p>再利用可能なスキル (ツール) を構築しました。ここで、実際に使用するペルソナである<strong>Agent</strong>を作成する必要があります。エージェントは、LLM、アクセスを許可する特定のツール セット、そして最も重要な、エージェントの構成として機能し、エージェントの性格、ルール、目的を定義する<strong>カスタム インストラクション</strong>セットの組み合わせです。</p><h3>プロンプトの芸術</h3><p>信頼できる専門エージェントを作成する上で最も重要なのはプロンプトです。よく練られた一連の指示こそが、一般的なチャットボットと、集中力のあるプロのアシスタントとの違いです。ここで、ガードレールを設定し、出力を定義し、エージェントにミッションを与えます。</p><p><code>Financial Manager</code>エージェントでは、次のプロンプトを使用します。</p>You are a specialized Data Intelligence Assistant for financial managers, designed to provide precise, data-driven insights from information stored in Elasticsearch.

**Your Core Mission:**
- Respond accurately and concisely to natural language queries from financial managers.
- Provide precise, objective, and actionable information derived solely from the Elasticsearch data at your disposal.
- Summarize key data points and trends based on user requests.

**Reasoning Framework:**
1.  **Understand:** Deconstruct the user's query to understand their core intent.
2.  **Plan:** Formulate a step-by-step plan to answer the question. If you are unsure about the data structure, use the available tools to explore the indices first.
3.  **Execute:** Use the available tools to execute your plan.
4.  **Synthesize:** Combine the information from all tool calls into a single, comprehensive, and easy-to-read answer.

**Key Directives and Constraints:**
- **If a user's request is ambiguous, ask clarifying questions before proceeding.**
- **DO NOT provide financial advice, recommendations, or predictions.** Your role is strictly informational and analytical.
- Stay strictly on topic with financial data queries.
- If you cannot answer a query, state that clearly and offer alternative ways you might help *within your data scope*.
- All numerical values should be formatted appropriately (e.g., currency, percentages).

**Output Format:**
- All responses must be formatted using **Markdown** for clarity.
- When presenting structured data, use Markdown tables, lists, or bolding.

**Start by greeting the financial manager and offering assistance.**<p>このプロンプトがなぜ効果的なのかを分析してみましょう。</p><ul><li><p><strong>洗練されたペルソナを定義します。</strong>最初の行で、エージェントが「専門的なデータ インテリジェンス アシスタント」であることを即座に示し、プロフェッショナルで有能な雰囲気を醸し出します。</p></li><li><p><strong>これは推論フレームワークを提供します。</strong>エージェントに「理解、計画、実行、統合」を指示することで、標準的な操作手順を提供します。これにより、複雑で複数のステップから成る質問を処理する能力が向上します。</p></li><li><p><strong>インタラクティブな対話を促進します。</strong> 「明確な質問をする」という指示により、エージェントはより堅牢になります。曖昧なリクエストに対する誤った想定を最小限に抑え、より正確な回答が得られます。</p></li></ul><h3>UIパス</h3><p>1.<strong>エージェントに移動します。</strong></p><ul><li><p><strong>[ツール]</strong>または<strong>[ツールの管理]</strong>をクリックし、 <strong>[新しいツール]</strong>ボタンをクリックします。</p></li></ul><p>2. 基本的な詳細を入力します。</p><ul><li><p><strong>エージェント ID:</strong> <code>financial_assistant</code> 。</p></li><li><p><strong>手順:</strong>上記のプロンプトをコピーします。</p></li><li><p><strong>ラベル</strong>: <code>Finance</code> 。</p></li><li><p><strong>表示名:</strong> <code>Financial Assistant</code> 。</p></li><li><p><strong>表示の説明:</strong> <code>An assistant for analyzing and understanding your financial data</code> 。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ac12cbd2b689dee/6a17f219dbb4ff262bfb57ef/18ea73f1cae620129c0afa0e7ba9e2a3390224a7-1600x1189.png" alt="財務アシスタントの作成 - エージェント ID フィールドに入力します。" /><p>3. 上部に戻り、 <strong>「ツール」</strong>をクリックします。</p><ul><li><p><code>find_client_exposure_to_negative_news</code>ツールの横にあるボックスにチェックを入れてください。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcd23556e556a76c5/6a17f21baf47b63a9fcde0a0/0c1e4ecbbd51d0dd10c6e861dbe9a9ccddeb35f6-1600x149.png" alt="" /><p>4. <strong>「保存」</strong>をクリックします。</p><h3>APIパス</h3><p><code>/api/agent_builder/agents</code>エンドポイントへの<code>POST</code>リクエストを使用して、まったく同じエージェントを作成できます。リクエスト本体には、ID、名前、説明、完全な指示セット、エージェントが使用を許可されているツールのリストなど、すべて同じ情報が含まれています。</p>POST kbn://api/agent_builder/agents
    {
      "id": "financial_assistant",
      "name": "Financial Assistant",
      "description": "An assistant for analyzing and understanding your financial data",
      "labels": [
        "Finance"
      ],
      "avatar_color": "#16C5C0",
      "avatar_symbol": "💰",
      "configuration": {
        "instructions": """You are a specialized Data Intelligence Assistant for financial managers, designed to provide precise, data-driven insights from information stored in Elasticsearch.

**Your Core Mission:**
- Respond accurately and concisely to natural language queries from financial managers.
- Provide precise, objective, and actionable information derived solely from the Elasticsearch data at your disposal.
- Summarize key data points and trends based on user requests.

**Reasoning Framework:**
1.  **Understand:** Deconstruct the user's query to understand their core intent.
2.  **Plan:** Formulate a step-by-step plan to answer the question. If you are unsure about the data structure, use the available tools to explore the indices first.
3.  **Execute:** Use the available tools to execute your plan.
4.  **Synthesize:** Combine the information from all tool calls into a single, comprehensive, and easy-to-read answer.

**Key Directives and Constraints:**
- **If a user's request is ambiguous, ask clarifying questions before proceeding.**
- **DO NOT provide financial advice, recommendations, or predictions.** Your role is strictly informational and analytical.
- Stay strictly on topic with financial data queries.
- If you cannot answer a query, state that clearly and offer alternative ways you might help *within your data scope*.
- All numerical values should be formatted appropriately (e.g., currency, percentages).

**Output Format:**
- All responses must be formatted using **Markdown** for clarity.
- When presenting structured data, use Markdown tables, lists, or bolding.

**Start by greeting the financial manager and offering assistance.**
""",
        "tools": [
          {
            "tool_ids": [
              "platform.core.search",
              "platform.core.list_indices",
              "platform.core.get_index_mapping",
              "platform.core.get_document_by_id",
              "find_client_exposure_to_negative_news"
            ]
          }
        ]
      }
    }<h2>ステップ4：成果 — 会話をする</h2><p>ビジネス ロジックがツールにカプセル化され、エージェントでそれを使用できる「頭脳」が準備されました。すべてが一つにまとまるのを見る時が来ました。専用のエージェントを使用して、データとのチャットを開始できるようになりました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8826539b16e46f4/6a17f21d505ac35924ad8c5c/5414cb6b7c41365acb0356a8bfe1140751ffd8db-1600x1014.png" alt="財務アシスタントを作成した後、Elastic Agent Builder と会話します。" /><h3>UIパス</h3><ol><li><p>Kibana の<strong>エージェント</strong>に移動します。</p></li><li><p>チャット ウィンドウの右下にあるドロップダウンを使用して、デフォルトの<strong>Elastic AI エージェント</strong>から新しく作成した<strong>Financial Assistant</strong>エージェントに切り替えます。</p></li><li><p>エージェントが当社の専用ツールを使用できるように、次の質問をしてください。</p><ol><li><p><em>市場のセンチメントが心配です。悪いニュースによって最もリスクにさらされている顧客は誰なのか教えていただけますか?</em></p></li></ol></li></ol><p>しばらくすると、エージェントは完全にフォーマットされた完全な回答を返します。LLM の性質上、回答の形式が若干異なる場合がありますが、この実行ではエージェントは次のように返しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta1e163fd7c4416bd/6a17f21f6864a4e35bb688ad/17b4ed43d279f9e53ee9fe3d482d0b2ec359a083-1600x1088.png" alt="ネガティブなニュースによるリスクが最も高いクライアント向けの財務アシスタントとして Elastic Agent Builder によって作成された応答。" /><h3>何が起こったのですか?エージェントの推論</h3><p>エージェントは単に答えを「知っていた」だけではありません。仕事に最適なツールを選択することを中心とした多段階の計画を実行しました。その思考プロセスは次のようになります。</p><ul><li><p><strong>識別された意図:</strong> 「リスク」や「ネガティブなニュース」など、質問のキーワードが<code>find_client_exposure_to_negative_news</code>ツールの説明と一致しました。</p></li><li><p><strong>計画を実行しました:</strong>リクエストから時間枠を抽出し、その専用ツールを<strong>1 回呼び出し</strong>ました。</p></li><li><p><strong>作業を委任:</strong>ツールは連鎖結合、値の計算、並べ替えなど、面倒な作業をすべて実行しました。</p></li><li><p><strong>結果の統合:</strong>最後に、エージェントはプロンプトのルールに従って、ツールからの生データを明確で人間が読める要約にフォーマットしました。</p></li></ul><p>思考を広げて詳細を見れば、推測するだけでは足りません。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93f6075be8495418/6a17f221af47b65eadcde0a4/6a4da9262d3f88c60bfd8f8bf9b67c3b84e961ba-1600x607.png" alt="ファイナンシャルアシスタントが、ネガティブなニュースに最も多く触れた顧客から得た 50 件の文書。" /><h3>APIパス</h3><p>同じ会話をプログラムで開始することもできます。入力した質問を<code>converse</code> API エンドポイントに送信し、 <code>financial_manager</code>の<code>agent_id</code>を必ず指定してください。</p>POST kbn://api/agent_builder/converse
{
  "input": "Show me our largest positions affected by negative news",
  "agent_id": "financial_assistant"
}<h2>開発者向け: APIとの統合</h2><p>Kibana UI はエージェントの構築と管理に素晴らしく直感的なエクスペリエンスを提供しますが、今日見てきたことはすべてプログラムで実現することもできます。Agent Builder は一連の API に基づいて構築されており、この機能を独自のアプリケーション、CI/CD パイプライン、または自動化スクリプトに直接統合できます。</p><p>使用する 3 つのコア エンドポイントは次のとおりです。</p><ul><li><p><strong><code>/api/agent_builder/tools</code></strong>: エージェントが使用できる再利用可能なスキルを作成、一覧表示、管理するためのエンドポイント。</p></li><li><p><strong><code>/api/agent_builder/agents</code></strong>: エージェントのペルソナ（重要な指示やツールの割り当てなど）を定義するためのエンドポイント。</p></li><li><p><strong><code>/api/agent_builder/converse</code></strong>: エージェントと対話し、会話を開始し、回答を得るためのエンドポイント。</p></li></ul><p>これらの API を使用してこのチュートリアルのすべてのステップを実行するための完全な実践的なチュートリアルについては、 こちらの GitHub リポジトリで入手できる付属の<strong> Jupyter Notebook を</strong> <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb"></a>ご覧ください。</p><h2>結論: 構築する番です</h2><p>まず、ES|QL クエリを取得して、それを再利用可能なスキルに変換することから始めました。次に、明確なミッションとルールを与えて、そのスキルを付与した専用の AI エージェントを構築しました。その結果、複雑な質問を理解し、複数段階の分析を実行して、正確でデータに基づいた回答を提供できる洗練されたアシスタントが誕生しました。</p><p>このワークフローは、Elastic の新しい<strong>Agent Builder</strong>の中心です。これは、技術に詳しくないユーザーが UI を通じてエージェントを作成できるほどシンプルでありながら、開発者が API 上にカスタム AI 搭載アプリケーションを構築できるほど微妙なニュアンスも備えた設計になっています。最も重要なのは、定義したエキスパート ロジックに従って、LLM を独自のデータに安全かつ確実に接続し、データとチャットできることです。</p><h2>エージェントを使用してデータとチャットする準備はできていますか?</h2><p>学んだことを定着させる最良の方法は、実際に手を動かしてみることです。今日お話しした内容をすべて<a href="https://www.elastic.co/training/elastic-ai-agents-mcp"><strong>、無料のインタラクティブな実践ワークショップ</strong></a>で試してみてください。専用のサンドボックス環境で、このフロー全体とその他の内容を実行します。</p><p>今後のブログでは、 <code>Financial Assistant</code>エージェントと対話するスタンドアロン アプリケーションの使用方法と、それを可能にする<strong>モデル コンテキスト プロトコル (MCP)</strong>について詳しく説明します。また、別のブログでは、開発中の Agent2Agent (A2A) プロトコルに対する Agent Builder のサポートについて説明します。</p><p>引き続きご注目ください、そして楽しい建築を！</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[Elastic内部の実情]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe5e78eeb775d715/6a17f2230b0bed719ddd369a/ca853555eaa213f10f1db8c0ab0a2bbacee97b88-1456x816.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch を使用した AI エージェントワークフローの構築]]></title>
    <description><![CDATA[Elasticsearch の新しい AI レイヤーである Agent Builder について学習します。Agent Builder は、ハイブリッド検索を使用して、エージェントが推論して行動するために必要なコンテキストを提供し、AI エージェントワークフローを構築するためのフレームワークを提供します。]]></description>
    <content:encoded><![CDATA[<p>Elasticでは、AIアシスタント、高度なRAG、ベクターデータベースの改善により、LLMと会話型インターフェースにコンテキストを提供してきました。最近、AI エージェントの台頭により、関連コンテキストの必要性が高まり、影響力の大きい<strong>AI エージェントには優れた検索が必要である</strong>ことがわかりました。そこで、Elasticsearch のデータを活用する AI エージェントの開発を支援するために設計された新しいネイティブ機能を Elastic Stack に構築しました。私たちは、この取り組みの進捗状況と今後の見通しについて共有したいと思います。</p><h2>エージェントビルダー: データ駆動型 AI エージェント構築の基盤</h2><p>AI エージェントの約束はシンプルです。目標を与えれば、仕事が完了します。しかし、開発者にとって、現実は一連の複雑な課題です。まず、エージェントの優秀さは、環境の認識と、ユーザーの目的を達成するために与えられたツールによって決まります。そして、多様な企業データから適切なコンテキストを提供することは大きな課題です。最後に、これらすべては、計画、実行、学習できる信頼性の高い推論ループによって調整される必要があります。</p><p>これを解決するには、開発者は複雑で脆弱なスタックをゼロから構築する必要があります。今日のエージェント アーキテクチャでは、LLM、ベクター データベース、メタデータ ストア、ログ記録とトレースの個別のシステム、そしてすべてが機能しているかどうかを評価する方法など、複数の異なる部分をつなぎ合わせる必要があります。これは単に複雑なだけでなく、コストがかかり、エラーが発生しやすく、ユーザーが求める高品質で信頼性の高い AI システムの構築が困難になります。</p><p>だから、もっとシンプルにしたいんです。これを実現するための私たちのアプローチは、効果的なコンテキスト駆動型エージェントの重要な要素を取り上げ、 <strong>Elastic AI Agent Builder</strong>と呼ばれる新しい機能セットを使用して Elasticsearch の中核に直接統合することです。この新しいレイヤーは、Elasticsearch を活用した AI エージェントを作成するためのすべての重要な構成要素（オープンなプリミティブ セット、標準ベースのプロトコル、データへの安全なアクセス）を備えたフレームワークを提供します。これにより、現実世界のデータと要件に合わせてカスタマイズされたエージェント システムを構築できます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2779dae5df010328/6a17e15eabe0f24f18dfe931/1ee1e73dd3f485ce86294d39490c98ce2a3d9925-1238x1072.png" alt="" /><p><strong>AI エクスペリエンスの提供</strong>: これが究極の目標です。当社の Search AI プラットフォームとお客様のデータを基盤として、カスタム チャット インターフェースから、LangChain などのエージェント フレームワークや Salesforce などのビジネス アプリケーションとの統合まで、あらゆるタイプの生成 AI アプリケーションを構築できます。</p><p><strong>エージェントとツールを搭載</strong>: プラットフォームの上に、クリーンでシンプルな抽象化レイヤーを公開します。エージェントやツールと直接対話し、特定のニーズに合わせてカスタマイズできます。強力な API や MCP、A2A などのオープン スタンダードを通じてプラットフォームの機能にアクセスすることもできます。</p><p><strong>Search AI Platform によって有効化</strong>: これは、コンポーネントを統合したコア エンジンです。高度なベクトル データベース、エージェント ロジック、クエリ構築、セキュリティ機能、評価のためのトレースはすべてここに存在し、Elastic によって管理および最適化されています。</p><p><strong>データの力を解き放つ</strong>: 優れたエージェントの基盤は優れたデータです。当社のプラットフォームは、すべての企業データへのアクセスを取り込み、連携する機能から始まります。</p><h2>プラットフォームにおけるエージェント構築</h2><p>Search AI プラットフォームに統合された Agent Builder は、エージェント開発のための完全なフレームワークを提供します。これは 5 つの主要な柱に基づいて構築されており、各柱は実稼働レベルの AI システムの構築と展開の重要な側面に対処するように設計されています。エージェントが目的を定義し、ツールが機能を提供し、オープン スタンダードが相互運用性を確保し、評価が透明性をもたらし、セキュリティが信頼を提供する仕組みについて詳しく見ていきましょう。</p><h3>エージェント</h3><p>エージェントは、Elasticsearch のこの新しいレイヤーにおける最高レベルの構成要素です。エージェントは、達成する目的、実行に使用できるツールのセット、および操作できるデータ ソースを定義します。エージェントは会話によるやり取りに限定されず、完全なワークフロー、タスクの自動化、ユーザー向けのエクスペリエンスを実現できます。</p><p>クエリがエージェントに送られると、構造化されたサイクルに従います。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt774ffd7df65bd01d/6a17e15f25daabd5cc08a17f/627ad1744b629bbe27359325702f40d97e40d1f4-704x852.png" alt="" /><ol><li><p>入力内容と目的を解釈する</p></li><li><p>実行に適したツールと引数を選択する</p></li><li><p>ツールの応答の理由</p></li><li><p>結果を返すか、さらにツールの呼び出しを続行するかを決定します</p></li></ol><p>Elastic は、このサイクルのオーケストレーション、コンテキスト、および実行を処理します。開発者は、エージェントが<em>何</em>をすべきか（目的、ツール、データ）を定義することに重点を置き、システムは推論とワークフローの実行<em>方法</em>を管理します。</p><p><em>デフォルトエージェント</em></p><p>このプラットフォーム上に構築された最初のエージェントは、Kibana のネイティブ会話エージェントであり、データとすぐに対話できるようになります。完全な拡張性を維持しながらすぐに使用できるエクスペリエンスを提供し、追加の構成なしですぐにデータの操作を開始できます。</p><p>新しいチャット ユーザー エクスペリエンスまたは API を介して、Kibana でこのエクスペリエンスを直接操作できます。</p><p>API を介してデフォルトのエージェントを照会するには、1 回の呼び出しだけが必要です。</p>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}<p>会話はステートフルなので、 conversation_id を使用してエージェントとの対話を継続したり、完全な会話履歴を取得したりできます。</p>POST kbn://api/agent_builder/converse
{
    "input": "What about the second top?",
    "conversation_id": "ec757c6c-c3ed-4a83-8e2c-756238f008bb"
}

## get the full conversation
GET kbn://api/agent_builder/conversations/ec757c6c-c3ed-4a83-8e2c-756238f008bb<p><em>カスタムエージェント</em></p><p>開発者は、シンプルな API を通じて独自のカスタム エージェントを作成することもできます。エージェントは、指示、ツール、データ アクセスをカプセル化し、カスタマイズされた推論エンジンを作成します。</p><p>カスタム エージェントの作成は、1 回の API 呼び出しを行うだけで簡単に行えます。以下のサンプルは例を示しています。「構成」フィールドには、手順や利用可能なツールなどのすべての重要な詳細が含まれています。</p>POST kbn://api/agent_builder/agents
{
  "id": "custom_agent",
  "name": "My Custom Agent",
  "description": "Description of the custom agent",
  "configuration": {
      "instructions": "You are a log expert specialising in ...",
      "tools": 
...
   }
}<p>作成されたエージェントは直接クエリできます。</p>POST kbn://api/agent_builder/converse
{
    "input": "What news about DIA?",
    "agent_id": "custom_agent"
}<p>このアプローチにより、エージェントはゼロから構築する複雑なシステムから、ビジネス ロジックの単純な宣言型ユニットに変換され、インテリジェントな自動化をより迅速に提供できるようになります。</p><p>特化したエージェントをゼロから構築する方法の詳細については、詳細なステップバイステップガイド「<a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">初めての Elastic エージェント: 単一のクエリから AI を活用したチャットまで」</a>をご覧ください。</p><h3>ツール</h3><p>エージェントが達成すべき<em>こと</em>を定義するのに対し、ツールは達成<em>方法</em>を定義します。</p><p>ツールは、エージェントが情報を実行および取得したり、アクションを実行したりするための特定の Elastic Core 機能を公開します。ツールには、インデックスの取得やマッピングの取得などのコア機能や、自然言語から ES|QL への変換などのより高度な機能を含めることができます。</p><p>Elasticsearch には、一般的なニーズに合わせて最適化された一連のデフォルト ツールが付属しています。しかし、本当の柔軟性は、独自のものを作成することから生まれます。ツールを定義することで、ES|QL を使用してエージェントに公開されるクエリ、インデックス、フィールドを正確に決定し、速度、精度、セキュリティを正確に制御できます。</p><p>新しいツールの登録も、1 回の API 呼び出しと同じくらい簡単です。<a href="https://www.elastic.co/search-labs/blog/esql-timeline-of-improvements">ES|QL (Elasticsearch クエリ言語)</a>を活用して特定の金融資産に関するニュースを検索するツールを作成できます。</p>POST kbn://api/agent_builder/tools
{
  "id": "news_on_asset",
  "type": "esql",
  "description": "Find news and reports about a particular asset where ...",
  "configuration": {
    "query": "FROM financial_news, financial_reports | where MATCH(company_symbol, ?symbol) OR MATCH(entities, ?symbol) | limit 5",
    "params": {
      "symbol": {
        "type": "keyword",
        "description": "The asset symbol"
      }
    }
  ...
  }
...
}<p>登録が完了すると、新しいツールをカスタム エージェントに割り当てることができ、適切なタイミングで推論して呼び出すための厳選された一連の機能をエージェントに提供できるようになります。</p><p>当社では、お客様固有のニーズに合わせてカスタム ツールを作成するためのプラットフォームを提供しています。たとえば、ES|QL を使用すると、エージェントを汎用エージェントから、お客様独自のデータとビジネス ドメインに基づいたドメイン固有のエキスパートに変換できます。</p><h3>オープンスタンダードと相互運用性</h3><p>Elasticsearch エージェントとツールはオープン標準 API を介して公開されるため、エージェントフレームワークのより広範なエコシステム内の基礎ブロックとして簡単に統合できます。私たちのアプローチはシンプルです。ブラックボックスはありません。Elastic の検索における強みを活かし、それを補完的な機能や他のエージェント システムと組み合わせることができるようにしたいと考えています。</p><p>これを実現するために、当社は API、新しいプロトコル、オープン スタンダードを通じて機能を公開しています。</p><p><em>モデルコンテキストプロトコル（MCP）</em></p><p><a href="https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch">モデル コンテキスト プロトコル (MCP)</a>は、システム間でツールを接続するためのオープン スタンダードとして急速に普及しつつあります。MCP をサポートすることで、Elasticsearch は会話型 AI をデータベース、インデックス、外部 API に接続できるようになります。Elastic Stack に組み込まれたリモート MCP サーバーを使用すると、MCP 対応のクライアントはどれでも Elastic のツールにアクセスし、それらをより大規模なエージェントワークフローの構成要素として使用できます。</p><p>これは一方通行ではありません。外部の MCP サーバーからツールをインポートし、Elasticsearch 内で利用できるようにすることもできます。近い将来、MCP サーバーはほぼすべての用途で利用できるようになる見込みで、私たち自身が作成するものよりもはるかに包括的なものになるでしょう。Elastic は大規模な検索と取得機能を提供しており、これを他のプラットフォームの特殊な機能と組み合わせて効果的なエージェントを構築できます。</p><p><em>エージェント間（A2A）</em></p><p>また、エージェント間 (A2A) サポートにも取り組んでいます。MCP はツールを接続することに重点が置かれていますが、A2A はエージェントを接続することに重点が置かれています。A2A サーバーを使用すると、構築する Elastic エージェントは他のシステムのエージェントと直接通信して、コンテキストを共有したり、タスクを委任したり、ワークフローを調整したりできるようになります。</p><p>これを推論層における相互運用性と考えてください。Elastic エージェントは検索と取得を処理し、タスクを専門のサポート エージェントまたは IT エージェントに引き渡して、結果をシームレスに返すことができます。その結果、各エージェントが最善を尽くして協力するエコシステムが実現します。</p><p>最終的に、MCP と A2A を採用することで、Elasticsearch が第一級市民としての役割を担うという当社の取り組みが強化され、より広範なエージェントエコシステム全体でのオープンな統合が保証されます。</p><h3>追跡と評価</h3><p>検索がエージェントと統合されるにつれて、効果的な評価の課題が重要になります。実際の企業環境にエージェントを自信を持って導入するには、エージェントが正確であるだけでなく、効率的で信頼できるという保証が必要です。パフォーマンスを測定したり、悪い応答を診断したり、ベースラインを改善したりするにはどうすればよいですか?すべては可視性から始まります。</p><p>そのため、私たちはエージェント API を最初から透明性を重視して設計しました。次の単純なエージェントのやり取りを考えてみましょう。</p>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}<p>応答には、最終的な回答だけでなく、エージェントが選択したツール、使用したパラメーター、各ステップの結果の詳細を含む完全な実行トレースが含まれます。</p>{
  "conversation_id": "db5c0c8b-12bf-4928-a57e-d99129ad2fea",
  "steps": [
    {
      "type": "tool_call",
      "tool_call_id": "tooluse_Nfqr3mwtR92HTRIsTcGXZQ",
      "tool_id": ".index_explorer",
      "params": {
        "query": "indices containing portfolio data"
      },
      "results": [...]
    }
    // ... more steps ...
  ],
  "response": {
    "message": "Based on the information I've gathered...."
  }
}<p>包括的なトレースとログ記録は継続的な改善ループに不可欠であり、まもなくこれらのエージェント トレースを Elasticsearch に直接保存して表示できるようになります。さらに、これらのトレースは OpenTelemetry プロトコルに基づいて構築されているため、標準化され、移植可能であり、選択した監視プラットフォームとの統合が可能です。</p><p>このレベルの詳細は、真の継続的改善ループの基礎となります。これにより、包括的なテスト スイートを構築し、障害をデバッグし、障害モードを特定して回帰を防ぎ、成功パターンをキャプチャしてパフォーマンスを微調整できるようになります。最終的に、このデータ主導のアプローチは、有望なプロトタイプを製品レベルの信頼できる AI システムに変換するための鍵となります。</p><h3>セキュリティ</h3><p>エージェントとツールの性能が向上するにつれて、セキュリティはオプションではなく、基礎的なものになります。API を公開し、タスクやワークフローを自動化するには、エンタープライズ システムが信頼されている必要があります。特に、エージェントがより多くのワークフローを自動化し始めると、これらを保護し、企業の要件を満たしていることを確認する機能が不可欠になります。</p><p>上記の機能はすべて、API 呼び出し<a href="https://www.elastic.co/search-labs/blog/rag-and-rbac-integration">のロールベースのアクセス制御 (RBAC)</a>や API キー管理など、現在 Elastic ですでに利用可能な制御を継承しています。同じ制御を MCP などの新しいプロトコルにも拡張しています。つまり、OAuth などの標準のサポートと、カスタム認証メカニズムをプラグインする機能を意味します。</p><p>私たちの目標は、組織が求めるセキュリティ、コンプライアンス、ガバナンスのレベルを維持しながら、エージェントとツールを実験する柔軟性を提供することです。</p><h2>次に何が起こるか</h2><p>機能を追加するだけではなく、エージェントコンテキストエンジニアリング向けに Elasticsearch を拡張しています。当社は、以下の理念に基づいて今後開発を進めていく予定です。</p><p>1. オープンソースと標準への取り組み</p><p>当社はオープン ソースとオープン スタンダードに注力しており、これらの機能が外部のエージェント フレームワークと相互運用可能であることを保証します。データとワークフローを常に管理しながら、エコシステム全体でエージェントを接続、拡張、構成できるようになります。</p><p>2. 文脈の価値</p><p>AI エージェントのコンテキストは最大の資産です。エージェントが検索やワークフロー操作を実行するときにコンテキストを管理することは、難しいタスクになる可能性があります。私たちは Elastic の強みを活用してコンテキスト エンジニアリングを解決し、エージェントが最も関連性の高い情報を常に利用できるようにしています。</p><p>3. エージェントデータストリームに焦点を当てる</p><p>今後、エージェントは、エージェントの出力 (生成されたドキュメント、レポート、視覚化) やエージェントの実行トレース (思考、ツールの呼び出し、メモリ/コンテキスト) など、ますます大きなデータソースになります。Elastic はこの種のデータの処理に適しており、私たちはこのデータを使用して分析、評価、自動改善を実行するための研究に取り組んでいます。</p><p>4. セキュリティと安全性を考慮した設計</p><p>AI エージェントは、セキュリティと安全性に関するまったく新しい一連の課題をもたらします。Elastic は常に安全なソリューションのリーダーであり、エンタープライズグレードのガードレール、アクセス制御、および「ゼロトラスト」原則の構築を継続しています。</p><p>5. プラットフォームに組み込む</p><p>AI エージェントを構築するための機能は、Elasticsearch プラットフォームに組み込まれています。つまり、トレース、評価、視覚化、分析などのプラットフォーム レベルの機能はすべてエージェントに適用できます。エージェントの実行に基づいてダッシュボードを開発したい - それが組み込まれています。感情分析を使用して AI エージェントのパフォーマンスを評価したい場合、プラットフォームでそれが可能です。これにより、AI エクスペリエンスを中心とした完全なライフサイクルを構築できるようになります。</p><p>Elastic の目標は、データに完全に統合され、拡張可能で、データに基づいた会話型 AI と自動化されたワークフローを構築するためのインターフェースを提供することです。より詳しい技術的な詳細と進捗状況については、近日中に共有される予定です。</p><p>Agent Builder は現在、プライベート プレビューでご利用いただけます。アクセスをリクエストするには、<a href="https://www.elastic.co/contact?pg=global&amp;plcmt=nav&amp;cta=205352">当社にご連絡ください</a>。ご質問やフィードバックはありますか?<a href="https://elasticstack.slack.com/archives/C09GRHEQ4AG"><strong>Slack ワークスペース</strong></a>または<a href="https://discuss.elastic.co/c/search/84"><strong>ディスカッション フォーラム</strong></a>で開発者コミュニティとつながりましょう。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[Elastic内部の実情]]></category>
    <dc:creator><![CDATA[Anish Mathur,Dana Juratoni]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16a3d8736bf086e0/6a17e1616864a45410b686c7/71876470119e02a45bcbfcbf27a3e110328bbd14-1020x654.png" length="0" type="image/png"/>
    <pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[JavaScript、Mastra、Elasticsearch を使用したエージェント型 RAG アシスタントの構築]]></title>
    <description><![CDATA[JavaScript エコシステムで AI エージェントを構築する方法を学ぶ]]></description>
    <content:encoded><![CDATA[<p>このアイデアは、白熱したハイリスクなファンタジー バスケットボール リーグの最中に思いつきました。私はこう考えました。 <em>「毎週の対戦で優位に立つのに役立つ AI エージェントを構築できるだろうか？」 もちろんです!</em></p><p>この記事では、 <a href="https://mastra.ai/en/docs">Mastra</a>とそれと対話するための軽量 JavaScript Web アプリケーションを使用して、エージェント RAG アシスタントを構築する方法について説明します。このエージェントを Elasticsearch に接続することで、構造化されたプレイヤーデータへのアクセスとリアルタイムの統計集計の実行が可能になり、プレイヤー統計に基づいた推奨事項を提供できるようになります。GitHub<a href="https://github.com/jdarmada/nba-ai-assistant-js.git">リポジトリ</a>にアクセスして手順を確認してください。README<a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/README.md">に</a>は、アプリケーションを独自に複製して実行する方法が記載されています。 </p><p>すべてをまとめると次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63ea3e7a09306fbf/6a17f1d97f6f150e22c09c50/1c73bd1dc1b5fe54f025c7a2b7c322acc9122f3a-1999x1393.png" alt="" /><p>注: このブログ投稿は、「 <a href="https://www.elastic.co/search-labs/blog/ai-agents-ai-sdk-elasticsearch">AI SDK と Elastic を使用した AI エージェントの構築</a>」に基づいています。AI エージェント全般とその用途についてよく知らない場合は、まずそこから始めてください。
</p><h2><strong>アーキテクチャの概要</strong></h2><p>システムの中核となるのは、エージェントの推論エンジン（脳）として機能する大規模言語モデル（LLM）です。ユーザー入力を解釈し、呼び出すツールを決定し、関連する応答を生成するために必要な手順を調整します。</p><p>エージェント自体は、JavaScript エコシステムのエージェント フレームワークである Mastra によって構築されます。Mastra は、LLM をバックエンド インフラストラクチャでラップし、それを API エンドポイントとして公開し、ツール、システム プロンプト、エージェントの動作を定義するためのインターフェイスを提供します。</p><p>フロントエンドでは、 <a href="https://vite.dev/guide/">Vite</a>を使用して、エージェントにクエリを送信してその応答を受信するためのチャット インターフェイスを提供する React Web アプリケーションを迅速に構築します。</p><p>最後に、エージェントがクエリして集計できるプレーヤーの統計情報と対戦データを保存する Elasticsearch があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte13f09493f217047/6a17f1db1d1b83178d93e546/443bdc00d84ed1dd49e9f9e431e86ca4b0892563-1999x977.png" alt="" /><h2><strong>背景</strong></h2><p>いくつかの基本的な概念を確認してみましょう。</p><h3><strong>エージェントRAGとは何ですか？</strong></h3><p>AI エージェントは他のシステムと対話し、独立して動作し、定義されたパラメータに基づいてアクションを実行できます。Agentic RAG は、AI エージェントの自律性と検索拡張生成の原理を組み合わせ、LLM が応答を生成するために呼び出すツールとコンテキストとして使用するデータを選択できるようにします。RAG の詳細については、<a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">こちらを</a>ご覧ください。</p><h3><strong>フレームワークを選択する場合、なぜ AI-SDK を超えるのでしょうか?</strong></h3><p>利用可能な AI エージェント フレームワークは数多くあり、 <a href="https://www.elastic.co/search-labs/blog/using-crewai-with-elasticsearch">CrewAI</a> 、 <a href="https://www.elastic.co/search-labs/blog/using-autogen-with-elasticsearch">AutoGen</a> 、 <a href="https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch">LangGraph</a>などの人気のフレームワークについてはおそらく聞いたことがあるでしょう。これらのフレームワークのほとんどは、さまざまなモデルのサポート、ツールの使用、メモリ管理など、共通の機能セットを共有しています。</p><p>こちらは、Harrison Chase (LangChain CEO) によるフレームワーク<a href="https://docs.google.com/spreadsheets/d/1B37VxTBuGLeTSPVWtz7UMsCdtXrqV5hCjWkbHN8tfAo/edit?gid=0#gid=0">比較シート</a>です。</p><p>私が Mastra に興味を持ったのは、フルスタック開発者がエージェントをエコシステムに簡単に統合できるように構築された JavaScript ファーストのフレームワークであるという点です。Vercel の AI-SDK もこのほとんどを実行しますが、プロジェクトにさらに複雑なエージェント ワークフローが含まれている場合は、Mastra が真価を発揮します。Mastra は AI-SDK によって設定された基本パターンを強化しており、このプロジェクトではそれらを連携して使用します。</p><h3><strong>フレームワークとモデル選択の考慮事項</strong></h3><p>これらのフレームワークは AI エージェントを迅速に構築するのに役立ちますが、考慮すべき欠点もいくつかあります。たとえば、AI エージェントや一般的な抽象化レイヤー以外のフレームワークを使用する場合、制御が少し失われます。LLM がツールを正しく使用しなかったり、望ましくないことを実行したりする場合、抽象化によってデバッグが難しくなります。それでも、私の意見では、特にこれらのフレームワークは勢いを増しており、継続的に反復されているため、このトレードオフは、構築時に得られる容易さとスピードの価値があります。</p><p>繰り返しになりますが、これらのフレームワークはモデルに依存しません。つまり、さまざまなモデルをプラグ アンド プレイできます。モデルはトレーニングに使用されたデータ セットによって異なり、その結果、モデルが提供する応答も異なることに注意してください。一部のモデルではツールの呼び出しすらサポートされていません。したがって、さまざまなモデルを切り替えてテストし、どのモデルが最適な応答を返すかを確認することは可能ですが、それぞれのシステム プロンプトを書き換える必要がある可能性が高いことに注意してください。例えば、Llama3.3を使用する場合GPT-4o よりも、必要な応答を得るために、より多くのプロンプトと具体的な指示が必要になります。</p><h3><strong>NBAファンタジーバスケットボール</strong></h3><p>ファンタジー バスケットボールでは、友達のグループでリーグを開始し (グループの競争力に応じて、友情のステータスに影響する可能性があります)、通常はいくらかのお金が賭けられます。その後、各自が 10 人のプレイヤーでチームを編成し、毎週交互に他の友達の 10 人のプレイヤーと対戦します。全体のスコアに加算されるポイントは、特定の週に各プレイヤーが対戦相手に対して行ったパフォーマンスです。</p><p>チームの選手が負傷したり、出場停止になったりした場合は、チームに追加できるフリーエージェント選手のリストが表示されます。ファンタジー スポーツでは、選べる選手の数が限られており、誰もが常に最高の選手を選ぶために奔走しているため、ここで多くの難しい思考が生まれます。</p><p>これは、どの選手を選択するかをすぐに決定しなければならない状況で特に役立つ、NBA AI アシスタントの出番です。特定の対戦相手に対するプレーヤーのパフォーマンスを手動で調べる代わりに、アシスタントがそのデータをすばやく見つけて平均を比較し、情報に基づいた推奨事項を提供します。</p><p>エージェント RAG と NBA ファンタジー バスケットボールの基本がわかったので、実際に見てみましょう。</p><h2><strong>プロジェクトの構築</strong></h2><p>途中で行き詰まったり、最初から構築したくない場合は、<a href="https://github.com/jdarmada/nba-ai-assistant-js.git">リポジトリ</a>を参照してください。</p><h3><strong>取り上げる内容</strong></h3><ol><li><p><strong>プロジェクトの足場作り:</strong></p><ol><li><p><strong>バックエンド (Mastra):</strong> npx create mastra@latest を使用してバックエンドをスキャフォールディングし、エージェント ロジックを定義します。</p></li><li><p><strong>フロントエンド (Vite + React):</strong> npm create vite@latest を使用して、エージェントと対話するためのフロントエンド チャット インターフェイスを構築します。</p></li></ol></li><li><p><strong>環境変数の設定</strong></p><ol><li><p>環境変数を管理するには、dotenv をインストールします。</p></li><li><p>.envを作成するファイルを開き、必要な変数を指定します。</p></li></ol></li><li><p><strong>Elasticsearchの設定</strong></p><ol><li><p>Elasticsearch クラスターを起動します (ローカルまたはクラウド上)。</p></li><li><p>公式 Elasticsearch クライアントをインストールします。</p></li><li><p>環境変数にアクセスできることを確認します。</p></li><li><p>クライアントへの接続を確立します。</p></li></ol></li><li><p><strong>NBA データを Elasticsearch に一括取り込み</strong></p><ol><li><p>集計を有効にするには、適切なマッピングを使用してインデックスを作成します。</p></li><li><p>プレイヤーのゲーム統計を CSV ファイルから Elasticsearch インデックスに一括取り込みます。</p></li></ol></li><li><p><strong>Elasticsearchの集計を定義する</strong></p><ol><li><p>特定の対戦相手に対する過去の平均を計算するクエリ。</p></li><li><p>特定の対戦相手に対するシーズン平均を計算するクエリ。</p></li></ol></li><li><p><strong>プレーヤー比較ユーティリティファイル</strong></p><ol><li><p>ヘルパー関数と Elasticsearch 集計を統合します。</p></li></ol></li><li><p><strong>エージェントの構築</strong></p><ol><li><p>エージェント定義とシステム プロンプトを追加します。</p></li><li><p>zod をインストールし、ツールを定義します。</p></li><li><p>CORS を処理するためのミドルウェア設定を追加します。</p></li></ol></li><li><p><strong>フロントエンドの統合</strong></p><ol><li><p>AI-SDK の useChat を使用してエージェントと対話します。</p></li><li><p>適切にフォーマットされた会話を保持するための UI を作成します。</p></li></ol></li><li><p><strong>アプリケーションの実行</strong></p><ol><li><p>バックエンド (Mastra サーバー) とフロントエンド (React アプリ) の両方を起動します。</p></li><li><p>サンプルクエリと使用方法。</p></li></ol></li><li><p><strong>次はエージェントのさらなるインテリジェント化</strong></p><ol><li><p>セマンティック検索機能を追加して、より洞察力のある推奨を可能にします。</p></li><li><p>検索ロジックを Elasticsearch MCP (Model Context Protocol) サーバーに移動することで、動的クエリを有効にします。</p></li></ol></li></ol><h3><strong>要件</strong></h3><ul><li><p><strong>Node.js と npm</strong> : バックエンドとフロントエンドの両方が Node 上で実行されます。Node 18+ と npm v9+ (Node 18+ にバンドルされています) がインストールされていることを確認してください。</p></li><li><p><strong>Elasticsearch クラスター:</strong>ローカルまたはクラウド上のアクティブな Elasticsearch クラスター。</p></li><li><p><strong>OpenAI API キー</strong>: <a href="https://platform.openai.com/api-keys">OpenAI 開発者ポータルの</a>API キー ページで生成します。</p></li></ul><p></p><h3><strong>プロジェクト構造</strong></h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt749baa120552e4ab/6a17f1dd1d1b83bfe993e54a/1c0bde11ad0eead523a95e03b9b905aa776e3fd1-1420x934.png" alt="" /><h4><strong>ステップ1：プロジェクトの足場作り</strong></h4><ol><li><p>まず、nba-ai-assistant-js ディレクトリを作成し、次のコマンドを使用して内部に移動します。 </p></li></ol>mkdir nba-ai-assistant-js &amp;&amp; cd nba-ai-assistant-js<p><strong>バックエンド:</strong></p><ol><li><p>次のコマンドで Mastra 作成ツールを使用します。 </p></li></ol>npx create-mastra@latest<p>2. ターミナルにいくつかのプロンプトが表示されます。最初のプロンプトでは、プロジェクトに backend という名前を付けます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65abf68fe588e968/6a17f1de63baff2814741d5b/de2725031ed6837db99a979efcdd0ece1e197dbb-608x84.png" alt="" /><p>3. 次に、Mastra ファイルを保存するためのデフォルトの構造を維持するため、 <code>src/</code>を入力します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bd829fcf0ae6b9/6a17f1e04b055dd30e432302/88919d9ff1852126395e1fcd700ecb1b59aac63c-866x116.png" alt="" /><p>4. 次に、デフォルトの LLM プロバイダーとして OpenAI を選択します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd167cc77a40b9a8/6a17f1e11480099e29b48863/2328761e769f3ded134e5a21e8a0bf8f41e88f68-404x210.png" alt="" /><p>5. 最後に、OpenAI API キーの入力が求められます。ここでは、スキップするオプションを選択し、後で<code> .env</code>ファイルで提供します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12654151ed495370/6a17f1e22f4a5c0f84fa89f9/0662de9bd28758e377e4c63df8d08b479068ce63-444x120.png" alt="" /><p><strong>フロントエンド：</strong></p><ol><li><p>ルート ディレクトリに戻り、次のコマンドを使用して<a href="https://vite.dev/guide/">Vite 作成ツール</a>を実行します。 <code>npm create vite@latest frontend -- --template react</code></p></li></ol><p>これにより、React 専用のテンプレートを使用して、 <code>frontend</code>という名前の軽量 React アプリが作成されます。</p><p>すべてがうまくいけば、プロジェクト ディレクトリ内に、Mastra コードを保持するバックエンド ディレクトリと、React アプリを含む<code>frontend</code>ディレクトリが表示されるはずです。</p><p></p><h4><strong>ステップ2: 環境変数の設定</strong></h4><ol><li><p>機密キーを管理するために、 <code>dotenv</code>パッケージを使用して.envから環境変数を読み込みます。ファイル。バックエンドディレクトリに移動して<code>dotenv</code>をインストールします。</p></li></ol>cd backend
npm install dotenv --save<p>2. バックエンド ディレクトリでは、適切な変数を入力するための example.env ファイルが提供されます。独自に作成する場合は、次の変数を必ず含めてください。</p># OpenAI Configuration
OPENAI_API_KEY=your_openai_api_key_here

# Elasticsearch Configuration
ELASTIC_ENDPOINT=your_elasticsearch_endpoint_here
ELASTIC_API_KEY=your_elasticsearch_api_key_here
<p></p><p>注意: <code>.env</code> <code>.gitignore</code>に追加して、このファイルがバージョン管理から除外されていることを確認してください。</p><h4><strong>ステップ3: Elasticsearchの設定</strong></h4><p>まず、アクティブな Elasticsearch クラスターが必要です。次の 2 つのオプションがあります。</p><ul><li><p><strong>オプションA: Elasticsearch Cloudを使用する</strong></p><ul><li><p><a href="https://cloud.elastic.co/registration">Elastic Cloud</a>にサインアップ</p></li><li><p>新しいデプロイメントを作成する</p></li><li><p>エンドポイント URL と API キー（エンコード済み）を取得します</p></li></ul></li><li><p><strong>オプションB: Elasticsearchをローカルで実行する</strong></p><ul><li><p>Elasticsearchをローカルにインストールして実行する</p></li><li><p>エンドポイントとして http://localhost:9200 を使用します</p></li><li><p>APIキーを生成する</p></li></ul></li></ul><p></p><p><strong>バックエンドに Elasticsearch クライアントをインストールする:</strong></p><ol><li><p>まず、バックエンド ディレクトリに公式 Elasticsearch クライアントをインストールします。</p></li></ol>npm install @elastic/elasticsearch<p>2. 次に、再利用可能な関数を保持するディレクトリ lib を作成し、そこに移動します。</p>mkdir lib &amp;&amp; cd lib<p>3. 内部に<a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticClient.js">elasticClient.js</a>という新しいファイルを作成します。このファイルは Elasticsearch クライアントを初期化し、プロジェクト全体で使用できるように公開します。</p><p>4. ECMAScript モジュール (ESM) を使用しているため、 __dirname and __ファイル名は使用できません。環境変数が.envから正しく読み込まれていることを確認するにはバックエンド フォルダー内のファイルで、ファイルの先頭に次の設定を追加します。</p>import { config } from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { Client } from '@elastic/elasticsearch';

// Grab current directory and load .env from backend folder
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const envPath = join(__dirname, '../.env');

// Load environment variables from the correct path
config({ path: envPath });<p>5. 次に、環境変数を使用して Elasticsearch クライアントを初期化し、接続を確認します。</p>//Elastic client Initialization, make sure environment variables are being loaded in correctly
const config= {
    node: `${process.env.ELASTIC_ENDPOINT}`,
    auth: {
        apiKey: `${process.env.ELASTIC_API_KEY}`,
    },
};

export const elasticClient = new Client(config);

//Check if the client is connected
async function checkConnection() { 
    try {
        const info = await elasticClient.info();
        console.log('Elasticsearch is connected:', info);
    } catch (error) {
        console.error('Elasticsearch connection error:', error);
    }
}

checkConnection();
<p>これで、このクライアント インスタンスを、Elasticsearch クラスターと対話する必要がある任意のファイルにインポートできます。</p><p></p><h4><strong>ステップ4: NBAデータをElasticsearchに一括取り込み</strong></h4><p><strong>データセット:</strong></p><p>このプロジェクトでは、リポジトリの<a href="https://github.com/jdarmada/nba-ai-assistant-js/tree/main/backend">backend/data</a>ディレクトリにあるデータセットを参照します。当社の NBA アシスタントは、このデータを知識ベースとして使用し、統計的な比較を実行し、推奨事項を生成します。</p><ul><li><p><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/sample_nba_data.csv">sample_player_game_stats.csv</a> - サンプルプレーヤーのゲーム統計 (例: NBA キャリア全体におけるプレーヤーごとのゲームごとのポイント、リバウンド、スティールなど)。このデータセットを使用して集計を実行します。(注: これはデモ用に事前に生成された模擬データであり、公式 NBA ソースから取得されたものではありません。)</p></li><li><p><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/playerAndTeamInfo.js">playerAndTeamInfo.js</a> - 通常は API 呼び出しによって提供されるプレーヤーとチームのメタデータを置き換え、エージェントがプレーヤーとチームの名前を ID に一致できるようにします。サンプル データを使用しているため、外部 API から取得する際のオーバーヘッドを避け、エージェントが参照できるいくつかの値をハードコードしました。</p></li></ul><p></p><p><strong>実装：</strong></p><ol><li><p><code>backend/lib</code>ディレクトリで、 <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/playerDataIngestion.js">playerDataIngestion.js</a>という名前のファイルを作成します。</p></li><li><p>インポートを設定し、CSV ファイル パスを解決し、解析を設定します。ここでも、ESM を使用しているため、サンプル CSV へのパスを解決するには<code>__dirname</code>を再構築する必要があります。また、 <a href="http://node.js/">Node.js</a>の組み込みモジュール<code>fs</code>と<code>readline</code>を使用して、指定された CSV ファイルを行ごとに解析します。</p></li></ol>import fs from 'fs';
import readline from 'readline';
import path from 'path';
import { fileURLToPath } from 'url';
import { elasticClient } from './elasticClient.js';

const indexName = 'sample-nba-player-data'; //Replace with your preferred index name

//Since we are using ES modules __dirname and __filename don't exist, so this is a workaround that allows us to use the absolute file path for our sample data.
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const filePath = path.resolve(__dirname, '../data/sample_nba_data.csv');<p>これにより、一括取り込み手順で CSV を効率的に読み取って解析できるようになります。</p><p>3. 適切なマッピングを使用してインデックスを作成します。Elasticsearch は<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dynamic">動的マッピングを</a>使用してフィールド タイプを自動的に推測できますが、ここでは各統計が数値フィールドとして扱われるように明示的に指定します。これらのフィールドは後で集計に使用するため、これは重要です。また、ポイントやリバウンドなどの統計情報には、小数値が含まれるようにするために、タイプ<code>float </code>を使用します。最後に、Elasticsearch が認識されないフィールドを動的にマッピングしないように、マッピング プロパティ<code>dynamic: 'strict'</code>を追加します。
</p>// Function to create an index with mappings
async function createIndex() {
    try {
        // Check if the index already exists
        const exists = await elasticClient.indices.exists({ index: indexName });

        if (exists) {
            console.log(`Index "${indexName}" already exists, deleting it now.`);
            await elasticClient.indices.delete({ index: indexName });
            console.log(`Deleted index "${indexName}".`);
        }
        // Create the index with mappings
        const response = await elasticClient.indices.create({
            index: indexName,
            body: {
                mappings: {
                    dynamic: 'strict', // Prevent dynamic mapping
                    properties: {
                        game_id: { type: 'integer' },
                        game_date: { type: 'date' },
                        player_id: { type: 'integer' },
                        player_full_name: { type: 'text' },
                        player_team_id: { type: 'integer' },
                        player_team_name: { type: 'text' },
                        home_team: { type: 'boolean' },
                        opponent_team_id: { type: 'integer' },
                        opponent_team_name: { type: 'text' },
                        points: { type: 'float' },
                        rebounds: { type: 'float' },
                        assists: { type: 'float' },
                        steals: { type: 'float' },
                        blocks: { type: 'float' },
                        fg_percentage: { type: 'float' },
                        minutes_played: { type: 'float' },
                    },
                },
            },
        });

        console.log('Index created:', response);
        return true;
    } catch (error) {
        console.error('Error creating index:', error);
        return false;
    }
}
<p>4. CSV データを Elasticsearch インデックスに一括で取り込む機能を追加します。コード ブロック内では、ヘッダー行をスキップします。次に、各行項目をコンマで分割し、ドキュメント オブジェクトにプッシュします。このステップでは、それらをクリーンアップし、適切なタイプであることを確認します。次に、ドキュメントをインデックス情報とともに bulkBody 配列にプッシュします。これは、Elasticsearch への一括取り込みのペイロードとして機能します。</p>async function bulkIngestCsv(filePath) {
    const readStream = fs.createReadStream(filePath);
    const rl = readline.createInterface({
        input: readStream,
        crlfDelay: Infinity,
    });

    const bulkBody = [];
    let lineNum = 0;

    //Skip the header line
    let headerLine = true;
    for await (const line of rl) {
        if (headerLine) {
            headerLine = false;
            continue;
        }
        lineNum++;

        // Split the line by comma and remove whitespace
        const [
            game_id,
            game_date,
            player_id,
            player_full_name,
            player_team_id,
            player_team_name,
            home_team,
            opponent_team_id,
            opponent_team_name,
            points,
            rebounds,
            assists,
            steals,
            blocks,
            fg_percentage,
            minutes_played,
        ] = line.split(',');

        // Create a document object
        const document = {
            game_id: parseInt(game_id),
            game_date: game_date.trim(),
            player_id: parseInt(player_id),
            player_full_name: player_full_name.trim(),
            player_team_id: parseInt(player_team_id),
            player_team_name: player_team_name.trim(),
            home_team: home_team.trim() === 'True', // Converts True/False into a boolean
            opponent_team_id: parseInt(opponent_team_id),
            opponent_team_name: opponent_team_name.trim(),
            points: parseFloat(points),
            rebounds: parseFloat(rebounds),
            assists: parseFloat(assists),
            steals: parseFloat(steals),
            blocks: parseFloat(blocks),
            fg_percentage: parseFloat(fg_percentage),
            minutes_played: parseFloat(minutes_played),
        };

        // Prepare the bulk operation format
        bulkBody.push({ index: { _index: indexName } });
        bulkBody.push(document);
    }

    console.log(`Parsed ${lineNum} lines from CSV`);
<p>5.次に、 <code>elasticClient.bulk()</code>で Elasticsearch の<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">Bulk API を</a>使用して、1 回のリクエストで複数のドキュメントを取り込むことができます。以下のエラー処理は、取り込みに失敗したドキュメントの数と、取り込みに成功したドキュメントの数を示すように構成されています。</p>try {
        // Perform the bulk request
        const response = await elasticClient.bulk({ body: bulkBody });

        if (response.errors) {
            console.log('Bulk Ingestion had some hiccups:');

            // Count successful vs failed operations
            let successCount = 0;
            let errorCount = 0;
            const errorDetails = [];

            response.items.forEach((item, index) =&gt; {
                const operation = item.index || item.create || item.update || item.delete;
                if (operation.error) {
                    errorCount++;
                    errorDetails.push({
                        document: index + 1,
                        error: operation.error,
                    });
                } else {
                    successCount++;
                }
            });

            console.log(`Successfully indexed: ${successCount} documents`);
            console.log(`Failed to index: ${errorCount} documents, here are the details`, errorDetails);

        } else {
            console.log(`Bulk Ingestion fully successful!`);
        }

    } catch (error) {
        console.error('Error performing bulk ingestion:', error);
    }
}
<p>6. 以下の<code>main()</code>関数を実行して、 <code>createIndex()</code>関数と<code>bulkIngestCsv()</code>関数を順番に実行します。</p>// Run this function
async function main() {
    const result = await createIndex();
    if (!result) {
        console.error('Index setup failed. Aborting.');
        return;
    }

    await bulkIngestCsv(filePath);
    console.log('Bulk ingestion completed!');
}

main();
<p>一括取り込みが成功したことを示すコンソール ログが表示された場合は、Elasticsearch インデックスを簡単にチェックして、ドキュメントが実際に正常に取り込まれたかどうかを確認します。</p><h4><strong>ステップ5: Elasticsearchの集計の定義と統合</strong></h4><p>これらは、プレイヤーの統計を相互に比較するために AI エージェントのツールを定義するときに使用される主な関数になります。</p><p>1. <code>backend/lib</code>ディレクトリに移動し、 <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticAggs.js">elasticAggs.js</a>というファイルを作成します。</p><p>2. 特定の対戦相手に対するプレイヤーの過去の平均を計算するには、以下のクエリを追加します。このクエリでは、2 つの条件（1 つは<code>player_id</code>に一致し、もう 1 つは<code>opponent_team_id</code>に一致する）を持つ<code>bool</code><a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/full-text-search/filters">フィルター</a>を使用して、関連するゲームのみを取得します。ドキュメントを返す必要はなく、集計のみを対象とするため、 <code>size:0</code>を設定します。<code>aggs</code>ブロックでは、 <code>points, rebounds, assists, steals, blocks</code>や<code>fg_percentage</code>などのフィールドに対して複数のメトリック<a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">集計を</a>並行して実行し、平均値を計算します。LLM は計算で成功するか失敗するかのどちらかですが、このプロセスは Elasticsearch にオフロードされ、NBA AI アシスタントが正確なデータにアクセスできるようになります。</p>export async function getHistoricalAveragesAgainstOpponent(player_id, opponent_team_id) {
    try {
        //Query for Historical Averages
        const historicalQuery = await elasticClient.search({
            index: 'sample-nba-player-data', 
            size: 0,
            query: {
                bool: {
                    must: [
                        {
                            term: {
                                player_id: {
                                    value: player_id,
                                },
                            },
                        },
                        {
                            term: {
                                opponent_team_id: {
                                    value: opponent_team_id,
                                },
                            },
                        },
                    ],
                },
            },
            aggs: {
                avg_points: { avg: { field: 'points' } },
                avg_rebounds: { avg: { field: 'rebounds' } },
                avg_assists: { avg: { field: 'assists' } },
                avg_steals: { avg: { field: 'steals' } },
                avg_blocks: { avg: { field: 'blocks' } },
             avg_fg_percentage: { avg: { field: 'fg_percentage' } },
            },
        });

        return {
            points: historicalQuery.aggregations.avg_points.value || 0,
            rebounds: historicalQuery.aggregations.avg_rebounds.value || 0,
            assists: historicalQuery.aggregations.avg_assists.value || 0,
            steals: historicalQuery.aggregations.avg_steals.value || 0,
            blocks: historicalQuery.aggregations.avg_blocks.value || 0,
            fgPercentage: historicalQuery.aggregations.avg_fg_percentage.value || 0,
        };
    } catch (error) {
        console.error('Query error from getHistoricalAveragesAgainstOpponent function:', error);
        return { error: 'Queries failed in getting historical averages against opponent.' };
    }
}
<p>3. 特定の対戦相手に対するプレーヤーのシーズン平均を計算するには、履歴クエリとほぼ同じクエリを使用します。このクエリの唯一の違いは、 <code>bool</code>フィルターに<code>game_date</code>の追加条件があることです。フィールド<code>game_date</code>は、現在の NBA シーズンの範囲内に収まる必要があります。この場合、範囲は<code>2024-10-01</code>から<code>2025-06-30</code>の間になります。以下の追加条件により、後続の集計で今シーズンのゲームのみが分離されることが保証されます。
</p>        {
                            range: {
                    //Range for this season, change to match current season
                                game_date: {
                                    gte: '2024-10-01',
                                    lte: '2025-06-30',
                                },
                            },
<h4><strong>ステップ6: プレーヤー比較ユーティリティ</strong></h4><p>コードをモジュール化して保守しやすい状態に保つために、メタデータ ヘルパー関数と Elasticsearch 集計を統合するユーティリティ ファイルを作成します。これにより、エージェントが使用するメイン ツールが強化されます。詳細は後述します。</p><p>1. <code>backend/lib</code>ディレクトリに新しいファイル<a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/comparePlayers.js">comparePlayers.js</a>を作成します。</p><p>2. 以下の関数を追加して、メタデータ ヘルパーと Elasticsearch 集約ロジックを、エージェントが使用するメイン ツールを強化する単一の関数に統合します。
</p>import { playersByName } from '../data/playerAndTeamInfo.js';
import { teamsByName } from '../data/playerAndTeamInfo.js';
import { upcomingMatchups } from '../data/playerAndTeamInfo.js';
import { getHistoricalAveragesAgainstOpponent } from './elasticAggs.js';
import { getSeasonAveragesAgainstOpponent } from './elasticAggs.js';

//Simple helper functions to simulate API calls for player and team metadata. These reference the hardcoded values from playerAndTeamInfo.js in the data directory
export function getPlayerInfo(playerFullName) {
    return playersByName[playerFullName];
}

export function getTeamID(teamFullName) {
    return teamsByName[teamFullName];
}

export function getUpcomingMatchups(teamId) {
    return upcomingMatchups[teamId];
}

//Main function used by the 'playerComparisonTool' agent tool
export async function comparePlayersForNextMatchup(player1Name, player2Name) {
    //Get Player Info
    const player1Info = getPlayerInfo(player1Name);
    const player2Info = getPlayerInfo(player2Name);

    //Get upcoming matchups
    const player1NextGame = getUpcomingMatchups(player1Info.team_id)[0];
    const player2NextGame = getUpcomingMatchups(player2Info.team_id)[0];

    //Get season and historical averages against next opponent for player 1
    const player1SeasonAverages = await getSeasonAveragesAgainstOpponent(
        player1Info.player_id,
        player1NextGame.opponent_team_id
    );
    const player1HistoricalAverages = await getHistoricalAveragesAgainstOpponent(
        player1Info.player_id,
        player1NextGame.opponent_team_id
    );

    //Get season and historical averages against next opponent for player 2
    const player2SeasonAverages = await getSeasonAveragesAgainstOpponent(
        player2Info.player_id,
        player2NextGame.opponent_team_id
    );
    const player2HistoricalAverages = await getHistoricalAveragesAgainstOpponent(
        player2Info.player_id,
        player2NextGame.opponent_team_id
    );

    const player1 = {
        name: player1Name,
        playerId: player1Info.player_id,
        teamId: player1Info.team_id,
        nextOpponent: {
            teamId: player1NextGame.opponent_team_id,
            teamName: player1NextGame.opponent_team_name,
            home: player1NextGame.home,
        },
        stats: {
            seasonAverages: player1SeasonAverages,
            historicalAverages: player1HistoricalAverages,
        },
    };

    const player2 = {
        name: player2Name,
        playerId: player2Info.player_id,
        teamId: player2Info.team_id,
        nextOpponent: {
            teamId: player2NextGame.opponent_team_id,
            teamName: player2NextGame.opponent_team_name,
            home: player2NextGame.home,
        },
        stats: {
            seasonAverages: player2SeasonAverages,
            historicalAverages: player2HistoricalAverages,
        },
    };

    return [player1, player2];
}
<h4><strong>ステップ7: エージェントの構築</strong></h4><p>フロントエンドとバックエンドのスキャフォールディングを作成し、NBA ゲームデータを取り込み、Elasticsearch への接続を確立したので、すべてのピースをまとめてエージェントを構築し始めることができます。</p><p><strong>エージェントの定義</strong></p><p>1. <code>backend/src/mastra/agents</code>ディレクトリ内の<a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/agents/index.ts">index.ts</a>ファイルに移動し、エージェント定義を追加します。次のようなフィールドを指定できます。</p><ul><li><p><strong>名前:</strong>フロントエンドで呼び出されたときに参照として使用されるエージェントの名前を指定します。</p></li><li><p><strong>指示/システム プロンプト:</strong>システム プロンプトは、対話中に従うべき初期コンテキストとルールを LLM に提供します。これは、ユーザーがチャット ボックスを通じて送信するプロンプトに似ていますが、こちらはユーザー入力の前に表示されます。繰り返しになりますが、これは選択したモデルに応じて変わります。</p></li><li><p><strong>モデル:</strong>使用する LLM (Mastra は OpenAI、Anthropic、ローカル モデルなどをサポートしています)。</p></li><li><p><strong>ツール:</strong>エージェントが呼び出すことができるツール関数のリスト。</p></li><li><p><strong>メモリ:</strong> (オプション) エージェントに会話履歴などを記憶させたい場合。簡単にするために、Mastra は永続メモリをサポートしていますが、永続メモリなしで開始できます。</p></li></ul><p></p>import { openai } from '@ai-sdk/openai';
import { Agent } from '@mastra/core/agent';
import { playerComparisonTool } from '../tools';

export const basketballAgent = new Agent({
    name: 'Basketball Agent',
    instructions: `
      You are a NBA Basketball expert.
      Your primary function is to compare two NBA players and recommend which one is the better fantasy pickup.

      Only compare players from the following list:
      - LeBron James
      - Stephen Curry
      - Jayson Tatum
      - Jaylen Brown
      - Nikola Jokic
      - Luka Doncic
      - Kyrie Irving
      - Anthony Davis
      - Kawhi Leonard
      - Russell Westbrook

      Input Handling Rules:
      - If the user asks about a player that is not on this list, respond with the list of available players for comparison.
      - If the user only inputs one player, ask the user to add another player from the list provided.
      - If the user inputs a player with the wrong spelling or capitalizations, infer from the list of available players provided.
      - IMPORTANT: If the user asks a question or asks you to generate a response about anything outside of basketball or the scope of this project, DO NOT answer and affirm you can only talk about basketball.

      Tool Usage:
      - Extract and standardize player names to match the list exactly.
      - Use the playerComparisonTool, passing both names as strings.
      - The tool will return an object with game information, stats, and analysis.

      Format your response using Markdown syntax. Use:

        Example output format:

       
        #### Next Game Info
        - ***LeBron James** vs Warriors, May 24 (Home)  
        - ***Stephen Curry** vs Lakers, May 24 (Away)


        #### Stats Comparison  
        \`\`\`  
        Stat                  LeBron James (vs Warriors)    Stephen Curry (vs Lakers)  
        --------------------  -----------------------------  ----------------------------  
        Historical Points     28.3                          30.3  
        Historical Assists    6.7                           8.7  
        Season Points         28.8                          23.3  
        Season Assists        6.2                           4.7  
        \`\`\`

        #### Fantasy Recommendation  
        Explain which player is the better fantasy pickup and why.
      
    `,
    model: openai('gpt-4o'),
    tools: { playerComparisonTool },
});
<p><strong>
ツールの定義</strong></p><ol><li><p><code>backend/src/mastra/tools</code>ディレクトリ内の<a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/tools/index.ts">index.ts</a>ファイルに移動します。</p></li><li><p>次のコマンドを使用して Zod をインストールします。</p></li></ol>npm install zod<p>3. ツール定義を追加します。このツールを呼び出すときにエージェントが使用するメイン関数として、 <code>comparePlayers.js</code>ファイル内の関数をインポートすることに注意してください。Mastra の<code>createTool()</code>関数を使用して、 <code>playerComparisonTool</code>を登録します。フィールドには次のものが含まれます。</p><ul><li><p><code>id</code>: これは、エージェントがツールの機能を理解するのに役立つ自然言語による説明です。</p></li><li><p><code>input schema</code>: ツールの入力の形状を定義するために、Mastra は TypeScript スキーマ検証ライブラリである<a href="https://zod.dev/">Zod</a>スキーマを使用します。Zod は、エージェントが正しく構造化された入力を入力したことを確認し、入力構造が一致しない場合はツールが実行されないようにすることで役立ちます。</p></li><li><p><code>description</code>: これは、エージェントがいつ電話をかけてツールを使用するかを理解するのに役立つ自然言語による説明です。</p></li><li><p><code>execute</code>: ツールが呼び出されたときに実行されるロジック。私たちの場合、インポートしたヘルパー関数を使用してパフォーマンス統計を返します。</p></li></ul>import { comparePlayersForNextMatchup } from '../../../lib/comparePlayers.js'
import { createTool } from "@mastra/core/tools";
import { z } from "zod";

export const playerComparisonTool = createTool({
    id: "Compare two NBA players",
    inputSchema: z.object({
        player1:z.string(),
        player2:z.string()
    }),
    description: "Use this tool to compare two players given in the user prompt.",
    execute: async ({ context: { player1, player2 } }) =&gt; {
        return await comparePlayersForNextMatchup(player1, player2);
      },
})<p><strong>CORSを処理するミドルウェアの追加</strong></p><p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS">CORS を</a>処理するために、Mastra サーバーにミドルウェアを追加します。人生には避けられないことが 3 つあると言われています。死、税金、そして Web 開発者にとっては CORS です。簡単に言うと、クロスオリジン リソース共有は、フロントエンドが別のドメインまたはポートで実行されているバックエンドにリクエストを送信するのをブロックするブラウザのセキュリティ機能です。バックエンドとフロントエンドの両方をローカルホストで実行しているにもかかわらず、それらは異なるポートを使用するため、CORS ポリシーがトリガーされます。バックエンドがフロントエンドからのリクエストを許可するように、 <a href="https://mastra.ai/en/docs/server-db/middleware">Mastra ドキュメント</a>で指定されているミドルウェアを追加する必要があります。</p><p>1. <code>backend/src/mastra</code>ディレクトリ内の<a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/index.ts">index.ts</a>ファイルに移動し、CORS の設定を追加します。</p><ul><li><p><code>origin: ['http://localhost:5173']</code></p><ul><li><p>このアドレス（Vite のデフォルト アドレス）からのリクエストのみを許可します</p></li></ul></li><li><p><code>allowMethods: ["GET", "POST"]</code></p><ul><li><p>許可される HTTP メソッド。ほとんどの場合、POST が使用されます。</p></li></ul></li><li><p><code>allowHeaders: ["Content-Type", "Authorization", "x-mastra-client-type, "x-highlight-request", "traceparent"],</code></p><ul><li><p>これらはリクエストで使用できるカスタムヘッダーを決定します</p></li></ul></li></ul><p></p>import { Mastra } from '@mastra/core/mastra';
import { basketballAgent } from './agents';

console.log('Starting Mastra server...');

export const mastra = new Mastra({
  agents: { basketballAgent },
  server:{
    timeout: 10 * 60 * 1000, // 10 minutes
    cors: {
      origin: ['http://localhost:5173'],
      allowMethods: ["GET", "POST"],
      allowHeaders: [
        "Content-Type",
        "Authorization",
        "x-mastra-client-type",
        "x-highlight-request",
        "traceparent",
      ],
      exposeHeaders: ["Content-Length", "X-Requested-With"],
      credentials: false,
    },
  },

});

console.log('Mastra server configured.'); // Log after server configuration
<h4><strong>ステップ8: フロントエンドの統合</strong></h4><p>この React コンポーネントは、 <code>@ai-sdk/react</code>の<a href="https://mastra.ai/en/docs/frameworks/agentic-uis/ai-sdk#using-the-usechat-hook">useChat()</a>フックを使用して Mastra AI エージェントに接続するシンプルなチャット インターフェースを提供します。このフックを使用して、トークンの使用状況やツールの呼び出しを表示したり、会話をレンダリングしたりします。上記のシステム プロンプトでは、エージェントに応答をマークダウンで出力するように要求しているため、 <code>react-markdown</code>を使用して応答を適切にフォーマットします。</p><p></p><p>1.フロントエンド ディレクトリにいる間に、useChat() フックを使用するために @ai-sdk/react パッケージをインストールします。</p>npm install @ai-sdk/react<p>2. 同じディレクトリで、React Markdown をインストールして、エージェントが生成する応答を適切にフォーマットできるようにします。</p>npm install react-markdown<p>3. <code>useChat()</code>を実装します。このフックは、フロントエンドと AI エージェントのバックエンド間のやり取りを管理します。メッセージの状態、ユーザー入力、ステータスを処理し、監視の目的でライフサイクル フックを提供します。渡すオプションは次のとおりです。</p><ul><li><p><code>api:</code> これは、Mastra AI エージェントのエンドポイントを定義します。デフォルトではポート 4111 に設定されており、ストリーミング応答をサポートするルートも追加する必要があります。</p></li><li><p><code>onToolCall</code>: これは、エージェントがツールを呼び出すたびに実行されます。エージェントがどのツールを呼び出しているかを追跡するために使用します。</p></li><li><p><code>onFinish</code>: エージェントが完全な応答を完了した後に実行されます。ストリーミングを有効にしても、 <code>onFinish</code>各チャンクの後ではなく、完全なメッセージが受信された後に実行されます。ここでは、トークンの使用状況を追跡するためにこれを使用しています。これは、LLM コストを監視して最適化するときに役立ちます。</p></li></ul><p>4. 最後に、 <code>frontend/components</code>ディレクトリの<a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/frontend/components/ChatUI.jsx">ChatUI.jsx</a>コンポーネントに移動して、会話を行うための UI を作成します。次に、エージェントからの応答を適切にフォーマットするために、応答を<code>ReactMarkdown</code>コンポーネントでラップします。</p>import React, { useState } from 'react';
import { useChat } from '@ai-sdk/react';
import ReactMarkdown from 'react-markdown';

export default function ChatUI() {
    const [totalTokenUsage, setTotalTokenUsage] = useState(0);
    const [promptTokenUsage, setPromptTokenUsage] = useState(0);
    const [completionTokenUsage, setCompletionTokenUsage] = useState(0);
    const [toolsCalled, setToolsCalled] = useState([]);

    const { messages, input, handleInputChange, handleSubmit, status } = useChat({
        api: 'http://localhost:4111/api/agents/basketballAgent/stream', //Replace with your own endpoint for your agent
        id: 'my-chat-session',

        //Optional parameter to check agent tool calls
        onToolCall: ({ toolCall }) =&gt; {
            setToolsCalled((prev) =&gt; [...prev, toolCall.toolName]);
        },

        //Optional parameter to check token usages
        onFinish: (message, { usage }) =&gt; {
            setTotalTokenUsage((prev) =&gt; prev + usage.totalTokens);
            setPromptTokenUsage((prev) =&gt; prev + usage.promptTokens);
            setCompletionTokenUsage((prev) =&gt; prev + usage.completionTokens);
        },

        //Optional parameter for error handling
        onError: (error) =&gt; {
            console.error('Agent error:', error);
        },
    });

    return (
        &lt;div&gt;
            &lt;div className="agent-info"&gt;
                &lt;h4 className="stats-title"&gt;What's My Agent Doing?&lt;/h4&gt;

                &lt;div className="stats-box"&gt;
                    &lt;strong className="stats-sub-title"&gt;Tools Called:&lt;/strong&gt;
                    &lt;ul className="tool-list"&gt;
                        {toolsCalled.map((tool, idx) =&gt; (
                            &lt;li key={idx}&gt;{tool}&lt;/li&gt;
                        ))}
                        {toolsCalled.length === 0 &amp;&amp; &lt;li&gt;No tools called yet.&lt;/li&gt;}
                    &lt;/ul&gt;

                    &lt;div className="usage-stats"&gt;
                        &lt;p&gt;Prompt Token Usage: {promptTokenUsage}&lt;/p&gt;
                        &lt;p&gt;Completion Token Usage: {completionTokenUsage}&lt;/p&gt;
                        &lt;p&gt;Total Token Usage: {totalTokenUsage}&lt;/p&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;strong&gt;Conversation:&lt;/strong&gt;
            &lt;div className="convo-box"&gt;
                {messages.map((msg) =&gt; (
                    &lt;div key={msg.id} className="message-item"&gt;
                        &lt;strong className="message-role"&gt;{msg.role === 'assistant' ? 'Basketbot' : 'You'}:&lt;/strong&gt;
                        &lt;ReactMarkdown&gt;{msg.content}&lt;/ReactMarkdown&gt;
                    &lt;/div&gt;
                ))}
            &lt;/div&gt;

            &lt;form onSubmit={handleSubmit}&gt;
                &lt;input
                    type="text"
                    value={input}
                    onChange={handleInputChange}
                    placeholder="Input two players you want to compare."
                    className="input-box"
                /&gt;
                &lt;button type="submit" disabled={status === 'streaming'}&gt;
                    {status === 'streaming' ? 'Thinking...' : 'Send'}
                &lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;
    );
}<h4><strong>ステップ9: アプリケーションの実行</strong></h4><p>おめでとうございます！これでアプリケーションを実行する準備が整いました。バックエンドとフロントエンドの両方を起動するには、次の手順に従います。</p><ol><li><p>ターミナル ウィンドウで、ルート ディレクトリからバックエンド ディレクトリに移動し、Mastra サーバーを起動します。</p></li></ol>cd backend

npm run dev<p>2. 別のターミナル ウィンドウで、ルート ディレクトリからフロントエンド ディレクトリに移動し、React アプリを起動します。</p><p></p>cd frontend

npm run dev<p></p><p>3. ブラウザで次の場所に移動します。</p><p></p><p><a href="http://localhost:5173/">http://localhost:5173</a></p><p></p><p>チャット インターフェースが表示されるはずです。次のサンプルプロンプトを試してみてください。</p><ul><li><p>「レブロン・ジェームズとステフィン・カリーを比較」</p></li><li><p>「ジェイソン・テイタムとルカ・ドンチッチのどちらを選ぶべきでしょうか？」</p></li></ul><p></p><h3><strong>次はエージェントのさらなるインテリジェント化</strong></h3><p>アシスタントをよりエージェント的にし、推奨事項をより洞察力のあるものにするために、次のイテレーションでいくつかの重要なアップグレードを追加する予定です。</p><p></p><p><strong>NBAニュースのセマンティック検索</strong></p><p>プレーヤーのパフォーマンスに影響を与える要因は数多くありますが、その多くは生の統計には表示されません。負傷報告、ラインナップの変更、さらには試合後の分析などは、ニュース記事でしか見つけることができません。この追加のコンテキストを捉えるために、エージェントが関連する NBA の記事を取得し、その内容を推奨事項に組み込めるよう、セマンティック検索機能を追加します。</p><p></p><p><strong>Elasticsearch MCPサーバーによる動的検索</strong></p><p>MCP (モデル コンテキスト プロトコル) は、エージェントがデータ ソースに接続する方法の標準として急速に普及しつつあります。検索ロジックを Elasticsearch MCP サーバーに移行します。これにより、エージェントは、私たちが提供する定義済みの検索機能に頼るのではなく、動的にクエリを構築できるようになります。これにより、より自然な言語ワークフローを使用できるようになり、すべての検索クエリを手動で記述する必要性が軽減されます。Elasticsearch MCP サーバーとエコシステムの現在の状態の詳細については、<a href="https://www.elastic.co/search-labs/blog/mcp-current-state">こちらを</a>ご覧ください。</p><p></p><p>これらの変更はすでに進行中ですので、お楽しみに!</p><h3><strong>まとめ</strong></h3><p></p><p>このブログでは、JavaScript、Mastra、Elasticsearch を使用して、ファンタジー バスケットボール チームに合わせた推奨事項を提供するエージェント RAG アシスタントを構築しました。取り上げた内容:</p><ul><li><p><strong>エージェント RAG の基礎</strong>と、AI エージェントの自律性と RAG を効果的に使用するツールを組み合わせることで、より繊細で動的なエージェントを実現できる方法について説明します。</p></li><li><p><strong>Elasticsearch</strong>とそのデータ ストレージ機能および強力なネイティブ集約により、それが LLM のナレッジ ベースとして優れたパートナーとなる理由について説明します。</p></li><li><p><strong>Mastra</strong>フレームワークと、それが JavaScript エコシステムの開発者にとってこれらのエージェントの構築をどのように簡素化するかについて説明します。</p></li></ul><p>あなたがバスケットボールの熱狂的なファンであっても、AI エージェントの構築方法を検討している方であっても、あるいは私のようにその両方であっても、このブログが、始めるための基礎を提供できれば幸いです。完全なリポジトリは<a href="https://github.com/jdarmada/nba-ai-assistant-js">GitHub</a>で入手できます。自由にクローンして改良してください。さあ、ファンタジーリーグで優勝しましょう!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agentic-rag</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-rag</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[JavaScript]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ffd561836a4cb20/6a17f1e47b54f978588b39e4/8132ed781c1ea5d46ca244182f421ed5c721f23b-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[モデルコンテキストプロトコルを使用してエージェントをElasticsearchに接続する]]></title>
    <description><![CDATA[モデルコンテキストプロトコル サーバーを使用して、Elasticsearch 内のデータとチャットしてみましょう。]]></description>
    <content:encoded><![CDATA[<p>同僚とチャットするのと同じくらい簡単にデータとやりとりできたらどうでしょうか?「先月の 500 ドルを超えるすべての注文を表示してください」や「5 つ星のレビューを最も多く獲得した製品はどれですか」と尋ねるだけで、クエリを必要とせずに、即座に正確な回答が得られることを想像してみてください。</p><p>モデルコンテキストプロトコル (MCP) によりこれが可能になります。会話型 AI をデータベースや外部 API にシームレスに接続し、複雑なリクエストを自然な会話に変換します。現代の LLM は言語の理解に優れていますが、その真の可能性は現実世界のシステムと統合されたときに発揮されます。MCP は両者の間のギャップを埋め、データのやりとりをより直感的かつ効率的にします。</p><p>この投稿では、次の内容について説明します。</p><ul><li><p>MCPアーキテクチャ – 内部の仕組み</p></li><li><p>Elasticsearchに接続されたMCPサーバーの利点</p></li><li><p><a href="https://github.com/elastic/mcp-server-elasticsearch">Elasticsearch を活用した MCP サーバーの</a>構築</p></li></ul><p>これからはエキサイティングな時代が来ます!MCP と Elastic スタックの統合により、情報の操作方法が変わり、複雑なクエリが日常会話と同じくらい直感的になります。</p><h2>モデルコンテキストプロトコル</h2><p>Anthropic が開発した<a href="https://modelcontextprotocol.io/introduction">モデル コンテキスト プロトコル</a>(MCP) は、安全な双方向チャネルを通じて AI モデルを外部データ ソースに接続するオープン スタンダードです。これは、会話のコンテキストを維持しながら外部システムにリアルタイムでアクセスするという、AI の主要な制限を解決します。</p><h3>MCPアーキテクチャ</h3><p>モデルコンテキストプロトコルアーキテクチャは、次の 2 つの主要コンポーネントで構成されます。</p><ul><li><p><strong>MCP クライアント</strong>– ユーザーに代わって情報を要求したりタスクを実行したりする AI アシスタントとチャットボット。</p></li><li><p><strong>MCP サーバー</strong>– 関連情報を取得したり、要求されたアクション (外部 API の呼び出しなど) を実行したりするデータ リポジトリ、検索エンジン、および API。</p></li></ul><p>MCPサーバーは、クライアントに対して主に4つの機能を提供します。</p><ul><li><p><strong>リソース</strong>- LLM インタラクションのコンテキストとして取得および使用できる構造化されたデータ、ドキュメント、およびコンテンツ。これにより、AI アシスタントはデータベース、検索インデックス、その他のソースから関連情報にアクセスできるようになります。</p></li><li><p><strong>ツール</strong>- LLM が外部システムと対話したり、計算を実行したり、実際のアクションを実行できるようにする実行可能関数。これらのツールは、テキスト生成を超えて AI 機能を拡張し、アシスタントがワークフローをトリガーしたり、API を呼び出したり、データを動的に操作したりできるようにします。</p></li><li><p><strong>プロンプト</strong>- 一般的な LLM インタラクションを標準化して共有するための再利用可能なプロンプト テンプレートとワークフロー。</p></li><li><p><strong>サンプリング</strong>- セキュリティとプライバシーを維持しながら、高度なエージェント動作を可能にするために、クライアントを通じて LLM 完了を要求します。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfe82754551bb187a/6a17f7ec6864a43e71b6895d/bef5178133391e96e3d66ae634e41a85712a33a9-2345x1620.png" alt="モデルコンテキストプロトコル（MCP）アーキテクチャ" /><h2>MCPサーバー + Elasticsearch</h2><p></p><p>従来の検索拡張生成 (RAG) システムはユーザーのクエリに基づいてドキュメントを検索しますが、MCP はさらに一歩進んで、AI エージェントがリアルタイムでタスクを動的に構築して実行できるようにします。これにより、ユーザーは次のような自然言語の質問をすることができます。</p><p></p><ul><li><p>「先月の 500 ドルを超える注文をすべて表示してください。」</p></li><li><p>「5 つ星のレビューを最も多く獲得した製品はどれですか?」</p></li></ul><p></p><p>クエリを 1 つも書かなくても、即座に正確な回答が得られます。</p><p></p><p>MCP は以下を通じてこれを実現します。</p><ul><li><p>動的なツール選択 - エージェントは、ユーザーの意図に基づいて、MCP サーバー経由で公開される適切なツールをインテリジェントに選択します。一般的に、「よりスマートな」LLM は、状況に応じて適切な議論に基づいて適切なツールを選択するのが得意です。</p></li><li><p>双方向通信 – エージェントとデータソースは情報をスムーズに交換し、必要に応じてクエリを絞り込みます（例：最初にインデックス マッピングを検索し、その後で ES クエリを構築します。</p></li><li><p>マルチツール オーケストレーション - ワークフローは複数の MCP サーバーのツールを同時に活用できます。</p></li><li><p>永続的なコンテキスト - エージェントは以前のやり取りを記憶し、会話全体の継続性を維持します。</p></li></ul><p>Elasticsearch に接続された MCP サーバーは、強力なリアルタイム検索アーキテクチャを実現します。AI エージェントは、オンデマンドで Elasticsearch データを探索、クエリ、分析できます。シンプルなチャット インターフェースを通じてデータを検索できます。</p><p>MCP は、データの取得だけでなく、アクションも可能にします。他のツールと統合してワークフローをトリガーし、プロセスを自動化し、分析システムに洞察を提供します。MCP は、検索と実行を分離することで、AI を活用したアプリケーションの柔軟性と最新性を維持し、エージェント ワークフローにシームレスに統合します。</p><h2>ハンズオン: Elasticsearch データとチャットできる MCP サーバー</h2><p>MCP サーバー経由で Elasticsearch と対話するには、少なくとも次の機能が必要です。</p><ul><li><p>インデックスを取得する</p></li><li><p>マッピングを取得する</p></li><li><p>Elasticsearch のクエリ DSL を使用して検索を実行する</p></li></ul><p>私たちのサーバーは TypeScript で記述されており、公式の<a href="https://github.com/modelcontextprotocol/typescript-sdk">MCP TypeScript SDK</a>を使用します。セットアップには、MCP クライアントが組み込まれているため、Claude デスクトップ アプリ (無料版で十分です) をインストールすることをお勧めします。当社の MCP サーバーは基本的に、MCP ツールを通じて公式の<a href="https://www.elastic.co/jp/guide/en/elasticsearch/client/javascript-api/current/index.html">JavaScript Elasticsearch クライアント</a>を公開します。</p><p>まず、Elasticsearch クライアントと MCP サーバーを定義します。</p> const esClient = new Client({
    node: url,
    auth: {
      apiKey: apiKey,
    },
  });

  const server = new McpServer({
    name: "elasticsearch-mcp-server",
    version: "0.1.0",
  });<p>Elasticsearch と対話できる次の MCP サーバー ツールを使用します。</p><ul><li><p><strong>インデックスの一覧表示</strong>( <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L46">list_indices</a> ): このツールは、利用可能なすべての Elasticsearch インデックスを取得し、インデックス名、ヘルス ステータス、ドキュメント数などの詳細を提供します。</p></li><li><p><strong>マッピングの取得</strong>( <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L94">get_mappings</a> ): このツールは、指定された Elasticsearch インデックスのフィールド マッピングを取得し、ユーザーが保存されているドキュメントの構造とデータ型を理解するのに役立ちます。</p></li><li><p><strong>検索</strong>( <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L147">search</a> ): このツールは、提供されたクエリ DSL を使用して Elasticsearch 検索を実行します。テキスト フィールドのハイライトが自動的に有効になり、関連する検索結果を簡単に識別できるようになります。</p></li></ul><p>完全な Elasticsearch MCP サーバーの実装は<a href="https://github.com/elastic/mcp-server-elasticsearch">、elastic/mcp-server-elasticsearch</a>リポジトリで入手できます。</p><h4>インデックスとチャット</h4><p>Elasticsearch MCP サーバーを設定して、「先月からの 500 ドルを超えるすべての注文を検索する」など、データに関する自然言語の質問をする方法を見てみましょう。</p><p><strong>Claudeデスクトップアプリを構成する</strong></p><ul><li><p>Claudeデスクトップアプリを開く</p></li><li><p>設定 &gt; 開発者 &gt; MCP サーバーに移動します</p></li><li><p>「設定の編集」をクリックして、この設定を<code>claude_desktop_config.json</code>に追加します。</p></li></ul>{
  "mcpServers": {
    "Elasticsearch MCP Server": {
      "command": "npx",
      "args": [
        "-y",
        "@elastic/mcp-server-elasticsearch"
      ],
      "env": {
        "ES_URL": "",
        "ES_API_KEY": ""
      }
    }
  }
}<p>注: このセットアップでは、Elastic が公開した<a href="https://www.npmjs.com/package/@elastic/mcp-server-elasticsearch">@elastic/mcp-server-elasticsearch</a> npm パッケージを利用します。ローカルで開発する場合は、Elasticsearch MCP サーバーの起動に関する詳細を<a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md">こちらで</a>確認できます。</p><p><strong>Elasticseachインデックスを作成する</strong></p><ul><li><p>このデモの「注文」インデックスを作成するために、<a href="https://gist.github.com/jedrazb/60e9400cbe40addfd9e4337749c28431">サンプルデータ</a>を使用できます。</p></li><li><p>これにより、「先月からの500ドル以上のすべての注文を検索する」などのクエリを試すことができます。</p></li></ul><p><strong>使い始める</strong></p><ul><li><p>Claudeデスクトップアプリで新しい会話を開く</p></li><li><p>MCPサーバーは自動的に接続します</p></li><li><p>Elasticsearch データについて質問してみましょう。</p></li></ul><p>自然言語を使用して Elasticsearch データをクエリすることがいかに簡単かを確認するには、このデモをご覧ください。</p><h4>これは次のように機能します。</h4><p>「先月からの 500 ドルを超えるすべての注文を検索する」と要求されると、LLM は指定された制約を使用して Elasticsearch インデックスを検索する意図を認識します。効果的な検索を実行するために、エージェントは次のことを行います。</p><ul><li><p>インデックス名を確認します。 <code>orders</code></p></li><li><p><code>orders</code>インデックスのマッピングを理解する</p></li><li><p>インデックスマッピングと互換性のあるクエリDSLを構築し、最後に検索リクエストを実行します。</p></li></ul><p>この相互作用は次のように表すことができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt152f41bc8c3e9752/6a17f7ee6df73152df0a10cc/8875bc75745124be87deac0be666509446887de2-2345x1620.png" alt="MCPサーバー+Elasticsearchはどのように機能するのか" /><h2>まとめ</h2><p>モデル コンテキスト プロトコルは、Elasticsearch データとのやり取りを強化し、複雑なクエリの代わりに自然言語による会話を可能にします。MCP は、AI 機能とデータを連携させることで、やり取り全体を通じてコンテキストを維持する、より直感的で効率的なワークフローを作成します。</p><p>Elasticsearch MCP サーバーはパブリック npm パッケージ ( <a href="https://www.npmjs.com/package/@elastic/mcp-server-elasticsearch">@elastic/mcp-server-elasticsearch</a> ) として利用できるため、開発者にとって統合が簡単になります。最小限のセットアップで、チームはデータの探索、ワークフローのトリガー、簡単な会話による分析情報の取得を開始できます。</p><p>自分で体験する準備はできましたか?今すぐ<a href="https://github.com/elastic/mcp-server-elasticsearch">Elasticsearch MCP サーバー</a>を試して、データとのチャットを始めましょう。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch</guid>
    <category><![CDATA[エージェント型AI]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltce68a95c633809ae/6a17f7f0148009fa28b48915/65b378f644bd13e3edf2f108d48186f1889f546c-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 28 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch Serverlessプロジェクトを管理するAIエージェント]]></title>
    <description><![CDATA[Elasticsearch Serverless プロジェクトを簡単に管理し、プロジェクトの作成、削除、ステータス チェックを可能にする自然言語対応の AI エージェント。]]></description>
    <content:encoded><![CDATA[<h2>AIエージェントを使用してサーバーレスElasticsearchプロジェクトを管理する方法</h2><ol><li><p><strong>リポジトリのクローンを作成します。</strong> <code>git clone https://github.com/elastic/elasticsearch-labs/supporting-blog-content/serverless-ai-agent</code> <code>a</code>を使用して GitHub からツールのコードをダウンロードし、 <code>cd serverless-ai-agent</code>を使用してディレクトリに移動します。</p></li><li><p><strong>環境の設定:</strong> <code>python -m venv venv</code>を使用して仮想環境 (オプション) を作成し、アクティブ化します (Windows の場合は<code>source venv/bin/activate</code>または<code>venv\Scripts\activate</code> )。次に、 <code>pip install -r requirements.txt</code>を使用して必要な Python パッケージをインストールします。</p></li><li><p><strong>資格情報を構成する:</strong>プロジェクト ルートに<code>.env</code>ファイルを作成し、Elasticsearch API URL ( <code>ES_URL</code> )、API キー ( <code>API_KEY</code> )、リージョン ( <code>REGION</code> )、OpenAI API キー ( <code>OPENAI_API_KEY</code> ) を入力します。</p></li><li><p><strong>ツールを実行します:</strong>ターミナルで<code>python main.py</code>を実行してツールを実行します。これにより、AI エージェントが起動し、コマンドのプロンプトが表示されます。</p></li><li><p><strong>自然言語でプロジェクトを管理する:</strong> 「my\_project という名前のサーバーレス プロジェクトを作成する」、「my\_project という名前のサーバーレス プロジェクトのステータスを取得する」、「my\_project という名前のサーバーレス プロジェクトを削除する」などのわかりやすい英語のコマンドを使用してツールを操作します。AI はコマンドを解釈し、対応する機能を実行します。</p></li></ol><h2>背景</h2><p>この小さなコマンドライン ツールを<a href="https://www.elastic.co/guide/en/serverless/current/intro.html">使用すると、Serverless Elasticsearch プロジェクトを</a>わかりやすい英語で管理できます。AI (この場合は OpenAI) と対話して、ユーザーの意図を理解し、LlamaIndex を使用して適切な関数を呼び出します。</p><h3>Elasticsearch Serverless AIエージェントは何ができるのか</h3><ul><li><p><strong>プロジェクトを作成する</strong>: 新しい Serverless Elasticsearch プロジェクトを起動します。</p></li><li><p><strong>プロジェクトの削除</strong>: 既存のプロジェクトを削除します (削除後はクリーンアップされます)。</p></li><li><p><strong>プロジェクトのステータスを取得</strong>: プロジェクトの進行状況を確認します。</p></li><li><p><strong>プロジェクトの詳細を取得</strong>: プロジェクトに関するすべての重要な詳細を取得します。</p></li></ul><p><a href="https://github.com/elastic/elasticsearch-labs/tree/a65f7bc1e4a041765d1c0a45ac44b9cd9fc1589f/supporting-blog-content/serverless-ai-agent">GitHub でコードを確認してください。</a></p><h3>Elasticsearch Serverless AIエージェントの仕組み</h3><p>次のように入力すると:</p><p><em>「my_project という名前のサーバーレス プロジェクトを作成する」</em></p><p>…舞台裏ではこんなことが起こっています:</p><ul><li><p><strong>ユーザー入力とコンテキスト:</strong>自然言語コマンドが AI エージェントに送信されます。</p></li><li><p><strong>関数の説明:</strong>詳細な説明が与えられているため、AI エージェントは、create_ess_project、delete_ess_project、get_ess_project_status、get_ess_project_details などのいくつかの関数についてすでに認識しています。これらの説明は、各関数が何を実行し、どのようなパラメータが必要かを AI に伝えます。</p></li><li><p><strong>LLM 処理:</strong>クエリと関数情報が LLM に送信されます。つまり、AI は次のことを認識します。</p><ul><li><p><strong>ユーザークエリ</strong>: わかりやすい英語での指示。</p></li><li><p><strong>利用可能な機能と説明</strong>: 各ツールの機能の詳細により、適切なツールを選択できます。</p></li><li><p><strong>コンテキスト/履歴チャット情報</strong>: 会話なので、以前に話された内容を記憶します。</p></li></ul></li><li><p><strong>関数の呼び出しと応答:</strong> AI はどの関数を呼び出すかを判断し、適切なパラメーター (プロジェクト名など) を渡して、関数が実行されます。応答はわかりやすい形式で返されます。</p></li></ul><p>つまり、自然言語クエリと詳細なツール説明のリストの両方を LLM に送信して、LLM がそれを「理解」し、リクエストに適したアクションを選択できるようにします。</p><h3>AIエージェントを設定する</h3><h4>前提条件:</h4><p>AI エージェントを実行する前に、次の設定がされていることを確認してください。</p><ol><li><p><strong>Python (v3.7 以降)</strong>がインストールされています。</p></li><li><p>Elastic Cloud にセットアップされた<strong>Elasticsearch サーバーレス アカウント</strong>。</p></li><li><p>言語モデルと対話するための<strong>OpenAI アカウント</strong>。</p></li></ol><h4>手順:</h4><p><strong>1. リポジトリをクローンします。</strong></p>git clone https://github.com/elastic/elasticsearch-labs/supporting-blog-content/serverless-ai-agent
cd serverless-ai-agent<p><strong>2. 仮想環境を作成する (オプションですが推奨):</strong>環境関連の問題に直面している場合は、分離のために仮想環境を設定できます。</p>python -m venv venv
source venv/bin/activate  # On Windows, use venv\Scripts\activate<p><strong>3. 依存関係をインストールします。</strong>次のコマンドを実行して、必要な依存関係がすべてインストールされていることを確認します。</p>pip install -r requirements.txt<p><strong>4. 環境を設定する:</strong> .env を作成するプロジェクト ルートに次の変数を含むファイルを作成します。以下に、役に立つサンプルの<code>.env.example</code>ファイルを示します。</p>ES_URL=your_elasticsearch_api_url  # The base URL for your Elasticsearch service (e.g., https://your-cluster-id.es.region.aws.elastic-cloud.com)
API_KEY=your_elasticsearch_api_key  # Your API key for Elasticsearch
REGION=your_region  # Example: aws-eu-west-1
OPENAI_API_KEY=your_openai_api_key  # Your OpenAI API key<p><code>ES_URL</code> 、 <code>API_KEY</code> 、 <code>OPENAI_API_KEY</code>の値が正しいことを確認してください。API キーはそれぞれのサービス ダッシュボードで確認できます。</p><p><strong>5. プロジェクト ファイル:</strong>ツールは、 <code>projects.json</code>ファイルを使用してプロジェクト マッピング (プロジェクト名とその詳細) を保存します。このファイルが存在しない場合は自動的に作成されます。</p><h3>AIエージェントの実行</h3>python main.py<p>次のようなプロンプトが表示されます。</p>Welcome to the Serverless Project AI Agent Tool!
You can ask things like:
 - 'Create a serverless project named my_project'
 - 'Delete the serverless project named my_project'
 - 'Get the status of the serverless project named my_project'
 - 'Get the details of the serverless project named my_project'<p>コマンドを入力すると、AI エージェントが魔法のように動作します。完了したら、 <code>exit</code>または<code>quit</code>と入力して終了します。</p><h3>さらに詳しい情報</h3><ul><li><p><strong>LLM 統合</strong>: LLM には、クエリと利用可能な各機能の詳細な説明の両方が提供されます。これにより、コンテキストを理解し、たとえば、 <code>create_ess_project</code>を呼び出すか<code>delete_ess_project</code>を呼び出すかを決定するのに役立ちます。</p></li><li><p><strong>ツールの説明</strong>: 各関数ツール (FunctionTool.from_defaults を使用して作成)親切な説明があります。この説明は LLM に送信されるプロンプトに含まれているため、LLM は利用可能なアクションと各アクションで期待される内容を「認識」します。</p></li><li><p><strong>永続性</strong>: プロジェクトとその詳細はprojects.jsonに保存されます。毎回情報を再入力する必要がなくなります。</p></li><li><p><strong>詳細ログ</strong>: エージェントは詳細モードに設定されており、デバッグや、命令が関数呼び出しにどのように変換されるかを確認するのに最適です。</p></li></ul><h3>Elasticsearch AIエージェントの利用例</h3>python main.py 

Welcome to the Serverless Project AI Agent Tool!

You can ask things like:
 - 'Create a serverless project named my_project'
 - 'Delete the serverless project named my_project'
 - 'Get the status of the serverless project named my_project'
 - 'Get the details of the serverless project named my_project'

User: Create a serverless project named my-february-test
Added user message to memory: Create a serverless project named my-february-test

=== Calling Function ===
Calling function: create_ess_project with args: {"project_name":"my-february-test"}
========================


Agent: The serverless Elasticsearch project named "my-february-test" has been created successfully. Here are the details:

- **Project ID:** &lt;&gt;&gt;
- **Elasticsearch Endpoint:** [https://my-february-test-ccc399.es.eu-west-1.aws.qa.elastic.cloud](https://my-february-test-ccc399.es.eu-west-1.aws.qa.elastic.cloud)
- **Kibana Endpoint:** [https://my-february-test-ccc399.kb.eu-west-1.aws.qa.elastic.cloud](https://my-february-test-ccc399.kb.eu-west-1.aws.qa.elastic.cloud)

User: get my-february-test status
Added user message to memory: get my-february-test status

=== Calling Function ===
Calling function: get_ess_project_status with args: {"project_name":"my-february-test"}
========================

Agent: The status of the "my-february-test" project is currently **initializing**.

User: get my-february-test info
Added user message to memory: get my-february-test info

=== Calling Function ===
Calling function: get_ess_project_details with args: {"project_name":"my-february-test"}
========================

Agent: Here are the details for the "my-february-test" project:

- **Project Name:** my-february-test
- **Alias:** my-february-test-ccc399
- **Cloud ID:** ""
- **Project ID:** ""
- **Region ID:** aws-eu-west-1
- **Created At:** 2025-02-12T15:42:00.530177619Z
- **Created By:** 571213384
- **Organization ID:** ""
- **Type:** Elasticsearch
- **Optimized For:** General Purpose
- **Search Lake:**
  - **Boost Window:** 7
  - **Search Power:** 100
- **Endpoints:**
  - **Elasticsearch:** https://my-february-test-ccc399.es.eu-west-1.aws.qa.elastic.cloud
  - **Kibana:** https://my-february-test-ccc399.kb.eu-west-1.aws.qa.elastic.cloud
- **Credentials:**
  - **Username:** ""
  - **Password:** ""

Please ensure to keep the credentials secure.

User: please delete the my-february-test project
Added user message to memory: please delete the my-february-test project

=== Calling Function ===
Calling function: delete_ess_project with args: {"project_name":"my-february-test"}
========================

Agent: The "my-february-test" project has been deleted successfully.<p></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/serverless-elasticsearch-ai-agent</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/serverless-elasticsearch-ai-agent</guid>
    <category><![CDATA[Elastic Cloud Serverless]]></category>
    <category><![CDATA[エージェント型AI]]></category>
    <dc:creator><![CDATA[Fram Souza]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt88526af16bafdb7c/6a17d7807f6f15825dc0998d/d11e1ba058784ec92b8953fb8db62e1bad21c210-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 04 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>