Retrieval and memory
После главы вы сможете выбрать между domain tool, retrieval и memory, а также измерить retrieval отдельно от качества final answer.
Не всё прошлое — память
| Объект | Пример | Source of truth | Типичный срок |
|---|---|---|---|
| Transcript | «Где мой заказ?» | Conversation store | Policy-dependent |
| Task state | pending approval | Event projection | До terminal/retention |
| Observation | get_order rev 5 | Domain system + event | До invalidation |
| User memory | язык ответа | Explicit preference store | До удаления/изменения |
| Episodic memory | прошлый успешный сценарий | Curated outcome | Ограниченный |
| Knowledge | cargo policy | Versioned corpus/API | По версии документа |
Сохранить transcript навсегда и назвать это memory — не архитектура: нет namespace, consent, freshness и deletion semantics.
Tool или retrieval?
API-shaped факты лучше читать domain tool: order status, balance, slots. Document-shaped знания подходят retrieval: policy, manuals, product comparison, FAQ.
точный изменяемый record → domain API/tool
неструктурированный корпус → retrieval
устойчивая user preference → scoped memory
текущий progress → task state
Поэтому RAG не обязателен каждому агенту. Для telehealth booking slots важнее API; для pharmacy/policy Q&A — retrieval.
RAG pipeline
Failure может возникнуть на каждом слое. Нельзя лечить низкий recall изменением final prompt.
Chunking
Fixed-size
Просто и воспроизводимо, но разрывает таблицы, исключения и определения.
Structural
По headings/sections/records. Лучше сохраняет смысл, требует parser для каждого формата.
Semantic/task-aware
Строится вокруг возможных вопросов или entities. Может улучшить retrieval, но дороже, сложнее обновляется и рискует закодировать текущие use cases.
Хороший chunk сохраняет provenance: source URI, version, section, effective date, access label.
Метрики retrieval
- Recall@k: доля всех релевантных документов, найденных в top-k:
|top-k ∩ relevant| / |relevant|. - Hit@k: нашёлся ли хотя бы один релевантный документ. Если релевантных два, а найден один, Hit@k равен 1, Recall@k — 0.5.
- MRR: насколько рано появился первый релевантный результат.
- nDCG: учитывает порядок и градуированную релевантность.
- Filter/authorization recall: не отрезали ли нужное; не показали ли запрещённое.
- Freshness: соответствует ли найденная версия effective policy.
Answer groundedness не заменяет retrieval eval: модель не может процитировать документ, который pipeline не нашёл.
Memory write policy
Наивная схема записывает каждую фразу пользователя. Она быстро накапливает ошибки и sensitive data.
Перед записью спросите:
- Пользователь явно выразил устойчивую preference/fact?
- Есть consent и lawful purpose?
- Каков namespace: thread, user, tenant?
- Как подтвердить/изменить/удалить record?
- Когда истекает TTL?
- Как provenance попадёт обратно в context?
Model-generated inference вроде «пользователь, вероятно, богат» не должна автоматически становиться долговечной памятью.
Namespace isolation
Helios memory store возвращает tenant-scoped records плюс records точного thread. Record tenant-alpha не виден tenant-sol; preference thread-1 не переносится в thread-2.
Production identity обычно сложнее: organization, workspace, end user, legal region, purpose and sensitivity labels. Filter должен выполняться до semantic ranking и повторно на выдаче.
Freshness и provenance
Для каждого retrieved item полезны:
- stable source ID;
- source version/effective time;
- retrieved_at;
- access scope;
- relevance score и retriever version;
- transformation/chunk lineage.
Если cargo rule обновилась, старое memory нельзя «исправить» красивым summary. Нужна invalidation/re-index и domain policy check на актуальной версии.
Deletion и retention
Удаление source требует ответа на вопросы:
- удалить chunk из index;
- удалить embeddings/cache;
- перестроить summaries, где он использован;
- сохранить минимально необходимый audit без исходного content;
- прекратить future retrieval;
- доказать completion deletion job.
Для PHI/PII de-identification снижает риск, но не превращает данные в свободный учебный corpus. Назначьте purpose, access, retention и deletion до выгрузки.
Стратегии
| Решение | Выигрыш | Цена | Когда плохой выбор |
|---|---|---|---|
| Full corpus in context | Нет retrieval miss | Tokens/latency/leakage | Большой corpus |
| Lexical search | Прозрачно, дешёво | Слабые paraphrases | Семантически разнообразные вопросы |
| Dense retrieval | Semantic recall | Model/index lifecycle | Exact IDs/codes |
| Hybrid + reranker | Обычно сильнее | Latency и eval complexity | Малый low-traffic corpus |
| Domain API | Свежий structured state | Нужен integration | Документы/объяснения |
Helios и реальный аналог
Пользователь спрашивает cargo restrictions. Order route читается tool, а нормативные исключения ищутся в versioned corpus с jurisdiction metadata. Перед booking policy service проверяет правило ещё раз; retrieved paragraph помогает объяснить, но не авторизует shipment.
Лаборатория
npm run test:course:pattern -- "memory|stale"
Добавьте memory другой tenant и убедитесь, что retrieval его не возвращает. Затем перейдите к эксперименту ниже: он отдельно показывает ошибки поиска и ограничения найденных evidence.
Лаборатория: от корпуса до выдержки с источником
Нужны Python 3.12 и uv 0.12.7; подготовка окружения. Из корня репозитория:
uv run --directory examples/helios_ml --frozen python scripts/compare_retrieval.py
Корпус synthetic-cargo-v1 содержит правила доставки, возврата, рекламный текст, старую версию правил и документ другого tenant. Source.sections задаёт уже разобранные заголовки и тексты. build_chunks сохраняет каждый раздел целиком, с source ID, версией и namespace; это structural chunking после parsing. PDF/HTML parser в лабораторию не входит.
Путь данных:
trusted source metadata → structural chunks → tenant/current filter
→ candidate retrieval → optional heading rerank → top-k → verbatim extracts
current назначается ingestion-процессом, а tenant приходит из execution context. Модель не может назначить эти значения. В учебном корпусе source IDs глобально уникальны, для одного source допускается только одна current-версия. В production нужны собственные контракты identity и effective dates.
Сравнить по одному изменению
| Метод | Представление / ранжирование | Что меняется относительно предыдущего |
|---|---|---|
word | Существующий TF-IDF по словам и биграммам | Исходный baseline |
char | TF-IDF по символьным n-граммам длины 3–5 | Только лексическое представление |
char_heading | Те же char candidates, сначала больше точных совпадений слов заголовка, затем score | Только порядок уже найденных candidates |
Все методы используют один корпус, фильтры и k. По умолчанию candidate_k=4, k=1. Reranker не читает gold labels и не может вернуть chunk вне своих candidates. Score остаётся исходным retrieval score; после перестановки его значения могут идти не по убыванию.
Это два лексических baseline и rule-based reranker. Здесь нет dense embeddings, hybrid fusion, cross-encoder или генеративной LLM. Цель первого опыта — увидеть и локализовать ошибки на прозрачной реализации, прежде чем подключать более сложные компоненты.
Ожидаемые наблюдения
| Case | Наблюдение при k=1 | Что это объясняет |
|---|---|---|
word_form | word не находит «посылки»; char находит раздел «Посылка» | Лексическая форма может быть причиной miss |
exception | Находится текущий раздел про батареи вместе с исключением | Chunk должен сохранять ограничение, а не только общее правило |
heading | word и char ставят рекламу выше правил; char_heading возвращает правила | Candidate retrieval и порядок выдачи — разные этапы |
missing_fact | Все методы находят правила, Recall@1=1, но точной суммы возврата там нет | Retrieval relevance не доказывает answerability |
no_overlap | Пустая выдача и abstain; Recall/RR равны null | Для пустого gold relevant set эти метрики здесь не определены |
В отчёте reciprocal_rank относится к одному запросу; среднее по запросам с непустым relevant set даст MRR. gold_answerable_from_corpus — разметка evaluator; она не передаётся поиску и не вычисляется из score.
Что означает результат
extract_response возвращает только буквальные выдержки и citation IDs выбранных chunks. При непустой выдаче статус — evidence_only, а не «вопрос решён». При отсутствии положительного лексического score — abstain. Это ограниченная эвристика отсутствия совпадений: даже маленький положительный score ещё не означает полезный ответ.
В missing_fact нужно перейти к order/refund API. В настоящем RAG после context selection добавляется model call; отдельные graders проверяют, подтверждают ли выдержки каждое утверждение ответа и достаточно ли их для вопроса. Ссылка на существующий chunk не доказывает корректность сгенерированного текста.
Три контролируемых эксперимента
- Запустите
--k 2 --candidate-k 4. Запишите, какой relevant chunk теперь вошёл в выдачу и сколько лишнего контекста добавилось. Рост recall не обязательно улучшит final answer. - Запустите
--k 1 --candidate-k 1. Вheadingreranker уже не может поднять правила: их нет среди candidates. Причина — раннее отсечение, а не плохой final prompt. - В учебной копии источника перенесите исключение о повреждённых батареях в отдельный раздел и задайте оба chunk IDs как релевантные вопросу «когда батареи принимаются и когда запрещены». При
k=1Recall@1 не может превысить 0.5. Не меняйте разметку, чтобы скрыть потерю условия.
Артефакт: таблица «один изменённый фактор → rankings → метрики → ошибка → следующее действие». До запуска запишите прогноз; после запуска объясните расхождение. Критерий готовности — локализовать проблему на ingestion, retrieval, reranking или answerability, а не просто получить высокий средний score.
Следующий уровень: dense, hybrid и генерация
На отдельном, более разнообразном корпусе можно последовательно добавить learned embeddings, затем объединение lexical/dense rankings, затем trained reranker и генеративный ответ. Сохраняйте те же cases и filters, версионируйте embedding model/index и отдельно измеряйте затраты каждого этапа. После настройки на development set нужна проверка на новых frozen cases.
Это продолжение самостоятельного эксперимента, не реализованная возможность текущего CLI. Синтетические пять запросов специально демонстрируют разные ошибки; преимущество метода на них не переносится на реальный корпус. Исходная работа RAG исследует сочетание retrieval и генерации; данная лаборатория изолирует только подготовку evidence.
Self-check
- Это API-shaped или document-shaped вопрос?
- Как измеряется retrieval до model call?
- Где применяются tenant/access filters?
- Как отмечается версия и freshness?
- Какие записи memory создаются только с consent?
- Как выполнить deletion во всех производных stores?
- Почему Hit@k и Recall@k расходятся при нескольких релевантных chunks?
- Может ли reranker восстановить отсутствующего candidate?
- Как отличить найденный документ от достаточного ответа?
Источники для лаборатории
Проверено 2026-09-07: Introduction to Information Retrieval: ranked evaluation — определения и интерпретация метрик; scikit-learn: feature extraction — word/character TF-IDF; Lewis et al., RAG, 2020 — исследование retrieval-augmented generation. Правило heading rerank и конкретный корпус — учебная эвристика.