Azure Database for PostgreSQL は、 LangChain などの主要な大規模言語モデル (LLM) オーケストレーション パッケージとシームレスに統合されます。 この統合により、開発者はアプリケーションで高度な AI 機能を使用できます。 LangChain は、生成型 AI アプリケーションの開発が容易になるように、LLM、埋め込みモデル、データベースの管理と使用を合理化できます。
この記事では、Azure Database for PostgreSQL の統合 ベクター データベース を使用して、LangChain を使用してコレクション内のドキュメントを格納および管理する方法について説明します。 また、インデックスを作成し、コサイン距離、L2 距離 (ユークリッド距離)、内部積などの最近隣アルゴリズムを使用してベクトル検索クエリを実行し、クエリ ベクターに近いドキュメントを検索する方法についても説明します。
ベクターのサポート
Azure Database for PostgreSQL を使用すると、PostgreSQL に何百万ものベクター埋め込みを効率的に格納し、クエリを実行できます。 このサービスは、概念実証から運用環境に AI ユース ケースをスケーリングするのに役立ちます。 次の利点があります。
- ベクター埋め込みとリレーショナル データに対してクエリを実行するための使い慣れた SQL インターフェイスを提供します。
- DiskANN インデックス作成アルゴリズムを使用して、1 億を超えるベクター間で高速かつ正確な類似性検索を行うことで、
pgvector
を向上させます。 - リレーショナル メタデータ、ベクター埋め込み、時系列データを 1 つのデータベースに統合することで、操作を簡略化します。
- 堅牢な PostgreSQL エコシステムと Azure クラウド プラットフォームの機能を使用して、レプリケーションや高可用性などのエンタープライズ レベルの機能を実現します。
認証
Azure Database for PostgreSQL では、パスワードベースの 認証と Microsoft Entra (旧称 Azure Active Directory) 認証がサポートされています。
Microsoft Entra 認証を使用すると、Microsoft Entra ID を使用して PostgreSQL サーバーに対する認証を行えます。 Microsoft Entra ID を使用すると、データベース ユーザーの個別のユーザー名とパスワードを管理する必要がなくなります。 これにより、他の Azure サービスに使用するのと同じセキュリティ メカニズムを使用できます。
この記事では、どちらの認証方法も使用できます。
設定
Azure Database for PostgreSQL では、オープンソースの LangChain Postgres サポート を使用して Azure Database for PostgreSQL に接続します。 まず、パートナー パッケージをダウンロードします。
%pip install -qU langchain_postgres
%pip install -qU langchain-openai
%pip install -qU azure-identity
Azure Database for PostgreSQL で pgvector を有効にする
Azure Database for PostgreSQL での pgvector の有効化と使用に関するページを参照してください。
資格情報を設定する
Azure Database for PostgreSQL 接続の詳細 を取得し、環境変数として追加する必要があります。
Microsoft Entra 認証を使用する場合は、 USE_ENTRA_AUTH
フラグを True
に設定します。 Microsoft Entra 認証を使用している場合は、ホスト名とデータベース名のみを指定する必要があります。 パスワード認証を使用している場合は、ユーザー名とパスワードも設定する必要があります。
import getpass
import os
USE_ENTRA_AUTH = True
# Supply the connection details for the database
os.environ["DBHOST"] = "<server-name>"
os.environ["DBNAME"] = "<database-name>"
os.environ["SSLMODE"] = "require"
if not USE_ENTRA_AUTH:
# If you're using a username and password, supply them here
os.environ["DBUSER"] = "<username>"
os.environ["DBPASSWORD"] = getpass.getpass("Database Password:")
Azure OpenAI 埋め込みを設定する
os.environ["AZURE_OPENAI_ENDPOINT"] = "<azure-openai-endpoint>"
os.environ["AZURE_OPENAI_API_KEY"] = getpass.getpass("Azure OpenAI API Key:")
AZURE_OPENAI_ENDPOINT = os.environ["AZURE_OPENAI_ENDPOINT"]
AZURE_OPENAI_API_KEY = os.environ["AZURE_OPENAI_API_KEY"]
from langchain_openai import AzureOpenAIEmbeddings
embeddings = AzureOpenAIEmbeddings(
model="text-embedding-3-small",
api_key=AZURE_OPENAI_API_KEY,
azure_endpoint=AZURE_OPENAI_ENDPOINT,
azure_deployment="text-embedding-3-small",
)
初期化
Microsoft Entra 認証を使用する
次のセクションには、Microsoft Entra 認証を使用するように LangChain を設定する関数が含まれています。 関数get_token_and_username
は、azure.identity
ライブラリからDefaultAzureCredential
を使用して、Azure Database for PostgreSQL サービスのトークンを取得します。 これにより、SQLAlchemy エンジンに、新しい接続を作成するための有効なトークンが与えられます。 また、JSON Web トークン (JWT) であるトークンを解析して、データベースへの接続に使用されるユーザー名を抽出します。
create_postgres_engine
関数は、トークン マネージャーからフェッチされたトークンに基づいてユーザー名とパスワードを動的に設定する SQLAlchemy エンジンを作成します。 このエンジンは、PGVector
LangChain ベクター ストアの connection
パラメーターに渡すことができます。
Azure にサインインする
Azure にサインインするには、 Azure CLI がインストールされていることを確認します。 ターミナルで次のコマンドを実行します。
az login
サインインすると、次のコードによってトークンがフェッチされます。
import base64
import json
from functools import lru_cache
from azure.identity import DefaultAzureCredential
from sqlalchemy import create_engine, event
from sqlalchemy.engine.url import URL
@lru_cache(maxsize=1)
def get_credential():
"""Memoized function to create the Azure credential, which caches tokens."""
return DefaultAzureCredential()
def decode_jwt(token):
"""Decode the JWT payload to extract claims."""
payload = token.split(".")[1]
padding = "=" * (4 - len(payload) % 4)
decoded_payload = base64.urlsafe_b64decode(payload + padding)
return json.loads(decoded_payload)
def get_token_and_username():
"""Fetches a token and returns the username and token."""
# Fetch a new token and extract the username
token = get_credential().get_token(
"https://ossrdbms-aad.database.windows.net/.default"
)
claims = decode_jwt(token.token)
username = claims.get("upn")
if not username:
raise ValueError("Could not extract username from token. Have you logged in?")
return username, token.token
def create_postgres_engine():
db_url = URL.create(
drivername="postgresql+psycopg",
username="", # This will be replaced dynamically
password="", # This will be replaced dynamically
host=os.environ["DBHOST"],
port=os.environ.get("DBPORT", 5432),
database=os.environ["DBNAME"],
)
# Create a SQLAlchemy engine
engine = create_engine(db_url, echo=True)
# Listen for the connection event to inject dynamic credentials
@event.listens_for(engine, "do_connect")
def provide_dynamic_credentials(dialect, conn_rec, cargs, cparams):
# Fetch the dynamic username and token
username, token = get_token_and_username()
# Override the connection parameters
cparams["user"] = username
cparams["password"] = token
return engine
パスワード認証を使用する
Microsoft Entra 認証を使用していない場合、 get_connection_uri
は環境変数からユーザー名とパスワードを取得する接続 URI を提供します。
import urllib.parse
def get_connection_uri():
# Read URI parameters from the environment
dbhost = os.environ["DBHOST"]
dbname = os.environ["DBNAME"]
dbuser = urllib.parse.quote(os.environ["DBUSER"])
password = os.environ["DBPASSWORD"]
sslmode = os.environ["SSLMODE"]
# Construct the connection URI
# Use Psycopg 3!
db_uri = (
f"postgresql+psycopg://{dbuser}:{password}@{dbhost}/{dbname}?sslmode={sslmode}"
)
return db_uri
ベクター ストアを作成する
from langchain_core.documents import Document
from langchain_postgres import PGVector
from langchain_postgres.vectorstores import PGVector
collection_name = "my_docs"
# The connection is either a SQLAlchemy engine or a connection URI
connection = create_postgres_engine() if USE_ENTRA_AUTH else get_connection_uri()
vector_store = PGVector(
embeddings=embeddings,
collection_name=collection_name,
connection=connection,
use_jsonb=True,
)
ベクター ストアの管理
ベクター ストアに項目を追加する
ID でドキュメントを追加すると、その ID に一致する既存のドキュメントが上書きされます。
docs = [
Document(
page_content="there are cats in the pond",
metadata={"id": 1, "location": "pond", "topic": "animals"},
),
Document(
page_content="ducks are also found in the pond",
metadata={"id": 2, "location": "pond", "topic": "animals"},
),
Document(
page_content="fresh apples are available at the market",
metadata={"id": 3, "location": "market", "topic": "food"},
),
Document(
page_content="the market also sells fresh oranges",
metadata={"id": 4, "location": "market", "topic": "food"},
),
Document(
page_content="the new art exhibit is fascinating",
metadata={"id": 5, "location": "museum", "topic": "art"},
),
Document(
page_content="a sculpture exhibit is also at the museum",
metadata={"id": 6, "location": "museum", "topic": "art"},
),
Document(
page_content="a new coffee shop opened on Main Street",
metadata={"id": 7, "location": "Main Street", "topic": "food"},
),
Document(
page_content="the book club meets at the library",
metadata={"id": 8, "location": "library", "topic": "reading"},
),
Document(
page_content="the library hosts a weekly story time for kids",
metadata={"id": 9, "location": "library", "topic": "reading"},
),
Document(
page_content="a cooking class for beginners is offered at the community center",
metadata={"id": 10, "location": "community center", "topic": "classes"},
),
]
vector_store.add_documents(docs, ids=[doc.metadata["id"] for doc in docs])
ベクター ストア内の項目を更新する
docs = [
Document(
page_content="Updated - cooking class for beginners is offered at the community center",
metadata={"id": 10, "location": "community center", "topic": "classes"},
)
]
vector_store.add_documents(docs, ids=[doc.metadata["id"] for doc in docs])
ベクター ストアから項目を削除する
vector_store.delete(ids=["3"])
ベクター ストアへのクエリ
ベクター ストアを作成し、関連するドキュメントを追加したら、チェーンまたはエージェントのベクター ストアに対してクエリを実行できます。
フィルター処理のサポート
ベクター ストアでは、ドキュメントのメタデータ フィールドに対して適用できるフィルターのセットがサポートされています。
オペレーター | 意味/カテゴリ |
---|---|
$eq |
等式 (==) |
$ne |
不等式 (!=) |
$lt |
未満 (<) |
$lte |
以下 (<=) |
$gt |
より大きい (>) |
$gte |
以上または等しい (>=) |
$in |
特殊なケース (in) |
$nin |
特殊なケース (含まれていない) |
$between |
特殊なケース (between) |
$like |
テキスト (類似) |
$ilike |
テキスト (大文字と小文字の区別がない類似) |
$and |
論理 (積) |
$or |
論理 (和) |
直接クエリ
単純な類似性検索は、次のように実行できます。
results = vector_store.similarity_search(
"kitty", k=10, filter={"id": {"$in": [1, 5, 2, 9]}}
)
for doc in results:
print(f"* {doc.page_content} [{doc.metadata}]")
* there are cats in the pond [{'id': 1, 'topic': 'animals', 'location': 'pond'}]
* ducks are also found in the pond [{'id': 2, 'topic': 'animals', 'location': 'pond'}]
* the new art exhibit is fascinating [{'id': 5, 'topic': 'art', 'location': 'museum'}]
* the library hosts a weekly story time for kids [{'id': 9, 'topic': 'reading', 'location': 'library'}]
複数のフィールドを持つディクショナリに演算子を指定しない場合、最上位レベルは論理 AND
フィルターとして解釈されます。
vector_store.similarity_search(
"ducks",
k=10,
filter={"id": {"$in": [1, 5, 2, 9]}, "location": {"$in": ["pond", "market"]}},
)
[Document(id='2', metadata={'id': 2, 'topic': 'animals', 'location': 'pond'}, page_content='ducks are also found in the pond'),
Document(id='1', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'}, page_content='there are cats in the pond')]
vector_store.similarity_search(
"ducks",
k=10,
filter={
"$and": [
{"id": {"$in": [1, 5, 2, 9]}},
{"location": {"$in": ["pond", "market"]}},
]
},
)
[Document(id='2', metadata={'id': 2, 'topic': 'animals', 'location': 'pond'}, page_content='ducks are also found in the pond'),
Document(id='1', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'}, page_content='there are cats in the pond')]
類似性検索を実行し、対応するスコアを受け取る場合は、次のコマンドを実行できます。
results = vector_store.similarity_search_with_score(query="cats", k=1)
for doc, score in results:
print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]")
* [SIM=0.528338] there are cats in the pond [{'id': 1, 'topic': 'animals', 'location': 'pond'}]
PGVector
ベクター ストアで実行できる検索の完全な一覧については、API リファレンスを参照してください。
取得元への変換
ベクトル ストアをレトリバーに変換して、チェーンで簡単に使用することもできます:
retriever = vector_store.as_retriever(search_type="mmr", search_kwargs={"k": 1})
retriever.invoke("kitty")
[Document(id='1', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'}, page_content='there are cats in the pond')]
現在の制限
langchain_postgres
Psycopg 3 (psycopg3
) でのみ機能します。 接続文字列をpostgresql+psycopg2://...
からpostgresql+psycopg://langchain:langchain@...
に更新します。- 埋め込みストアとコレクションのスキーマが、ユーザー指定の ID で正しく動作するように
add_documents
変更されました。 - ここで明示的な接続オブジェクトを渡す必要があります。
- 現時点では、スキーマの変更に関する簡単なデータ移行をサポートする メカニズムはありません 。 ベクター ストアでスキーマを変更する場合は、テーブルを再作成し、ドキュメントをもう一度追加する必要があります。