RAG ищет оборудование на 48 вольт: числовые ограничения после смыслового поиска

Инженер просит подобрать блок питания для линии на 48 вольт. В ответе RAG появляется устройство с выходом 24 вольта: его описание похоже на вопрос, а в длинной карточке несколько раз встречается число 48. Оно относится к габариту корпуса. Ассистент получил правдоподобный текст, но не проверил электрическое условие. Для такой задачи смыслового сходства недостаточно: числовой допуск должен стать отдельным правилом отбора.

Разберём узкую задачу: найти устройства, у которых допустимое входное напряжение включает 48 V DC. Мы не решаем всю задачу совместимости оборудования. Полярность, ток, класс защиты и условия эксплуатации потребуют собственных условий. Однако уже одна корректная проверка диапазона устраняет ошибку, которую трудно надёжно исправить просьбой «будь внимательнее» в промпте.

Подготовьте поля до вопроса пользователя

В карточке стоит хранить input_min_v и input_max_v числами, а input_kind отдельным значением DC или AC. Исходную строку «36–60 V DC» сохраните рядом с полями, чтобы инженер мог проверить преобразование. Размер корпуса, выходное напряжение и входное напряжение должны иметь разные названия. Поле voltage без указания назначения станет источником новых ошибок.

При загрузке нормализуйте единицы: 0.048 kV превращается в 48 V, а десятичная запятая разбирается по принятому формату источника. Отсутствующее значение нельзя заменять нулём. Если диапазон не удалось прочитать, карточка получает состояние «нужна проверка» и не проходит строгий подбор. Условие с пустым полем не должно случайно стать разрешением.

Ниже дан локальный этап отбора после получения кандидатов. Оценка similarity изображает уже выполненный смысловой поиск и задаётся вручную; новая модель здесь не вызывается. Самый похожий кандидат рассчитан на другое напряжение. Два других проходят по диапазону, но имеют разные оценки. У карточки с неизвестным допуском отсутствует нижняя граница.

cards = [
    {"id": "supply-a", "input_min_v": 18, "input_max_v": 30, "input_kind": "DC", "similarity": .95},
    {"id": "supply-b", "input_min_v": 36, "input_max_v": 60, "input_kind": "DC", "similarity": .82},
    {"id": "supply-c", "input_min_v": 48, "input_max_v": 48, "input_kind": "DC", "similarity": .75},
    {"id": "unknown", "input_min_v": None, "input_max_v": 60, "input_kind": "DC", "similarity": .90},
]
def compatible(card, voltage):
    low, high = card["input_min_v"], card["input_max_v"]
    return (card["input_kind"] == "DC" and low is not None
            and high is not None and low <= voltage <= high)
accepted = sorted((c for c in cards if compatible(c, 48)),
                  key=lambda c: c["similarity"], reverse=True)
assert not compatible(cards[1], 35.9)
assert compatible(cards[1], 36) and compatible(cards[1], 60)
assert not compatible(cards[1], 60.1)
assert not compatible(dict(cards[1], input_kind="AC"), 48)
print([c["id"] for c in accepted])

Вывод содержит supply-b и supply-c, именно в таком порядке. Блок supply-a с наибольшим сходством отброшен: 48 больше его верхней границы 30. Supply-b включает 48 в диапазон 36–60, а supply-c допускает ровно 48. Границы включены с обеих сторон, что соответствует выбранному правилу. Запись unknown исключена, поскольку её совместимость нельзя установить по имеющимся данным.

Перенесите правило ближе к хранилищу

В базе с поддержкой фильтров лучше ограничить кандидатов во время поиска. Для числовых фильтров Qdrant используются условия диапазона. Наше правило выражается как input_min_v меньше либо равно 48 и input_max_v больше либо равно 48 вместе с точным совпадением input_kind. Данные должны быть числами: строка «48» не заменяет числовое значение для диапазонного условия.

Почему это влияет на полноту? Если сначала получить только пять наиболее похожих карточек, а затем отсеять четыре несовместимые, подходящие устройства за пределами первой пятёрки не появятся. Фильтрация внутри поиска даёт ему возможность искать среди допустимых записей. Если используемое хранилище не умеет такой отбор, увеличивайте пул кандидатов осознанно и измеряйте, сколько совместимых карточек остаётся после проверки.

Пользовательский запрос для генерации после отбора: «Сравни подходящие блоки для входной линии 48 V DC. Покажи диапазон входа и источник каждого значения. Если для окончательного выбора не хватает тока или условий среды, спроси их». Передайте модели только разрешённые карточки с идентификаторами и исходными строками характеристик. Модель объяснит различия, но список допустимых вариантов уже определён проверяемым правилом.

Что проверять при изменении каталога

В тестовом наборе нужны точная граница, значение чуть ниже и чуть выше допуска, AC вместо DC и неизвестная характеристика. Отдельно проверьте импорт карточки с киловольтами. Если ошибка возникла при извлечении характеристики, исправляйте загрузчик и повторно обрабатывайте затронутые записи. Изменение системного промпта не восстановит неверное число в метаданных.

В платном курсе «LangChain 1.0.x: с нуля до продакшн LLM-приложений» вы будете собирать RAG с гибридным поиском и реранжированием и измерять его качество. Следующая задача — включить проверяемые ограничения в настоящий поиск и оценить, получает ли генератор достаточный набор подходящих источников. Откройте программу курса и продолжите от локального фильтра к работающему приложению.

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

  • LangChain 1.0.x: c нуля до продакшн LLM-приложений ↗

    Практический курс по LangChain. Соберёте продакшн LLM-бота: RAG по PDF с цитатами, веб-поиск и трекинг заказов, многошаговый агент для анализа CSV и финальное веб-приложение (UI + API). Плюс профессиональные блоки: PromptOps и версионирование промптов, метрики качества RAG, гибридный поиск с реранком, observability/стоимость, надёжность (ретраи, таймауты), guardrails и отдельный модуль по LangGraph для устойчивых workflow. P.S. Полностью обновили материалы на актуальную версию LangChain