Память
Память OpenClaw — это обычный Markdown в рабочем пространстве агента. Файлы являются источником истины; модель «помнит» только то, что записано на диск. Инструменты поиска по памяти предоставляются активным плагином памяти (по умолчанию:memory-core). Отключить плагины памяти можно с помощью plugins.slots.memory = "none".
Файлы памяти (Markdown)
Макет рабочего пространства по умолчанию использует два уровня памяти:memory/YYYY-MM-DD.md- Ежедневный журнал (только добавление).
- Читается сегодняшний и вчерашний день при старте сеанса.
MEMORY.md(необязательно)- Кураторская долгосрочная память.
- Загружается только в основном, приватном сеансе (никогда в групповых контекстах).
agents.defaults.workspace, по умолчанию
~/.openclaw/workspace). Полную структуру см. в разделе Agent workspace.
Когда записывать в память
- Решения, предпочтения и устойчивые факты записывайте в
MEMORY.md. - Повседневные заметки и текущий контекст — в
memory/YYYY-MM-DD.md. - Если кто-то говорит «запомни это», запишите это (не храните в RAM).
- Этот раздел всё ещё развивается. Полезно напоминать модели сохранять воспоминания; она знает, что делать.
- Если вы хотите, чтобы что-то сохранилось, попросите бота записать это в память.
Автоматический сброс памяти (предварительный пинг перед уплотнением)
Когда сеанс приближается к авто-уплотнению, OpenClaw запускает тихий, агентный ход, который напоминает модели записать устойчивую память до уплотнения контекста. В подсказках по умолчанию прямо сказано, что модель может ответить, но обычно правильным ответом являетсяNO_REPLY, чтобы пользователь не видел этот ход.
Это управляется через agents.defaults.compaction.memoryFlush:
- Мягкий порог: сброс срабатывает, когда оценка токенов сеанса превышает
contextWindow - reserveTokensFloor - softThresholdTokens. - Тихо по умолчанию: подсказки включают
NO_REPLY, поэтому ничего не доставляется. - Две подсказки: пользовательская и системная подсказки добавляют напоминание.
- Один сброс за цикл уплотнения (отслеживается в
sessions.json). - Рабочее пространство должно быть доступно для записи: если сеанс выполняется в sandbox с
workspaceAccess: "ro"или"none", сброс пропускается.
Векторный поиск по памяти
OpenClaw может построить небольшой векторный индекс поверхMEMORY.md и memory/*.md, чтобы
семантические запросы находили связанные заметки даже при различной формулировке.
Значения по умолчанию:
- Включено по умолчанию.
- Отслеживает изменения файлов памяти (с debounce).
- Настройте поиск памяти в разделе
agents.defaults.memorySearch(а не в корневомmemorySearch). - По умолчанию использует удалённые эмбеддинги. Если
memorySearch.providerне задан, OpenClaw автоматически выбирает:local, если настроенmemorySearch.local.modelPathи файл существует.openai, если удаётся определить ключ OpenAI.gemini, если удаётся определить ключ Gemini.voyage, если удаётся определить ключ Voyage.- В противном случае поиск по памяти остаётся отключённым до настройки.
- Локальный режим использует node-llama-cpp и может требовать
pnpm approve-builds. - Использует sqlite-vec (когда доступен) для ускорения векторного поиска внутри SQLite.
models.providers.*.apiKey или переменных
окружения. Codex OAuth покрывает только chat/completions и не подходит для
эмбеддингов поиска по памяти. Для Gemini используйте GEMINI_API_KEY или
models.providers.google.apiKey. Для Voyage используйте VOYAGE_API_KEY или
models.providers.voyage.apiKey. При использовании пользовательского OpenAI-совместимого эндпоинта
задайте memorySearch.remote.apiKey (и необязательно memorySearch.remote.headers).
Бэкенд QMD (экспериментально)
Установитеmemory.backend = "qmd", чтобы заменить встроенный индексатор SQLite на
QMD: локальный поисковый сайдкар, объединяющий
BM25 + векторы + переранжирование. Markdown остаётся источником истины; OpenClaw
вызывает QMD для извлечения. Ключевые моменты:
Предварительные требования
- По умолчанию отключено. Включение — на уровне конфига (
memory.backend = "qmd"). - Установите CLI QMD отдельно (
bun install -g https://github.com/tobi/qmdили скачайте релиз) и убедитесь, что бинарникqmdнаходится вPATHшлюза. - QMD требует сборку SQLite с поддержкой расширений (
brew install sqliteна macOS). - QMD полностью работает локально через Bun +
node-llama-cppи автоматически загружает модели GGUF с HuggingFace при первом использовании (отдельный демон Ollama не требуется). - Gateway (шлюз) запускает QMD в изолированном XDG home под
~/.openclaw/agents/<agentId>/qmd/, устанавливаяXDG_CONFIG_HOMEиXDG_CACHE_HOME. - Поддержка ОС: macOS и Linux работают «из коробки» после установки Bun + SQLite. Windows лучше всего поддерживается через WSL2.
- Gateway (шлюз) создаёт самодостаточный QMD home под
~/.openclaw/agents/<agentId>/qmd/(конфиг + кэш + sqlite DB). - Коллекции создаются через
qmd collection addизmemory.qmd.paths(плюс файлы памяти рабочего пространства по умолчанию), затемqmd update+qmd embedвыполняются при загрузке и с настраиваемым интервалом (memory.qmd.update.interval, по умолчанию 5 м). - Gateway теперь инициализирует менеджер QMD при запуске, поэтому таймеры периодического обновления
активируются ещё до первого вызова
memory_search. - Обновление при загрузке теперь по умолчанию выполняется в фоне, чтобы запуск чата
не блокировался; установите
memory.qmd.update.waitForBootSync = true, чтобы сохранить прежнее блокирующее поведение. - Поиск выполняется через
qmd query --json. Если выбранный режим отклоняет флаги в вашей сборке QMD, OpenClaw повторяет попытку сqmd query. Если QMD падает или бинарник отсутствует, OpenClaw автоматически возвращается к встроенному менеджеру SQLite, чтобы инструменты памяти продолжали работать. - OpenClaw сегодня не предоставляет настройку batch-size эмбеддингов QMD; пакетное поведение контролируется самим QMD.
- Первый поиск может быть медленным: QMD может загружать локальные модели GGUF
(переранжирование/расширение запроса) при первом запуске
qmd query.-
OpenClaw автоматически устанавливает
XDG_CONFIG_HOME/XDG_CACHE_HOME, когда запускает QMD. -
Если вы хотите предварительно загрузить модели вручную (и прогреть тот же индекс,
который использует OpenClaw), выполните одноразовый запрос с XDG-директориями агента.
Состояние QMD OpenClaw находится в вашем каталоге состояния (по умолчанию
~/.openclaw). Вы можете направитьqmdна точно такой же индекс, экспортировав те же XDG-переменные, которые использует OpenClaw:
-
OpenClaw автоматически устанавливает
memory.qmd.*)
command(по умолчаниюqmd): переопределить путь к исполняемому файлу.includeDefaultMemory(по умолчаниюtrue): автоиндексацияMEMORY.md+memory/**/*.md.includeDefaultMemory(по умолчаниюtrue): автоиндексацияMEMORY.md+memory/**/*.md.paths[]: добавить дополнительные каталоги/файлы (path, необязательноpattern, необязательно стабильныйname).sessions: включить индексацию JSONL сеансов (enabled,retentionDays,exportDir).update: управляет частотой обновления и выполнением обслуживания: (interval,debounceMs,onBoot,waitForBootSync,embedInterval,commandTimeoutMs,updateTimeoutMs,embedTimeoutMs).limits: ограничение полезной нагрузки recall (maxResults,maxSnippetChars,maxInjectedChars,timeoutMs).scope: та же схема, что иsession.sendPolicy. По умолчанию — только DMs (denyвсе,allowпрямые чаты); ослабьте, чтобы показывать результаты QMD в группах/каналах.match.keyPrefixсопоставляется с нормализованным ключом сессии (в нижнем регистре, с удалённым ведущимagent:<id>:). Пример:discord:channel:.match.rawKeyPrefixсопоставляется с сырым ключом сессии (в нижнем регистре), включаяagent:<id>:. Пример:agent:main:discord:.- Устаревшее:
match.keyPrefix: "agent:..."по-прежнему рассматривается как префикс сырого ключа, однако для ясности предпочтительнее использоватьrawKeyPrefix.
- Когда
scopeзапрещает поиск, OpenClaw записывает предупреждение с вычисленнымиchannel/chatType, чтобы пустые результаты было проще отлаживать. - Фрагменты, полученные вне рабочего пространства, отображаются как
qmd/<collection>/<relative-path>в результатахmemory_search;memory_getпонимает этот префикс и читает из настроенного корня коллекции QMD. - Когда
memory.qmd.sessions.enabled = true, OpenClaw экспортирует очищенные транскрипты сеансов (ходы User/Assistant) в отдельную коллекцию QMD под~/.openclaw/agents/<id>/qmd/sessions/, чтобыmemory_searchмог вспоминать недавние разговоры, не затрагивая встроенный индекс SQLite. - Фрагменты
memory_searchтеперь включают подвалSource: <path#line>, когдаmemory.citationsравноauto/on; установитеmemory.citations = "off", чтобы оставить метаданные пути внутренними (агент всё равно получает путь дляmemory_get, но текст фрагмента опускает подвал, а системная подсказка предупреждает агента не цитировать его).
memory.citationsприменяется независимо от бэкенда (auto/on/off).- Когда выполняется
qmd, мы помечаемstatus().backend = "qmd", чтобы диагностика показывала, какой движок выдал результаты. Если подпроцесс QMD завершается или JSON-вывод не удаётся разобрать, менеджер поиска пишет предупреждение и возвращает встроенного провайдера (существующие эмбеддинги Markdown), пока QMD не восстановится.
Дополнительные пути памяти
Если вы хотите индексировать Markdown-файлы вне макета рабочего пространства по умолчанию, добавьте явные пути:- Пути могут быть абсолютными или относительными к рабочему пространству.
- Каталоги сканируются рекурсивно на наличие файлов
.md. - Индексируются только Markdown-файлы.
- Символические ссылки игнорируются (файлы и каталоги).
Эмбеддинги Gemini (нативно)
Установите провайдерgemini, чтобы использовать API эмбеддингов Gemini напрямую:
remote.baseUrlнеобязателен (по умолчанию — базовый URL API Gemini).remote.headersпозволяет добавлять дополнительные заголовки при необходимости.- Модель по умолчанию:
gemini-embedding-001.
remote с провайдером OpenAI:
memorySearch.provider = "local" или установите
memorySearch.fallback = "none".
Fallback’и:
memorySearch.fallbackможет бытьopenai,gemini,localилиnone.- Провайдер fallback используется только когда основной провайдер эмбеддингов не срабатывает.
- Включена по умолчанию для эмбеддингов OpenAI и Gemini. Установите
agents.defaults.memorySearch.remote.batch.enabled = false, чтобы отключить. - Поведение по умолчанию ждёт завершения пакета; при необходимости настройте
remote.batch.wait,remote.batch.pollIntervalMsиremote.batch.timeoutMinutes. - Установите
remote.batch.concurrencyдля управления числом параллельно отправляемых batch-задач (по умолчанию: 2). - Пакетный режим применяется, когда
memorySearch.provider = "openai"или"gemini", и использует соответствующий ключ API. - Пакетные задания Gemini используют асинхронный batch-эндпоинт эмбеддингов и требуют доступности Gemini Batch API.
- Для крупных обратных заполнений OpenAI обычно самый быстрый поддерживаемый вариант, потому что мы можем отправлять множество запросов эмбеддингов в одном batch-задании и позволить OpenAI обрабатывать их асинхронно.
- OpenAI предлагает скидки для нагрузок Batch API, поэтому крупные прогоны индексации обычно дешевле, чем отправка тех же запросов синхронно.
- Подробности см. в документации и ценах OpenAI Batch API:
memory_search— возвращает фрагменты с файлом и диапазонами строк.memory_get— читает содержимое файла памяти по пути.
- Установите
agents.defaults.memorySearch.provider = "local". - Укажите
agents.defaults.memorySearch.local.modelPath(GGUF или URIhf:). - Необязательно: установите
agents.defaults.memorySearch.fallback = "none", чтобы избежать удалённого fallback.
Как работают инструменты памяти
memory_searchвыполняет семантический поиск по Markdown-фрагментам (целевой размер ~400 токенов, перекрытие 80 токенов) изMEMORY.md+memory/**/*.md. Он возвращает текст фрагмента (ограничение ~700 символов), путь к файлу, диапазон строк, оценку, провайдера/модель и факт fallback’а с локальных → удалённые эмбеддинги. Полные файлы не возвращаются.memory_getчитает конкретный Markdown-файл памяти (относительно рабочего пространства), при необходимости начиная с указанной строки и на N строк. Пути внеMEMORY.md/memory/отклоняются.- Оба инструмента включены только когда
memorySearch.enabledвычисляется как true для агента.
Что индексируется (и когда)
- Тип файла: только Markdown (
MEMORY.md,memory/**/*.md). - Хранилище индекса: SQLite для каждого агента в
~/.openclaw/memory/<agentId>.sqlite(настраивается черезagents.defaults.memorySearch.store.path, поддерживает токен{agentId}). - Актуальность: наблюдатель за
MEMORY.md+memory/помечает индекс «грязным» (debounce 1,5 с). Синхронизация планируется при старте сеанса, при поиске или по интервалу и выполняется асинхронно. Транскрипты сеансов используют пороги дельты для запуска фоновой синхронизации. - Триггеры переиндексации: индекс хранит провайдера/модель эмбеддингов + отпечаток эндпоинта + параметры чанкинга. При изменении любого из них OpenClaw автоматически сбрасывает и переиндексирует всё хранилище.
Гибридный поиск (BM25 + вектор)
Когда включён, OpenClaw объединяет:- Векторное сходство (семантическое совпадение, формулировки могут отличаться)
- Ключевую релевантность BM25 (точные токены, такие как ID, переменные окружения, символы кода)
Зачем гибрид?
Векторный поиск отлично справляется с «это означает то же самое»:- «Mac Studio gateway host» vs «the machine running the gateway»
- «debounce file updates» vs «avoid indexing on every write»
- ID (
a828e60,b3b9895a…) - символы кода (
memorySearch.query.hybrid) - строки ошибок («sqlite-vec unavailable»)
Как мы объединяем результаты (текущий дизайн)
Набросок реализации:- Получаем пул кандидатов с обеих сторон:
- Вектор: top
maxResults * candidateMultiplierпо косинусному сходству. - BM25: top
maxResults * candidateMultiplierпо рангу FTS5 BM25 (меньше — лучше).
- Преобразуем ранг BM25 в оценку примерно 0..1:
textScore = 1 / (1 + max(0, bm25Rank))
- Объединяем кандидатов по id чанка и считаем взвешенную оценку:
finalScore = vectorWeight * vectorScore + textWeight * textScore
vectorWeight+textWeightнормализуются до 1.0 при разрешении конфига, поэтому веса ведут себя как проценты.- Если эмбеддинги недоступны (или провайдер возвращает нулевой вектор), мы всё равно выполняем BM25 и возвращаем совпадения по ключевым словам.
- Если FTS5 не удаётся создать, мы сохраняем поиск только по векторам (без жёсткого сбоя).
Кэш эмбеддингов
OpenClaw может кэшировать эмбеддинги чанков в SQLite, чтобы переиндексация и частые обновления (особенно транскрипты сеансов) не переэмбеддили неизменённый текст. Конфиг:Поиск по памяти сеанса (экспериментально)
При желании можно индексировать транскрипты сеансов и показывать их черезmemory_search.
Это скрыто за экспериментальным флагом.
- Индексация сеансов — opt-in (по умолчанию выключено).
- Обновления сеансов дебаунсятся и индексируются асинхронно после превышения порогов дельты (best-effort).
memory_searchникогда не блокируется на индексации; результаты могут быть слегка устаревшими, пока не завершится фоновая синхронизация.- Результаты по-прежнему содержат только фрагменты;
memory_getостаётся ограниченным файлами памяти. - Индексация сеансов изолирована на агента (индексируются только логи сеансов этого агента).
- Логи сеансов хранятся на диске (
~/.openclaw/agents/<agentId>/sessions/*.jsonl). Любой процесс/пользователь с доступом к файловой системе может их читать, поэтому границей доверия является доступ к диску. Для более строгой изоляции запускайте агентов под разными пользователями ОС или на разных хостах.
Ускорение векторов SQLite (sqlite-vec)
Когда доступно расширение sqlite-vec, OpenClaw хранит эмбеддинги в виртуальной таблице SQLite (vec0) и выполняет запросы расстояния векторов
в базе данных. Это сохраняет высокую скорость поиска без загрузки всех эмбеддингов в JS.
Конфигурация (необязательно):
enabledпо умолчанию true; при отключении поиск возвращается к вычислению косинусного сходства в процессе по сохранённым эмбеддингам.- Если расширение sqlite-vec отсутствует или не загружается, OpenClaw логирует ошибку и продолжает с JS-fallback’ом (без векторной таблицы).
extensionPathпереопределяет путь к встроенному sqlite-vec (полезно для кастомных сборок или нестандартных мест установки).
Автозагрузка локальных эмбеддингов
- Локальная модель эмбеддингов по умолчанию:
hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf(~0,6 ГБ). - Когда
memorySearch.provider = "local",node-llama-cppразрешается вmodelPath; если GGUF отсутствует, он автоматически загружается в кэш (илиlocal.modelCacheDir, если задан), затем загружается в память. Загрузки возобновляются при повторе. - Требование нативной сборки: выполните
pnpm approve-builds, выберитеnode-llama-cpp, затемpnpm rebuild node-llama-cpp. - Fallback: если локальная настройка не удалась и
memorySearch.fallback = "openai", мы автоматически переключаемся на удалённые эмбеддинги (openai/text-embedding-3-small, если не переопределено) и фиксируем причину.
Пример пользовательского OpenAI-совместимого эндпоинта
remote.*имеет приоритет надmodels.providers.openai.*.remote.headersобъединяются с заголовками OpenAI; при конфликте ключей побеждают удалённые. Уберитеremote.headers, чтобы использовать значения OpenAI по умолчанию.