Выделитель текстаCompatible with Milvus 2.6.8+

Выделитель в Milvus аннотирует совпадающие термины в текстовых полях, оборачивая их настраиваемыми тегами. Выделение помогает объяснить, почему документ совпадает, улучшает читаемость результатов и поддерживает богатый рендеринг в приложениях поиска и RAG.

Выделение выполняется как этап постобработки конечного набора результатов поиска. Оно не влияет на поиск кандидатов, логику фильтрации, ранжирование или оценку.

Выделитель обеспечивает три независимых измерения контроля:

  • Какие термины выделяются

    Вы можете выбрать, откуда берутся выделенные термины. Например, выделить поисковые термины, используемые в полнотекстовом поиске BM25, или термины запроса, указанные в выражениях фильтрации на основе текста (например, условия TEXT_MATCH ).

  • Как отображаются выделенные термины

    Вы можете управлять тем, как совпавшие термины отображаются в результатах подсветки, настраивая метки, вставляемые до и после каждого совпадения. Например, используйте простые маркеры, такие как {}, или HTML-теги, такие как <em></em>, для богатой визуализации.

  • Как возвращается выделенный текст

    Вы можете управлять тем, как выделенные результаты возвращаются в виде фрагментов, включая начало фрагментов, их длину и количество возвращаемых фрагментов.

В следующих разделах рассматриваются эти сценарии.

Когда вы выполняете полнотекстовый поиск в BM25, вы можете выделить поисковые термины в возвращаемых результатах, чтобы объяснить, почему документ соответствует запросу. Чтобы узнать больше о полнотекстовом поиске в BM25, обратитесь к разделу Полнотекстовый поиск.

В этом сценарии выделенные термины напрямую связаны с поисковыми терминами, используемыми в полнотекстовом поиске BM25. Выделитель использует эти термины для аннотирования совпадающего текста в итоговом результате.

Предположим, что в текстовом поле хранится следующее содержимое:

Milvus supports full text search. Use BM25 for keyword relevance. Filters can narrow results.

Конфигурация выделителя

Чтобы выделить поисковые термины в полнотекстовом поиске BM25, создайте LexicalHighlighter и включите подсветку поисковых терминов для полнотекстового поиска BM25:

from pymilvus import LexicalHighlighter

highlighter = LexicalHighlighter(
    pre_tags=["{"],              # Tag inserted before each highlighted term
    post_tags=["}"],             # Tag inserted after each highlighted term
    highlight_search_text=True   # Enable search term highlighting for BM25 full text search
)

В этом примере:

  • pre_tags и post_tags управляют тем, как выделенный текст появляется в выдаче. В этом случае совпадающие термины обернуты тегом {} (например, {term}). Можно также указать несколько тегов в виде списка (например, ["<b>", "<i>"]). При выделении нескольких терминов теги применяются по порядку и поворачиваются в соответствии с последовательностью совпадений.

  • highlight_search_text=True указывает Milvus на использование поисковых терминов в полнотекстовом поиске BM25 в качестве источника выделенных терминов.

После создания объекта Highlighter примените его конфигурацию к запросу полнотекстового поиска BM25:

results = client.search(
    ...,
    data=["BM25"],      # Search term used in BM25 full text search
    highlighter=highlighter # Pass highlighter config here
)

Вывод выделения

Когда подсветка включена, Milvus возвращает выделенный текст в специальном поле highlight. По умолчанию выделенная выдача возвращается в виде фрагмента, начиная с первого найденного термина.

В этом примере поисковым термином является "BM25", поэтому он выделен в возвращаемом результате:

{
    ...,
    "highlight": {
        "text": [
            "{BM25} for keyword relevance. Filters can narrow results."
        ]
    }
}

Чтобы управлять положением, длиной и количеством возвращаемых фрагментов, см. раздел Возвращать выделенный текст в виде фрагментов.

Выделение поисковых терминов при фильтрации

Помимо выделения поисковых терминов, вы можете выделять термины, используемые в текстовых выражениях фильтрации.

В настоящее время для выделения терминов запроса поддерживается только условие фильтрации TEXT_MATCH. Чтобы узнать больше, обратитесь к разделу "Текстовое соответствие".

В этом сценарии выделенные термины используются в выражениях фильтрации на основе текста. Фильтрация определяет, какие документы совпадают, а подсветка аннотирует совпавшие участки текста.

Предположим, что следующее содержимое хранится в текстовом поле:

This document explains how text filtering works in Milvus.

Конфигурация выделителя

Чтобы выделить термины запроса, используемые при фильтрации, создайте LexicalHighlighter и определите highlight_query, соответствующий условию фильтрации:

from pymilvus import LexicalHighlighter

highlighter = LexicalHighlighter(
    pre_tags=["{"],              # Tag inserted before each highlighted term
    post_tags=["}"],             # Tag inserted after each highlighted term
    highlight_query=[{
        "type": "TextMatch",     # Text filtering type
        "field": "text",         # Target text field
        "text": "text filtering" # Terms to highlight
    }]
)

В этой конфигурации:

  • pre_tags post_tags и управляют тем, как выделенный текст появляется в выводе. В этом случае совпадающие термины оборачиваются тегами {} (например, {term}). Можно также указать несколько тегов в виде списка (например, ["<b>", "<i>"]). При выделении нескольких терминов теги применяются по порядку и поворачиваются в соответствии с последовательностью совпадений.

  • highlight_query определяет, какие термины фильтрации должны быть выделены.

После создания объекта Highlighter примените то же выражение фильтрации и конфигурацию Highlighter к поисковому запросу:

results = client.search(
    ...,
    filter='TEXT_MATCH(text, "text filtering")',
    highlighter=highlighter # Pass highlighter config here
)

Вывод с подсветкой

Когда для фильтрации включена подсветка терминов запроса, Milvus возвращает выделенный текст в специальном поле highlight. По умолчанию выделенная выдача возвращается в виде фрагмента, начиная с первого найденного термина.

В этом примере первый найденный термин - "text", поэтому возвращаемый выделенный текст начинается с этой позиции:

{
    ...,
    "highlight": {
        "text": [
            "{text} {filtering} works in Milvus."
        ]
    }
}

Для управления позицией, длиной и количеством возвращаемых фрагментов см. раздел Возвращать выделенный текст в виде фрагментов.

Вывод выделенного текста на основе фрагментов

По умолчанию Milvus возвращает выделенный текст в виде фрагментов, начиная с первого найденного термина. Настройки, связанные с фрагментами, позволяют дополнительно контролировать возврат фрагментов, не меняя при этом, какие термины выделяются.

Предположим, что следующее содержимое хранится в текстовом поле:

Milvus supports full text search. Use BM25 for keyword relevance. Filters can narrow results.

Конфигурация выделителя

Чтобы управлять формой выделенных фрагментов, настройте параметры, связанные с фрагментами, на странице LexicalHighlighter:

from pymilvus import LexicalHighlighter

highlighter = LexicalHighlighter(
    pre_tags=["{"],
    post_tags=["}"],
    highlight_search_text=True,
    fragment_offset=5,     # Number of characters to reserve before the first matched term
    fragment_size=60,      # Max. length of each fragment to return
    num_of_fragments=1     # Max. number of fragments to return
)

В этой конфигурации:

  • fragment_offset резервирует ведущий контекст перед первым выделенным фрагментом.

  • fragment_size ограничивает объем текста, включаемого в каждый фрагмент.

  • num_of_fragments управляет количеством возвращаемых фрагментов.

После создания объекта Highlighter примените конфигурацию Highlighter к поисковому запросу:

results = client.search(
    ...,
    data=["BM25"],
    highlighter=highlighter # Pass highlighter config here
)

Вывод с выделением

При включенном выделении на основе фрагментов Milvus возвращает выделенный текст в виде фрагментов в поле highlight:

{
    ...,
    "highlight": {
        "text": [
            "Use {BM25} for keyword relevance. Filters can narrow results."
        ]
    }
}

В этом выводе:

  • Фрагмент не начинается точно с {BM25}, потому что установлено значение fragment_offset.

  • Возвращается только один фрагмент, потому что num_of_fragments равен 1.

  • Длина фрагмента ограничена значением fragment_size.

Примеры

Подготовка

Перед использованием подсветки убедитесь, что ваша коллекция правильно настроена.

В приведенном ниже примере создается коллекция, поддерживающая полнотекстовый поиск BM25 и запросы TEXT_MATCH, а затем в нее вставляются примеры документов.

Подготовьте коллекцию

from pymilvus import (
    MilvusClient,
    DataType,
    Function,
    FunctionType,
    LexicalHighlighter,
)

client = MilvusClient(uri="http://localhost:19530")
COLLECTION_NAME = "highlighter_demo"

# Clean up existing collection
if client.has_collection(COLLECTION_NAME):
    client.drop_collection(COLLECTION_NAME)

# Define schema
schema = client.create_schema(enable_dynamic_field=False)
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
schema.add_field(
    field_name="text",
    datatype=DataType.VARCHAR,
    max_length=2000,
    enable_analyzer=True,  # Required for BM25
    enable_match=True,     # Required for TEXT_MATCH
)
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)

# Add BM25 function
schema.add_function(Function(
    name="text_bm25",
    function_type=FunctionType.BM25,
    input_field_names=["text"],
    output_field_names=["sparse_vector"],
))

# Create index
index_params = client.prepare_index_params()
index_params.add_index(
    field_name="sparse_vector",
    index_type="SPARSE_INVERTED_INDEX",
    metric_type="BM25",
    params={"inverted_index_algo": "DAAT_MAXSCORE", "bm25_k1": 1.2, "bm25_b": 0.75},
)

client.create_collection(collection_name=COLLECTION_NAME, schema=schema, index_params=index_params)

# Insert sample documents
docs = [
    "my first test doc",
    "my second test doc",
    "my first test doc. Milvus is an open-source vector database built for GenAI applications.",
    "my second test doc. Milvus is an open-source vector database that suits AI applications "
    "of every size from running a demo chatbot to building web-scale search.",
]
client.insert(collection_name=COLLECTION_NAME, data=[{"text": t} for t in docs])
print(f"✓ Collection created with {len(docs)} documents\n")

# Helper for search params
SEARCH_PARAMS = {"metric_type": "BM25", "params": {"drop_ratio_search": 0.0}}

# Expected output:
# ✓ Collection created with 4 documents

В этом примере показано, как выделить условия поиска в полнотекстовом поиске BM25.

  • В полнотекстовом поиске BM25 в качестве поискового термина используется "test".

  • Выделитель обводит все вхождения слова "test" тегами { и }.

highlighter = LexicalHighlighter(
    pre_tags=["{"],
    post_tags=["}"],
    highlight_search_text=True,  # Highlight BM25 query terms
)

results = client.search(
    collection_name=COLLECTION_NAME,
    data=["test"],
    anns_field="sparse_vector",
    limit=10,
    search_params=SEARCH_PARAMS,
    output_fields=["text"],
    highlighter=highlighter,
)

for hit in results[0]:
    print(f"  {hit.get('highlight', {}).get('text', [])}")
print()

Ожидаемый результат

['{test} doc']
['{test} doc']
['{test} doc. Milvus is an open-source vector database built for GenAI applications.']
['{test} doc. Milvus is an open-source vector database that suits AI applications of every size from run']

Пример 2: Выделение терминов запроса при фильтрации

В этом примере показано, как выделить термины, соответствующие фильтру TEXT_MATCH.

  • В полнотекстовом поиске BM25 в качестве термина запроса используется "test".

  • Параметр queries добавляет "my doc" в список выделения.

  • Выделитель обводит все совпадающие термины ("my", "test", "doc") с { и }

highlighter = LexicalHighlighter(
    pre_tags=["{"],
    post_tags=["}"],
    highlight_search_text=True,   # Also highlight BM25 term
    highlight_query=[                     # Additional TEXT_MATCH terms to highlight
        {"type": "TextMatch", "field": "text", "text": "my doc"},
    ],
)

results = client.search(
    collection_name=COLLECTION_NAME,
    data=["test"],
    anns_field="sparse_vector",
    limit=10,
    search_params=SEARCH_PARAMS,
    output_fields=["text"],
    highlighter=highlighter,
)

for hit in results[0]:
    print(f"  {hit.get('highlight', {}).get('text', [])}")
print()

Ожидаемый результат

['{my} first {test} {doc}']
['{my} second {test} {doc}']
['{my} first {test} {doc}. Milvus is an open-source vector database built for GenAI applications.']
['{my} second {test} {doc}. Milvus is an open-source vector database that suits AI applications of every siz']

Пример 3: Возвращение фрагментов выделения

В этом примере запрос ищет "Milvus" и возвращает фрагменты выделения со следующими настройками:

  • fragment_offset сохраняет до 20 символов перед первым выделенным фрагментом в качестве ведущего контекста (по умолчанию 0).

  • fragment_size ограничивает каждый фрагмент примерно 60 символами (по умолчанию 100).

  • num_of_fragments ограничивает количество возвращаемых фрагментов на одно текстовое значение (по умолчанию 5).

highlighter = LexicalHighlighter(
    pre_tags=["{"],
    post_tags=["}"],
    highlight_search_text=True,
    fragment_offset=20,  # Keep 20 chars before match
    fragment_size=60,    # Max ~60 chars per fragment
)

results = client.search(
    collection_name=COLLECTION_NAME,
    data=["Milvus"],
    anns_field="sparse_vector",
    limit=10,
    search_params=SEARCH_PARAMS,
    output_fields=["text"],
    highlighter=highlighter,
)

for i, hit in enumerate(results[0]):
    frags = hit.get('highlight', {}).get('text', [])
    print(f"  Doc {i+1}: {frags}")
print()

Ожидаемый результат

Doc 1: ['my first test doc. {Milvus} is an open-source vector database ']
Doc 2: ['my second test doc. {Milvus} is an open-source vector database']

Пример 4: Выделение нескольких запросов

При поиске по нескольким запросам в полнотекстовом поиске BM25 результаты каждого запроса выделяются независимо. Результаты первого запроса содержат подсветку для его поискового запроса, результаты второго запроса содержат подсветку для его поискового запроса и так далее. Каждый запрос использует одну и ту же конфигурацию highlighter, но применяет ее независимо.

В примере ниже:

  • Первый запрос выделяет "test" в своем наборе результатов.

  • Второй запрос выделяет "Milvus" в своем наборе результатов.

highlighter = LexicalHighlighter(
    pre_tags=["{"],
    post_tags=["}"],
    highlight_search_text=True,
)

results = client.search(
    collection_name=COLLECTION_NAME,
    data=["test", "Milvus"],  # Two queries
    anns_field="sparse_vector",
    limit=2,
    search_params=SEARCH_PARAMS,
    output_fields=["text"],
    highlighter=highlighter,
)

for nq_idx, hits in enumerate(results):
    query_term = ["test", "Milvus"][nq_idx]
    print(f"  Query '{query_term}':")
    for hit in hits:
        print(f"    {hit.get('highlight', {}).get('text', [])}")
print()

Ожидаемый результат

Query 'test':
  ['{test} doc']
  ['{test} doc']
Query 'Milvus':
  ['{Milvus} is an open-source vector database built for GenAI applications.']
  ['{Milvus} is an open-source vector database that suits AI applications of every size from running a dem']

Пример 5: Пользовательские HTML-теги

Для подсветки можно использовать любые теги, например HTML-безопасные теги для веб-интерфейсов. Это удобно при отображении результатов поиска в браузере.

highlighter = LexicalHighlighter(
    pre_tags=["<mark>"],
    post_tags=["</mark>"],
    highlight_search_text=True,
)

results = client.search(
    collection_name=COLLECTION_NAME,
    data=["test"],
    anns_field="sparse_vector",
    limit=2,
    search_params=SEARCH_PARAMS,
    output_fields=["text"],
    highlighter=highlighter,
)

for hit in results[0]:
    print(f"  {hit.get('highlight', {}).get('text', [])}")
print()

Ожидаемый результат

['<mark>test</mark> doc']
['<mark>test</mark> doc']