HardТеория8 min

Memory Engineering: память для AI-агентов

Как создать систему памяти для обучающихся AI-агентов

Проблема памяти

Каждый раз, когда вы начинаете новую сессию с AI-агентом, происходит одно и то же: агент не помнит ничего. Ни ваших конвенций, ни решений, принятых час назад, ни ошибок, которые уже были исправлены. Каждая сессия -- tabula rasa.

Сессия 1: "Используй UUID v7 для первичных ключей"
  → Агент: "Понял, использую UUID v7" ✓

Сессия 2: "Создай новую сущность Product"
  → Агент: "Создаю с auto-increment ID" ✗
  → Человек: "Нет! UUID v7! Я же говорил!"
  → Агент: "Извините, исправляю" ✓

Сессия 3: "Создай сущность Category"
  → Агент: "Создаю с auto-increment ID" ✗
  → Человек: ... (╯°□°)╯︵ ┻━┻

Это не баг -- это фундаментальное свойство LLM. У них нет долговременной памяти. Контекстное окно -- это оперативная память, которая очищается при каждом перезапуске.

Memory Engineering -- это инженерная дисциплина создания систем внешней памяти, которые компенсируют отсутствие нативной долговременной памяти у AI-агентов.


Типы памяти агента

1. Context Memory (Контекстная память)

Это то, что агент "видит" прямо сейчас -- содержимое текущего контекстного окна.

Характеристика Значение
Объём 100K-200K токенов (в зависимости от модели)
Срок жизни Одна сессия
Скорость доступа Мгновенная
Надёжность Высокая (пока в окне)
Управление Автоматическое (модель)
Контекстное окно:
┌─────────────────────────────────────────────┐
│ System prompt                               │
│ AGENT.md (загружен)                         │
│ Текущий файл (открыт)                      │
│ История диалога                             │
│ Результаты предыдущих команд               │
│ ─────────────────────────────────────────── │
│ [Здесь заканчивается то, что агент "знает"]│
└─────────────────────────────────────────────┘

Ограничение: при заполнении контекстного окна старая информация "выталкивается". Агент буквально забывает то, о чём говорили в начале длинной сессии.

2. Project Memory (Проектная память)

Персистентные файлы, которые загружаются при каждом запуске агента.

Характеристика Значение
Объём Не ограничен (но ограничен контекстным окном)
Срок жизни Постоянный (файлы на диске)
Скорость доступа Требует чтения файла
Надёжность Высокая (файловая система)
Управление Ручное (человек) + автоматическое (агент)
project/
├── CLAUDE.md          # Instructions for AI agent
├── AGENTS.md          # Build/test commands
└── .claude/
    └── memory/
        └── MEMORY.md  # Persistent cross-session knowledge

Это самый важный тип памяти для практической работы. CLAUDE.md и MEMORY.md -- это "мозг" вашего агента между сессиями.

3. Learning Memory (Обучающаяся память)

Накопленный опыт, который делает агента эффективнее с каждой итерацией.

Характеристика Значение
Объём Растёт со временем
Срок жизни Постоянный с возможностью ротации
Скорость доступа Требует поиска
Надёжность Зависит от качества записей
Управление Автоматическое (агент) + ручная модерация
# Learning Memory — пример записи

## Solution: PostgreSQL JSONB query performance
**Problem**: JSONB queries slow on large tables (>500K rows)
**Solution**: GIN index on JSONB column + containment operator @>
**Context**: users table, 500K rows, metadata JSONB column
**Date**: 2026-02-15
**Confidence**: High (verified in production)
**Tags**: postgresql, performance, jsonb, indexing

4. Shared Memory (Командная память)

Знания, доступные нескольким агентам или членам команды.

Характеристика Значение
Объём Большой (общая база знаний)
Срок жизни Постоянный
Скорость доступа Зависит от хранилища
Надёжность Зависит от инфраструктуры
Управление Командное
Shared Memory примеры:
├── Team wiki / Confluence
├── ADR (Architecture Decision Records)
├── Shared CLAUDE.md conventions
├── Common troubleshooting guide
└── Reusable code snippets library

Реализация проектной памяти

Структура файлов памяти

project/
├── CLAUDE.md                    # Primary AI instructions
├── AGENTS.md                    # Build commands for AI agents
├── .claude/
│   └── memory/
│       └── MEMORY.md            # Auto-updated cross-session memory
├── docs/
│   ├── architecture.md          # System architecture
│   ├── decisions/               # Architecture Decision Records
│   │   ├── 001-use-uuid-v7.md
│   │   ├── 002-cursor-pagination.md
│   │   └── 003-event-driven-side-effects.md
│   └── troubleshooting.md       # Known issues and solutions

CLAUDE.md: основная инструкция

CLAUDE.md -- это файл, который Claude Code автоматически загружает при каждом запуске. Это основной механизм проектной памяти.

# Project: E-Commerce API

## Critical Rules
- UUID v7 for all primary keys (NEVER auto-increment)
- declare(strict_types=1) in every PHP file
- final classes by default
- Repository pattern for data access
- No business logic in controllers

## Build Commands
- Test: `php bin/phpunit`
- Lint: `vendor/bin/phpstan analyse -l 9`
- Format: `vendor/bin/php-cs-fixer fix`

## Known Gotchas
- EasyAdmin CodeEditorField: no 'json' language — use 'javascript'
- Content import requires: `php -d memory_limit=2G bin/console app:content:import`
- security.yaml: specific ROLE_USER rules BEFORE general PUBLIC_ACCESS

MEMORY.md: автоматическая межсессионная память

MEMORY.md обновляется агентом в конце каждой сессии. Он содержит то, что агент "узнал":

# Project Memory

## Architecture
- Backend: Symfony 7.4 + PHP 8.4
- Frontend: Nuxt 3 SPA
- DB: PostgreSQL 18
- Cache: Redis 7
- Queue: RabbitMQ (Symfony Messenger)

## Recent Changes
- 2026-03-07: Added product reviews API (Review entity, ReviewService, ReviewController)
- 2026-03-06: Migrated from Paddle to free model
- 2026-03-05: Implemented Telegram notifications

## Discovered Patterns
- All repositories extend AbstractRepository with common CRUD methods
- All DTOs use Symfony Validator attributes for validation
- All controllers inherit from AbstractApiController with json() helper
- Error responses follow RFC 7807 format

## Known Issues
- Memory limit needed for content import (2G minimum)
- Google Calendar webhook requires real domain (not localhost)
- RabbitMQ occasionally loses connection — messenger:consume auto-restarts

## Troubleshooting
- "Class not found" after adding entity → run `composer dump-autoload`
- "Table not found" → run `php bin/console doctrine:migrations:migrate`
- Nuxt "#internal/nuxt/paths" error → `rm -rf .nuxt` + restart

Architecture Decision Records (ADR)

ADR фиксируют почему было принято конкретное решение. Это критически важно для AI-агентов, которые иначе могут "переизобрести" то, что уже было решено.

# ADR-001: Use UUID v7 for Primary Keys

## Status
Accepted

## Context
We need a primary key strategy for all entities.
Options considered:
1. Auto-increment SERIAL
2. UUID v4 (random)
3. UUID v7 (time-ordered)

## Decision
Use UUID v7 for all primary keys.

## Rationale
- UUID v7 is time-ordered → good B-tree index performance (unlike UUID v4)
- No coordination needed → works with distributed systems
- No information leakage → user can't guess IDs (unlike SERIAL)
- Sortable by creation time → implicit ordering without additional column

## Consequences
- Slightly larger storage (16 bytes vs 4 bytes for INT)
- Need UUID v7 generation library (symfony/uid)
- All foreign keys are UUID type
- Index size slightly larger

## Date
2026-01-15

Правило: Если вы приняли архитектурное решение -- запишите ADR. Через 3 месяца ни вы, ни AI-агент не вспомните, почему выбрали UUID v7 вместо auto-increment.


Реализация обучающейся памяти

Концепция Compound Learning

Compound Learning (составное обучение) -- это процесс, при котором каждая итерация Ralph Loop добавляет знания, используемые в последующих итерациях.

Итерация 1: Агент создаёт User entity
  → Записывает: "Entity pattern: UUID v7 PK, timestamptz for dates"

Итерация 5: Агент создаёт Order entity
  → Читает: "Entity pattern: UUID v7 PK, timestamptz for dates"
  → Результат: корректный с первой попытки

Итерация 10: Агент создаёт Review entity
  → Читает: "Entity pattern: UUID v7 PK, timestamptz for dates"
  → Дополнительно записывает: "Entities with user ownership need userId field + ownership check in service"

Итерация 20: Агент создаёт Comment entity
  → Знает всё: UUID v7, timestamps, userId, ownership check
  → Качество кода максимальное
Качество кода vs. Количество итераций:

  Итерация 1:    ████████              70%
  Итерация 5:    ██████████████        85%
  Итерация 10:   ████████████████      90%
  Итерация 20:   ██████████████████    95%
  Итерация 50:   ███████████████████   97%

Стандартная библиотека (stdlib)

Концепция "stdlib" для AI-агентов -- это набор проверенных, многоразовых компонентов, которые контролируют качество генерации кода.

stdlib/
├── templates/
│   ├── entity.php.tpl           # Reference entity template
│   ├── repository.php.tpl       # Reference repository template
│   ├── service.php.tpl          # Reference service template
│   ├── controller.php.tpl       # Reference controller template
│   ├── dto-request.php.tpl      # Reference request DTO template
│   ├── dto-response.php.tpl     # Reference response DTO template
│   └── unit-test.php.tpl        # Reference test template
├── patterns/
│   ├── pagination.md            # How to implement cursor pagination
│   ├── error-handling.md        # Error handling patterns
│   ├── validation.md            # Validation patterns
│   └── authentication.md       # Auth patterns
└── examples/
    ├── ProductController.php    # Complete working example
    ├── ProductService.php       # Complete working example
    └── ProductServiceTest.php   # Complete working example

Когда агент создаёт новый контроллер, он смотрит на шаблон и примеры, а не выдумывает с нуля. Это радикально повышает консистентность.

# In AGENT.md:

## Reference Templates
When creating new files, ALWAYS look at these references first:
- New entity → see stdlib/templates/entity.php.tpl
- New repository → see stdlib/templates/repository.php.tpl
- New service → see stdlib/templates/service.php.tpl
- New controller → see stdlib/templates/controller.php.tpl
- New test → see stdlib/templates/unit-test.php.tpl

## Working Examples
For complete implementation reference:
- src/Controller/ProductController.php (exemplary controller)
- src/Service/ProductService.php (exemplary service)
- tests/Unit/Service/ProductServiceTest.php (exemplary test)

Категории обучающейся памяти

Записи в обучающейся памяти делятся на категории:

1. Решения (Solutions):

## Solution: Circular dependency in Symfony services
**Problem**: ServiceA depends on ServiceB, ServiceB depends on ServiceA
**Solution**: Extract shared logic into ServiceC, both depend on ServiceC
**Alternative considered**: Use lazy injection — rejected (hides design problem)
**Date**: 2026-03-01
**Tags**: symfony, di, architecture

2. Ошибки (Mistakes):

## Mistake: Raw SQL in service layer
**What happened**: Agent wrote raw SQL query in OrderService
**Why it's wrong**: Violates repository pattern, untestable, SQL injection risk
**Correct approach**: Add method to OrderRepository, call from service
**Prevention**: Added explicit rule to AGENT.md
**Date**: 2026-02-28
**Tags**: architecture, anti-pattern

3. Паттерны (Patterns):

## Pattern: Cursor-based pagination in this project
**Implementation**:
1. Repository method accepts `?string $cursor` and `int $limit`
2. If cursor provided, add `WHERE id > :cursor` to query
3. Return items + `next_cursor` (last item's ID) or null
4. Controller passes cursor from query parameter
**Reference**: src/Repository/ArticleRepository.php::findPaginated()
**Tags**: pagination, api, repository

4. Конвенции (Conventions):

## Convention: Naming patterns in this project
**Entities**: Singular PascalCase (Order, not Orders)
**Repositories**: {Entity}Repository (OrderRepository)
**Services**: {Domain}Service (OrderService, not OrderManager or OrderHandler)
**DTOs**: {Action}{Entity}Request/Response (CreateOrderRequest, OrderResponse)
**Controllers**: {Entity}Controller (OrderController)
**Tests**: {Class}Test (OrderServiceTest)
**Tags**: naming, conventions

Стратегии поиска по памяти

Полнотекстовый поиск

Простейший подход -- grep по файлам памяти:

# Search memory files for relevant information
grep -r "pagination" docs/ MEMORY.md CLAUDE.md

# Search for solutions to a specific problem
grep -r "JSONB" docs/troubleshooting.md docs/decisions/

AI-агент может использовать эту стратегию при каждой итерации:

# In PROMPT.md:

## Before implementing any task:
1. Search MEMORY.md for related solutions: grep -i "{keywords}" MEMORY.md
2. Search troubleshooting.md for known issues: grep -i "{keywords}" docs/troubleshooting.md
3. Search ADRs for relevant decisions: grep -rl "{keywords}" docs/decisions/

Семантический поиск (Embeddings)

Для больших баз знаний -- поиск по смыслу, а не по точному совпадению:

Запрос: "как сделать пагинацию в API"
Результат: документ о cursor-based pagination (даже если слово "пагинация" не упоминается)

Инструменты: MCP Memory (встроен в Claude Code), Pinecone, Weaviate, ChromaDB.

Тегирование

Каждая запись в памяти содержит теги для быстрой фильтрации:

## Solution: Fix N+1 query problem
**Tags**: performance, database, doctrine, orm

## Convention: API error format
**Tags**: api, errors, rfc7807, conventions
# Find all memory entries tagged with "performance"
grep -B5 "Tags:.*performance" MEMORY.md docs/**/*.md

Ранжирование по свежести

Недавние записи имеют больший приоритет, чем старые:

# Memory entries ordered by date (newest first):

## 2026-03-07: Review entity uses embedded value objects for ratings
## 2026-03-05: Telegram notifications via Messenger async
## 2026-02-28: Cursor pagination pattern established
## 2026-02-15: JSONB GIN index solution discovered
## 2026-01-20: UUID v7 decision made (see ADR-001)

Правило: При конфликте между старой и новой записью -- доверяйте новой. Проект эволюционирует, и старые решения могут устареть.


Антипаттерны памяти

1. Слишком много памяти

Проблема: MEMORY.md на 5000 строк.
Последствие: Занимает 80% контекстного окна, не оставляя места для кода.
Решение: Разделить на файлы по категориям, загружать только релевантные.
Правильная структура:
├── MEMORY.md              # 50-100 строк — ключевые факты
├── docs/
│   ├── solutions.md       # Загружать при troubleshooting
│   ├── conventions.md     # Загружать при создании кода
│   ├── decisions/         # Загружать при архитектурных вопросах
│   └── troubleshooting.md # Загружать при ошибках

2. Слишком мало памяти

Проблема: Только CLAUDE.md с 10 строками.
Последствие: Агент повторяет одни и те же ошибки каждую сессию.
Решение: Добавить MEMORY.md, troubleshooting.md, ADR-ы.

3. Устаревшая память

Проблема: MEMORY.md говорит "используем Paddle для платежей",
          но Paddle был удалён 2 недели назад.
Последствие: Агент пытается интегрироваться с несуществующей системой.
Решение: Обновлять память при каждом значимом изменении.

4. Противоречивая память

Проблема: CLAUDE.md: "Используй UUID v4"
          MEMORY.md: "Используй UUID v7"
          ADR-001: "Решение: UUID v7"
Последствие: Агент "угадывает", какому источнику верить.
Решение: Единственный источник истины. CLAUDE.md — главный.
         При обновлении — обновлять ВСЕ файлы.

Обслуживание памяти

Регулярная очистка

# Monthly memory maintenance checklist

## MEMORY.md
- [ ] Remove resolved issues from "Known Issues"
- [ ] Update "Recent Changes" — remove entries older than 30 days
- [ ] Verify "Architecture" section matches actual stack
- [ ] Check "Discovered Patterns" — still accurate?

## Troubleshooting
- [ ] Remove solved issues that won't recur
- [ ] Update solutions for issues that changed
- [ ] Add new issues discovered this month

## ADRs
- [ ] Mark superseded decisions as "Superseded by ADR-XXX"
- [ ] Add new decisions made this month
- [ ] Verify existing decisions still apply

Консолидация

Когда несколько записей описывают одно и то же -- объединяйте:

# Before consolidation:
## Solution: Redis connection timeout (2026-01-15)
## Fix: Redis reconnection issue (2026-02-01)
## Workaround: Redis drops connection (2026-02-20)

# After consolidation:
## Solution: Redis connection stability
**Problem**: Redis connections drop under load or after idle period
**Root cause**: Default timeout too low (60s), no keepalive
**Solution**: Set timeout=300, tcp-keepalive=60 in redis.conf
**History**: First seen 2026-01-15, root cause found 2026-02-20

Валидация против реального состояния

#!/bin/bash
# validate-memory.sh — Check if MEMORY.md matches reality

echo "=== Memory Validation ==="

# Check if mentioned files exist
grep -oP 'src/[^\s]+\.php' MEMORY.md | while read -r file; do
  if [ ! -f "$file" ]; then
    echo "WARNING: $file mentioned in MEMORY.md but does not exist"
  fi
done

# Check if mentioned commands work
grep -oP '`[^`]+`' MEMORY.md | grep -E '^`(php|npm|go)' | tr -d '`' | while read -r cmd; do
  echo "Testing: $cmd"
  eval "$cmd" >/dev/null 2>&1 && echo "  OK" || echo "  FAILED"
done

echo "=== Validation complete ==="

Практические инструменты

Claude Code MCP Memory

Claude Code имеет встроенную систему памяти через MCP (Model Context Protocol):

Доступные операции:
├── memory_save     — Сохранить факт/решение/урок
├── memory_recall   — Найти по ключевым словам
├── memory_search   — Семантический поиск
└── memory_list     — Показать все записи

Преимущество MCP Memory: семантический поиск (по смыслу, а не по ключевым словам).

Obsidian для человеческой памяти

Obsidian vault/
├── sessions/
│   ├── 2026-03-07.md    # What was done today
│   ├── 2026-03-06.md
│   └── ...
├── Memory.md            # Current project state
├── Tasks.md             # Active tasks
├── Decisions.md         # Architecture decisions
└── Troubleshooting.md   # Known issues

Двойная система хранения

Оптимальный подход -- хранить знания в двух системах одновременно:

Obsidian (для человека):
  + Читаемые отчёты
  + Богатое форматирование
  + Связи между заметками
  + Визуализация графа знаний

MCP Memory (для агентов):
  + Семантический поиск
  + Быстрый доступ из кода
  + Автоматическая индексация
  + Кросс-сессионная доступность

Правило: Сохраняйте в ОБЕ системы. Obsidian -- подробно для человека. MCP Memory -- кратко для машинного поиска.


Выводы

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

  2. Четыре типа памяти: контекстная (в сессии), проектная (CLAUDE.md), обучающаяся (compound learning), командная (shared knowledge).

  3. CLAUDE.md + MEMORY.md -- минимум. Без этих файлов каждая сессия начинается с нуля. С ними -- агент "помнит" проект.

  4. Stdlib повышает консистентность. Шаблоны и эталонные примеры -- лучший способ контролировать качество генерации.

  5. Память нужно обслуживать. Устаревшая или противоречивая память хуже, чем отсутствие памяти.

  6. Compound Learning -- конкурентное преимущество. Агент, который учится на каждой итерации, через 50 итераций работает на порядок лучше, чем агент без памяти.

Связанные темы