Гайд · TNWS AI
Как найти релевантный фрагмент через semantic_search в Sentence Transformers
Кодируем вопрос и документы, связываем corpus_id с текстом, проверяем top_k и строим отказ при отсутствии подходящего источника.
Задача и применимость
Поисковый запрос: «как найти нужный фрагмент в документах util.semantic_search». Ниже одна практическая задача для Python с библиотекой Sentence Transformers 6.1.0, опубликованной 18 сентября 2026 года, и checkpoint sentence-transformers/all-MiniLM-L6-v2. Пример на английских предложениях выбран потому, что поддержка русского выбранной демонстрационной моделью не должна подразумеваться автоматически. Для реального русского корпуса подберите подтверждённую карточкой языков модель и повторите оценку. Внешний поиск, генерация LLM и принятие бизнес-решения остаются отдельными этапами.
Фактическая основа: util.semantic_search(query_embeddings, corpus_embeddings, top_k) возвращает для каждого запроса список словарей с corpus_id и score. Из индекса corpus_id необходимо вернуться к исходному корпусу; функция не генерирует готовый ответ и не фильтрует разрешения доступа. Точный прогноз значений векторных оценок без запуска весов модели не обещается. Примеры кода ниже сверены с текущей официальной документацией и проверены на синтаксис Python; в этом окружении сами веса не загружались. При обновлении модели или зависимости сначала повторите контрольный пример.
Нумерованная инструкция
- Подготовьте маленький корпус с ID и тестовым вопросом, у которого есть один известный источник.
- Закодируйте документы и вопросы тем же checkpoint.
- Вызовите util.semantic_search с top_k меньше числа документов.
- Сопоставьте каждый corpus_id с тем текстом, который действительно был закодирован.
- Проверьте нерелевантный запрос и не допускайте ответ без проверенного источника.
Подготовьте изолированное окружение и установите зафиксированный пакет: pip install "sentence-transformers==6.1.0". Библиотека потребует подходящих зависимостей и свободной памяти под выбранные веса; установка пакета не скачивает заранее любую модель. Перед тестом напечатайте установленную версию и зафиксируйте имя checkpoint. Не отправляйте в примеры клиентские имена, закрытые документы или ключи доступа.
Готовый шаблон
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
docs = [("R7", "Returns are accepted for 14 days."),
("D9", "Delivery is available on weekdays."),
("P2", "Payment is possible by card.")]
queries = ["What is the return period?"]
hits = util.semantic_search(model.encode(queries),
model.encode([text for _, text in docs]), top_k=2)
for hit in hits[0]:
print(docs[hit["corpus_id"]][0], hit["score"],
docs[hit["corpus_id"]][1])
Запускайте первый раз на очень маленьком обезличенном наборе. Для каждой строки и документа сохраняйте стабильный ID: иначе невозможно выяснить, почему нейросеть выбрала конкретную запись. Если вывод отличается от ожидаемой формы, сначала проверьте состав входа, затем конфигурацию модели и только после этого настройки ранжирования. Не маскируйте ошибки ручной подстановкой заранее «правильного» ответа.
Конкретный вход и ожидаемый результат
На входе R7 со сроком 14 дней, D9 о доставке, P2 об оплате и вопрос «What is the return period?». Ожидаемая проверка — верхний doc_id R7; API при этом вернёт до двух кандидатов, не одно готовое предложение про возврат. Если R7 не первый, сначала исследуйте качество выбора текста и модели.
Проверяйте структуру отдельно от смысла. Массив верного размера не означает хорошего поиска, а высокий score не удостоверяет факт в документе. Сохраните реальные выводы программы и сопоставьте их с заранее размеченными эталонными ID. При переносе на другой корпус обновите проверочные данные; простая замена входного предложения в коде не доказывает обобщения метода.
Разбор метода и ограничений
Удобно хранить корпус как пары (ID, текст). util.semantic_search сообщает числовую позицию corpus_id в том массиве embeddings, который передали функции. Если между кодированием и чтением результата отсортировать docs заново, метка источника станет ложной. В длительном сервисе фиксируйте snapshot корпуса или храните прямое соответствие позиции документу. Когда top_k=2, функция по определению выдаст два ближайших текста, даже если второй совершенно не подходит; модель не получает понятие «нет ответа» автоматически. Порог релевантности и отказ надо проверять отдельно на запросах без факта. Доступ к данным другого клиента также не реализован этой функцией: фильтруйте корпус на стороне приложения до передачи embeddings либо обеспечивайте правильную изоляцию на уровне хранилища.
Дополнительный сценарий проверки
Сформируйте контрольную таблицу из трёх вопросов: о возврате, о доставке и о гарантийном ремонте, которого нет. Для первых двух ожидается соответствующий ID в top-1, для третьего — отказ приложения от утверждения без основания. Даже если абстрактная модель выбрала P2 для гарантии, это не превращает P2 в источник такого ответа. Сохраните численные результаты и вручную отметьте ложные срабатывания. Отрицания, даты и артикулы проверяйте отдельным точным фильтром или reranker; смысловая близость может не различить номер договора. Если нужны тысячи и миллионы документов, этот пример с прямым поиском по матрице — лишь эталон; для быстрого продакшен-поиска выбирайте подходящий индекс и измеряйте recall относительно прямого перебора.
Критерии принятия
- corpus_id отображает исходный, а не переставленный документ.
- R7 присутствует в результатах контрольного вопроса.
- Отсутствующая гарантия не превращается в придуманную цитату.
Если один пункт не проходит, настройка не готова для массового применения. При перезапуске сервиса повторно прогоните контрольный запрос и сверяйте ID, а не только наличие числовой оценки. Разделяйте ошибки получения embeddings, поиска кандидатов, фильтрации доступа и формирования финального ответа; хорошая метрика одного этапа не подтверждает работу остальных.
Типичные ошибки и что не делать
- Не считать второй результат по top_k подтверждением факта.
- Не перемешивать docs после вычисления corpus_embeddings.
- Не забывать фильтр разрешений перед поиском.
Не обещайте точность и экономию ресурсов без измерений на своём оборудовании и корпусе. Не подменяйте документ нейросетевым выводом при объяснении решения пользователю. Помните о правах на доступ к исходным материалам: embedding не является анонимизацией приватного текста. При изменении модели, языка или способа подготовки данных повторите тесты до переключения рабочего трафика.
FAQ
Возвращает ли функция текст ответа?
Нет, возвращает ID позиции и score.
Что значит corpus_id?
Индекс в переданном массиве документов.
Можно ли ждать отказа при отсутствии факта?
Нет, отказ реализуется отдельно.
Официальные источники
Частые вопросы
Возвращает ли функция текст ответа?
Нет, возвращает ID позиции и score.
Что значит corpus_id?
Индекс в переданном массиве документов.
Можно ли ждать отказа при отсутствии факта?
Нет, отказ реализуется отдельно.
Читайте также
Как квантовать embeddings в int8 через SentenceTransformer.encode
Сравниваем тип и размер массива float32/int8, объясняем необходимость калибровки и измерения recall перед заменой хранилища.
Как найти смысловые дубли вопросов через paraphrase_mining
Ищем пары похожих вопросов, читаем позиции и score, исключаем отрицания и цифры ручной проверкой до объединения записей.
Как переранжировать результаты поиска через CrossEncoder.rank
Повторно проверяем top-k кандидатов для одного вопроса, сохраняем corpus_id и не применяем дорогую парную модель ко всей коллекции.
Комментарии
Пока тихо. Скажите первое слово