Pamięć
Pamięć OpenClaw to zwykły Markdown w obszarze roboczym agenta. Pliki są źródłem prawdy; model „pamięta” tylko to, co zostanie zapisane na dysku. Narzędzia wyszukiwania pamięci są dostarczane przez aktywną wtyczkę pamięci (domyślnie:memory-core). Wtyczki pamięci można wyłączyć za pomocą plugins.slots.memory = "none".
Pliki pamięci (Markdown)
Domyślny układ obszaru roboczego używa dwóch warstw pamięci:memory/YYYY-MM-DD.md- Dziennik dzienny (tylko dopisywanie).
- Odczytywany jest dzień bieżący + wczorajszy na początku sesji.
MEMORY.md(opcjonalne)- Kuratorowana pamięć długoterminowa.
- Ładowana wyłącznie w głównej, prywatnej sesji (nigdy w kontekstach grupowych).
agents.defaults.workspace, domyślnie
~/.openclaw/workspace). Pełny układ opisano w Agent workspace.
Kiedy zapisywać pamięć
- Decyzje, preferencje i trwałe fakty trafiają do
MEMORY.md. - Codzienne notatki i bieżący kontekst trafiają do
memory/YYYY-MM-DD.md. - Jeśli ktoś mówi „zapamiętaj to”, zapisz to (nie trzymaj w RAM-ie).
- Ten obszar wciąż się rozwija. Pomaga przypominanie modelowi o zapisywaniu wspomnień; będzie wiedział, co zrobić.
- Jeśli chcesz, aby coś się utrwaliło, poproś bota o zapisanie tego w pamięci.
Automatyczne opróżnianie pamięci (ping przed kompakcją)
Gdy sesja jest bliska automatycznej kompakcji, OpenClaw uruchamia cichy, agentowy krok, który przypomina modelowi o zapisaniu trwałej pamięci zanim kontekst zostanie skompaktowany. Domyślne prompty wyraźnie mówią, że model może odpowiedzieć, ale zazwyczajNO_REPLY jest poprawną odpowiedzią, dzięki czemu użytkownik nigdy nie widzi tego kroku.
Jest to kontrolowane przez agents.defaults.compaction.memoryFlush:
- Miękki próg: opróżnianie uruchamia się, gdy estymacja tokenów sesji przekroczy
contextWindow - reserveTokensFloor - softThresholdTokens. - Cisza domyślna: prompty zawierają
NO_REPLY, więc nic nie jest dostarczane. - Dwa prompty: prompt użytkownika oraz prompt systemowy dołączają przypomnienie.
- Jedno opróżnienie na cykl kompakcji (śledzone w
sessions.json). - Obszar roboczy musi być zapisywalny: jeśli sesja działa w sandboxie z
workspaceAccess: "ro"lub"none", opróżnianie jest pomijane.
Wyszukiwanie pamięci wektorowej
OpenClaw może zbudować niewielki indeks wektorowy nadMEMORY.md i memory/*.md, aby
zapytania semantyczne mogły znajdować powiązane notatki nawet przy różnym brzmieniu.
Ustawienia domyślne:
- Włączone domyślnie.
- Obserwuje pliki pamięci pod kątem zmian (z opóźnieniem).
- Skonfiguruj wyszukiwanie pamięci w
agents.defaults.memorySearch(nie w głównymmemorySearch). - Domyślnie używa zdalnych embeddingów. Jeśli
memorySearch.providernie jest ustawione, OpenClaw automatycznie wybiera:local, jeśli skonfigurowanomemorySearch.local.modelPathi plik istnieje.openai, jeśli można rozwiązać klucz OpenAI.gemini, jeśli można rozwiązać klucz Gemini.voyage, jeśli można rozwiązać klucz Voyage.- W przeciwnym razie wyszukiwanie pamięci pozostaje wyłączone do czasu konfiguracji.
- Tryb lokalny używa node-llama-cpp i może wymagać
pnpm approve-builds. - Używa sqlite-vec (gdy dostępne) do przyspieszenia wyszukiwania wektorowego w SQLite.
models.providers.*.apiKey lub zmiennych
środowiskowych. OAuth Codex obejmuje tylko czat/uzupełniania i nie spełnia
wymagań embeddingów do wyszukiwania pamięci. Dla Gemini użyj GEMINI_API_KEY lub
models.providers.google.apiKey. Dla Voyage użyj VOYAGE_API_KEY lub
models.providers.voyage.apiKey. Przy użyciu niestandardowego endpointu zgodnego z OpenAI
ustaw memorySearch.remote.apiKey (oraz opcjonalnie memorySearch.remote.headers).
Backend QMD (eksperymentalny)
Ustawmemory.backend = "qmd", aby zastąpić wbudowany indeksator SQLite przez
QMD: lokalny sidecar wyszukiwania łączący
BM25 + wektory + reranking. Markdown pozostaje źródłem prawdy; OpenClaw wywołuje
QMD do pobierania wyników. Kluczowe punkty:
Wymagania wstępne
- Domyślnie wyłączone. Włączane per-konfiguracja (
memory.backend = "qmd"). - Zainstaluj osobno CLI QMD (
bun install -g https://github.com/tobi/qmdlub pobierz wydanie) i upewnij się, że binarkaqmdznajduje się naPATHgateway’a. - QMD wymaga kompilacji SQLite ze wsparciem rozszerzeń (
brew install sqlitena macOS). - QMD działa w pełni lokalnie przez Bun +
node-llama-cppi automatycznie pobiera modele GGUF z HuggingFace przy pierwszym użyciu (nie jest wymagany osobny daemon Ollama). - Gateway uruchamia QMD w samodzielnym katalogu XDG pod
~/.openclaw/agents/<agentId>/qmd/, ustawiającXDG_CONFIG_HOMEorazXDG_CACHE_HOME. - Wsparcie OS: macOS i Linux działają od razu po zainstalowaniu Bun + SQLite. Windows najlepiej wspierany przez WSL2.
- Gateway zapisuje samodzielny katalog domowy QMD pod
~/.openclaw/agents/<agentId>/qmd/(konfiguracja + cache + baza sqlite). - Kolekcje są tworzone przez
qmd collection addzmemory.qmd.paths(plus domyślne pliki pamięci obszaru roboczego), następnieqmd update+qmd embedsą uruchamiane przy starcie oraz w konfigurowalnym interwale (memory.qmd.update.interval, domyślnie 5 min). - Gateway inicjalizuje teraz menedżera QMD przy uruchomieniu, więc okresowe timery aktualizacji
są aktywowane jeszcze przed pierwszym wywołaniem
memory_search. - Odświeżanie przy starcie działa teraz domyślnie w tle, aby nie blokować uruchomienia czatu;
ustaw
memory.qmd.update.waitForBootSync = true, aby zachować poprzednie blokujące zachowanie. - Wyszukiwania są wykonywane przez
qmd query --json. Jeśli wybrany tryb odrzuca flagi w Twojej wersji QMD, OpenClaw ponowi próbę zqmd query. Jeśli QMD zawiedzie lub brakuje binarki, OpenClaw automatycznie wraca do wbudowanego menedżera SQLite, dzięki czemu narzędzia pamięci nadal działają. - OpenClaw nie udostępnia obecnie strojenia rozmiaru batcha embeddingów QMD; zachowanie batcha kontroluje samo QMD.
- Pierwsze wyszukiwanie może być wolne: QMD może pobrać lokalne modele GGUF
(reranker/rozszerzanie zapytań) przy pierwszym uruchomieniu
qmd query.-
OpenClaw automatycznie ustawia
XDG_CONFIG_HOME/XDG_CACHE_HOME, gdy uruchamia QMD. -
Jeśli chcesz wstępnie pobrać modele ręcznie (i rozgrzać ten sam indeks,
którego używa OpenClaw), uruchom jednorazowe zapytanie z katalogami XDG agenta.
Stan QMD OpenClaw znajduje się w Twoim katalogu stanu (domyślnie
~/.openclaw). Możesz wskazaćqmdna dokładnie ten sam indeks, eksportując te same zmienne XDG, których używa OpenClaw:
-
OpenClaw automatycznie ustawia
memory.qmd.*)
command(domyślnieqmd): nadpisanie ścieżki do pliku wykonywalnego.searchMode(domyślniesearch): wybierz, które polecenie QMD obsługujememory_search(search,vsearch,query).includeDefaultMemory(domyślnietrue): automatyczne indeksowanieMEMORY.md+memory/**/*.md.paths[]: dodanie dodatkowych katalogów/plików (path, opcjonalniepattern, opcjonalnie stabilnename).sessions: włączenie indeksowania JSONL sesji (enabled,retentionDays,exportDir).update: kontrola kadencji odświeżania i wykonywania utrzymania: (interval,debounceMs,onBoot,waitForBootSync,embedInterval,commandTimeoutMs,updateTimeoutMs,embedTimeoutMs).limits: ograniczenie ładunku przywołań (maxResults,maxSnippetChars,maxInjectedChars,timeoutMs).scope: ten sam schemat cosession.sendPolicy. Domyślnie tylko DM-y (denywszystkie,allowczaty bezpośrednie); poluzuj, aby ujawniać trafienia QMD w grupach/kanałach.match.keyPrefixdopasowuje znormalizowany klucz sesji (zamieniony na małe litery, z usuniętym początkowymagent:<id>:). Przykład:discord:channel:.match.rawKeyPrefixdopasowuje surowy klucz sesji (zamieniony na małe litery), włącznie zagent:<id>:. Przykład:agent:main:discord:.- Legacy:
match.keyPrefix: "agent:..."jest nadal traktowane jako prefiks surowego klucza, ale dla jasności zalecane jest użycierawKeyPrefix.
- When
scopedenies a search, OpenClaw logs a warning with the derivedchannel/chatTypeso empty results are easier to debug. - Fragmenty pochodzące spoza obszaru roboczego pojawiają się jako
qmd/<collection>/<relative-path>w wynikachmemory_search;memory_getrozumie ten prefiks i czyta z skonfigurowanego katalogu głównego kolekcji QMD. - Gdy
memory.qmd.sessions.enabled = true, OpenClaw eksportuje zanonimizowane transkrypty sesji (tury Użytkownik/Asystent) do dedykowanej kolekcji QMD pod~/.openclaw/agents/<id>/qmd/sessions/, dzięki czemumemory_searchmoże przywoływać ostatnie rozmowy bez dotykania wbudowanego indeksu SQLite. - Fragmenty
memory_searchzawierają teraz stopkęSource: <path#line>, gdymemory.citationsma wartośćauto/on; ustawmemory.citations = "off", aby zachować metadane ścieżki jako wewnętrzne (agent nadal otrzymuje ścieżkę domemory_get, ale tekst fragmentu pomija stopkę, a prompt systemowy ostrzega agenta, aby jej nie cytował).
memory.citationsobowiązuje niezależnie od backendu (auto/on/off).- Gdy działa
qmd, oznaczamystatus().backend = "qmd", aby diagnostyka pokazywała, który silnik obsłużył wyniki. Jeśli podproces QMD zakończy się lub wyjście JSON nie może zostać sparsowane, menedżer wyszukiwania loguje ostrzeżenie i zwraca wbudowanego dostawcę (istniejące embeddingi Markdown), dopóki QMD się nie podniesie.
Dodatkowe ścieżki pamięci
Jeśli chcesz indeksować pliki Markdown poza domyślnym układem obszaru roboczego, dodaj jawne ścieżki:- Ścieżki mogą być bezwzględne lub względne względem obszaru roboczego.
- Katalogi są skanowane rekurencyjnie w poszukiwaniu plików
.md. - Indeksowane są tylko pliki Markdown.
- Dowiązania symboliczne są ignorowane (pliki lub katalogi).
Embeddingi Gemini (natywne)
Ustaw dostawcę nagemini, aby używać bezpośrednio API embeddingów Gemini:
remote.baseUrljest opcjonalne (domyślnie bazowy URL API Gemini).remote.headerspozwala dodać dodatkowe nagłówki, jeśli są potrzebne.- Domyślny model:
gemini-embedding-001.
remote z dostawcą OpenAI:
memorySearch.provider = "local" lub ustaw
memorySearch.fallback = "none".
Fallbacki:
memorySearch.fallbackmoże mieć wartośćopenai,gemini,locallubnone.- Dostawca fallback jest używany tylko wtedy, gdy podstawowy dostawca embeddingów zawiedzie.
- Włączone domyślnie dla embeddingów OpenAI i Gemini. Ustaw
agents.defaults.memorySearch.remote.batch.enabled = false, aby wyłączyć. - Domyślne zachowanie czeka na zakończenie batcha; dostrój
remote.batch.wait,remote.batch.pollIntervalMsiremote.batch.timeoutMinutes, jeśli potrzeba. - Ustaw
remote.batch.concurrency, aby kontrolować liczbę równoległych zadań batch (domyślnie: 2). - Tryb batch obowiązuje, gdy
memorySearch.provider = "openai"lub"gemini"i używa odpowiadającego klucza API. - Zadania batch Gemini używają asynchronicznego endpointu batch embeddingów i wymagają dostępności Gemini Batch API.
- Dla dużych uzupełnień wstecznych OpenAI jest zwykle najszybszą wspieraną opcją, ponieważ możemy wysyłać wiele żądań embeddingów w jednym zadaniu batch i pozwolić OpenAI przetwarzać je asynchronicznie.
- OpenAI oferuje obniżone ceny dla obciążeń Batch API, więc duże przebiegi indeksowania są zwykle tańsze niż wysyłanie tych samych żądań synchronicznie.
- Szczegóły w dokumentacji i cenniku OpenAI Batch API:
memory_search— zwraca fragmenty z plikiem + zakresami linii.memory_get— odczyt zawartości pliku pamięci po ścieżce.
- Ustaw
agents.defaults.memorySearch.provider = "local". - Podaj
agents.defaults.memorySearch.local.modelPath(GGUF lub URIhf:). - Opcjonalnie: ustaw
agents.defaults.memorySearch.fallback = "none", aby uniknąć zdalnego fallbacku.
Jak działają narzędzia pamięci
memory_searchprzeszukuje semantycznie fragmenty Markdown (~400 tokenów docelowo, 80-tokenowa nakładka) zMEMORY.md+memory/**/*.md. Zwraca tekst fragmentu (limit ~700 znaków), ścieżkę pliku, zakres linii, wynik, dostawcę/model oraz informację, czy nastąpił fallback z lokalnych → zdalnych embeddingów. Nie jest zwracana pełna zawartość pliku.memory_getodczytuje konkretny plik Markdown pamięci (względny względem obszaru roboczego), opcjonalnie od wskazanej linii i przez N linii. Ścieżki pozaMEMORY.md/memory/są odrzucane.- Oba narzędzia są włączone tylko wtedy, gdy
memorySearch.enabledrozwiązuje się jako true dla agenta.
Co jest indeksowane (i kiedy)
- Typ pliku: tylko Markdown (
MEMORY.md,memory/**/*.md). - Przechowywanie indeksu: SQLite per-agent w
~/.openclaw/memory/<agentId>.sqlite(konfigurowalne przezagents.defaults.memorySearch.store.path, obsługuje token{agentId}). - Świeżość: watcher na
MEMORY.md+memory/oznacza indeks jako brudny (debounce 1,5 s). Synchronizacja jest planowana na start sesji, przy wyszukiwaniu lub w interwale i działa asynchronicznie. Transkrypty sesji używają progów delta do wyzwalania synchronizacji w tle. - Wyzwalacze reindeksacji: indeks przechowuje dostawcę/model embeddingów + odcisk endpointu + parametry chunkowania. Jeśli którekolwiek z nich się zmieni, OpenClaw automatycznie resetuje i reindeksuje cały magazyn.
Wyszukiwanie hybrydowe (BM25 + wektor)
Gdy włączone, OpenClaw łączy:- Podobieństwo wektorowe (dopasowanie semantyczne, brzmienie może się różnić)
- Trafność słów kluczowych BM25 (dokładne tokeny, jak identyfikatory, zmienne środowiskowe, symbole kodu)
Dlaczego hybryda?
Wyszukiwanie wektorowe świetnie radzi sobie z „to znaczy to samo”:- „Mac Studio gateway host” vs „maszyna uruchamiająca gateway”
- „debounce aktualizacje plików” vs „unikać indeksowania przy każdym zapisie”
- identyfikatory (
a828e60,b3b9895a…) - symbole kodu (
memorySearch.query.hybrid) - ciągi błędów („sqlite-vec unavailable”)
Jak łączymy wyniki (obecny projekt)
Szkic implementacji:- Pobierz pulę kandydatów z obu stron:
- Wektor: top
maxResults * candidateMultiplierwedług podobieństwa cosinusowego. - BM25: top
maxResults * candidateMultiplierwedług rangi FTS5 BM25 (niżej = lepiej).
- Przekształć rangę BM25 w wynik ~0..1:
textScore = 1 / (1 + max(0, bm25Rank))
- Połącz kandydatów po id fragmentu i oblicz wynik ważony:
finalScore = vectorWeight * vectorScore + textWeight * textScore
vectorWeight+textWeightjest normalizowane do 1,0 podczas rozwiązywania konfiguracji, więc wagi zachowują się jak procenty.- Jeśli embeddingi są niedostępne (lub dostawca zwraca wektor zerowy), nadal uruchamiamy BM25 i zwracamy dopasowania słów kluczowych.
- Jeśli FTS5 nie może zostać utworzone, pozostajemy przy wyszukiwaniu tylko wektorowym (bez twardej awarii).
Cache embeddingów
OpenClaw może buforować embeddingi fragmentów w SQLite, aby reindeksowanie i częste aktualizacje (zwłaszcza transkrypty sesji) nie wymagały ponownego embedowania niezmienionego tekstu. Konfiguracja:Wyszukiwanie pamięci sesji (eksperymentalne)
Opcjonalnie możesz indeksować transkrypty sesji i udostępniać je przezmemory_search.
Funkcja jest ukryta za flagą eksperymentalną.
- Indeksowanie sesji jest opcjonalne (domyślnie wyłączone).
- Aktualizacje sesji są debouncowane i indeksowane asynchronicznie po przekroczeniu progów delta (best-effort).
memory_searchnigdy nie blokuje na indeksowaniu; wyniki mogą być lekko nieaktualne do czasu zakończenia synchronizacji w tle.- Wyniki nadal zawierają tylko fragmenty;
memory_getpozostaje ograniczone do plików pamięci. - Indeksowanie sesji jest izolowane per agent (indeksowane są tylko logi sesji tego agenta).
- Logi sesji znajdują się na dysku (
~/.openclaw/agents/<agentId>/sessions/*.jsonl). Każdy proces/użytkownik z dostępem do systemu plików może je odczytać, więc granicą zaufania jest dostęp do dysku. Dla ostrzejszej izolacji uruchamiaj agentów pod oddzielnymi użytkownikami systemu lub hostami.
Przyspieszenie wektorów SQLite (sqlite-vec)
Gdy rozszerzenie sqlite-vec jest dostępne, OpenClaw przechowuje embeddingi w wirtualnej tabeli SQLite (vec0) i wykonuje zapytania odległości wektorów w
bazie danych. Utrzymuje to szybkie wyszukiwanie bez ładowania każdego embeddingu do JS.
Konfiguracja (opcjonalna):
enableddomyślnie ma wartość true; po wyłączeniu wyszukiwanie wraca do obliczania podobieństwa cosinusowego w procesie nad zapisanymi embeddingami.- Jeśli rozszerzenie sqlite-vec jest niedostępne lub nie uda się go załadować, OpenClaw loguje błąd i kontynuuje z fallbackiem JS (bez tabeli wektorowej).
extensionPathnadpisuje dołączoną ścieżkę sqlite-vec (przydatne dla niestandardowych buildów lub niestandardowych lokalizacji instalacji).
Automatyczne pobieranie lokalnych embeddingów
- Domyślny lokalny model embeddingów:
hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf(~0,6 GB). - Gdy
memorySearch.provider = "local",node-llama-cpprozwiązujemodelPath; jeśli GGUF brakuje, jest automatycznie pobierany do cache (lublocal.modelCacheDir, jeśli ustawione), a następnie ładowany. Pobieranie jest wznawiane przy ponownej próbie. - Wymaganie natywnej kompilacji: uruchom
pnpm approve-builds, wybierznode-llama-cpp, następniepnpm rebuild node-llama-cpp. - Fallback: jeśli lokalna konfiguracja się nie powiedzie i
memorySearch.fallback = "openai", automatycznie przełączamy się na zdalne embeddingi (openai/text-embedding-3-small, o ile nie nadpisano) i zapisujemy powód.
Przykład niestandardowego endpointu zgodnego z OpenAI
remote.*ma pierwszeństwo przedmodels.providers.openai.*.remote.headersłączą się z nagłówkami OpenAI; zdalne wygrywają przy konfliktach kluczy. Pomińremote.headers, aby użyć domyślnych ustawień OpenAI.