Dela via


Migreringsguide för Semantic Kernel Python Vector Store

Översikt

Den här guiden beskriver de viktigaste uppdateringarna av vektorarkivet som introducerades i Semantic Kernel version 1.34, vilket representerar en betydande översyn av implementeringen av vektorarkivet för att anpassa till .NET SDK och tillhandahålla ett mer enhetligt, intuitivt API. Ändringarna konsoliderar allt under semantic_kernel.data.vector och förbättrar anslutningsarkitekturen.

Sammanfattning av viktiga förbättringar

  • Enhetlig fältmodell: En klass VectorStoreField ersätter flera fälttyper
  • Integrerade inbäddningar: Direkt inbäddningsgenerering i specifikationer för vektorfält
  • Förenklad sökning: Enkelt att skapa sökfunktioner direkt i samlingar
  • Konsoliderad struktur: Allt under semantic_kernel.data.vector och semantic_kernel.connectors
  • Förbättrad textsökning: Förbättrade textsökningsmöjligheter med strömlinjeformade kopplingar
  • Utfasning: Gamla memory_stores är inaktuella till förmån för den nya arkitekturen för vektorlager

1. Integrerade inbäddningar och vektorlagringsmodeller/fältuppdateringar

Det finns ett antal ändringar i hur du definierar vektorlagringsmodellen, det största är att vi nu stöder integrerade inbäddningar direkt i definitionerna för vektorlagerfält. Det innebär att när du anger ett fält som ska vara en vektor bäddas innehållet i fältet automatiskt in med den angivna inbäddningsgeneratorn, till exempel OpenAI:s textinbäddningsmodell. Detta förenklar processen för att skapa och hantera vektorfält.

När du definierar det fältet måste du se till att det finns tre saker, särskilt när du använder en pydantisk modell:

  1. sv-SE: typning: Fältet kommer sannolikt att ha tre typer, list[float], str eller något annat för indata som ska användas av inbäddningsgeneratorn och None för när fältet inte är konfigurerat.
  2. standardvärde: Fältet måste ha ett standardvärde på None eller något annat, så att det inte uppstår något fel när poster hämtas från get eller search som include_vectors=False är standard nu.

Det finns två problem här, den första är att när du dekorerar en klass med vectorstoremodelanvänds den första typen av anteckning för fältet för att fylla type parametern i VectorStoreField klassen, så du måste se till att den första typen av anteckning är rätt typ för vektorlagringssamlingen som ska skapas med, ofta list[float]. Som standard inkluderar metoderna get och search inte vektorer i resultaten, så fältet behöver ett standardvärde och datatypen måste motsvara detta; därför tillåts ofta None och standarden sätts till None. När fältet skapas finns de värden som måste bäddas in i det här fältet, ofta strängar, så str måste också inkluderas. Anledningen till den här ändringen är att ge mer flexibilitet i vad som är inbäddat och vad som faktiskt lagras i datafält, detta skulle vara en vanlig konfiguration:

from semantic_kernel.data.vector import VectorStoreField, vectorstoremodel
from typing import Annotated
from dataclasses import dataclass

@vectorstoremodel
@dataclass
class MyRecord:
    content: Annotated[str, VectorStoreField('data', is_indexed=True, is_full_text_indexed=True)]
    title: Annotated[str, VectorStoreField('data', is_indexed=True, is_full_text_indexed=True)]
    id: Annotated[str, VectorStoreField('key')]
    vector: Annotated[list[float] | str | None, VectorStoreField(
        'vector', 
        dimensions=1536, 
        distance_function="cosine",
        embedding_generator=OpenAITextEmbedding(ai_model_id="text-embedding-3-small"),
    )] = None

    def __post_init__(self):
        if self.vector is None:
            self.vector = f"Title: {self.title}, Content: {self.content}"

Observera post_init metoden, detta skapar ett värde som inbäddas, vilket är mer än ett enda fält. De tre typerna finns också.

Före: Avgränsa fältklasser

from semantic_kernel.data import (
    VectorStoreRecordKeyField,
    VectorStoreRecordDataField, 
    VectorStoreRecordVectorField
)

# Old approach with separate field classes
fields = [
    VectorStoreRecordKeyField(name="id"),
    VectorStoreRecordDataField(name="text", is_filterable=True, is_full_text_searchable=True),
    VectorStoreRecordVectorField(name="vector", dimensions=1536, distance_function="cosine")
]

Efter: Unified VectorStoreField med integrerade inbäddningar

from semantic_kernel.data.vector import VectorStoreField
from semantic_kernel.connectors.ai.open_ai import OpenAITextEmbedding

# New unified approach with integrated embeddings
embedding_service = OpenAITextEmbedding(
    ai_model_id="text-embedding-3-small"
)

fields = [
    VectorStoreField(
        "key",
        name="id",
    ),
    VectorStoreField(
        "data",
        name="text",
        is_indexed=True,  # Previously is_filterable
        is_full_text_indexed=True  # Previously is_full_text_searchable
    ),
    VectorStoreField(
        "vector",
        name="vector",
        dimensions=1536,
        distance_function="cosine",
        embedding_generator=embedding_service  # Integrated embedding generation
    )
]

Viktiga ändringar i fältdefinition

  1. Enskild fältklass: VectorStoreField ersätter alla tidigare fälttyper
  2. Fälttypspecifikation: Använd field_type: Literal["key", "data", "vector"] parameter, det kan vara en positionsparameter, så VectorStoreField("key") den är giltig.
  3. Förbättrade egenskaper:
    • storage_name har lagts till, när den anges, som används som fältnamn i vektorlagret, annars används parametern name .
    • dimensions är nu en obligatorisk parameter för vektorfält.
    • distance_function och index_kind är båda valfria och kommer att anges till DistanceFunction.DEFAULT respektive IndexKind.DEFAULT om de inte anges. Endast för vektorfält har varje implementering av en vektorbutik logik som väljer en standard för den butiken.
  4. Egenskapsnamnbyten:
    • property_type type_→ som ett attribut och type i konstruktorer
    • is_filterableis_indexed
    • is_full_text_searchableis_full_text_indexed
  5. Integrerade inbäddningar: Lägg till embedding_generator direkt till vektorfält, alternativt kan du ange embedding_generator på själva vektorlagringssamlingen, som ska användas för alla vektorfält i det arkivet. Det här värdet har företräde framför inbäddningsgeneratorn på samlingsnivå.

2. Nya metoder för butiker och samlingar

Förbättrat butiksgränssnitt

from semantic_kernel.connectors.in_memory import InMemoryStore

# Before: Limited collection methods
collection = InMemoryStore.get_collection("my_collection", record_type=MyRecord)

# After: Slimmer collection interface with new methods
collection = InMemoryStore.get_collection(MyRecord)
# if the record type has the `vectorstoremodel` decorator it can contain both the collection_name and the definition for the collection.

# New methods for collection management
await store.collection_exists("my_collection")
await store.ensure_collection_deleted("my_collection")
# both of these methods, create a simple model to streamline doing collection management tasks.
# they both call the underlying `VectorStoreCollection` methods, see below.

Förbättrat samlingsgränssnitt

from semantic_kernel.connectors.in_memory import InMemoryCollection

collection = InMemoryCollection(
    record_type=MyRecord,
    embedding_generator=OpenAITextEmbedding(ai_model_id="text-embedding-3-small")  # Optional, if there is no embedding generator set on the record type
)
# If both the collection and the record type have an embedding generator set, the record type's embedding generator will be used for the collection. If neither is set, it is assumed the vector store itself can create embeddings, or that vectors are included in the records already, if that is not the case, it will likely raise.

# Enhanced collection operations
await collection.collection_exists()
await collection.ensure_collection_exists()
await collection.ensure_collection_deleted()

# CRUD methods
# Removed batch operations, all CRUD operations can now take both a single record or a list of records
records = [
    MyRecord(id="1", text="First record"),
    MyRecord(id="2", text="Second record")
]
ids = ["1", "2"]
# this method adds vectors automatically
await collection.upsert(records)

# You can do get with one or more ids, and it will return a list of records
await collection.get(ids)  # Returns a list of records
# you can also do a get without ids, with top, skip and order_by parameters
await collection.get(top=10, skip=0, order_by='id')
# the order_by parameter can be a string or a dict, with the key being the field name and the value being True for ascending or False for descending order.
# At this time, not all vector stores support this method.

# Delete also allows for single or multiple ids
await collection.delete(ids)

query = "search term"
# New search methods, these use the built-in embedding generator to take the value and create a vector
results = await collection.search(query, top=10)
results = await collection.hybrid_search(query, top=10)

# You can also supply a vector directly
query_vector = [0.1, 0.2, 0.3]  # Example vector
results = await collection.search(vector=query_vector, top=10)
results = await collection.hybrid_search(query, vector=query_vector, top=10)

Implementeringen av det nya vektorarkivet flyttas från strängbaserade FilterClause-objekt till mer kraftfulla och typsäkra lambda-uttryck eller anropsbara filter.

Före: FilterClause-objekt

from semantic_kernel.data.text_search import SearchFilter, EqualTo, AnyTagsEqualTo
from semantic_kernel.data.vector_search import VectorSearchFilter

# Creating filters using FilterClause objects
text_filter = SearchFilter()
text_filter.equal_to("category", "AI")
text_filter.equal_to("status", "active")

# Vector search filters
vector_filter = VectorSearchFilter()
vector_filter.equal_to("category", "AI")
vector_filter.any_tag_equal_to("tags", "important")

# Using in search
results = await collection.search(
    "query text",
    options=VectorSearchOptions(filter=vector_filter)
)

Efter: Lambda-uttrycksfilter

# When defining the collection with the generic type hints, most IDE's will be able to infer the type of the record, so you can use the record type directly in the lambda expressions.
collection = InMemoryCollection[str, MyRecord](MyRecord)

# Using lambda expressions for more powerful and type-safe filtering
# The code snippets below work on a data model with more fields then defined earlier.

# Direct lambda expressions
results = await collection.search(
    "query text", 
    filter=lambda record: record.category == "AI" and record.status == "active"
)

# Complex filtering with multiple conditions
results = await collection.search(
    "query text",
    filter=lambda record: (
        record.category == "AI" and 
        record.score > 0.8 and
        "important" in record.tags
    )
)

# Combining conditions with boolean operators
results = await collection.search(
    "query text",
    filter=lambda record: (
        record.category == "AI" or record.category == "ML"
    ) and record.published_date >= datetime(2024, 1, 1)
)

# Range filtering (now possible with lambda expressions)
results = await collection.search(
    "query text",
    filter=lambda record: 0.5 <= record.confidence_score <= 0.9
)

Migreringstips för filter

  1. Enkel likhet: filter.equal_to("field", "value") blir lambda r: r.field == "value"
  2. Flera villkor: Kedja med and/or operatorer i stället för flera filteranrop
  3. Tagg/matris-inneslutning: filter.any_tag_equal_to("tags", "value") blir lambda r: "value" in r.tags
  4. Förbättrade funktioner: Stöd för intervallfrågor, komplex boolesk logik och anpassade predikat

4. Enklare att skapa sökfunktioner

Före: Skapa sökfunktion med VectorStoreTextSearch

from semantic_kernel.connectors.in_memory import InMemoryCollection
from semantic_kernel.data import VectorStoreTextSearch

collection = InMemoryCollection(collection_name='collection', record_type=MyRecord)
search = VectorStoreTextSearch.from_vectorized_search(vectorized_search=collection, embedding_generator=OpenAITextEmbedding(ai_model_id="text-embedding-3-small"))

search_function = search.create_search(
    function_name='search',
    ...
)

Efter: Skapa direktsökningsfunktion

collection = InMemoryCollection(MyRecord)
# Create search function directly on collection
search_function = collection.create_search_function(
    function_name="search",
    search_type="vector",  # or "keyword_hybrid"
    top=10,
    vector_property_name="vector",  # Name of the vector field
)

# Add to kernel directly
kernel.add_function(plugin_name="memory", function=search_function)

5. Byta namn på anslutningar och ändra import

Konsolidering av importsökväg

# Before: Scattered imports
from semantic_kernel.connectors.memory.azure_cognitive_search import AzureCognitiveSearchMemoryStore
from semantic_kernel.connectors.memory.chroma import ChromaMemoryStore
from semantic_kernel.connectors.memory.pinecone import PineconeMemoryStore
from semantic_kernel.connectors.memory.qdrant import QdrantMemoryStore

# After: Consolidated under connectors
from semantic_kernel.connectors.azure_ai_search import AzureAISearchStore
from semantic_kernel.connectors.chroma import ChromaVectorStore
from semantic_kernel.connectors.pinecone import PineconeVectorStore
from semantic_kernel.connectors.qdrant import QdrantVectorStore

# Alternative after: Consolidated with lazy loading:
from semantic_kernel.connectors.memory import (
    AzureAISearchStore,
    ChromaVectorStore,
    PineconeVectorStore,
    QdrantVectorStore,
    WeaviateVectorStore,
    RedisVectorStore
)

Byten av namn på anslutningsklasser

Gammalt namn Nytt namn
AzureCosmosDBforMongoDB* CosmosMongo*
AzureCosmosDBForNoSQL* CosmosNoSql*

6. Förbättringar av textsökning och borttagen Bing-anslutning

Bing-anslutningsprogrammet har tagits bort och textsökningsgränssnittet har förbättrats

Anslutningsappen för textsökning i Bing har tagits bort. Migrera till alternativa sökprovidrar:

# Before: Bing Connector (REMOVED)
from semantic_kernel.connectors.search.bing import BingConnector

bing_search = BingConnector(api_key="your-bing-key")

# After: Use Brave Search or other providers
from semantic_kernel.connectors.brave import BraveSearch
# or
from semantic_kernel.connectors.search import BraveSearch

brave_search = BraveSearch()

# Create text search function
text_search_function = brave_search.create_search_function(
    function_name="web_search",
    query_parameter_name="query",
    description="Search the web for information"
)

kernel.add_function(plugin_name="search", function=text_search_function)

Förbättrade sökmetoder

Före: Tre separata sökmetoder med olika returtyper

from semantic_kernel.connectors.brave import BraveSearch
brave_search = BraveSearch()
# Before: Separate search methods
search_results: KernelSearchResult[str] = await brave_search.search(
    query="semantic kernel python",
    top=5,
)

search_results: KernelSearchResult[TextSearchResult] = await brave_search.get_text_search_results(
    query="semantic kernel python",
    top=5,
)

search_results: KernelSearchResult[BraveWebPage] = await brave_search.get_search_results(
    query="semantic kernel python",
    top=5,
)

Efter: Samlad sökmetod med parameter för utdatatyp

from semantic_kernel.data.text_search import SearchOptions
# Enhanced search results with metadata
search_results: KernelSearchResult[str] = await brave_search.search(
    query="semantic kernel python",
    output_type=str, # can also be TextSearchResult or anything else for search engine specific results, default is `str`
    top=5,
    filter=lambda result: result.country == "NL",  # Example filter
)

async for result in search_results.results:
    assert isinstance(result, str)  # or TextSearchResult if using that type
    print(f"Result: {result}")
    print(f"Metadata: {search_results.metadata}")

7. Utfasning av gamla minneslager

Alla gamla minneslager, baserade på MemoryStoreBase , har flyttats till semantic_kernel.connectors.memory_stores och har nu markerats som inaktuella. De flesta av dem har en motsvarande ny implementering baserad på VectorStore och VectorStoreCollection, som finns i semantic_kernel.connectors.memory.

Dessa kontakter kommer att tas bort helt och hållet.

  • AstraDB
  • Milvus
  • Usearch

Om du behöver något av dessa fortfarande måste du antingen ta över koden från den inaktuella modulen semantic_kernel.memory och mappen, eller implementera din egen vektorlagringssamling baserat på den nya VectorStoreCollection klassen.

Om det finns en stor efterfrågan baserat på github-feedback överväger vi att ta tillbaka dem, men för tillfället underhålls de inte och tas bort i framtiden.

Migrering från SemanticTextMemory

# Before: SemanticTextMemory (DEPRECATED)
from semantic_kernel.memory import SemanticTextMemory
from semantic_kernel.connectors.ai.open_ai import OpenAITextEmbeddingGenerationService

embedding_service = OpenAITextEmbeddingGenerationService(ai_model_id="text-embedding-3-small")
memory = SemanticTextMemory(storage=vector_store, embeddings_generator=embedding_service)

# Store memory
await memory.save_information(collection="docs", text="Important information", id="doc1")

# Search memory  
results = await memory.search(collection="docs", query="important", limit=5)
# After: Direct Vector Store Usage
from semantic_kernel.data.vector import VectorStoreField, vectorstoremodel
from semantic_kernel.connectors.in_memory import InMemoryCollection

# Define data model
@vectorstoremodel
@dataclass
class MemoryRecord:
    id: Annotated[str, VectorStoreField('key')]
    text: Annotated[str, VectorStoreField('data', is_full_text_indexed=True)]
    embedding: Annotated[list[float] | str | None, VectorStoreField('vector', dimensions=1536, distance_function="cosine", embedding_generator=OpenAITextEmbedding(ai_model_id="text-embedding-3-small"))] = None

# Create vector store with integrated embeddings
collection = InMemoryCollection(
    record_type=MemoryRecord,
    embedding_generator=OpenAITextEmbedding(ai_model_id="text-embedding-3-small")  # Optional, if not set on the record type
)

# Store with automatic embedding generation
record = MemoryRecord(id="doc1", text="Important information", embedding='Important information')
await collection.upsert(record)

# Search with built-in function
search_function = collection.create_search_function(
    function_name="search_docs",
    search_type="vector"
)

Migrering av minnesplugin

När du vill ha ett plugin-program som också kan spara information kan du enkelt skapa det så här:

# Before: TextMemoryPlugin (DEPRECATED)
from semantic_kernel.core_plugins import TextMemoryPlugin

memory_plugin = TextMemoryPlugin(memory)
kernel.add_plugin(memory_plugin, "memory")
# After: Custom plugin using vector store search functions
from semantic_kernel.functions import kernel_function

class VectorMemoryPlugin:
    def __init__(self, collection: VectorStoreCollection):
        self.collection = collection
    
    @kernel_function(name="save")
    async def save_memory(self, text: str, key: str) -> str:
        record = MemoryRecord(id=key, text=text, embedding=text)
        await self.collection.upsert(record)
        return f"Saved to {self.collection.collection_name}"
    
    @kernel_function(name="search") 
    async def search_memory(self, query: str, limit: int = 5) -> str:
        results = await self.collection.search(
            query, top=limit, vector_property_name="embedding"
        )        
        return "\n".join([r.record.text async for r in results.results])

# Register the new plugin
memory_plugin = VectorMemoryPlugin(collection)
kernel.add_plugin(memory_plugin, "memory")

Steg 1: Uppdatera importer

  • [ ] Ersätt importer av minneslager med motsvarande vektorlager
  • [ ] Uppdatera fältimporter som ska användas VectorStoreField
  • [ ] Ta bort importer av Bing-anslutningsprogram

Steg 2: Uppdatera fältdefinitioner

  • [ ] Konvertera till enhetlig VectorStoreField klass
  • [ ] Uppdatera egenskapsnamn (is_filterableis_indexed)
  • [ ] Lägg till integrerade inbäddningsgeneratorer i vektorfält

Steg 3: Uppdatera insamlingsanvändning

  • [ ] Ersätt minnesåtgärder med metoder för vektorlagring
  • [ ] Använd nya batchåtgärder där det är tillämpligt
  • [ ] Implementera skapande av ny sökfunktion

Steg 4: Uppdatera sökimplementeringen

  • [ ] Ersätt manuella sökfunktioner med create_search_function
  • [ ] Uppdatera textsökning för att använda nya leverantörer
  • [ ] Implementera hybridsökning där det är fördelaktigt
  • [ ] Migrera från FilterClause till lambda uttryck för filtrering

Steg 5: Ta bort inaktuell kod

  • [ ] Ta bort SemanticTextMemory användning
  • [ ] Ta bort TextMemoryPlugin beroenden

Prestanda- och funktionsfördelar

Prestandaförbättringar

  • Batch-åtgärder: Nya batch-upsert/delete-operationer förbättrar genomströmningen
  • Integrerade inbäddningar: Eliminerar separata inbäddningsgenereringssteg
  • Optimerad sökning: Inbyggda sökfunktioner är optimerade för varje butikstyp

Funktionsförbättringar

  • Hybridsökning: Kombinerar vektor- och textsökning för bättre resultat
  • Avancerad filtrering: Förbättrade filteruttryck och indexering

Utvecklarupplevelse

  • Förenklat API: Färre klasser och metoder att lära sig
  • Konsekvent gränssnitt: Enhetlig metod för alla vektorlager
  • Bättre dokumentation: Rensa exempel och migreringsvägar
  • Framtidssäker: Justerad med .NET SDK för konsekvent plattformsoberoende utveckling

Slutsats

Uppdateringarna av vektorarkivet som beskrivs ovan representerar en betydande förbättring av Semantic Kernel Python SDK. Den nya enhetliga arkitekturen ger bättre prestanda, förbättrade funktioner och en mer intuitiv utvecklarupplevelse. Även om migrering kräver uppdatering av importer och refaktorisering av befintlig kod, rekommenderas uppgraderingen starkt av fördelarna med underhåll och funktioner.

Mer hjälp med migrering finns i de uppdaterade exemplen samples/concepts/memory/ i katalogen och den omfattande API-dokumentationen.