> ## Documentation Index
> Fetch the complete documentation index at: https://docs.velesagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# План развития памяти Veles

> Технический план по развитию долговременной памяти Veles на основе исследования OpenClaw.

# План развития памяти Veles

> Дата: 2026-03-25
> Основано на: [Исследование памяти OpenClaw](./openclaw-research-memory.md)
> Цель: Внедрить систему долговременной памяти в стиле OpenClaw в Veles с минимальными изменениями основного цикла.

***

## Текущее состояние

### Файловая память (`veles/agent/memory.py`)

* `MemoryStore`: управляет `memory/MEMORY.md` (структурированные долгосрочные факты).
* `MemoryConsolidator`: консолидация на базе LLM, которая архивирует старые сообщения в MEMORY.md, когда контекст превышает половину окна.
* Содержимое MEMORY.md встраивается в системный промпт через `ContextBuilder`.
* Консолидация использует принудительный вызов инструмента `save_memory` с откатом к обычному архивированию.

### Векторная память (`veles/agent/vector_memory.py`)

* `VectorMemoryManager`: индексирует `workspace/docs/<project>/` с использованием гибридного поиска (BM25 + векторный).
* SQLite + sqlite-vec + FTS5, эмбеддинги через litellm.
* Фоновый цикл синхронизации (интервал 60с), поиск с ограничением по проекту.
* `VectorSearchTool`: предоставляет агенту инструмент `vector_search`.

### Сессии (`veles/session/manager.py`)

* JSONL-файлы в `workspace/sessions/` (один на пару канал:chat\_id).
* Строка метаданных + дополняемые словари сообщений.
* `get_history()` возвращает `messages[last_consolidated:]`.

### Что отсутствует (по сравнению с OpenClaw)

1. ~~**Нет датированных файлов памяти**~~ ✅ Реализовано (Фаза 1)
2. ~~**Нет сброса памяти перед сжатием контекста**~~ ✅ Реализовано (Фаза 2)
3. ~~**Файлы памяти не индексируются для поиска**~~ ✅ Реализовано (Фаза 3)
4. ~~**Нет инструмента `memory_search`**~~ ✅ Реализовано (Фаза 4)
5. ~~**HISTORY.md избыточен**~~ ✅ Удален (заменен датированными файлами + memory\_search)
6. ~~**Нет временного затухания (temporal decay)**~~ ✅ Реализовано (отдельная конфигурация для памяти и документов)

***

## Что мы создаем

Четыре функции в порядке реализации:

| # | Функция                                        | Источник вдохновения                 | Влияние на основной цикл       |
| - | ---------------------------------------------- | ------------------------------------ | ------------------------------ |
| 1 | Датированные файлы памяти по завершении сессии | Хук session-memory в OpenClaw        | 1 строка в обработчике `/new`  |
| 2 | Сброс памяти перед сжатием контекста           | memory-flush.ts в OpenClaw           | 1 вызов перед консолидацией    |
| 3 | Индексация файлов памяти                       | Индексация memory-core в OpenClaw    | Расширение VectorMemoryManager |
| 4 | Инструмент `memory_search`                     | Инструмент memory\_search в OpenClaw | Регистрация 1 инструмента      |

**Что остается без изменений:**

* Текущий алгоритм гибридного поиска (BM25 + векторный).
* Текущий конвейер эмбеддингов (litellm).
* Текущая логика консолидация (MemoryConsolidator).
* Текущий формат сессий JSONL.
* Наличие MEMORY.md в системном промпте.

***

## Фаза 1: Датированные файлы памяти по завершении сессии

**Цель**: При вызове `/new` резюмировать завершающуюся сессию в файл `memory/YYYY-MM-DD-slug.md`.

### Новый класс: `SessionMemoryWriter`

**Файл**: `veles/agent/memory.py` (добавить в существующий файл)

```python theme={null}
class SessionMemoryWriter:
    """Создает датированные файлы памяти из стенограмм сессий при их завершении."""

    def __init__(self, workspace: Path, provider: LLMProvider, model: str):
        self.memory_dir = workspace / "memory"
        self.provider = provider
        self.model = model

    async def write_session_memory(
        self, session_key: str, messages: list[dict], max_messages: int = 20,
    ) -> Path | None:
        """Резюмирует недавние сообщения и записывает в memory/YYYY-MM-DD-slug.md."""
```

### Логика

1. Взять последние `max_messages` сообщений пользователя/ассистента из снимка сессии (пропустить вызовы инструментов для сокращения контекста LLM).
2. Если сообщений от пользователя меньше 3 — пропустить (слишком короткая сессия).
3. Вызвать LLM со специализированным промптом:
   * System: "Вы — составитель памяти. Создайте краткое резюме этого диалога."
   * Использовать принудительный вызов `create_session_memory` с параметрами:
     * `slug`: короткий идентификатор файла в kebab-case (макс. 40 символов, например "api-design-review").
     * `summary`: описание ключевых тем, решений и фактов в формате markdown.
4. Записать в `memory/YYYY-MM-DD-slug.md`:

```markdown theme={null}
# Память сессии: 2026-03-25 14:30

- **Сессия**: telegram:12345
- **Дата**: 2026-03-25 14:30:00

## Резюме

[Текст резюме от LLM]

## Ключевые сообщения

user: [фрагмент первого сообщения]
assistant: [фрагмент ответа]
...
```

5. При ошибке вызова LLM записать сырой фрагмент стенограммы (по аналогии с `_raw_archive` в MemoryStore).

### Точка интеграции в `loop.py`

Текущий обработчик `/new`:

```python theme={null}
if cmd == "/new":
    snapshot = session.messages[session.last_consolidated:]
    session.clear()
    self.sessions.save(session)
    self.sessions.invalidate(session.key)
    if snapshot:
        self._schedule_background(self.memory_consolidator.archive_messages(snapshot))
    return OutboundMessage(...)
```

Добавляем одну строку — запуск записи памяти сессии в фоне:

```python theme={null}
if cmd == "/new":
    snapshot = session.messages[session.last_consolidated:]
    session.clear()
    self.sessions.save(session)
    self.sessions.invalidate(session.key)
    if snapshot:
        self._schedule_background(self.memory_consolidator.archive_messages(snapshot))
        self._schedule_background(self.session_memory_writer.write_session_memory(
            session.key, snapshot,
        ))
    return OutboundMessage(...)
```

### Дополнение в конфиг (`schema.py`)

```python theme={null}
class SessionMemoryConfig(Base):
    """Настройка создания датированных файлов памяти сессий."""
    enabled: bool = True
    max_messages: int = 20       # Макс. кол-во сообщений для резюме
    min_user_messages: int = 3   # Пропускать слишком короткие сессии
```

Вложить в `ToolsConfig` рядом с `vector_memory`:

```python theme={null}
class ToolsConfig(Base):
    ...
    session_memory: SessionMemoryConfig = Field(default_factory=SessionMemoryConfig)
```

### Измененные файлы

* `veles/agent/memory.py` — добавлен класс `SessionMemoryWriter` (\~80 строк).
* `veles/agent/loop.py` — инициализация `self.session_memory_writer` + 1 строка в `/new`.
* `veles/config/schema.py` — добавлена `SessionMemoryConfig`.

***

## Фаза 2: Сброс памяти перед сжатием контекста

**Цель**: Перед тем как `maybe_consolidate_by_tokens` начнет архивировать сообщения, запустить выделенный ход LLM для записи важных фактов в `memory/YYYY-MM-DD.md`.

### Новый класс: `MemoryFlusher`

**Файл**: `veles/agent/memory.py` (добавить в существующий файл)

```python theme={null}
class MemoryFlusher:
    """Запускает ход LLM для извлечения фактов перед сжатием контекста."""

    def __init__(self, workspace: Path, provider: LLMProvider, model: str):
        self.memory_dir = workspace / "memory"
        self.provider = provider
        self.model = model
        self._flushed_sessions: set[str] = set()  # отслеживание внутри цикла сжатия
```

### Логика

1. **Условие срабатывания**: `estimated_tokens > context_window - threshold_tokens`.
2. **Защита**: Пропускать, если для этой сессии в текущем цикле сжатия сброс уже выполнялся.
3. **Дедупликация**: Прочитать текущий `memory/YYYY-MM-DD.md`, чтобы не дублировать информацию.
4. Вызвать LLM с системным промптом:
   ```
   Вы — агент по извлечению памяти. Проанализируйте диалог ниже и выделите
   важные факты, решения, предпочтения и задачи, достойные запоминания.

   Запишите их в виде списка. Если ничего достойного упоминания нет, ответьте
   ровно одной фразой: "[silent]".

   Целевой файл: memory/YYYY-MM-DD.md (только дозапись, никогда не перезаписывайте старое)
   ```
5. Если ответ `[silent]` — пропустить запись.
6. Иначе — **дозаписать** в `memory/YYYY-MM-DD.md`:

```markdown theme={null}
## Сброс в 14:30 (сессия: telegram:12345)

- Пользователь предпочитает REST вместо GraphQL для нового API.
- Решение: использовать PostgreSQL для сервиса аналитики.
- Задача: проверить предложение вендора до пятницы.
```

### Точка интеграции

В `MemoryConsolidator.maybe_consolidate_by_tokens()` (`memory.py`), добавить вызов флеша перед циклом консолидации:

```python theme={null}
async def maybe_consolidate_by_tokens(self, session: Session) -> None:
    ...
        # НОВОЕ: Сброс памяти перед сжатием
        if self.memory_flusher:
            await self.memory_flusher.maybe_flush(
                session.key,
                session.messages[session.last_consolidated:],
                estimated,
                self.context_window_tokens,
            )

        # ... далее стандартный цикл консолидации ...
```

### Измененные файлы

* `veles/agent/memory.py` — добавлен класс `MemoryFlusher` (\~70 строк), изменен `MemoryConsolidator.__init__` для его приема.
* `veles/agent/loop.py` — передача экземпляра `MemoryFlusher` в `MemoryConsolidator`.
* `veles/config/schema.py` — добавлена `MemoryFlushConfig`.

***

## Фаза 3: Индексация файлов памяти

**Цель**: Расширить `VectorMemoryManager` для индексации `memory/MEMORY.md` и `memory/*.md`, сделав их доступными для поиска наравне с документацией.

### Изменения в `VectorMemoryManager`

**Файл**: `veles/agent/vector_memory.py`

#### 1. Расширение `_discover_files()`

Добавляем поиск в папке `memory/` с зарезервированным именем проекта `_memory`.

```python theme={null}
def _discover_files(self) -> list[tuple[Path, str]]:
    results: list[tuple[Path, str]] = []
    # ... существующий поиск в docs/ ...

    # НОВОЕ: файлы памяти
    if self.memory_dir.exists():
        memory_md = self.workspace / "MEMORY.md"
        if memory_md.exists():
            results.append((memory_md, "_memory"))
        for fpath in sorted(self.memory_dir.glob("**/*.md")):
            if not fpath.name.startswith("."):
                results.append((fpath, "_memory"))
    return results
```

#### 2. Добавление метода `search_memory()`

Удобный метод для поиска только по файлам памяти (фильтр `project='_memory'`).

### Измененные файлы

* `veles/agent/vector_memory.py` — расширено обнаружение файлов, добавлен метод `search_memory()`.

***

## Фаза 4: Инструмент `memory_search`

**Цель**: Дать агенту инструмент для поиска по его собственным файлам памяти.

### Новый файл: `veles/agent/tools/memory_search.py`

Инструмент `memory_search` позволяет агенту искать по `MEMORY.md` и датированным логам в папке `memory/`, используя гибридный поиск. Это критически важно для восстановления контекста прошлых диалогов, решений и предпочтений.

### Регистрация в `loop.py`

Добавить регистрацию нового инструмента в `_register_default_tools()` после `vector_search`.

### Обновление системного промпта (`context.py`)

Обновить описание рабочего пространства в `_get_identity()`, указав на наличие инструментов поиска по памяти.

***

## Архитектура после реализации

Veles получает многослойную систему памяти:

* **MEMORY.md**: Долгосрочные, отобранные факты (всегда в промпте).
* **Датированные логи**: Автоматически создаваемые резюме сессий (доступны через поиск).
* **Поиск по памяти**: Инструмент `memory_search` для доступа к истории.
* **Векторная база**: Единое хранилище для документации проектов и личной памяти агента.

### Жизненный цикл сессии

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

***

## План работ и оценка

| Фаза | Функция                    | Объем кода | Файлы                   | Оценка   |
| ---- | -------------------------- | ---------- | ----------------------- | -------- |
| 1    | Датированные логи сессий   | \~80 строк | memory.py, loop.py      | 2-3 часа |
| 2    | Флеш перед сжатием         | \~70 строк | memory.py, loop.py      | 2-3 часа |
| 3    | Индексация памяти          | \~20 строк | vector\_memory.py       | 1 час    |
| 4    | Инструмент `memory_search` | \~50 строк | tools/memory\_search.py | 1-2 часа |

**Итого**: \~220 новых строк кода, около 6-9 рабочих часов.

### Зависимости

* Фазы 3 и 4 требуют включенной векторной памяти (`tools.vector_memory.enabled: true`).
* Фазы 1 и 2 работают независимо (записывают обычные markdown-файлы).
* Фаза 4 зависит от Фазы 3.
