Konačno razumeo sistem memorije OpenClaw-a: Markdown čuva izvorne podatke, SQLite radi vektorski indeks
Iskreno, ova dva dana me je mehanizam memorije OpenClaw-a ozbiljno zbunio.
U početku sam mislio da je memorija prosto MEMORY.md plus memory/*.md Markdown fajlovi. Ali dok sam istraživao, iznenada se pojavio još jedan ~/.openclaw/memory/paismart.sqlite. U mojoj glavi bio je samo jedan upitnik: zar već imamo md, kako se sada pojavljuje i sqlite? Da li je ovo nadogradnja ili sam nešto pogrešno razumeo?
Zatim sam, uz istovremeno proveravanje konfiguracije, čitanje logova i igranje sa Ollama embedding-om, konačno razjasnio stvar:OpenClaw ne nadograđuje memoriju iz Markdown-a u SQLite, već „sadržaj memorije“ i „pretragu memorije“ razdvaja u dva sloja. Markdown je zadužen za čuvanje, SQLite za pronalaženje.
Kada jednom razumete ovu razliku, mnoge pojave kasnije dobiju objašnjenje. Na primer, zašto ispod workspace-a jasno postoji MEMORY.md, a memory_search ne pronalazi; zašto sam za model razgovora konfigurisao GPT-5.4, dok vektorski indeks radi sa Ollama-inim nomic-embed-text; ili zašto sam jasno izmenio konfiguraciju, a provider i dalje stoji kao none.
Ako se i vi u ovom trenutku mučite sa OpenClaw-om, ili planirate da svom Agentu dodate sposobnost „dugoročne memorije + semantičke pretrage“, preporučujem vam da strpljivo pročitate ovaj članak. Jer ću vam razložiti zamke u koje sam ovog puta upao, konačno rešenje koje je proradilo i razmišljanje koje stoji iza ovog dizajna.
01. Šta sam u početku pogrešno razumeo
Moja početna zabluda je zapravo veoma tipična: shvatio sam „fajlove memorije“ i „indeks memorije“ kao jednu te istu stvar.
U intuiciji mnogih, pošto već postoje fajlovi poput MEMORY.md i memory/YYYY-MM-DD.md, ne bi li sistem memorije trebalo da se vrti isključivo oko tih fajlova? Ti kažeš da zapamtim — ja to upišem u Markdown; sledeći put me pitaš — ja izvadim iz Markdown-a. Logika je potpuno zatvorena, izgleda bez problema.
Problem je upravo tu.
„Moći sačuvati“ i „moći efikasno pronaći“ zapravo su dve različite stvari.
Kada upišete sadržaj u Markdown, zaista ste završili korak „čuvanja“. Ali kada kasnije pretražujete, posebno ako pitate na način koji više liči na razgovor sa čovekom — na primer „da li ranije više volim povremeno izveštavanje o napretku u malim koracima“, „šta smo prošli put diskutovali o vektorskom indeksu“, „kako se konfiguriše ono rešenje sa Ollama embedding-om“ — ako se oslonite samo na poklapanje ključnih reči, poklapanje je zapravo prilično slabo.
Jer način na koji stvarno postavljate pitanje često se ne poklapa sa originalom. Zapisali ste „posle svakog malog koraka izvesti o napretku, ne čekati da se sve završi pa onda povratnu informaciju“, ali pri pretrazi pitate „preferenca u komunikaciji“, „ritam povratne informacije“, „da li ne treba sve završiti pa tek reći“. U tom trenutku čisto poklapanje teksta počinje da gubi dah.
Upravo iz tog razloga OpenClaw memory deli u dva sloja: jedan sloj su Markdown fajlovi koje zaista možete videti, uređivati i trajno čuvati; drugi sloj je SQLite indeks koji se automatski održava u pozadini i služi za podršku semantičkoj pretrazi.
[Ovde umetnite snimak strukture direktorijuma memorije: cilj snimka: dokazati da OpenClaw istovremeno ima workspace/memory i ~/.openclaw/memory, dva skupa direktorijuma; ključne reči: workspace memory, sqlite, paismart.sqlite; preporučeno mesto: Finder ili lista direktorijuma u terminalu]
02. U koja dva sloja je podeljena memorija OpenClaw-a
Prvo da iznesemo zaključak:
~/.openclaw/workspace/MEMORY.mdi~/.openclaw/workspace/memory/*.md— to jesloj izvornih podataka.~/.openclaw/memory/*.sqlite— to jesloj indeksa.
Ova dva sloja nisu u odnosu zamene, već u odnosu saradnje.
Markdown: pravi sadržaj memorije
Markdown fajlovi su vaša prava „suština memorije“.
Njegove prednosti su vrlo proste, ali veoma važne.
Prvo, to je čist tekst; možete ga direktno otvoriti, pregledati, uređivati i predati Git-u. Ako nastane problem, možete ga proveriti golim okom, umesto da buljite u gomilu binarnih struktura.
Drugo, prirodno je pogodan za dugoročno akumuliranje. Na primer, MEMORY.md čuva dugoročne preferencije, važne zaključke, stabilan kontekst; memory/YYYY-MM-DD.md čuva dnevne događaje, prolazne napretke, zapise o otklanjanju problema. Ovakva podela je mnogo prijatnija nego ugurati sve u jedno polje baze podataka.
Treće, dovoljno je bezbedan. Čak i ako kasnije vektorski model padne, SQLite se sruši, provider embedding-a se promeni — Markdown je tu, memorija je tu. Nećete izgubiti stvarni istorijski sadržaj zbog jednog neuspeha obnove indeksa.
SQLite: radi „pretragu koja više liči na čoveka“
SQLite ovde nije glavni lik, ali je nosilac efikasnosti.
Njegov zadatak nije „čuvanje činjenica“, već „učiniti činjenice lakšim za pronalaženje“.
OpenClaw seče sadržaj Markdown-a na nekoliko chunk-ova, zatim ih predaje embedding modelu na vektorizaciju i konačno te vektore i informacije o isečcima čuva u SQLite-u. Kada kasnije pozovete memory_search, on i vaš upit pretvara u vektor, upoređuje sa chunk-ovima u indeksu po sličnosti i vraća vam najrelevantnije fragmente.
Zato ćete videti jednu veoma zanimljivu pojavu: iako u oba slučaja pitamo memoriju,read MEMORY.md vraća originalni tekst, dok memory_search pronalazi relevantne fragmente.
Jedno je više „originalni sadržaj“, drugo je više „ulaz za pretragu“.
Ovaj dizajn je zapravo veoma sličan pretraživaču. Telo veb stranice je i dalje u veb stranici; pretraživač je samo dodatno izgradio skup indeksa. Niko ne kaže „Google je veb stranice nadogradio u obrnuti indeks“, zar ne? Odnos ovde u OpenClaw-u je otprilike isti.
[Ovde umetnite dijagram strukture memorije: cilj snimka: objasniti da je Markdown izvor podataka, a SQLite sloj indeksa; ključne reči: Markdown source, SQLite index, memory_search; preporučeno mesto: dijagram toka ili ručno nacrtan dijagram arhitekture]
03. Zašto samo Markdown nije dovoljan
Ovo pitanje ćete veoma duboko osetiti tek kada zaista pokrenete memory_search i pretražite.
Ako koristite samo Markdown, način pretrage se uglavnom svodi na dve kategorije:
Jedna je da sami prelazite očima.
Druga je pretraga po ključnoj reči.
Oba rade, ali oba imaju jasnu gornju granicu.
Za prelazak očima ne treba trošiti reči — količina poraste, sve pada. Sada vam se čini da je MEMORY.md sa nekoliko desetina redova lak za pronalaženje, ali to je zato što memorija još uvek nije zaista akumulirana. Kada kasnije budete imali jedna-dve stotine dugoročnih sećanja, plus dnevne logove, nikako nećete moći ručno da ih prelazite.
Pretraga po ključnoj reči deluje naprednije, ali veoma zavisi od poklapanja formulacija. Vi zapišete „vektorski indeks trenutno koristi Ollama-in nomic-embed-text“, a pri pretragi pitate „koji je lokalni embedding model“, „da li memorija vektor ide preko ollame“, „zašto provider prikazuje openai“. U ovim pitanjima ključne reči uopšte nisu jedinstvene, ali semantički zapravo pitaju isto.
U tom trenutku vrednost vektorske pretrage izbija.
Ona ne bulji fiksirano u par karaktera, već nastoji da razume „o čemu otprilike govori ova rečenica“. Baš zato je i pogodnija za obradu dugoročne memorije Agenta, jer komunikacija između čoveka i Agenta u suštini nije pisanje SQL uslova, već postavljanje pitanja na prirodnom jeziku.
Još zanimljivije — kada OpenClaw počne da radi, memory_search vraća mode: hybrid. To znači da se ne oslanja samo na vektore, niti samo na ključne reči, već na oba istovremeno.
- Ključne reči su dobre u preciznom pogađanju, na primer imena, imena modela, imena fajlova.
- Vektori su dobri u semantičkoj bliskosti, na primer „preferenca u komunikaciji“ i „izveštavanje o napretku“.
Kombinacija oba daje stabilniji rezultat.
[Ovde umetnite snimak rezultata pogotka memory_search: cilj snimka: dokazati da je memory_search vratio provider, model, mode i pogođene fragmente; ključne reči: provider openai, nomic-embed-text, hybrid; preporučeno mesto: izlaz terminala]
04. Zašto sam se konačno odlučio za Ollama kao model za vektore
Ovo u početku nije bilo ovako konfigurisano.
U početku sam krenuo „standardnijim“ putem iz OpenClaw dokumentacije: memorySearch.provider = local, a zatim da node-llama-cpp konzumira lokalni GGUF model.
Teorijski, taj put je ispravan, čak i prilično ortodoksan. Ali u praksi, zaista je pomalo naporno.
Jer u tom lancu morate da rešite nekoliko pitanja:
- kompajliranje
node-llama-cpp - preuzimanje lokalnog modela
- da li je Gateway zaista uzeo novu konfiguraciju
- da li je SQLite indeks ponovo izgrađen po novom provajderu
A ja lokalno već imam Ollamu.
To me je navelo na praktičnije rešenje: pošto Ollama već pokreće modele, zašto embedding ne bi prepustili njoj?
Probao sam — i zaista radi.
Na kraju sam pullovao nomic-embed-text i testirao dva Ollama interfejsa:
- nativni interfejs:
/api/embeddings - OpenAI kompatibilni interfejs:
/v1/embeddings
Oba normalno vraćaju vektore.
To je ključno.
Jer iako memorySearch u OpenClaw-u nema direktno polje „provider: ollama“, podržava OpenAI kompatibilni interfejs. Drugim rečima, potpuno mogu konfigurisati memorySearch kao provider: openai, a zatim baseUrl usmeriti na Ollama-in /v1.
Tako je na kraju nastala trenutna kombinacija:
- Model za razgovor: GPT-5.4
- Model za vektorski indeks: Ollama / nomic-embed-text
Ovu kombinaciju posebno volim.
Sa jedne strane, kvalitet razgovora i dalje ide preko GPT-5.4 — stabilnije.
Sa druge strane, embedding je posao koji se može završiti čisto lokalno; prepustiti ga Ollami štedi novac, štedi vreme i više odgovara ideji „lokalno kontrolisano“.
Konfiguracija koju zaista koristim
{
agents: {
defaults: {
model: {
primary: "openai-codex/gpt-5.4"
},
memorySearch: {
provider: "openai",
model: "nomic-embed-text",
remote: {
baseUrl: "http://localhost:11434/v1",
apiKey: "ollama"
},
sync: {
watch: true
}
}
}
}
}Ono što u ovoj konfiguraciji najlakše pogrešno protumačite jeste provider: "openai".
Mnogi kad to vide, instinktivno pomisle: ah, znači ipak koristi OpenAI.
Nije tako.
Ovde openai značikompatibilnost sa protokolom, a ne da se zahtev zaista šalje u OpenAI oblak. Onaj koji zaista radi jeste nomic-embed-text koji je pokrenut na vašoj lokalnoj Ollami.
To je kao kada neka usluga kaže „kompatibilna sa MySQL protokolom“ — ne znači da donji sloj nužno jeste sam MySQL, već da „možete se povezati na ovaj način“.
[Ovde umetnite snimak openclaw.json konfiguracije: cilj snimka: dokazati da su model za razgovor i model memorySearch odvojeni; ključne reči: gpt-5.4, memorySearch, nomic-embed-text; preporučeno mesto: editor konfiguracionog fajla]
05. Nekoliko klasičnih zamki u koje sam ovog puta upao
Ako i vi planirate da krenete ovim putem, predlažem vam da pažljivo pročitate ovaj odeljak. Jer ono što zaista troši vreme nikada nije samo „pisanje konfiguracije“, već oni detalji za koje mislite da ste ih ispravili, a sistem ih zapravo nije prihvatio.
Zamka 1: Gateway deluje kao da je restartovan, ali stari proces i dalje živi
Ovo je najdosadnija zamka u koju sam upao.
Jasno sam izmenio openclaw.json, u logovima se pojavilo config change detected, ali memory_search i dalje vraća:
provider: nonemode: fts-only
To je prilično čudno.
Kasnije, pri proveri, otkrio sam da stari proces Gateway-a i dalje drži port. Misliš da si ga restartovao, a zapravo si „poslao komandu za restart“; onaj stari koji zaista služi i dalje je živ.
Rezultat je: nova konfiguracija je upisana u fajl, ali proces koji radi nije zaista zamenjen.
Dok ne očistiš stari proces, ma kako testirao, izgleda kao da udaraš pesnicom u vazduh.
Zamka 2: SQLite zadržava stare metapodatke indeksa
I druga zamka je klasična.
Promenio sam provider iz none u Ollama rešenje; trebalo bi da se ponovo indeksira, zar ne? Ali sqlite i dalje ima stare metapodatke:
provider: nonemodel: fts-only
Druga rečima, iako ste promenili konfiguraciju, baza indeksa je i dalje u starom stanju.
U tom trenutku problem nije u sadržaju, već u tome štoindeks nije ponovo izgrađen.
Rešenje je prilično direktno:
- očistiti stare metapodatke indeksa
- po potrebi ponovo izgraditi sqlite
- zatim aktivirati još jedan
memory_search
Kada se ovaj korak završi, rezultat će se očigledno promeniti.
Zamka 3: Mešanje modela za razgovor i modela za embedding
Ova zamka nije tehnička, već kognitivna.
I sam sam se zamorio.
Jer jasno sam konfigurisao GPT-5.4; kada je memory_search vratio provider: openai, model: nomic-embed-text, moja prva reakcija je bila: čekaj, zar nije rekao da je sada podrazumevani GPT? Otkud sada nomic?
Tek kasnije potpuno shvatih:ovo su dva modela, ne jedan.
- razgovor ide preko GPT-5.4
- vektorizacija memorije ide preko Ollama embedding-a
Kada jednom ovo razumete, čitav sistem postaje mnogo prozirniji.
Zamka 4: U člancima je lako objašnjavati principe, ali u stvarnosti se često zaglavi na „poslednjoj milji“
Prosto rečeno, principe može ko god da objasni.
Ali kada zaista kreneš, ono što te najviše muči nije „šta je vektorski indeks“, već:
- da li je konfiguracija zaista ispravno napisana
- da li je proces zaista restartovan
- da li se model zaista povezao
- da li u SQLite-u zaista ima chunk-ova
- zašto su rezultati pretrage i dalje prazni
Zato i sve više smatram da pisanje tehničkih članaka ne sme da ostane na „nivou koncepata“. Ako nema stvarnog procesa otklanjanja problema, ma koliko ovaj članak bio uredan, teško da će kod čitaoca stvoriti poverenje „ako ovo kod kuće ponovim, velika je šansa da će uspeti“.
[Ovde umetnite snimak sqlite metapodataka ili stanja indeksa: cilj snimka: dokazati razliku između starog i novog indeksa; ključne reči: provider none, provider openai, fts-only, hybrid; preporučeno mesto: rezultati sqlite upita ili izlaz loga]
06. Ako tek počinjete sa OpenClaw-om, evo šta preporučujem
Ako još uvek niste počeli da konfigurišete memoriju, moj savet je zapravo vrlo jednostavan:ne ulazite odmah u najkompleksnije rešenje.
Prva faza: koristite samo Markdown
Na početku, aktivirajte ova dva fajla:
MEMORY.mdmemory/YYYY-MM-DD.md
Prvo razvijte naviku „imam zaključak — upišem ga“, što je mnogo važnije nego da prvog dana maksimalno dignete vektorski indeks.
Jer bez izvornih podataka, ni najnaprednija pretraga nema smisla.
Druga faza: kada se memorija nakupi, uključite vektorski indeks
Kada zaista počnete da nailazite na ove probleme:
- memorije sve više
- ne možete da zapamtite gde je šta zapisano
- želite da
memory_searchviše liči na „pitanje čoveku“ nego na „pretragu fajlova“
Tada uključite vektorski indeks i rezultat će biti očigledan.
A ako lokalno već imate Ollamu, sada preporučujem da direktno krenete putem nomic-embed-text. Razlog je jednostavan:
- model je gotov
- veličina nije prevelika
- standardan interfejs
- lokalno kontrolisan
- kasnija zamena embedding modela je takođe laka
Treća faza: pretvorite „pisanje memorije“ i „pretragu memorije“ u deo radnog toka
Najidealnije stanje, po mom mišljenju, nije tretirati memoriju kao „naprednu funkciju“, već je integrisati u svakodnevni radni tok Agenta.
Četvrta faza: naučite da procenite da li ste zaista dobro konfigurisali
I ovo je posebno važno.
Često ono što najviše odsustvuje vremena nije „ne umeš da konfigurišeš“, već „misliš da si već konfigurisao“. Zato sada koristim jednu grubu, ali efikasnu metodu za proveru: gledaj rezultat, ne nagađaj stanje.
Kako konkretno proceniti?
Prvo, pogledajte provider i model koje vraća memory_search. Ako i dalje stoji provider: none, mode: fts-only, to znači da za sada imate samo pretragu po ključnoj reči i da još uvek niste ušli u vektorski režim. Ako vraća provider: openai, model: nomic-embed-text, mode: hybrid, to znači da je embedding lanac prohodao.
Drugo, pogledajte da li u sqlite počinju da se pojavljuju chunk-ovi. Jer pravi vektorski indeks nije „u konfiguracionom fajlu piše i vrijedi“, već sqlite zaista ima sadržaja, a pretraga zaista pogađa relevantne fragmente.
Treće, pogledajte da li prirodan jezički upit može da pogodi semantički blizak sadržaj. Na primer, zapisali ste „posle svakog malog koraka izvesti o napretku“; ako pretragom „preferenca u komunikaciji“ ili „ritam povratne informacije“ takođe pogodi, tek onda možemo reći da ovaj sistem počinje da liči na nešto.
Na primer:
- nakon što konfigurišete jednu važnu stavku, odmah je zapišite u
MEMORY.md - nakon što rešite jedan tipičan kvar, odmah ga zapišite u
memory/YYYY-MM-DD.md - sledeći put kada pitate, prvo
memory_search - nakon pogotka odlučite da li će te
memory_getili direktno pročitati original
Tako postepeno akumuliranjem, memorija Agenta zaista počinje da ima „kontinuitet“, umesto da svaki put izgleda kao da je privremeni radnik koji iznova stupa na posao.
Umesto da pri svakom novom razgovoru izgleda kao da ste upravo izgubili pamćenje i iznova pogađate šta volite i do koje ste tačke već stigli.
[Ovde umetnite snimak konačnog rezultata: cilj snimka: prikazati kompletnu petlju od upisivanja u Markdown do pogotka memory_search; ključne reči: MEMORY.md, memory_search, hybrid, pogodak; preporučeno mesto: mozaik terminal + editor]
ending
Kada sam se ovog puta igrao sa memory-jem OpenClaw-a, najdublji utisak nije bio „konačno sam pokrenuo vektorski indeks“, već iznenadno bolje razumevanje zašto Agent razdvaja „čuvanje“ i „pronalaženje“.
I mi ljudi u svakodnevnom životu radimo isto.
Ono što je zaista važno nije da li imate u glavi, već da li u trenutku potrebe možete brzo da ga povratite.
Memorija sama po sebi određuje da li imate akumulaciju.
Sposobnost pretrage određuje da li ta akumulacija može u ključnom trenutku da postane procena, akcija, efikasnost.
Markdown je kao pažljivo vođenje beleški.
SQLite je kao izgradnja indeksa.
Jedan je zadužen za zapisivanje, drugi za pronalaženje.
Prvi čini da stvari ne nestanu, drugi čini da stvari ne leže i ne skupljaju prašinu.
[Zaista korisna memorija nije „sačuvao sam“, već „sledeći put mogu da iskoristim“.]
Zato, ako iz ovog članka odnesete samo jednu rečenicu, neka to bude ova:
OpenClaw ne nadograđuje memoriju iz Markdown-a u SQLite; on Markdown-om čuva činjenice, a SQLite-om vam pomaže da te činjenice pronadete.
Ta razlika, naizgled je samo tehnički detalj implementacije.
Ali kada je jednom razumete, bilo da konfigurišete OpenClaw, pravite Agenta ili svom sistemu dodajete dugoročnu memoriju, razmišljanje će biti mnogo jasnije.
Dobro, za danas toliko o ovome; završiću ovde.
Ako se i vi mučite sa OpenClaw-om ili ste se ikada izgubili u ovom mehanizmu memorije, javite mi u komentarima; mogu kasnije da razložim i nekoliko preostalih zamki i nastavimo razgovor.
