Память
Память OpenClaw — это обычный Markdown в рабочем пространстве агента. Файлы являются источником истины; модель "помнит" только то, что записано на диск.
Инструменты поиска по памяти предоставляются активным плагином памяти (по умолчанию: memory-core). Отключите плагины памяти с помощью plugins.slots.memory = "none".
Файлы памяти (Markdown)
Структура рабочего пространства по умолчанию использует два уровня памяти:
- memory/YYYY-MM-DD.md
- Ежедневный журнал (только добавление).
- Читаются сегодня + вчера при запуске сессии.
- MEMORY.md (опционально)
- Кураторская долгосрочная память.
- Загружается только в основной приватной сессии (никогда в групповых контекстах).
Эти файлы находятся в рабочем пространстве (agents.defaults.workspace, по умолчанию ~/.openclaw/workspace). См. Рабочее пространство агента для полной структуры.
Когда записывать память
- Решения, предпочтения и долговечные факты отправляются в MEMORY.md.
- Ежедневные заметки и текущий контекст отправляются в memory/YYYY-MM-DD.md.
- Если кто-то говорит "запомни это", запишите это (не храните в RAM).
- Эта область все еще развивается. Полезно напоминать модели сохранять воспоминания; она будет знать, что делать.
- Если вы хотите, чтобы что-то запомнилось, попросите бота записать это в память.
Автоматическая очистка памяти (предкомпактизационный пинг)
Когда сессия близка к автокомпактизации, OpenClaw запускает тихий агентный ход, который напоминает модели записать долговечную память перед компактизацией контекста. Промпты по умолчанию явно говорят, что модель может ответить, но обычно NO_REPLY является правильным ответом, поэтому пользователь никогда не видит этот ход.
Это контролируется через agents.defaults.compaction.memoryFlush:
\{
agents: \{
defaults: \{
compaction: \{
reserveTokensFloor: 20000,
memoryFlush: \{
enabled: true,
softThresholdTokens: 4000,
systemPrompt: "Session nearing compaction. Store durable memories now.",
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store."
\}
\}
\}
\}
\}
Детали:
- Мягкий порог: очистка запускается, когда оценка токенов сессии пересекает contextWindow - reserveTokensFloor - softThresholdTokens.
- Тихая по умолчанию: промпты включают NO_REPLY, поэтому ничего не доставляется.
- Два промпта: пользовательский промпт плюс системный промпт добавляют напоминание.
- Одна очистка на цикл компактизации (отслеживается в sessions.json).
- Рабочее пространство должно быть доступно для записи: если сессия выполняется в изолированной среде с workspaceAccess: "ro" или "none", очистка пропускается.
Для полного жизненного цикла компактизации см. Управление сессией + компактизация.
Векторный поиск по памяти
OpenClaw может построить небольшой векторный индекс над MEMORY.md и memory/*.md (плюс любые дополнительные каталоги или файлы, которые вы включаете), чтобы семантические запросы могли найти связанные заметки, даже когда формулировка отличается.
По умолчанию:
- Включено по умолчанию.
- Отслеживает изменения в файлах памяти (с задержкой).
- Использует удаленные эмбеддинги по умолчанию. Если memorySearch.provider не установлен, OpenClaw автоматически выбирает:
- local, если настроен memorySearch.local.modelPath и файл существует.
- openai, если можно разрешить ключ OpenAI.
- gemini, если можно разрешить ключ Gemini.
- В противном случае поиск по памяти остается отключенным до настройки.
- Локальный режим использует node-llama-cpp и может потребовать pnpm approve-builds.
- Использует sqlite-vec (когда доступен) для ускорения векторного поиска внутри SQLite.
Удаленные эмбеддинги требуют API-ключ для провайдера эмбеддингов. OpenClaw разрешает ключи из профилей авторизации, models.providers.*.apiKey или переменных окружения. OAuth Codex покрывает только chat/completions и не удовлетворяет эмбеддингам для поиска по памяти. Для Gemini используйте GEMINI_API_KEY или models.providers.google.apiKey. При использовании пользовательской конечной точки, совместимой с OpenAI, установите memorySearch.remote.apiKey (и опциональные memorySearch.remote.headers).
Дополнительные пути памяти
Если вы хотите индексировать файлы Markdown за пределами стандартной структуры рабочего пространства, добавьте явные пути:
agents: \{
defaults: \{
memorySearch: \{
extraPaths: ["../team-docs", "/srv/shared-notes/overview.md"]
\}
\}
\}
Примечания:
- Пути могут быть абсолютными или относительно рабочего пространства.
- Каталоги сканируются рекурсивно на наличие .md файлов.
- Индексируются только файлы Markdown.
- Символические ссылки игнорируются (файлы или каталоги).
Эмбеддинги Gemini (нативные)
Установите провайдер на gemini для прямого использования API эмбеддингов Gemini:
agents: \{
defaults: \{
memorySearch: \{
provider: "gemini",
model: "gemini-embedding-001",
remote: \{
apiKey: "YOUR_GEMINI_API_KEY"
\}
\}
\}
\}
Примечания:
- remote.baseUrl опционален (по умолчанию используется базовый URL API Gemini).
- remote.headers позволяет добавлять дополнительные заголовки при необходимости.
- Модель по умолчанию: gemini-embedding-001.
Если вы хотите использовать пользовательскую конечную точку, совместимую с OpenAI (OpenRouter, vLLM или прокси), вы можете использовать конфигурацию remote с провайдером OpenAI:
agents: \{
defaults: \{
memorySearch: \{
provider: "openai",
model: "text-embedding-3-small",
remote: \{
baseUrl: "https://api.example.com/v1/",
apiKey: "YOUR_OPENAI_COMPAT_API_KEY",
headers: \{ "X-Custom-Header": "value" \}
\}
\}
\}
\}
Если вы не хотите устанавливать API-ключ, используйте memorySearch.provider = "local" или установите memorySearch.fallback = "none".
Резервные варианты:
- memorySearch.fallback может быть openai, gemini, local или none.
- Резервный провайдер используется только когда основной провайдер эмбеддингов не работает.
Пакетное индексирование (OpenAI + Gemini):
- Включено по умолчанию для эмбеддингов OpenAI и Gemini. Установите agents.defaults.memorySearch.remote.batch.enabled = false для отключения.
- Поведение по умолчанию ожидает завершения пакета; настройте remote.batch.wait, remote.batch.pollIntervalMs и remote.batch.timeoutMinutes при необходимости.
- Установите remote.batch.concurrency для контроля количества пакетных заданий, отправляемых параллельно (по умолчанию: 2).
- Пакетный режим применяется, когда memorySearch.provider = "openai" или "gemini" и использует соответствующий API-ключ.
- Пакетные задания Gemini используют асинхронную конечную точку пакетных эмбеддингов и требуют доступности Gemini Batch API.
Почему пакетный режим OpenAI быстрый + дешевый:
- Для больших обратных заполнений OpenAI обычно является самым быстрым вариантом, который мы поддерживаем, потому что мы можем отправить много запросов эмбеддингов в одном пакетном задании и позволить OpenAI обрабатывать их асинхронно.
- OpenAI предлагает скидки на рабочие нагрузки Batch API, поэтому крупные запуски индексирования обычно дешевле, чем отправка тех же запросов синхронно.
- См. документацию и цены Batch API OpenAI для деталей:
Пример конфигурации:
agents: \{
defaults: \{
memorySearch: \{
provider: "openai",
model: "text-embedding-3-small",
fallback: "openai",
remote: \{
batch: \{ enabled: true, concurrency: 2 \}
\},
sync: \{ watch: true \}
\}
\}
\}
Инструменты:
- memory_search — возвращает фрагменты с путем к файлу + диапазонами строк.
- memory_get — чтение содержимого файла памяти по пути.
Локальный режим:
- Установите agents.defaults.memorySearch.provider = "local".
- Укажите agents.defaults.memorySearch.local.modelPath (GGUF или hf: URI).
- Опционально: установите agents.defaults.memorySearch.fallback = "none" для избежания удаленного резерва.
Как работают инструменты памяти
- memory_search семантически ищет фрагменты Markdown (~400 токенов целевой размер, 80-токенов перекрытие) из MEMORY.md + memory/**/*.md. Возвращает текст фрагмента (ограничен ~700 символов), путь к файлу, диапазон строк, оценку, провайдера/модель и была ли использована резервная замена с local → remote эмбеддингов. Полное содержимое файла не возвращается.
- memory_get читает конкретный файл Markdown памяти (относительно рабочего пространства), опционально с начальной строки и на N строк. Пути вне MEMORY.md / memory/ разрешены только когда явно перечислены в memorySearch.extraPaths.
- Оба инструмента включены только когда memorySearch.enabled разрешается как true для агента.
Что индексируется (и когда)
- Тип файла: только Markdown (MEMORY.md, memory/**/*.md, плюс любые .md файлы в memorySearch.extraPaths).
- Хранилище индекса: SQLite для каждого агента в ~/.openclaw/memory/<agentId>.sqlite (настраивается через agents.defaults.memorySearch.store.path, поддерживает токен \{agentId\}).
- Актуальность: наблюдатель на MEMORY.md, memory/ и memorySearch.extraPaths помечает индекс устаревшим (задержка 1.5s). Синхронизация планируется при запуске сессии, при поиске или по интервалу и выполняется асинхронно. Транскрипты сессий используют пороги дельты для запуска фоновой синхронизации.
- Триггеры переиндексации: индекс хранит эмбеддинг провайдер/модель + отпечаток конечной точки + параметры разбиения на фрагменты. Если любой из них изменяется, OpenClaw автоматически сбрасывает и переиндексирует все хранилище.
Гибридный поиск (BM25 + вектор)
Когда включено, OpenClaw комбинирует:
- Векторное сходство (семантическое совпадение, формулировка может отличаться)
- Релевантность ключевых слов BM25 (точные токены, такие как ID, env переменные, символы кода)
Если полнотекстовый поиск недоступен на вашей платформе, OpenClaw возвращается к векторному поиску.
Зачем гибрид?
Векторный поиск отлично справляется с "это означает то же самое":
- "Mac Studio gateway host" vs "машина, работающая на шлюзе"
- "debounce file updates" vs "избегайте индексации при каждой записи"
Но он может быть слабым для точных высокосигнальных токенов:
- ID (a828e60, b3b9895a…)
- символы кода (memorySearch.query.hybrid)
- строки ошибок ("sqlite-vec unavailable")
BM25 (полный текст) — наоборот: силен в точных токенах, слабее в перефразировках. Гибридный поиск — это прагматичная золотая середина: используйте оба сигнала извлечения, чтобы вы получали хорошие результаты как для запросов на "естественном языке", так и для запросов "иголка в стоге сена".
Как мы объединяем результаты (текущий дизайн)
Эскиз реализации:
- Извлечь пул кандидатов с обеих сторон:
- Вектор: топ maxResults * candidateMultiplier по косинусному сходству.
- BM25: топ maxResults * candidateMultiplier по рангу FTS5 BM25 (ниже лучше).
- Преобразовать ранг BM25 в оценку 0..1-ish:
- textScore = 1 / (1 + max(0, bm25Rank))
- Объединить кандидатов по ID фрагмента и вычислить взвешенную оценку:
- finalScore = vectorWeight * vectorScore + textWeight * textScore
Примечания:
- vectorWeight + textWeight нормализуется к 1.0 в разрешении конфигурации, поэтому веса ведут себя как проценты.
- Если эмбеддинги недоступны (или провайдер возвращает нулевой вектор), мы все равно выполняем BM25 и возвращаем совпадения ключевых слов.
- Если FTS5 не может быть создан, мы сохраняем векторный поиск (без жесткого отказа).
Это не "IR-теория совершенна", но это просто, быстро и обычно улучшает полноту/точность на реальных заметках. Если мы хотим стать более фантазийными позже, обычные следующие шаги — это Reciprocal Rank Fusion (RRF) или нормализация оценки (min/max или z-score) перед смешиванием.
Конфигурация:
agents: \{
defaults: \{
memorySearch: \{
query: \{
hybrid: \{
enabled: true,
vectorWeight: 0.7,
textWeight: 0.3,
candidateMultiplier: 4
\}
\}
\}
\}
\}
Кэш эмбеддингов
OpenClaw может кэшировать эмбеддинги фрагментов в SQLite, чтобы переиндексация и частые обновления (особенно транскрипты сессий) не переэмбеддировали неизмененный текст.
Конфигурация:
agents: \{
defaults: \{
memorySearch: \{
cache: \{
enabled: true,
maxEntries: 50000
\}
\}
\}
\}
Поиск по памяти сессий (экспериментально)
Вы можете опционально индексировать транскрипты сессий и показывать их через memory_search. Это защищено экспериментальным флагом.
agents: \{
defaults: \{
memorySearch: \{
experimental: \{ sessionMemory: true \},
sources: ["memory", "sessions"]
\}
\}
\}
Примечания:
- Индексация сессий включается по желанию (выключена по умолчанию).
- Обновления сессий задерживаются и индексируются асинхронно, как только они пересекают пороги дельты (по мере возможности).
- memory_search никогда не блокируется на индексации; результаты могут быть слегка устаревшими, пока фоновая синхронизация не завершится.
- Результаты все еще включают только фрагменты; memory_get остается ограниченным файлами памяти.
- Индексация сессий изолирована для каждого агента (индексируются только журналы сессий этого агента).
- Журналы сессий находятся на диске (~/.openclaw/agents/<agentId>/sessions/*.jsonl). Любой процесс/пользователь с доступом к файловой системе может их прочитать, поэтому относитесь к доступу к диску как к границе доверия. Для более строгой изоляции запускайте агентов под отдельными пользователями ОС или хостами.
Пороги дельты (показаны значения по умолчанию):
agents: \{
defaults: \{
memorySearch: \{
sync: \{
sessions: \{
deltaBytes: 100000, // ~100 KB
deltaMessages: 50 // строки JSONL
\}
\}
\}
\}
\}
Ускорение векторов SQLite (sqlite-vec)
Когда расширение sqlite-vec доступно, OpenClaw хранит эмбеддинги в виртуальной таблице SQLite (vec0) и выполняет запросы векторного расстояния в базе данных. Это сохраняет поиск быстрым без загрузки каждого эмбеддинга в JS.
Конфигурация (опционально):
agents: \{
defaults: \{
memorySearch: \{
store: \{
vector: \{
enabled: true,
extensionPath: "/path/to/sqlite-vec"
\}
\}
\}
\}
\}
Примечания:
- enabled по умолчанию true; когда отключено, поиск возвращается к косинусному сходству внутри процесса над сохраненными эмбеддингами.
- Если расширение sqlite-vec отсутствует или не загружается, OpenClaw логирует ошибку и продолжает с резервным вариантом JS (без векторной таблицы).
- extensionPath переопределяет путь к встроенному sqlite-vec (полезно для пользовательских сборок или нестандартных мест установки).
Автозагрузка локальных эмбеддингов
- Локальная модель эмбеддингов по умолчанию: hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf (~0.6 GB).
- Когда memorySearch.provider = "local", node-llama-cpp разрешает modelPath; если GGUF отсутствует, он автоматически загружается в кэш (или local.modelCacheDir, если установлен), затем загружается. Загрузки возобновляются при повторной попытке.
- Требование нативной сборки: запустите pnpm approve-builds, выберите node-llama-cpp, затем pnpm rebuild node-llama-cpp.
- Резервный вариант: если локальная настройка не удалась и memorySearch.fallback = "openai", мы автоматически переключаемся на удаленные эмбеддинги (openai/text-embedding-3-small, если не переопределено) и записываем причину.
Пример пользовательской конечной точки, совместимой с OpenAI
agents: \{
defaults: \{
memorySearch: \{
provider: "openai",
model: "text-embedding-3-small",
remote: \{
baseUrl: "https://api.example.com/v1/",
apiKey: "YOUR_REMOTE_API_KEY",
headers: \{
"X-Organization": "org-id",
"X-Project": "project-id"
\}
\}
\}
\}
\}
Примечания:
- remote.* имеет приоритет над models.providers.openai.*.
- remote.headers объединяются с заголовками OpenAI; remote выигрывает при конфликтах ключей. Опустите remote.headers, чтобы использовать значения OpenAI по умолчанию.