Перейти к содержанию

Схема записей памяти и контракта извлечения

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

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

1. Зачем нужен отдельный слой схем

Очень частая ошибка с памятью устроена так:

  • агент что-то запомнил;
  • извлечение что-то вернуло;
  • дальше команда уже не может уверенно ответить:
  • что это была за запись;
  • откуда она взялась;
  • кто имел право ее читать;
  • по каким правилам она попала в prompt.

Поэтому слой памяти полезно описывать не как «у нас есть vector store», а как набор типизированных записей и типизированных правил извлечения.

2. Базовые сущности

Минимальный слой здесь удобно строить вокруг трех сущностей:

  • memory_record
  • retrieval_query
  • retrieval_result

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

3. Запись памяти

memory_record описывает одну конкретную запись в слое памяти.

kind: memory_record
record_id: mem-tenant-acme-001
tenant_id: tenant-acme
memory_class: profile
key: preferred_language
value: English
source: user_confirmed_preference
provenance: user_confirmed_preference
revision: 1
trust_level: high
created_at: 2026-04-07T12:00:00Z
retention: long_term

Что здесь важно:

  • tenant_id не дает извлечению пересекать границы арендаторов;
  • memory_class позволяет отличать short_term, long_term и profile;
  • source и provenance помогают не путать наблюдение и подтвержденный факт;
  • revision нужен, чтобы не терять историю тихими перезаписями;
  • trust_level помогает не ставить все записи в один ряд.

4. Запрос на извлечение

retrieval_query описывает не просто текстовый запрос, а полный рабочий контекст чтения памяти.

kind: retrieval_query
trace_id: trace-001
session_id: session-001
tenant_id: tenant-acme
principal_id: user-42
purpose: answer_generation
allowed_classes:
  - profile
  - long_term
filters:
  trust_min: medium
  max_age_days: 90
  require_provenance: true
limit: 5

Здесь особенно важно то, что извлечение становится не «магическим поиском», а нормальным управляемым контуром чтения.

5. Результат извлечения

retrieval_result фиксирует, что именно рантайм решил вернуть в контекст.

kind: retrieval_result
trace_id: trace-001
session_id: session-001
selected_records:
  - record_id: mem-tenant-acme-001
    memory_class: profile
    trust_level: high
    provenance: user_confirmed_preference
  - record_id: mem-tenant-acme-177
    memory_class: long_term
    trust_level: medium
    provenance: validated_service_rule
selection_reason:
  - profile_match
  - tenant_match
  - trust_filter_passed
excluded_records: 12

Это важно потому, что потом можно объяснить:

  • почему именно эти записи попали в prompt;
  • какие ограничения сработали;
  • сколько записей было отброшено.

6. Как это связано со слоем политик

Контур чтения памяти и контур записи памяти почти никогда не должны жить по одним и тем же правилам:

  • контур записи больше смотрит на validation, provenance и retention;
  • контур чтения больше смотрит на границы арендаторов, фильтры доверия и ограничения по классам.

Поэтому хорошая схема памяти почти всегда живет рядом с политиками как кодом.

7. Как это связано со схемой трасс

В схеме трасс уже есть события и поля, которые поддерживают дисциплину памяти:

  • context_layers_built
  • memory_persisted
  • memory_class
  • provenance
  • revision

То есть контракт памяти и извлечения полезен не только сам по себе, но и как основа для понятной телеметрии.

8. Как это связано со справочным пакетом

В agent_runtime_ref уже есть рабочие примитивы для этой модели:

Встроенный memory.yaml делает это конкретным через seed_records: каждая seed record несёт стабильный memory_id, а также tenant_id, memory_class, kind, content, source, confidence, provenance и revision; bundled kinds — language_preference, validated_fact и working_note, чтобы demo показывало и retrieval filtering, и lineage записи. Эталонные seed records (mem-001, mem-002 и mem-003) намеренно покрывают sources trusted_profile, trusted_service и session_state, включая provenance вроде ephemeral_session_note, чтобы retrieval examples показывали разные trust и persistence levels. Non-profile seed content также включает policy-like fact Support tickets must use the support queue and include requester_id. и working note Recent runtime demo used create_ticket as the main write capability. Loader тоже валидирует эту форму: Memory store config must be a mapping, 'memory' must be a mapping, 'seed_records' must be a list, Memory record #{idx} must be a mapping, Memory record #{idx} field must be a string: {key}, Memory record #{idx} field must be a string: {field}, Memory record #{idx} field must be a string: memory_id, Memory record #{idx} field must be a string: provenance, Memory record #{idx} field is required: {key}, Memory record #{idx} field is required: memory_id, Memory record #{idx} confidence must be a number, Memory record #{idx} confidence must be between 0 and 1 и Memory record #{idx} revision must be an integer и Memory record #{idx} revision must be positive, а direct memory store construction отвергает malformed injected records через Memory store records must be MemoryRecord и malformed direct candidates через Memory store candidate must be MemoryCandidate, while direct construction records использует стабильные errors Memory record field must be a string: {field}, Memory record field is required: {field}, Memory record confidence must be a number, Memory record confidence must be between 0 and 1, Memory record revision must be an integer и Memory record revision must be positive, и Memory candidate revision mode must be a string и Memory candidate revision mode is not supported: {revision_mode} и Memory candidate confidence must be a number и Memory candidate confidence must be between 0 and 1 и Memory candidate field must be a string: {field} и Memory candidate field is required: {field} и Memory lookup field must be a string: {field} и Memory lookup field is required: {field} и Memory lookup limit must be an integer и Memory lookup limit must be non-negative.

Книга не только описывает этот контракт, но и показывает исполняемый каркас.

9. Минимальные инварианты

У здорового слоя памяти и извлечения обычно есть такие инварианты:

  • каждая запись имеет tenant_id и memory_class;
  • постоянные записи имеют provenance и revision;
  • извлечение всегда ограничено по классам и объему;
  • запрос на извлечение знает, кто читает и зачем;
  • результат извлечения можно восстановить по трассе;
  • summaries не считаются truth by default.

10. Что чаще всего ломается

Типовые проблемы здесь очень узнаваемы:

  • извлечение возвращает «похожее», но не «полезное»;
  • записи памяти не различаются по уровню доверия;
  • summaries тихо перезаписывают более надежные данные;
  • извлечение игнорирует границы арендатора;
  • prompt получает слишком много контекста без фильтров;
  • происхождение есть только на бумаге, но не в рантайме.

11. Что сделать сразу

Сначала пройди по короткому списку и отдельно отметь все ответы «нет»:

  • Есть ли у каждой записи tenant_id, memory_class, provenance и revision?
  • Отличаются ли политика чтения памяти и политика записи?
  • Ограничено ли извлечение по доверию, классам и объему?
  • Можно ли объяснить, почему конкретная запись попала в prompt?
  • Есть ли защита от чтения через чужой tenant?
  • Видно ли решения о памяти в трассе и экспорте сессии?

Если на несколько вопросов подряд ответ «нет», значит память у тебя уже есть, а вот дисциплины работы с ней пока нет.

Что делать дальше