TECH PLAY

サイオステクノロジー((DXSL))

サイオステクノロジー((DXSL)) の技術ブログ

48

目次 はじめに 開発環境 事前準備 メインデータの用意(Sample web logs) 国マスタのインポート Lookup Index への変換 【実践】ES|QL での集計比較 Lookup Join なし(国コードのみ) Lookup Join あり(国名を結合) まとめ 参考資料 添付資料:country_list.csv はじめに これまで Elasticsearch で「メインのインデックスに、別インデックスの情報を紐付けて表示したい」場合、Enrich Processor を使用して、取り込み(Ingest)フェーズであらかじめデータを結合しておくのが一般的でした。 しかし、この方法ではデータの更新のたびに Enrich Policy の再実行が必要になるなど、運用の手間がかかる側面がありました。 最新の Elasticsearch では、ES|QL の LOOKUP JOIN 機能が登場し、クエリ実行時にリアルタイムで別インデックスの情報を参照できるようになりました。本記事では、この便利な新機能の使い方を紹介します。 開発環境 Elasticsearch / Kibana: v9.2.4 Chrome ※注意事項: LOOKUP JOIN … ON … == … の記法は、Elasticsearch 9.2 時点で Preview です。 事前準備 メインデータの用意(Sample web logs) 今回は Kibana に標準で用意されている「Sample web logs」を使用します。 まだ取り込んでいない場合は、Kibana ホームの「Add data」から Sample web logs を追加してください。 参考: https://elastic.sios.jp/blog/elasticsearch-esql-introduction-log-analysis/ 国マスタのインポート この記事の末尾に記載している country_list.csv を使用して、マスタデータを作成します。 ※筆者は、Webブラウザに Chrome を使用しました。 Kibana の Home > Upload a file を開きます。 country_list.csv をアップロードし、設定を以下のように変更します。 Index name: country_list Advanced options > Data view: off(マスタなので Data view は不要です) [Import] をクリックします。 Lookup Index への変換 ES|QL の Lookup Join を利用するには、通常のインデックスを「Lookup 用」に変換する必要があります。 Stack Management > Index Management から country_list を選択します。 2. Manage ボタンから Convert to lookup index をクリックします。 3. Lookup index name に lookup-country_list と入力し、Convert を実行します。 Note: この操作により、インメモリで高速に結合可能な特殊なインデックスが生成されます。元の country_list もそのまま残ります。 【実践】ES|QL での集計比較 Lookup Join なし(国コードのみ) まずは、通常の集計を行ってみます。 Kibana の Discover を開き、上部の [Try ES|QL] をクリックします。 以下のクエリを入力して実行します。 FROM kibana_sample_data_logs | STATS bytes_sum = sum(bytes) BY geo.dest | KEEP geo.dest, bytes_sum | SORT bytes_sum DESC | LIMIT 8 集計対象の期間は、適当に調整してください。下記の例では、Last 24 hours を指定しています。 結果: geo.dest 項目に CN や US といった 2 桁の国コードが表示されます。これだけでは、直感的にどの国か分かりにくいですよね。 Lookup Join あり(国名を結合) 次に、LOOKUP JOIN を使って国マスタから国名を引っ張ってきます。 FROM kibana_sample_data_logs | STATS bytes_sum = sum(bytes) BY geo.dest | LOOKUP JOIN lookup-country_list ON geo.dest == country_code | KEEP country_name, bytes_sum | SORT bytes_sum DESC | LIMIT 8 結果: country_name が結合され、「CHINA」「UNITED STATES」といった具体的な国名が表示されるようになります! まとめ ES|QL の LOOKUP JOIN を使うメリットは以下の通りです。 運用の簡略化: Enrich Policy を管理・更新する手間が省ける。 直感的な記述: SQL の JOIN に近い感覚で、クエリ内で動的に結合できる。 柔軟性: 必要なときだけマスタ情報を付与できる。 現在は Preview 機能ですが、将来的に Elasticsearch でのログ分析やレポート作成において、欠かせない機能になるはずです。 参考資料 Elastic公式: LOOKUP JOIN command https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join 添付資料:country_list.csv 国マスタのCSVです。 ※このデータは、 https://www.e-tax.nta.go.jp/toiawase/qa/crs/countrycode.htm を元に生成したものです。 country_code,country_name AF,AFGHANISTAN AX,ALAND ISLANDS AL,ALBANIA DZ,ALGERIA AS,AMERICAN SAMOA AD,ANDORRA AO,ANGOLA AI,ANGUILLA AQ,ANTARCTICA AG,ANTIGUA AND BARBUDA AR,ARGENTINA AM,ARMENIA AW,ARUBA AU,AUSTRALIA AT,AUSTRIA AZ,AZERBAIJAN BS,BAHAMAS BH,BAHRAIN BD,BANGLADESH BB,BARBADOS BY,BELARUS BE,BELGIUM BZ,BELIZE BJ,BENIN BM,BERMUDA BT,BHUTAN BO,"BOLIVIA, PLURINATIONAL STATE OF" BQ,"BONAIRE, SINT EUSTATIUS AND SABA" BA,BOSNIA AND HERZEGOVINA BW,BOTSWANA BV,BOUVET ISLAND BR,BRAZIL IO,BRITISH INDIAN OCEAN TERRITORY BN,BRUNEI DARUSSALAM BG,BULGARIA BF,BURKINA FASO BI,BURUNDI KH,CAMBODIA CM,CAMEROON CA,CANADA CV,CABO VERDE KY,CAYMAN ISLANDS CF,CENTRAL AFRICAN REPUBLIC TD,CHAD CL,CHILE CN,CHINA CX,CHRISTMAS ISLAND CC,COCOS (KEELING) ISLANDS CO,COLOMBIA KM,COMOROS CG,CONGO CD,"CONGO, THE DEMOCRATIC REPUBLIC OF THE" CK,COOK ISLANDS CR,COSTA RICA CI,COTE D'IVOIRE HR,CROATIA CU,CUBA CW,CURACAO CY,CYPRUS CZ,CZECHIA DK,DENMARK DJ,DJIBOUTI DM,DOMINICA DO,DOMINICAN REPUBLIC EC,ECUADOR EG,EGYPT SV,EL SALVADOR GQ,EQUATORIAL GUINEA ER,ERITREA EE,ESTONIA ET,ETHIOPIA FK,FALKLAND ISLANDS (MALVINAS) FO,FAROE ISLANDS FJ,FIJI FI,FINLAND FR,FRANCE GF,FRENCH GUIANA PF,FRENCH POLYNESIA TF,FRENCH SOUTHERN TERRITORIES GA,GABON GM,GAMBIA GE,GEORGIA DE,GERMANY GH,GHANA GI,GIBRALTAR GR,GREECE GL,GREENLAND GD,GRENADA GP,GUADELOUPE GU,GUAM GT,GUATEMALA GG,GUERNSEY GN,GUINEA GW,GUINEA-BISSAU GY,GUYANA HT,HAITI HM,HEARD ISLAND AND MCDONALD ISLANDS VA,HOLY SEE (VATICAN CITY STATE) HN,HONDURAS HK,HONG KONG HU,HUNGARY IS,ICELAND IN,INDIA ID,INDONESIA IR,"IRAN, ISLAMIC REPUBLIC OF" IQ,IRAQ IE,IRELAND IM,ISLE OF MAN IL,ISRAEL IT,ITALY JM,JAMAICA JP,JAPAN JE,JERSEY JO,JORDAN KZ,KAZAKHSTAN KE,KENYA KI,KIRIBATI KP,"KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF" KR,"KOREA, REPUBLIC OF" KW,KUWAIT KG,KYRGYZSTAN LA,LAO PEOPLE'S DEMOCRATIC REPUBLIC LV,LATVIA LB,LEBANON LS,LESOTHO LR,LIBERIA LY,LIBYA LI,LIECHTENSTEIN LT,LITHUANIA LU,LUXEMBOURG MO,MACAO MK,NORTH MACEDONIA MG,MADAGASCAR MW,MALAWI MY,MALAYSIA MV,MALDIVES ML,MALI MT,MALTA MH,MARSHALL ISLANDS MQ,MARTINIQUE MR,MAURITANIA MU,MAURITIUS YT,MAYOTTE MX,MEXICO FM,"MICRONESIA, FEDERATED STATES OF" MD,"MOLDOVA, REPUBLIC OF" MC,MONACO MN,MONGOLIA ME,MONTENEGRO MS,MONTSERRAT MA,MOROCCO MZ,MOZAMBIQUE MM,MYANMAR NA,NAMIBIA NR,NAURU NP,NEPAL NL,NETHERLANDS NC,NEW CALEDONIA NZ,NEW ZEALAND NI,NICARAGUA NE,NIGER NG,NIGERIA NU,NIUE NF,NORFOLK ISLAND MP,NORTHERN MARIANA ISLANDS NO,NORWAY OM,OMAN PK,PAKISTAN PW,PALAU PS,"PALESTINE, STATE OF" PA,PANAMA PG,PAPUA NEW GUINEA PY,PARAGUAY PE,PERU PH,PHILIPPINES PN,PITCAIRN PL,POLAND PT,PORTUGAL PR,PUERTO RICO QA,QATAR RE,REUNION RO,ROMANIA RU,RUSSIAN FEDERATION RW,RWANDA BL,SAINT BARTHELEMY SH,"SAINT HELENA, ASCENSION AND TRISTAN DA CUNHA" KN,SAINT KITTS AND NEVIS LC,SAINT LUCIA MF,SAINT MARTIN (FRENCH PART) PM,SAINT PIERRE AND MIQUELON VC,SAINT VINCENT AND THE GRENADINES WS,SAMOA SM,SAN MARINO ST,SAO TOME AND PRINCIPE SA,SAUDI ARABIA SN,SENEGAL RS,SERBIA SC,SEYCHELLES SL,SIERRA LEONE SG,SINGAPORE SX,SINT MAARTEN (DUTCH PART) SK,SLOVAKIA SI,SLOVENIA SB,SOLOMON ISLANDS SO,SOMALIA ZA,SOUTH AFRICA GS,SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS SS,SOUTH SUDAN ES,SPAIN LK,SRI LANKA SD,SUDAN SR,SURINAME SJ,SVALBARD AND JAN MAYEN SZ,ESWATINI SE,SWEDEN CH,SWITZERLAND SY,SYRIAN ARAB REPUBLIC TW,"TAIWAN, PROVINCE OF CHINA" TJ,TAJIKISTAN TZ,"TANZANIA, UNITED REPUBLIC OF" TH,THAILAND TL,TIMOR-LESTE TG,TOGO TK,TOKELAU TO,TONGA TT,TRINIDAD AND TOBAGO TN,TUNISIA TR,TURKEY TM,TURKMENISTAN TC,TURKS AND CAICOS ISLANDS TV,TUVALU UG,UGANDA UA,UKRAINE AE,UNITED ARAB EMIRATES GB,UNITED KINGDOM OF GREAT BRITAIN AND NORTHERN IRELAND US,UNITED STATES UM,UNITED STATES MINOR OUTLYING ISLANDS UY,URUGUAY UZ,UZBEKISTAN VU,VANUATU VE,"VENEZUELA, BOLIVARIAN REPUBLIC OF" VN,VIET NAM VG,"VIRGIN ISLANDS, BRITISH" VI,"VIRGIN ISLANDS, U.S." WF,WALLIS AND FUTUNA EH,WESTERN SAHARA YE,YEMEN ZM,ZAMBIA ZW,ZIMBABWE XK,KOSOVO The post ES|QL の Lookup Join でインデックス結合が驚くほど簡単に first appeared on Elastic Portal .
アバター
情報源: Elastic公式サイト – ブラジル警察機関の顧客事例 目次 はじめに 導入前の課題 時間のかかるデータベース検索 スケーラビリティの問題 Elasticsearch導入によるソリューション ベクトルデータベースを活用した顔認証システム カスタムモバイル・ウェブアプリケーションとElasticsearchの連携 導入効果 検索時間の大幅な短縮 犯罪者の迅速な特定と逮捕 市民サービスの向上 現場での捜査活動の強化 技術的アーキテクチャの特徴 大規模データ処理能力 高速ベクトル検索 まとめ はじめに 300万人以上の住民を抱えるブラジルの大都市圏において、警察機関が最先端の技術を活用した捜査システムを導入しました。Elasticsearchのベクトルデータベースを中心とした顔認証技術により、犯罪捜査の効率が大幅に改善し、市民の安全確保に大きく貢献しています。本記事では、この取り組みについてご紹介します。 ※ベクトルデータベースとは、画像や文章の特徴を数値(ベクトル)として保存し、似ているものを高速に探せる仕組みです。 導入前の課題 時間のかかるデータベース検索 導入前、警察官は膨大なデータベースを検索するために 数時間から数日 を要していました。このデータベースには、文書、写真、動画など、数十億件もの情報が含まれており、効率的な検索システムの構築が急務でした。 本プロジェクトを担当した技術責任者は次のように語っています、「警察官たちはデータベースの検索に多くの時間を費やし、本来の犯罪防止や現場対応向上という任務から離れてしまっていました。私たちは検索時間を数分、あるいは数秒にまで短縮する必要があったのです」 スケーラビリティの問題 都市の成長と共に、データ量は増加の一途を辿っていました。数百万件の検索クエリと数テラバイトの写真、文書、デジタルメディアを処理できる、スケーラブルなアーキテクチャが求められていました。 Elasticsearch導入によるソリューション ベクトルデータベースを活用した顔認証システム ブラジルの警察機関は、 Elasticsearchのベクトルデータベース を顔認証技術の基盤として採用しました。このシステムにより、警察官は現場で撮影した写真を使って、迅速かつ正確にデータベース検索を実行できるようになりました。 カスタムモバイル・ウェブアプリケーションとElasticsearchの連携 ブラジルの公共安全組織は、警察官が簡単に写真を撮影・アップロードできるよう、専用のモバイルアプリケーションとウェブアプリケーションを開発しました。このアプリと画像データベースの橋渡しをするために、強力なベクトル検索機能を持つElasticsearchが採用されています。 導入効果 検索時間の大幅な短縮 以前は数時間から数日かかっていたデータベース検索が、現在では 数分から数秒 で完了するようになりました。これにより、警察官は迅速な意思決定を行い、事件現場でより効果的に対応できるようになっています。 犯罪者の迅速な特定と逮捕 導入初期段階から、顕著な成果が現れています。従来のシステムでは、データベース検索に時間がかかりすぎるため、その間に容疑者が現場から逃走してしまうケースがありました。しかし、Elasticsearchの導入後は、現場で撮影した写真を使って数秒から数分で容疑者を特定できるようになり、その場で逮捕に至るケースが増えています。 市民サービスの向上 顔認証技術は、犯罪捜査だけでなく、市民支援にも活用されています。 印象的な事例: ある警察官が、約2週間行方不明になっていたアルツハイマー病の高齢男性を発見しました。警察官が写真を撮影し、行方不明者データベースで検索したところ、すぐに男性の名前と連絡先が判明しました。 技術責任者によると、「ご家族は非常に喜び、ほっとされています。数日間希望を持って待っていたご家族に、この良い知らせを伝えることができました。」 また、犯罪被害者の迅速な身元確認により、遺族や愛する人々に確実な情報と心の安らぎを提供することも可能になっています。 現場での捜査活動の強化 Elasticsearchの導入により、警察官はコンピュータの前で過ごす時間が減り、地域社会での重要な捜査活動により多くの時間を割けるようになりました。これは、犯罪予防と公共の安全において、極めて重要な改善です。 技術的アーキテクチャの特徴 大規模データ処理能力 Elasticsearchのベクトルデータベースを採用したアーキテクチャは、以下の能力を備えています。 導入規模: 13ノードで構成される大規模なElasticsearchベクトルデータベース 6テラバイトの膨大なデータを保管 このシステムは、数百万件の検索クエリと数テラバイトの写真、文書、デジタルメディアを処理できる高いスケーラビリティと拡張性を実現しています。 高速ベクトル検索 ベクトル検索技術により、画像の特徴を数値化し、類似画像を高速で検索することが可能になっています。これが、顔認証システムの高速化と精度向上の鍵となっています。 まとめ ブラジルの警察機関によるElasticsearchの導入事例は、最先端のテクノロジーが公共安全の分野でどのように活用されているかを示す優れた例です。これは、決して特別な国・特別な事情だから成立したわけではありません。同じ構造の課題は、日本の自治体や公共機関にも数多く存在します。 たとえば、日本では次のようなデータが日々蓄積されています。 防犯カメラの画像・動画データ 行方不明者や高齢者に関する記録 災害時の被災者情報、安否確認情報 住民対応の履歴、問い合わせ記録 これらは「量が多い」「形式がバラバラ」「必要なときにすぐ探せない」という共通の課題を抱えています。 Elasticsearchを検索基盤として使うと、これらの バラバラなデータを一元的に検索・分析 できるようになります。 テキスト(記録・報告書・メモ) ログ(システム・アクセス履歴) 画像や動画から抽出した特徴データ を1つの検索エンジンでまとめて探せるため、 迅速な横断検索 が可能になります。これは日本の自治体、警察、消防、防災部門にとっても非常に現実的で、かつ導入効果を説明しやすい価値といえます。 本記事は、Elastic社の公式カスタマー事例を基に作成されました。詳細については、 Elastic公式サイト をご覧ください。 The post 警察機関におけるElasticsearch活用事例 first appeared on Elastic Portal .
アバター
目次 はじめに 再帰チャンキング (Recursive Chunking) とは 概略 分割のイメージ Recursive Chunkingによる分割結果 参考URL 実践:インデキシング 1. 準備 2. Inference Endpoint の作成 3. インデックスの作成 4. マッピングの追加 5. ドキュメントの取り込み 6. データの反映 実践:検索と結果検証 ケース1. 表データの検索 考察 比較:Sentence Chunking の場合 ケース2. 階層が深いブロックの検索 比較:Word Chunking の場合 採用時の重要な注意点 まとめ はじめに RAG(Retrieval-Augmented Generation)や検索アプリケーションの構築において、Markdown文書の「チャンキング戦略」に頭を悩ませたことはありませんか? 従来、Markdown文書に対して単純な word(単語数)や sentence(文)単位でのチャンキングを行うと、見出しと本文が分離してしまったり、文脈が複数のブロックに分断されたりするケースが多々ありました。その結果、検索結果にノイズが含まれ、回答精度の低下を招く要因となっていました。 Elasticsearch 8.19.0 および 9.1.0 では、この課題を解決する新たな戦略として 再帰チャンキング(Recursive Chunking) が追加されました。 本記事では、この再帰チャンキングの仕組みと、実際に有価証券報告書(Markdown形式)を用いたインデキシングおよび検索実験の結果をご紹介します。 ※補足: 本ブログで使用したスクリプトやテストデータは、下記のリポジトリで公開しています。 https://github.com/sios-elastic-tech/blogs/tree/main/2025-12-recursive-chunking 再帰チャンキング (Recursive Chunking) とは 概略 再帰チャンキングは、定義されたセパレーター(区切り文字)のリストに基づいて、文書を階層的かつ再帰的に分割する手法です。 Markdownの見出し構造(#, ##, ###…)をセパレーターとして定義することで、文書の論理構造を保ったままチャンクを生成できます。 例: "chunking_settings": { "strategy": "recursive", "max_chunk_size": 300, "separators": [ "\n# ", "\n## ", "\n### ", "\n#### ", "\n##### ", "\n###### ", "\n^(?!\\s*$).*\\n-{1,}\\n", "\n^(?!\\s*$).*\\n={1,}\\n" ] } ※ “separators” の指定は、 “separator_group” : “markdown” と書くことも可能ですが、 ここではカスタマイズ性を重視し、 “separators” を明示的に記述する形式で解説します。 分割のイメージ この戦略を用いると、Markdown文書は以下のように構造的に分割されます。 元の Markdown 文書 # 1. 企業の概況 ## 1.1. 主要な経営指標 ### 1.1.1. 売上高の推移 (本文テキスト...) ### 1.1.2. 利益の推移 (本文テキスト...) ## 1.2. 沿革 (本文テキスト...) # 2. 事業の内容 (本文テキスト...) Recursive Chunkingによる分割結果 チャンク1 # 1. 企業の概況 ## 1.1. 主要な経営指標 ### 1.1.1. 売上高の推移 (本文テキスト...) チャンク2 ### 1.1.2. 利益の推移 (本文テキスト...) チャンク3 ## 1.2. 沿革 (本文テキスト...) チャンク4 # 2. 事業の内容 (本文テキスト...) ※重要なポイント: 構造を無視した分割(例:1.1.2. の途中から始まり、1.2. の冒頭が含まれるなど)が発生しません。 見出しの階層が変わるタイミングで適切に分割されるため、各チャンクが「意味のあるまとまり」を持ちます。 参考URL Recursive chunking in Elasticsearch for structured documents - Elasticsearch Labs Learn how to configure recursive chunking in Elasticsearch with chunk size, separator groups, and custom separator lists... www.elastic.co Chunking strategies: Elasticsearch chunking & its strategies - Elasticsearch Labs Learn the fundamentals of document chunking in Elasticsearch, compare different chunking strategies, and discover how yo... www.elastic.co Elasticsearch chunking: Set up chunking for inference endpoints - Elasticsearch Labs Explore Elasticsearch chunking strategies, learn how Elasticsearch chunks text, and how to configure chunking settings f... www.elastic.co Inference integrations | Elastic Docs Elasticsearch provides a machine learning inference API to create and manage inference endpoints that integrate with ser... www.elastic.co 実践:インデキシング 実際に Elasticsearch を用いて、Markdown文書の取り込みとベクトル化を行います。 ※使用するデータについて テストデータとして、サイオス株式会社の2024年12月期の有価証券報告書を使用します。 出典: https://www.sios.com/ja/ir/news/docs/20250328houkokusho.pdf 本検証作業では、上記の PDF の内容を抜粋、改変して markdown 化したものを利用します。 https://github.com/sios-elastic-tech/blogs/blob/main/2025-12-recursive-chunking/input_data/ir_report_20250328.md 1. 準備 Elasticsearch のバージョン 8.19.0 以上、または 9.1.0 以上 (Enterprise License) (本記事での検証環境: Docker 上の Elasticsearch 9.2.2 ) 必要なプラグイン analysis-icu https://www.elastic.co/docs/reference/elasticsearch/plugins/analysis-icu analysis-kuromoji https://www.elastic.co/docs/reference/elasticsearch/plugins/analysis-kuromoji モデルのデプロイ 今回は、.multilingual-e5-small_linux-x86_64 を使用します。 参考手順: https://www.elastic.co/search-labs/jp/blog/multilingual-embedding-model-deployment-elasticsearch のステップ3 2. Inference Endpoint の作成 ドキュメント取り込み時に「チャンク分割」と「密ベクトル生成」を行うための inference endpoint を作成します。 ここで strategy: “recursive” を指定します。 参考URL: https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-elasticsearch Kibana の Dev Tools (Console) から次のリクエストを発行します。 PUT _inference/text_embedding/e5_chunk_recursive { "service": "elasticsearch", "service_settings": { "num_allocations": 1, "num_threads": 1, "model_id": ".multilingual-e5-small_linux-x86_64" }, "chunking_settings": { "strategy": "recursive", "max_chunk_size": 300, "separators": [ "\n# ", "\n## ", "\n### ", "\n#### ", "\n##### ", "\n###### " ] } } ※今回のテストデータでは、— や === による区切りは使用していないので、separators から除外してシンプルにしています。 3. インデックスの作成 日本語検索に最適化するため、kuromoji と icu_normalizer を設定したインデックスを作成します。 PUT /ir_report_2024_chunk_recursive { "settings": { "index": { "number_of_shards": 1, "number_of_replicas": 1, "refresh_interval": "3600s" }, "analysis": { "char_filter": { "ja_normalizer": { "type": "icu_normalizer", "name": "nfkc_cf", "mode": "compose" } }, "tokenizer": { "ja_kuromoji_tokenizer": { "mode": "search", "type": "kuromoji_tokenizer", "discard_compound_token": true } }, "analyzer": { "ja_kuromoji_index_analyzer": { "type": "custom", "char_filter": [ "ja_normalizer", "kuromoji_iteration_mark" ], "tokenizer": "ja_kuromoji_tokenizer", "filter": [ "kuromoji_baseform", "kuromoji_part_of_speech", "cjk_width", "ja_stop", "kuromoji_number", "kuromoji_stemmer" ] }, "ja_kuromoji_search_analyzer": { "type": "custom", "char_filter": [ "ja_normalizer", "kuromoji_iteration_mark" ], "tokenizer": "ja_kuromoji_tokenizer", "filter": [ "kuromoji_baseform", "kuromoji_part_of_speech", "cjk_width", "ja_stop", "kuromoji_number", "kuromoji_stemmer" ] } } } } } 4. マッピングの追加 作成したエンドポイント (e5_chunk_recursive) を使用するようにマッピングを定義します。 semantic_text 型を利用することで、テキスト処理とベクトル化を自動化します。 PUT /ir_report_2024_chunk_recursive/_mappings { "dynamic": false, "_source": { "excludes": [ "content.text_embedding" ] }, "properties": { "content": { "type": "text", "analyzer": "ja_kuromoji_index_analyzer", "search_analyzer": "ja_kuromoji_search_analyzer", "fields": { "text_embedding": { "type": "semantic_text", "inference_id": "e5_chunk_recursive" } } } } } 5. ドキュメントの取り込み Markdown化した有価証券報告書データを取り込みます。(以下は抜粋です) POST /ir_report_2024_chunk_recursive/_doc { "content": """ # 第1 【企業の概況】 ## 1 【主要な経営指標等の推移】 ### (1) 連結経営指標等 | 回次 | 第24期 | 第25期 | 第26期 | 第27期 | 第28期 | |:--|:--|:--|:--|:--|:--:| | 決算年月 | 2020年12月 | 2021年12月 | 2022年12月 | 2023年12月 | 2024年12月 | | 売上高(千円) | 14,841,739 | 15,725,371 | 14,420,269 | 15,889,487 | 20,561,583 | ... 当連結会計年度におきましては、個別決算において関係会社株式の減損による特別損失の計上を行うことから、利 益剰余金が大幅に減少することになり、誠に遺憾ではございますが、期末配当を無配とさせていただきたいと存じま す。 """ } 全文は、下記を参照してください。 File not found ?? sios-elastic-tech/blogs A sample code for blogs about elasticsearch. Contribute to sios-elastic-tech/blogs development by creating an account on... github.com 6. データの反映 データを検索可能にするため、リフレッシュを行います。 POST /ir_report_2024_chunk_recursive/_refresh 実践:検索と結果検証 取り込んだデータに対してセマンティック検索を行い、再帰チャンキングの効果を確認します。 ケース1. 表データの検索 クエリ: “2024年度の有給休暇取得率” GET /ir_report_2024_chunk_recursive/_search { "_source": false, "query": { "semantic": { "field": "content.text_embedding", "query": "2024年度の有給休暇取得率" } }, "highlight": { "fields": { "content.text_embedding": { "order": "score", "number_of_fragments": 1 } } } } 検索結果 """ ### (3) 指標及び目標 当社グループは、上記の「(2)①人材の多様性の確保を含む人材の育成に関する方針」及び「(2)②社内環境整備に 関する方針」について、次の指標を用いています。当該指標に関する目標及び実績は、次の通りです。 | 指標 | 2023年度実績 | 2024年度実績 | 目標 | |:--|:--|:--|:--:| | 管理職に占める女性労働者の割合 | 15.5% | 10.5% | 20%(2025年度) | | 男性労働者の育児休業取得率 | 38.5% | 100.0% | 100%(2025年度) | | 労働者の男女の賃金の差異 | 76.3% | 78.4% | 80%(2025年度) | | 有給休暇取得率 | 74.9% | 69.8% | 80%(2025年度) | | 離職率 | 6.4% | 7.7% | 5%以下(毎年) | | 月平均所定外残業時間 | 14.3時間 | 14.1時間 | − | | 障がい者雇用率 | 2.96% | 2.48% | − | (注) 1.国内グループ会社(当社、サンプルテクノロジー株式会社)を対象に計算しています。 2.当社グループでは定年制を廃止しているため、離職率については、すべての退職者を含めて 計算しています。 3.目標の「−」は、設定がないことを示しています。 """ 考察 Markdownの表全体が一つのチャンクとして綺麗に取得できており、必要な文脈(表のヘッダーや注釈)が保持されています。 LLM にこのテキストを渡した場合でも、表構造を正しく解釈できると思われます。 比較:Sentence Chunking の場合 一方で、sentence 戦略を用いた場合、表の途中でチャンクが分割されてしまい、ヘッダー情報が欠落するケースが見られました。 Sentence Chunking での検索結果の抜粋: """(100-999人)では管理職を除く55歳以上のシニア世代がいきいきと働く企業として、3位に選出されました。 ### (3) 指標及び目標 当社グループは、上記の「(2)①人材の多様性の確保を含む人材の育成に関する方針」及び「(2)②社内環境整備に 関する方針」について、次の指標を用いています。当該指標に関する目標及び実績は、次の通りです。 | 指標 | 2023年度実績 | 2024年度実績 | 目標 | |:--|:--|:--|:--:| | 管理職に占める女性労働者の割合 | 15.5% | 10.5% | 20%(2025年度) | | 男性労働者の育児休業取得率 | 38.5% | 100.0% | 100%(2025年度) | """, """| 労働者の男女の賃金の差異 | 76.3% | 78.4% | 80%(2025年度) | | 有給休暇取得率 | 74.9% | 69.8% | 80%(2025年度) | | 離職率 | 6.4% | 7.7% | 5%以下(毎年) | | 月平均所定外残業時間 | 14.3時間 | 14.1時間 | − | | 障がい者雇用率 | 2.96% | 2.48% | − | (注) 1.国内グループ会社(当社、サンプルテクノロジー株式会社)を対象に計算しています。 2.当社グループでは定年制を廃止しているため、離職率については、すべての退職者を含めて 計算しています。 3.目標の「−」は、設定がないことを示しています。 ## 3 【事業等のリスク】 """ このようにヘッダーとデータ行が分断されると、数値だけがヒットしても 「それが何の数値か(2023年度の実績なのか、それとも、2024年度の実績なのか)」を正しく理解できず、 回答精度の低下に直結します。 ケース2. 階層が深いブロックの検索 クエリ: “当連結会計年度のアプリケーション事業の販売実績” GET /ir_report_2024_chunk_recursive/_search { "_source": false, "query": { "semantic": { "field": "content.text_embedding", "query": "当連結会計年度のアプリケーション事業の販売実績" } }, "highlight": { "fields": { "content.text_embedding": { "order": "score", "number_of_fragments": 1 } } } } 検索結果 """ ##### (d) 販売実績 当連結会計年度の販売実績をセグメントごとに示すと、次のとおりであります。 | セグメントの名称 | 当連結会計年度(自2024年1月1日至2024年12月31日) | 前年同期比(%) | |:--|:--|:--:| | オープンシステム基盤事業(千円) | 14,573,839 | 147.1 | | アプリケーション事業(千円) | 5,986,143 | 100.3 | | 合計(千円) | 20,559,983 | 129.5 | (注)1.セグメント間の内部売上高又は振替高を除いた外部顧客に対する売上高を記載しております。 2.最近2連結会計年度の主要な販売先及び当該販売実績の総販売実績に対する割合は次のとおりであります。 | 販売先 | 前連結会計年度(自2023年1月1日至2023年12月31日) | | 当連結会計年度(自2024年1月1日至2024年12月31日) | | |:--|:--|:--|:--|:--:| | | 金額(千円) | 割合(%) | 金額(千円) | 割合(%) | | 株式会社*** | 4,229,893 | 26.6 | 6,067,031 | 29.5 | | 株式会社*** | 2,026,750 | 12.8 | 2,599,494 | 12.6 | """ ピンポイントで検索したいブロック(見出し (d) 販売実績 配下)のみがヒットしています。 比較:Word Chunking の場合 一方で、word 戦略を用いた場合、単純な単語数で区切るため、目的のブロックの前に直前のセクション の残骸が含まれたり、取得したい情報が途中で切れたりする現象が発生しました。 Word Chunking での検索結果の抜粋: """(前年同期は157百万円の使用)となりました。 #### ③ 生産、受注及び販売の状況 ##### (a) 生産実績 当連結会計年度の生産実績をセグメントごとに示すと、次のとおりであります。 | セグメントの名称 | 当連結会計年度(自2024年1月1日至2024年12月31日) | 前年同期比(%) | |:--|:--|:--:| | オープンシステム基盤事業(千円) | 894,022 | 128.9 | | アプリケーション事業(千円) | 1,979,893 | 88.3 | | 合計(千円) | 2,873,915 | 97.9 | ##### (b) 仕入実績 当連結会計年度の仕入実績をセグメントごとに示すと、次のとおりであります。 | セグメントの名称 | 当連結会計年度(自2024年1月1日至2024年12月31日) | 前年同期比(%) | |:--|:--|:--:| | オープンシステム基盤事業(千円) | 11,053,511 | 165.7 | | アプリケーション事業(千円) | 1,303,180 | 123.3 | | 合計(千円) | 12,356,691 | 159.9 | ##### (c) 受注実績 当連結会計年度の受注実績をセグメントごとに示すと、次のとおりであります。 | セグメントの名称 | 受注高(千円) | 前年同期比(%) | 受注残高(千円) | 前年同期比(%) | |:--|:--|:--|:--|:--:| | オープンシステム基盤事業 | 14,871,485 | 141.6 | 2,742,583 | 112.2 | | アプリケーション事業 | 6,400,717 | 115.5 | 2,477,333 | 120.1 | | 合計 | 21,272,202 | 132.6 | 5,219,917 | 115.8 | ##### (d) 販売実績 当連結会計年度の販売実績をセグメントごとに示すと、次のとおりであります。 | セグメントの名称 | 当連結会計年度(自2024年1月1日至2024年12月31日) | 前年同期比(%) | |:--|:--|:--:| | オープンシステム基盤事業(千円) | 14,573,839 | 147.1 | | アプリケーション事業(千円) | 5,986,143 | 100.3 | | 合計(千円) | 20,559,983 | 129.5 | (注)1.セグメント間の内部売上高又は振替高を除いた外部顧客に対する売上高を記載しております。 2.最近2連結会計年度の主要な販売先及び当該 """ 採用時の重要な注意点 Elasticsearch の chunking_settings (Inference API) を使用してチャンキングを行った場合、 データ構造は以下のようになります。 content (text 型) content.text_embedding (semantic_text 型) 複数の密ベクトル (分割されたチャンクの数だけ生成される) この構造下では、「チャンク単位でのキーワード検索 + ベクトル検索(ハイブリッド検索)」を行うことが困難です。 標準のキーワード検索(Match Queryなど)を行うと、ドキュメント全体(content フィールド)に対してマッチングが行われるため、ベクトル検索でヒットしたチャンクとは無関係の箇所にあるキーワードにも反応してしまいます。 もし、「チャンクごとに、厳密なキーワード検索とベクトル検索を組み合わせたい」 という要件がある場合は、Elasticsearch 側での自動チャンキング機能(Inference API の chunking)は使用せず、以下のアプローチを検討してください。 LangChain などの外部ライブラリを使用して、アプリケーション側で事前に Markdown を分割する。 分割した各チャンクを、それぞれ独立した Elasticsearch ドキュメントとしてインデックスする。 こうすることで、1つのドキュメントに対して「1つのテキスト」と「1つのベクトル」が対応するシンプルな構造となり、チャンク単位でのハイブリッド検索が容易になります。 まとめ 検証の結果、Markdown 文書に対して Recursive Chunking(再帰チャンキング) を適用することで、以下のメリットが確認できました。 文脈の保持: 見出しや表などの論理的なまとまりが維持される。 ノイズの削減: 無関係なセクションの混入を防ぎ、検索精度の向上に寄与する。 RAGへの最適化: LLMに渡すコンテキストとして、より意味の通った情報を提供できる。 Markdown形式のドキュメント(技術仕様書、社内Wiki、レポートなど)を検索対象とする場合、 Elasticsearchの再帰チャンキング(Recursive Chunking)は非常に強力な選択肢となります。ぜひ一度お試しください。 The post Markdown 文書のための再帰チャンキング入門 — Elasticsearch での実践と比較 first appeared on Elastic Portal .
アバター
Elasticsearch を使っていると、クエリ記述には KQL や ES|QL、Query DSLなどいくつかの書き方が登場します。しかし、そのすべての 下で動いているのは Apache Lucene の検索エンジン です。 Luceneを「高性能なエンジン」だとすれば、Elasticsearchはそのエンジンを搭載した「高級車」のようなものです。Lucene を一言でいうと、 テキストを検索しやすい形に変えて、素早く答えてくれるエンジン です。 この記事では、Kibanaにデフォルトで入っている Kibana Sample Data Logs を使い、Lucene クエリの基本とよく使う構文を整理します。 目次 Luceneモードへの切り替え 基本クエリ 1. フリーテキスト検索 2. キーワード検索 3. Wildcard Matching 4. Proximity Matching(近接検索) 5. Range Searches(範囲検索) 6. Fuzzy Searches(あいまい検索) 7. Boosts(スコアの重み付け) 8. Boolean Operators & Grouping(論理演算とグループ化) よくある落とし穴 まとめ Luceneモードへの切り替え Kibanaの検索バーは現在、デフォルトで KQL (Kibana Query Language) になっています。この記事で紹介するクエリ(特に ~ を使う近接検索や TO を使う範囲指定)を正しく動作させるには、以下の手順でモードを切り替えてください。 基本クエリ 検索のやり方として2つあります。 1. フリーテキスト検索 フリーテキスト検索を実行するには、テキスト文字列を入力するだけです。たとえば、Web サーバーのログを検索する場合、safari と入力してすべてのフィールドを検索できます。 2. キーワード検索 特定のフィールドが特定の値と一致するものを探します。 クエリ 説明 response: 404 ステータスコードが 404 のログのみを検索します。 machine.os: “win 8” OSが “win 8” というフレーズと完全に一致するものを検索します。 geo.src: US -geo.dest: US アクセス元がアメリカで、かつ目的地がアメリカ ではない ものを検索します(マイナス記号  – は除外を意味します)。 3. Wildcard Matching 文字の一部が不明な場合や、パターン検索に使用します。 クエリ 説明 agent: Mozilla* User Agentが “Mozilla” で始まるすべてのログを検索します。 extension: d* 拡張子が “d” で始まるファイル(debなど)へのアクセスを検索します。 referer: *twitter* リファラー(参照元URL)の中間に “twitter” が含まれるログを検索します。 ⚠️ 注意: 先頭に * を置く検索(例: *name )は、全インデックスを走査するため非常に遅くなる可能性があり、推奨されません。 4. Proximity Matching(近接検索) 2つの単語が互いに指定した距離(単語数)以内で出現するドキュメントを探します。 クエリ 説明 “beats HTTP”~4 メッセージ内で “beats” と “HTTP” が 4単語以内 の距離にあるものを検索します。単なるAND検索よりも文脈の強いつながりを見つけられます。 5. Range Searches(範囲検索) 数値や日付の範囲を指定します。 クエリ 説明 bytes: [10000 TO *] 転送量が 10,000バイト以上 のログを検索します(* は上限なし)。 response: [500 TO 599] ステータスコードが 500番台 (サーバーエラー) の範囲にあるログを検索します。 bytes: {0 TO 100} 転送量が0より大きく100未満を検索します。[] は境界値を含み(以上・以下)、{} は境界値を含みません(より大きい・より小さい)。 6. Fuzzy Searches(あいまい検索) スペルミスや表記ゆれを許容して検索します。 クエリ 説明 index:kibana_smple_dat_logs スペルミスを許容して検索します(例: “kibana_sample_data_logs” にヒットします)。 index:kibana_smple_dat_logs~1 1文字違いまで許容します(例: 1文字までなのでkibana_sample_data_logsにヒットしません)。 💡 ポイント: ~ は単語の後ろにつけるとFuzzy(roan~)、フレーズの後ろにつけるとProximity(”foo bar”~4)として機能します。 7. Boosts(スコアの重み付け) 特定の条件に合致するドキュメントの検索スコア(関連度)を高くします。 クエリ 説明 response:404^2 OR response:503 404エラーの重要度(スコア)を2倍にして検索します。関連度順に並べた際、404エラーの方が上位に来やすくなります。 8. Boolean Operators & Grouping(論理演算とグループ化) 複雑な条件を組み合わせます。Lucene の演算子(AND, OR, NOT)は 大文字 が必須です。小文字にすることで、特定の文字が演算子としてではなく、単語として認識されます。 クエリ 説明 NOT response: 200 ステータスコードが 200 (成功) ではない (=エラーやリダイレクト) ログをすべて表示します。 geo.src: US AND response: 404 アメリカからのアクセス かつ 404エラーだったログを検索します。 (extension: rmp OR extension:deb) AND geo.dest:GB グループ化: 「拡張子がrmpかdeb」という条件を優先し、かつイギリスからのアクセスであるものを検索します。 よくある落とし穴 Luceneクエリが思ったように動かない場合、以下の原因が考えられます。 KeywordフィールドでのFuzzy検索: keyword 型のフィールドは解析(Analyzer処理)されないため、Fuzzy検索(~)が効きません。 句読点や記号の消失: データ取り込み時に Standard Analyzer を通っている場合、記号が削除されていることがあります。「検索できない」と思ったら、元のテキストがどうトークン化されているか確認が必要です。 特殊文字のエスケープ: + – && || ! ( ) { } [ ] ^ ” ~ * ? : \ などの文字を検索値として含める場合は、直前にバックスラッシュ \ を置いてエスケープする必要があります(例:file_name:report\-2025\.pdf)。 まとめ 全文検索、ログ検索、アラート条件、検索パフォーマンス — すべての土台は Lucene にあります。 KQL: Kibanaでの日常的な利用に最適(簡単・安全)。 Lucene: 複雑な検索(正規表現、Fuzzy、Proximity)を行いたい時に利用。 まずはSample Dataを使って、Luceneならではの柔軟な検索を体験してみてください。 The post ElasticsearchのためのLuceneクエリ入門ガイド first appeared on Elastic Portal .
アバター
目次 はじめに 準備作業 環境 サンプルデータの取り込み ダッシュボードの作成 1. ダッシュボードの新規作成 2. Variable control の作成 3. グラフと Variable control の紐づけ 動作確認 まとめ はじめに Elastic の Kibana では、これまでグラフごとに集計項目を固定して作成する必要がありました。 しかし、Elastic Stack 8.18 以降(または 9.0 以降)で強化された「Variable control」機能と ES|QL を組み合わせることで、一つのパネルで集計項目を動的に切り替えることが可能になりました。 ダッシュボード上でプルダウンメニューを操作するだけで集計項目を変えられるため、似たようなグラフを何個も作る必要がなくなり、分析の柔軟性が大幅に向上します。本記事では、その具体的な実装手順をご紹介します。 準備作業 環境 以下の環境を使用します。 Elasticsearch / Kibana : 8.18以降 または 9.0以降   – 本記事では、バージョン 9.2.0 で検証しています。 サンプルデータの取り込み 下記の記事などを参考に、「Sample web logs」データを Kibana に取り込んでください。 参考: https://elastic.sios.jp/blog/elasticsearch-esql-introduction-log-analysis/ ダッシュボードの作成 1. ダッシュボードの新規作成 1.1. メインメニューの Analytics / Dashboards をクリックし、Dashboard 一覧画面へ遷移します。 1.2. 右上の [+ Create dashboard] をクリックします。 ダッシュボードの新規作成画面へ遷移します。 1.3. 対象期間を調整します。(例: Last 15 minutes を、Last 7 days に変更) 1.4. [(+) Add] > New Panel を選択します。 1.5. パネルの種類として、ES|QL を選択します。 ES|QLの入力画面に切り替わります。 1.6. ES|QL の入力欄に以下のクエリーを貼り付けます。 まずは、ベースとなるグラフを作成するため、geo.dest (宛先地域)ごとのアクセス数を集計するクエリーを入力します。 FROM kibana_sample_data_logs | STATS count = count() BY geo.dest | SORT count DESC | LIMIT 10 1.7. 右上の [▷ Run Query] をクリックし、グラフが描画されることを確認します。 1.8. 右下の [✓ Apply and close] をクリックします。 2. Variable control の作成 次に、先ほどのグラフの集計項目を簡単に変更できるようにするための Variable control を追加します。 2.1. 画面上部の [(+) Add] をクリックし、Controls > Variable control を選択します。 Variable control の新規作成画面が表示されます。 2.2. Variable control の設定画面で以下のように入力します。 Type を Values from a query から Static values に変更します。 Name を ??stats_field に変更します。 Values に集計対象としたいフィールド名を入力していきます。 agent clientip geo.dest host machine.os response tags url 2.3. 右下の [Save] をクリックします。ダッシュボードに stats_field の control が追加されます。 3. グラフと Variable control の紐づけ 作成した Variable control の値に応じてグラフが変化するように、クエリーを修正します。 3.1. 作成したグラフパネルの右上のオプションから鉛筆アイコンをクリックします。 グラフ部分の編集画面へ遷移します。 3.2. ES|QL クエリー内の geo.dest 部分を削除します。 | STATS count = count() BY geo.dest   <-- この geo.dest を削除 3.3. 削除した箇所でキーボードのスペースバーを押すと、候補が表示されます。 選択肢の中から、先ほど作成した、 ??stats_field を選択します。 3.4. [▷ Run query] をクリックします。グラフの Horizontal axis (横軸)が ??stats_field に変わり、グラフが再描画されます。 3.5. 右下の [✓ Apply and close] をクリックします。 3.6. ダッシュボード右上の [Save] をクリックし、タイトル(例:”variable control test”)を入力して保存します。 動作確認 実際に Variable control を操作してみましょう。 ダッシュボード上の stats_field プルダウンを clientip に変更すると、クライアント IP ごとの集計結果に切り替わります。 同様に、stats_field を、geo.dest, host, machine.os, response, tags, url に変えると、それぞれのフィールドに基づいた集計結果が即座に表示されます。 まとめ Variable control を活用することで、1つのグラフパネルであっても多角的な視点での分析が可能になることがお分かりいただけたかと思います。これにより、ダッシュボードの表示領域を節約しつつ、インタラクティブなデータ探索が可能になります。 今回は「集計フィールド(BY句)」を変数化する例を紹介しましたが、この機能は「集計関数(STATS句)」や「フィルタ条件(WHERE句)」などにも応用可能です。ぜひ皆さんの環境でも試してみてください。 参考URL: https://www.elastic.co/search-labs/blog/kibana-dashboard-interactivity-variable-controls-overview The post 【Kibana】ES|QLとVariable control で実現! 集計項目を動的に切り替えるダッシュボード作成術 first appeared on Elastic Portal .
アバター
Elasticは、データを素早く検索・分析するための強力な基盤として長年活用されてきました。しかし、クラウドネイティブな環境が標準となるにつれ、従来のデータ管理手法である「ステートフル」モデルでは対応しきれない課題も顕在化しています。 これまでのElasticは、データ処理を行う「コンピューティング」と、データを保存する「ストレージ」が一体となった構成でした。この方式は確かな実績を持つ一方で、システムの拡張や管理運用において、手間やコストが増大しやすいという側面がありました。 そこで新たに登場したのが、「サーバーレス」アーキテクチャです。本稿では、これら二つの方式を比較しながら、サーバーレスアーキテクチャが実現する「コンピューティングとストレージの分離」が、いかにして高い運用効率、柔軟な拡張性、そして優れたコスト効率をもたらすのかを解説します。 目次 従来のステートフルアーキテクチャのレビュー 基本構成とデータ階層化 運用上の課題 サーバーレスアーキテクチャの登場 中核概念:コンピューティングとストレージの分離 「スィンシャード(Thin Shards)」による効率化 シャード再配置問題の解決 詳細なプロセス比較:インデックス作成と検索 ステートフルモデルのデータフロー サーバーレスモデルのデータフロー 最適化:バッチコミット 運用の変革:自動スケーリングとコスト効率 負荷に応じた自動スケーリング コストモデルの最適化 現状の考慮事項と制限 サービス提供開始時期と日本での利用について Elastic Cloud Serverless についてのよくある質問 料金と提供地域 データ管理 セキュリティ、準拠、アクセス プロジェクトのライフサイクルとサポート 結論 参考:用語の意味 参考:リンク集 従来のステートフルアーキテクチャのレビュー サーバーレスモデルがもたらす革新性を正確に評価するためには、まずその前身である従来のステートフルアーキテクチャの構造と、それが直面していた運用上の課題を理解することが重要です。 基本構成とデータ階層化 ステートフルアーキテクチャにおけるデータ管理は、一般的にインデックスライフサイクル管理(ILM)に基づいた階層化が特徴です。 Hot層: 最もアクティブなデータが配置される階層です。インデックス作成(書き込み)と検索クエリが集中するため、高性能なローカルSSDが使用されます。ここでは可用性と耐障害性を確保するため、プライマリとレプリカの両方のシャードが保持されます。 Warm/Cold層: アクセス頻度が低下したデータが移動する階層であり、コスト効率の良いストレージが利用される傾向にあります。 Frozen層: ほとんどアクセスされないアーカイブデータを格納する階層です。S3などのオブジェクトストレージ上のスナップショットをソースとし、必要なデータのみをキャッシュにマウントすることで、ストレージコストの大幅な削減を図ります。 運用上の課題 この階層化モデルは有効に機能してきましたが、運用上、いくつかの課題も指摘されていました。 コンピューティングとストレージの密結合: 大きな課題の一つは、CPUやRAMとローカルディスクが不可分である点です。ストレージ容量のみを増強したい場合でもコンピューティングリソースを同時にスケールアップする必要があり、結果として「オーバープロビジョニング」による不要なコストが発生するケースが見られました。 シャード再配置のコスト: ノードの追加・削除や障害時には、クラスター全体でシャードの物理的なデータコピー(再配置)が発生します。これには帯域消費と時間を要するため、頻繁なアップグレードやスケーリングが躊躇され、結果としてCVEへの対応遅延など俊敏性に影響を与える要因となっていました。 管理の複雑さ: 利用者は、自身のワークロードに合わせてクラスターのサイジングを決定し、適切なILMポリシーを設計・設定する必要があります。どのデータをどのタイミングでHot層からWarm層へ移動させるかといった判断は、利用者の専門知識と経験に依存し、運用管理の負担となる場合がありました。 サーバーレスアーキテクチャの登場 これらの課題への回答の一つとして登場したのが、サーバーレスアーキテクチャです。 中核概念:コンピューティングとストレージの分離 最大の変化は、データ(セグメントファイル)がノードのローカルディスクではなく、S3のようなオブジェクトストレージにプライマリストアとして一元保存される点にあります。これにより、ノードはデータの永続保持責任から解放され、実質的に「ステートレス」となります。インデックス作成(書き込み)専用ノードと、検索(読み取り)専用ノードに分離され、それぞれ独立して管理・スケーリングすることが可能になりました。 「スィンシャード(Thin Shards)」による効率化 このアーキテクチャを支える技術が「ツィンシャード」です。従来のシャードとは異なり、初期状態ではメタデータのみを保持します。データの実体はオブジェクトストレージから必要に応じて読み込まれるため、ノードは軽量な状態を維持できます。これは大規模クラスターにおけるリソース効率の向上に寄与すると考えられます。 シャード再配置問題の解決 この分離により、ノード追加時にはオブジェクトストレージからメタデータを読み込むだけで済むため、ノードの起動が非常に高速化されました。大規模なデータ転送が発生しにくいため、ローリングアップグレードやスケーリングがネットワーク帯域を圧迫することなく実行可能となります。 詳細なプロセス比較:インデックス作成と検索 データライフサイクルにおける具体的なプロセスも変革されています。 ステートフルモデルのデータフロー 従来は、インメモリバッファへの格納と同時にローカルディスクのトランザクションログへ書き込み、その後refresh操作で検索可能にし、flush操作でディスクへ永続化するというフローが一般的でした。 サーバーレスモデルのデータフロー インデックス作成ノード: ドキュメント受信後、トランザクションログがオブジェクトストレージにアップロードされ、その完了確認をもってクライアントにACKが返されます。これにより、単一ノードのディスクに依存するよりも高い耐久性が期待できます。 検索ノード: クエリに必要なセグメントファイルのみをオブジェクトストレージからオンデマンドで取得します。また、インデックス直後の最新データに対しては、検索ノードがインデックス作成ノードに直接問い合わせを行うことで、リアルタイムに近い検索体験を提供するよう設計されています。 出典)https://www.elastic.co/jp/cloud/serverless 最適化:バッチコミット オブジェクトストレージへのAPIコール回数を最適化するため、「バッチコミット」という技術が採用されています。複数のコミットを論理的にグループ化し、差分データのみを追加することで、コストとパフォーマンスの両立を図っています。 運用の変革:自動スケーリングとコスト効率 このアーキテクチャは、運用の自動化とコスト最適化にも直結します。 負荷に応じた自動スケーリング インジェスト量や検索クエリ量に基づき、インデックス作成ノードと検索ノードがそれぞれ独立して自動的にスケールします。なお、コールドスタート(スケールアップ時の初期レイテンシ)を回避したい場合は、「サーチパワー(search power)」設定により、アイドル時でも最低限のノードを維持する運用が可能です。 コストモデルの最適化 ストレージとコンピューティングの分離、自動スケーリングによるオーバープロビジョニングの解消、そして従量課金モデルの組み合わせにより、TCO(総所有コスト)の削減が期待できる構造となっています。 現状の考慮事項と制限 サーバーレスアーキテクチャは強力ですが、採用にあたっては現時点での実装における制約を理解しておく必要があります。 リフレッシュレートの調整: オブジェクトストレージへの負荷軽減のため、リフレッシュレートにはスロットリング(制限)が設けられています(例:15秒に1回など)。 シャード数設定: ユーザーによる手動設定は提供されず、プラットフォームによる自動管理となります。 サービス提供開始時期と日本での利用について 本サービスは2024年12月に一般提供(GA)が開始されました。そして、日本のユーザーにとって待望のAWS 東京リージョン(ap-northeast-1)での提供は、2025年10月より開始されています。 Elastic Cloud Serverlessは、開発者がインフラ管理よりもデータの価値創出に集中できる環境を提供します。現在は14日間の無料トライアルも提供されているため、興味のある方はぜひ実際にその挙動を試し、次世代のパフォーマンスを体感してみてください。 日本国内での導入支援体制についてご案内します: サイオステクノロジー は、Elasticの国内初のディストリビューターです。現在、Elastics社と共同で、Elasticがグローバルに提供している「サーチ」「セキュリティ」「オブザーバビリティ」の3つのソリューションについて、日本国内における展開を強化しております。 サーバーレスアーキテクチャへの移行や、具体的な導入・活用方法についてご不明な点がございましたら、どうぞご遠慮なく弊社までお問い合わせください。 Elastic Cloud Serverless についてのよくある質問 以下は、2025年11月時点の公式ドキュメント(Elastic Docs)に基づくFAQの抜粋です。 料金と提供地域 Q:Serverless の料金についてはどこで確認できますか? A:Elasticsearch Serverless、Observability Serverless、および Elastic Security Serverless の各料金情報ページをご覧ください。 Q:Elastic Cloud Serverless はどのクラウドリージョンでサポートされていますか? A:選定されたAWS/GCP/Azureのリージョンで利用可能です。今後、対応リージョンの拡大も計画されています。詳細は公式ドキュメントの「Regions」をご参照ください。 データ管理 Q:Serverless プロジェクトへ、またプロジェクトからデータを移動するにはどうすればいいですか? A:現在、データ移行ツールを開発中です。暫定的には、Logstashを使用し、Elasticsearch入出力プラグインを通じてServerlessプロジェクトとのデータ移動を実施してください。 Q:Serverless プロジェクトに対してバックアップやリストアのリクエストはできますか? A:プロジェクトのバックアップやリストア要求は、現時点ではサポートされていません。データ移行ツールの強化が進められています。 セキュリティ、準拠、アクセス Q:Elastic Cloud Serverless のサービスアカウントはどう作成できますか? A:Serverlessプロジェクト内でサービスアカウントのAPIキーを作成してください。将来的にはTerraform等のツールによるAPIキー作成の自動化オプションも提供される予定です。 Q:Elastic Cloud Serverless はどのようなコンプライアンスおよびプライバシー基準に準拠していますか? A:Elasticプラットフォーム全体と同様に、独立監査を受け、主要なコンプライアンスおよびプライバシー基準を満たすよう認証されています。詳しくはElastic Trust Centerをご覧ください。特定の基準に関してはロードマップに記載があります。 プロジェクトのライフサイクルとサポート Q:Elastic Cloud Serverless はソフトウェアのバージョン互換性をどのように確保していますか? A:アップグレード時にも接続や設定には影響が出ないよう設計されています。互換性維持のため、品質テストおよびAPIバージョニングが用いられています。 Q:Serverless プロジェクトを Hosted 型(Elastic Cloud Hosted)への変換、あるいは Hosted 型を Serverless プロジェクトに変換できますか? A:プロジェクトおよびデプロイメントは異なるアーキテクチャに基づいているため、相互の変換はできません。 Q:Serverless プロジェクトを別のタイプのプロジェクトに変換できますか? A:プロジェクトを別タイプに変換することはできませんが、必要に応じて任意の数のプロジェクトを作成可能です。課金は実際の使用量に基づきます。 Q:Elastic Cloud Serverless のサポートケースを提出するにはどうすればいいですか? A:普段お使いのサブスクリプションに対してケースを作成してください。ケース本文に「Serverless プロジェクトを使用中」である旨を記載すると、適切なサポートが受けられます。 結論 Elasticのサーバーレスアーキテクチャへの移行は、コンピューティングとストレージの分離、オブジェクトストレージの活用、そしてスィンシャードという技術革新によって実現されました。これにより、スケーラビリティの向上や運用管理の簡素化、コスト効率の改善が見込まれます。 参考:用語の意味 インデックスライフサイクル管理 (ILM): データを作成から削除までポリシーに基づいて自動管理する機能。 オーバープロビジョニング: ピーク時の負荷や将来の増加を見越して、必要以上のリソース(CPU、メモリ、ストレージ)をあらかじめ確保しておくこと。 トランザクションログ (Translog): データの変更操作を記録するログ。メモリ上のデータがディスクに永続化される前にクラッシュした場合のデータ復旧に使用されます。 セグメントファイル: Elasticsearch(内部のLucene)におけるインデックスの構成単位。不変(Immutable)なファイルとして保存されます。 ローリングアップグレード: サービスを停止させることなく、クラスター内のノードを1台ずつ(または一部ずつ)順番に更新していく手法。 コールドスタート: サーバーレス環境において、リクエストが発生してからリソースが立ち上がり、処理が可能になるまでに生じる初期遅延のこと。 CVE (Common Vulnerabilities and Exposures): 共通脆弱性識別子。公開されているセキュリティ脆弱性に対して割り当てられる固有のID。 総所有コスト (TCO): システムの導入から運用、維持管理にかかる費用の総額。 ACK (確認応答): 一般的なネットワーク用語です。 参考:リンク集 https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/serverless https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud#general-what-is-serverless-elastic-differences-between-serverless-projects-and-hosted-deployments-on-ecloud https://www.elastic.co/search-labs/blog/thin-indexing-shards-elasticsearch-serverless https://www.elastic.co/search-labs/blog/elasticsearch-serverless-tier-autoscaling https://www.elastic.co/search-labs/blog/elasticsearch-refresh-costs-serverless https://www.elastic.co/search-labs/blog/elastic-serverless-performance-stress-testing https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/manage-serverless-projects-using-api The post Elastics Cloud Serverless:ステートフルからサーバーレスへの技術的移行 first appeared on Elastic Portal .
アバター
目次 【10秒で診断】あなたの会社は大丈夫ですか? 1. なぜ今、多くの企業が「セキュリティの穴」に気づいていないのか よくある4つの問題 実態:攻撃発見までの平均時間は一週間以上 2. Elasticが実現する「防御・検知・対応・調査」の統合基盤 理由① エンドポイントで攻撃を「入口」で止める、Elastic Defend (EDR) 理由② 端末からクラウドまで「すべて」を可視化🔍 理由③ フィールドレベルの厳格なアクセス制御 理由④ 通信・保存データの完全暗号化 理由⑤ 誰が・いつ・何をしたかを完全記録 理由⑥ コストと速度を両立するデータティアリング 3. 攻撃の「前」「中」「後」すべてに対応、Elastic Securityの実力 攻撃前:異常を早期に検知 攻撃中:リアルタイムで防御・封じ込め 攻撃後:迅速な初動調査と復旧 4. 【実績】なぜ業界リーダーが Elastic を選ぶのか 世界中の企業が選ぶプラットフォーム 企業が得られる5つの価値 5. まとめ:「攻撃されることを前提」に設計された統合防御基盤 Elastic Securityが統合する機能 6. 【次のステップ】SIOSテクノロジーにご相談ください こんなお悩みをお持ちの方へ まずは無料相談から 【10秒で診断】あなたの会社は大丈夫ですか? ✅ 監査で「3年前のログを見せて」と言われたら、すぐ出せますか? ✅ インシデント発生時、複数システムのログを横断検索できますか? ✅ 端末(PC・サーバ)で何が起きているか、リアルタイムに把握できますか? ✅ ランサムウェアが実行される前に、異常な動きを検知・遮断できますか? 1つでも「NO」なら、このまま読み進めてください。 1. なぜ今、多くの企業が「セキュリティの穴」に気づいていないのか サイバー攻撃は日々高度化しています。しかし、多くの企業は以下の 致命的なギャップ を抱えたままです。 よくある4つの問題 問題①「エンドポイントが攻撃の入口なのに、見えていない」 PC・サーバは攻撃者が最初に侵入する接点 ファイアウォール・アンチウイルスでは、既に侵入された後の「ポスト侵入フェーズ」をカバーできない → 気づいた時には横展開され、被害が全社に拡大 問題②「必要な時にログが見られない」 古いログはアーカイブされ、復元に数時間〜数日 システムごとにログが分散し、横断検索が困難 → インシデント対応の初動が遅れ、被害が拡大 問題③「機密情報の管理が不十分」 誰が何を見られるか制御できていない 監査時に「誰がいつアクセスしたか」を証明できない → 内部不正リスク、コンプライアンス違反の危険 問題④「コストか速度か、選べない」 全データ保管 → インフラ費用が膨大 コスト削減でログ削除 → 調査不能 → 結局、中途半端な対策しかできない 実態:攻撃発見までの平均時間は一週間以上 従来型の「ログだけ」「エンドポイント防御だけ」の分断された基盤では、もはやセキュリティ運用を支えきれません。 2. Elasticが実現する「防御・検知・対応・調査」の統合基盤 Elasticは、 エンドポイント防御 + ログ基盤 + SIEM を単一プラットフォームで統合。攻撃のライフサイクル全体をカバーします。 理由① エンドポイントで攻撃を「入口」で止める、Elastic Defend (EDR) 攻撃者が侵入を試みる瞬間に対応 Elastic Defendは、Windows・macOS・Linuxの全端末に対して、次世代の防御機能を提供します: 多層防御の仕組み マルウェア防御 : 機械学習ベースのシグネチャレス検知 ランサムウェア防御 : 振る舞い分析+カナリーファイルで暗号化を事前検知 メモリ脅威防御 : シェルコードインジェクション等のメモリ攻撃を遮断 悪意ある振る舞い検知 : 権限昇格、不審なプロセス起動、ツール悪用を検知 即座に対応する自動アクション ✅ 脅威プロセスの自動停止 ✅ 端末のネットワーク隔離 ✅ 感染ファイルの自動隔離 ✅ セッション記録による詳細なフォレンジック   価値 : 攻撃が本格化する 前 に封じ込め、被害を最小化 理由② 端末からクラウドまで「すべて」を可視化🔍 環境全体を一望できる統合ビューで、セキュリティの抜け穴を作らない 従来の課題: ❌ EDRとログ基盤が別々 → 端末とネットワークの相関が見えない ❌ 調査時に複数ツールを往復 → 初動が遅れる Elasticの解決策: ✅ 単一プラットフォーム で端末・ネットワーク・クラウドを統合 ✅ 「端末で何が起きたか」→「どこに広がったか」→「いつ発覚したか」を一貫追跡 ✅ Attack Discoveryで複数のアラートを関連付け、攻撃チェーンを自動的に整理・可視化 理由③ フィールドレベルの厳格なアクセス制御 「必要な人が、必要な範囲だけアクセスできる」を実現 ※ 正しい設定・運用が前提です インデックスごと の権限分離 ドキュメントレベル のセキュリティ(条件付きアクセス) フィールドレベル のセキュリティ(機密情報のマスキング) 導入効果 ✅ 内部不正リスクを大幅低減 ✅ 監査対応の証跡管理が容易に ✅ GDPR、個人情報保護法への対応を強化 理由④ 通信・保存データの完全暗号化 データのライフサイクル全体を保護 TLSによるノード間/クライアント通信の暗号化 IPフィルタリングによるアクセス制限 at-rest(保存データ)暗号化のサポート オンプレ/クラウドを問わず統一ポリシーで運用可能 理由⑤ 誰が・いつ・何をしたかを完全記録 監査とコンプライアンス対応を支える証跡管理 設定変更・検索操作・認証試行の全記録 LDAP / Active Directory / SAML / OIDC との統合 監査ログそのものの保護機能 エンドポイントの動き、異常な振る舞い、対応履歴も含めて記録 こんな場面で威力を発揮 インシデント時の「何が起きたか」「何をしたか」の説明 内部統制の証跡提出 法令対応の監査対応 理由⑥ コストと速度を両立するデータティアリング 「高速アクセス」と「長期保管」を同時実現 ティア 特徴 主な用途 コストメリット Hot 最速アクセス リアルタイム監視、直近の調査 高性能が必要な範囲に集中投資 Warm 速度とコストのバランス 日常調査、数週〜数ヶ月前のログ 多くの調査で活用される期間を最適化 Cold 低コスト+実用速度 過去の大規模分析 大幅なコスト削減 Frozen Searchable Snapshot 法令対応の長期保管 環境により異なるが、導入事例では最大50%のコスト削減が報告されている ポイント : 古いログを「削除」するのではなく「適切なティアに移動」することで、 必要な時にすぐアクセスできる状態を保ちながらコスト最適化 を実現。 3. 攻撃の「前」「中」「後」すべてに対応、Elastic Securityの実力 攻撃前:異常を早期に検知 Elasticは1,300以上の事前構築ルールと70以上の機械学習ジョブを提供しており、以下を実現します: 多層的な検知の仕組み MITRE ATT&CK準拠の検知ルール(随時更新) 機械学習による時系列異常検知 新規用語検知、閾値検知、インジケーターマッチ UEBA(User and Entity Behavior Analytics) アカウント侵害の兆候 内部脅威の早期発見 異常な横展開の検知 結果 : 攻撃者の内部偵察や権限昇格を本格化する 前 に気づける     ※ 検知精度や可視化の範囲は、収集できるデータの種類や運用環境に依存します 攻撃中:リアルタイムで防御・封じ込め Elastic Defendによる即座の対応 マルウェア実行を検知した瞬間に 自動停止 ランサムウェアの暗号化動作を 事前遮断 侵害された端末を ネットワークから自動隔離 (Host Isolation) 攻撃の横展開を阻止 脅威ライフサイクルの短縮 従来:侵入 → 数週間の潜伏 → 発見時には全社感染 Elastic:侵入検知 → 封じ込め → 被害を最小化 攻撃後:迅速な初動調査と復旧 データティアリング × 高速検索 × エンドポイント記録で実現 数年分のログへの直ちにアクセス 端末での実行プロセス、ネットワーク接続を完全記録 複数システムの横断検索がスムーズ セッションリプレイで攻撃の全容を再現 監査証跡の抽出も高速化 Elasticの強み : 巨大データを前提に設計された 分散検索エンジン 。データが増えるほど、他製品との差が顕著に。 4. 【実績】なぜ業界リーダーが Elastic を選ぶのか 世界中の企業が選ぶプラットフォーム Fortune 500 の 54%以上 が 採用 50億回以上 の ダウンロード実績 1,500以上のグローバルパートナーとのエコシステム 300以上のターンキー統合で既存環境とシームレスに連携 NYSE上場企業(ティッカーシンボル: ESTC)としての信頼性 米国国防総省・情報機関でも採用される実戦 レベルの技術 EDR・SIEM・ログ管理・ML検知を単一プラットフォームで提供 オープンソースベース で 透明性が高く 、 拡張性に優れる 企業が得られる5つの価値 攻撃の入口で防御を可能にする設計 エンドポイントでの防御・検知・対応をリアルタイム実行 統合された可視性 端末・ネットワーク・クラウドを単一コンソールで管理 コスト効率の最適化 エージェント数無制限 データ使用量ベースの透明な料金体系 ティアリングで長期保管を現実的コストで実現 内部統制の強化 フィールドレベルの細かなアクセス制御+完全な監査証跡 投資対効果の説明力 「何が起きたか」「どう対応したか」を経営層に明確に説明可能 5. まとめ:「攻撃されることを前提」に設計された統合防御基盤 完全な防御は存在しません。重要なのは: ✅ 攻撃を入口(エンドポイント)で止めること ✅ 攻撃が本格化する前に気付けること ✅ 攻撃を受けた後、迅速に原因を特定できること Elastic Securityが統合する機能 レイヤー 機能 効果 エンドポイント防御 Elastic Defend (EDR) マルウェア・ランサムウェアを実行前に遮断 リアルタイム検知 1,300+ルール+ML 既知・未知の脅威を早期検知 振る舞い分析 UEBA 内部脅威・異常な振る舞いを分析 アクセス制御 フィールドレベルセキュリティ 内部不正リスク低減、監査対応強化 暗号化 通信・保存データ保護 セキュリティポリシーへの準拠 証跡管理 認証連携と監査ログ コンプライアンス対応 コスト最適化 データティアリング 高速アクセスと長期保管の両立 Elasticは、「分断された点の防御」から「統合された面の防御」へのパラダイムシフトを実現します。 6. 【次のステップ】SIOSテクノロジーにご相談ください こんなお悩みをお持ちの方へ 「エンドポイント防御とログ管理を統合したい」 「既存のEDR・SIEMからの移行を検討している」 「Elasticが自社に合うか知りたい」 「具体的な構築・運用支援が必要」 「コスト試算とROIを確認したい」 まずは無料相談から 弊社サイオステクノロジーはElasticsearchの国内初のディストリビューターです 。 お問い合わせフォームへ 💬 詳しく知る : Elastic公式サイト Elastic Security ソリューション Elastic Defend (EDR) Elastic公式ドキュメント Elastic Labs(ハンズオン環境) The post もう、攻撃後の「調査できない」は許されない、Elasticが実現する次世代セキュリティ基盤 first appeared on Elastic Portal .
アバター
このガイドでは、( 以前の記事 で)「Elastic Agent Builder」を使って作ったAIエージェントを、「Claude Desktop」というアプリに接続する手順を説明します。 目次 Elastic Agent 前提条件 手順 必要な情報を収集する Claude Desktopの設定ファイルを開く Claude Desktopを再起動する 6. 接続を確認する 質問を実施 トラブルシューティング エラー: “Server disconnected” エラー: “401 Unauthorized” 次のステップ 参考リンク Elastic Agent Elastic Agent Builderは、2025年10月に技術プレビューとして登場した、 Elasticsearch 上で動作するAIエージェント構築フレームワークです。企業データをチャット形式で活用できるよう、自然言語→検索/分析クエリ(例:ES|QL)への変換、ツールの組み込み、LLM(大規模言語モデル)との連携を一元管理可能にします。使用にはElasticsearchインスタンス(クラウドもしくはセルフマネージド)が必要で、Agent Builder自身はElasticsearchのライセンス(サブスクリプション)上で提供されています。なお、LLMはAgent Builderが既定の接続を用意していますが、独自モデルを使うためには別途モデルのプロバイダー契約や接続設定が必要となります。 前提条件 Claude Desktopがインストールされていること Elasticのサブスクリプション 適切な権限を持つAPI keyが生成されていること 手順 必要な情報を収集する 以下の情報を準備してください: Kibana : Elastic Agent Builderにアクセス https://agentbuildertry ****-a12e4c.kb.ap-****-1.aws.elastic.cloud/app/agent_builder API Key : Elastic Agent Builderへのアクセス権限を持つAPI key Elasticの画面で生成できます 例:dGFSelk1b0JfM05YMjlQMFtg66Q6TXo1ZTM1ZzJDdVF2QnB1eXpxeTdQUQ== MCP Server URL : Agent BuilderのMCPエンドポイント Agents→Manage tools 画面の右上にあるMCP ServerをクリックしてCopy MCP Server URLをクリックします。 Claude Desktopの設定ファイルを開く macOSの場合、以下のパスにある設定ファイルを編集します: ~/Library/Application Support/Claude/claude_desktop_config.json デスクトップアプリからもいけます: 設定ー>開発者ー>設定を編集 を選びます Claudeのフォルダが開きます。claude_desktop_config.jsonを選びます、なかった場合は作成します。 claude_desktop_config.jsonファイルに以下の設定を追加します: { "mcpServers": { "elastic-agent-builder": { "command": "npx", "args": [ "-y", "mcp-remote", "https://your-deployment.kb.region.aws.elastic.cloud/api/agent_builder/mcp",  "--header", "Authorization:ApiKey YOUR_API_KEY_HERE" ] } } } 重要な注意点: https://your-deployment.kb.region.aws.elastic.cloud/api/agent_builder/mcp を実際の MCP Server URL に置き換えてください YOUR_API_KEY_HERE を実際の API key に置き換えてください Authorization:ApiKey の後ろに半角スペース1つと API key を記載します Claude Desktopを再起動する 設定ファイルを保存したら、Claude Desktopを完全に終了して再起動します。 macOSの場合: Command + Q でClaude Desktopを終了 アプリケーションフォルダからClaude Desktopを再起動 6. 接続を確認する Claude Desktop起動後、以下の方法で接続を確認できます: Claude Desktopの画面で検索とツールをクリック 接続されているサーバーのリストに「elastic-agent-builder」が表示されることを確認 利用可能なツールが表示されていれば成功です 設定→開発者の画面から running の文字を確認 質問を実施 MCPサーバーの接続を確認してから実際に質問をしてみます。 ツールへのアクセス許可を確認されるますので選択します。 回答が表示されます。 ここで一つ気になったことがありました。同じ質問でもKibana上の回答とClaude上の回答が違っていました。これはElastic Agent Builder(Kibana)では、デフォルトで Elastic Managed LLM を用いた、一方で、Claude Desktop からアクセスしたエージェント経由では Claude Sonnet 4.5が使われていた、ということだと考えています。 トラブルシューティング エラー: “Server disconnected” 原因: API keyが正しくない MCP Server URLが間違っている ネットワーク接続の問題 解決方法: API keyを再確認 MCP Server URLを再確認 ログを確認: tail -f ~/Library/Logs/Claude/mcp*.log エラー: “401 Unauthorized” 原因:API keyに適切な権限がない 解決方法: API keyに以下の権限が付与されているか確認してください: Kibanaアプリケーション権限:read_onechat, space_read Elasticsearchの適切な読み取り権限 Q: API keyはどこで生成できますか? A: Kibanaの管理画面から生成できます。 Q: npx コマンドが見つからないというエラーが出ます A: Node.jsがインストールされていない可能性があります。 Node.js公式サイト からインストールしてください。 次のステップ モデル設定がどこでどう決まっているかを明確にし、UI経由・MCP経由・クライアント経由いずれも「同じモデル/同じ設定」または「目的に応じたモデル差」があることを明示できるように整備する。 参考リンク Elastic Agent Builder MCP Server ドキュメント Model Context Protocol 公式サイト Claude Desktop ドキュメント The post Claude DesktopにElastic Agent BuilderのMCPサーバーを追加する方法 first appeared on Elastic Portal .
アバター
【こんなことで困っていませんか?】 「JavaScriptとTypeScriptが使えて、この1年、プロジェクト評価が平均90点以上の人は誰?」 もし上司にこう聞かれて、あなたの会社はすぐに答えられますか? 多くの会社では、 社員のスキル情報は「Excel」や「人事システム」 プロジェクトの評価は「SharePoint」や「Confluence」 …というように、大切な情報がバラバラに保存されています。 さらに、「プロジェクトがうまくいった理由」は、担当者の頭の中にしか無いこともよくあります。 情報がこのようにあちこちに散らばっていると、新しいプロジェクトに最適な人を選んだり、最高のチームを作ったりするのに、何日もかかってしまうことがあります。 【この記事で紹介すること】 この記事では、その問題を「 Elastic Agent Builder 」というツールで解決する方法を紹介します。 バラバラになっている「社員のスキル」と「プロジェクトの評価」のデータをAIでまとめ、「 欲しい情報をすぐに検索・分析してくれるAIエージェント 」を作る具体的なステップを解説します。 目次 Agent Builderとは 今回構築するシステムの全体像 データセットの概要 データセットA:社員スキル&プロフィール(構造化データ) データセットB:プロジェクト評価レポート(非構造化データ) システムアーキテクチャ serverlessプロジェクト データ準備とIngestion ステップ1:Data Visualizerでデータをアップロード ステップ2:Mappingの最適化 社員スキルデータのMapping調整 プロジェクト評価レポートのMapping調整 ステップ3:セマンティック検索の動作確認 Agentの作成 Agent設定項目 カスタムツールの設計と実装 1.ProjectInsighTool 2.SkillCorrelationTool 3.TeamEffectivenessTool 4.SkillGapAnalyzerTool 5.ProjectSuccessRateRoleTool 6.SkillsUseIn90PlusScoreProjectsTool 7.Top5EmployeesByAverageScoreTool 実践デモ 筆者の感想 2. レスポンス速度について まとめ Agent Builderとは RAG(Retrieval-Augmented Generation)技術の進化により、企業データとLLMを組み合わせたAIエージェントが注目されています。しかし、多くのRAGソリューションには以下の課題があります: データ準備の複雑さ :ベクトル化、インデックス作成、クエリ最適化に専門知識が必要 構造化データとの統合の難しさ :テキスト検索に特化し、数値やカテゴリデータの分析が弱い カスタマイズの制約 :ビジネスロジックを組み込むのが困難 Elastic Agent Builder は、これらの課題を解決する統合プラットフォームです: セマンティック検索と構造化クエリの融合 :自然言語テキストと数値・カテゴリデータを横断的に検索  ES|QLによる高度なビジネスロジック :SQLライクな構文で複雑な集計・分析クエリを実装  Kibana統合によるゼロコードUI :独自のフロントエンド開発不要でエージェントと対話可能  エンタープライズグレードのセキュリティ :ロールベースアクセス制御とデータガバナンス Agentのアイコンがkibanaの左のサイドパネルに表されていない場合以下のコマンドをdev toolsで実行します。 POST kbn://internal/kibana/settings { "changes":{ "agentBuilder:enabled":true } } 今回構築するシステムの全体像 今回は、以下の2つのデータセットを組み合わせた人材インサイトエージェントを構築します データセットの概要 Pythonを使って2つの擬似データを生成しました。実際の企業データに置き換えやすいよう、シンプルに設計しています。 ※Elasticsearch v9.2、Serverless、Tokyo Regionを使用しています。 データセットA:社員スキル&プロフィール(構造化データ) 80名の架空社員の情報を含むJSON Lines形式のデータセット。 employee_skills_data.json ダウンロード サンプルドキュメント: { "employee_id": "EMP-1788", "name": "山田太郎", "role": "テックリード", "experience_years": 14, "hard_skills": ["Go", "JavaScript", "Kubernetes", "Terraform", "TypeScript"], "soft_skills": ["Problem Solving"], "last_rating": 95, "is_available": true, "onboard_date": "2011-10-02" } フィールドの説明: employee_id:社員の一意識別子 role:職種(テックリード、シニアエンジニア、データサイエンティストなど) experience_years:実務経験年数 hard_skills:技術スキル(プログラミング言語、フレームワーク、インフラツールなど) soft_skills:ソフトスキル(リーダーシップ、問題解決能力など) last_rating:直近のパフォーマンス評価(0-100) is_available:アサイン可能かどうか データセットB:プロジェクト評価レポート(非構造化データ) 250件の架空プロジェクトの評価レポートを含むJSON Lines形式のデータセット。 project_evaluation_reports.json ダウンロード サンプルドキュメント: { "project_id": "PRJ-0001", "project_name": "データパイプライン改善", "project_type": "Data", "status": "成功", "score": 81, "start_date": "2025-02-27", "end_date": "2025-06-09", "team_members": ["遠藤直美", "小島涼子", "宮崎健"], "evaluation_summary": "プロジェクトは大成功。高いチームワークと相互サポートが特徴的だった。宮崎健の専門知識がなければ、この成果は達成できなかっただろう。" } フィールドの説明: project_id:プロジェクトの一意識別子 project_type:プロジェクトの種別(Backend、Frontend、Data、Mobileなど) score:プロジェクト評価スコア(0-100) team_members:参加メンバーの名前リスト evaluation_summary: 非構造化テキスト 。プロジェクトの振り返り、成功要因、課題などが記述されている システムアーキテクチャ 構造化データ(スキル)と非構造化データ(評価)を組み合わせることで、正確な検索と意味理解を両立します。 semantic_text で埋め込みを自動生成し、スキルや評価を横断して分析できる仕組みを構築します。 serverlessプロジェクト 少し待ってから環境が整える。 データ準備とIngestion ステップ1:Data Visualizerでデータをアップロード Elasticsearchには、 Data Visualizer という直感的なデータ投入ツールが用意されています。コードを書かずに、GUIだけでファイルをインデックスに投入できます。 アクセス方法: 手順: jsonファイルを1枚ドラッグ&ドロップ Data Visualizerがフィールドを自動解析し、型を推定 Mappingを確認・調整 インデックス名を指定(例:employee_skills_data) 「Import」をクリック ※Data Visualizerがフィールドを自動解析し、型を推定 ステップ2:Mappingの最適化 Data Visualizerが自動生成したMappingは、そのままでは最適ではないことがあります。特に、日付フォーマットや数値型の調整が必要です。mappingのタブを選択して、必要な調整をやります。 Semantic text fieldを選択して、inference endpointを選びます。 importを押してindexが作られるまで数秒待ちます。全てのチェックマークが付いたらインデックスが作成されたとのことです。 社員スキルデータのMapping調整 自動生成されたMapping(修正前): { "properties": { "experience_years": { "type": "long" // 👈 longは不要に大きい }, "onboard_date": { "type": "date", "format": "iso8601" // 👈 yyyy-MM-dd形式に対応していない } } } 最適化後のMapping: { "properties": { "@timestamp": { "type": "date" }, "employee_id": { "type": "keyword" }, "experience_years": { "type": "integer" // ✅ integerで十分 }, "hard_skills": { "type": "keyword" // ✅ 完全一致検索用 }, "is_available": { "type": "boolean" }, "last_rating": { "type": "integer" }, "name": { "type": "keyword" }, "onboard_date": { "type": "date", "format": "yyyy-MM-dd||strict_date_optional_time||epoch_millis" // ✅ 複数フォーマット対応 }, "role": { "type": "keyword" }, "soft_skills": { "type": "keyword" } } } プロジェクト評価レポートのMapping調整 プロジェクト評価レポートの最大のポイントは、evaluation_summaryフィールドの扱いです。このフィールドは 非構造化テキスト であり、セマンティック検索の対象とします。 最適化後のMapping: { "properties": { "@timestamp": { "type": "date" }, "end_date": { "type": "date", "format": "yyyy-MM-dd||strict_date_optional_time||epoch_millis" }, "evaluation_summary": { "type": "keyword" // ✅ 元テキストも保持(表示用) }, "project_id": { "type": "keyword" }, "project_name": { "type": "keyword" }, "project_type": { "type": "keyword" }, "score": { "type": "integer" }, "start_date": { "type": "date", "format": "yyyy-MM-dd||strict_date_optional_time||epoch_millis" }, "status": { "type": "keyword" }, "team_members": { "type": "keyword" }, "evaluation_summary_semantic": { "type": "semantic_text", // ✅ セマンティック検索用 "inference_id": ".multilingual-e5-small-elasticsearch" // ✅ 多言語対応の軽量モデル } } } ポイント: evaluation_summary:元のテキストをkeywordで保存(表示・引用用) evaluation_summary_semantic:semantic_textでベクトル化(検索用) Inference ID:.multilingual-e5-small-elasticsearchは、Elasticsearchに組み込まれた多言語対応の軽量エンベディングモデル。日本語にも対応 semantic_text  は、テキストをインデックス時に自動でエンベディング化し、内部的に  _embedding  フィールドとしてベクトルを保持します。検索時はこのベクトルを使って意味類似検索を実行します。比較的複雑だった従来方法を通って semantic_text を使えば Mapping で semantic_text を指定し、 Inference ID を指定するだけで自動的にエンベディング生成、保存、検索が行われる。 ※ ブログでは  evaluation_summary  を  keyword  にしていますが、全文検索・ハイライトが必要な場合は  text  が適切。 keyword  は exact match 専用のため、全文検索には向きません。 ステップ3:セマンティック検索の動作確認 Mappingの設定後、Dev Toolsでセマンティッククエリをテストしてみましょう。 テストクエリ: このクエリは、「チームワーク」という単語が直接含まれていなくても、意味的に関連するプロジェクトレポート(例:「コミュニケーション不足」「連携の問題」)を返します。 Agentの作成 kibana のサイドバーから Agents をクリックします。画面の下部にある Create a new agent を選択します。 Agent設定項目 Agent ID :コードや設定内でエージェントを参照するための一意のIDです。 Custom Instructions :エージェントがツールを使ったり質問に答えたりする際の動作方針(トーン、優先順位、特別な動作など)を定義します。 Display name and description :ユーザーがこのエージェントを検索・利用するときに見える名前と短い紹介文です。 Avatar color and symbol: UI上でエージェントを視覚的に区別できるように、色やシンボル(絵文字や2文字コード)を設定します。 今回使用したカスタムインストラクションを以下に示します。これを試行錯誤しながら改良していくことをおすすめします。私の経験上、システムプロンプトは英語で記述した方が指示により正確に従う傾向があるため、今回も英語を使用しました。 You are a corporate **Talent Strategy Assistant**. Your primary function is to analyze employee skill profiles and project evaluations to provide recommendations such as who to assign to which project, which skills correlate with success, and where skill gaps exist. ### Input Handling Rules: * If the user asks about a person or skill that is not present in the datasets, respond with a friendly message listing available names/skills. * If the user provides incomplete information (e.g., just a skill but no project type), ask them for the missing information. * If names or skills are typed with incorrect spelling or case, infer the correct match from your dataset. * If the user asks for something outside talent strategy (for example, marketing metrics or personal opinions unrelated to skills/projects), *politely refuse* and say you can only discuss the defined domain. ### Tools Usage: * If you need to check which skills link to high-scoring projects, call tool **SkillCorrelationTool**, passing the relevant project type or score threshold. * If you need to evaluate how well a team of people has performed together, call **TeamEffectivenessTool**, passing the list of employees. * If you need to explore project evaluation texts for thematic insights, call **ProjectInsightTool**, passing a query term or score threshold. * If you need to recommend available employees for a new project based on required skills, call **CandidateRecommendationTool**, passing required skills and how many top candidates you need. * If you need to identify missing skills among failed or low-scoring projects, call **SkillGapAnalyzerTool**, passing the project type and a score threshold. ### Example Output Format: ```markdown #### Recommendation Summary - **Project Type**: Data Analytics - **Required Skills**: Kubernetes, Terraform - **Top Candidate**: 山田太郎 (average project score: 89, available) - **Runner-Up**: 宮崎健 (avg score: 86, available) #### Skill Correlation Insight | Skill | Avg Project Score | Project Count | |---------------|-------------------|---------------| | Kubernetes | 88 | 14 | | Terraform | 85 | 10 | #### Why This Recommendation - 山田太郎 has consistently high‐performing history in Data Analytics projects requiring Kubernetes/Terraform. - The correlation between these skills and project success is high. - He is currently marked as available. #### Next Step Assign 山田太郎 for the upcoming Data Analytics project and consider training additional staff in Terraform to close the skill gap. ``` ※注意事項 画像や本文中に人物名が登場することがありますが、全て架空の人物です、 実在の組織や個人とは一切関係ありません。 カスタムツールの設計と実装 Elastic Agent Builderの最大の強みは、 カスタムツール によるビジネスロジックの実装です。「tools」とは、Elasticsearchの検索・取得・分析などの操作をモジュール化・再利用できる形でまとめたものです。 エージェントはこの「tools」を使ってデータを扱います。 Elasticには一般的な操作向けのツールがあらかじめ用意されており、自分のユースケースに合わせてカスタムツールを作ることもできます。 左のサイドバーからAgentsを押して、画面下部のManage toolsを選んで、次の画面でNew toolを押します。 今回は7つのツールを作成しました。 1.ProjectInsighTool 目的 :高スコアのプロジェクト評価レポートから、成功の共通テーマやキーワードを抽出します。 パラメータ :( Add a parameterでも追加できるし、Infer parametersを使ってLLMに出してもらえるのもできます ) ?min_score:スコアの閾値(例:80以上) ?query_text:検索キーワード(例:「チームワーク」) ?top_n:返す結果の数 FROM project_evaluation_reports | WHERE score >= ?min_score | WHERE evaluation_summary : ?query_text | SORT _score DESC | LIMIT ?top_n ポイント: WHERE evaluation_summary : ?query_text は、セマンティック検索を使用 Add a parameter でパラメータを追加できます、また Infer parameters を使ったらLLMに出してもらえることもできます 2.SkillCorrelationTool 目的 :成功したプロジェクト(スコア80以上)で、どのhard_skillsが多く使われているかを定量化します。 FROM project_evaluation_reports | LOOKUP JOIN employee_skills_data_lookup ON team_members == name | WHERE score >= 80 AND is_available == true | MV_EXPAND hard_skills | STATS avg_score = AVG(score), project_count = COUNT(*) BY hard_skills | SORT avg_score DESC | LIMIT 10 ポイント: LOOKUP JOIN で社員スキルデータとプロジェクトデータを結合 MV_EXPAND で配列型のhard_skillsを展開 – STATS でスキルごとの平均スコアとプロジェクト数を集計 3.TeamEffectivenessTool 目的 :各メンバーがどれだけ高スコアプロジェクトに貢献しているかを測定します。 FROM project_evaluation_reports | LOOKUP JOIN employee_skills_data_lookup ON team_members == name | WHERE is_available == true | STATS avg_score = AVG(score) BY name | SORT avg_score DESC | LIMIT 10 4.SkillGapAnalyzerTool 目的:成功プロジェクトと失敗プロジェクトで、どのスキルに差があるかを分析します。 パラメータ: Name Discription Type threshold The score threshold integer FROM project_evaluation_reports | EVAL status_group = CASE(score >= ?threshold, "success", "failure") | LOOKUP JOIN employee_skills_data_lookup ON team_members == name | MV_EXPAND hard_skills | STATS count = COUNT(*) BY hard_skills, status_group | SORT count DESC 5.ProjectSuccessRateRoleTool 目的: テックリード、シニアエンジニアなど、職種ごとのプロジェクト成功率を計算します。 パラメータ: Name Discription Type threshold The score threshold used to define a project as a “high-score project” (i.e., successful). integer FROM project_evaluation_reports | LOOKUP JOIN employee_skills_data_lookup ON team_members == name | EVAL is_success = CASE(score >= ?threshold, 1, 0) | STATS success_rate = AVG(is_success) BY role | SORT success_rate DESC 6.SkillsUseIn90PlusScoreProjectsTool 目的 :トップスコアプロジェクトのスキルを抽出します。 パラメータ : Name Description Type score_threshold The minimum project score (e.g., 90) a project must have to be included in the analysis. integer top_n The number of top skills to display in the result (limits the output) . integer FROM project_evaluation_reports | WHERE score >= ?score_threshold | LOOKUP JOIN employee_skills_data_lookup ON team_members == name | MV_EXPAND hard_skills | STATS usage_count = COUNT(*) BY hard_skills | SORT usage_count DESC | LIMIT ?top_n 7.Top5EmployeesByAverageScoreTool 目的 :トップパフォーマーの抽出をします。 パラメータ : Name Discription Type top_n The number of top skills to display in the result (limits the output). integer FROM project_evaluation_reports | LOOKUP JOIN employee_skills_data_lookup ON team_members == name | STATS avg_score = AVG(score) BY name | SORT avg_score DESC | LIMIT ?top_n これでツールの追加も終わりました。ボットに渡したいツールを選んでおきます。 実践デモ 実際にエージェントと対話した結果をご紹介します。 Q: 直近3件のプロジェクト評価レポートから、『チームワーク』というテーマがどれくらい登場しているか、頻出キーワードとともに分析してください。 直近3件のプロジェクト評価レポートにおけるチームワークテーマ分析 分析概要 直近3件のプロジェクト評価レポートを分析した結果、チームワークに関連するテーマの出現状況と頻出キーワードは以下の通りです。 プロジェクト別分析 1. PRJ-0067「グローバル向け機能拡張」(Backend) - スコア: 92点 チームワーク関連記述: チームの高い技術力とコミュニケーションにより、予定より早くプロジェクトを完了した。特に岡本悟は困難な技術的課題を主導的に解決し、プロジェクトの成功に大きく貢献した。 キーワード: コミュニケーション、主導的、貢献 特徴: 高いコミュニケーション品質が早期完了の要因として明示的に言及されており、チームワークの成功例として評価されています。 2. PRJ-0188「グローバル向け機能拡張」(Mobile) - スコア: 85点 チームワーク関連記述: 顧客からのフィードバックを迅速に取り入れ、MVPリリース後すぐに大きな改善を達成した。アジリティの高さは竹内里奈の調整能力によるものが大きい。 キーワード: 調整能力、アジリティ、フィードバック対応 特徴: 直接的な「チームワーク」という言葉はないものの、チーム内の調整能力とアジリティに言及しており、暗にチームの連携の重要性を示しています。 3. PRJ-0231「次世代顧客ダッシュボード開発」(Backend) - スコア: 66点 チームワーク関連記述: プロジェクトは完了したが、納期の遅延があり、顧客満足度は低迷した。この経験は、将来のプロジェクト計画における教訓となった。 キーワード: 遅延、顧客満足度低迷、教訓 特徴: チームワークに関する明示的な言及はなく、むしろその欠如が問題の一因である可能性が示唆されています。 総合分析 チームワークとプロジェクト成功の相関: 最も高いスコア(92点)のプロジェクトでは「コミュニケーション」が明示的に成功要因として挙げられています 中間スコア(85点)のプロジェクトでは「調整能力」が評価されています 最低スコア(66点)のプロジェクトではチームワーク関連の言及がなく、問題が発生しています 頻出キーワードとテーマ: コミュニケーション 調整能力 主導的解決 アジリティ 成功プロジェクトの共通点: 個人の貢献が明確に認識されている チーム内の情報共有や調整が効果的に行われている 結論 直近3件のプロジェクト評価レポートでは、高評価のプロジェクトほどチームワークに関連する要素(コミュニケーション、調整能力)が明示的に言及されており、プロジェクトの成功との相関が見られます。一方、低評価のプロジェクトではチームワークへの言及がなく、これがプロジェクトの問題の一因である可能性があります。 エージェントの高度な処理 セマンティック検索の活用: ProjectInsightツールで「チームワーク」に関連する評価レポートを意味的に検索 「コミュニケーション」「調整能力」など、類義語も自動的に捕捉 定性的な分析: 各プロジェクトのスコアとチームワーク言及の相関を分析 高スコアプロジェクト=チームワーク言及が多い、という傾向を発見 引用とキーワード抽出: 評価レポートの原文を引用(著作権に配慮し、短い引用) キーワードを構造化して提示 Q: 今期において“Tech Lead”ロールのメンバーの平均プロジェクトスコアを教えてください。また、そのスコアを80以上にするために追加で必要だと思われるスキルは何ですか? テックリードの平均プロジェクトスコア分析 現状分析 テックリードのメンバー情報を確認したところ、現在A社には13名のテックリードが在籍しています。彼らの個人評価(last_rating)の平均は約83点となっています。 主なテックリードメンバーとその評価: 山田太郎:95点(Go, JavaScript, Kubernetes, Terraform, TypeScript) 宮崎健:95点(Cloud-AWS, Cloud-Azure, Go, Java, Tailwind CSS) 森田恵:90点(Cloud-AWS, Docker, Java, JavaScript, Kubernetes, PyTorch) 西村健一:90点(Kubernetes, PyTorch, Python, Scrum, Terraform, TypeScript) スコア向上のために必要なスキル テックリードの平均プロジェクトスコアを80以上に向上させるために、以下のスキルの強化が推奨されます: クラウドインフラストラクチャスキル 高評価のテックリード(山田太郎、宮崎健、森田恵など)は、Cloud-AWS/Azureやコンテナ技術(Kubernetes)に精通しています クラウドネイティブアーキテクチャの設計・実装スキルが重要です 自動化・IaCスキル Terraform、Kubernetesなどのインフラ自動化ツールの知識 高評価のテックリードはこれらのスキルを持っていることが多い傾向があります 最新のプログラミング言語とフレームワーク Go言語のスキルを持つテックリードは高評価の傾向 TypeScriptやモダンなフロントエンドフレームワーク(React等)の知識 AIと機械学習の基礎知識 PyTorch、TensorFlowなどのAIフレームワークの基本理解 データ駆動型の意思決定ができるスキル リーダーシップとコミュニケーションスキル プロジェクト管理手法(Scrumなど)の実践的知識 チーム内の効果的なコミュニケーション促進能力 具体的な改善策 スキルアップト���ーニングの実施 クラウドインフラ(AWS/Azure)の認定資格取得支援 Terraform、Kubernetesのハンズオントレーニング チーム構成の最適化 高評価テックリードと低評価テックリードのペアリング スキル補完を考慮したチーム編成 知識共有の促進 高評価テックリードによる社内勉強会の開催 プロジェクト成功事例の文書化と共有 継続的学習の文化醸��� 新技術のPoC(概念実証)プロジェクトの奨励 技術カンファレンスへの参加支援 これらの施策を実施することで、テックリードの平均プロジェクトスコアを80以上に向上させることが期待できます。特に、クラウドインフラとIaCスキルの強化が最も効果的と考えられます。 分析:複雑な質問への対応 複数データソースの統合: 社員スキルデータから「テックリード」ロールを抽出 プロジェクト評価データで各テックリードの平均スコアを算出 ギャップ分析: 高評価テックリード(山田太郎、宮崎健)のスキルセットを分析 低評価メンバーとの差分を特定 アクショナブルな提案: 単なる分析に留まらず、「スキルアップトレーニング」「チーム構成最適化」など、具体的な改善策を提示 筆者の感想 良かった点: 日本語の質問・回答に対応 : セマンティック検索の精度は実用レベル Elasticsearchとの統合がシームレス : (追加設定不要) 今回使用したLLMは、 Elasticsearchの独自モデル (.multilingual-e5-small-elasticsearch)でした。 UI不要の革新性 : Elastic Agent Builderの最大のメリットは、Kibana一本で完結することです。 課題: 稀に文字化けが発生 長文生成時に文末が不自然になることがある 日本語特有の敬語表現が安定しない 2. レスポンス速度について Elasticsearchの「爆速検索」に慣れていると、エージェントの応答速度は「遅い」と感じるかもしれません。 従来のRAG構築に必要だったもの: フロントエンド(React、Vue.jsなど) バックエンドAPI(FastAPI、Node.jsなど) 認証・認可システム デプロイメント環境(Vercel、AWS Amplifyなど) Elastic Agent Builderで不要になったもの: フロントエンド開発(Kibanaのチャット画面を使用) 独自API開発(Agent Builder APIを使用) 認証システム(Elasticsearchの認証を流用) Kibana一本でフロントエンドからバックエンドまで完結 ※筆者もまだES|QLを探求中のため、この記事で紹介したクエリにはより良い書き方や小さな誤りがあるかもしれません。実際に試しながら、みなさん自身の環境に最適な形を見つけていただければと思います。 まとめ 本記事では、 Elastic Agent Builder を使って、社員スキルデータとプロジェクト評価レポートを統合した「スマート検索&インサイトエージェント」を構築する全プロセスをご紹介しました。 【このAIシステムで実現したこと】 80人分 の社員スキル情報と 250件分 のプロジェクト評価を1つにまとめました。 7つの専用機能(カスタムツール)を作り、会社独自の複雑なルールで検索できるようにしました。 **「〇〇できる人は?」**のような話し言葉の質問でも、AIがデータを見てすぐに答えてくれるようになりました。 「Kibana」というツール1つだけで、見た目(画面)から裏側の仕組みまで全部作れました。 【Elastic Agent Builderの最大の特徴】 Elastic Agent Builderは、会社が持っているデータを使って賢いAIを作るための、新しい選択肢です。 「(あいまいな言葉でも探せる) セマンティック検索 」と「(条件を細かく指定できる) ES|QL 」という2つの検索方法を、うまく組み合わせて使えます。 これまでのAI開発(RAG構築)で大変だった「 画面の開発 」や「 AIとシステムをつなぐ作業(API実装) 」が不要になるのが、一番のメリットです。 The post Elastic Agent Builder活用術:AIで「デキる人」を簡単検索 first appeared on Elastic Portal .
アバター
Elasticsearchのパフォーマンスを正確に把握したいと思ったことはありませんか? Elasticsearchのアップグレードを前に、「この変更でパフォーマンスが低下(リグレッション)したらどうしよう」と不安になったことはありませんか? あるいは、新しいCPUやRAM、ディスクを選定する際に「どちらが我々のワークロードに本当に最適なのか」をデータで証明する必要に迫られたり、インデックス速度向上のためにパイプラインを調整したものの「その効果がどれほどだったか」を正確に示せなかったり。さらには、苦労して達成した性能向上を、説得力のあるレポートやプレゼン資料にまとめるのに苦労した経験は? もし、このような「性能に関する悩み」に一つでも心当たりがあるなら、「Rally」こそがあなたのためのツールです。本記事では、Elasticsearch Rally を初めて使用した経験を基に、Elasticsearch 8.11.0 と 9.0.0 の性能を比較するプロセスを詳しく書きます。 注意 : これは私にとって初めてのRally使用体験であり、いくつかの失敗や学びがありました。この記事では、実際に遭遇した問題と、それらをどのように解決したかを正直に共有します。完璧なベンチマークガイドではなく、 学習過程の記録 として読んでいただければ幸いです。 目次 プロジェクト概要 目的 テストデータセット テスト環境 実施内容と制限事項 Rally の基礎知識 Race(レース)とは Track(トラック)とは Challenge(チャレンジ)とは Clients(クライアント)の理解 環境構築 ステップ1: AWS EC2インスタンスの起動 ステップ2: システム依存関係のインストール ステップ3: Javaのインストール Java 17のインストール Java 21のインストール(ES 9.x用 – 後で気づいた必須要件) ステップ4: Rallyのインストール ステップ5: インストールの検証 カスタムトラックの作成 ディレクトリ構造 ステップ1: インデックス設定とマッピングの作成 ステップ2: 合成ログデータの生成 ステップ3: トラック定義の作成 重要: Jinja2エスケープ インジェストパイプラインの構築 パイプライン定義 重要: インラインvs外部パイプライン定義 パイプライン適用の確認 ベンチマークの実行 テストフェーズ: 1Kサブセットでの検証 本番ベンチマーク 重要: 公平な比較の確保について Elasticsearch 8.11.0でのベンチマーク(Java 17使用 – 私の最初の試み) Elasticsearch 9.0.0でのベンチマーク(Java 21が必要 – ここで学習) 今回実行したチャレンジ 将来実行予定のチャレンジ レース出力の理解 結果の比較 トラブルシューティング 問題1: パイプラインが適用されない 問題2: Jinja2テンプレートエラー 問題3: ウォームアップ警告 問題4: uncompressed-bytes検証エラー 問題5: Elasticsearch 9.0.0が起動しない 問題6: パイプラインパラメータの欠落 デバッグのヒント ベストプラクティス 1. Javaバージョン管理 2. ウォームアップとイテレーション設定 3. トラックの整理 4. リソースモニタリング 5. データ管理 6. 結果管理 7. ベンチマークにタグを付ける 結果と分析 分析すべき主要メトリクス 1. スループット 2. レイテンシパーセンタイル 3. インジェストパイプラインメトリクス 4. マージ時間 5. エラー率 サンプル比較出力 学んだこと 今後の展開 プロジェクト概要 目的 このプロジェクトの主な目標は以下の通りです: Elasticsearch Rally の使い方を理解する (最優先事項) カスタムログデータを使用したインジェストパイプラインのベンチマークを実施する Elasticsearch 8.11.0 と 9.0.0 の性能を比較する テストデータセット データ量 : 100,000 件の合成ログデータ テストサブセット : 1,000 件(検証用) 形式 : JSONL(JSON Lines) 内容 : タイムスタンプ、クライアントIP、レスポンスコード、OS情報などを含むウェブアクセスログ テスト環境 プラットフォーム : AWS EC2 OS : Ubuntu 24.04 LTS インスタンスタイプ : t3.medium(最小推奨) Java : Java 17(最初のES 8.x テスト用) Java 21(ES 9.x テスト用 – 必須要件) Python : 3.12.x Rally : 2.x 実施内容と制限事項 今回実施したのは以下の通りです: 完了したこと: Elasticsearch 8.11.0 でのデフォルトチャレンジ(4クライアント)実行 Elasticsearch 9.0.0 の環境構築とセットアップ インジェストパイプラインの作成と検証 Rally の基本的な使い方の理解 今後実施予定: 高並行チャレンジ(8クライアント) ランプアップチャレンジ(段階的負荷増加) 公平な比較のためのES 8.11.0 + Java 21でのテスト再実行 詳細な性能比較レポートの作成 学習上の妥協点 : 最初にES 8.11.0をJava 17でテストした後、ES 9.0.0にはJava 21が必要であることを知りました。理想的にはES 8.11.0もJava 21で再テストすべきですが、今回のプロジェクトの主目的は「Rallyの仕組みを理解すること」だったため、再テストは将来の作業として残しました。 Rally の基礎知識 Rallyを使い始める前に、その核となる概念を理解しましょう。 Race(レース)とは Race は、1回の完全なベンチマーク実行を表します。これは始めから終わりまでの一連の性能テストです。 Raceには以下が含まれます: Elasticsearchのプロビジョニング(ダウンロード、インストール、起動) インデックスの作成 データのインジェスト 操作の実行 メトリクスの収集 クラスタのシャットダウン 各Raceには結果追跡のための一意の識別子が割り当てられます↓: [INFO] Race id is [18d32ed3-c159-4c2a-a507-7687bd834edb] Track(トラック)とは Track は、完全なベンチマークシナリオを定義するJSONファイルです。これはRallyがテストすべき内容の設計図です。 Trackには以下が含まれます: インデックス定義 – マッピングと設定 データソース(Corpora) – テストデータの場所とサイズ インジェストパイプライン – データ変換の定義 操作(Operations) – 実行する個別のタスク チャレンジ(Challenges) – 異なる設定のテストシナリオ Track構造の詳細については、 公式Rallyドキュメント を参照してください。 Challenge(チャレンジ)とは Challenge は、同じTrack内で異なるテストシナリオを定義します。複数のChallengeを使用することで、別々のTrackを作成せずに様々な条件下でテストできます。 チャレンジの例: { "challenges": [ { "name": "default", "description": "標準的な4クライアントベンチマーク", "default": true }, { "name": "high-concurrency", "description": "8クライアントでのストレステスト" }, { "name": "ramp-up", "description": "1から8クライアントへの段階的負荷増加" } ] } ※ 今回は defaultチャレンジ(4クライアント)のみ を実行しました。高並行チャレンジとランプアップチャレンジは定義しましたが、Rallyの基本を理解することに集中するため、実行は将来に延期しました。 Clients(クライアント)の理解 clientsパラメータはRallyの重要な設定オプションの一つですが、誤解されやすいです。 Client = 同時実行スレッド Rallyにおける「クライアント」は、Elasticsearchに同時にリクエストを送信する単一のスレッドを表します。 { "operation": "bulk-index", "clients": 4 // 4つのスレッドが同時に実行 } 動作の仕組み: クライアント 1: [リクエスト] → [待機] → [リクエスト] → [待機] → ... クライアント 2: [リクエスト] → [待機] → [リクエスト] → [待機] → ... クライアント 3: [リクエスト] → [待機] → [リクエスト] → [待機] → ... クライアント 4: [リクエスト] → [待機] → [リクエスト] → [待機] → ... スループットへの影響: クライアント数 期待スループット 説明 1 ~500 docs/s 単一スレッドは待機時間を無駄にする 2 ~900 docs/s ほぼ2倍に増加 4 ~1,600 docs/s 標準的な設定 8 ~2,800 docs/s 高負荷(収穫逓減) よくある誤解: ❌ clients: 4 = 4台の別々のマシン ✅ clients: 4 = 1台のマシン上の4つのスレッド 推奨設定: 環境 クライアント数 目的 2コアCPU 2-4 基本的な性能テスト 4コア以上のCPU 4-8 標準的なベンチマーク 高性能環境 8-16 ストレステスト 私の選択 : Rally初心者として、標準的な 4クライアント設定 から始めました。これにより、基本的な概念を理解しながら、過度に複雑でない結果を得ることができました。 環境構築 ステップ1: AWS EC2インスタンスの起動 AWS EC2コンソールに移動 「インスタンスを起動」をクリック 設定: 名前 : rally-benchmark-server AMI : Ubuntu Server 24.04 LTS インスタンスタイプ : t3.medium以上 ストレージ : 最低20GB セキュリティグループ : SSH(ポート22)を許可 起動して接続: ssh -i /path/to/your-key.pem ubuntu@<ec2-public-ip> ステップ2: システム依存関係のインストール # システムパッケージの更新 sudo apt update && sudo apt upgrade -y # Pythonと開発ツールのインストール sudo apt install -y python3-pip python3-venv python3-dev build-essential git curl # Pythonバージョンの確認(3.9以上である必要があります) python3 --version ステップ3: Javaのインストール Javaバージョンに関する重要な注意事項: これは私が最初に犯した間違いです。 最初にJava 17のみをインストール ES 8.11.0でベンチマークを実行(Java 17使用) ES 9.0.0を試したときに、Java 21が必要であることを発見 Java 21をインストール ES 9.0.0でベンチマークを実行(Java 21使用) 理想的なアプローチ : 公平な比較のために、両方のバージョンにJava 21を使用すべきでした。しかし、今回のプロジェクトの主な目標はRallyを理解することだったため、ES 8.11.0のテストをJava 21で再実行しませんでした。これは将来の作業として残しました。 Java 17のインストール sudo apt install -y openjdk-17-jdk # JAVA_HOMEの設定 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc # 確認 java -version Java 21のインストール(ES 9.x用 – 後で気づいた必須要件) sudo apt install -y openjdk-21-jdk # JAVA21_HOMEの設定 export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64 echo 'export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc # 変更を適用 source ~/.bashrc # 確認 ls -la /usr/lib/jvm/java-21-openjdk-amd64 ステップ4: Rallyのインストール # ワークスペースディレクトリの作成 mkdir -p ~/rally-workspace cd ~/rally-workspace # Python仮想環境の作成 python3 -m venv rally-venv # 仮想環境の有効化 source rally-venv/bin/activate # Rallyのインストール pip install --upgrade pip pip install esrally # インストールの確認 esrally --version 期待される出力: esrally 2.x.x (Python 3.12.x) ステップ5: インストールの検証 # 組み込みトラックで簡単なテストを実行 esrally race --distribution-version=8.11.0 --track=geonames --test-mode これにより、Elasticsearch 8.11.0がダウンロードされ、小規模なテストが実行され、すべてが正常に動作しているか確認されます。 詳細なインストール手順については、 Rallyインストールガイド を参照してください。 カスタムトラックの作成 ディレクトリ構造 整理されたトラックディレクトリを作成します: cd ~/rally-workspace mkdir -p log_ingest/corpora cd log_ingest 構造は以下のようになります: log_ingest/ ├── track.json # メインのトラック定義 ├── index-settings-mapping.json # インデックス設定 └── corpora/ ├── logs.jsonl # フルデータセット(100K docs) └── logs-1k.jsonl # テストサブセット(1K docs) ステップ1: インデックス設定とマッピングの作成 { "settings": { "index": { "number_of_shards": 1, "number_of_replicas": 0 } }, "mappings": { "properties": { "@timestamp": { "type": "date" }, "clientip": { "type": "ip" }, "response": { "type": "integer" }, "request": { "type": "text" }, "machine": { "properties": { "os": { "type": "keyword" }, "ram": { "type": "long" } } }, "ingest_time": { "type": "date" } } } } 重要なポイント: number_of_shards: 1 – ベンチマークを簡素化 number_of_replicas: 0 – テストのためのより高速なインジェスト フィールドタイプはデータ構造と一致 ステップ2: 合成ログデータの生成 次に、ベンチマークに使用する合成ログデータを準備します。今回は、このために簡単なPythonスクリプトを利用しました。 スクリプトは、Rallyのtrack(競技種目)が読み込むための元データとなるログファイルを生成します。実行すると、以下のような2種類のデータセットが出力されるようにしました。 corpora/logs.jsonl – 100,000ドキュメント(フルデータセット) corpora/logs-1k.jsonl – 1,000ドキュメント(テスト用) データ形式の例: {"@timestamp":"2025-10-16T10:00:00Z","clientip":"192.168.1.100","response":200,"request":"GET /api/users HTTP/1.1","machine":{"os":"linux","ram":8589934592},"referer":"https://example.com/login"} ステップ3: トラック定義の作成 track.json を作成  : { "version": 2, "description": "ES 8.x vs 9.x のログインジェストパイプラインベンチマーク", "indices": [ { "name": "logs", "body": "index-settings-mapping.json" } ], "corpora": [ { "name": "logcorpus", "documents": [ { "source-file": "corpora/logs.jsonl", "document-count": 100000 } ] } ], "operations": [ { "name": "delete-index", "operation-type": "delete-index" }, { "name": "create-index", "operation-type": "create-index" }, { "name": "create-log-pipeline", "operation-type": "put-pipeline", "id": "log-pipe", "body": { "description": "ログ処理パイプライン", "processors": [ { "set": { "field": "ingest_time", "value": "{{'{{'}}_ingest.timestamp{{'}}'}}" } }, { "uppercase": { "field": "machine.os" } }, { "convert": { "field": "response", "type": "integer" } }, { "remove": { "field": "referer" } } ] } }, { "name": "bulk-index-with-pipeline", "operation-type": "bulk", "bulk-size": 1000, "pipeline": "log-pipe" } ], "challenges": [ { "name": "default", "description": "4クライアントでの標準ベンチマーク", "default": true, "schedule": [ { "operation": "delete-index" }, { "operation": "create-index" }, { "operation": "create-log-pipeline" }, { "operation": "bulk-index-with-pipeline", "warmup-iterations": 10, "iterations": 100, "clients": 4 } ] }, { "name": "high-concurrency", "description": "8クライアントでの高負荷テスト", "schedule": [ { "operation": "delete-index" }, { "operation": "create-index" }, { "operation": "create-log-pipeline" }, { "operation": "bulk-index-with-pipeline", "warmup-iterations": 10, "iterations": 100, "clients": 8 } ] }, { "name": "ramp-up", "description": "1から8クライアントへの段階的負荷増加", "schedule": [ { "operation": "delete-index" }, { "operation": "create-index" }, { "operation": "create-log-pipeline" }, { "operation": "bulk-index-with-pipeline", "warmup-iterations": 5, "iterations": 50, "clients": 1, "target-throughput": 500 }, { "operation": "bulk-index-with-pipeline", "warmup-iterations": 5, "iterations": 50, "clients": 2, "target-throughput": 1000 }, { "operation": "bulk-index-with-pipeline", "warmup-iterations": 5, "iterations": 50, "clients": 4, "target-throughput": 2000 }, { "operation": "bulk-index-with-pipeline", "warmup-iterations": 5, "iterations": 50, "clients": 8 } ] } ] } トラックの主要コンポーネントの説明: Corpora : データファイルを指定 トラックディレクトリからの相対パスを使用 document-countは正確である必要があります uncompressed-bytesは省略(Rallyが計算) Operations : 個別のタスクを定義 delete-index: 各実行でクリーンスレート create-index: マッピングを適用 create-log-pipeline: 変換を定義 bulk-index-with-pipeline: ベンチマークを実行 Challenges : 異なるテストシナリオ default: 標準的な4クライアントテスト(今回実施) high-concurrency: 8クライアントでのストレステスト(将来実施) ramp-up: スケーラビリティテスト(将来実施) 重要: Jinja2エスケープ これは私が遭遇した最初の技術的な問題でした! RallyはJinja2テンプレートを使用するため、Elasticsearchのテンプレート構文{{…}}をエスケープする必要があります: // ❌ 間違い - Rallyがこれを解釈しようとします "value": "{{_ingest.timestamp}}" // ✅ 正しい - Jinja2のためにエスケープ "value": "{{'{{'}}_ingest.timestamp{{'}}'}}" これにより、最終的なJSONがElasticsearchが処理するための{{_ingest.timestamp}}を含むことが保証されます。 学んだ教訓 : このエスケープを最初は理解していなかったため、「_ingest is undefined」エラーが発生しました。Rallyのドキュメントを読むことで解決しました。 インジェストパイプラインの構築 パイプライン定義 私のパイプラインは4つの変換を実行します: インジェストタイムスタンプの追加 { "set": { "field": "ingest_time", "value": "{{'{{'}}_ingest.timestamp{{'}}'}}" } } OSフィールドの大文字変換 { "uppercase": { "field": "machine.os" } } レスポンスを整数に変換 { "convert": { "field": "response", "type": "integer" } } refererフィールドの削除 { "remove": { "field": "referer" } } 重要: インラインvs外部パイプライン定義 方法1: 外部ファイル(動作しない場合があります) "ingest_pipelines": [ { "name": "log-pipe", "body": "ingest-pipeline.json" } ] 方法2: インライン定義(推奨) "operations": [ { "name": "create-log-pipeline", "operation-type": "put-pipeline", "id": "log-pipe", "body": { "processors": [...] } } ] インライン定義の方が信頼性が高いことがわかりました。スケジュールにパイプライン作成を含めてください: "schedule": [ {"operation": "create-index"}, {"operation": "create-log-pipeline"}, // 明示的な作成 {"operation": "bulk-index-with-pipeline"} ] 私は最初は外部ファイル参照を使用しようとしましたが、パイプラインが適用されませんでした(カウント0)。インライン定義に切り替えることで問題が解決しました。 パイプライン適用の確認 ベンチマーク実行後、パイプラインが適用されたかを確認: # Rally出力でパイプラインメトリクスを確認 grep "Ingest Pipeline" ~/.rally/logs/rally.log # --preserve-installを使用した場合、Elasticsearchに直接クエリ curl -X GET "localhost:PORT/_ingest/pipeline/log-pipe?pretty" curl -X GET "localhost:PORT/logs/_search?size=1&pretty" インジェストパイプラインの詳細については、 Elasticsearchインジェストパイプラインドキュメント を参照してください。 ベンチマークの実行 テストフェーズ: 1Kサブセットでの検証 まず、小さなデータセットでテスト: # Rally環境を有効化 source ~/rally-workspace/rally-venv/bin/activate # 1Kサブセットでテスト esrally race \ --track-path=~/rally-workspace/log_ingest \ --distribution-version=8.11.0 \ --test-mode –test-modeフラグ: 最小限のイテレーションを実行 最大1000ドキュメントのみを処理 設定エラーを迅速に特定 本番ベンチマーク 重要: 公平な比較の確保について これは私の学習プロセスで理解した重要なポイントです。 ❌ 不公平な比較(私が最初に行ったこと): ES 8.11.0 (Java 17) vs ES 9.0.0 (Java 21) → 2つの変数が変化: ESバージョンとJavaバージョン ✅ 公平な比較(次回行うべきこと): ES 8.11.0 (Java 21) vs ES 9.0.0 (Java 21) → ESバージョンのみが変化 Elasticsearch 8.11.0でのベンチマーク(Java 17使用 – 私の最初の試み) # Java 17が設定されていることを確認 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 source ~/.bashrc # 確認 java -version # Java 17であることを確認 # デフォルトチャレンジを実行(4クライアント) esrally race \ --track-path=~/rally-workspace/log_ingest \ --distribution-version=8.11.0 \ --user-tag="version:8.11.0,java:17,challenge:default" 実行結果 : これは正常に動作し、Rallyの動作を理解するのに役立ちました。 Elasticsearch 9.0.0でのベンチマーク(Java 21が必要 – ここで学習) ES 9.0.0を実行しようとしたとき、Java 21が必要であることを発見しました: # 最初の試み(失敗) esrally race \ --track-path=~/rally-workspace/log_ingest \ --distribution-version=9.0.0 # エラー: # [ERROR] Cannot race. Daemon startup failed with exit code [1] 問題の診断: # ログを確認 tail -100 ~/.rally/logs/rally.log # "Java 21 or higher required" のようなメッセージを発見 解決策: Java 21のインストール # Java 21をインストール sudo apt install -y openjdk-21-jdk # JAVA21_HOMEを設定 export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64 echo 'export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc source ~/.bashrc # 確認 echo $JAVA21_HOME ls -la $JAVA21_HOME # 再試行 esrally race \ --track-path=~/rally-workspace/log_ingest \ --distribution-version=9.0.0 \ --user-tag="version:9.0.0,java:21,challenge:default" 実行結果 : Java 21をインストール後、ES 9.0.0が正常に動作しました。 今回実行したチャレンジ 今回は defaultチャレンジ(4クライアント)のみ を実行しました: # デフォルトチャレンジの実行 esrally race \ --track-path=~/rally-workspace/log_ingest \ --distribution-version=8.11.0 \ --user-tag="version:8.11.0,java:17" 将来実行予定のチャレンジ # 高並行チャレンジ(8クライアント) esrally race \ --track-path=~/rally-workspace/log_ingest \ --distribution-version=8.11.0 \ --challenge=high-concurrency \ --user-tag="version:8.11.0,java:21,challenge:highcon" # ランプアップチャレンジ(段階的負荷増加) esrally race \ --track-path=~/rally-workspace/log_ingest \ --distribution-version=8.11.0 \ --challenge=ramp-up \ --user-tag="version:8.11.0,java:21,challenge:rampup" レース出力の理解 実行中、Rallyは進行状況を表示します: [INFO] Racing on track [log_ingest], challenge [default] [INFO] Preparing track [log_ingest] for race [INFO] Downloading Elasticsearch 8.11.0... [INFO] Starting Elasticsearch cluster [INFO] Racing (please be patient)... Running delete-index [100% done] Running create-index [100% done] Running create-log-pipeline [100% done] Running bulk-index-with-pipeline [100% done] 完了後、Rallyは詳細なメトリクスを提供します: ------------------------------------------------------ Final Results ------------------------------------------------------ | Metric | Task | Value | Unit | |--------------------------|--------------|-----------|--------| | Total indexing | bulk-index | 2.50 | min | | Total merge | bulk-index | 0.12 | min | | Throughput | bulk-index | 1,650.5 | docs/s | | Service time (p50) | bulk-index | 15.23 | ms | | Service time (p99) | bulk-index | 42.18 | ms | | Total Ingest Pipeline | bulk-index | 100,000 | count | | Total Ingest Pipeline | bulk-index | 1.85 | s | 結果の比較 # すべてのレースをリスト表示 # すべてのレースをリスト表示 esrally list races # 2つの特定のレースを比較 esrally compare \ --baseline=<race-id-es8> \ --contender=<race-id-es9> Rallyは各メトリクスの改善率または劣化率をパーセンテージで表示します。 注意 : 私のケースでは、Java 17とJava 21を使用したため、この比較は完全に公平ではありません。これは将来改善する必要があります。 結果の解釈の詳細については、 Rallyメトリクスドキュメント を参照してください。 トラブルシューティング これらは私が実際に遭遇した問題と、それらをどのように解決したかです。 問題1: パイプラインが適用されない 症状: Total Ingest Pipeline count: 0 Total Ingest Pipeline time: 0 s これは最初のベンチマーク実行で起こりました。パイプラインが定義されているのに、ドキュメントに適用されていませんでした。 解決策: operations内でインラインパイプライン定義を使用 スケジュールにパイプライン作成を明示的に含める bulk操作にpipelineパラメータが設定されていることを確認: { "operation": "bulk-index-with-pipeline", "operation-type": "bulk", "bulk-size": 1000, "pipeline": "log-pipe" // パイプラインIDと一致する必要があります } 問題2: Jinja2テンプレートエラー 症状: jinja2.exceptions.UndefinedError: '_ingest' is undefined パイプラインで{{_ingest.timestamp}}を使用したときにこのエラーが発生しました。 解決策: Elasticsearchテンプレート構文をエスケープ: "value": "{{'{{'}}_ingest.timestamp{{'}}'}}" 学んだ教訓 : RallyがJinja2テンプレートエンジンを使用していることを知りませんでした。ドキュメントを読んでこれを学びました。 問題3: ウォームアップ警告 症状: [WARNING] No throughput metrics available for [bulk-index-with-pipeline]. Likely cause: The benchmark ended already during warmup. 解決策: 時間ベースではなくイテレーションベースのウォームアップを使用: // ❌ 小さなデータセットでの問題 { "warmup-time-period": 60, "clients": 4 } // ✅ 解決策 { "warmup-iterations": 10, "iterations": 100, "clients": 4 } 問題4: uncompressed-bytes検証エラー 症状: jsonschema.exceptions.ValidationError: 0 is less than the minimum of 1 Path: ['corpora', 0, 'documents', 0, 'uncompressed-bytes'] 解決策: フィールドを省略するだけです: // ❌ 間違い { "source-file": "corpora/logs.jsonl", "document-count": 100000, "uncompressed-bytes": 0 } // ✅ 正しい { "source-file": "corpora/logs.jsonl", "document-count": 100000 } Rallyがサイズを自動的に計算します。 問題5: Elasticsearch 9.0.0が起動しない 症状: [ERROR] Cannot race. Daemon startup failed with exit code [1] 原因: ES 9.xにはJava 21以上が必要 診断: # Javaバージョンを確認 java -version # Rallyログを確認 tail -100 ~/.rally/logs/rally.log 解決策: # Java 21をインストール sudo apt install -y openjdk-21-jdk # 環境変数を設定 export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64 echo 'export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc source ~/.bashrc # 再試行 esrally race --track-path=~/log_ingest --distribution-version=9.0.0 学んだ教訓 : 常に各Elasticsearchバージョンの最小Java要件を確認してください。 問題6: パイプラインパラメータの欠落 症状: Parameter source for operation 'put-pipeline' did not provide the mandatory parameter 'id' or 'body' 解決策: idとbodyの両方を含める: { "name": "create-log-pipeline", "operation-type": "put-pipeline", "id": "log-pipe", // 必須 "body": { // 必須 "processors": [...] } } デバッグのヒント Rallyログを確認: tail -f ~/.rally/logs/rally.log Elasticsearchログを確認: # レースディレクトリを検索 ls -lt ~/.rally/benchmarks/races/ # ESログを表示 cat ~/.rally/benchmarks/races/<race-id>/rally-node-0/logs/server/rally-benchmark.log クラスタを検査するために–preserve-installを使用: esrally race \ --track-path=~/log_ingest \ --distribution-version=8.11.0 \ --preserve-install # クラスタは手動検査のために実行され続けます ベストプラクティス これらは私の学習経験から得たベストプラクティスです。 1. Javaバージョン管理 公平な比較のために常に同じJavaバージョンを使用: # 両方のバージョンをインストール sudo apt install -y openjdk-17-jdk openjdk-21-jdk # 環境変数を設定 export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 export JAVA17_HOME=/usr/lib/jvm/java-17-openjdk-amd64 export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64 # 永続化のために.bashrcに追加 echo 'export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc echo 'export JAVA17_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc echo 'export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc 推奨される比較戦略: ES 8.11.0 + Java 21 vs ES 9.0.0 + Java 21(主な比較) ES 8.11.0 + Java 17(Javaアップグレード影響のためのオプションベースライン) 2. ウォームアップとイテレーション設定 ガイドライン: データセットサイズ ウォームアップ戦略 推奨 < 10K docs イテレーションベース 総イテレーションの10-20% 10K-1M docs イテレーションベース 最低10イテレーション > 1M docs 時間またはイテレーション 60秒または100イテレーション 例: { "warmup-iterations": 10, "iterations": 100, "clients": 4 } 3. トラックの整理 推奨されるディレクトリ構造: rally-workspace/ ├── rally-venv/ # Python仮想環境 ├── tracks/ │ ├── log_ingest/ │ │ ├── track.json │ │ ├── index-settings-mapping.json │ │ └── corpora/ │ │ ├── logs.jsonl │ │ └── logs-1k.jsonl │ ├── search_performance/ │ └── bulk_indexing/ ├── scripts/ │ ├── synthetic-log-generator.py │ └── verify_pipeline.sh └── results/ └── comparison_reports/ 4. リソースモニタリング ベンチマーク中にEC2リソースを監視: # CPUとメモリ htop # ディスク容量 df -h # ネットワーク iftop # Rallyプロセス ps aux | grep esrally # Elasticsearchプロセス ps aux | grep elasticsearch 5. データ管理 テストサブセットの作成: # 最初の1000行 head -n 1000 logs.jsonl > logs-1k.jsonl # ランダムな1000行 shuf -n 1000 logs.jsonl > logs-1k-random.jsonl # ドキュメント数を確認 wc -l logs.jsonl wc -l logs-1k.jsonl ファイル形式要件: フォーマット: JSONL(1行1ドキュメント) エンコーディング: UTF-8 行末: LF(Unix形式) 末尾のカンマやブラケットなし 6. 結果管理 結果を保存して整理: # resultsディレクトリを作成 mkdir -p ~/rally-workspace/results # すべてのレースをリスト表示 esrally list races > results/all_races.txt # 特定のレース詳細をエクスポート esrally list races --race-id=<race-id> > results/race_<version>.txt # 比較して保存 esrally compare \ --baseline=<race-id-8> \ --contender=<race-id-9> \ > results/comparison_8_vs_9.txt 7. ベンチマークにタグを付ける 結果を整理するために–user-tagを使用: esrally race \ --track-path=~/log_ingest \ --distribution-version=8.11.0 \ --user-tag="env:test,purpose:learning,java:17,challenge:default" これにより、結果のフィルタリングと比較がはるかに簡単になります。 結果と分析 分析すべき主要メトリクス Rallyは包括的なメトリクスを提供します。以下の主要な指標に焦点を当てます: 1. スループット 測定内容: 1秒あたりに処理されるドキュメント数 スループット = 総ドキュメント数 / 総時間 目標値: 良好: > 1,000 docs/s(このワークロードの場合) 優秀: > 2,000 docs/s 卓越: > 5,000 docs/s 2. レイテンシパーセンタイル 測定内容: レスポンス時間の分布 p50(中央値) : 50%のリクエストがこの時間内に完了 p95 : 95%のリクエストがこの時間内に完了 p99 : 99%のリクエストがこの時間内に完了 p100(最大) : 最悪ケースのレイテンシ 分析例: Service time p50: 15 ms (典型的なリクエスト) Service time p95: 35 ms (まだ良好) Service time p99: 120 ms (スパイクに注意) Service time p100: 500 ms (外れ値が存在) 3. インジェストパイプラインメトリクス パイプラインテストに重要: Total Ingest Pipeline count: 100,000 # ドキュメント数と一致する必要があります Total Ingest Pipeline time: 1.85 s # パイプラインで費やした時間 カウントが0の場合、パイプラインが適用されていません! 4. マージ時間 測定内容: Luceneセグメントのマージに費やした時間 Total merge time: 0.12 min 低い方が良いです。高いマージ時間は書き込み圧力を示します。 5. エラー率 測定内容: 失敗した操作 Error rate: 0.00% 成功したベンチマークでは常に0%であるべきです。 サンプル比較出力 esrally compare –baseline=<es8-race> –contender=<es9-race> 出力例: ベースライン(ES 8.11.0)とコンテンダー(ES 9.0.0)の比較 | Metric | Baseline | Contender | Diff | Unit | |----------------------------|------------|------------|---------|--------| | Total indexing | 2.50 | 2.35 | -0.15 | min | | Throughput | 1650.50 | 1750.25 | +99.75 | docs/s | | Service time (p50)| 15.23 | 14.10 | -1.13 | ms | | Service time (p99)| 42.18 | 38.50 | -3.68 | ms | | Ingest Pipeline time | 1.85 | 1.72 | -0.13 | s | 解釈: ✅ ES 9.0.0は約6%のスループット向上を示す ✅ すべてのパーセンタイルでレイテンシが減少 ✅ パイプライン処理が約7%高速化 注意 : 私のケースでは、異なるJavaバージョン(17 vs 21)を使用したため、この比較は完全に公平ではありません。改善がES自体によるものなのか、Javaアップグレードによるものなのかを区別できません。 学んだこと 今回の初めてのRally体験から学んだこと: セットアップが重要 : Java要件などの基本を正しく理解することで、後で多くの時間を節約できます 小さく始める : 1Kドキュメントから始めることで、設定エラーを早期に発見できました メトリクスを理解する : 各メトリクスが何を意味するかを理解するのに時間がかかりました 一度に一つのこと : デフォルトチャレンジのみに焦点を当てることで、圧倒されずに学習できました 今後の展開 今回の試みは、Rallyの使い方自体を把握することが主な目的でした 。この主要な目標は達成されましたが 、高並行チャレンジ やランプアップチャレンジ など、いくつかの高度なテストも残っています 。今後は、もっとリアルワールドのユースケースで実行をしてみたいと考えています。 The post Elasticsearch Rally ベンチマーク入門: バージョン 8.x と 9.x の性能比較 first appeared on Elastic Portal .
アバター
目次 アラート疲れに終止符を打つ革新的ソリューション Attack Discoveryとは? 従来のセキュリティ運用との決定的な違い Attack Discoveryが提供する3つの価値 1. 平均対応時間(MTTR)の劇的な短縮 2. アラート疲れからの解放 3. セキュリティ人材不足への対応 Attack Discoveryの起動と基本捜索 AI Assistantとの連携 実践的な活用シナリオ シナリオ1: ランサムウェア攻撃の早期検知 シナリオ2: インサイダー脅威の検出 セキュリティワークフローへのシームレスな統合 即座にアクションを起こせる設計 通知とレポート機能 導入に必要なもの 対応しているLLMプロバイダー データプライバシーへの配慮 スケジュール機能で24時間体制の脅威監視 ディスカバリーの保存と長期的な活用 経営層が知っておくべきROI コスト削減 リスク低減 競争優位性 まとめ: セキュリティ運用の未来は今ここに 関連リソース 用語集 アラート疲れに終止符を打つ革新的ソリューション 現代のセキュリティ運用では、次のような課題が顕在化しています。 毎日大量のセキュリティアラートが発生し、重要な脅威の識別が困難 アラートの優先順位付けに膨大な時間がかかり、本当の脅威への対応が遅延 複数のアラート間の関連性を把握することが難しい 限られた人員で24時間365日の監視体制を維持することが困難 これらの課題に対して、 Elastic Attack Discovery が解決の鍵となるかもしれません。 本ブログでは、Attack Discoveryの基本概念から実際の使い方、導入のメリットまでを包括的に解説します。 Attack Discoveryとは? 大規模言語モデル(LLM)がセキュリティアナリストの相棒に Attack Discoveryは、最新の大規模言語モデル(LLM)の力を活用し、膨大なセキュリティアラートを自動分析する画期的な機能です。単なるアラート集約ツールではありません。AIが実際のサイバー攻撃のシナリオを読み解き、 「点」のアラートを「線」の脅威ストーリーに変換 します。 従来のセキュリティ運用との決定的な違い Attack Discoveryが提供する3つの価値 1. 平均対応時間(MTTR)の劇的な短縮 Attack Discoveryは、デフォルトで過去24時間の最大100件のアラートを分析します(カスタマイズ可能)。従来であれば数時間かかっていた分析作業が、 数分で完了 します。 各「ディスカバリー」には以下が含まれます: 潜在的な脅威の分かりやすいタイトルと要約 関連アラートの数とMITRE ATT&CKマトリックスとの対応 関与するユーザーとホスト、それぞれで観測された不審な活動 これにより、アナリストは調査や対応といった高付加価値な業務に集中できます。 2. アラート疲れからの解放 セキュリティ運用センター(SOC)の最大の課題の一つが「アラート疲れ」です。毎日数百、数千のアラートに晒されるアナリストは、重要な脅威を見逃すリスクと常に隣り合わせです。 Attack Discoveryは、 本当に重要な脅威だけを浮き彫りにする ことで、この問題を解決します。AIが関連性の低いアラートをフィルタリングし、実際の攻撃キャンペーンを示すパターンを抽出します。 3. セキュリティ人材不足への対応 熟練したセキュリティアナリストの採用は、多くの企業にとって大きな課題です。Attack Discoveryは、経験の浅いアナリストでも、 AIのサポートを受けながら高度な脅威分析が可能 になります。 さらに、Elastic Stack 9.1からは スケジュール機能 が追加され、毎日または毎週といった定期的な自動分析が可能になりました。深夜や週末でも、AIが休まず監視を続けます。 Attack Discoveryの起動と基本捜索 Kibana左上メニュー ≡ → Security > Alerts を開き、下部の Attack discovery をクリックすると、デフォルトで直近のアラート(過去24時間・最大100件)を自動分析します。期間の選択も可能で、アラート件数に応じて完了までの時間は変動します。 今回の例では、10件のDiscoveries(関連アラートを束ねた“攻撃ストーリー”)と 45件のAlerts が表示されています。表示件数が元の総アラート(例:50件)と異なるのは、クローズ済みや時間範囲外の除外、関連性の薄い単発アラートの非採用、処理件数の上限などが考えられます。 各ディスカバリーを展開すると、以下の情報が表示されます: タイトルと要約 – 攻撃の概要を一目で理解 関連アラート数 – グループ化されたアラートの件数 MITRE ATT&CK対応 – 攻撃手法の分類 関与エンティティ – ユーザー名やホスト名(バッジクリックで詳細に遷移) Attack chain – 攻撃チェーンのタイムライン AI Assistantとの連携 各ディスカバリーの画面で View in AI Assistant をクリックすると、AIとの対話型分析画面に移行します。 右上のメニューから以下の設定が可能です: LLM選択 – 使用するLLMモデルの選択(ローカルLLMも可) Select Prompt – アシスタントの振る舞いプリセット Setup Knowledge Base – 社内ドキュメントや最近のアラート等を取り込み、回答の根拠を強化 右上のメニューから LLM選択(ローカルLLMも可)、Select Prompt(アシスタントの振る舞いプリセット)、Setup Knowledge Base(社内ドキュメントや最近のアラート等を取り込み、回答の根拠を強化)を設定できます。画面下部の入力欄から日本語で質問できます。 実践的な活用シナリオ シナリオ1: ランサムウェア攻撃の早期検知 複数のエンドポイントで以下のアラートが発生: 不審なPowerShellスクリプトの実行 大量のファイル暗号化活動 外部への通信試行 管理者権限での横展開 Attack Discoveryは、これらのバラバラなアラートを 一つの協調的なランサムウェア攻撃 として識別し、攻撃の全体像を提示します。 シナリオ2: インサイダー脅威の検出 通常と異なる行動パターン: 深夜の大量データダウンロード 通常アクセスしないシステムへのログイン USBデバイスの使用 クラウドストレージへの転送 Attack Discoveryは、これらを 潜在的なデータ流出の試み として関連付け、調査の優先順位を示します。 セキュリティワークフローへのシームレスな統合 Attack Discoveryの真価は、既存のElastic Securityワークフローとの統合にあります: 即座にアクションを起こせる設計 エンティティ詳細の表示 : ユーザーやホスト名をクリックするだけで詳細情報を確認 Timelineで調査 : 「Investigate in timeline」ボタンで時系列での詳細分析が可能 ケース管理 : ディスカバリーを既存または新規のケースに追加し、チームで共有 AI Assistantとの連携 : さらに詳しい質問や分析をAIに依頼 通知とレポート機能 Elastic Stack 9.1からは、ディスカバリーが見つかった際に SlackやEmailで自動通知 を送ることができます。夜間に重大な脅威が検出されても、担当者に即座に通知が届きます。 導入に必要なもの Attack Discoveryを機能させるには、LLMバックエンドが必ず必要です。そして、そのモデルは十分なコンテキストウィンドウを備えていなければなりません。これが不足していると、多数のアラートや前後の文脈を十分にモデルに渡せず、誤った発見や情報の切り落としが起こり得ます。 対応しているLLMプロバイダー Attack Discoveryは以下のLLMプロバイダーに対応しています: OpenAI (GPT-4など) Anthropic (Claude) Google Cloud Vertex AI (Gemini含む) Azure AI Foundry ローカルLLM 最適なモデル選択については、Elasticの 大規模言語モデルパフォーマンスマトリックス を参照してください。モデルによってパフォーマンスに差があるため、環境に応じた適切な選択が重要です。 データプライバシーへの配慮 Attack DiscoveryはElastic AI Assistantと同じデータ匿名化設定を使用します。サードパーティのLLMに送信する前に、どのフィールドを難読化するかを細かく制御できます。コンプライアンス要件の厳しい環境でも安心して利用できます。 スケジュール機能で24時間体制の脅威監視 Elastic Stack 9.1で追加された スケジュール機能 により、Attack Discoveryの活用の幅が大きく広がりました: 定期的な自動実行 : 毎日、毎週など、自由に設定可能 カスタマイズ可能な分析範囲 : KQLクエリ、時間フィルター、アラート数を細かく調整 自動通知 : ディスカバリーが見つかった場合のみ通知を送信 手動実行も可能 : スケジュールとは別に、いつでも手動で分析を実行 これにより、人手を介さずに継続的な脅威監視が実現します。 ディスカバリーの保存と長期的な活用 生成されたディスカバリーは自動的に保存され、後から確認・レビューが可能です: トレンド分析 : 時間経過による脅威の傾向を把握 監査証跡 : コンプライアンスやインシデントレポートに活用 ステータス管理 : 調査中、解決済みなどのステータスを設定 共有機能 : チーム全体でディスカバリーを共有(手動生成の場合) バルクアクション : 複数のディスカバリーを一括処理 経営層が知っておくべきROI コスト削減 アナリストの作業効率が大幅に向上(分析時間を60-80%削減) セキュリティ人材の採用・育成コストを抑制 インシデント対応の迅速化によるダウンタイム削減 リスク低減 高度な攻撃の早期検知 見落としリスクの最小化 MITRE ATT&CKフレームワークに基づく体系的な脅威把握 競争優位性 最新のAI技術を活用したセキュリティ運用 24時間365日の自動監視体制 データドリブンな意思決定の実現 まとめ: セキュリティ運用の未来は今ここに 本ブログでは、Elastic Attack Discoveryの概要から基本的な使い方、実践的な活用シナリオ、そして導入のメリットまでを解説しました。Attack Discoveryは単なる新機能ではなく、セキュリティ運用のパラダイムシフトを実現するソリューションです。 AIとヒューマンの協働により、より少ないリソースで、より高度な脅威に対応できる時代が到来しました。増え続けるサイバー脅威、深刻化するセキュリティ人材不足、複雑化する攻撃手法―これらの課題に対し、従来型のアプローチでは限界があります。Attack Discoveryは、これらの課題に真正面から立ち向かうソリューションです。 次回のブログでは、Attack Discoveryをさらに効果的に活用するための実践的な設定方法について解説する予定です。 関連リソース Attack Discovery公式ドキュメント Elastic AI Assistantについて デモビデオを見る Elastic 9.1リリースノート 用語集 このブログで使用されている技術用語を、分かりやすく解説します。 アラート(Alert) :セキュリティシステムが異常や疑わしい活動を検知した際に発する通知。火災報知器が煙を感知して鳴るのと同じように、システムが潜在的な脅威を知らせる仕組みです。 AI Assistant :Elasticが提供するAI搭載の対話型アシスタント機能。セキュリティアナリストがAIに質問したり、分析を依頼したりできる、デジタルな相談相手のようなツールです。 ECS (Elastic Common Schema) :Elasticが定義するデータ構造の標準規格。異なるシステムやツールからのデータを統一的に扱えるようにするための「共通言語」のようなものです。 Elastic Stack :Elasticが提供する一連のソフトウェア製品群。データの収集、保存、検索、分析、可視化を一貫して行えるプラットフォームです。 KQLクエリ (Kibana Query Language) :Elasticのデータを検索するための専用言語。Googleで検索する際のキーワード入力をより高度にしたもので、複雑な条件でデータを絞り込めます。 LLM (Large Language Model / 大規模言語モデル) :膨大な量の文章データで学習したAI。ChatGPTのように、人間の言語を理解し、文章を生成したり、複雑な分析を行ったりできる技術です。 LLMコネクタ :ElasticとLLMサービス(OpenAI、Anthropicなど)を接続するための設定。橋渡し役として、Elastic内のデータをLLMに送り、分析結果を受け取ります。 MITRE ATT&CK :サイバー攻撃者が使用する戦術や手法を体系的に整理した国際的なフレームワーク。攻撃の「辞書」や「地図」のようなもので、世界中のセキュリティ専門家が共通言語として使用しています。 MTTR (Mean Time To Respond / 平均対応時間) :セキュリティインシデントを検知してから対応を完了するまでにかかる平均時間。この時間が短いほど、被害を最小限に抑えられます。 RBAC (Role-Based Access Control / ロールベースアクセス制御) :ユーザーの役割(管理者、アナリストなど)に応じて、システムへのアクセス権限を管理する仕組み。「入室許可証」のように、役職に応じて見られる情報や実行できる操作を制限します。 SOC (Security Operations Center / セキュリティ運用センター) :組織のセキュリティを24時間365日監視・管理する専門チームや施設。空港の管制塔のように、あらゆるセキュリティ情報を集約して監視する拠点です。 Timeline :Elasticのセキュリティイベントを時系列で表示・分析するツール。事件の経緯を時間軸で追いかけながら調査できる、デジタル版の「事件簿」です。 エンティティ (Entity) :セキュリティ分析における注目対象。主にユーザーアカウントやコンピューター(ホスト)を指します。事件における「関係者」のようなものです。 ケース管理 :セキュリティインシデントを一つの「案件」として追跡・管理する機能。証拠、メモ、対応履歴などを一元管理し、チームで情報共有できます。 データ匿名化 :個人情報や機密情報を、第三者が特定できない形に変換すること。例えば「佐藤太郎」を「ユーザーA」に置き換えるなど、プライバシーを保護しながらデータを分析できるようにします。 ディスカバリー (Discovery) :Attack Discoveryが生成する、関連する複数のアラートをまとめた「脅威のストーリー」。バラバラの手がかりを組み合わせて、一つの事件として提示する調査レポートのようなものです。 ランサムウェア :コンピューターのデータを暗号化して使えなくし、復元と引き換えに金銭を要求する悪質なプログラム。デジタル版の「身代金誘拐」とも言えます。 横展開 (Lateral Movement) :攻撃者が最初に侵入したコンピューターから、ネットワーク内の他のコンピューターへと移動していく攻撃手法。家の一つの窓から侵入した泥棒が、家中の部屋を物色するイメージです。 The post AIがサイバー攻撃を自動検知!Elastic Attack Discoveryで実現する次世代のセキュリティ運用 first appeared on Elastic Portal .
アバター
Elasticsearch は、ログやメトリクスの検索・分析に広く使われている分散型検索エンジンです。これまでの検索には Lucene ベースの DSL が広く使われてきましたが、2023年に登場した新しいクエリ言語「ES|QL(Elasticsearch Query Language)」によって、より柔軟で強力な分析が可能になりました。 目次 概説 ES|QL とは? ES|QL の主な特徴 ユースケース ES|QL を使うには? サンプル実行 1. 準備:環境 2. サンプルデータの準備 3. 基本のクエリ 4. 条件で絞り込む 5. 集計してみる 6. 時系列での分析 7. ダッシュボードへのグラフの追加 7.1. Discover からダッシュボードへ追加する方法 7.2. ダッシュボードから直接追加する方法 8. Dev Tools からの実行 まとめ 概説 ES|QL とは? ES|QL は、Elasticsearch に特化した新しいクエリ言語で、パイプ構文を使って複数の処理を直感的に記述できるのが特徴です。UNIX のパイプ(|)のように、クエリの各ステップをつなげてデータを変換・整形・集計していくスタイルは、ログ分析やセキュリティ監視において非常に有効です。 例: FROM kibana_sample_data_logs | STATS count = COUNT() by clientip | SORT count DESC | LIMIT 10 このように、clientip ごとのアクセス数を集計し、降順に並べる処理をシンプルに記述できます。 ※ES|QLの文法の詳細については、 公式ドキュメント を参照してください。 ES|QL の主な特徴 パイプ構文:複数の処理を直列につなげて記述できる 高速実行:Elasticsearch の分散処理と統合されており、大量データでも高速 直感的な構文:SQL に似た構文で学習コストが低い 一貫した操作:フィルタ・集計・変換が一貫して可能(WHERE、STATS、KEEP など) ユースケース ES|QL は特に以下のようなユースケースで威力を発揮します: セキュリティログの異常検知 アプリケーションログのエラーパターン分析 メトリクスの時系列集計と可視化 Kibana でのダッシュボード作成やアラート設定 ES|QL を使うには? ES|QL は、Elasticsearch 8.11 以降で正式に利用可能です。Kibana の Discover や Dev Tools から直接クエリを実行できるほか、API 経由でも利用できます。 POST /_query { "query": "FROM kibana_sample_data_logs | STATS count = COUNT() by clientip | SORT count DESC | LIMIT 10" } サンプル実行 ここからは、実際に ES|QL を試してみましょう。 1. 準備:環境 Elasticsearch 8.11 以降 (筆者は、Elasticsearch 9.1.4 で検証) ES|QLを利用可能な場所: Kibana の Discover Kibana の Dev Tools Kibana の Dashboard API 経由 ※Elastic Security の detection rule でも利用可能ですが、本記事では割愛します。詳しく知りたい方は 公式ドキュメント を参照してください。 2. サンプルデータの準備 Kibana には、3種類のサンプルデータが用意されています。今回は、そのうちの kibana_sample_data_logs を利用します。 手順: 2.1. Kibana にログインし、Home 画面を表示します。 2.2. Home 画面から Try sample data をクリックします。 2.3. Sample data タブの > Other sample data sets を展開します。 2.4. Sample web logs の [Add data] をクリックします。 2.5. しばらく待ちます。 2.6. [View data] と表示されれば、サンプルデータの取り込み完了です。 3. 基本のクエリ まずは kibana_sample_data_logs インデックスからデータを取得してみましょう。 3.1. Analytics / Discover 画面に遷移します。 3.2. 画面上部の [Try ES|QL] をクリックします。 ES|QL を入力する画面に切り替わります。 3.3. クエリーの入力欄に次の ES|QL クエリーを入力し、 [▶ Run] を押します。 FROM kibana_sample_data_logs | LIMIT 5 3.4. 最初の5件がリストに表示されます。 表示されない場合は、対象となる期間を調節してみてください。(Last 1 year など) 4. 条件で絞り込む response が “200” でないドキュメントだけを抽出するには WHERE を使います。 FROM kibana_sample_data_logs | WHERE response != "200" | KEEP @timestamp, response, clientip, url | LIMIT 10 KEEP を使うと必要なフィールドだけを残せるため、ログの可読性が向上します。 5. 集計してみる clientip ごとのアクセス数を集計してみましょう。 FROM kibana_sample_data_logs | STATS count = count() BY clientip | SORT count DESC | LIMIT 10 アクセス数の多い clientip を簡単に特定できます。 6. 時系列での分析 時間ごとのアクセス数を確認するには DATE_TRUNC(), STATS, BY を組み合わせます。 FROM kibana_sample_data_logs | EVAL hour = DATE_TRUNC(1h, timestamp) | STATS count = count() BY hour | SORT hour ASC これで「1時間ごとのアクセス数」をグラフ化でき、ピーク時間帯を把握できます。 7. ダッシュボードへのグラフの追加 ES|QL の結果をグラフとしてダッシュボードへ追加するには、2つの方法があります。 7.1. Discover からダッシュボードへ追加する方法 7.1.1. Discover 画面のグラフの右上のフロッピーディスクアイコンをクリックします。 7.1.2. Save Lens visualization ダイアログが開くので、Title, 追加先のダッシュボード名などを入力して、[Save and go to Dashboard] をクリックします。 ※注 (将来的には変わるかもしれませんが)v9.1.4 の時点では、library に追加することはできません。 7.1.3. ダッシュボードへグラフが追加されます。 7.2. ダッシュボードから直接追加する方法 7.2.1. ダッシュボードを新規作成します。あるいは、既存のダッシュボードを開きます。 7.2.2. [ (+) Add panel ] をクリックします。 7.2.3. Visualizations から ES|QL をクリックします。 7.2.4. クエリーの入力欄が表示されるので、そこに実行したい ES|QL のクエリーを入力し、[ ▷ Run query ] をクリックします。 7.2.5. 左側にグラフが表示されるので、それで問題なければ [Apply and close] をクリックします。 7.2.6. ダッシュボードへグラフが追加されます。 8. Dev Tools からの実行 Dev Tools の Console から ES|QL で問い合わせを行うには、 POST /query { "query": "..." } という形式を利用します。 Dev Tools の Console を開いて、下記を実行してみてください。 POST /_query { "query": """ FROM kibana_sample_data_logs | WHERE @timestamp >= NOW() - 10d | STATS count = COUNT() by clientip | SORT count DESC | LIMIT 10 """ } レスポンス { "took": 11, "is_partial": false, "documents_found": 14024, "values_loaded": 0, "columns": [ { "name": "count", "type": "long" }, { "name": "clientip", "type": "ip" } ], "values": [ [ 100, "30.156.16.164" ], [ 29, "164.85.94.243" ], [ 26, "50.184.59.162" ], [ 26, "236.212.255.77" ], [ 25, "16.241.165.21" ], [ 25, "246.106.125.113" ], [ 24, "111.237.144.54" ], [ 24, "81.194.200.150" ], [ 23, "56.28.213.27" ], [ 23, "26.131.108.13" ] ] } POST /query の構文については、 公式ドキュメント を参照してください。 まとめ ES|QL の特徴は以下の通りです。: パイプ構文で直感的に書ける フィルタ・集計・変換を一貫して実行できる ログ分析やセキュリティ監視に最適 まずはシンプルなクエリから試し、徐々に複雑な分析へと広げていくのがおすすめです。Kibana の可視化機能と組み合わせれば、リアルタイムに「気づき」を得られる強力な武器になります。 The post Elasticsearch ES|QL 入門:ログ分析をもっとスマートに first appeared on Elastic Portal .
アバター
「AIアシスタントに任せよう」、そんな言葉が日常的に使われる時代がやってきました。 例えば、PerplexityとPayPalが提携し、AIが「探す→比べる→支払う」までをつなぐエージェント型コマースを実装するとされ、個人の買い物体験がチャット完結に近づいています。また、2024年には企業のAI導入率が70%を超え、AIは単なる便利ツールから共に考え働くパートナー、同僚という位置にへと変化しています。 その背景には「LLM」「RAG」「Agentic」「MCP」「A2A」といった進化の階段があります。本記事では、それぞれがどのように繋がり、未来を形作っているのかを整理します。 後半に、Elasticが、AI時代の波にどのように対応し、進化してきたのかについて軽く見ていきます。 目次 発射台「LLM(大規模言語モデル)」 意味を扱う「ベクトル&セマンティック検索」 外部知識を統合する「RAG」 自律的に動く「エージェント」 連携を統一する「MCP」 MCPを使う意味 AI同士が協力する「A2Aプロトコル」 なぜA2Aが重要? Elastic と AI まとめ 発射台「LLM(大規模言語モデル)」 LLMは、与えられた文脈から統計的に「次に来そうな単語」を選ぶことで文章を生成します。つまり、「理解している」のではなく、膨大な学習データの中から最も自然な応答を選び出している仕組みなのです。 GPT‑4o、Claude 3.5 Sonnet、Gemini 1.5 Proなど最新のモデルは、テキストに加えて画像や音声にも対応するマルチモーダル機能を備えています。この進化により、人と自然に対話でき、複雑な状況や文脈も鋭く捉えるようになりました。 ただし、LLMには限界があります。単体では常に最新情報を持つわけではなく、ハルシネーション(誤情報)を出すこともあります。それでも、「すぐに答えが欲しい」「シンプルなチャットやQ&Aで十分」「リアルタイム性やコストを最優先にしたい」といった場面には、非常に頼りになる存在です。 意味を扱う「ベクトル&セマンティック検索」 LLMの力を支えるのは、「ベクトル化」によって得られる検索精度です。文章や画像、音声などを多次元の数値(ベクトル)に変換し、それらの意味的な「近さ」で関連性を判断できるようにします。たとえば「AIの最新動向」という検索語でも、単語の一致に縛られず、意味的に近い論文や記事を高い精度で抽出できる、これが「セマンティック検索」の肝です。つまり、キーワードが違っても“意味が近ければ見つかる”という価値があり、企業のグローバル知識探索や多言語対応の内部データ検索で特に威力を発揮します。 しかし、KNNによるベクトル検索自体はそこまで新しい話でもありません。では、なぜこれがエージェントの進化に重要な貢献をしたのでしょうか。 セマンティック検索の概念は90年代後半に遡りますが、実用化への道のりは長いものでした。2012年のGoogleによるナレッジグラフの導入で注目を集め、2013年にはWord2Vecがニューラルネットワークを用いた初の単語埋め込み技術として登場しました。この頃からビッグデータの活用が本格化し、大量のデータで言語モデルを訓練できるようになったのです。そして現代的なセマンティック検索は、BERTやTransformerの登場により2018年頃から実用レベルに到達しました。つまり、アルゴリズム的基盤(KNN)は数十年前から存在していたものの、意味理解に必要な高品質な埋め込み技術とコンピューティング能力が揃ったのは比較的最近のことなのです。 外部知識を統合する「RAG」 LLMの弱点を補完するのが「RAG」です。RAG(Retrieval‑Augmented Generation)では、モデルに答えを生成させる前に、外部データベースから関連情報を検索(Retrieval)し、その内容を踏まえて応答(Generation)を構築します。これにより、回答に根拠が加わり、誤情報(ハルシネーション)のリスクを減らすことができます。 さらに進化した「マルチモーダルRAG」では、文章だけでなく画像や音声、動画なども対象に含めて検索・生成ができます。たとえば画像をCLIPでベクトル化し、テキストや映像と一緒に最適な情報を取り出すことが可能です。 こうしたRAGは、専門知識が必要で、常に最新情報を参照したい場合や、回答の品質と信頼性が特に重要な場面で真価を発揮します。つまり、単なるチャットではなく、内容の正確さと根拠を重視する「現場の頼れるAIアシスタント」として活躍します。 自律的に動く「エージェント」 RAGが「正確に答える力」を与える一方、エージェントには「自ら行動する力」が与えられます。つまり、ユーザーの意図を読み取り、最適なツールを選び出し、複数の手順を組み合わせてタスクを自律的に実行する存在です。さらに高度な形として、Agentic RAGでは「検索→生成」という静的な流れにとどまらず、AI自身が動的に検索戦略を立て、文脈を見ながら知識を取得・活用して応答を進化させます。 例えば、 GitHub Copilot の「Agent mode」(VS Code)は、自然言語の指示からコード理解・テスト・修正のループを支援する“自律的な相棒”として進化している。 さらに Copilot agent mode では、VS Code上でコードベースの読み取り、テストの自動実行、エラー修正をループで進行し、自律的なピア・プログラマーとして動作します。 現状では、ツールや仕組みの呼び方が乱立しており、統一された定義がありません。 ここから示すのは、 あくまでも私自身の理解に基づいた整理 です。 AI Agent LLMをコアに、利用可能なツールを呼び出して処理を自動で進める自律的システム。比較的「静的」で、あらかじめ設定された動作に従う存在。 Agentic AI 複数のエージェントがチームのように協力し、異なるLLMや外部リソースとつないで複雑な目標を達成する仕組み。 連携を統一する「MCP」 ここまで、「LLM × 検索」で知識を補強し、「エージェント × ツール」でタスクを自律的に遂行する進化を見てきました。しかし、現場では“どのツールに、どうつなぐのか”が統一されていないという課題があります。そこで登場したのが、MCP(Model Context Protocol)です。 MCPは、エージェントがツールやデータソースに接続するための標準的な「接続口」を提供する、Microsoft Build 2025でも、MCPが「エージェント時代のHTTPのようなプロトコル」として紹介されました。ツールやサービスごとに専用インテグレーションを書かなくても、共通仕様でつなげるようにします。 MCPを使う意味 開発・保守の効率化 :使用するLLMやツールの数が増えるにつれて発生する、大量のカスタム接続を記述する必要がなくなるため、N×M問題が解消されます。結果として、開発コストを大幅に削減できます。 再利用可能なエコシステム :一度つなげたツールは、他のエージェントやプロジェクトでもそのまま使えます。更新の波及もスムーズです。 サードパーティとの接続も容易 :OAuthや認証仕組みを備えた安全で柔軟な接続設計がしやすくなり、導入障壁が下がります AI同士が協力する「A2Aプロトコル」 今や、エージェントがツールに接続する方法が「MCP」で標準化された一方で、エージェント同士の“対話”や“協調”には別の基盤が必要です。そこで注目されるのが、 A2A(Agent‑to‑Agent)プロトコル 。これは、エージェント間の「共通言語」となる通信ルールを定めた仕組みです。 A2Aの主な機能は以下の通りです: Discovery(発見) :各エージェントが自分の役割やスキルを“Agent Card”として公開し、他のエージェントから見つけられるようになります。 Negotation(交渉) :どの通信形式(テキスト、フォーム、音声など)や認証方式を使うかを、お互いにすり合わせられます。 Task/State Management(タスク/状態管理) :タスクの進捗や状態(送信済み・実行中・完了など)をJSON‑RPCやSSEを用いてリアルタイムに共有できます。 Collaboration(協力) :必要に応じて他のエージェントに処理を依頼し、結果を取得することで、連携した処理が可能になります。 たとえば、旅行手配エージェントが、スケジュール確認を担当するエージェントを見つけてやり取りし、空き時間を確認した上で予約を自動的に確定するような流れが、人の介在なしに完結できるようになります。 なぜA2Aが重要? 標準化された協調設計 :各社・各フレームワークで別々に組まれるエージェント連携を、統一された仕様で実現できます。 マルチエージェントの未来が見通せる :AccentureなどはすでにA2Aをベースに、複数のエージェントが協働する仕組みを導入し始めています。 このように、MCPが「ツールへの接続口を揃える」役割とすれば、A2Aは「エージェント同士の会話や協調を可能にする土台」。それぞれの強みを活かすことで、「ツールを使うエージェント」から「互いに話し合いながらタスクを完遂できるエージェント」へと進化する設計が実現できます。 Elastic と AI 2022年: Elasticsearch 8.0がリリースされ、近似最近傍検索機能と最新自然言語処理モデルのネイティブサポートを搭載。2022年を通して、ベクトル検索はテクニカルプレビューの段階でした。 Elasticsearch 8.4で kNN検索を _search API に統合するなど実運用に向けた大きな前進になり、フィルタリング、再ランキング、レキシカル検索とセマンティック検索を組み合わせたハイブリッド検索が可能になりました。 2023年: Elasticが学習済みスパースモデルELSERを公開。追加学習なしで”意図”に基づくセマンティック検索を実現し、同年の8.9ではRRF(Reciprocal Rank Fusion)によるハイブリッド検索を導入してBM25/ベクトル/セマンティックを安定統合。 2024年: Open/Inference APIを拡充し、OpenAI・Azure・Anthropic・Mistral・Hugging Faceなど外部推論/埋め込み・リランクを公式に統合。RAG実装の柔軟性が大きく向上。 Elastic AI Ecosystemを発表。Elasticsearchベクターデータベースと主要AI企業の事前統合(モデル、クラウド、MLOps、データ準備など)により、RAGアプリ開発の立ち上げと運用投入を加速。Google Cloud/Microsoft/Hugging Face/LangChainなどと連携。 2025年: 社内向けジェネレーティブAIアシスタントElasticGPTを公開。Elasticsearchを中核にしたRAGと観測基盤の組み合わせで、安全で文脈を踏まえた知識探索を従業員に提供。プラットフォーム一体運用の実例に。 セキュリティ領域でのエージェント活用を強化。Elastic AI Assistant for Securityによる自然言語クエリ生成や調査支援、Attack Discoveryによる大量アラートの要約・優先度付けなど、Search AI × RAG × エージェントの実運用を具体化。 現在のポジション: Elasticsearchは2022年8月のバージョン8.4で本格的なベクトルデータベースとして名乗りを上げました。セマンティック検索が主流になってから3-4年遅れでの参入でしたが、既存の大規模ユーザーベースと、従来のBM25検索とベクトル検索を組み合わせたハイブリッド検索機能を武器に、この分野で確固たる地位を築きました。現在、Elasticは「Search AI Platform」として、ベクターDB/ハイブリッド検索(RRF)/リランク/Inference APIを統合し、「見つかる・根拠づける・守る」を土台に、RAGやエージェント実装を本番水準で支える位置づけを確立しています。 まとめ AIアシスタントの進化は、 LLM :言語を扱う力 セマンティック検索 :意味を捉える力 RAG :外部知識を統合する力 エージェント :自律的に行動する力 MCPとA2A :連携と協力の力 これらの要素が順に積み重なることで、AIは「人に命令される存在」から「人と一緒に考え、動く存在」へと進化しました。この変化のスピードは早すぎて、今後どんな技術が現れ、私たちの仕事や生活がどう変わるかは、まだ見通しが立ちません。 The post 2025年のAIアシスタント:LLMからAIエージェント(Agentic AI)へ first appeared on Elastic Portal .
アバター
キャプションやタグがなくても、テキストで近い画像を検索できる。そんな仕組みを、ElasticsearchとAIを組み合わせ試作しました。画像資産の整理や業務効率化に役立ちます。 Elasticsearch を選んだ理由はシンプルです。 ベクトル検索に強い: 画像やテキストを CLIP で 512 次元ベクトルに変換し、それをそのまま保存して KNN 検索できる。 スケールに強い: 最初は手元の16枚の画像でも、将来的に何千・何万枚に増えても同じ仕組みで動かせる。 検索をカスタマイズできる: 近似検索(速さ)と再ランク(精度)を組み合わせたり、メタデータで絞り込みしたりが簡単。 ※動作環境 Elasticsearch: 8.19.0 Python: 3.11 実装コードとデータセット⇨ 2025-08-image-search ダウンロード 目次 やりたかったこと モデル選びでの失敗と気づき 日本語翻訳の工夫 動かしてみる インデックス作成 データセットの写真 コードの準備 1. 基本設定と依存ライブラリ 2. CLIPのロード 3. 日本語→英語の翻訳 4. ESのインデックス設計 5. 既存の画像をチェック 6. 画像をインデックス 7. 画像検索 なぜ“正規化”が必須? コマンドラインから実行 検索例と結果 日本語クエリ「池と山」 日本語クエリ「猫とメガネ」 英語クエリ「man and sea」 日本語クエリ「動物園」 まとめ やりたかったこと 画像検索: テキストを入力すると、それに近い画像を探せる 日本語サポート: 日本語で検索しても正しく動く シンプル構成: 余計な仕組みは極力排除して、わかりやすくする モデル選びでの失敗と気づき 試したモデル 最初に試したのは 多言語対応のCLIPモデル ( clip-ViT-B-32-multilingual-v1 ) でした。 CLIP は「画像」と「テキスト」を同じベクトル空間に変換するモデルで、これにより「テキストで画像を探す」「画像に合う説明文を探す」が自然にできます。 ざっくり仕組みを説明すると: 画像エンコーダ:画像を入力して、512次元ほどの数列(=埋め込み)に変換 テキストエンコーダ:文章も同じ次元の埋め込みに変換 同じ空間で距離を測る:両者が近いほど「意味が似ている」とみなす 問題点 たとえば「ヒマワリ」と「時計」を検索しても、ほぼ同じ結果が返ってきてしまい、 埋め込みの類似度も 0.95〜0.99 と高すぎて区別がつきません。 改善 と課題 精度が低すぎたため、別のモデルを試すことにしました。そこで改めて OpenAIオリジナルのCLIP ( clip-vit-base-patch32) を試してみたところ、こちらであれば納得できる結果が得られそうでした。 物体やシーンを正しく区別してくれる 安定して動作する 日本語は7割程度は理解できない( 課題! ) 日本語翻訳の工夫 次の課題は 日本語クエリをどう扱うか でした。 CLIPだけで日本語検索を行うと、正しい結果が返る場合もあれば、外れてしまう場合もありました。これではプロダクション環境で使うには不安定です。 そこで、いくつか方法を試しました。 多言語CLIPをそのまま使う → うまくいかない 手作りの和英辞書を追加 → 手間がかかりすぎる Elasticsearchに翻訳パイプラインを組み込む → 作り込みすぎて保守が大変 最終解決策: LLMを使って日本語クエリを翻訳させる! 結果的に、この最後の方法が最もシンプルで、しかも精度が良かった。さらに、 gpt-3.5-turbo の 利用コストは非常に低く 、実運用でも十分現実的です。 動かしてみる インデックス作成 画像を data/images/ に入れて次を実行: python smart_clip_search.py index CLIP が画像をベクトルに変換し、Elasticsearch に保存してくれます。 実際のログはこんな感じ↓ (.venv) /path/to/data/code % python smart_clip_search.py index 🔄 Starting image indexing process... This will look at each image and convert it to AI-readable format Loaded CLIP model: openai/clip-vit-base-patch32 Created index: images-openai-only Found 0 already indexed images Found 16 image files 📝 Processing 16 new images ✅ Indexed 1/16: 10-unsplash.jpg ✅ Indexed 2/16: 3-unsplash.jpg ✅ Indexed 3/16: 4-unsplash.jpg ✅ Indexed 4/16: 16-unsplash.jpg ✅ Indexed 5/16: 11-unsplash.jpg ✅ Indexed 6/16: 5-unsplash.jpg ✅ Indexed 7/16: 2-unsplash.jpg ✅ Indexed 8/16: 8-unsplash.jpg ✅ Indexed 9/16: 9-unsplash.jpg ✅ Indexed 10/16: 13-unsplash.jpg ✅ Indexed 11/16: 14-unsplash.jpg ✅ Indexed 12/16: 7-unsplash.jpg ✅ Indexed 13/16: 15-unsplash.jpg ✅ Indexed 14/16: 12-unsplash.jpg ✅ Indexed 15/16: 6-unsplash.jpg ✅ Indexed 16/16: 1-unsplash.jpg 🎉 Indexing complete! Processed 16 new images dev toolで確認すると…  GET images-openai-only/_count { "count": 16, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 } } 確かに16枚、すべて保存されているのがわかります。 一つのドキュメントを例に…  GET images-openai-only/_doc/loHWwpgBBkwHewC3XB1a { "_index": "images-openai-only", "_id": "loHWwpgBBkwHewC3XB1a", "_version": 1, "_seq_no": 0, "_primary_term": 1, "found": true, "_source": { "image_embedding": [ 0.005093493033200502, 0.004333182703703642, -0.02418620139360428, -0.03343995288014412, ... ], "image_name": "10-unsplash.jpg", "image_path": "/path/to/data/images/10-unsplash.jpg" } } データセットの写真 コードの準備 ここから、コードの説明をします。 1. 基本設定と依存ライブラリ 最初に必要なライブラリを読み込みます。 torch / transformers → CLIPモデルを動かすため PIL → 画像を読み込むため Elasticsearch クライアント → 検索用データベースと接続するため import os, time, torch, numpy as np from pathlib import Path from PIL import Image from transformers import CLIPModel, CLIPProcessor from project_image_search.elastic_client import get_client IMAGE_ROOT = Path("/path/to/data/images") INDEX_NAME = "images-openai-only" .envファイルの最小構成例 ELASTICSEARCH_URL=https://localhost:9200 ELASTICSEARCH_USERNAME=elastic ELASTICSEARCH_PASSWORD=your_password ELASTICSEARCH_CA_CERT=/path/to/ca.crt OPENAI_API_KEY=your_openai_api_key_here 2. CLIPのロード ここで OpenAI が公開している CLIP を読み込みます。 GPU があれば GPU で動かし、なければ CPU で実行します。 def load_clip_model(): model_name = "openai/clip-vit-base-patch32" model = CLIPModel.from_pretrained(model_name) processor = CLIPProcessor.from_pretrained(model_name, use_fast=True) device = "cuda" if torch.cuda.is_available() else "cpu" model.to(device) return model, processor, device 3. 日本語→英語の翻訳 CLIPは英語が得意なので、日本語クエリは GPT で翻訳してから検索。 def translate_with_openai(query: str): # 日本語が含まれていたら OpenAI に翻訳を依頼 4. ESのインデックス設計 Elasticsearch に「画像データを保存する箱」を作ります。保存するのは以下の3つ: 画像の埋め込みをdense_vectorとして(512次元ベクトル) ファイル名 ファイルパス def create_index(): mapping = { "mappings": { "properties": { "image_embedding": {"type": "dense_vector", "dims": 512, "similarity": "cosine"}, "image_name": {"type": "keyword"}, "image_path": {"type": "keyword"} } } } es.indices.create(index=INDEX_NAME, body=mapping) 5. 既存の画像をチェック def get_existing_images(es): res = es.search(index=INDEX_NAME, body={"_source": ["image_path"], "query": {"match_all": {}}}) existing_paths = {hit["_source"]["image_path"] for hit in res["hits"]["hits"]} return existing_paths 既に保存されている画像を重複インデックスしない工夫。 6. 画像をインデックス フォルダ内の画像を読み込み、1枚ずつ CLIP で数値化(埋め込み生成) します。その数値を Elasticsearch に保存することで、後から検索できるようにします。 def index_images(): image = Image.open(img_path).convert("RGB") inputs = processor(images=image, return_tensors="pt").to(device) image_features = model.get_image_features(**inputs) image_features = image_features / image_features.norm(dim=-1, keepdim=True) 7. 画像検索 検索プロセスは大きく5ステップです: 日本語なら英語に翻訳 テキストを CLIP に渡し、埋め込み(512次元ベクトル)に変換 Elasticsearch で「近い画像」を高速に検索(KNN検索) 上位候補を取り出して、再ランク 結果をスコア付きで返す def search_images(query: str, k: int = 5): translated_query = translate_with_openai(query) inputs = processor(text=translated_query, return_tensors="pt").to(device) text_features = model.get_text_features(**inputs) text_features = text_features / text_features.norm(dim=-1, keepdim=True) なぜ“正規化”が必須? CLIPの出力は512次元ベクトルですが、ベクトルには「方向」と「長さ」があります。 検索で本当に比べたいのは「意味の近さ=方向」なのに、正規化をしないと「長さの違い」に結果が引っ張られてしまいます。 そのため、全てのベクトルを 長さ1に揃える(正規化) ことで、大きさの違いを無視して「意味の近さ」だけを比べられるようにする必要があります。 コマンドラインから実行 コマンドラインから2つの使い方ができます: index → 画像を読み込んで保存 search <query> → テキストで検索 python smart_clip_search.py index python smart_clip_search.py search "ひまわり" 検索例と結果 いくつか例を見てみましょう。 日本語クエリ「池と山」 python smart_clip_search.py search "池と山" 🔍 Searching for images matching: '池と山' Loaded CLIP model: openai/clip-vit-base-patch32 Translated '池と山' -> 'Lake and mountain' Search Query: 池と山 Translated to: Lake and mountain 1. 6-unsplash.jpg (similarity: 0.228) 2. 3-unsplash.jpg (similarity: 0.205) 3. 2-unsplash.jpg (similarity: 0.190) 4. 7-unsplash.jpg (similarity: 0.187) 5. 10-unsplash.jpg (similarity: 0.177) Low confidence - may not contain '池と山' Total Search Time: 4.904s 結果 →1位はまさに池と山の写真でした。 6-unsplash.jpg 日本語クエリ「猫とメガネ」 python smart_clip_search.py search "猫とメガネ" 🔍 Searching for images matching: '猫とメガネ' Loaded CLIP model: openai/clip-vit-base-patch32 Translated '猫とメガネ' -> 'Cat and glasses' Search Query: 猫とメガネ Translated to: Cat and glasses 1. 13-unsplash.jpg (similarity: 0.294) 2. 9-unsplash.jpg (similarity: 0.253) 3. 11-unsplash.jpg (similarity: 0.169) 4. 7-unsplash.jpg (similarity: 0.141) 5. 10-unsplash.jpg (similarity: 0.133) Medium confidence match Total Search Time: 4.277s 結果 →サングラスをかけた猫の写真が1位に出現。 13-unsplash.jpg 英語クエリ「man and sea」 python smart_clip_search.py search "man and sea" 🔍 Searching for images matching: 'man and sea' Loaded CLIP model: openai/clip-vit-base-patch32 Search Query: man and sea 1. 1-unsplash.jpg (similarity: 0.260) 2. 7-unsplash.jpg (similarity: 0.235) 3. 10-unsplash.jpg (similarity: 0.224) 4. 11-unsplash.jpg (similarity: 0.200) 5. 2-unsplash.jpg (similarity: 0.197) Medium confidence match Total Search Time: 3.058s 結果 → 1位は海辺に立つ男性の写真。英語でも正しい結果が得られました。 1-unsplash.jpg 日本語クエリ「動物園」 python smart_clip_search.py search "動物園" 🔍 Searching for images matching: '動物園' Loaded CLIP model: openai/clip-vit-base-patch32 Translated '動物園' -> 'Zoo' Search Query: 動物園 Translated to: Zoo 1. 15-unsplash.jpg (similarity: 0.260) 2. 13-unsplash.jpg (similarity: 0.205) 3. 5-unsplash.jpg (similarity: 0.199) 4. 3-unsplash.jpg (similarity: 0.194) 5. 10-unsplash.jpg (similarity: 0.156) Medium confidence match Total Search Time: 3.822s 結果 → お見事!1位は確かに動物園の写真でした。 まとめ 今回使った画像データセットは、ファイル名が「1-unsplash.jpg」「13-unsplash.jpg」のように中身が全くわからないもので、写真の説明文も一切ありませんでした。 つまり「テキスト情報ゼロ、画像の中身だけ」という条件です。 それでも CLIP が画像を意味ベクトルに変換し、Elasticsearch が高速に検索してくれたおかげで、 「猫とメガネ」→ サングラス猫の写真 「動物園」→ 動物園の写真 「ひまわり」→ ひまわり畑の写真 と、まさに欲しい画像を返してくれました。 「キャプションなし・タグなし・名前からは何もわからない画像」 を検索できるのは、実務的にも大きなメリットだと思います。 🛠️ トラブルと学び APIキーを入れ忘れる と「No OpenAI API key found」と怒られる “slow image processor”警告 が出るけど、これは無害 インデックスが壊れた時は再作成すればOK 次の一歩 数万枚規模にスケールして性能を検証したい メタデータでフィルタリングを追加してみたい 「画像 → 画像検索」に拡張したい The post Elasticsearch × CLIP × GPTで画像検索システムを作ってみた first appeared on Elastic Portal .
アバター
目次 はじめに 対象読者 対象バージョン 検索の準備 1. インデックスの作成 2. インデックスのマッピング設定 3. モデルの準備 4. インジェストパイプラインの作成 5. インジェストパイプラインの確認 6. データの登録 6.1 NDJSONの用意 6.2 一時インデックスへのアップロード 6.3 _reindexの実行 6.4 タスクの完了確認 6.5 _refreshの実行 登録データのストレージ利用量確認 ベクトル検索の実行 rescore_vector を行わないベクトル検索 rescore_vector を行ったベクトル検索 まとめ はじめに 前回 は、Elasticsearchで bbq_hnsw を利用したベクトルの量子化について、その基礎を解説しました。今回は、bbq_hnsw を使って実際にベクトル検索を行う手順を解説します。 対象読者 Elasticsearchでベクトル検索の導入を検討している方 Elasticsearchの初心者〜中級者 対象バージョン Elasticsearch 8.19.0以上 Elasticsearch 9.1.0以上 1 この記事のサンプルでは rescore_vector の oversample に0を指定していますが、これがサポートされているのは上記のバージョン以降です。筆者はElasticsearch 8.19.0で検証しました。 検索の準備 1. インデックスの作成 今回の検証に利用するインデックスを作成します。今回はベクトル検索のみを行うため、形態素解析の設定は省略します。 以下のリクエストをKibanaのDev Toolsから実行してください。 (インデックス名 = waganeko_with_bbq_hnsw) PUT /waganeko_with_bbq_hnsw/ { "settings": { "index": { "number_of_shards": 1, "number_of_replicas": 1, "refresh_interval": "3600s" } } } 2. インデックスのマッピング設定 インデックスにフィールドを作成します。ここでは、以下の3つのフィールドを定義します。 フィールド名 タイプ 説明 chunk_no long チャンク番号 content text 本文 text_embedding dense_vector 384次元のfloat32密ベクトル 上記には bbq_hnsw で量子化された項目を記載していません。 bbq_hnsw による量子化は、厳密にはフィールドではなく、dense_vector タイプのフィールドのindex_optionsとして設定されます。 なお、bbq_hnsw を使ったベクトル検索時のオーバーサンプリング係数は5としています。 現在の最新バージョンでのデフォルト値は3のようですが、ドキュメントに明記されているわけではないため、ご自身のバージョンでご確認ください。 以下のリクエストをDev Toolsから実行します。 PUT /waganeko_with_bbq_hnsw/_mapping { "dynamic": false, "properties": { "chunk_no": { "type": "long" }, "content": { "type": "text" }, "text_embedding": { "type": "dense_vector", "dims": 384, "index_options": { "type": "bbq_hnsw", "rescore_vector": { "oversample": 5 } } } } } dense_vector 型のフィールドの設定については、 公式ドキュメント を参照してください。 3. モデルの準備 テキストから密ベクトルを生成するためのモデルを準備します。 Elasticsearchに内包されているモデルを利用する(例: .multilingual-e5-small_linux-x86_64) または Elasticsearchの外部にあるモデルを利用する(例: OpenAIのtext-embedding-3-small) 筆者は、前者の.multilingual-e5-small_linux-x86_64を利用しました。モデルの準備の詳細な手順は割愛します。 2 4. インジェストパイプラインの作成 準備したモデルを利用するインジェストパイプラインを作成します。 .multilingual-e5-small_linux-x86_64を使う場合の例は以下のとおりです。 PUT /_ingest/pipeline/my-e5-small-pipeline { "description" : "Text embedding pipeline", "processors" : [ { "inference": { "model_id": ".multilingual-e5-small_linux-x86_64", "input_output": [ { "input_field": "content", "output_field": "text_embedding" } ] } } ] } “content” と “text_embedding” は、上で作成したインデックスのフィールド名と一致させてください。 このリクエストをDev Toolsから実行します。 5. インジェストパイプラインの確認 Embeddingモデルとパイプラインが正しく登録できたか確認します。 以下のリクエストをDev Toolsから実行します。 POST /_ingest/pipeline/my-e5-small-pipeline/_simulate { "docs": [ { "_index": "index", "_id": "id", "_source": { "content": "吾輩は猫である" } } ] } 正しく登録できていれば、以下のように384次元のfloat32のベクトルが返却されます。 { "docs": [ { "doc": { "_index": "index", "_version": "-3", "_id": "id", "_source": { "text_embedding": [ 0.06494276970624924, 0.0074624731205403805, ... ] } } } ] } 6. データの登録 今回は、 青空文庫 で公開されている 「吾輩は猫である」 のデータを検証に利用しました。データ登録方法はいくつかありますが、ここでは詳細な手順は割愛し、筆者が行った方法を紹介します。 6.1 NDJSONの用意 検証用のため、青空文庫のデータを1行1チャンクとしました。RAGなどの用途では、より適切なチャンキングを検討してください。 {"chunk_no":1, "content":"一\n吾輩は猫である。名前はまだ無い。"} ... {"chunk_no":2207, "content":"次第に楽になってくる。...ありがたいありがたい。"} 6.2 一時インデックスへのアップロード 作成した NDJSON を Elastic の File Upload 機能を使って一時的なインデックス(waganeko_tmp)へアップロードします。(Data View は作成しません。) 6.3 _reindexの実行 作成したパイプラインを使って、一時インデックス(waganeko_tmp) からwaganeko_with_bbq_hnsw インデックスへ _reindex します。 POST /_reindex?wait_for_completion=false { "source": { "index": "waganeko_tmp" }, "dest": { "index": "waganeko_with_bbq_hnsw", "pipeline": "my-e5-small-pipeline" } } 時間がかかるので、wait_for_completion=false を指定しておきます。 これにより、上記のリクエストを発行すると、taskid が返却されます。 6.4 タスクの完了確認 タスクが完了したか、以下のコマンドで確認します。 GET /_tasks/{taskid} 6.5 _refreshの実行 タスクが completed になったら、インデックスをリフレッシュします。 POST /waganeko_with_bbq_hnsw/_refresh 登録データのストレージ利用量確認 bbq_hnsw を利用する場合、量子化されたベクトルだけでなく、元のベクトルも保存されていると前回説明しました。実際にストレージ利用量を確認してみましょう。 まず、登録されたドキュメント数を確認します。 GET /waganeko_with_bbq_hnsw/_count レスポンス { "count": 2207, ... } 2207件のドキュメント、つまり2207個のベクトルが登録されています。(今回は、1ドキュメント = 1ベクトル としているため。) 次に、インデックスのストレージ利用量を調べます。 POST /waganeko_with_bbq_hnsw/_disk_usage?run_expensive_tasks=true レスポンス { "_shards": { "total": 1, "successful": 1, "failed": 0 }, "waganeko_with_bbq_hnsw": { ... "text_embedding": { ... "knn_vectors_in_bytes": 3577380 } } } knn_vectors_in_bytes に表示された3577380バイトが、ベクトルデータのサイズです。 この値を理論値と比較してみましょう。 項目 値 ベクトルの総数 2207個 次元数 384次元 float32ベクトルの1要素のサイズ 4バイト bbq量子化後のベクトルの1要素のサイズ 1/8バイト 量子化前のサイズと量子化後のサイズを合計すると… 量子化前のサイズ: 2207 * 384 * 4 = 3,389,952バイト 量子化後のサイズ: 2207 * 384 * 1/8 = 105,936バイト 合計: 3,389,952+105,936=3,495,888バイト 実測値の3,577,380バイトと理論値の3,495,888バイトは非常に近い値です。この結果から、インデックス内には量子化前のベクトルと量子化後のベクトルの両方が保存されていると推測されます。 参考までに、他の量子化方法でも計測した結果を記載しておきます。 量子化方法 knn_vectors_in_bytes (実測値) 理論値 量子化なし 3,398,780 3,389,952 bbq_hnsw 3,577,380 3,495,888 int8_hnsw 4,310,912 4,237,440 いずれも、理論値に近い実測値となっています。 ベクトル検索の実行 それでは、いよいよbbq_hnswを使ったベクトル検索を実行してみましょう。 rescore_vector を行わないベクトル検索 まずは、rescore_vector を行わない場合の検索結果を確認します。 検索クエリ: “鏡が持ってこられた場所” 取得件数: 10件 Elasticsearch 8.19.0ではデフォルトで rescore_vector が行われるため、”oversample”: 0を指定して意図的に無効化しています。これはあくまで検証目的であり、通常は推奨されません。 GET /waganeko_with_bbq_hnsw/_search { "_source": false, "fields": [ "chunk_no", "content" ], "knn": { "field": "text_embedding", "query_vector_builder": { "text_embedding": { "model_id": ".multilingual-e5-small_linux-x86_64", "model_text": "鏡が持ってこられた場所" } }, "k": 10, "num_candidates": 100, "rescore_vector": { "oversample": 0 } } } このリクエストを実行した結果が以下です。 rank score chunk_no content 1 0.9310 1472 鏡と云えば風呂場にあるに極まっている。現に吾輩は今朝風呂場でこの鏡を見たのだ。… 2 0.9283 1477 風呂場にあるべき鏡が、しかも一つしかない鏡が書斎に来ている以上は… 3 0.9252 1493 鏡は己惚の醸造器であるごとく、… 4 0.9234 1471 …しかし主人は何のために書斎で鏡などを振り舞わしているのであろう。鏡と云えば風呂場にあるに極まっている。 5 0.9230 1786 「誰が警察から油壺を貰ってくるものか。… 6 0.9228 1458 …図書館もしくは博物館へ馳けつけて、… 7 0.9226 2009 「…僕が昔し姥子の温泉に行って、一人のじじいと相宿になった事がある。… 8 0.9223 1478 …なにさ今鏡を造ろうと思うて一生懸命にやっておるところじゃと答えた。 9 0.9215 1480 かくとも知らぬ主人ははなはだ熱心なる容子をもって一張来の鏡を見つめている。… 10 0.9206 1263 …不用心だって金のないところに盗難のあるはずはない。… 最も関連性の高いと思われるchunk_no = 1477が2位になっており、全体的に検索結果の精度が微妙な印象です。 rescore_vector を行ったベクトル検索 次に、検索結果の上位50件(k(=10) * oversample(=5))を元のベクトルでrescoreしてみます。rescore_vector を指定することで、元のベクトルを使った再評価が行われます。 GET /waganeko_with_bbq_hnsw/_search { "_source": false, "fields": [ "chunk_no", "content" ], "knn": { "field": "text_embedding", "query_vector_builder": { "text_embedding": { "model_id": ".multilingual-e5-small_linux-x86_64", "model_text": "鏡が持ってこられた場所" } }, "k": 10, "num_candidates": 100, "rescore_vector": { "oversample": 5 } } } ※mappings作成時に “rescore_vector”: { “oversample”: 5 } を指定しているので、本来は省略可能ですが、ここでは、わかりやすいよう明示しています。 rescore_vector の動作: 公式ドキュメント によると、このリクエストを実行した場合、以下の流れで検索が行われます。 (1) 各シャードごとに上位 num_candidates 件(今回は100件)のドキュメントを bbq_hnsw を使って取得します。 (2) そのうち、上位(k × oversample)件(今回は10 * 5=50件)のドキュメントを、元のベクトルを使って再評価(rescore)します。 (3) 再評価されたドキュメントの中から、最終的な上位k件(今回は10件)を取得します。 (4) 複数のシャードがある場合は、(1) – (3) をシャードごとに行い、最終的にマージして上位10件を取得します。今回はシャードが1つなので、この手順は省略されます。 rescore_vector を行った結果が以下です。 rank score chunk_no content 1 0.9288 1477 風呂場にあるべき鏡が、しかも一つしかない鏡が書斎に来ている以上は… 2 0.9248 1493 鏡は己惚の醸造器であるごとく、… 3 0.9219 1480 かくとも知らぬ主人ははなはだ熱心なる容子をもって一張来の鏡を見つめている。… 4 0.9215 1472 鏡と云えば風呂場にあるに極まっている。現に吾輩は今朝風呂場でこの鏡を見たのだ。… 5 0.9214 1786 「誰が警察から油壺を貰ってくるものか。… 6 0.9212 1684 …昨日は鏡の手前もある事だから、おとなしく独乙皇帝陛下の真似をして整列したのであるが、… 7 0.9206 633 「…今日は土曜ですからこれから廻ったら、もう帰っておりましょう。… 8 0.9205 1471 …しかし主人は何のために書斎で鏡などを振り舞わしているのであろう。鏡と云えば風呂場にあるに極まっている。 9 0.9197 1505 …例のごとく赤い手をぬっと書斎の中へ出した。右手に髯をつかみ、左手に鏡を持った主人は、そのまま入口の方を振りかえる。… 10 0.9184 533 行きたいところへ行って聞きたい話を聞いて、舌を出し尻尾を掉って、髭をぴんと立てて悠々と帰るのみである。… 検索結果を見ると、最も関連性の高いドキュメント(chunk_no = 1477)が1位に上がっています。他のドキュメントも、rescoreなしの場合と比べて、より関連性の高いものが上位に来ている印象です。 この結果から、bbq による量子化を行った場合でも rescore_vector を使用することで、良好な検索結果が得られることがわかります。 なお、ベクトル値を再評価する方法は、rescore_vector 以外にも rescore セクションを指定する方法などがあります。詳しくは 公式ドキュメント を参照してください。 まとめ bbq_hnsw で密ベクトルを格納した場合、インデックス内には量子化前のベクトルと量子化されたベクトルの両方が保持されていることを実測値と理論値の比較で確認しました。 bbq_hnsw を利用した密ベクトル検索では、 rescore_vector を使うことで、検索精度を向上させられることを実証しました。 Elasticsearch 9.1.0 は、デフォルトで directio を使用する、という仕様になっています。bbq_hnsw を利用時でメモリが十分に足りている場合は directio を使用しない方がいいケースもあるため、directio を使用しないよう設定する、あるいは Elasticsearch 9.1.1 以上へバージョンアップする、なども検討してみてください。 – https://www.elastic.co/docs/release-notes/elasticsearch/known-issues#elasticsearch-9.1.0-known-issues – https://www.elastic.co/search-labs/blog/knn-vector-search-rescoring-direct-io ↩︎ .multilingual-e5-small_linux-x86_64 を利用する準備の手順は、下記を参考にしてください。 https://elastic.sios.jp/blog/preparing-for-vector-search/ ↩︎ The post Elasticsearchの bbq_hnsw を使ったベクトル検索(実践編) first appeared on Elastic Portal .
アバター
目次 はじめに 対象読者 対象バージョン 量子化を利用しない密ベクトル検索 量子化を行わない場合のストレージ ベクトル検索に必要なメモリ量 量子化を利用した密ベクトル検索 量子化の種類 量子化を行う場合のストレージ ベクトル検索に必要なメモリ量 量子化のメリット・デメリット メリット デメリット まとめ 次回予告 はじめに Elasticsearchで密ベクトル検索を行う場合、Elasticsearch 9.0から bbq_hnsw を使った検索がデフォルトになりました。 このブログでは、 bbq_hnsw の基礎について解説します。 対象読者 Elasticsearchでベクトル検索を始める予定の方 Elasticsearchの初心者~中級者 対象バージョン Elasticsearch 8.x(8.18.0以降、bbqとrescore_vectorが利用可能なバージョン) Elasticsearch 9.x 量子化を利用しない密ベクトル検索 まず、量子化を行わない密ベクトル検索について考えてみましょう。 Elasticsearchで密ベクトル検索を行う場合、通常は検索対象となるテキストの float32 ベクトルを格納します。 量子化を行わない場合のストレージ 密ベクトルの量子化を行わない場合、Elasticsearchのストレージには、主に以下のデータが格納されます。 ドキュメントのテキストデータ(text、keyword、longなど) ドキュメントをキーワード検索するための転置インデックス float32型の密ベクトル (上記は簡略化した内容です) 超概略図(レプリカは考慮していません) ベクトル検索に必要なメモリ量 量子化を行わない密ベクトル検索では、全ベクトルのデータをメモリ上に保持して検索を行います。これにより、高速なベクトル検索が可能になります。 では、float32型の密ベクトルを使う場合に、どのくらいのメモリが必要になるか概算してみましょう。 例:検索対象の密ベクトルの総数 = 1億個、次元数 = 2048の場合 ベクトルの1要素は、4バイト(float32)なので、 必要なメモリサイズ(概算)=1億個 * 2048 * 4バイト = 1億個 * 8192バイト = 約800GB この場合、密ベクトル検索には約 800GB のRAMが必要となります。これは非常に大きなメモリ量です。 量子化を利用した密ベクトル検索 次に、ベクトルを量子化して密ベクトル検索を行う場合を考えます。 量子化を一言で説明すると、 ベクトルの圧縮  です。 量子化の種類 圧縮の度合いに応じて、いくつかの量子化手法があります。Elasticsearchでは数種類の量子化を利用可能です。 詳細は、 Elasticsearchの公式ドキュメント をご覧ください。 int8: 1つのベクトル要素を1バイトで表現します。(従来のElasticsearchのデフォルト) int4: 1つのベクトル要素を1/2バイトで表現します。 bbq: 1つのベクトル要素を1ビット(1/8バイト)で表現します。(Elasticsearch 9.0以降のデフォルト) bbqについての詳細は、 Better Binary Quantization in Lucene and Elasticsearch をご覧ください。 量子化を行う場合のストレージ 量子化を行う場合、Elasticsearchのストレージには、元のデータに加えて量子化された密ベクトルが追加で格納されます。 ドキュメントのテキストデータ(text、keyword、longなど) ドキュメントをキーワード検索するための転置インデックス float32型の密ベクトル 量子化された密ベクトル 超概略図(レプリカは考慮していません) このため、量子化された密ベクトルの分だけストレージサイズは大きくなりますが、このデメリットを上回る大きなメリットが得られます(後述)。 ベクトル検索に必要なメモリ量 量子化を行った場合、元のベクトルデータをすべてメモリ上に保持する必要はありません。その代わりに、圧縮された全ベクトルデータをメモリ上に保持します。 量子化の種類によって必要なメモリサイズは異なりますが、ここでは bbq を使った場合で計算してみましょう。 bbq 量子化を行った場合のメモリサイズは、以下の計算式で求められます(条件は、前述と同じく1億件で384次元とします)。 メモリサイズ=1億個 * (2048/8 + 14) + 1億個 * 4 * 16 = 334億バイト = 約 32.6GB この計算式は Elasticsearchの公式ドキュメント に記載されています。 量子化しない場合に必要なメモリが 800GB だったのに対し、bbqを使えばわずか 32.6GB に抑えられます。これは驚くべき削減効果です。 量子化のメリット・デメリット メリット 検索に必要なメモリ量を大幅に削減できます。 検索処理に必要なCPUリソースも削減できます。 デメリット 量子化されたベクトルの分だけ、ストレージの消費量が増えます。 ベクトルを圧縮するため、検索精度がわずかに低下する可能性があります。 この検索精度の低下を抑えるために、Elasticsearchでは、量子化されたベクトルで検索を行った後、検索結果の上位n件に対して量子化前のfloat32ベクトルを使って再検索を行う機能を提供しています。 詳細は、 Dense Vector kNN Search Rescoring をご覧ください。 bbq量子化を利用する場合、再検索は本来取得したい件数の3〜5倍の検索結果に対して行うことが推奨されています。 なお、 int8 量子化の場合、再検索の必要性はそれほど高くないとされています。 まとめ ここまでの内容を整理しましょう。 Elasticsearchは、密ベクトル検索において量子化によるメモリ削減を推奨しています。 Elasticsearch 9.0からは、密ベクトル検索のデフォルトとして bbqによる量子化 が採用されました。 Elasticsearchで検索用に密ベクトルを保持する場合、ストレージには float32ベクトルと量子化されたベクトル(bbqなど)の両方 が格納されます。 bbqで量子化されたベクトルを使って検索する場合、必要なメモリ量は、元のベクトルをそのまま使う場合に比べて大幅に削減されます。 bbqで量子化されたベクトルを使う場合、検索精度の低下を防ぐために、上位n件に対して元のベクトルを使った再検索を行うことが推奨されます。推奨されるnの値は、取得したい件数の3〜5倍です。 次回予告 次回は、実際にbbqベクトルを使った密ベクトル検索を試してみましょう。 The post Elasticsearh の bbq_hnsw について(基礎編) first appeared on Elastic Portal .
アバター
目次 はじめに 対象読者 Elastic 認定試験 Elastic Security for SIEM のトレーニングコース トレーニング用 Kibana 画面へのアクセス方法 Lab 環境の開始 lab-machine の bash 画面とURLの確認 CREDENTIALS の取得 Lab 環境のURLへアクセス Kibana 画面へのログイン Kibana の Home 画面の表示 演習問題の表示 CTFd の URL の取得 CTFd へのアクセス 初回登録手順 ログイン(2回目以降) CTFd のトップメニュー 演習問題の表示 Challenge 一覧画面 回答入力画面のロック解除 回答入力画面 Windows-Endpoint Windows-Endpoint Windows 環境へのコピー&ペースト まとめ はじめに 今回は、Elastic Certified SIEM Analyst のトレーニングコースの Lab 環境について、その特徴と具体的な操作方法を紹介します。 (Elastic Certified SIEM Analyst の Lab 環境は、従来のトレーニングコースの Lab 環境とは操作方法が異なりますので、ご注意ください。) 対象読者 Elastic Certified SIEM Analyst のトレーニングコースの受講を検討している方 Elastic Certified SIEM Analyst のトレーニングコースの受講を始めたばかりで、Lab 環境の操作に戸惑っている方 Elastic 認定試験 これまで Elastic に関する認定試験には3種類の試験がありました。 Official Certification for Users of Elasticsearch and Kibana You mastered Elasticsearch, now it's time to enhance your professional visibility and grow opportunities for your compan... www.elastic.co Elastic Certified Engineer Elastic Certified Analyst Elastic Certified Observability Engineer ここに、先日、Elastic Certified SIEM Analyst が追加されました。 Elastic Security for SIEM のトレーニングコース Elastic Certified SIEM Analyst の認定試験のためのトレーニングとして、「Elastic Security for SIEM」トレーニングコースも追加されました。 Elastic Security for SIEM www.elastic.co Elastic Security for SIEM のトレーニングコースには、Virtual と On-Demand の2つの形式が用意されています。On-Demand コースは、 プロモーションとして2025年10月31日まで無償で受講できます 。 On-Demand コースの中には実際に Elastic の画面を操作して体験できる Lab 環境が用意されています。しかし、この Elastic Security for SIEM のコースが比較的新しいためか、これまでのトレーニングコースの Lab 環境とは操作方法が少し異なります。 トレーニング用 Kibana 画面へのアクセス方法 Lab 環境の開始 Elastic Security for SIEM のトレーニングコースを開始して進めていくと、次のような画面が表示されます。 ここまでは、従来のトレーニングコースと同じく、[LAB] をクリックします。 lab-machine の bash 画面とURLの確認 さきほどの [LAB] をクリックすると、次のような画面が表示されます。 ここが従来のトレーニングコースとは異なる点です。従来は、[Terminal] や [Editor] の隣に [Kibana] などが用意されていましたが、このコースでは Lab 環境の URL が表示されます(赤枠部分)。このURLを 必ずコピーしておきましょう 。 CREDENTIALS の取得 bash 画面から次のコマンドを実行してください。 cat CREDENTIALS すると、CREDENTIALS の内容が表示されます。 この内容も コピーしておいてください 。Kibana へのログイン時に必要となります。 Lab 環境のURLへアクセス 先ほどコピーした URL を、Web ブラウザの新しいタブに入力しアクセスします。 接続に成功すると、次の画面が表示されます。 ここで Kibana をクリックします。 Kibana 画面へのログイン Kibana のログイン画面が表示されます。 ここに、先ほどコピーした CREDENTIALS の内容を入力します。 Username: elastic Password: “…” で囲まれて表示された内容 Kibana の Home 画面の表示 ログインに成功すると、Kibana の Home 画面が表示されます。 ※注意点 Elastic Security for SIEM のトレーニングの Lab 画面を表示していない状態で、URL に直接アクセスしてログインしようとしても、この画面は表示されません。必ず Elastic Security for SIEM のトレーニングの Lab 画面を表示している状態でアクセスしてください。 演習問題の表示 Elastic Security for SIEM には演習問題が用意されています。その演習問題の表示方法もこれまでのトレーニングコースとは異なり、”CTFd”というプラットフォームを利用して、演習問題の表示や回答の入力を行います。 CTFd の URL の取得 lab-machine の bash を表示している画面の左上から [CTFd] を選択します。 すると、画面が CTFd の bash に切り替わります。 ここの URL を コピーしておきましょう (赤枠部分)。 CTFd へのアクセス 先ほどコピーした URL をWebブラウザの新しいタブへ入力し、アクセスします。 成功すると、以下の画面が表示されます。 初回登録手順 初回アクセス時は、上記の画面の Register をクリックし、初回登録画面へ進みます。 必要事項を入力して [Submit] をクリックして、登録を完了させます。 ログイン(2回目以降) 2回目以降は、Login をクリックしてログイン画面へ進みます。 必要事項を入力して、 [Submit] をクリックします。 CTFd のトップメニュー ログインに成功すると、Challenge の一覧が表示されますが、この時点ではまだ Challenge ボタンは操作しないでおきましょう。 演習問題の表示 さきほどの上部のメニュー内の Lab Guide をクリックすると、演習問題の一覧が表示されます。 この画面内のリンクをクリックすると、さらに詳細画面へ遷移し、具体的な演習内容が表示されます。 Challenge 一覧画面 上部のメニューの Challenges をクリックすると、Challenge の一覧画面に戻ります。 ここから、各演習問題の回答を入力していきますが、最初は、ロックされていて回答を入力することはできません。 回答入力画面のロック解除 例として、3.1 のボタンをクリックしてみます。 キーの入力欄が表示され、正しいキーを入力しないと、ロックが解除されません。 このキーは、Elastic Security for SIEM のテキスト内に記載されています。3.1 のキーは、3.1 章のテキストの終わり頃に記載されています。記載されているキーを入力して、 [Submit] をクリックします。 正しいキーを入力すると、上記のように、3.1 の中の各回答入力用ボタンが表示されます。 回答入力画面 例として、3.1 の 1 のボタンをクリックしてみます。 問題の内容と、回答入力欄が表示されます。回答を入力して [Submit] を押すと、回答の正誤が判定され採点されるものと思われます(筆者も、まだそこまで進んでいません)。 Windows-Endpoint Windows-Endpoint Lab 環境には Windows Endpoint が含まれています。Lab 環境内の Windows Endpoint を開くには、lab-machine の bash を表示している画面の左上から [Windows-Endpoint] を選択します。 Windows Endpoint 画面が表示されます。 Windows 環境へのコピー&ペースト 前述の操作で、Windows 環境が表示されるのですが、デフォルトの状態では Windows 環境へのコピー&ペーストができません。 (指示の一部には、Kibanaの操作画面からコマンドをコピーして、それをペーストするよう指示されているものがあります。) ※注意事項 操作には、 Chrome を使用してください。他のWebブラウザでは確認できていません。 Windows 環境へコピー&ペーストできるようにします。 Windows-Endpoint の右側に歯車アイコンがありますが、さらにその右にクリップボードのアイコンがあります。これをクリックします。すると Lab クリップボードの画面が開きます。 この Lab クリップボードを経由して、コマンドなどを張り付けるのですが、そのままでは操作できません。 右下の Learn how をクリックします。すると次のダイアログが表示されます。 上部の Open Chrome’s clipboard permission dialog をクリックします。 すると、次のポップアップが表示されます。 ここで、[許可する] をクリックしてください。 (※要は、app.strigo.io からのクリップボードへのアクセスを許可する必要があります。) これで、Kibanaの操作画面 → Lab クリップボード → Windows内のPowershell のように、Lab クリップボードを経由することで、コピー&ペーストができるようになります。 まとめ Elastic Security for SIEM のトレーニングコースの Lab 環境の雰囲気や、おおよその操作方法を理解していただけたでしょうか。 繰り返しになりますが、On-Demand コースは、 2025年10月31日まで無料で受講できます ので、この機会に受講を検討してみてはいかがでしょうか? The post Elastic Security for SIEM のトレーニング用Lab環境の解説 first appeared on Elastic Portal .
アバター
「社内のドキュメントを、ChatGPTのように対話形式で検索できたら…」 「ノーコードツールでプロトタイプを作りたいけど、検索精度が物足りない…」 そんな悩みを抱える業務改善担当者の方へ。本記事では、オープンソースのワークフロー自動化ツール n8n と、強力な検索エンジン Elasticsearch を組み合わせ、実用的な RAG システムを構築する方法を説明します。 完成後のAIチャットボットが、以下のようなものになります。 データ検索: 映画のタイトル、監督、ジャンル、あらすじなど、複数の要素を横断して検索します。 自然言語での対話: 「アクション映画でおすすめは?」といった曖昧な質問にも的確に応答します。 会話記憶: 文脈を理解した、人間らしいスムーズな対話を実現します。 目次 なぜこの技術スタックなのか?n8n, Elasticsearch, LLMの強力なシナジー その前に:n8nとは? 必要な準備 n8nの使い方: 最初のステップ クラウド版 セルフホスト版 今回構築したワークフロー  パート1:セットアップフロー(データベース構築) 1. Setup Trigger(セットアップトリガー) 2. Check Index Exists(インデックス存在確認) 3. Index Exists?(条件分岐) 4. Delete Existing Index(既存インデックス削除) 5. Create Index(インデックス作成) 6. Load CSV Data(CSVデータ読み込み) 7. Extract CSV Data(CSV解析) 8. Index Movie Data(映画データインデックス化) 9. Setup Complete(セットアップ完了) パート2:チャットボットフロー(実際の検索・応答) 10. When chat message received(チャットメッセージ受信) 11. Movie Expert AI(AI エージェント) 12. OpenAI Chat Model(言語モデル) 13. Movie Search Tool(映画検索ツール) 14. Conversation Memory(会話記憶) 15. Format Response(レスポンス整形) 作成手順のまとめ ステップ1:環境準備 ステップ2:認証情報の設定 ステップ3:セットアップフローの構築 ステップ4:チャットボットフローの構築 ステップ5:接続とテスト トラブルシューティング よくある問題と解決策 1. Elasticsearch接続エラー 2. CSVファイル読み込みエラー 3. OpenAI API呼び出しエラー 4. 日本語文字化け 機能拡張のアイデア 参考リンク なぜこの技術スタックなのか?n8n, Elasticsearch, LLMの強力なシナジー このシステムの中核をなすのが「RAG」という考え方です。これは、外部の知識データベースから関連情報を「検索(Retrieve)」し、その情報を基に大規模言語モデル(LLM)が回答を「生成(Generate)」する技術です。これにより、LLMが元々知らない情報(例:自社データ)についても、正確な回答が可能になります。 今回の構成では、各ツールがRAGにおいて完璧な役割分担を果たします。 Retriever (検索役): Elasticsearch Generator (生成役): OpenAI (GPT-3.5/4) Orchestrator (指揮者): n8n これら3つを繋ぎ合わせ、データの前処理からユーザーとの対話まで、一連の流れを 自動化 するのがn8nの役割です。ノーコード/ローコードの直感的なUIで複雑なロジックを組める柔軟性が、今回のシステム構築を可能にします。 その前に:n8nとは? ここまでn8nという名前が当たり前のように出てきましたが、ここで改めてその正体と、なぜ今多くの技術者から注目されているのか、その魅力について説明します。 n8n(エヌ・エイト・エヌ 「nodemation」の略 )は、 2019年に誕生した オープンソースのワークフロー自動化ツールです。 一言でいえば、様々なデジタルサービスやツールを、まるでパズルのピースをはめ込むように自由に組み合わせて、定型業務を自動化できるプラットフォームです。 n8nが他のツールと一線を画し、技術者・非技術者にとって強力な武器となる理由は、主に以下の3点に集約されます。 オープンソースによる柔軟性とコントロール :n8nは、クラウド版とセルフホスト版から選択可能です。 ノーコードとプロコードの融合 :ドラッグ&ドロップで簡単に自動化でき、JavaScriptやPythonでの拡張も可能です。 豊富な接続性 :数百種類のサービス(Gmail、Slack、Notionといった日常的なツールから、AWS、Google Cloud、MCPまで)に対応し、インテリジェントなシステム構築を可能にする「司令塔」として機能します。 単純作業の 自動化 で時間を生み出すだけでなく、今回のように複数の高度な技術を連携させ、新たな価値を創造する。n8nは、そんな「攻めの自動化」を実現するためのプラットフォームなのです。 この強力な自動化ツールを指揮者として、いよいよ具体的なシステム構築に入っていきましょう。 必要な準備 n8n(今回のバージョン;セルフホストv.1.97.1) Elasticsearch(今回のバージョン;9.0.3) OpenAI API(GPT-3.5/4) CSVファイル(映画データ:LLMを使用して、架空の映画のcsvのデータセットを作成した。カラムはタイトル、ジャンル、監督、作成年、映画紹介となります。90編の映画の情報が入っています。) n8nの使い方: 最初のステップ n8nを始めるための具体的な手順を、クラウド版とセルフホスト版それぞれについて説明します。 クラウド版 一番手軽に始める方法です。公式サイトからサインアップすれば、すぐに利用を開始できます。 14日間の無料トライアル が提供されています。まずは 公式サイト にアクセスし登録してみましょう。 トライアル期間終了後の料金プランは公式サイトでご確認ください。2025年7月23日現在の料金プランは以下の通りです。 セルフホスト版 ご自身のPCやサーバーでn8nを動かす方法です。 前提条件: PCに Node.js がインストールされている必要があります。 インストール: ターミナル(コマンドプロンプト)を開き、以下のコマンドを実行するだけでn8nがダウンロードされます。 npx n8n 起動: ダウンロード後、同じくターミナルで以下のコマンドを実行します。 n8n 起動が完了すると、ブラウザで http://localhost:5678/ にアクセスすることでn8nの画面を開くことができます。 この記事ではn8nの詳細な使用方法については触れませんが、後の手順でスムーズに進められるよう、主要な画面について簡単に説明します。 起動後、何もないキャンバスが表示されます。画面中央または右上のプラスアイコンをクリックしてノードを選択します。 右側からノードリストが表示されます。 検索欄から目標のものを検索できます。 ターゲットのノードをキャンバスにドラッグ&ドロップします。 プラスのマークをクリックして次のノードに繋げます。まだ設定が完了していないので赤いマークが表示されている。 ノードをダブルクリックすると設定画面が開きます。後ほど説明します。 今回構築したワークフロー  このワークフローは、CSVファイルから映画データを読み込み、Elasticsearchに保存し、OpenAI GPTを活用した自然言語での映画検索を可能にします。 大きく分けて2つの部分で構成されています: パート1:セットアップフロー(データベース構築) まず、AIの知識源となる映画データをElasticsearchに投入(インデックス)します。 こちらのワークフローの目的は、CSVデータを読み込み、いつでも検索できる形でElasticsearchに保存することです。主に「①CSV読み込みと整形」「②Elasticsearchへの登録」というステップで構成されます。 1. Setup Trigger(セットアップトリガー) 役割 :ワークフローを手動で開始するトリガー ノード :Manual Triggerを使用 2. Check Index Exists(インデックス存在確認) 役割 :Elasticsearchに「movies3」という名前のインデックスが既に存在するかチェック ノード :HTTP Request 設定詳細 : メソッド:HEAD URL:https://localhost:9200/movies3 認証:Basic認証(Elasticsearchの認証情報) Basic AuthのプールダウンからCreate new credentialを選びます。 elasticのIDとパスワードを入力します。画面の左上をダブルクリックするとノード名が変えられます。 今回、設定を簡単にするためにIgnore SSLを使いました。画面下のAdd optionをクリックすると選択できます。 Parametersタブ以外にSettingsタブもありますが今回は使用しません。 3. Index Exists?(条件分岐) 役割 :インデックスの存在状況に基づいて処理を分岐 ノード: IF 設定 :条件判定 条件 :$response.statusCode == 404(存在しない場合) 分岐 : True(存在しない)→ 新規作成へ False(存在する)→ 削除してから新規作成へ ※設定画面を閉じたい場合、黄色のエリアをクリックします。 4. Delete Existing Index(既存インデックス削除) 役割 :既存のインデックスを削除 ノード :Elasticsearch 設定 : リソース:Index オペレーション:Delete インデックスID:movies3 5. Create Index(インデックス作成) 役割 :新しいElasticsearchインデックスを作成 ノード:Elasticsearch 設定 : リソース:Index オペレーション:Create インデックスID:movies3 6. Load CSV Data(CSVデータ読み込み) 役割 :ローカルファイルからCSVデータを読み込み ノード :Read/Write File 設定 : ファイルパス:/Users/※※※/n8n/映画情報.csv (ファイルパスは自分の環境に合わせて変更が必要です) データプロパティ名:data 7. Extract CSV Data(CSV解析) 役割 :読み込んだCSVファイルを構造化データに変換 ノード :Extract from File 設定 :Raw Dataを無効化してJSONとして解析 8. Index Movie Data(映画データインデックス化) 役割 :解析したCSVデータをElasticsearchに保存 ノード :Elasticsearch 設定詳細 : オペレーション:Create インデックスID:movies3 フィールドマッピング: タイトル ← CSVのタイトル列 監督 ← CSVの監督列 映画紹介 ← CSVの映画紹介列 作成年 ← CSVの作成年列 ジャンル ← CSVのジャンル列 9. Setup Complete(セットアップ完了) 役割 :セットアップの完了状況を記録 ノード :Set 出力 :完了メッセージとインデックス化されたレコード数 これで最初のフローが完成しました。 右下にある緑の✅がうまく動いていたとの意味になります。 パート2:チャットボットフロー(実際の検索・応答) 次に、ユーザーからの質問にAIが答えるためのフローを構築します。 このワークフローの目的は、ユーザーの質問をトリガーに、LangChainのAIエージェントが自律的に思考・行動し、最適な回答を返すことです。AIエージェントは、必要に応じて専用検索ツール、Elasticsearch を呼び出し、関連情報を取得します。 10. When chat message received(チャットメッセージ受信) 役割 :ユーザーからのチャットメッセージを受信するトリガー ノード :Chat Trigger 設定 :メッセージを受信 11. Movie Expert AI(AI エージェント) 役割 :チャットボットの中核となるAIエージェント ノード :AI Agent システムメッセージ 12. OpenAI Chat Model(言語モデル) 役割 :自然言語理解と生成を担当 ノード :OpenAI Chat Model  設定 : モデル:gpt-3.5-turbo 最大トークン:1000 温度:0.7(創造性のバランス) Credential to connect withからCreate new credentialを選んでOpenAIのAPIを入力する必要があります。 13. Movie Search Tool(映画検索ツール) 役割 :Elasticsearchでの映画検索を実行 ノード :HTTP Request Tool 検索クエリの詳細 : { "query": { "bool": { "should": [ { "multi_match": { "query": "{{ $parameter.searchText }}", "fields": [ "タイトル^3", "監督^2", "ジャンル^2", "映画紹介^1" ], "type": "best_fields", "fuzziness": "AUTO", "operator": "or" } }, { "wildcard": { "タイトル.keyword": "*{{ $parameter.searchText }}*" } } ] } } } 検索の特徴 : bool / should: ここに含まれる条件のいずれかに一致すれば結果として返されます。「OR検索」のような振る舞いをし、検索の網羅性を高めます 。 multi_match: ユーザーからの検索語 {{ $parameter.searchText }} を、複数のフィールド (タイトル, 監督など) に対して同時に検索します 。 fields とブースト (^3) : ここがチューニングの要です。「タイトル」フィールドの重要度を3倍、「監督」「ジャンル」を2倍にしています 。これにより、検索語が紹介文に含まれるだけの映画よりも、タイトルそのものに含まれる映画が、より関連度が高いと判断され、検索結果の上位に表示されます。 fuzziness: “AUTO”: ユーザーが多少タイポしても(例:「インターステラ」を「インタステラー」と入力)、Elasticsearchが賢く解釈して正しい映画を見つけてくれます 。 wildcard: multi_matchが単語単位での検索なのに対し、こちらは部分一致を可能にします 。例えば「スター」と検索すれば「スター・ウォーズ」がヒットするようになります。このように性質の異なる検索方法を should で組み合わせることで、検索の精度と網羅性を両立させています。 size: 5 : 検索結果を最大5件に絞り込み、LLMに渡す情報量を適切にコントロールします 。 14. Conversation Memory(会話記憶) 役割 :対話の文脈を記憶・維持 ノード :Memory Buffer Window 設定 :セッションキー「movie-chat-session」で会話を識別 15. Format Response(レスポンス整形) 役割 :AIの回答を整形して返す ノード :Set 出力項目 : response:AIの回答 timestamp:回答時刻 status:処理状況 clean_output:整形済み回答 これで最後のフローが完成しました。 右下にある緑の✅がうまく動いていたとの意味になります。 左下の黄色の点線が質問を書くとこになります。 作成手順のまとめ ステップ1:環境準備 n8nをインストール・起動 Elasticsearchをインストール・起動 OpenAI APIキーを取得 映画データCSVファイルを準備 ステップ2:認証情報の設定 n8nの「Credentials」でElasticsearch認証情報を作成 OpenAI API認証情報を作成 HTTP Basic認証情報を作成 ステップ3:セットアップフローの構築 Manual Triggerノードを配置 HTTP Requestノードで存在確認を設定 IFノードで条件分岐を作成 Elasticsearchノードでインデックス操作を設定 ファイル読み込み・解析ノードを配置 データインデックス化ノードを設定 ステップ4:チャットボットフローの構築 Chat Triggerノードを配置 LangChain AgentノードでAIエージェントを設定 OpenAI Chat Modelノードを接続 HTTP Request Toolで検索機能を実装 Memory Buffer Windowで記憶機能を追加 Setノードで回答整形を設定 ステップ5:接続とテスト 全ノードを適切に接続 セットアップフローを実行してデータを投入 チャットボットをテストして動作確認 トラブルシューティング よくある問題と解決策 1. Elasticsearch接続エラー 症状 :「Connection refused」エラー 解決策 :Elasticsearchが起動しているか確認、URLとポート番号を確認 2. CSVファイル読み込みエラー 症状 :「File not found」エラー 解決策 :ファイルパスを正しく設定、ファイルの存在を確認 3. OpenAI API呼び出しエラー 症状 :「Unauthorized」エラー 解決策 :APIキーが正しく設定されているか確認 4. 日本語文字化け 症状 :日本語が正しく表示されない 解決策 :CSVファイルの文字エンコードをUTF-8に設定 機能拡張のアイデア このシステムは出発点ですが、さらに進化させることも可能です。 ベクトル検索への移行: 今回はキーワードベースの検索が中心でしたが、Elasticsearchのベクトル検索機能を活用すれば、「宇宙がテーマの壮大な映画」のような、より抽象的な意味での類似映画検索が可能になります。 Kibanaでの可視化連携: Elasticsearchとセットで使われる可視化ツールKibanaを連携させれば、「年代別の映画ジャンル構成」などをダッシュボードで分析できます 。 音声入力対応: n8nのWhisperノードなどを組み合わせれば、音声で質問できるチャットボットへの拡張も夢ではありません 。 MCP(Model Context Protocol)の導入 n8n は MCP クライアント/サーバー機能をベータ公開しており、AI エージェントが外部ツールや n8n の別ワークフローを連携しやすくなっています。 たとえば、Elasticsearch 検索後に別のデータベースをGPTに問い合わせたり、Slack通知やファイル出力ワークフローを「AI による判断で自動実行」するといった高度な連携がノーコードで可能です。 私もこのプロジェクトは初めてのトライでしたが、まず基本的な構成で動作させてから、段階的に機能を追加していくことをお勧めします。このチャットボットをベースに、様々な分野のデータベース検索システムに応用することが可能です。 参考リンク n8n公式ドキュメント https://github.com/n8n-io/n8n Elasticsearch公式ガイド   Elasticの14日間無料期間もあります。 OpenAI API ドキュメント LangChain ドキュメント 映画情報ダウンロード(CSV) The post ローコードRAG構築:n8nとElasticsearchで作るAI検索チャットボット first appeared on Elastic Portal .
アバター
Elasticsearchの標準アナライザーは  Kuromoji  ですが、他にも日本語向けのアナライザーが存在します。本記事では  Sudachi  や  MeCab 、およびPythonライブラリの  Janome 、そして  LLM(GPT-4)  といった選択肢を比較し、どんな場面でどれを使うべきかを検討しました。 なお、Elasticsearch 9.xではSudachiやMeCabの公式対応プラグインはまだリリースされていません。そのため今回は  Python環境で事前に形態素解析してからElasticsearchにインデックスする方法 を採用しています。一方、Janomeは純Python実装であるため、追加プラグインなしで使用可能です。 目次 比較対象アナライザー ステップ0:事前準備 Python環境とテキスト設定 トークン正規化・クリーニング関数 比較戦略 トークン化関数の定義(共通フォーマット出力) 包括的トークナイザー比較分析 📊 Kuromoji(ベースライン)出力 📊 Sudachi A/B/C、MeCab、Janome、GPT-4 出力 分析結果サマリー・統計表 1. トークン数比較表 2. ユニークトークン分析 考察 まとめ 比較対象アナライザー 1. Elasticsearchプラグイン系    Kuromoji :Elasticsearch標準の日本語アナライザー 2. Pythonライブラリ系(アプリ側で事前解析) SudachiPy :3種類の粒度(A/B/C)に対応 MeCab :高速かつ実績豊富な形態素解析器 Janome :Pure Pythonで導入が簡単 3. LLM系(外部API)    OpenAI GPT-4o :文脈を考慮した柔軟な分割が可能 ステップ0:事前準備 1. 仮想環境の作成(uvを使用) curl -LsSf https://astral.sh/uv/install.sh | sh mkdir analyzer-project && cd analyzer-project uv venv source .venv/bin/activate 2. 必要なPythonライブラリのインストール uv pip install elasticsearch janome openai sudachipy sudachidict_core mecab-python3 3. Elasticsearchプラグインの確認(Kuromoji) bin/elasticsearch-plugin install analysis-kuromoji 4. MeCab本体のインストール(macOS向け) brew install mecab mecab-ipadic 5. OpenAI APIキーの設定 export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" Python環境とテキスト設定 import os import sys import platform import re from dotenv import load_dotenv # Import the working elasticsearch connection utility from elastic_conection import es, test_connection # Japanese text analysis libraries import MeCab from janome.tokenizer import Tokenizer from sudachipy import tokenizer as sudachi_tokenizer from sudachipy import dictionary as sudachi_dictionary # OpenAI for LLM comparison from openai import OpenAI # Environment and target text setup print("Python環境情報") print("="*30) print(f"Python Version: {sys.version.split()[0]}") print(f"Platform: {platform.platform()}") print(f"Architecture: {platform.machine()}") # Check if in virtual environment if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): print("仮想環境で実行中") else: print("仮想環境ではありません") print(f"実行環境: {sys.executable}") # Define the target text for analysis TARGET_TEXT = """ロキソニン錠の説明書ですね。ロキソニンは、解熱鎮痛作用のある非ステロイド性抗炎症薬(NSAIDs)で、 痛みや発熱、炎症を抑える効果があります。具体的には、関節リウマチ、変形性関節症、腰痛症、肩こり、 歯痛、手術後や外傷後の炎症や痛み、風邪による熱や痛みなどに用いられます。""" print(f"\n分析対象テキスト: {TARGET_TEXT}") print(f"文字数: {len(TARGET_TEXT)}") print() Python環境情報 ============================== Python Version: 3.13.3 Platform: macOS-15.5-arm64-arm-64bit-Mach-O Architecture: arm64 仮想環境で実行中 実行環境: /Users/*****/es-analyzer-project/.venv/bin/python 分析対象テキスト: ロキソニン錠の説明書ですね。ロキソニンは、解熱鎮痛作用のある非ステロイド性抗炎症薬(NSAIDs)で、 痛みや発熱、炎症を抑える効果があります。具体的には、関節リウマチ、変形性関節症、腰痛症、肩こり、 歯痛、手術後や外傷後の炎症や痛み、風邪による熱や痛みなどに用いられます。 文字数: 137 トークン正規化・クリーニング関数 Kuromoji を基準(ベースライン)として使用 し、他のアナライザーの結果をKuromojiレベルまでクリーニングして比較します。 比較戦略 1. Kuromoji = ベースライン クリーニングなし : Kuromojiの生出力をそのまま使用 理由 : Elasticsearchに最適化済み、自然にストップワード除去済み 役割 : 他のアナライザーの目標レベルとして機能 2. 他のアナライザー = Kuromojiレベルまでクリーニング MeCab, SudachiPy, Janome, OpenAI : クリーニング関数を適用 目標 : Kuromojiと同等の品質レベルに調整 比較 : クリーニング後にKuromojiとの類似度を測定 クリーニング処理内容(Kuromoji以外) 1. 助詞・助動詞の除去 : 「は」「を」「に」「が」など機能語の削除 2. 句読点・記号の除去 : 「、」「。」「(」「)」などの除去 3. ストップワードの除去 : 検索で意味の薄い語の削除 4. 空白・数字の除去 : 純粋な数字や空白文字の除去 期待される効果 公平な比較 : 全アナライザーが同じ品質レベルで比較される 実用性評価 : 検索エンジンでの実際の使用場面を想定 最適化効果 : 各アナライザーのクリーニング後の性能向上を確認 Kuromoji優位性 : Elasticsearchプラグインとしての最適化効果を確認 def clean_tokens_like_kuromoji(tokens): """ Clean tokens to match Kuromoji's behavior by removing stop words and punctuation. Args: tokens (list): List of token strings Returns: list: Cleaned tokens with stop words and punctuation removed """ # Japanese stop words and particles commonly filtered out stop_words = { 'は', 'の', 'を', 'に', 'が', 'で', 'と', 'から', 'まで', 'より', 'へ', 'や', 'か', 'も', 'て', 'た', 'だ', 'である', 'です', 'ます', 'した', 'する', 'ある', 'いる', 'なる', 'この', 'その', 'あの', 'どの', 'これ', 'それ', 'あれ', 'どれ', 'ここ', 'そこ', 'あそこ', 'どこ', 'こう', 'そう', 'ああ', 'どう', 'という', 'といった', 'による', 'において', 'について', 'に関して', 'に対して', 'に関する', 'について', 'さん', 'ちゃん', 'くん', 'さま', 'さあ', 'まあ', 'ああ', 'いや', 'はい', 'いいえ', 'うん', 'ううん', 'えー', 'あー', 'うー', 'おー' } # Punctuation and symbols to remove punctuation_patterns = [ r'^[、。!?.,!?;:()()\[\]「」『』【】〈〉《》〔〕…‥・]+$', # Pure punctuation r'^[ー\-~〜]+$', # Long vowel marks and dashes r'^[ \s]+$', # Whitespace (including full-width) r'^\d+$', # Pure numbers r'^[a-zA-Z]+$', # Pure alphabet r'^[0-9]+$', # Full-width numbers r'^[a-zA-Z]+$' # Full-width alphabet ] cleaned = [] for token in tokens: if not token or not token.strip(): continue # Remove stop words if token in stop_words: continue # Remove punctuation and unwanted patterns is_punctuation = False for pattern in punctuation_patterns: if re.match(pattern, token): is_punctuation = True break if not is_punctuation: cleaned.append(token) return cleaned print("Token cleaning function defined") トークナイザー初期化・接続確認 各トークナイザーを初期化し、動作確認を行います。 初期化対象 : 1. Elasticsearch + Kuromoji ローカルElasticsearchサーバー(localhost:9200)への接続 Kuromojiアナライザーの利用可能性確認 2. MeCab Homebrew環境のIPA辞書パス設定 分かち書きモード(-O wakati)での初期化 3. SudachiPy 標準辞書の読み込み A/B/Cモード対応トークナイザーオブジェクト作成 4. Janome 純Python実装のトークナイザー初期化 依存関係なしの簡単セットアップ 5. OpenAI GPT-4o 環境変数からAPIキー取得 GPT-4モデルへの接続確認 注意 : 各ツールが正常に初期化されない場合、エラーメッセージが表示されます。 # === システム接続確認とトークナイザー初期化 === print("=== システム接続確認 ===") # Elasticsearch接続と初期化 print("Elasticsearchに接続中...") try: # elastic_conection.pyからESクライアントをインポート from elastic_conection import es, test_connection # 接続テスト if test_connection(): es_available = True print("Elasticsearch接続成功") else: es_available = False print("Elasticsearch接続失敗") print("解決方法: Elasticsearchサーバーを起動してください") print(" brew services start elasticsearch") except Exception as e: es_available = False print(f"Elasticsearch接続失敗: {e}") print("解決方法: Elasticsearchサーバーを起動してください") print(" brew services start elasticsearch") # 各トークナイザーの初期化 print("\nトークナイザーを初期化中...") # MeCab初期化 (Homebrew環境対応) tagger = None try: import MeCab # Homebrew環境用の設定リスト(正しいmecabrcパスを含む) configs_to_try = [ "-r /opt/homebrew/etc/mecabrc -Owakati", # Homebrew mecabrc + wakati mode "-r /opt/homebrew/etc/mecabrc", # Homebrew mecabrc only "-Owakati", # wakati mode only "", # デフォルト設定 "-d /opt/homebrew/lib/mecab/dic/ipadic", # ipadic辞書パス (Homebrew) "-d /usr/local/lib/mecab/dic/ipadic", # ipadic辞書パス (従来のパス) ] for config in configs_to_try: try: print(f" MeCab設定を試行中: '{config if config else 'デフォルト'}'") tagger = MeCab.Tagger(config) # テスト実行して動作確認 test_result = tagger.parse("テスト") if test_result and len(test_result.strip()) > 0: mecab_config = config if config else "デフォルト設定" print(f"MeCab初期化成功 (設定: {mecab_config})") print(f" テスト結果: {test_result.strip()}") break except Exception as e: print(f" 設定失敗: {e}") continue if not tagger: print("MeCab初期化失敗: MeCab could not be initialized with any configuration") print("解決方法: brew install mecab mecab-ipadic") except ImportError: print("MeCab初期化失敗: MeCabがインストールされていません") print("解決方法: brew install mecab mecab-ipadic") # SudachiPy初期化 tokenizer_obj = None try: from sudachipy import tokenizer from sudachipy import dictionary tokenizer_obj = dictionary.Dictionary().create() print("SudachiPy初期化成功") except Exception as e: print(f"SudachiPy初期化失敗: {e}") # Janome初期化 janome_tokenizer = None try: from janome.tokenizer import Tokenizer janome_tokenizer = Tokenizer() print("Janome初期化成功") except Exception as e: print(f"Janome初期化失敗: {e}") # OpenAI API初期化 openai_client = None try: import openai from dotenv import load_dotenv import os load_dotenv() api_key = os.getenv("OPENAI_API_KEY") if api_key: openai_client = openai.OpenAI(api_key=api_key) print("OpenAI API初期化成功") else: print("OpenAI API初期化失敗: APIキーが設定されていません") print("解決方法: .envファイルにOPENAI_API_KEYを設定してください") except Exception as e: print(f"OpenAI API初期化失敗: {e}") # 初期化サマリー print("\n初期化サマリー:") print(f" Elasticsearch: {'OK' if es_available else 'NG'}") print(f" MeCab: {'OK' if tagger else 'NG'}") print(f" SudachiPy: {'OK' if tokenizer_obj else 'NG'}") print(f" Janome: {'OK' if janome_tokenizer else 'NG'}") print(f" OpenAI: {'OK' if openai_client else 'NG'}") トークン化関数の定義(共通フォーマット出力) tokenize_with_kuromoji(text) tokenize_with_mecab(text) tokenize_with_sudachi(text, mode) tokenize_with_janome(text) tokenize_with_openai(text) すべての関数でエラーハンドリングを実装し、失敗時は空リストを返します。 def tokenize_with_kuromoji(text): """Tokenize text using Elasticsearch Kuromoji analyzer""" if not es_available: print("Kuromoji tokenization skipped: Elasticsearch not available") return [] try: response = es.indices.analyze( body={ "analyzer": "kuromoji", "text": text } ) return [token['token'] for token in response['tokens']] except Exception as e: print(f"Kuromoji tokenization error: {e}") return [] def tokenize_with_mecab(text): """Tokenize text using MeCab""" if not tagger: print("MeCab tokenization skipped: MeCab not available") return [] try: result = tagger.parse(text).strip().split() return [token for token in result if token] except Exception as e: print(f"MeCab tokenization error: {e}") return [] def tokenize_with_sudachi(text, mode='C'): """Tokenize text using SudachiPy with specified mode (A, B, or C)""" if not tokenizer_obj: print("SudachiPy tokenization skipped: SudachiPy not available") return [] try: mode_map = {'A': sudachi_tokenizer.Tokenizer.SplitMode.A, 'B': sudachi_tokenizer.Tokenizer.SplitMode.B, 'C': sudachi_tokenizer.Tokenizer.SplitMode.C} tokens = tokenizer_obj.tokenize(text, mode_map[mode]) return [token.surface() for token in tokens] except Exception as e: print(f"SudachiPy tokenization error: {e}") return [] def tokenize_with_janome(text): """Tokenize text using Janome""" if not janome_tokenizer: print("Janome tokenization skipped: Janome not available") return [] try: tokens = janome_tokenizer.tokenize(text, wakati=True) return list(tokens) except Exception as e: print(f"Janome tokenization error: {e}") return [] def tokenize_with_openai(text): """Tokenize text using OpenAI GPT-4""" if not openai_client: print("OpenAI tokenization skipped: OpenAI client not available") return [] try: prompt = f""" Please tokenize the following Japanese text into meaningful segments. Return only a comma-separated list of tokens, no explanations. Text: {text} Tokens:""" response = openai_client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": prompt}], max_tokens=200, temperature=0 ) result = response.choices[0].message.content.strip() tokens = [token.strip() for token in result.split(',')] return [token for token in tokens if token] except Exception as e: print(f"OpenAI tokenization error: {e}") return [] print("All tokenization functions defined (with availability checks)") 包括的トークナイザー比較分析 def compare_all_tokenizers(text): """Compare all tokenizers on the given text - using Kuromoji as baseline (no cleaning)""" print(f"分析対象テキスト: {text}") print("=" * 80) results = {} # 1. Kuromoji (Elasticsearch) - BASELINE (no cleaning applied) print("\n1. Kuromoji (Elasticsearch) - ベースライン") kuromoji_tokens = tokenize_with_kuromoji(text) results['kuromoji'] = { 'raw': kuromoji_tokens, 'cleaned': kuromoji_tokens # No cleaning - use as baseline } print(f"Raw/Baseline ({len(kuromoji_tokens)}): {kuromoji_tokens}") print("Kuromojiはベースラインとして使用(クリーニングなし)") # 2. SudachiPy (all modes) - cleaned to match Kuromoji behavior for mode in ['A', 'B', 'C']: print(f"\n2. SudachiPy Mode {mode} - Kuromojiベース調整済み") sudachi_tokens = tokenize_with_sudachi(text, mode) sudachi_cleaned = clean_tokens_like_kuromoji(sudachi_tokens) results[f'sudachi_{mode}'] = { 'raw': sudachi_tokens, 'cleaned': sudachi_cleaned } print(f"Raw ({len(sudachi_tokens)}): {sudachi_tokens}") print(f"Cleaned ({len(sudachi_cleaned)}): {sudachi_cleaned}") # 3. MeCab - cleaned to match Kuromoji behavior print(f"\n3. MeCab - Kuromojiベース調整済み") mecab_tokens = tokenize_with_mecab(text) mecab_cleaned = clean_tokens_like_kuromoji(mecab_tokens) results['mecab'] = { 'raw': mecab_tokens, 'cleaned': mecab_cleaned } print(f"Raw ({len(mecab_tokens)}): {mecab_tokens}") print(f"Cleaned ({len(mecab_cleaned)}): {mecab_cleaned}") # 4. Janome - cleaned to match Kuromoji behavior print(f"\n4. Janome - Kuromojiベース調整済み") janome_tokens = tokenize_with_janome(text) janome_cleaned = clean_tokens_like_kuromoji(janome_tokens) results['janome'] = { 'raw': janome_tokens, 'cleaned': janome_cleaned } print(f"Raw ({len(janome_tokens)}): {janome_tokens}") print(f"Cleaned ({len(janome_cleaned)}): {janome_cleaned}") # 5. OpenAI GPT-4 - cleaned to match Kuromoji behavior if openai_client: print(f"\n5. OpenAI GPT-4 - Kuromojiベース調整済み") openai_tokens = tokenize_with_openai(text) openai_cleaned = clean_tokens_like_kuromoji(openai_tokens) results['openai'] = { 'raw': openai_tokens, 'cleaned': openai_cleaned } print(f"Raw ({len(openai_tokens)}): {openai_tokens}") print(f"Cleaned ({len(openai_cleaned)}): {openai_cleaned}") else: print(f"\n5. OpenAI GPT-4 (スキップ - API key not available)") # Comparison summary with Kuromoji as baseline print(f"\nKuromojiベースライン比較:") kuromoji_baseline = set(results['kuromoji']['cleaned']) for name, data in results.items(): if name != 'kuromoji': cleaned_tokens = set(data['cleaned']) overlap = len(kuromoji_baseline & cleaned_tokens) total_unique = len(kuromoji_baseline | cleaned_tokens) similarity = (overlap / total_unique * 100) if total_unique > 0 else 0 print(f" {name:<12}: {similarity:.1f}% similarity to Kuromoji baseline") return results # Run the comparison analysis_results = compare_all_tokenizers(TARGET_TEXT) 📊 Kuromoji(ベースライン)出力 トークン数:42 特徴:分かち書きが細かく、Elasticsearchでのインデックスに最適 📊 Sudachi A/B/C、MeCab、Janome、GPT-4 出力 Sudachiモードごとに粒度が変化 GPT-4oは語彙的まとまり重視で分割が異なる MeCab・Janomeは細かく分かれ、類似度が高い 1. Kuromoji (Elasticsearch) - ベースライン Raw/Baseline (42): ['ロキソニン', '錠', '説明', '書', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', 'nsaids', '痛み', '発熱', '炎症', '抑える', '効果', '具体', '的', '関節', 'リウマチ', '変形', '性', '関節', '症', '腰痛', '症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛む', '風邪', '熱', '痛み', '用いる'] Kuromojiはベースラインとして使用(クリーニングなし) 2. SudachiPy Mode A Raw (86): ['ロキソニン', '錠', 'の', '説明', '書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '\n', '痛', 'み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体', '的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形', '性', '関節', '症', '、', '腰痛', '症', '、', '肩こり', '、', '\n', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛', 'み', '、', '風邪', 'に', 'よる', '熱', 'や', '痛', 'み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (49): ['ロキソニン', '錠', '説明', '書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛', 'み', '発熱', '炎症', '抑える', '効果', 'あり', '具体', '的', '関節', 'リウマチ', '変形', '性', '関節', '症', '腰痛', '症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛', 'み', '風邪', 'よる', '熱', '痛', 'み', 'など', '用い', 'られ'] 2. SudachiPy Mode B Raw (78): ['ロキソニン', '錠', 'の', '説明書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '\n', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形性', '関節症', '、', '腰痛症', '、', '肩こり', '、', '\n', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛み', '、', '風邪', 'に', 'よる', '熱', 'や', '痛み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (41): ['ロキソニン', '錠', '説明書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あり', '具体的', '関節', 'リウマチ', '変形性', '関節症', '腰痛症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛み', '風邪', 'よる', '熱', '痛み', 'など', '用い', 'られ'] 2. SudachiPy Mode C Raw (78): ['ロキソニン', '錠', 'の', '説明書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '\n', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形性', '関節症', '、', '腰痛症', '、', '肩こり', '、', '\n', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛み', '、', '風邪', 'に', 'よる', '熱', 'や', '痛み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (41): ['ロキソニン', '錠', '説明書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あり', '具体的', '関節', 'リウマチ', '変形性', '関節症', '腰痛症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛み', '風邪', 'よる', '熱', '痛み', 'など', '用い', 'られ'] 3. MeCab Raw (80): ['ロキソニン', '錠', 'の', '説明', '書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体', '的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形', '性', '関節', '症', '、', '腰痛', '症', '、', '肩こり', '、', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛み', '、', '風邪', 'による', '熱', 'や', '痛み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (45): ['ロキソニン', '錠', '説明', '書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あり', '具体', '的', '関節', 'リウマチ', '変形', '性', '関節', '症', '腰痛', '症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛み', '風邪', '熱', '痛み', 'など', '用い', 'られ'] 4. Janome Raw (82): ['ロキソニン', '錠', 'の', '説明', '書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '\n', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体', '的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形', '性', '関節', '症', '、', '腰痛', '症', '、', '肩こり', '、', '\n', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛み', '、', '風邪', 'による', '熱', 'や', '痛み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (45): ['ロキソニン', '錠', '説明', '書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あり', '具体', '的', '関節', 'リウマチ', '変形', '性', '関節', '症', '腰痛', '症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛み', '風邪', '熱', '痛み', 'など', '用い', 'られ'] 5. OpenAI GPT-4o Raw (40): ['ロキソニン錠', 'の', '説明書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱鎮痛作用', 'の', 'ある', '非ステロイド性抗炎症薬', '(', 'NSAIDs', ')', 'で', '、', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あります', '。', '具体的', 'に', 'は', '、', '関節リウマチ', '、', '変形性関節症', '、', '腰痛症', '、', '肩こり'] Cleaned (17): ['ロキソニン錠', '説明書', 'ね', 'ロキソニン', '解熱鎮痛作用', '非ステロイド性抗炎症薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あります', '具体的', '関節リウマチ', '変形性関節症', '腰痛症', '肩こり'] 分析結果サマリー・統計表 トークン化結果を定量的に分析し、各アナライザーの特性を明確にします。 サマリーテーブル内容 : 1. トークン数比較表 Raw Tokens : 各アナライザーの生トークン数 Cleaned Tokens : クリーニング後のトークン数 Effectiveness : クリーニング効果率(ノイズ除去率) 2. ユニークトークン分析 各アナライザー固有のトークン : 他では検出されない独自トークン 全アナライザー共通トークン : すべてで一致する基本トークン トークン多様性 : 全体での語彙カバレッジ 分析指標 : トークン総数 : 各手法の分割粒度の違い 共通度 : アナライザー間の一致率 独自性 : 各手法の特徴的な分割パターン 活用方法 : 検索システム : 共通トークンは検索精度向上に寄与 NLP処理 : 用途に応じた最適アナライザー選択 品質評価 : トークン化の一貫性・信頼性評価 Tokenization Results Summary - Kuromoji Baseline ========================================================================================== Tokenizer Raw Tokens Final Tokens vs Kuromoji Note ------------------------------------------------------------------------------------------ kuromoji 42 42 100.0% ベースライン sudachi_A 86 49 71.4% クリーニング済み sudachi_B 78 41 53.3% クリーニング済み sudachi_C 78 41 53.3% クリーニング済み mecab 80 45 79.5% クリーニング済み janome 82 45 79.5% クリーニング済み openai 40 17 15.9% クリーニング済み 上の表の読み解き方; Raw Tokens (生トークン数) アナライザーがテキストを最初に分割した直後のトークン(単語)の総数です。この数値が大きいほど、より細かく単語を分割していることを示します。 Final Tokens (最終トークン数) 「生トークン」から助詞(「は」「が」など)、句読点、記号といった検索ノイズになりやすい不要なトークンを取り除いた(クリーニングした)後の数です。 このクリーニング処理により、各アナライザーを公平な土俵で比較できるようになります。 vs Kuromoji (Kuromojiとの類似度) クリーニング後のトークンセットが、基準であるKuromojiのトークンセットとどれだけ似ているかを示す割合(Jaccard係数)です。 計算式 : (両者に共通するトークン数) ÷ (どちらか一方にでも存在するユニークなトークン総数) このパーセンテージが高いほど、そのアナライザーの分割結果がKuromojiと似ていることを意味します。例えば、MeCabとJanomeはクリーニング後に80%の類似度となり、Kuromojiと非常に近い結果を出していることがわかります。 🔍 Unique Tokens Analysis (Cleaned Results vs Kuromoji Baseline) ====================================================================== sudachi_A: ['あり', 'など', 'ね', 'み', 'よる', 'られ', '用い', '痛'] sudachi_B: ['あり', 'など', 'ね', 'よる', 'られ', '具体的', '変形性', '用い', '腰痛症', '説明書', '関節症'] sudachi_C: ['あり', 'など', 'ね', 'よる', 'られ', '具体的', '変形性', '用い', '腰痛症', '説明書', '関節症'] mecab: ['あり', 'など', 'ね', 'られ', '用い'] janome: ['あり', 'など', 'ね', 'られ', '用い'] openai: ['あります', 'ね', 'ロキソニン錠', '具体的', '変形性関節症', '腰痛症', '解熱鎮痛作用', '説明書', '関節リウマチ', '非ステロイド性抗炎症薬'] Kuromoji unique tokens (not found in cleaned versions of others): kuromoji: ['nsaids', '用いる', '痛む'] Common tokens across all tokenizers (after cleaning): ['ロキソニン', '効果', '抑える', '炎症', '発熱', '肩こり'] 📊 統計サマリー: 📊 Kuromojiベースライン総トークン数: 34 📊 全アナライザー共通トークン数: 6 📊 共通度: 17.6% 上記は「各アナライザーのクリーニング後トークン」から「Kuromojiのトークン」を引いた残りのリストです。つまり、 Kuromojiにはないが、そのアナライザーだけが生成したユニークなトークン です。各ツールの辞書や分割ルールの違いが見てとれます。 Kuromojiだけが生成したトークン の後に、クリーニング後に すべてのアナライザーが共通して生成したトークン です。これらは、どのツールを使っても分割結果が変わらない、文章の核となる重要な単語と言えます。 考察 Kuromoji / MeCab / Janome 「専門職」→「専門」「職」、「看護師」→「看護」「師」のように、単語を細かく分割します。 これにより検索ヒット率が向上し、部分一致検索や強調表示に適しています。 Sudachi 「専門職」や「看護師」などの複合語を1トークンとして保持します。 意味のまとまりを重視する分析に向いており、モード切り替え(A/B/C)で粒度を調整できます。 Cモード : 「より良い検索体験を提供したい」が1語扱いになるため、特定の複合語での検索には不向きな場合があります。 Bモード : 実用面でバランスの取れた粒度を提供します。 LLM(GPT-4o) 文脈理解に基づいた分かち書きが可能です。 「介護福祉士」や「認知症」など、語彙として自然なまとまりで出力されます。 トークンの一貫性がないため、Elasticsearchのインデックス用途には不向きですが、意味理解や質問応答に最適です。 まとめ 日本語検索において、どのアナライザーを使うかは「検索したい内容」と「求める粒度」によって変わります。 細かく一致させたいなら Kuromoji 検索文をそのまま一致させたいなら Sudachi Cモード バランス重視なら Sudachi Bモード The post 日本語アナライザーの比較:Kuromoji・Sudachi・MeCab・Janome・LLMの性能検証 first appeared on Elastic Portal .
アバター
目次 はじめに 対象読者 環境 Elasticsearch同梱モデル vs 外部モデル Elasticsearch同梱の Model を利用する場合 Elasticsearchの外部のEmbed Modelを利用する場合 比較表 Elasticsearchで密ベクトル生成に利用可能なサービス 準備 Cohere API Key の取得 Machine Learning インスタンス /_inference/text_embedding/用エンドポイントの作成 インデックスの作成 マッピングの作成 ドキュメントの登録 登録された密ベクトルの確認 密ベクトル検索 まとめ はじめに 今回は、Elasticsearchでの密ベクトル検索において、外部のEmbed Modelを利用する方法について解説します。 これまで、 ホワイトペーパー 「Elasticsearchを使った簡易RAGアプリケーションの作成」やブログ記事「 Elasticsearchでのベクトル検索の準備 」で密ベクトル検索をご紹介してきました。これらはElasticsearchが同梱している .multilingual-e5-small というEmbed Modelを利用していました。 本記事では、Elasticsearchの外部にあるEmbed Modelを使って密ベクトル検索を行う方法を詳しく説明します。 対象読者 Elasticsearchの初級者~中級者 環境 Elasticsearch 8.18以上、かつPlatinum License以上(筆者はElasticsearch 9.0.3 Enterprise Licenseで動作確認しました。) Cohere embed-multilingual-light-v3.0 (Elasticsearchの /_inference/text_embedding/ が対応しているEmbed Modelであれば動作します。筆者は左記のモデルで動作確認しました。) なお、本ブログで紹介する /_inference/text_embedding/ を使った密ベクトル生成は、Basic Licenseでは動作しません。Basic Licenseをご利用の場合は、ベクトル変換処理のプログラムを作成して、それらを呼び出すなどの対応が必要です。 また、本ブログではフィールドタイプに semantic_text を指定しています。semantic_text はv8.18でGAとなりました。 ※本記事では密ベクトル検索に焦点を当てるため、形態素解析や、形態素解析を利用したハイブリッド検索については省略します。 Elasticsearch同梱モデル vs 外部モデル Elasticsearchが同梱しているEmbed Modelを利用する方法と、外部のEmbed Modelを利用する方法を比較してみましょう。 Elasticsearch同梱の Model を利用する場合 Elasticsearch同梱の Embed Model を利用する場合のドキュメント登録時の概略図を以下に示します。 Elasticsearchの外部のEmbed Modelを利用する場合 Elasticsearch の外部の Embed Model を利用する場合のドキュメント登録時の概略図を以下に示します。 比較表 項目 Elasticsearch同梱のModelを利用 Elasticsearchの外部のModelを利用 手軽さ ○ Elasticsearchのみ契約すればよい × 別途、Embed Modelのサービス契約が必要 モデルの種類 × 少ない ○ 多い 費用 Elasticsearchの費用はかかるが、それ以外は不要。 外部のEmbed Modelの利用料は増えるが、Elasticsearchの費用を抑えられる。 頻繁に密ベクトル生成処理が呼ばれる場合、外部モデルを利用するよりも安くなる可能性あり。 密ベクトル生成処理が一定回数以下の場合、こちらが安くなる可能性あり。 外部のEmbed Modelの利用料金はモデルによって異なるため一概には言えませんが、モデルによっては低価格で利用できるものもあり、割安で導入できるケースもあります。 Elasticsearchで密ベクトル生成に利用可能なサービス Elasticsearchの/_inference/text_embedding/を使って文字列から密ベクトルを生成する際に利用可能なサービス名の一覧を以下に挙げます。 Elasticsearch公式ドキュメント の「Create … inference endpoint」のうち、task_type = text_embeddingが密ベクトル生成用のサービスに該当します。 サービス名の一覧 Alibaba Cloud AI Search Amazon Bedrock Azure AI Studio Azure OpenAI Cohere Elasticsearch Google AI Studio Google Vertex AI Hugging Face JinaAI Mistral OpenAI VoyageAI Watsonx inference integration ※V9用の公式ドキュメントでは1か所にまとめられていないようです。なお、V8用の公式ドキュメントでは、 こちら のようにリストアップされています。 ※/_inference/text_embedding/を使わず、独自にプログラムを作成して密ベクトル生成処理を組み込めば、上記以外のモデルも利用可能です。 今回は、この中から Cohere を使用します。モデルは embed-multilingual-light-v3.0(次元数:384)を使います。 準備 Cohere API Key の取得 Cohereにサインイン後、API Keyを取得してください。 (本ブログではCohereのembed-multilingual-light-v3.0を取り上げていますが、他のモデルでも問題ありません。) Machine Learning インスタンス Elasticsearchに同梱しているEmbed Model(.multilingual-e5-smallなど)を利用する場合はElasticsearchのMachine Learningインスタンスが必要ですが、外部のEmbed Modelを利用して密ベクトルを生成する場合は、 Machine Learningインスタンスは不要 です。 (取得したログに対して異常値検出を行うなど、ベクトル生成以外でMachine Learningの機能を利用する場合は Machine Learning インスタンスが必要となりますが、今回はMachine Learningの機能を使用しないため不要です。) /_inference/text_embedding/用エンドポイントの作成 https://www.elastic.co/docs/api/doc/elasticsearch/v9/operation/operation-inference-put-cohere を参考に、Cohere用のtext_embeddingエンドポイントを作成します。 以下のリクエストをKibanaのDevToolsのConsoleから発行します。 PUT /_inference/text_embedding/my_cohere_emb_light_v3_float { "service": "cohere", "service_settings": { "api_key": "取得した Cohere の Api Key", "model_id": "embed-multilingual-light-v3.0", "embedding_type": "float" } } service_settings内に記載するパラメーターは、サービスごとに異なります。 Cohereの場合は、embedding_typeなどを指定できます。今回は”float”とします。 ここで作成したinference_idは、後ほど参照します。 _inferenceのtext_embeddingエンドポイントの作成に成功すると、次のようなレスポンスが返ってきます。 { "inference_id": "my_cohere_emb_light_v3_float", "task_type": "text_embedding", "service": "cohere", "service_settings": { "similarity": "cosine", "dimensions": 384, "model_id": "embed-multilingual-light-v3.0", "rate_limit": { "requests_per_minute": 10000 }, "embedding_type": "float" }, "chunking_settings": { "strategy": "sentence", "max_chunk_size": 250, "sentence_overlap": 1 } } インデックスの作成 今回登録するドキュメントの内容は、ホワイトペーパーで使用した「柿之助」( 青空文庫 より入手した「桃太郎」を改変したもの)とします。 下記のリクエストを Kibana の DevTools の Console から発行します。 PUT /kakinosuke_cohere_emb3_light_float/ { "settings": { "index": { "number_of_shards": 1, "number_of_replicas": 1, "refresh_interval": "3600s" } } } 今回は、形態素解析などの設定は省略しています。 マッピングの作成 下記のリクエストを Kibana の DevTools の Console から発行します。 PUT /kakinosuke_cohere_emb3_light_float/_mappings { "dynamic": false, "properties": { "chunk_no": { "type": "integer" }, "content": { "type": "text", "fields": { "text_embedding": { "type": "semantic_text", "inference_id": "my_cohere_emb_light_v3_float" } } } } } 今回は、形態素解析などの設定は省略しています。 作成するフィールドは3つです。 フィールド名 フィールドタイプ 説明 chunk_no integer チャンク番号 content text 内容(本文) content.text_embedding semantic_text 密ベクトル(384次元, float) content.text_embeddingにCohereで生成した密ベクトルを格納するよう、inference_idに先ほど作成した”my_cohere_emb_light_v3_float”を指定します。 ドキュメントの登録 実際の検索サービスであれば、登録処理を別途作成する必要がありますが、今回は事前に登録用のリクエストを用意し、それらを発行するだけとします。 登録する内容は、ホワイトペーパーで利用した「柿之助」( 青空文庫 より入手した「桃太郎」を改変したもの)を利用します。 ただし、今回は説明を簡略化するため、オーバーラップなしで手動でチャンキングしています(繰り返しになりますが、あくまでサンプルアプリケーションのため簡略化しています)。 ドキュメントの登録リクエスト: ※Trial Licenseをご利用の場合、登録する時間間隔を空けるなど、利用レートが制限を超えないようにしてください。 POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 1, "content": """むかし、むかし、あるところに、おじいさんとおばあさんがありました。まいにち、おじいさんは山へしば刈りに、おばあさんは川へ洗濯に行きました。" ある日、おばあさんが、川のそばで、せっせと洗濯をしていますと、川上から、大きな柿が一つ、 「ドンブラコッコ、スッコッコ。 ドンブラコッコ、スッコッコ。」 と流れて来ました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 2, "content": """「おやおや、これはみごとな柿だこと。おじいさんへのおみやげに、どれどれ、うちへ持って帰りましょう。」 おばあさんは、そう言いながら、腰をかがめて柿を取ろうとしましたが、遠くって手がとどきません。おばあさんはそこで、 「あっちの水は、かあらいぞ。 こっちの水は、ああまいぞ。 かあらい水は、よけて来い。 ああまい水に、よって来い。 と歌いながら、手をたたきました。すると柿はまた、 「ドンブラコッコ、スッコッコ。 ドンブラコッコ、スッコッコ。」 といいながら、おばあさんの前へ流れて来ました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 3, "content": """おばあさんはにこにこしながら、 「早くおじいさんと二人で分けて食べましょう。」 と言って、柿をひろい上げて、洗濯物といっしょにたらいの中に入れて、えっちら、おっちら、かかえておうちへ帰りました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 4, "content": """夕方になってやっと、おじいさんは山からしばを背負って帰って来ました。 「おばあさん、今帰ったよ。」 「おや、おじいさん、おかいんなさい。待っていましたよ。さあ、早くお上がんなさい。いいものを上げますから。」 「それはありがたいな。何だね、そのいいものというのは。」 こういいながら、おじいさんはわらじをぬいで、上に上がりました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 5, "content": """その間に、おばあさんは戸棚の中からさっきの柿を重そうにかかえて来て、 「ほら、ごらんなさいこの柿を。」 と言いました。 「ほほう、これはこれは。どこからこんなみごとな柿を買って来た。」 「いいえ、買って来たのではありません。今日川で拾って来たのですよ。」 「え、なに、川で拾って来た。それはいよいよめずらしい。」 こうおじいさんは言いながら、柿を両手にのせて、ためつ、すがめつ、ながめていますと、だしぬけに、柿はぽんと中から二つに割れて、 「おぎゃあ、おぎゃあ。」 と勇ましいうぶ声を上げながら、かわいらしい赤さんが元気よくとび出しました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 6, "content": """「おやおや、まあ。」 おじいさんも、おばあさんも、びっくりして、二人いっしょに声を立てました。 「まあまあ、わたしたちが、へいぜい、どうかして子供が一人ほしい、ほしいと言っていたものだから、きっと神さまがこの子をさずけて下さったにちがいない。」 おじいさんも、おばあさんも、うれしがって、こう言いました。 そこであわてておじいさんがお湯をわかすやら、おばあさんがむつきをそろえるやら、大さわぎをして、赤さんを抱き上げて、うぶ湯をつかわせました。するといきなり、 「うん。」 と言いながら、赤さんは抱いているおばあさんの手をはねのけました。 「おやおや、何という元気のいい子だろう。」 おじいさんとおばあさんは、こう言って顔を見合わせながら、「あッは、あッは。」とおもしろそうに笑いました。 そして柿の中から生まれた子だというので、この子に柿之助という名をつけました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 7, "content": """おじいさんとおばあさんは、それはそれはだいじにして柿之助を育てました。柿之助はだんだん成長するにつれて、あたりまえの子供にくらべては、ずっと体も大きいし、力がばかに強くって、すもうをとっても近所の村じゅうで、かなうものは一人もないくらいでしたが、そのくせ気だてはごくやさしくって、おじいさんとおばあさんによく孝行をしました。 柿之助は十五になりました。 もうそのじぶんには、日本の国中で、柿之助ほど強いものはないようになりました。柿之助はどこか外国へ出かけて、腕いっぱい、力だめしをしてみたくなりました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 8, "content": """するとそのころ、ほうぼう外国の島々をめぐって帰って来た人があって、いろいろめずらしい、ふしぎなお話をした末に、 「もう何年も何年も船をこいで行くと、遠い遠い海のはてに、悪霊島という所がある。悪い悪霊どもが、いかめしいくろがねのお城の中に住んで、ほうぼうの国からかすめ取った貴い宝物を守っている。」 と言いました。 柿之助はこの話をきくと、その悪霊島へ行ってみたくって、もう居ても立ってもいられなくなりました。そこでうちへ帰るとさっそく、おじいさんの前へ出て、 「どうぞ、わたくしにしばらくおひまを下さい。」 と言いました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 9, "content": """おじいさんはびっくりして、 「お前どこへ行くのだ。」 と聞きました。 「悪霊島へ悪霊せいばつに行こうと思います。」 と柿之助はこたえました。 「ほう、それはいさましいことだ。じゃあ行っておいで。」 とおじいさんは言いました。 「まあ、そんな遠方へ行くのでは、さぞおなかがおすきだろう。よしよし、おべんとうをこしらえて上げましょう。」 とおばあさんも言いました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 10, "content": """そこで、おじいさんとおばあさんは、お庭のまん中に、えんやら、えんやら、大きな臼を持ち出して、おじいさんがきねを取ると、おばあさんはこねどりをして、 「ぺんたらこっこ、ぺんたらこっこ。ぺんたらこっこ、ぺんたらこっこ。」 と、おべんとうのおむすびをつきはじめました。 おむすびがうまそうにでき上がると、柿之助のしたくもすっかりでき上がりました。 """ } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 11, "content": """柿之助はお侍の着るような陣羽織を着て、刀を腰にさして、おむすびの袋をぶら下げました。そして柿の絵のかいてある軍扇を手に持って、 「ではおとうさん、おかあさん、行ってまいります。」 と言って、ていねいに頭を下げました。 「じゃあ、りっぱに悪霊を退治してくるがいい。」 とおじいさんは言いました。 「気をつけて、けがをしないようにおしよ。」 とおばあさんも言いました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 12, "content": """「なに、大丈夫です、日本一のおむすびを持っているから。」と柿之助は言って、 「では、ごきげんよう。」 と元気な声をのこして、出ていきました。おじいさんとおばあさんは、門の外に立って、いつまでも、いつまでも見送っていました。 """ } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 13, "content": """柿之助はずんずん行きますと、大きな山の上に来ました。すると、草むらの中から、「ワン、ワン。」と声をかけながら、猫が一ぴきかけて来ました。 柿之助がふり返ると、猫はていねいに、おじぎをして、 「柿之助さん、柿之助さん、どちらへおいでになります。」 とたずねました。 「悪霊島へ、悪霊せいばつに行くのだ。」 「お腰に下げたものは、何でございます。」 「日本一のおむすびさ。」 「一つ下さい、お供しましょう。」 「よし、よし、やるから、ついて来い。」 猫はおむすびを一つもらって、柿之助のあとから、ついて行きました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 14, "content": """山を下りてしばらく行くと、こんどは森の中にはいりました。すると木の上から、「キャッ、キャッ。」とさけびながら、ゴリラが一ぴき、かけ下りて来ました。 柿之助がふり返ると、ゴリラはていねいに、おじぎをして、 「柿之助さん、柿之助さん、どちらへおいでになります。」 とたずねました。 「悪霊島へ悪霊せいばつに行くのだ。」 「お腰に下げたものは、何でございます。」 「日本一のおむすびさ。」 「一つ下さい、お供しましょう。」 「よし、よし、やるから、ついて来い。」 ゴリラもおむすびを一つもらって、あとからついて行きました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 15, "content": """山を下りて、森をぬけて、こんどはひろい野原へ出ました。すると空の上で、「ケン、ケン。」と鳴く声がして、鷹が一羽とんで来ました。 柿之助がふり返ると、鷹はていねいに、おじぎをして、 「柿之助さん、柿之助さん、どちらへおいでになります。」 とたずねました。 「悪霊島へ悪霊せいばつに行くのだ。」 「お腰に下げたものは、何でございます。」 「日本一のおむすびさ。」 「一つ下さい、お供しましょう。」 「よし、よし、やるから、ついて来い。」 鷹もおむすびを一つもらって、柿之助のあとからついて行きました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 16, "content": """猫と、ゴリラと、鷹と、これで三にんまで、いい家来ができたので、柿之助はいよいよ勇み立って、またずんずん進んで行きますと、やがてひろい海ばたに出ました。  そこには、ちょうどいいぐあいに、船が一そうつないでありました。  柿之助と、三にんの家来は、さっそく、この船に乗り込みました。 「わたくしは、漕ぎ手になりましょう。」  こう言って、猫は船をこぎ出しました。 「わたくしは、かじ取りになりましょう。」  こう言って、ゴリラがかじに座りました。 「わたくしは物見をつとめましょう。」  こう言って、鷹がへさきに立ちました。 """ } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 17, "content": """うららかないいお天気で、まっ青な海の上には、波一つ立ちませんでした。稲妻が走るようだといおうか、矢を射るようだといおうか、目のまわるような速さで船は走って行きました。ほんの一時間も走ったと思うころ、へさきに立って向こうをながめていた鷹が、「あれ、あれ、島が。」とさけびながら、ぱたぱたと高い羽音をさせて、空にとび上がったと思うと、スウッとまっすぐに風を切って、飛んでいきました。 柿之助もすぐ鷹の立ったあとから向こうを見ますと、なるほど、遠い遠い海のはてに、ぼんやり雲のような薄ぐろいものが見えました。船の進むにしたがって、雲のように見えていたものが、だんだんはっきりと島の形になって、あらわれてきました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 18, "content": """「ああ、見える、見える、悪霊島が見える。」 柿之助がこういうと、猫も、ゴリラも、声をそろえて、「万歳、万歳。」とさけびました。 見る見る悪霊島が近くなって、もう硬い岩で畳んだ悪霊のお城が見えました。いかめしいくろがねの門の前に見はりをしている悪霊の兵隊のすがたも見えました。 そのお城のいちばん高い屋根の上に、鷹がとまって、こちらを見ていました。 こうして何年も、何年もこいで行かなければならないという悪霊島へ、ほんの目をつぶっている間に来たのです。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 19, "content": """柿之助は、猫とゴリラをしたがえて、船からひらりと陸の上にとび上がりました。 見はりをしていた悪霊の兵隊は、その見なれないすがたを見ると、びっくりして、あわてて門の中に逃げ込んで、くろがねの門を固くしめてしまいました。その時猫は門の前に立って、 「日本の柿之助さんが、お前たちをせいばいにおいでになったのだぞ。あけろ、あけろ。」 とどなりながら、ドン、ドン、扉をたたきました。悪霊はその声を聞くと、ふるえ上がって、よけい一生懸命に、中から押さえていました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 20, "content": """すると鷹が屋根の上からとび下りてきて、門を押さえている悪霊どもの目をつつきまわりましたから、悪霊はへいこうして逃げ出しました。その間に、ゴリラがするすると高い岩壁をよじ登っていって、ぞうさなく門を中からあけました。 「わあッ。」とときの声を上げて、柿之助の主従が、いさましくお城の中に攻め込んでいきますと、悪霊の大将も大ぜいの家来を引き連れて、一人一人、太い鉄の棒をふりまわしながら、「おう、おう。」とさけんで、向かってきました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 21, "content": """けれども、体が大きいばっかりで、いくじのない悪霊どもは、さんざん鷹に目をつつかれた上に、こんどは猫に向こうずねをくいつかれたといっては、痛い、痛いと逃げまわり、ゴリラに顔を引っかかれたといっては、おいおい泣き出して、鉄の棒も何もほうり出して、降参してしまいました。  おしまいまでがまんして、たたかっていた悪霊の大将も、とうとう柿之助に組みふせられてしまいました。柿之助は大きな悪霊の背中に、馬乗りにまたがって、 「どうだ、これでも降参しないか。」  といって、ぎゅうぎゅう、ぎゅうぎゅう、押さえつけました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 22, "content": """悪霊の大将は、柿之助の大力で首をしめられて、もう苦しくってたまりませんから、大つぶの涙をぼろぼろこぼしながら、 「降参します、降参します。命だけはお助け下さい。その代わりに宝物をのこらずさし上げます。」 こう言って、ゆるしてもらいました。 悪霊の大将は約束のとおり、お城から、かくれみのに、かくれ笠、うちでの小づちに如意宝珠、そのほかさんごだの、たいまいだの、るりだの、世界でいちばん貴い宝物を山のように車に積んで出しました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 23, "content": """柿之助はたくさんの宝物をのこらず積んで、三にんの家来といっしょに、また船に乗りました。帰りは行きよりもまた一そう船の走るのが速くって、間もなく日本の国に着きました。 船が陸に着きますと、宝物をいっぱい積んだ車を、猫が先に立って引き出しました。鷹が綱を引いて、ゴリラがあとを押しました。 「えんやらさ、えんやらさ。」 三にんは重そうに、かけ声をかけかけ進んでいきました。 """ } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 24, "content": """うちではおじいさんと、おばあさんが、かわるがわる、 「もう柿之助が帰りそうなものだが。」 と言い言い、首をのばして待っていました。そこへ柿之助が三にんのりっぱな家来に、ぶんどりの宝物を引かせて、さもとくいらしい様子をして帰って来ましたので、おじいさんもおばあさんも、目も鼻もなくして喜びました。 「えらいぞ、えらいぞ、それこそ日本一だ。」 とおじいさんは言いました。 「まあ、まあ、けががなくって、何よりさ。」 とおばあさんは言いました。""" } POST /kakinosuke_cohere_emb3_light_float/_doc { "chunk_no": 25, "content": """柿之助は、その時猫とゴリラと鷹の方を向いてこう言いました。 「どうだ。悪霊せいばつはおもしろかったなあ。」 猫はワン、ワンとうれしそうにほえながら、前足で立ちました。 ゴリラはキャッ、キャッと笑いながら、白い歯をむき出しました。 鷹はケン、ケンと鳴きながら、くるくると宙返りをしました。 空は青々と晴れ上がって、お庭には桜の花が咲き乱れていました。""" } 最後に、_refreshを呼び出します。 POST /kakinosuke_cohere_emb3_light_float/_refresh 登録された密ベクトルの確認 下記のリクエストを Kibana の DevTools の Console から発行します。 GET /kakinosuke_cohere_emb3_light_float/_search { "size": 30, "query": { "match_all": {} }, "fields": [ "_inference_fields" ] } 以下のようなレスポンスが返却されます。 { "took": 15, ... "hits": { "hits": [ { "chunk_no": 4, "content": """夕方になってやっと、おじいさんは山からしばを背負って帰って来ました。... """, "_inference_fields": { "content.text_embedding": { "inference_id": "my_cohere_emb_light_v3_float", "model_settings": { "task_type": "text_embedding", "dimensions": 384, "similarity": "cosine", "element_type": "float" }, "chunks": { "content": [ { "start_offset": 0, "end_offset": 168, "embeddings": [ 0.095214844, -0.061676025, ... 0.05307007 ] } ] } } } }, ... ] } } float の密ベクトルが格納されていることがわかります。 ベクトル生成を明示的に行っていないにもかかわらず、ベクトルが計算され格納されているのは、/_inference/text_embeddingsのエンドポイントを作成し、それをインデックスのマッピングに指定したおかげです。 密ベクトル検索 準備ができたので、実際に密ベクトルを使った検索を行ってみましょう。 下記のリクエストを Kibana の DevTools の Console から発行します。 GET /kakinosuke_cohere_emb3_light_float/_search { "query": { "semantic": { "field": "content.text_embedding", "query": "川に流れてきた果物は何?" } } } レスポンス { "took": 103, ... "hits": [ { ... "_source": { "chunk_no": 1, "content": """むかし、むかし、あるところに、... 川上から、大きな柿が一つ、... """ } }, { ... "_source": { "chunk_no": 5, "content": """その間に、おばあさんは戸棚の中から... 「ほほう、これはこれは。どこからこんなみごとな柿を買って来た。」 「いいえ、買って来たのではありません。今日川で拾って来たのですよ。」 「え、なに、川で拾って来た。それはいよいよめずらしい。」 こうおじいさんは言いながら、柿を両手にのせて、... """ } }, { ... "_source": { "chunk_no": 2, "content": """「おやおや、これはみごとな柿だこと。... といいながら、おばあさんの前へ流れて来ました。""" } }, ... ] } } クエリー「川に流れてきた果物は何?」に近いドキュメントが返却されています。 検索時にもベクトル生成を明示していませんが、これも/_inference/text_embedding/のエンドポイントを作成した効果です。 なお、以前のベクトル検索では、 Elasticsearchでのベクトル検索 で紹介したように、次のようなやや複雑なクエリーを記述する必要がありました。 GET /momotaro_v3/_search { "knn": { "field": "text_embedding.predicted_value", "k": "10", "num_candidates": "100", "query_vector_builder": { "text_embedding": { "model_id": ".multilingual-e5-small_linux-x86_64", "model_text": "桃太郎が鬼が島へ向かった際の乗り物は" } } } } しかし、v8.18からは、 GET /インデックス名/_search { "query": { "semantic": { "field": "密ベクトルを格納しているフィールド名", "query": "検索したいテキスト" } } } といった簡素な構文で検索できるようになりました。詳細は こちら をご参照ください。 他の検索クエリも試してみます。 GET /kakinosuke_cohere_emb3_light_float/_search { "query": { "semantic": { "field": "content.text_embedding", "query": "柿之助の家来は誰?" } } } レスポンス: { "took": 173, ... "hits": { ... "hits": [ { ... "_source": { "chunk_no": 23, "content": """柿之助はたくさんの宝物をのこらず積んで、三にんの家来といっしょに... 猫が先に立って引き出しました。鷹が綱を引いて、ゴリラがあとを押しました。...""" } }, { ... "_source": { "chunk_no": 16, "content": """猫と、ゴリラと、鷹と、これで三にんまで、いい家来ができたので、柿之助はいよいよ...""" } }, ... ] } } 検索クエリ「柿之助の家来は誰?」に近いドキュメントが返却されています。 まとめ このように、Elasticsearchの外部のEmbed Modelを利用しても密ベクトル検索を行えることがご理解いただけたかと思います。 要件に合ったEmbed Modelを利用して環境を構築してみてください。 The post Elasticsearchでの外部のEmbed Modelを使った密ベクトル検索 first appeared on Elastic Portal .
アバター