セマンティック検索をローカルで動かす
Orizm をローカルで動かして開発するときに、セマンティック検索 と 検索辞書 を手元で試す手順です。外部サービスは使わず、docker compose の OpenSearch だけで動きます。
この記事は Orizm 本体をローカルで起動する人(セルフホストや Orizm 自体の開発)向けです。マネージドの Orizm(app.orizm.com)を使っている場合、この手順は不要です。
前提
Orizm 本体の開発環境が、リポジトリの README のセットアップ手順どおりに動いていることが前提です。pnpm install、pnpm run generate、pnpm run build --filter='./packages/*' を済ませます。DB を初期化し、開発者コンソールにログインできる状態から始めます。
clone 直後の状態で手順 3 を実行すると、生成コードやビルド成果物が無く、モジュールが見つからないエラーで止まります。パッケージをビルドしたあとは pnpm install をもう一度実行してください。実行しないと orizm コマンドがリンクされず、手順 5 の push が orizm: command not found になります。
仕組み
既定の ORIZM_EMBEDDING_SEARCH=off では、検索は keyword だけが動き、ベクトルインデックスは作られません。local にすると、埋め込みの推論を OpenSearch クラスタ内のローカルモデル(多言語 MiniLM・384 次元)で行います。docker compose の OpenSearch には必要なプラグインが同梱されているため、追加のコンテナは要りません。
本番で使う Amazon Bedrock にはローカルから到達できないため、本番と同じ設定での確認はステージング以降で行います。
手順
1. 推論の設定を local にする
apps/server/.env に以下を設定します。
ORIZM_EMBEDDING_SEARCH=local同じキーを .env の中に 2
回書かないでください。後に書いた値が使われるため、重複していると意図しない値になります。
2. OpenSearch を起動する
docker compose up3. 埋め込み基盤を用意する
DB のマイグレーションを当てたあと、埋め込みモデルを登録し、取り込みパイプラインを作成します。名前が似ていますが別のコマンドで、順番はこのとおりです。
pnpm run -F ./apps/server dev:migrate # DB のテーブル定義(Prisma)
pnpm run -F ./apps/server dev:migration # Content Plane と埋め込み基盤の同期どちらも再実行できます。初回はモデルのダウンロードに数分かかります。待機の上限は 10 分です。ログに次の行が出れば完了です。
[semanticSearch] embedding infrastructure is ready4. サーバーとワーカーを起動する
pnpm run dev
pnpm dev:sandbox # 動作確認に sandbox を使う場合は別ターミナルでpnpm run dev には手順 3 の dev:migration は含まれません。起動時のバナーの EmbeddingSearch: 行で、現在の推論の設定を確認できます。
5. プロジェクトで有効にする
セマンティック検索は、システム側の設定(手順 1)とプロジェクト側の設定の両方が有効なときだけ動きます。orizm.config.ts でセマンティック検索を有効にし、対象の列を指定して push します(有効化と対象の列)。
import { defineConfig } from "@orizm/cli/config";
export default defineConfig({
semanticSearch: { enabled: true },
tables: {
article: {
columns: {
title: { type: "string", required: true, semanticSearch: true },
body: { type: "string", required: true, semanticSearch: true },
},
enableSearch: true,
},
},
});同梱の sandbox-cms は宣言済みです。orizm CLI は .env を読まないため、開発者キーとプロジェクト名を環境変数で渡して push します(認証・プロジェクトのリンク)。orizm auth login と orizm link で済ませてもかまいません。非対話シェルでは --yes を付けます。
ORIZM_API_KEY=<開発者キー> ORIZM_PROJECT=demo pnpm run -F ./apps/sandbox-cms orizm:push --yes初期化スクリプト(pnpm -F ./apps/server run initialize)が作る demo プロジェクトの開発者キーは x_dev_superxdev です。環境変数なしで実行すると次のエラーで止まります。
Not authenticated. Run `orizm auth login`, or set ORIZM_API_KEY.push すると semanticSearch: true の列を持つテーブルにベクトルインデックスが作られ、既存コンテンツの埋め込みがワーカーで非同期に作られます。
6. コンテンツを入れる
初期化直後の demo プロジェクトにはコンテンツが無いため、検索で見比べる前に記事を入れます。数件で足りるなら、sandbox の http://localhost:8000/admin/articles/new から作成します。作成のたびに埋め込みが同期されるため、作成直後から semantic で見つかります。
意味検索と全文検索の差を見比べるには、評価用のサンプル記事 76 件を article2 テーブルに投入します。スクリプトは Management API 経由で動きます。Management API キー を開発者コンソールか orizm key management create で作り、環境変数で渡します。
EVAL_PROJECT=demo EVAL_MANAGEMENT_KEY=<Management API キー> pnpm run -F ./apps/server eval:search:modes -- seed
EVAL_PROJECT=demo EVAL_MANAGEMENT_KEY=<Management API キー> pnpm run -F ./apps/server eval:search:modes -- waitseed は同じタイトルの既存コンテンツを削除してから投入し、wait は全件が検索可能になるまで待ちます。投入分は cleanup で削除できます。接続先は EVAL_BASE_URL で指定し、既定は http://localhost:4000 です。localhost 以外に向けるときは EVAL_ALLOW_REMOTE_WRITE=1 を付けないと seed と cleanup は動きません。
7. 動かして確かめる
- sandbox の検索画面で mode を切り替えて結果を見比べます。CMS SDK 経由は http://localhost:8000/admin/articles/search 、Consumer SDK 経由は http://localhost:8001/articles/search です。sandbox のログイン(
1@example.com/12341234)はプロジェクトコンソールが表示するため、手順 4 のpnpm run devも起動しておきます - 開発者コンソールのテーブル画面で検索インデックス
published_vectorのドキュメント数が増えていれば、埋め込みは作られています。enableDraftsのテーブルではdraft_vectorも見ます semantic/hybridの結果の各行に_semanticが付いていれば、意味検索が動いています
注意点
手順 3 を飛ばすと動かない
埋め込み基盤が無い状態でサーバーを起動すると、書き込み時のベクトル同期は失敗します。semantic / hybrid の検索は search index is not ready の 403 になります。起動時に次の error ログが出ます。
[semanticSearch] ORIZM_EMBEDDING_SEARCH is set but no deployed embedding model was foundこれが出たら手順 3 を実行してから起動し直してください。推論の設定を変えたときも同様です。
有効化の直後は 403 になる
プロジェクトを有効化した直後や、テーブルに初めて semanticSearch: true を付けた直後は、既存コンテンツ全件の埋め込みを作ります。完了するまで semantic / hybrid は 403 になります。semanticSearch: true の列が 1 つも無いテーブルにはベクトルインデックスが作られません。そのテーブルの semantic / hybrid は search index not found (no column has semanticSearch: true) の 403 のままです。
本番とスコアは一致しない
ローカルと本番では埋め込みモデルが違います。スコアの絶対値や順位はローカルと本番で一致しません。検索の品質や検索辞書の最終確認は、本番と同じ設定のステージング環境で行ってください。
埋め込みが応答しないと keyword と同じ結果になる
ベクトルインデックスができたあとで埋め込みの推論が失敗すると、検索は失敗せずに keyword と同じ結果を 200 で返します。このとき各行の _semantic と _dictionary は付きません。「セマンティック検索が動いていない」と感じたら、まず _semantic の有無と OpenSearch のログを確認してください。
OpenSearch のメモリ
モデルの推論も OpenSearch のコンテナ内で行うため、メモリが足りないと OpenSearch が落ちたり推論がタイムアウトしたりします。compose.yaml の既定は JVM ヒープ 512MB です。不安定な場合は Docker のメモリ割り当てと合わせて増やしてください。
off に戻すのは非破壊
ORIZM_EMBEDDING_SEARCH=off に戻しても、既存のベクトルインデックスは残り、埋め込みの生成だけが止まります。local に戻して起動し直すと、古くなったインデックスは自動的に作り直されます。
古いモデルは自動では消えない
モデルや次元の設定を変えると新しいモデルが登録され、古いモデルは OpenSearch に残ります。ml-commons の API(POST /_plugins/_ml/models/_search)で登録済みのモデルを確認し、不要なものは undeploy してから削除してください。
索引の状態を比べ直す
定義を変えないまま、サーバー側で定義と索引の状態を比べ直したいときは、orizm schema push に --force を付けます。差分が無くても定義が送られ、あるべき状態と違う索引だけが作り直されます。