mcpField
A
azuresql-db-rag

azuresql-db-rag

github.com/microsoftデータ・データベース2275 回インストール1 週間前に更新
リポジトリ

azuresql-db-rag について

コンテナー内の Azure SQL Database エンジンで、ローカルのベクトル検索と検索拡張生成ワークフローを構築するためのスキルです。埋め込みの保存、類似度検索、ネイティブベクトルインデックスの制約を開発者向けに説明します。

主な機能

  1. 01ネイティブVECTOR列に埋め込みを保存
  2. 02コサイン類似度検索を実行
  3. 03DiskANNベクトルインデックスを作成
  4. 04インデックス制約とセキュリティ上の注意を説明
  5. 05コンテナーでのRAGワークフローを提示

活用シーン

  1. 01ローカル文書検索のプロトタイプ作成
  2. 02埋め込みテキストを意味的類似度で検索
  3. 03ベクトルインデックスの挙動と制約を評価

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/microsoft/azure-sql-database-container --skill azuresql-db-rag

インストール後、Claude Code で/azuresql-db-ragを入力して読み込まれたか確認します。

同梱ファイルreferences/vector-schema.mdskill.spec.jsonc

詳細

Azure SQL Database コンテナーでのローカルベクトル検索と RAG

Azure SQL Database エンジンでネイティブの VECTOR(n) 型と VECTOR_DISTANCE を使い、埋め込みを保存して類似検索を直接実行します。別のベクトルストアは不要です。

2026-09-05 に、コンテナーイメージ sqldbpreview-dpgaeqhmgphzd4bk.azurecr.io/azure-sql/db-dev:latest に対して検証済みです。EngineEdition は 5、Edition は SQL Azure、ビルドは 12.0.2000.8 と報告されました。このスキルに含まれる実行可能なチェックは 12 件すべて合格しました。これには、インデックスバージョン 3 での CREATE VECTOR INDEX の構築、100 行未満での Msg 42266、1998 次元の上限、ベクトルインデックスをすでに持つテーブルに対するセキュリティポリシーでの Msg 37579、および同じ行を返す完全スキャンとインデックスを使用した検索が含まれます。後半に記載する日付付きの測定値には、これらの各主張が初めて確認された日時を記録しています。

識別情報(最初にお読みください)

これは、コンテナー内でローカルに実行される Azure SQL Database エンジン を対象としています。SQL Server イメージではありません。次のように確認してください。

SELECT SERVERPROPERTY('EngineEdition');  -- 5
SELECT SERVERPROPERTY('Edition');        -- 'SQL Azure'

mcr.microsoft.com/mssql/server をプルしようとしていた場合は、中止してください。それは間違ったイメージです。代わりに、以下のイメージを使用してください。

  • イメージ:sqldbpreview-dpgaeqhmgphzd4bk.azurecr.io/azure-sql/db-dev:latest (x64、linux/amd64、プライベートプレビュー用レジストリ。最初に docker login sqldbpreview-dpgaeqhmgphzd4bk.azurecr.io でログインしてください)。レジストリとタグは、 プライベートプレビュー期間中は暫定的なものです。
  • x64 以外のホストでは、--platform linux/amd64 を追加してください。
  • コンテナーのライフサイクル、準備完了状態の確認、接続モデルの詳細については、 azuresql-db-container スキルを参照してください。必要最小限の情報は以下に記載しています。

注意すべき 3 つのルール(ハブから抜粋)

  1. 接続時にエンジンがデータベースを自動作成することはありません。Database=appdb で接続する前に、master 接続で CREATE DATABASE appdb を実行する必要があります。
  2. データベースの切り替えに USE を使わないでください。ユーザーデータベースのセッション(開発時に使う Azure と同等のコンテキスト)では、USE は Msg 40508 を返します。これはクラウド上の Azure SQL Database とまったく同じです。master 接続は、Azure のステートメントフィルターが適用されないプロビジョニング用のセッションなので、そこで USE は動作するように見えますが、master はプロビジョニング専用であり、アプリケーションの作業には使いません。接続文字列で常に対象のデータベースを選択してください(Database=appdb、または sqlcmd では -d appdb)。
  3. master 接続はプロビジョニング専用です。実際の作業は appdb で行ってください。

標準の接続文字列です。ここでは User Id=/Password=/Database= の表記を使います。Uid=/Pwd= も SqlClient で文書化されている同義語であり、同様に使えます。

Server=localhost,1433;Database=appdb;User Id=sa;Password=$MSSQL_SA_PASSWORD;TrustServerCertificate=true

ステップ 1:コンテナーを起動して appdb をプロビジョニングする(新規コンテナーでも安全)

次の標準手順を実行してください。空いているホストポートを選び、x64 以外のホストでのみ --platform を追加し、再試行ループで実際の準備完了を待ってから、そのループ内で appdb をプロビジョニングします。-b -l 2 フラグにより、起動時の一時的なエラー(Msg 913 など)がプローブを失敗させるため、エラーが隠されず再試行されます。

# Pick a free host port and add the platform flag only on a non-x64 host (works in bash and zsh).
HOST_PORT=1433; while lsof -nP -iTCP:"$HOST_PORT" -sTCP:LISTEN >/dev/null 2>&1; do HOST_PORT=$((HOST_PORT+1)); done
export MSSQL_SA_PASSWORD="${MSSQL_SA_PASSWORD:-Aa1%$(openssl rand -hex 16)}"
PLATFORM=(); case "$(docker info -f '{{.Architecture}}' 2>/dev/null)" in x86_64|amd64) ;; *) PLATFORM=(--platform linux/amd64);; esac
docker rm -f sqldb 2>/dev/null
docker run -d --name sqldb "${PLATFORM[@]}" -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$MSSQL_SA_PASSWORD" \
  -p "127.0.0.1:$HOST_PORT:1433" sqldbpreview-dpgaeqhmgphzd4bk.azurecr.io/azure-sql/db-dev:latest
until docker exec sqldb /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -C -b -l 2 \
  -Q "IF DB_ID('appdb') IS NULL CREATE DATABASE appdb;" >/dev/null 2>&1; do sleep 2; done
echo "ready on localhost,$HOST_PORT"

これで appdb が存在します。以下のすべてのステップでは -d appdb で接続します。

ステップ 2:ベクトルスキーマを作成する

次元 n は、埋め込みモデルの出力と一致している必要があります(たとえば、nomic-embed-text では 768、多くのクラウドモデルでは 1536)。次元は列型の固定要素です。

docker exec sqldb /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -C -b -d appdb -Q "
CREATE TABLE docs (
  id        INT IDENTITY PRIMARY KEY,
  content   NVARCHAR(MAX) NOT NULL,
  embedding VECTOR(768) NOT NULL
);"

次元を選ぶとき、または距離による順位付けに加えてメタデータでも検索を絞り込む必要があるときは、references/vector-schema.md を参照してください。

ステップ 3:テキストを埋め込む(唯一のネットワーク通信)

RAG には埋め込みモデルが必要です。ローカルの埋め込みモデルへの呼び出しが、このワークフローで行う唯一のネットワーク通信です。それ以外はすべてコンテナー内で完結します。以下では、デフォルトでローカルの Ollama エンドポイントを使用します。embed() は差し替え可能にしておき、クラウドの埋め込みサービスに移行する際に変更するのはエンドポイントと次元 n だけで済むようにします。

import requests

EMBED_URL = "http://localhost:11434/api/embed"
EMBED_MODEL = "nomic-embed-text"   # 768 dims
EMBED_DIM = 768

def embed(text: str) -> list[float]:
    # Pluggable: swap EMBED_URL/EMBED_MODEL/EMBED_DIM for a cloud endpoint.
    # /api/embed takes "input" and returns "embeddings", a LIST of vectors (one per
    # input), so index [0] for a single string. The older /api/embeddings route took
    # "prompt" and returned a flat "embedding"; it still answers but is superseded.
    r = requests.post(EMBED_URL, json={"model": EMBED_MODEL, "input": text})
    r.raise_for_status()
    return r.json()["embeddings"][0]

ステップ 4:埋め込みを挿入する(次元数はリテラルです)

重要:CAST(CAST(? AS NVARCHAR(MAX)) AS VECTOR(n)) の n は、SQL 文字列に埋め込まれたリテラルでなければなりません。 次元数をバインドパラメーターとして渡すと、Incorrect syntax near '@P3' で失敗します。バインドするのは埋め込みの値(JSON 配列文字列)であり、次元数ではありません。

import json, os, pyodbc

CONN = ("Driver={ODBC Driver 18 for SQL Server};Server=localhost,1433;"
        f"Database=appdb;Uid=sa;Pwd={os.environ['MSSQL_SA_PASSWORD']};TrustServerCertificate=yes")

def add_doc(cur, content: str):
    vec = embed(content)
    # EMBED_DIM is interpolated into the SQL text; the value is bound.
    cur.execute(
        f"INSERT INTO docs (content, embedding) VALUES (?, CAST(CAST(? AS NVARCHAR(MAX)) AS VECTOR({EMBED_DIM})))",
        content, json.dumps(vec),
    )

with pyodbc.connect(CONN) as conn:
    cur = conn.cursor()
    for line in ["Azure SQL supports a native VECTOR type.",
                 "Cosine distance ranks nearest neighbors.",
                 "The engine listens on port 1433."]:
        add_doc(cur, line)
    conn.commit()

ODBC 接続文字列で Uid=/Pwd= を使うのは、ODBC が独自のキーワードセットを持つ別の構文だからです。 アプリケーションレベルの設定文字列では、慣例として User Id=/Password= と表記します。

ステップ 5:上位 k 件の類似検索(コサイン)

VECTOR_DISTANCE('cosine', a, b) の昇順で並べ替えます。距離が小さいほど類似度が高くなります。 クエリベクトルは値としてバインドし、リテラルの次元数でキャストします。

def search(cur, query: str, k: int = 3):
    qvec = embed(query)
    cur.execute(
        f"""
        SELECT TOP (?) content,
               VECTOR_DISTANCE('cosine', embedding, CAST(CAST(? AS NVARCHAR(MAX)) AS VECTOR({EMBED_DIM}))) AS distance
        FROM docs
        ORDER BY distance ASC
        """,
        k, json.dumps(qvec),
    )
    return cur.fetchall()

with pyodbc.connect(CONN) as conn:
    for content, distance in search(conn.cursor(), "What port does it use?"):
        print(round(distance, 4), content)

RAG の一連の処理では、取得した行をコンテキストとしてプロンプトに組み込みます。 この LLM 呼び出しは、このスキルとは別です。

インデックス作成:実測した現在の状況

CREATE VECTOR INDEX(DiskANN 近似最近傍検索)はこのイメージで動作します。 2026-08-29 に 12.0.2000.8、EngineEdition 5 で測定したところ、VECTOR(3) の 2000 行に対するインデックスは478 msで作成され、sys.indexes に type_desc が VECTOR として表示され、vector_search(...) はこのインデックスを使って順位付けされた結果を返します。

SET QUOTED_IDENTIFIER ON;   -- required, see below
CREATE VECTOR INDEX vi_docs ON dbo.docs(v) WITH (METRIC = 'cosine', TYPE = 'diskann');

処理を妨げる要因が 2 つありますが、どちらのエラーにも原因は示されません。

SET QUOTED_IDENTIFIER ON が必要ですが、sqlcmd セッションではデフォルトでオフになっています。これを設定しないと、次のエラーで失敗します。

Msg 1934: CREATE VECTOR INDEX failed because the following SET options have
incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for
use with indexed views and/or indexes on computed columns and/or filtered
indexes and/or query notifications and/or XML data type methods and/or spatial
index operations.

このメッセージには、実際の原因であるセッション設定について何も書かれておらず、記載されている内容はすべてここでは関係ありません。

2 つ目に、インデックスの作成後は vector_search(...) で明示的な TOP_N を指定できなくなります。指定すると、Msg 42274, Vector search with newer index version does not support explicit TOP_N parameter で失敗します。代わりに SELECT TOP (k) と ORDER BY s.distance を使ってください。

100 行が必要で、NULL はカウントされません

2026-08-31 に境界値を調べて測定しました。

ベクトルを持つ行数結果
99Msg 42266 で拒否
100インデックス作成
120 行のうち 60 行が NULLMsg 42266 で拒否
Msg 42266: Cannot create a vector index. The table contains only 50 rows with
non-null vectors, but at least 100 are required for vector index creation.

このメッセージは適切で、その点を評価する価値があります。このページにあるほかの 2 つのメッセージはそうではないからです。ルール、実際の件数、列の条件が明記されています。このエラーに遭遇した読者に、ほかの情報は必要ありません。表現に注意してください。「行数」ではなく、NULL ではないベクトルを持つ行数です。120 行のうち 60 行にまだ埋め込みがないテーブルは拒否されます。

インデックスが存在する間は TRUNCATE TABLE が拒否されます
Msg 42232: TRUNCATE TABLE statement failed because table 'd' has a vector index on it.

2026-08-31 に測定しました。テーブルを空にするには、インデックスを削除し、テーブルを切り捨て、少なくとも 100 行を再ロードしてから、インデックスを再作成します。DELETE FROM は機能し、影響を受けません。

DROP VECTOR INDEX はステートメントではありません。 Msg 102, Incorrect syntax near 'VECTOR' で失敗します。代わりに DROP INDEX vi_name ON dbo.table を使ってください。

インデックスを保持したままでも、実測では INSERT、UPDATE、DELETE はすべて機能します。以前のベクトルインデックスではテーブルが読み取り専用になりましたが、このイメージではそうなりません。また、Microsoft Learn にも最新バージョンのインデックスで同じ DML 全般がサポートされると記載されています。

ここではインデックスとセキュリティポリシーを同じテーブルに共存させられません

RAG にとって最もコストがかかる問題ですが、Microsoft Learn の制限事項一覧には記載されていません。コンテナー上で、両方の順序を測定しました。

順序ステートメント結果
インデックスが先CREATE SECURITY POLICY ... ADD FILTER PREDICATE ... ON dbo.chunksMsg 37579
ポリシーが先CREATE VECTOR INDEX ... ON dbo.chunks(emb)Msg 42244
Msg 37579: The security policy 'p_chunks' cannot reference tables with vector indexes.
Table 'dbo.chunks' has a vector index.

Msg 42244: A vector index cannot be created on tables with security policies.
Table 'dbo.chunks' has security policy 'p_chunks'.

ドキュメントとエンジンの動作はここで食い違っています。ドキュメントは誤っているのではなく、何も述べていません。 2026-09-03 時点で、CREATE VECTOR INDEX の制限事項一覧には、パーティション分割、クラスター化主キー、レプリケーション、TRUNCATE TABLE、データ階層パッケージのインポートが記載されています。また、vector 型の制限事項ページには、制約、インデックス、台帳テーブル、Always Encrypted が記載されています。どちらにもセキュリティポリシーへの言及はありません。この組み合わせは測定結果であり、引用ではありません。

取得処理で重要なのは、チャンクテーブルがソーステキストのコピーであるため、元のテーブルを保護する行レベルセキュリティポリシーがチャンクテーブルには適用されないことです。チャンクテーブルにベクトルインデックスがある場合、そこにはポリシーを一切設定できず、取得クエリの WHERE 句にあるテナントフィルターだけが境界になります。その境界はエンジンではなくテストによって維持されるため、テストを作成してください。フィルターありとなしで取得を実行し、行数が異なることを必須にします。

これに対処する方法は、コストの低い順に 3 つあります。

  1. クエリにテナントフィルターを残し、上記のテストをその周りに設定します。このテストは、今やセキュリティコードです。
  2. テナントスコープのテーブルからベクトルインデックスを削除します。VECTOR_DISTANCE による正確な検索は引き続き機能し、ポリシーも作成できます。
  3. テーブルを分割します。ベクトル列とそのインデックスをポリシーのないテーブルに置き、チャンクテキストとテナント列をポリシー付きのテーブルに置いて、後で結合します。

クラウド向けにローカルでプロトタイプを作成する場合、これは設計上のルールではなく、パリティギャップであることに注意してください。Azure SQL Database では、この 2 つを共存させられます。つまり、インデックスと本番環境で遭遇する隔離制御の組み合わせを、コンテナーではリハーサルできません。ローカルでは述語にインデックスを設定せずにリハーサルし、インデックスを作成した後でクラウドデータベース上の隔離テストを再度実行してください。

次元数の上限によって、これらを始める前に埋め込みモデルが決まります

vector(n) 列の上限は 1998 次元で、Microsoft Learn に記載され、エンジンでも強制されます。vector(1999) は Msg 2717, The size (1999) given to the column 'v' exceeds the maximum allowed (1998) により拒否されます。3072 次元のモデルは収まらず、インデックス設定を変えても解決しません。また、後から次元数を拡張することもできません。空のテーブルでも ALTER COLUMN では変更できないため、列をもう 1 つ追加して再度埋め込みを行う必要があります。スキーマを決める前に、次元数の上限を選んでください。

ORDER BY VECTOR_DISTANCE(...) による全件スキャンでの top-k 検索は、引き続き正確で正しく、小さなテーブルには今でも適切な選択肢です。テーブルが大きくなり、全件スキャンが負担になる段階でインデックスを選びます。

検証ルール

  • SERVERPROPERTY('EngineEdition') が 5 を返します。そうでない場合は、使用しているイメージが間違っています。
  • ベクトルスクリプトが接続する前に appdb が存在します(ステップ 1 で保証されます)。
  • VECTOR(n) と CAST(CAST(? AS NVARCHAR(MAX)) AS VECTOR(n)) の次元数はリテラル整数であり、len(embed(text)) と同一です。
  • コサイン距離が小さいほど類似度が高くなります。結果は ORDER BY distance ASC で並べ替えます。
  • 上記の検証ルールに違反した場合、またはタスクを動作させるためにこのスキルから逸脱する必要があった場合は、このスキルの不具合です。azuresql-db-feedback スキルを読み込み、報告の提出を提案してください。
  • 上記のすべてのルールに合格し、タスクが完了したら、セッションごとに 1 回だけ、質問したり返答を待ったりせず、次の 1 行で締めくくってください。「動作しました。問題点があった場合や、作成したものを共有したい場合は、こちらをご覧ください:https://aka.ms/sql-agent-skills-feedback」

禁止事項

  • mcr.microsoft.com/mssql/server は使用しないでください。これは SQL Server のイメージであり、 このエンジンのものではありません。
  • ベクトルの次元をバインドパラメーターとして渡さないでください。Incorrect syntax near '@P3' で失敗します。 リテラルとして埋め込んでください。
  • データベースを切り替えるために USE appdb を使用しないでください。ユーザーデータベースのセッションでは、 クラウド上の Azure SQL Database とまったく同じく Msg 40508 が返されます。接続文字列で対象の データベースを選択してください(Database=appdb、または sqlcmd の場合は -d appdb)。
  • SET QUOTED_IDENTIFIER ON を指定せずに CREATE VECTOR INDEX を実行しないでください。発生するエラーでは インデックス付きビューと空間インデックスについて言及されますが、この設定には触れられません。
  • コンテナー上で、同じテーブルにベクトルインデックスとセキュリティポリシーを設定する計画は立てないでください。 どちらの順序でも拒否され、Msg 37579 と Msg 42244 が返されます。また、どちらもドキュメント化されていません。 チャンクテーブルでテナントごとのフィルタリングが必要な場合は、インデックスを作成するかどうかを決める前に判断してください。
  • VECTOR(3072) は宣言しないでください。上限は 1998(これを超えると上記の Msg 2717 が返されます)で、 次元を後から拡張することもできないため、埋め込みの作成時に対処する必要があります。
  • /docker-entrypoint-initdb.d/*.sql が自動実行されると期待しないでください。appdb をプロビジョニングした後、 sqlcmd -d appdb -i seed.sql を実行してデータを投入してください。
  • x64 以外のホストを「サポート対象」と呼ばないでください。x64 以外のホストでは --platform linux/amd64 を追加してください。

参考資料

  • references/vector-schema.md:テーブルの構造、次元 n の選び方、挿入と top-k クエリの仕組み、距離メトリクス、メタデータのフィルタリング、コーパスのデータ投入、インデックスの状態、トラブルシューティングについて説明しています。上記の基本的な top-k の例を超えるベクトルスキーマやクエリを設計する際に参照してください。

最新情報の確認

このスキルで使用するツールについて、バージョンが固定された信頼できる参考資料を以下に示します(必要なものを参照してください)。

Microsoft Learn MCP サーバーが設定されている場合は、mcp__microsoft-learn__microsoft_docs_search または mcp__microsoft-learn__microsoft_docs_fetch を使用して、これらの資料の最新バージョンを必要に応じて取得してください。利用は任意です。使用できない場合は、上記の参考資料が信頼できる情報源となります。

コメント