Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Обзор
В этом руководстве рассматриваются основные обновления векторного хранилища, представленные в семантической версии 1.34, которые представляют собой значительный ремонт реализации векторного хранилища для выравнивания с пакетом SDK для .NET и предоставления более унифицированного, интуитивно понятного API. Изменения объединяют все под semantic_kernel.data.vector
и улучшают архитектуру соединителя.
Сводка по ключевым улучшениям
-
Модель единого поля: один
VectorStoreField
класс заменяет несколько типов полей - Встроенные представления: прямое создание внедрений в спецификации векторного поля
- Упрощенный поиск: простое создание функций поиска непосредственно в коллекциях
-
Консолидированная структура: все под
semantic_kernel.data.vector
иsemantic_kernel.connectors
- Расширенный поиск текста: улучшенные возможности поиска текста с помощью оптимизированных соединителей
-
Нерекомендуемое: старое
memory_stores
не рекомендуется использовать в пользу новой архитектуры хранилища векторов
1. Интеграция эмбеддингов и обновление моделей/полей векторного хранилища данных
Существует ряд изменений в том, как вы определяете модель векторного хранилища, самое большое — это то, что теперь мы поддерживаем интегрированные внедрения непосредственно в определения полей векторного хранилища. Это означает, что при указании поля для вектора содержимое этого поля автоматически внедряется с помощью указанного генератора внедрения, например модели внедрения текста OpenAI. Это упрощает процесс создания полей векторов и управления ими.
При определении этого поля необходимо убедиться в трех вещах, особенно при использовании модели Pydantic:
-
типизация: поле, скорее всего, будет иметь три типа,
list[float]
,str
или что-то другое для входных данных генератора встраивания, аNone
- когда поле не задано. -
значение по умолчанию: поле должно иметь значение по умолчанию
None
или что-то другое, чтобы не возникало ошибки при получении записей изget
илиsearch
с использованиемinclude_vectors=False
, которое сейчас является значением по умолчанию.
Здесь есть две проблемы. Во-первых, при декорировании класса с помощью vectorstoremodel
, первая аннотация типа поля используется для заполнения type
параметра класса VectorStoreField
, поэтому необходимо убедиться, что первая аннотация типа является правильным типом для коллекции векторного хранилища, создаваемой с list[float]
. По умолчанию методы get
и search
не включают вектора в результаты, поэтому поле должно иметь значение по умолчанию, и типизация должна соответствовать этому, поэтому часто разрешено None
, и значение по умолчанию установлено на None
. При создании поля значения, которые необходимо внедрить, находятся в этом поле, часто строки, поэтому str
их также необходимо включить. Причина этого изменения заключается в том, чтобы обеспечить большую гибкость в том, что внедрено и что фактически хранится в полях данных, это будет распространенная настройка:
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}"
Обратите внимание на метод post_init , что создает внедренное значение, которое больше одного поля. Три типа также присутствуют.
До: раздельные классы полей
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")
]
После: Унифицированное поле VectorStore с интегрированными вложениями
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
)
]
Ключевые изменения в определении полей
-
Класс с одним полем:
VectorStoreField
заменяет все предыдущие типы полей -
Спецификация типа поля: используйте
field_type: Literal["key", "data", "vector"]
параметр, это может быть позиционный параметр, поэтомуVectorStoreField("key")
он является допустимым. -
Расширенные свойства:
-
storage_name
добавляется, и если задано, используется в качестве имени поля в хранилище векторов, в противном случае используется параметрname
. -
dimensions
теперь является обязательным параметром для полей векторов. -
distance_function
иindex_kind
являются необязательными и, если они не указаны, будут назначены значениямиDistanceFunction.DEFAULT
иIndexKind.DEFAULT
соответственно. Это применимо только к векторным полям, так как каждая реализация хранилища векторов имеет свою логику, которая выбирает значение по умолчанию для этого хранилища.
-
-
Переименование свойств:
-
property_type
→type_
в качестве атрибута и вtype
конструкторах -
is_filterable
→is_indexed
-
is_full_text_searchable
→is_full_text_indexed
-
-
Встроенные эмбеддинги: Добавьте
embedding_generator
непосредственно к векторным полям, или вы можете задатьembedding_generator
для самой коллекции в векторном хранилище, которое будет использоваться для всех векторных полей в этом хранилище. Это значение имеет приоритет над генератором внедрения на уровне коллекции.
2. Новые методы для магазинов и коллекций
Расширенный интерфейс хранилища
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.
Расширенный интерфейс коллекции
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)
3. Расширенные фильтры для поиска
Новая реализация хранилища векторов перемещается от объектов FilterClause на основе строк к более мощным и типобезопасным лямбда-выражениям или вызываемым фильтрам.
До: Объекты FilterClause
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)
)
После: лямбда-фильтры выражений
# 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
)
Советы по миграции для фильтров
-
Простое равенство:
filter.equal_to("field", "value")
становитсяlambda r: r.field == "value"
-
Несколько условий: цепочка с операторами
and
/or
вместо нескольких вызовов фильтра -
Содержимое тегов и массивов:
filter.any_tag_equal_to("tags", "value")
становитсяlambda r: "value" in r.tags
- Расширенные возможности: поддержка запросов диапазона, сложной логической логики и пользовательских предикатов
4. Улучшена простота создания функций поиска
Прежде чем: создание функции поиска с помощью 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',
...
)
После: создание функции прямого поиска
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. Переименование соединителя и импорт изменений
Консолидация пути импорта
# 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
)
Переименование класса соединителя
Старое имя | Новое имя |
---|---|
AzureCosmosDBforMongoDB* | CosmosMongo* |
AzureCosmosDBForNoSQL* | CosmosNoSql* |
6. Улучшения поиска текста и удаление соединителя Bing
Удалён коннектор Bing и улучшен интерфейс текстового поиска.
Соединитель текстового поиска Bing был удалён. Миграция на альтернативные поставщики поиска:
# 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)
Улучшенные методы поиска
Перед: три отдельных метода поиска с различными типами возвращаемых значений
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,
)
После: единый метод поиска с параметром типа вывода
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. Отмена устаревших хранилищ памяти
Все старые хранилища памяти, основанные на MemoryStoreBase
, были перемещены в semantic_kernel.connectors.memory_stores
и теперь помечены как устаревшие. Большинство из них имеют новую эквивалентную реализацию на основе VectorStore и VectorStoreCollection, которая может быть найдена в semantic_kernel.connectors.memory
.
Эти соединители будут полностью удалены:
AstraDB
Milvus
Usearch
Если вам потребуется любой из этих элементов, обязательно перенесите код из устаревшего модуля и semantic_kernel.memory
папки или реализуйте собственную коллекцию векторного хранилища на основе нового VectorStoreCollection
класса.
Если есть большой спрос на основе отзывов github, мы рассмотрим их возвращение, но на данный момент они не поддерживаются и будут удалены в будущем.
Миграция из 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"
)
Миграция плагина памяти
Если вы хотите иметь подключаемый модуль, который также может сохранять сведения, вы можете легко создать следующее:
# 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")
Контрольный список миграции для поиска векторов
Шаг 1. Обновление импорта
- [ ] Замена импорта хранилища памяти эквивалентами векторного хранилища
- [ ] Обновление импорта полей для использования
VectorStoreField
- [ ] Удалите импорт соединителя Bing
Шаг 2. Обновление определений полей
- [ ] Преобразование в унифицированный
VectorStoreField
класс - [ ] Обновление имен свойств (
is_filterable
→is_indexed
) - Добавьте интегрированные генераторы внедрения в векторные поля
Шаг 3. Обновление использования коллекции
- [ ] Замена операций памяти методами векторного хранилища
- [ ] Используйте новые пакетные операции, где применимо
- [ ] Реализация создания новой функции поиска
Шаг 4. Обновление реализации поиска
- [ ] Замените функции ручного поиска на
create_search_function
- [ ] Обновление текстового поиска для использования новых поставщиков
- [ ] Реализация гибридного поиска, где полезно
- [ ] Переход от выражений
FilterClause
к выражениямlambda
для фильтрации
Шаг 5. Удаление устаревшего кода
- [ ] Удалить использование
SemanticTextMemory
- [ ] Удаление
TextMemoryPlugin
зависимостей
Преимущества производительности и функций
Улучшения производительности
- Пакетные операции: новые методы upsert/delete повышают пропускную способность.
- Интегрированные внедрения: устраняет отдельные этапы внедрения
- Оптимизированный поиск: встроенные функции поиска оптимизированы для каждого типа хранилища.
Улучшения функций
- Гибридный поиск: объединяет векторный и текстовый поиск для улучшения результатов
- Расширенная фильтрация: расширенные выражения фильтров и индексирование
Опыт разработчика
- Упрощенный API: меньше классов и методов для изучения
- Согласованный интерфейс: единый подход во всех хранилищах векторов
- Улучшенная документация: четкие примеры и пути миграции
- Защита на будущее: согласовано с SDK .NET для согласованной кроссплатформенной разработки
Заключение
Обновления хранилища векторов, описанные выше, представляют собой значительное улучшение пакета SDK для Python для семантического ядра. Новая унифицированная архитектура обеспечивает более высокую производительность, расширенные возможности и более интуитивно понятный интерфейс разработчика. Хотя миграция требует обновления импорта и рефакторинга существующего кода, преимущества в обслуживании и функциональности делают это обновление настоятельно рекомендуемым.
Дополнительные сведения о миграции см. в обновленных примерах в каталоге samples/concepts/memory/
и комплексной документации по API.