Memoria
La memoria de OpenClaw es Markdown plano en el workspace del agente. Los archivos son la fuente de verdad; el modelo solo “recuerda” lo que se escribe en disco.
Las herramientas de búsqueda en memoria las proporciona el plugin de memoria activo (por defecto:
memory-core). Desactiva los plugins de memoria con plugins.slots.memory = "none".
Archivos de memoria (Markdown)
La estructura por defecto del workspace usa dos capas de memoria:
memory/YYYY-MM-DD.md- Registro diario (solo agregar).
- Leer el de hoy + el de ayer al inicio de sesión.
MEMORY.md(opcional)- Memoria curada a largo plazo.
- Solo cargar en la sesión principal y privada (nunca en contextos grupales).
Estos archivos residen en el workspace (agents.defaults.workspace, por defecto
~/.openclaw/workspace). Consulta Workspace del agente para la estructura completa.
Herramientas de memoria
OpenClaw expone dos herramientas orientadas al agente para estos archivos Markdown:
memory_search— recuperación semántica sobre fragmentos indexados.memory_get— lectura dirigida de un archivo Markdown específico/rango de líneas.
memory_get ahora degrada elegantemente cuando un archivo no existe (por ejemplo,
el registro diario de hoy antes de la primera escritura). Tanto el gestor integrado como el
backend QMD devuelven { text: "", path } en lugar de lanzar ENOENT, para que los agentes puedan
manejar “nada registrado aún” y continuar su flujo sin envolver la
llamada a la herramienta en lógica try/catch.
Cuándo escribir memoria
- Decisiones, preferencias y hechos duraderos van a
MEMORY.md. - Notas del día a día y contexto corriente van a
memory/YYYY-MM-DD.md. - Si alguien dice “recuerda esto,” escríbelo (no lo mantengas en RAM).
- Esta área sigue evolucionando. Ayuda recordarle al modelo que almacene memorias; él sabrá qué hacer.
- Si quieres que algo persista, pídele al bot que lo escriba en la memoria.
Vaciado automático de memoria (ping pre-compactación)
Cuando una sesión está cerca de la auto-compactación, OpenClaw activa un turno
silencioso y agentico que recuerda al modelo escribir memoria duradera antes de que
el contexto sea compactado. Los prompts por defecto dicen explícitamente que el modelo puede responder,
pero normalmente NO_REPLY es la respuesta correcta para que el usuario nunca vea este turno.
Esto se controla con 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.",
},
},
},
},
}
Detalles:
- Umbral suave: el vaciado se activa cuando la estimación de tokens de la sesión cruza
contextWindow - reserveTokensFloor - softThresholdTokens. - Silencioso por defecto: los prompts incluyen
NO_REPLYpara que nada se entregue. - Dos prompts: un prompt de usuario más un append de system prompt como recordatorio.
- Un vaciado por ciclo de compactación (rastreado en
sessions.json). - El workspace debe ser escribible: si la sesión se ejecuta en sandbox con
workspaceAccess: "ro"o"none", el vaciado se omite.
Para el ciclo de vida completo de compactación, consulta Gestión de sesiones + compactación.
Búsqueda vectorial en memoria
OpenClaw puede construir un pequeño índice vectorial sobre MEMORY.md y memory/*.md para que
las consultas semánticas puedan encontrar notas relacionadas incluso cuando la redacción difiere.
Valores por defecto:
- Habilitado por defecto.
- Vigila los archivos de memoria en busca de cambios (con debounce).
- Configura la búsqueda en memoria bajo
agents.defaults.memorySearch(no en el nivel superiormemorySearch). - Usa embeddings remotos por defecto. Si
memorySearch.providerno está configurado, OpenClaw auto-selecciona:localsi hay unmemorySearch.local.modelPathconfigurado y el archivo existe.openaisi se puede resolver una clave de OpenAI.geminisi se puede resolver una clave de Gemini.voyagesi se puede resolver una clave de Voyage.mistralsi se puede resolver una clave de Mistral.- De lo contrario, la búsqueda en memoria permanece deshabilitada hasta que se configure.
- El modo local usa node-llama-cpp y puede requerir
pnpm approve-builds. - Usa sqlite-vec (cuando está disponible) para acelerar la búsqueda vectorial dentro de SQLite.
memorySearch.provider = "ollama"también está soportado para embeddings locales/autoalojados de Ollama (/api/embeddings), pero no se auto-selecciona.
Los embeddings remotos requieren una clave API para el provider de embeddings. OpenClaw
resuelve claves desde perfiles de autenticación, models.providers.*.apiKey o variables de
entorno. Codex OAuth solo cubre chat/completions y no satisface
embeddings para búsqueda en memoria. Para Gemini, usa GEMINI_API_KEY o
models.providers.google.apiKey. Para Voyage, usa VOYAGE_API_KEY o
models.providers.voyage.apiKey. Para Mistral, usa MISTRAL_API_KEY o
models.providers.mistral.apiKey. Ollama típicamente no requiere una clave API
real (un marcador como OLLAMA_API_KEY=ollama-local es suficiente cuando lo requiere la
política local).
Al usar un endpoint personalizado compatible con OpenAI,
configura memorySearch.remote.apiKey (y opcionalmente memorySearch.remote.headers).
Backend QMD (experimental)
Configura memory.backend = "qmd" para reemplazar el indexador SQLite integrado por
QMD: un sidecar de búsqueda local-first que combina
BM25 + vectores + reranking. El Markdown sigue siendo la fuente de verdad; OpenClaw llama
a QMD externamente para la recuperación. Puntos clave:
Prerrequisitos
- Deshabilitado por defecto. Actívalo por configuración (
memory.backend = "qmd"). - Instala el CLI de QMD por separado (
bun install -g https://github.com/tobi/qmdo descarga un release) y asegúrate de que el binarioqmdesté en elPATHdel gateway. - QMD necesita un build de SQLite que permita extensiones (
brew install sqliteen macOS). - QMD se ejecuta completamente en local vía Bun +
node-llama-cppy descarga automáticamente modelos GGUF desde HuggingFace en el primer uso (no requiere un daemon Ollama separado). - El gateway ejecuta QMD en un home XDG autocontenido bajo
~/.openclaw/agents/<agentId>/qmd/configurandoXDG_CONFIG_HOMEyXDG_CACHE_HOME. - Soporte de SO: macOS y Linux funcionan directamente una vez que Bun + SQLite están instalados. Windows se soporta mejor vía WSL2.
Cómo funciona el sidecar
- El gateway escribe un home QMD autocontenido bajo
~/.openclaw/agents/<agentId>/qmd/(config + caché + DB sqlite). - Las colecciones se crean vía
qmd collection adddesdememory.qmd.paths(más archivos de memoria del workspace por defecto), luegoqmd update+qmd embedse ejecutan al inicio y en un intervalo configurable (memory.qmd.update.interval, por defecto 5 m). - El gateway ahora inicializa el gestor QMD al arrancar, para que los temporizadores de actualización
periódica se armen incluso antes de la primera llamada a
memory_search. - La actualización de arranque ahora se ejecuta en segundo plano por defecto para que el inicio del chat no se
bloquee; configura
memory.qmd.update.waitForBootSync = truepara mantener el comportamiento anterior de bloqueo. - Las búsquedas se ejecutan vía
memory.qmd.searchMode(por defectoqmd search --json; también soportavsearchyquery). Si el modo seleccionado rechaza flags en tu build de QMD, OpenClaw reintenta conqmd query. Si QMD falla o el binario no existe, OpenClaw recurre automáticamente al gestor SQLite integrado para que las herramientas de memoria sigan funcionando. - OpenClaw no expone ajuste de tamaño de lote de embed de QMD hoy; el comportamiento de lotes lo controla QMD mismo.
- La primera búsqueda puede ser lenta: QMD puede descargar modelos GGUF locales (reranker/expansión
de consultas) en la primera ejecución de
qmd query.-
OpenClaw configura
XDG_CONFIG_HOME/XDG_CACHE_HOMEautomáticamente cuando ejecuta QMD. -
Si quieres pre-descargar modelos manualmente (y calentar el mismo índice que usa OpenClaw), ejecuta una consulta puntual con los dirs XDG del agente.
El estado QMD de OpenClaw reside en tu directorio de estado (por defecto
~/.openclaw). Puedes apuntarqmdal mismo índice exacto exportando las mismas variables XDG que usa OpenClaw:# Elige el mismo directorio de estado que usa OpenClaw STATE_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}" export XDG_CONFIG_HOME="$STATE_DIR/agents/main/qmd/xdg-config" export XDG_CACHE_HOME="$STATE_DIR/agents/main/qmd/xdg-cache" # (Opcional) fuerza una actualización del índice + embeddings qmd update qmd embed # Calentar / disparar descargas de modelos por primera vez qmd query "test" -c memory-root --json >/dev/null 2>&1
-
Superficie de configuración (memory.qmd.*)
command(por defectoqmd): sobrescribir la ruta del ejecutable.searchMode(por defectosearch): elegir qué comando QMD respaldamemory_search(search,vsearch,query).includeDefaultMemory(por defectotrue): auto-indexarMEMORY.md+memory/**/*.md.paths[]: agregar directorios/archivos extra (path,patternopcional,nameestable opcional).sessions: optar por la indexación de JSONL de sesiones (enabled,retentionDays,exportDir).update: controla la cadencia de actualización y la ejecución de mantenimiento: (interval,debounceMs,onBoot,waitForBootSync,embedInterval,commandTimeoutMs,updateTimeoutMs,embedTimeoutMs).limits: limitar el payload de recuperación (maxResults,maxSnippetChars,maxInjectedChars,timeoutMs).scope: mismo esquema quesession.sendPolicy. Por defecto es solo DM (denytodos,allowchats directos); aflójalo para mostrar resultados de QMD en grupos/canales.match.keyPrefixcoincide con la clave de sesión normalizada (en minúsculas, con cualquieragent:<id>:inicial eliminado). Ejemplo:discord:channel:.match.rawKeyPrefixcoincide con la clave de sesión cruda (en minúsculas), incluyendoagent:<id>:. Ejemplo:agent:main:discord:.- Legado:
match.keyPrefix: "agent:..."aún se trata como prefijo de clave cruda, pero prefiererawKeyPrefixpara mayor claridad.
- Cuando
scopedeniega una búsqueda, OpenClaw registra una advertencia con elchannel/chatTypederivado para facilitar la depuración de resultados vacíos. - Los fragmentos de fuentes fuera del workspace aparecen como
qmd/<collection>/<relative-path>en los resultados dememory_search;memory_getentiende ese prefijo y lee desde la raíz de colección QMD configurada. - Cuando
memory.qmd.sessions.enabled = true, OpenClaw exporta transcripciones de sesión sanitizadas (turnos User/Assistant) en una colección QMD dedicada bajo~/.openclaw/agents/<id>/qmd/sessions/, para quememory_searchpueda recuperar conversaciones recientes sin tocar el índice SQLite integrado. - Los fragmentos de
memory_searchahora incluyen un pieSource: <path#line>cuandomemory.citationsesauto/on; configuramemory.citations = "off"para mantener los metadatos de ruta internos (el agente aún recibe la ruta paramemory_get, pero el texto del fragmento omite el pie y el system prompt advierte al agente que no lo cite).
Ejemplo
memory: {
backend: "qmd",
citations: "auto",
qmd: {
includeDefaultMemory: true,
update: { interval: "5m", debounceMs: 15000 },
limits: { maxResults: 6, timeoutMs: 4000 },
scope: {
default: "deny",
rules: [
{ action: "allow", match: { chatType: "direct" } },
// Prefijo de clave de sesión normalizado (elimina `agent:<id>:`).
{ action: "deny", match: { keyPrefix: "discord:channel:" } },
// Prefijo de clave de sesión crudo (incluye `agent:<id>:`).
{ action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
]
},
paths: [
{ name: "docs", path: "~/notes", pattern: "**/*.md" }
]
}
}
Citas y fallback
memory.citationsaplica independientemente del backend (auto/on/off).- Cuando
qmdse ejecuta, etiquetamosstatus().backend = "qmd"para que los diagnósticos muestren qué motor sirvió los resultados. Si el subproceso QMD sale o la salida JSON no puede parsearse, el gestor de búsqueda registra una advertencia y devuelve el provider integrado (embeddings Markdown existentes) hasta que QMD se recupere.
Rutas de memoria adicionales
Si quieres indexar archivos Markdown fuera de la estructura por defecto del workspace, agrega rutas explícitas:
agents: {
defaults: {
memorySearch: {
extraPaths: ["../team-docs", "/srv/shared-notes/overview.md"]
}
}
}
Notas:
- Las rutas pueden ser absolutas o relativas al workspace.
- Los directorios se escanean recursivamente buscando archivos
.md. - Por defecto, solo se indexan archivos Markdown.
- Si
memorySearch.multimodal.enabled = true, OpenClaw también indexa archivos de imagen/audio soportados bajoextraPathsúnicamente. Las raíces de memoria por defecto (MEMORY.md,memory.md,memory/**/*.md) permanecen solo Markdown. - Los symlinks se ignoran (archivos o directorios).
Archivos de memoria multimodal (imagen + audio con Gemini)
OpenClaw puede indexar archivos de imagen y audio desde memorySearch.extraPaths al usar Gemini embedding 2:
agents: {
defaults: {
memorySearch: {
provider: "gemini",
model: "gemini-embedding-2-preview",
extraPaths: ["assets/reference", "voice-notes"],
multimodal: {
enabled: true,
modalities: ["image", "audio"], // o ["all"]
maxFileBytes: 10000000
},
remote: {
apiKey: "YOUR_GEMINI_API_KEY"
}
}
}
}
Notas:
- La memoria multimodal actualmente solo está soportada para
gemini-embedding-2-preview. - La indexación multimodal aplica solo a archivos descubiertos a través de
memorySearch.extraPaths. - Modalidades soportadas en esta fase: imagen y audio.
memorySearch.fallbackdebe permanecer en"none"mientras la memoria multimodal esté habilitada.- Los bytes de archivos de imagen/audio coincidentes se suben al endpoint de embeddings de Gemini configurado durante la indexación.
- Extensiones de imagen soportadas:
.jpg,.jpeg,.png,.webp,.gif,.heic,.heif. - Extensiones de audio soportadas:
.mp3,.wav,.ogg,.opus,.m4a,.aac,.flac. - Las consultas de búsqueda siguen siendo texto, pero Gemini puede comparar esas consultas de texto contra embeddings de imagen/audio indexados.
memory_getsigue leyendo solo Markdown; los archivos binarios son buscables pero no se devuelven como contenido de archivo crudo.
Embeddings de Gemini (nativo)
Configura el provider como gemini para usar la API de embeddings de Gemini directamente:
agents: {
defaults: {
memorySearch: {
provider: "gemini",
model: "gemini-embedding-001",
remote: {
apiKey: "YOUR_GEMINI_API_KEY"
}
}
}
}
Notas:
remote.baseUrles opcional (por defecto la URL base de la API de Gemini).remote.headerste permite agregar cabeceras extra si es necesario.- Modelo por defecto:
gemini-embedding-001. gemini-embedding-2-previewtambién está soportado: límite de 8192 tokens y dimensiones configurables (768 / 1536 / 3072, por defecto 3072).
Gemini Embedding 2 (preview)
agents: {
defaults: {
memorySearch: {
provider: "gemini",
model: "gemini-embedding-2-preview",
outputDimensionality: 3072, // opcional: 768, 1536, o 3072 (por defecto)
remote: {
apiKey: "YOUR_GEMINI_API_KEY"
}
}
}
}
Requiere re-indexación: Cambiar de
gemini-embedding-001(768 dimensiones) agemini-embedding-2-preview(3072 dimensiones) cambia el tamaño del vector. Lo mismo aplica si cambiasoutputDimensionalityentre 768, 1536 y 3072. OpenClaw re-indexará automáticamente cuando detecte un cambio de modelo o dimensión.
Si quieres usar un endpoint personalizado compatible con OpenAI (OpenRouter, vLLM o un proxy),
puedes usar la configuración remote con el provider de 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" }
}
}
}
}
Si no quieres configurar una clave API, usa memorySearch.provider = "local" o configura
memorySearch.fallback = "none".
Fallbacks:
memorySearch.fallbackpuede seropenai,gemini,voyage,mistral,ollama,localonone.- El provider de fallback solo se usa cuando el provider de embeddings primario falla.
Indexación por lotes (OpenAI + Gemini + Voyage):
- Deshabilitada por defecto. Configura
agents.defaults.memorySearch.remote.batch.enabled = truepara habilitar la indexación de corpus grandes (OpenAI, Gemini y Voyage). - El comportamiento por defecto espera a que se complete el lote; ajusta
remote.batch.wait,remote.batch.pollIntervalMsyremote.batch.timeoutMinutessi es necesario. - Configura
remote.batch.concurrencypara controlar cuántos trabajos de lote se envían en paralelo (por defecto: 2). - El modo por lotes aplica cuando
memorySearch.provider = "openai"o"gemini"y usa la clave API correspondiente. - Los trabajos por lotes de Gemini usan el endpoint de lotes de embeddings async y requieren disponibilidad de la API Batch de Gemini.
Por qué el modo por lotes de OpenAI es rápido + económico:
- Para backfills grandes, OpenAI es típicamente la opción más rápida que soportamos porque podemos enviar muchas solicitudes de embedding en un solo trabajo de lote y dejar que OpenAI las procese asincrónicamente.
- OpenAI ofrece precios con descuento para cargas de trabajo de la API Batch, por lo que las ejecuciones de indexación grandes suelen ser más baratas que enviar las mismas solicitudes sincrónicamente.
- Consulta la documentación y precios de la API Batch de OpenAI para más detalles:
Ejemplo de configuración:
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
fallback: "openai",
remote: {
batch: { enabled: true, concurrency: 2 }
},
sync: { watch: true }
}
}
}
Herramientas:
memory_search— devuelve fragmentos con archivo + rangos de líneas.memory_get— lee contenido de archivo de memoria por ruta.
Modo local:
- Configura
agents.defaults.memorySearch.provider = "local". - Proporciona
agents.defaults.memorySearch.local.modelPath(GGUF o URIhf:). - Opcional: configura
agents.defaults.memorySearch.fallback = "none"para evitar fallback remoto.
Cómo funcionan las herramientas de memoria
memory_searchbusca semánticamente en chunks de Markdown (~400 tokens objetivo, 80 tokens de superposición) desdeMEMORY.md+memory/**/*.md. Devuelve texto de fragmento (limitado a ~700 caracteres), ruta del archivo, rango de líneas, puntuación, provider/modelo, y si se cayó de local a embeddings remotos. No se devuelve el payload completo del archivo.memory_getlee un archivo Markdown de memoria específico (relativo al workspace), opcionalmente desde una línea inicial y por N líneas. Las rutas fuera deMEMORY.md/memory/se rechazan.- Ambas herramientas solo se habilitan cuando
memorySearch.enabledse resuelve como true para el agente.
Qué se indexa (y cuándo)
- Tipo de archivo: solo Markdown (
MEMORY.md,memory/**/*.md). - Almacén del índice: SQLite por agente en
~/.openclaw/memory/<agentId>.sqlite(configurable víaagents.defaults.memorySearch.store.path, soporta token{agentId}). - Frescura: watcher en
MEMORY.md+memory/marca el índice como sucio (debounce 1.5s). La sincronización se programa al inicio de sesión, en búsqueda o en un intervalo y se ejecuta asincrónicamente. Las transcripciones de sesión usan umbrales delta para disparar la sincronización en segundo plano. - Disparadores de re-indexación: el índice almacena el provider/modelo de embedding + huella del endpoint + parámetros de chunking. Si cualquiera de esos cambia, OpenClaw resetea y re-indexa automáticamente todo el almacén.
Búsqueda híbrida (BM25 + vector)
Cuando está habilitada, OpenClaw combina:
- Similitud vectorial (coincidencia semántica, la redacción puede diferir)
- Relevancia por palabras clave BM25 (tokens exactos como IDs, variables de entorno, símbolos de código)
Si la búsqueda de texto completo no está disponible en tu plataforma, OpenClaw recurre a búsqueda solo vectorial.
Por qué búsqueda híbrida
La búsqueda vectorial es excelente para “esto significa lo mismo”:
- “Mac Studio gateway host” vs “the machine running the gateway”
- “debounce file updates” vs “avoid indexing on every write”
Pero puede ser débil con tokens exactos y de alta señal:
- IDs (
a828e60,b3b9895a…) - símbolos de código (
memorySearch.query.hybrid) - cadenas de error (“sqlite-vec unavailable”)
BM25 (texto completo) es lo opuesto: fuerte en tokens exactos, más débil en paráfrasis. La búsqueda híbrida es el punto medio pragmático: usa ambas señales de recuperación para obtener buenos resultados tanto para consultas en “lenguaje natural” como para consultas de “aguja en un pajar”.
Cómo fusionamos resultados (el diseño actual)
Esquema de implementación:
- Recuperar un pool de candidatos de ambos lados:
- Vector: top
maxResults * candidateMultiplierpor similitud coseno. - BM25: top
maxResults * candidateMultiplierpor rango BM25 de FTS5 (menor es mejor).
- Convertir rango BM25 en una puntuación de 0..1:
textScore = 1 / (1 + max(0, bm25Rank))
- Unir candidatos por ID de chunk y calcular una puntuación ponderada:
finalScore = vectorWeight * vectorScore + textWeight * textScore
Notas:
vectorWeight+textWeightse normaliza a 1.0 en la resolución de configuración, por lo que los pesos se comportan como porcentajes.- Si los embeddings no están disponibles (o el provider devuelve un vector cero), aún ejecutamos BM25 y devolvemos coincidencias por palabras clave.
- Si FTS5 no puede crearse, mantenemos la búsqueda solo vectorial (sin fallo duro).
Esto no es “teóricamente perfecto en RI”, pero es simple, rápido y tiende a mejorar recall/precisión en notas reales. Si queremos ser más sofisticados después, los próximos pasos comunes son Reciprocal Rank Fusion (RRF) o normalización de puntuación (min/max o z-score) antes de mezclar.
Pipeline de post-procesamiento
Después de fusionar las puntuaciones vectoriales y de palabras clave, dos etapas opcionales de post-procesamiento refinan la lista de resultados antes de que llegue al agente:
Vector + Keyword → Weighted Merge → Temporal Decay → Sort → MMR → Top-K Results
Ambas etapas están desactivadas por defecto y pueden habilitarse independientemente.
Re-ranking MMR (diversidad)
Cuando la búsqueda híbrida devuelve resultados, múltiples chunks pueden contener contenido similar o superpuesto. Por ejemplo, buscar “home network setup” podría devolver cinco fragmentos casi idénticos de diferentes notas diarias que mencionan la misma configuración de router.
MMR (Maximal Marginal Relevance) re-rankea los resultados para equilibrar relevancia con diversidad, asegurando que los resultados principales cubran diferentes aspectos de la consulta en lugar de repetir la misma información.
Cómo funciona:
- Los resultados se puntúan por su relevancia original (puntuación ponderada vector + BM25).
- MMR selecciona iterativamente resultados que maximizan:
λ × relevance − (1−λ) × max_similarity_to_selected. - La similitud entre resultados se mide usando similitud Jaccard de texto sobre contenido tokenizado.
El parámetro lambda controla el balance:
lambda = 1.0→ relevancia pura (sin penalización por diversidad)lambda = 0.0→ diversidad máxima (ignora relevancia)- Por defecto:
0.7(equilibrado, ligero sesgo hacia relevancia)
Ejemplo — consulta: “home network setup”
Dados estos archivos de memoria:
memory/2026-02-10.md → "Configured Omada router, set VLAN 10 for IoT devices"
memory/2026-02-08.md → "Configured Omada router, moved IoT to VLAN 10"
memory/2026-02-05.md → "Set up AdGuard DNS on 192.168.10.2"
memory/network.md → "Router: Omada ER605, AdGuard: 192.168.10.2, VLAN 10: IoT"
Sin MMR — top 3 resultados:
1. memory/2026-02-10.md (score: 0.92) ← router + VLAN
2. memory/2026-02-08.md (score: 0.89) ← router + VLAN (casi duplicado!)
3. memory/network.md (score: 0.85) ← doc de referencia
Con MMR (λ=0.7) — top 3 resultados:
1. memory/2026-02-10.md (score: 0.92) ← router + VLAN
2. memory/network.md (score: 0.85) ← doc de referencia (diverso!)
3. memory/2026-02-05.md (score: 0.78) ← AdGuard DNS (diverso!)
El casi duplicado del 8 de Feb se descarta, y el agente obtiene tres piezas distintas de información.
Cuándo habilitar: Si notas que memory_search devuelve fragmentos redundantes o casi duplicados,
especialmente con notas diarias que frecuentemente repiten información similar entre días.
Decaimiento temporal (impulso de recencia)
Los agentes con notas diarias acumulan cientos de archivos con fecha a lo largo del tiempo. Sin decaimiento, una nota bien redactada de hace seis meses puede superar en ranking a la actualización de ayer sobre el mismo tema.
El decaimiento temporal aplica un multiplicador exponencial a las puntuaciones basado en la antigüedad de cada resultado, para que las memorias recientes naturalmente se posicionen más alto mientras las antiguas se desvanecen:
decayedScore = score × e^(-λ × ageInDays)
donde λ = ln(2) / halfLifeDays.
Con la vida media por defecto de 30 días:
- Notas de hoy: 100% de la puntuación original
- Hace 7 días: ~84%
- Hace 30 días: 50%
- Hace 90 días: 12.5%
- Hace 180 días: ~1.6%
Los archivos perennes nunca se decaen:
MEMORY.md(archivo de memoria raíz)- Archivos sin fecha en
memory/(ej.,memory/projects.md,memory/network.md) - Estos contienen información de referencia duradera que siempre debe rankear normalmente.
Los archivos diarios con fecha (memory/YYYY-MM-DD.md) usan la fecha extraída del nombre de archivo.
Otras fuentes (ej., transcripciones de sesión) usan la hora de modificación del archivo (mtime).
Ejemplo — consulta: “what’s Rod’s work schedule?”
Dados estos archivos de memoria (hoy es 10 de Feb):
memory/2025-09-15.md → "Rod works Mon-Fri, standup at 10am, pairing at 2pm" (148 días)
memory/2026-02-10.md → "Rod has standup at 14:15, 1:1 with Zeb at 14:45" (hoy)
memory/2026-02-03.md → "Rod started new team, standup moved to 14:15" (7 días)
Sin decaimiento:
1. memory/2025-09-15.md (score: 0.91) ← mejor coincidencia semántica, pero obsoleta!
2. memory/2026-02-10.md (score: 0.82)
3. memory/2026-02-03.md (score: 0.80)
Con decaimiento (halfLife=30):
1. memory/2026-02-10.md (score: 0.82 × 1.00 = 0.82) ← hoy, sin decaimiento
2. memory/2026-02-03.md (score: 0.80 × 0.85 = 0.68) ← 7 días, decaimiento leve
3. memory/2025-09-15.md (score: 0.91 × 0.03 = 0.03) ← 148 días, casi invisible
La nota obsoleta de septiembre cae al fondo a pesar de tener la mejor coincidencia semántica bruta.
Cuándo habilitar: Si tu agente tiene meses de notas diarias y encuentras que información antigua y obsoleta supera en ranking al contexto reciente. Una vida media de 30 días funciona bien para flujos de trabajo con muchas notas diarias; auméntala (ej., 90 días) si consultas notas antiguas frecuentemente.
Configuración
Ambas funcionalidades se configuran bajo memorySearch.query.hybrid:
agents: {
defaults: {
memorySearch: {
query: {
hybrid: {
enabled: true,
vectorWeight: 0.7,
textWeight: 0.3,
candidateMultiplier: 4,
// Diversidad: reducir resultados redundantes
mmr: {
enabled: true, // por defecto: false
lambda: 0.7 // 0 = max diversidad, 1 = max relevancia
},
// Recencia: impulsar memorias más nuevas
temporalDecay: {
enabled: true, // por defecto: false
halfLifeDays: 30 // la puntuación se reduce a la mitad cada 30 días
}
}
}
}
}
}
Puedes habilitar cualquiera de las funcionalidades independientemente:
- Solo MMR — útil cuando tienes muchas notas similares pero la antigüedad no importa.
- Solo decaimiento temporal — útil cuando la recencia importa pero tus resultados ya son diversos.
- Ambos — recomendado para agentes con historiales extensos de notas diarias.
Caché de embeddings
OpenClaw puede cachear embeddings de chunks en SQLite para que la re-indexación y las actualizaciones frecuentes (especialmente transcripciones de sesión) no vuelvan a generar embeddings de texto sin cambios.
Configuración:
agents: {
defaults: {
memorySearch: {
cache: {
enabled: true,
maxEntries: 50000
}
}
}
}
Búsqueda en memoria de sesión (experimental)
Opcionalmente puedes indexar transcripciones de sesión y mostrarlas vía memory_search.
Esto está detrás de un flag experimental.
agents: {
defaults: {
memorySearch: {
experimental: { sessionMemory: true },
sources: ["memory", "sessions"]
}
}
}
Notas:
- La indexación de sesiones es opt-in (desactivada por defecto).
- Las actualizaciones de sesión tienen debounce y se indexan asincrónicamente una vez que cruzan umbrales delta (mejor esfuerzo).
memory_searchnunca se bloquea en la indexación; los resultados pueden estar ligeramente desactualizados hasta que la sincronización en segundo plano termine.- Los resultados aún incluyen solo fragmentos;
memory_getsigue limitado a archivos de memoria. - La indexación de sesiones es aislada por agente (solo se indexan los logs de sesión de ese agente).
- Los logs de sesión residen en disco (
~/.openclaw/agents/<agentId>/sessions/*.jsonl). Cualquier proceso/usuario con acceso al filesystem puede leerlos, así que trata el acceso al disco como la frontera de confianza. Para aislamiento más estricto, ejecuta agentes bajo usuarios de SO o hosts separados.
Umbrales delta (valores por defecto mostrados):
agents: {
defaults: {
memorySearch: {
sync: {
sessions: {
deltaBytes: 100000, // ~100 KB
deltaMessages: 50 // líneas JSONL
}
}
}
}
}
Aceleración vectorial SQLite (sqlite-vec)
Cuando la extensión sqlite-vec está disponible, OpenClaw almacena embeddings en una
tabla virtual SQLite (vec0) y realiza consultas de distancia vectorial en la
base de datos. Esto mantiene la búsqueda rápida sin cargar todos los embeddings en JS.
Configuración (opcional):
agents: {
defaults: {
memorySearch: {
store: {
vector: {
enabled: true,
extensionPath: "/path/to/sqlite-vec"
}
}
}
}
}
Notas:
enabledes true por defecto; cuando se desactiva, la búsqueda recurre a similitud coseno en proceso sobre embeddings almacenados.- Si la extensión sqlite-vec no existe o falla al cargar, OpenClaw registra el error y continúa con el fallback JS (sin tabla vectorial).
extensionPathsobrescribe la ruta de sqlite-vec incluida (útil para builds personalizados o ubicaciones de instalación no estándar).
Descarga automática de modelo de embeddings local
- Modelo de embeddings local por defecto:
hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf(~0.6 GB). - Cuando
memorySearch.provider = "local",node-llama-cppresuelvemodelPath; si el GGUF no existe lo descarga automáticamente al caché (olocal.modelCacheDirsi está configurado), luego lo carga. Las descargas se reanudan en reintento. - Requisito de build nativo: ejecuta
pnpm approve-builds, seleccionanode-llama-cpp, luegopnpm rebuild node-llama-cpp. - Fallback: si la configuración local falla y
memorySearch.fallback = "openai", cambiamos automáticamente a embeddings remotos (openai/text-embedding-3-smalla menos que se sobrescriba) y registramos la razón.
Ejemplo de endpoint personalizado compatible con 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"
}
}
}
}
}
Notas:
remote.*tiene prioridad sobremodels.providers.openai.*.remote.headersse fusiona con las cabeceras de OpenAI; remote gana en conflictos de claves. Omiteremote.headerspara usar los valores por defecto de OpenAI.