ГлавнаяБлогПамять ИИ-агентов: как проверять факты и устранять галлюцинации
AI / Нейросети

Память ИИ-агентов: как проверять факты и устранять галлюцинации

Как проверить память ИИ-агентов и устранить галлюцинации. Эксперимент с контаминацией памяти, механизмы отзыва и верификации. Читайте, чтобы защитить свои системы.

Al
Редакция Algolitalgolit.ru
12 мин чтения12 августа 2026 г.

Почему память ИИ-агентов может врать

Мы часто говорим о том, чтобы дать ИИ-агентам постоянную память — построить «второй мозг» или «операционную систему знаний», где агенты могут записывать решения и получать контекст. Но что происходит, когда эта память ошибочна? Я исследовал разрыв между механическим исполнением (агент вызвал инструмент, код скомпилировался, код возврата 0) и семантической истиной (вывод, сделанный из этого исполнения, на самом деле корректен). Легко предположить, что если механический слой надёжен, то и семантический последует за ним. Но я начал подозревать, что это опасное допущение.

Чтобы проверить это, я провёл контролируемый эксперимент на своём MCP-сервере codebase-intelligence (Python, 50K строк кода), в котором есть IntelligenceStore — слой постоянной памяти, где агенты могут записывать инциденты и собирать Architectural Decision Records (ADR). Я хотел узнать: если память агента отравлена смесью истинных и ложных фактов, проверит ли он их по коду или слепо доверится памяти?

Эксперимент: контаминация памяти

Я построил детерминированного прокси-агента и запустил его на контролируемом наборе фактов. Важное замечание о методологии: я не подключал живую LLM, поэтому использовал детерминированного прокси-агента на основе эвристик. Это означает, что результаты измеряют структурную способность системы, а не психологическое поведение живой модели. Живая модель может быть ленивее или умнее — я всё ещё пытаюсь это выяснить.

Настройка эксперимента

Я внедрил 50 фактов в изолированное хранилище памяти:

  • 25 истинных фактов (реальные архитектурные детали, сопоставленные с кодовой базой).
  • 25 ложных фактов, разделённых на две категории:
    • CONTRADICT (22): ложные факты, которые код явно опровергает (например, «мы используем Redis», хотя Redis отсутствует, но код явно использует DuckDB).
    • SILENT (3): правдоподобные ложные факты о внешних системах, о которых код полностью молчит (например, «мы используем Celery для фоновых задач», хотя в репозитории нет очереди задач).

Я протестировал три конфигурации агентов:

  • B (без памяти): базовый уровень, должен полагаться только на поиск по коду.
  • A_code_first (честный агент): сначала проверяет код, память использует только как вторичный контекст.
  • A_memory_first (ленивый агент): сначала читает память. Если находит ответ, прекращает поиск.

Для научной строгости эксперимент был повторён с независимым набором фактов (N=50) и проверен по 6 осям (включая аудит таблицы истинности и независимый аудит LLM «свежим взглядом»). Результаты были идентичны.

Первоначальные результаты

Вот как я интерпретировал эти цифры:

  • Ленивый агент доверяет отравленной памяти: конфигурация A_memory_first, которая отражает поведение многих оптимизирующих токены продакшн-агентов, приняла 100% ложных фактов. Если память говорила «мы используем RabbitMQ», агент доверял ей и переставал смотреть на код.
  • Ловушка SILENT-фактов: даже «честный агент» имел 12% принятия. Это происходило полностью на SILENT-фактах. Когда факт ложен, но код явно не кричит «НЕТ», память заполняет пустоту уверенной галлюцинацией. Память превращает честное состояние UNKNOWN в структурную догадку.
  • Ограничение «только добавление»: когда честный агент осознавал, что память ошибочна (способность к исправлению = 1.0), он ничего не мог с этим сделать. Я выполнил grep по delete или refute в API хранилища памяти. Ноль результатов. Система памяти была чисто аддитивной. Ложный факт оставался в базе данных, отравляя будущие сессии.

Первое исправление: тестирование жизненного цикла отзыва

Текущий отраслевой консенсус для слоёв доверия «ОС знаний» — использовать временные метки, приоритет источников и отношения supersedes/contradicts. Мой первоначальный эксперимент показал, что этого недостаточно. Временные метки и ссылки «supersedes» решают только историю на уровне узлов. Если ADR заменён, узел памяти обновляется, но нижестоящий код, тесты и документация, созданные на основе старого предположения, всё ещё находятся в графе. Они структурно устарели, но поисковый движок продолжает их подтягивать. Я предположил, что нужен явный переход состояния: VERIFIED → REFUTED.

Я реализовал механизм RetractionReceipt в своей системе:

  • Перечисление статусов: каждый узел памяти получает статус (ACTIVE, VERIFIED, REFUTED).
  • Жёсткая фильтрация: конвейер извлечения (load_memory) жёстко фильтрует всё, что не является ACTIVE или VERIFIED.
  • Явный инструмент отзыва: MCP-инструмент (intel_retract_memory_node) позволяет агенту активно помечать и аннулировать воспоминания, когда они противоречат живой кодовой базе.

Я снова запустил эксперимент (Эксперимент 1-R). Честному агенту разрешалось использовать инструмент отзыва в сессии 1. Затем в сессии 2 запускался свежий memory_first агент для чтения памяти после отзыва.

Результаты отзыва

Жизненный цикл отзыва сработал. Уровень принятия ленивого агента упал со 100% до 12%. Постоянные ложные факты сократились на 88%, а размер токенового контекста уменьшился на 45%, потому что опровергнутые факты отфильтровывались до попадания в LLM.

Честное ограничение: почему не упало до нуля

Мой ADR предсказывал, что принятие упадёт до 0. Этого не произошло. Оно упало до 0.12. Оставшиеся 12% были SILENT-фактами. Явный статус REFUTED требуется, чтобы программно исключить зависимые компоненты из конвейера извлечения. Но это работает только при наличии противоречащего сигнала в коде. Если память утверждает «мы используем Celery», а кодовая база вообще не упоминает Celery, у агента нет доказательств для запуска отзыва. Чтобы достичь нуля, я понял, что нужна «проверка при чтении» — механизм, который оспаривает утверждение памяти против кодовой базы, даже когда код молчит.

Закрытие разрыва: проверка при чтении

Я реализовал ленивый слой валидации (ADR-0003). Когда load_memory() извлекает узел, он извлекает лёгкие «якоря» из текста памяти (например, имена файлов, операторы импорта, переменные окружения). Затем он проверяет, существуют ли эти якоря в живом слепке кодовой базы (текущий git HEAD).

  • Если якорь найден в коде → статус становится VERIFIED.
  • Если код явно противоречит якорю (или якорь полностью отсутствует, когда должен присутствовать) → статус становится REFUTED.
  • Если не удаётся определить → статус остаётся ACTIVE (рассматривается как INCONCLUSIVE).

Я запустил эксперимент в последний раз (Эксперимент 1-V) с этим активным слоем. Чтобы предотвратить всплески задержки, валидация работает в строгом бюджете 50 мс на извлечение, с TTL-кэшем на git HEAD 30 секунд, так что чтение в стабильном состоянии стоит почти ничего.

Финальные результаты

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

Оставшиеся честные ограничения

Я не буду притворяться, что это идеальное серебряное пуленепробиваемое решение. Эксперимент выявил два крайних случая:

  • «Ловушка присутствия»: если ложная память утверждает «мы используем sqlite3», а sqlite3 случайно импортируется где-то в кодовой базе по несвязанной причине, слой проверки видит токен и помечает память как VERIFIED. Ленивый агент (A_memory_first) всё ещё попадался на это, что привело к уровню принятия 0.16. (Честный агент избежал этого, потому что читал контекст кода вокруг импорта).
  • Типизация якорей: при извлечении якорей из прозы (например, «мы используем fastmcp») система изначально не замечала, что фактический импорт Python был from mcp.server.fastmcp import .... Это вызывало некоторые ложные вердикты REFUTED на истинных фактах. Исправление — захват типизированных якорей на пути записи (когда память создаётся), а не попытка разобрать их из необработанного текста на пути чтения.

Практический вывод: что делать прямо сейчас

Построение надёжных ИИ-систем — это не просто предоставление им большего контекста. Это признание того, что память имеет жизненный цикл. Если ваша система не может программно опровергнуть память, ложные факты накапливаются и отравляют окно контекста со временем. Внедрение явного перехода состояния VERIFIED → REFUTED резко снижает контаминацию и экономит токены. Кроме того, добавление слоя «проверки при чтении» закрывает последний разрыв на «тихих» галлюцинациях, доводя контаминацию честного агента до нуля без значительной задержки.

Однако семантический дрейф остаётся сложной проблемой. Механическая проверка всё ещё может быть обманута «ловушками присутствия», если агент не читает окружающий контекст. Следующий шаг — перенести извлечение якорей на путь записи, чтобы воспоминания создавались со строгими проверяемыми ссылками с самого начала.

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

Если ваша система обрабатывает семантический дрейф иначе или вы решили проблему «ловушки присутствия», я искренне хотел бы услышать, как вы к этому подходите.

#память ИИ-агентов#галлюцинации LLM#верификация фактов#контаминация памяти#MCP
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →