Геометрический метод обнаружения галлюцинаций в LLM без моделей-судей | AiManual
AiManual Logo Ai / Manual.
17 Янв 2026 Гайд

Когда LLM врет: отлавливаем галлюцинации линейкой в векторном пространстве

Практическое руководство: как находить ложь в ответах LLM через анализ геометрии эмбеддингов. Без дополнительных моделей, только векторы и математика.

Зачем это вообще нужно?

Попроси GPT рассказать про историю вымышленного города - получишь связный текст с датами, именами и событиями. Красиво. Убедительно. Полная чушь.

Традиционный подход к детекции галлюцинаций - LLM-as-judge. Запускаем одну модель, получаем ответ, кормим его другой модели со словами "проверь, не врет ли здесь первая". Циркулярная логика уровня "вор у вора дубинку украл".

LLM-судьи часто повторяют ошибки проверяемых моделей. Если GPT склонна галлюцинировать про медицинские факты, Claude с высокой вероятностью пропустит эти же ошибки.

Геометрический метод работает иначе. Мы не спрашиваем "правда ли это?". Мы смотрим на внутреннюю структуру ответа. Если в тексте есть логические разрывы, семантические скачки или внутренние противоречия - это отражается в геометрии его векторного представления.

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

Как это работает на пальцах

Возьмем ответ LLM. Разобьем на предложения. Каждое предложение превратим в эмбеддинг (вектор). Получим последовательность точек в пространстве.

Теперь считаем расстояния между соседними точками. В идеальном тексте эти расстояния примерно одинаковы - мы движемся плавно от темы к теме. В тексте с галлюцинациями появляются рывки: модель "соскочила" с фактов на вымысел.

💡
Метод основан на гипотезе локальной согласованности: в правдивом тексте соседние семантические блоки связаны. В выдуманном - связь нарушается там, где автор (LLM) начинает импровизировать.

1 Готовим данные: от текста к векторам

Сначала нужен эмбеддер. Не гигантская LLM, а специализированная модель для векторных представлений. Sentence-BERT, E5, BGE - что угодно, что превращает текст в вектор фиксированной размерности.

from sentence_transformers import SentenceTransformer
import numpy as np

# Загружаем легковесный эмбеддер
model = SentenceTransformer('all-MiniLM-L6-v2')  # 384 измерения, быстро работает

# Разбиваем текст на предложения
import nltk
nltk.download('punkt')
text = """Илон Маск родился в 1971 году в Претории. 
Он основал SpaceX в 2002 году. 
В 2025 году он высадился на Марсе и основал первую колонию."""

sentences = nltk.sent_tokenize(text)
print(f"Предложений: {len(sentences)}")
# Вывод: Предложений: 3

# Получаем эмбеддинги
embeddings = model.encode(sentences)
print(f"Размерность векторов: {embeddings.shape}")
# Вывод: Размерность векторов: (3, 384)

2 Считаем расстояния: ищем аномалии

Теперь анализируем геометрию. Простейшая метрика - косинусное расстояние между соседними эмбеддингами.

from scipy.spatial.distance import cosine

distances = []
for i in range(len(embeddings) - 1):
    dist = cosine(embeddings[i], embeddings[i + 1])
    distances.append(dist)

print(f"Расстояния между предложениями: {distances}")
# Пример вывода: [0.15, 0.62]

Видите скачок? Первое расстояние 0.15 (нормальная семантическая связь между фактами о Маске). Второе - 0.62 (резкий скачок, потому что третье предложение - вымысел).

Не используйте евклидово расстояние для эмбеддингов! В высокомерных пространствах оно ведет себя плохо. Косинусное или скалярное произведение работают надежнее.

3 Ставим диагноз: пороговое значение

Как понять, что расстояние 0.62 - это галлюцинация, а не просто смена темы? Нужен baseline.

Соберите набор правдивых текстов по вашей тематике. Посчитайте распределение расстояний между их предложениями. 95-й перцентиль этого распределения - ваш порог.

# Пример расчета порога
baseline_distances = []  # Заполните расстояниями из правдивых текстов

threshold = np.percentile(baseline_distances, 95)
print(f"Порог аномалии: {threshold:.3f}")

# Проверяем наше третье предложение
anomaly_score = distances[1]  # 0.62
is_hallucination = anomaly_score > threshold
print(f"Аномалия: {anomaly_score:.3f}, Галлюцинация: {is_hallucination}")

Продвинутые метрики: не только расстояния

Расстояние между соседями - базовая метрика. Но есть более хитрые способы.

Кривизна траектории: считаем не просто расстояние A→B, а смотрим на угол между векторами A→B и B→C. Резкий поворот - признак семантического скачка.

Локальная плотность: для каждого предложения находим k ближайших соседей внутри текста. В связном тексте соседи будут рядом в последовательности. В тексте с галлюцинациями предложение-вымысел окажется "оторвано" от контекста.

Скалярное произведение с промптом: эмбеддинг каждого предложения сравниваем с эмбеддингом исходного запроса. Если предложение уходит далеко от темы запроса - подозрительно.

def calculate_curvature(embeddings, window=3):
    """Считаем кривизну траектории через углы между векторами"""
    curvatures = []
    
    for i in range(1, len(embeddings) - 1):
        # Вектор от i-1 к i
        vec1 = embeddings[i] - embeddings[i-1]
        # Вектор от i к i+1
        vec2 = embeddings[i+1] - embeddings[i]
        
        # Косинус угла между векторами
        cos_sim = np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
        # Кривизна = 1 - косинус (0 для прямого пути, 1 для поворота на 90°)
        curvature = 1 - cos_sim
        curvatures.append(curvature)
    
    return curvatures

# Пример использования
curvatures = calculate_curvature(embeddings)
print(f"Кривизна в точках перехода: {curvatures}")

Где метод ломается (и как это чинить)

1. Короткие тексты: на 2-3 предложениях статистика не работает. Решение - анализировать не предложения, а смысловые блоки по 2-3 предложения.

2. Намеренные переходы: автор может специально делать резкую смену темы. Это не галлюцинация, а стилистический прием. Решение - обучать модель на примерах таких переходов или использовать домен-специфичные пороги.

3. Консистентные галлюцинации: если LLM систематически врет в одном стиле (например, всегда придумывает даты), эти вымыслы могут образовывать свою собственную "гладкую" траекторию. Тут нужен внешний факт-чекинг.

💡
Сочетайте геометрический метод с другими подходами. Например, сначала отсеиваем явные выбросы через анализ эмбеддингов, потом проверяем подозрительные фрагменты через поиск в векторной базе знаний.

Практический кейс: проверка медицинских ответов

Допустим, мы делаем медицинского чат-бота. Пользователь спрашивает про симптомы гриппа. LLM отвечает:

  1. Грипп проявляется температурой, кашлем, слабостью.
  2. Инкубационный период 1-4 дня.
  3. Для профилактики принимайте витамин С и антибиотики.
  4. При температуре выше 40℃ вызывайте скорую.

Третье предложение - галлюцинация (антибиотики не работают против вирусов). Давайте поймаем ее.

medical_response = [
    "Грипп проявляется температурой, кашлем, слабостью.",
    "Инкубационный период 1-4 дня.",
    "Для профилактики принимайте витамин С и антибиотики.",
    "При температуре выше 40℃ вызывайте скорую."
]

# Получаем эмбеддинги
med_embeddings = model.encode(medical_response)

# Считаем расстояния
distances = []
for i in range(len(med_embeddings) - 1):
    dist = cosine(med_embeddings[i], med_embeddings[i + 1])
    distances.append(dist)

print(f"Медицинский кейс - расстояния: {[f'{d:.3f}' for d in distances]}")
# Вывод: [0.112, 0.453, 0.098]

Второе расстояние (0.453) явно выделяется. Это переход от фактов о гриппе к опасной рекомендации. Порог для медицинских текстов (посчитанный на достоверных источниках) около 0.3 - значит, ловим галлюцинацию.

Интеграция в пайплайн

Геометрический метод идеально встает в pre-filtering стадию:

  • LLM генерирует ответ
  • Анализируем эмбеддинги, находим подозрительные фрагменты
  • Только эти фрагменты отправляем на дорогую проверку (поиск в знаниях, факт-чекинг)
  • Или просто маркируем: "эта часть ответа требует проверки"

Стоимость обработки? Копейки. Эмбеддеры в сотни раз легче LLM. Обработка 1000 ответов стоит меньше запроса к GPT-4.

Метод Точность Стоимость Задержка
LLM-as-judge (GPT-4) Высокая $$$ 2-5 сек
Факт-чекинг по API Очень высокая $$$$ 10+ сек
Геометрический метод Средняя ¢ <0.1 сек

Что дальше? Эволюция метода

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

Второе направление - мультимодальность. Если LLM генерирует ответ с картинками, таблицами, кодом - анализируем согласованность между модами. Эмбеддинг текста про "рост продаж" должен быть близок к эмбеддингу графика, который идет следом. Если нет - подозрительно.

Третье - временные ряды. В диалоге каждый ответ LLM можно рассматривать как точку. Диалог с галлюцинациями будет "скакать" по семантическому пространству. Диалог с фактами - двигаться по предсказуемой траектории.

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

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