«Покажите похожие инструкции, но без архивных обходных путей»: поиск по примерам в Qdrant

Инженер открывает в базе знаний два подходящих руководства по восстановлению связи. Одно написано как краткая памятка, другое описывает полную процедуру для площадки. В прошлой выдаче рядом с ними были старые обходные способы, которые инженер пометил как неподходящие. Теперь он просит показать ещё источники. Поиск по одному текстовому запросу плохо передаёт, что оба формата устраивают, а документы про обходной способ следует опустить.

Как поисковик использует примеры

Возьмём векторы двух удачных источников и одного неподходящего. Если интерфейс передаёт идентификаторы точек, Qdrant сам найдёт их векторы. Мы получим новые кандидаты из той же коллекции. В клиенте для этого используется query_points с RecommendQuery. Векторы примеров и кандидатов должны описывать документы в одном представлении. Если проиндексировать часть документов другой моделью, сравнение потеряет общий смысл даже при одинаковой размерности.

У стратегии average_vector положительные примеры сворачиваются в средний вектор. При наличии отрицательных запрос смещается от их среднего. Такой запрос удобен, если мы ищем общую область сходства. Когда удачные примеры относятся к двум разным группам, их центр может лучше соответствовать общему обзору, чем любой из нужных типов инструкций.

best_score сравнивает кандидата с каждым примером. Для него важны лучшее положительное и лучшее отрицательное совпадения. Если положительное сильнее, кандидат получает положительную оценку, иначе отрицательную. Кандидат может хорошо совпасть с одной из двух удачных инструкций, даже если второй он совсем не похож. При большом числе примеров сравнения дорожают, поэтому начните с небольшого числа показательных документов.

Сравним два порядка выдачи

Запустите пример с пакетом qdrant-client==1.20.0. Коллекция работает локально в памяти. Три координаты условно передают сходство с памяткой, регламентом и обходным способом. Документ «Обзор» похож сразу на оба положительных примера. Две новые инструкции почти совпадают каждая со своим примером. Обходной способ с нужной терминологией имеет небольшую общность с памяткой, но ближе к отрицательному образцу.

from qdrant_client import QdrantClient, models as m

client = QdrantClient(":memory:")
collection = "guides"
client.create_collection(
    collection, vectors_config=m.VectorParams(size=3, distance=m.Distance.COSINE)
)
vectors = {
    1: ("Пример памятки", [1.0, 0.0, 0.0]),
    2: ("Пример регламента", [0.0, 1.0, 0.0]),
    3: ("Пример обхода", [0.0, 0.0, 1.0]),
    4: ("Новая памятка", [0.98, 0.0, 0.1]),
    5: ("Новый регламент", [0.0, 0.96, 0.12]),
    6: ("Обзор", [0.70, 0.70, 0.04]),
    7: ("Новый обход", [0.4, 0.0, 0.91]),
}
client.upsert(collection, [
    m.PointStruct(id=i, vector=v, payload={"title": title})
    for i, (title, v) in vectors.items()
])

def recommend(strategy):
    points = client.query_points(
        collection,
        query=m.RecommendQuery(
            recommend=m.RecommendInput(positive=[1, 2], negative=[3], strategy=strategy)
        ),
        limit=4, with_payload=True,
    ).points
    print(strategy.value, [(p.payload["title"], round(p.score, 3)) for p in points])
    return points

average = recommend(m.RecommendStrategy.AVERAGE_VECTOR)
best = recommend(m.RecommendStrategy.BEST_SCORE)
assert average[0].id == 6 and {p.id for p in best[:2]} == {4, 5}
assert best[-1].id == 7 and best[-1].score < 0
client.close()

На первом месте при усреднении появляется обзор. При best_score первыми становятся обе инструкции. Последний результат получает отрицательную оценку: он ближе к отрицательному примеру. Сами точки, переданные как примеры, Qdrant исключает из результатов этого запроса. Так можно предлагать новые документы после уже просмотренных.

Где провести границу между фильтром и оценкой

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

На реальной базе сохраните несколько сеансов с двумя различающимися подходящими источниками и явным отрицательным. Для каждого прогона отметьте, сколько новых инструкций из обеих групп вышло в первые позиции. Сравните с обычным поиском по запросу и обеими стратегиями рекомендаций. Если затем результаты пойдут в RAG, проверьте, что переданные фрагменты действительно отвечают на исходный вопрос. Обратная связь о похожести может вернуть хорошие документы по смежной теме.

Практика на Latorn

Откройте пробный урок «Qdrant на практике», чтобы разобраться с коллекцией и поисковыми запросами перед экспериментом со своими документами.

В платном курсе «RAG на Python» вы настроите поиск в Qdrant и фильтры, сравните выдачу с гибридным поиском и подключите выбранные источники к LLM. Посмотреть программу и практикум по Qdrant.

Продолжить на практике

  • RAG на Python: векторный поиск и ответы по своим документам ↗

    Создавайте поиск и LLM-ассистентов по своим документам. Практический курс RAG на Python: FAISS, Qdrant, Weaviate, гибридный поиск и проверка качества. Научитесь находить нужные фрагменты и подключать их к модели. Есть практикумы и пробные уроки.