Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Advertência
A funcionalidade Postgres Vector Store está em fase de pré-visualização, e melhorias que requerem alterações significativas podem ainda ocorrer em circunstâncias restritas antes do lançamento.
Advertência
A funcionalidade de armazenamento vetorial do Semantic Kernel está em pré-visualização, e melhorias que exigem alterações significativas ainda podem ocorrer em circunstâncias limitadas antes do lançamento.
Advertência
A funcionalidade de armazenamento vetorial do Semantic Kernel está em pré-visualização, e melhorias que exigem alterações significativas ainda podem ocorrer em circunstâncias limitadas antes do lançamento.
Visão geral
O conector Postgres Vetor Store pode ser usado para acessar e gerenciar dados no Postgres e também suporta Neon Serverless Postgres.
O conector tem as seguintes características.
| Área de Destaque | Apoio / Assistência |
|---|---|
| Mapeamento da coleção para | Tabela Postgres |
| Tipos de propriedade de chave suportados |
|
| Tipos de propriedade de dados suportados |
|
| Tipos de propriedade vetorial suportados |
|
| Tipos de índice suportados | Hnsw |
| Funções de distância suportadas |
|
| Cláusulas de filtro suportadas |
|
| Suporta vários vetores em um registro | Sim |
| IsIndexed é suportado? | Não |
| "É suportado IsFullTextIndexed?" | Não |
| Está StorageName suportado? | Sim |
| O HybridSearch é suportado? | Não |
Limitações
Importante
Ao inicializar NpgsqlDataSource manualmente, é necessário chamar UseVector no NpgsqlDataSourceBuilder. Isso permite o suporte vetorial. Sem isso, o uso da implementação do VectorStore falhará.
Aqui está um exemplo de como chamar UseVector.
NpgsqlDataSourceBuilder dataSourceBuilder = new("Host=localhost;Port=5432;Username=postgres;Password=example;Database=postgres;");
dataSourceBuilder.UseVector();
NpgsqlDataSource dataSource = dataSourceBuilder.Build();
Ao usar o método de registo de injeção de dependências através de uma cadeia de conexão, a fonte de dados será construída por este método e AddPostgresVectorStore será aplicada automaticamente.
Primeiros passos
Adicione o pacote NuGet do conector Postgres Vector Store ao seu projeto.
dotnet add package Microsoft.SemanticKernel.Connectors.PgVector --prerelease
Você pode adicionar o armazenamento de vetores ao contêiner de injeção de dependência IServiceCollection usando métodos de extensão fornecidos pelo Semantic Kernel.
using Microsoft.Extensions.DependencyInjection;
var kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.Services.AddPostgresVectorStore("<Connection String>");
Onde <Connection String> é uma cadeia de conexão para a instância Postgres, no formato que o Npgsql espera, por exemplo Host=localhost;Port=5432;Database=postgres;Username=postgres;Password=postgres.
Métodos de extensão que não usam parâmetros também são fornecidos. Eles exigem que uma instância de NpgsqlDataSource seja registrada separadamente com o contêiner de injeção de dependência. Observe que UseVector deve ser chamado no construtor para habilitar o suporte vetorial via pgvector-dotnet:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
// Using IServiceCollection with ASP.NET Core.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPostgresVectorStore("<Connection String>");
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Npgsql;
// Using IServiceCollection with ASP.NET Core.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<NpgsqlDataSource>(sp =>
{
NpgsqlDataSourceBuilder dataSourceBuilder = new("<Connection String>");
dataSourceBuilder.UseVector();
return dataSourceBuilder.Build();
});
builder.Services.AddPostgresVectorStore();
Você pode construir uma instância do Postgres Vetor Store diretamente com uma fonte de dados personalizada ou com uma cadeia de conexão.
using Microsoft.SemanticKernel.Connectors.PgVector;
using Npgsql;
NpgsqlDataSourceBuilder dataSourceBuilder = new("<Connection String>");
dataSourceBuilder.UseVector();
NpgsqlDataSource dataSource = dataSourceBuilder.Build();
var vectorStore = new PostgresVectorStore(dataSource, ownsDataSource: true);
using Microsoft.SemanticKernel.Connectors.PgVector;
var connection = new PostgresVectorStore("<Connection String>");
É possível construir uma referência direta a uma coleção nomeada com uma fonte de dados personalizada ou com uma cadeia de conexão.
using Microsoft.SemanticKernel.Connectors.PgVector;
using Npgsql;
NpgsqlDataSourceBuilder dataSourceBuilder = new("<Connection String>");
dataSourceBuilder.UseVector();
var dataSource = dataSourceBuilder.Build();
var collection = new PostgresCollection<string, Hotel>(dataSource, "skhotels", ownsDataSource: true);
using Microsoft.SemanticKernel.Connectors.PgVector;
var collection = new PostgresCollection<string, Hotel>("<Connection String>", "skhotels");
Mapeamento de dados
O conector Postgres fornece um mapeador padrão ao mapear dados do modelo de dados para o armazenamento. O mapeador padrão usa as anotações de modelo ou a definição de registro para determinar o tipo de cada propriedade e mapear o modelo em um dicionário que pode ser serializado para o Postgres.
- A propriedade do modelo de dados anotada como uma chave será mapeada para a CHAVE PRIMÁRIA na tabela Postgres.
- As propriedades do modelo de dados anotadas como dados serão mapeadas para uma coluna de tabela no Postgres.
- As propriedades do modelo de dados anotadas como vetores serão mapeadas para uma coluna de tabela que tenha o tipo pgvector
VECTORno Postgres.
Substituição do nome da propriedade
Você pode fornecer nomes de campo de substituição para uso no armazenamento que sejam diferentes dos nomes de propriedade no modelo de dados. Isso permite que você faça a correspondência entre os nomes das colunas da tabela, mesmo que eles não correspondam aos nomes de propriedade no modelo de dados.
A substituição do nome da propriedade é efetuada ao definir a opção StorageName através dos atributos do modelo de dados ou da definição de registo.
Aqui está um exemplo de um modelo de dados com StorageName definido em seus atributos e como ele será representado no Postgres como uma tabela, supondo que o nome da coleção seja Hotels.
using System;
using Microsoft.Extensions.VectorData;
public class Hotel
{
[VectorStoreKey(StorageName = "hotel_id")]
public int HotelId { get; set; }
[VectorStoreData(StorageName = "hotel_name")]
public string HotelName { get; set; }
[VectorStoreData(StorageName = "hotel_description")]
public string Description { get; set; }
[VectorStoreVector(Dimensions: 4, DistanceFunction = DistanceFunction.CosineDistance, IndexKind = IndexKind.Hnsw, StorageName = "hotel_description_embedding")]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
CREATE TABLE IF NOT EXISTS public."Hotels" (
"hotel_id" INTEGER PRIMARY KEY NOT NULL,
hotel_name TEXT,
hotel_description TEXT,
hotel_description_embedding VECTOR(4)
);
Indexação vetorial
O hotel_description_embedding no modelo acima Hotel é uma propriedade vetorial com IndexKind.HNSW indexação. Este índice será criado automaticamente quando a coleção for criada.
HNSW é o único tipo de índice suportado para a criação de índice. A construção do índice IVFFlat requer que os dados já existam na tabela no momento da criação do índice e, portanto, não é apropriado para a criação de uma tabela vazia.
Você é livre para criar e modificar índices em tabelas fora do conector, que serão usadas pelo conector ao executar consultas.
Usando a autenticação Entra
O Banco de Dados do Azure para PostgreSQL fornece a capacidade de se conectar ao seu banco de dados usando a autenticação do Entra. Isso elimina a necessidade de armazenar um nome de usuário e senha na cadeia de conexão. Para usar a autenticação do Entra para um banco de dados do Azure DB para PostgreSQL, você pode usar o seguinte método de extensão Npgsql e definir uma cadeia de conexão que não tenha um nome de usuário ou senha:
using System.Text;
using System.Text.Json;
using Azure.Core;
using Azure.Identity;
using Npgsql;
namespace Program;
public static class NpgsqlDataSourceBuilderExtensions
{
private static readonly TokenRequestContext s_azureDBForPostgresTokenRequestContext = new(["https://ossrdbms-aad.database.windows.net/.default"]);
public static NpgsqlDataSourceBuilder UseEntraAuthentication(this NpgsqlDataSourceBuilder dataSourceBuilder, TokenCredential? credential = default)
{
credential ??= new DefaultAzureCredential();
if (dataSourceBuilder.ConnectionStringBuilder.Username == null)
{
var token = credential.GetToken(s_azureDBForPostgresTokenRequestContext, default);
SetUsernameFromToken(dataSourceBuilder, token.Token);
}
SetPasswordProvider(dataSourceBuilder, credential, s_azureDBForPostgresTokenRequestContext);
return dataSourceBuilder;
}
public static async Task<NpgsqlDataSourceBuilder> UseEntraAuthenticationAsync(this NpgsqlDataSourceBuilder dataSourceBuilder, TokenCredential? credential = default, CancellationToken cancellationToken = default)
{
credential ??= new DefaultAzureCredential();
if (dataSourceBuilder.ConnectionStringBuilder.Username == null)
{
var token = await credential.GetTokenAsync(s_azureDBForPostgresTokenRequestContext, cancellationToken).ConfigureAwait(false);
SetUsernameFromToken(dataSourceBuilder, token.Token);
}
SetPasswordProvider(dataSourceBuilder, credential, s_azureDBForPostgresTokenRequestContext);
return dataSourceBuilder;
}
private static void SetPasswordProvider(NpgsqlDataSourceBuilder dataSourceBuilder, TokenCredential credential, TokenRequestContext tokenRequestContext)
{
dataSourceBuilder.UsePasswordProvider(_ =>
{
var token = credential.GetToken(tokenRequestContext, default);
return token.Token;
}, async (_, ct) =>
{
var token = await credential.GetTokenAsync(tokenRequestContext, ct).ConfigureAwait(false);
return token.Token;
});
}
private static void SetUsernameFromToken(NpgsqlDataSourceBuilder dataSourceBuilder, string token)
{
var username = TryGetUsernameFromToken(token);
if (username != null)
{
dataSourceBuilder.ConnectionStringBuilder.Username = username;
}
else
{
throw new Exception("Could not determine username from token claims");
}
}
private static string? TryGetUsernameFromToken(string jwtToken)
{
// Split the token into its parts (Header, Payload, Signature)
var tokenParts = jwtToken.Split('.');
if (tokenParts.Length != 3)
{
return null;
}
// The payload is the second part, Base64Url encoded
var payload = tokenParts[1];
// Add padding if necessary
payload = AddBase64Padding(payload);
// Decode the payload from Base64Url
var decodedBytes = Convert.FromBase64String(payload);
var decodedPayload = Encoding.UTF8.GetString(decodedBytes);
// Parse the decoded payload as JSON
var payloadJson = JsonSerializer.Deserialize<JsonElement>(decodedPayload);
// Try to get the username from 'upn', 'preferred_username', or 'unique_name' claims
if (payloadJson.TryGetProperty("upn", out var upn))
{
return upn.GetString();
}
else if (payloadJson.TryGetProperty("preferred_username", out var preferredUsername))
{
return preferredUsername.GetString();
}
else if (payloadJson.TryGetProperty("unique_name", out var uniqueName))
{
return uniqueName.GetString();
}
return null;
}
private static string AddBase64Padding(string base64) => (base64.Length % 4) switch
{
2 => base64 + "==",
3 => base64 + "=",
_ => base64,
};
}
Agora você pode usar o UseEntraAuthentication método para configurar a cadeia de conexão para o conector Postgres:
using Microsoft.SemanticKernel.Connectors.Postgres;
var connectionString = "Host=mydb.postgres.database.azure.com;Port=5432;Database=postgres;SSL Mode=Require;"; // No Username or Password
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
dataSourceBuilder.UseEntraAuthentication();
dataSourceBuilder.UseVector();
var dataSource = dataSourceBuilder.Build();
var vectorStore = new PostgresVectorStore(dataSource, ownsDataSource: true);
Por padrão, o UseEntraAuthentication método usa o DefaultAzureCredential para autenticar com o Azure AD. Você também pode fornecer uma implementação personalizada TokenCredential , se necessário.
Primeiros passos
Instale o kernel semântico com os extras do Postgres, que inclui o cliente Postgres.
pip install semantic-kernel[postgres]
Em seguida, você pode criar uma instância de armazenamento de vetores usando a PostgresStore classe.
Você pode passar um psycopg_poolAsyncConnectionPool diretamente ou usar o PostgresSettings para criar um pool de conexões a partir de variáveis de ambiente.
from semantic_kernel.connectors.postgres import PostgresStore, PostgresSettings
settings = PostgresSettings()
pool = await settings.create_connection_pool()
async with pool:
vector_store = PostgresStore(connection_pool=pool)
...
Você pode definir POSTGRES_CONNECTION_STRING em seu ambiente, ou as variáveis PGHOSTde ambiente , PGPORT, , PGUSERPGPASSWORD, PGDATABASEe, opcionalmentePGSSLMODE, conforme definido para libpq.
Esses valores serão usados pela classe para criar um pool de conexões PostgresSettings .
Você também pode criar uma coleção diretamente. A Coleção em si é um gerenciador de contexto, então você pode usá-la em um with bloco. Se você não passar em um pool de conexões, a coleção criará um usando a PostgresSettings classe.
from semantic_kernel.connectors.postgres import PostgresCollection
collection = PostgresCollection(collection_name="skhotels", record_type=Hotel)
async with collection: # This will create a connection pool using PostgresSettings
...
Mapeamento de dados
O conector Postgres fornece um mapeador padrão ao mapear dados do modelo de dados para o armazenamento.
O mapeador padrão utiliza as anotações do modelo ou a definição do registo para determinar o tipo de cada propriedade e mapear o modelo num dict que possa ser convertido em linhas do Postgres.
- A propriedade do modelo de dados anotada como uma chave será mapeada para a CHAVE PRIMÁRIA na tabela Postgres.
- As propriedades do modelo de dados anotadas como dados serão mapeadas para uma coluna de tabela no Postgres.
- As propriedades do modelo de dados anotadas como vetores serão mapeadas para uma coluna de tabela que tenha o tipo pgvector
VECTORno Postgres.
from typing import Annotated
from pydantic import BaseModel
from semantic_kernel.connectors.postgres import PostgresCollection
from semantic_kernel.data.vector import (
DistanceFunction,
IndexKind,
VectorStoreField,
vectorstoremodel,
)
@vectorstoremodel
class Hotel(BaseModel):
hotel_id: Annotated[int, VectorStoreField("key")]
hotel_name: Annotated[str, VectorStoreField("data")]
hotel_description: Annotated[str, VectorStoreField("data")]
hotel_description_embedding: Annotated[
list[float] | None,
VectorStoreField(
"vector",
dimensions=4,
index_kind=IndexKind.HNSW,
distance_function=DistanceFunction.COSINE_SIMILARITY,
),
] = None
collection = PostgresCollection(collection_name="Hotels", record_type=Hotel)
async with collection:
await collection.ensure_collection_exists()
CREATE TABLE IF NOT EXISTS public."Hotels" (
"hotel_id" INTEGER NOT NULL,
"hotel_name" TEXT,
"hotel_description" TEXT,
"hotel_description_embedding" VECTOR(4)
PRIMARY KEY ("hotel_id")
);
Indexação vetorial
O hotel_description_embedding no modelo acima Hotel é uma propriedade vetorial com IndexKind.HNSW indexação. Este índice será criado automaticamente quando a coleção for criada.
HNSW é o único tipo de índice suportado para a criação de índice. A construção do índice IVFFlat requer que os dados já existam na tabela no momento da criação do índice e, portanto, não é apropriado para a criação de uma tabela vazia.
Você é livre para criar e modificar índices em tabelas fora do conector, que serão usadas pelo conector ao executar consultas.
Usando a autenticação Entra
O Banco de Dados do Azure para PostgreSQL fornece a capacidade de se conectar ao seu banco de dados usando a autenticação do Entra. Isso elimina a necessidade de armazenar um nome de usuário e senha na cadeia de conexão. Para usar a autenticação Entra para um banco de dados do Azure DB for PostgreSQL, você pode usar a seguinte classe AsyncConnection personalizada:
import base64
import json
import logging
from functools import lru_cache
from azure.core.credentials import TokenCredential
from azure.core.credentials_async import AsyncTokenCredential
from azure.identity import DefaultAzureCredential
from psycopg import AsyncConnection
AZURE_DB_FOR_POSTGRES_SCOPE = "https://ossrdbms-aad.database.windows.net/.default"
logger = logging.getLogger(__name__)
async def get_entra_token_async(credential: AsyncTokenCredential) -> str:
"""Get the password from Entra using the provided credential."""
logger.info("Acquiring Entra token for postgres password")
async with credential:
cred = await credential.get_token(AZURE_DB_FOR_POSTGRES_SCOPE)
return cred.token
def get_entra_token(credential: TokenCredential | None) -> str:
"""Get the password from Entra using the provided credential."""
logger.info("Acquiring Entra token for postgres password")
credential = credential or get_default_azure_credentials()
return credential.get_token(AZURE_DB_FOR_POSTGRES_SCOPE).token
@lru_cache(maxsize=1)
def get_default_azure_credentials() -> DefaultAzureCredential:
"""Get the default Azure credentials.
This method caches the credentials to avoid creating new instances.
"""
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)
async def get_entra_conninfo(credential: TokenCredential | AsyncTokenCredential | None) -> dict[str, str]:
"""Fetches a token returns the username and token."""
# Fetch a new token and extract the username
if isinstance(credential, AsyncTokenCredential):
token = await get_entra_token_async(credential)
else:
token = get_entra_token(credential)
claims = decode_jwt(token)
username = claims.get("upn") or claims.get("preferred_username") or claims.get("unique_name")
if not username:
raise ValueError("Could not extract username from token. Have you logged in?")
return {"user": username, "password": token}
class AsyncEntraConnection(AsyncConnection):
"""Asynchronous connection class for using Entra auth with Azure DB for PostgreSQL."""
@classmethod
async def connect(cls, *args, **kwargs):
"""Establish an asynchronous connection using Entra auth with Azure DB for PostgreSQL."""
credential = kwargs.pop("credential", None)
if credential and not isinstance(credential, (TokenCredential, AsyncTokenCredential)):
raise ValueError("credential must be a TokenCredential or AsyncTokenCredential")
if not kwargs.get("user") or not kwargs.get("password"):
credential = credential or get_default_azure_credentials()
entra_conninfo = await get_entra_conninfo(credential)
if kwargs.get("user"):
entra_conninfo.pop("user", None)
kwargs.update(entra_conninfo)
return await super().connect(*args, **kwargs)
Você pode usar a classe de conexão personalizada com o PostgresSettings.get_connection_pool método para criar um pool de conexões.
from semantic_kernel.connectors.postgres import PostgresSettings, PostgresStore
pool = await PostgresSettings().create_connection_pool(connection_class=AsyncEntraConnection)
async with pool:
vector_store = PostgresStore(connection_pool=pool)
...
Por padrão, a AsyncEntraConnection classe usa o DefaultAzureCredential para autenticar com o Azure AD.
Você também pode fornecer outro TokenCredential nos kwargs, se necessário:
from azure.identity import ManagedIdentityCredential
pool = await PostgresSettings().create_connection_pool(
connection_class=AsyncEntraConnection, credential=ManagedIdentityCredential()
)
async with pool:
vector_store = PostgresStore(connection_pool=pool)
...
JDBC
O conector JDBC pode ser usado para se conectar ao Postgres.