Serialização do modelo de dados de e para diferentes armazenamentos (Visualização)
Para que seu modelo de dados seja armazenado em um banco de dados, ele precisa ser convertido em um formato que o banco de dados possa entender. Bancos de dados diferentes exigem esquemas e formatos de armazenamento diferentes. Alguns têm um esquema rigoroso que precisa ser respeitado, enquanto outros permitem que o esquema seja definido pelo usuário.
Opções de mapeamento
Os conectores de armazenamento vetorial fornecidos pelo Semantic Kernel fornecem várias maneiras de alcançar esse mapeamento.
Mapeadores integrados
Os conectores de armazenamento vetorial fornecidos pelo Semantic Kernel têm mapeadores internos que mapearão seu modelo de dados de e para os esquemas de banco de dados. Consulte a página para cada conector para obter mais informações sobre como os mapeadores internos mapeiam dados para cada banco de dados.
Mapeadores personalizados
Os conectores de armazenamento vetorial fornecidos pelo Semantic Kernel permitem fornecer mapeadores personalizados em combinação com um VectorStoreRecordDefinition
. Nesse caso, o VectorStoreRecordDefinition
pode diferir do modelo de dados fornecido.
O VectorStoreRecordDefinition
é usado para definir o esquema de banco de dados, enquanto o modelo de dados é usado pelo desenvolvedor para interagir com o repositório vetorial.
Neste caso, é necessário um mapeador personalizado para mapear do modelo de dados para o esquema de banco de dados personalizado definido pelo VectorStoreRecordDefinition
.
Dica
Consulte Como criar um mapeador personalizado para um conector do Vetor Store para obter um exemplo sobre como criar seu próprio mapeador personalizado.
Para que seu modelo de dados definido como uma classe ou uma definição seja armazenado em um banco de dados, ele precisa ser serializado para um formato que o banco de dados possa entender.
Há duas maneiras que podem ser feitas, usando a serialização interna fornecida pelo Kernel Semântico ou fornecendo sua própria lógica de serialização.
Os dois diagramas a seguir mostram que os fluxos são mostrados para serialização e desserialização de modelos de dados de e para um modelo de armazenamento.
Fluxo de serialização (usado no Upsert)
Fluxo de desserialização (usado em Obter e Pesquisar)
As etapas marcadas com * (em ambos os diagramas) são implementadas pelo desenvolvedor de um conector específico e são diferentes para cada loja. As etapas marcadas com ** (em ambos os diagramas) são fornecidas como um método em um registro ou como parte da definição de registro, isso é sempre fornecido pelo usuário, consulte de serialização direta para obter mais informações.
(De)Serialização: Abordagens
Serialização direta (Modelo de Dados para Modelo de Armazenamento)
A serialização direta é a melhor maneira de garantir o controle total sobre como seus modelos são serializados e otimizar o desempenho. A desvantagem é que ele é específico para um armazenamento de dados e, portanto, ao usá-lo, não é tão fácil alternar entre diferentes armazenamentos com o mesmo modelo de dados.
Você pode usar isso implementando um método que segue o protocolo SerializeMethodProtocol
em seu modelo de dados ou adicionando funções que seguem o SerializeFunctionProtocol
à sua definição de registro, ambas podem ser encontradas em semantic_kernel/data/vector_store_model_protocols.py
.
Quando uma dessas funções estiver presente, ela será usada para serializar diretamente o modelo de dados para o modelo de armazenamento.
Você pode até implementar apenas uma das duas e usar a (des)serialização interna para a outra direção; tal abordagem pode ser útil, por exemplo, quando está a lidar com uma coleção criada fora do seu domínio e precisa personalizar a forma como é desserializada (e não é possível fazer um "upsert" de qualquer maneira).
(des)serialização integrada (Data Model to Dict e Dict to Store Model e vice-versa)
A serialização interna é feita primeiro convertendo o modelo de dados em um dicionário e, em seguida, serializando-o para o modelo que o repositório entende, para cada armazenamento que é diferente e definido como parte do conector interno. A desserialização é feita na ordem inversa.
Etapa de serialização 1: Modelo de dados a ser ditado
Dependendo do tipo de modelo de dados que você tem, as etapas são feitas de maneiras diferentes. Há quatro maneiras de serializar o modelo de dados para um dicionário:
-
to_dict
método na definição (alinha-se com o atributo to_dict do modelo de dados, seguindo oToDictFunctionProtocol
) - Verifique se o registro é um
ToDictMethodProtocol
e use o métodoto_dict
- verifique se o registro é um modelo Pydantic e use o
model_dump
do modelo, veja a nota abaixo para mais informações. - percorrer os campos na definição e criar o dicionário
Etapa de Serialização 2: Dic para Guardar o Modelo
Um método deve ser fornecido pelo conector para converter o dicionário no modelo de armazenamento. Isto é realizado pelo desenvolvedor do conector e é diferente para cada loja.
Desserialização Etapa 1: Armazenar modelo para ditar
Um método deve ser fornecido pelo conector para converter o modelo de armazenamento em um dicionário. Isso é feito pelo desenvolvedor do conector e é diferente para cada loja.
Desserialização Etapa 2: Ditar para o modelo de dados
A desserialização é feita na ordem inversa, ele tenta estas opções:
-
from_dict
método na definição (alinha-se ao atributo from_dict do modelo de dados, seguindo oFromDictFunctionProtocol
) - Verifique se o registro é um
FromDictMethodProtocol
e use o métodofrom_dict
- verifique se o registro é um modelo Pydantic e use o
model_validate
do modelo, veja a nota abaixo para mais informações. - faça um loop pelos campos na definição e defina os valores, então esse ditado é passado para o construtor do modelo de dados como argumentos nomeados (a menos que o modelo de dados seja um ditado em si, nesse caso ele é retornado como está)
Observação
Usando o Pydantic com serialização integrada
Quando você define seu modelo usando um Pydantic BaseModel, ele usará os model_dump
métodos e model_validate
para serializar e desserializar o modelo de dados de e para um dict. Isso é feito usando o método model_dump sem parâmetros, se você quiser controlar isso, considere implementar o ToDictMethodProtocol
em seu modelo de dados, pois isso é tentado primeiro.
Serialização de vetores
Quando você tem um vetor em seu modelo de dados, ele precisa ser uma lista de floats ou uma lista de ints, já que é isso que a maioria das lojas precisa, se você quiser que sua classe armazene o vetor em um formato diferente, você pode usar o serialize_function
e deserialize_function
definido na VectorStoreRecordVectorField
anotação. Por exemplo, para uma matriz numpy, você pode usar a seguinte anotação:
import numpy as np
vector: Annotated[
np.ndarray | None,
VectorStoreRecordVectorField(
dimensions=1536,
serialize_function=np.ndarray.tolist,
deserialize_function=np.array,
),
] = None
Se você usar um repositório de vetores que possa lidar com matrizes numpy nativas e não quiser convertê-las de um lado para o outro, deverá configurar os métodos de serialização e desserialização diretas
Observação
Isso só é usado ao usar a serialização interna, ao usar a serialização direta, você pode manipular o vetor da maneira que desejar.
Brevemente
Mais informações em breve.