IHMT Memory
Memoria a largo plazo para agentes de codificación de IA como un árbol de archivos locales simples, compartido por Claude Code, Codex y opencode.
Documentación
IHMT — Árbol de Memoria Jerárquica Infinita
Memoria a largo plazo para tus agentes de codificación con IA. Dile algo a tu agente una sola vez — una decisión, cómo funciona tu configuración, una corrección — y lo recordará en cada sesión futura, en cualquier proyecto, con cualquiera de tus agentes.
Qué hace IHMT
- Recuerda entre sesiones. Decisiones y sus razones, tu entorno, tus preferencias, personas y proyectos, correcciones. Tu agente busca en la memoria antes de responder y guarda lo que perdura, para que dejes de repetirte.
- Una memoria para todos tus agentes y modelos. Claude Code, Codex y opencode pueden compartir la misma memoria: lo que uno guarda, los otros lo encuentran. Probado con modelos de Anthropic, OpenAI, Google y Meta.
- Ahorra tokens. En lugar de pegar tus notas o reexplicar el contexto cada sesión, el agente recupera solo lo que la pregunta necesita — típicamente 200–900 tokens, ya sea que la memoria tenga 50 entradas o 50,000, porque la búsqueda recorre un árbol en lugar de leer todo. Números honestos, incluyendo dónde no ahorra.
- Entiende el tiempo. Cuando algo cambia ("me mudé a Valencia", "staging ahora está en PostgreSQL 17"),
la memoria antigua se conserva como historial, marcada
OUTDATED, y las búsquedas responden primero con la actual. Dónde vives, dónde trabajas y tu stack se rastrean automáticamente; cualquier otra corrección se vincula cuando el agente la guarda conreplaces. Cuando una pregunta es ambigua ("Luis" — ¿cuál?), pregunta en lugar de adivinar. - Portátil. Tu memoria es una carpeta de archivos de texto plano. Cópiala a otra computadora, haz una copia de seguridad, o ponla bajo control de versiones — funciona donde la pongas.
- Local, privada y legible. Sin nube, sin base de datos, sin cuenta: IHMT guarda todo en tu disco y no envía nada a ningún lado. (Las memorias que tu agente recupera llegan a su modelo como cualquier otro contexto.) Cada memoria es un archivo de texto que puedes abrir, y cada persona que instala IHMT comienza con su propia memoria vacía.
Compatibilidad. Oficialmente soportado: Claude Code. También probado: Codex (CLI y la aplicación de escritorio de ChatGPT — configuración) y opencode (configuración). IHMT es un servidor MCP estándar de stdio, por lo que cualquier agente que soporte servidores MCP locales debería funcionar — GitHub Copilot, Antigravity, Cursor, Windsurf, Gemini CLI, Claude Desktop… — y
INSTALL.mdsabe cómo configurarlos, pero no los hemos probado aún. Todos ellos pueden compartir una memoria.
Instalación
Antes de comenzar
| Necesitas | Por qué |
|---|---|
| Python 3.10 o más reciente | IHMT está escrito en Python |
| git | para descargar IHMT y mantenerlo actualizado |
| Un agente de IA que pueda ejecutar comandos (Claude Code, Codex, opencode, Copilot en modo agente…) | instala IHMT y luego usa la memoria |
| Internet, durante la instalación | para descargar el código y el paquete MCP; no se necesita después |
¿Te falta Python o git? Tu agente de IA los instala por ti (está instruido para preguntarte primero). Python va en tu carpeta de usuario, sin contraseña de administrador, por lo que nada cambia a nivel del sistema. En una Mac nueva, git puede necesitar un clic: Apple muestra una ventana pidiendo instalar sus herramientas de línea de comandos.
En una Mac, ten en cuenta que el python3 que viene con macOS es la versión 3.9, que es demasiado antigua — por eso
tu agente puede decir que Python falta aunque python3 exista.
No necesitas derechos de administrador, una base de datos, una cuenta, ni ningún servicio de pago más allá de tu agente. Probado en macOS y Linux (Ubuntu); en Windows las instrucciones están incluidas pero aún no probadas. Detalles: GUIDE.md §3.
Deja que tu agente de IA lo instale
Pega esto en el agente de IA al que quieras darle una memoria — Claude Code, Codex y opencode están probados; GitHub Copilot, Antigravity, Cursor, Windsurf, Gemini CLI, Claude Desktop y otros clientes MCP deberían funcionar también:
Install the IHMT memory MCP server for me from https://github.com/gonzaroman/IHMT-MEMORY — follow the instructions in its INSTALL.md.
El agente sigue INSTALL.md: descarga IHMT a ~/IHMT-MEMORY, guarda tu
memoria en ~/.ihmt, registra el servidor solo consigo mismo, agrega las instrucciones de uso, y
te dice lo que hizo. Luego inicia una nueva sesión para que las herramientas de memoria se carguen.
¿Usas varios agentes? Pega el mismo prompt en cada uno, cuando quieras. Si IHMT ya está instalado — digamos que lo has usado con Claude durante meses y ahora lo quieres en Codex — el agente encuentra esa instalación, la actualiza si puede hacerlo de forma segura, y se conecta a la misma memoria, por lo que sabe lo que les dijiste a los demás desde el primer día.
Necesita un agente que pueda ejecutar comandos de terminal o editar archivos; los asistentes solo de chat en un navegador no pueden instalar nada.
Instalación manual
Requisitos: Python 3.10+, git, y el CLI de tu agente.
git clone https://github.com/gonzaroman/IHMT-MEMORY.git ~/IHMT-MEMORY
cd ~/IHMT-MEMORY
python3 -m venv .venv
.venv/bin/pip install -r requirements-mcp.txt
mkdir -p ~/.ihmt
Claude Code
claude mcp add ihmt-memory -s user -e IHMT_HOME="$HOME/.ihmt" -- "$PWD/.venv/bin/python" "$PWD/mcp_server.py"
claude mcp list # ihmt-memory … ✔ Connected
El nombre del servidor debe ir antes de -e. Luego agrega
templates/memory-instructions.md a ~/.claude/CLAUDE.md.
Codex — codex mcp add ihmt-memory --env IHMT_HOME="$HOME/.ihmt" -- "$PWD/.venv/bin/python" "$PWD/mcp_server.py",
luego agrega default_tools_approval_mode = "approve" a la tabla [mcp_servers.ihmt-memory] en
~/.codex/config.toml (encima de su tabla env) y agrega la plantilla a ~/.codex/AGENTS.md.
Detalles.
opencode — agrega una entrada "ihmt-memory" ("type": "local", "command": [<python>, <mcp_server.py>],
"environment": {"IHMT_HOME": <memory folder>}) al objeto "mcp" de
~/.config/opencode/opencode.json, y agrega la plantilla a ~/.config/opencode/AGENTS.md.
Detalles.
Windows, el alcance del proyecto, la configuración gráfica y la resolución de problemas están todos en la guía.
¿Nuevo aquí? Lee GUIDE.md — uso cotidiano, paso a paso, con salidas reales. El resto
de este README es la referencia técnica.
Cómo funciona
Una memoria a largo plazo universal, independiente del dominio, para LLMs, almacenada como un árbol recursivo de archivos de texto plano en el disco local. Sin base de datos vectorial, sin servidor, sin dependencias de terceros — Python 3.10+ y la biblioteca estándar.
En lugar de incrustar todo en un índice plano y escanearlo, IHMT organiza el conocimiento en un
árbol: hojas de texto crudo en la parte inferior, resúmenes JSON recursivos encima, y un solo tronco root.json
en la parte superior. Una consulta recorre ese árbol — raíz → rama → rama → hoja — por lo que el número de archivos
abiertos crece con la profundidad del árbol (≈ beam × log_B(n)), no con la cantidad almacenada.
| RAG plano | IHMT | |
|---|---|---|
| Costo de recuperación | escaneo / ANN sobre todos los n fragmentos | beam × log_B(n) lecturas de archivos |
| Estructura | ninguna — una bolsa de vectores | jerarquía explícita, inspeccionable |
| Fragmentación | ventanas de caracteres fijas | consciente de sintaxis, escena y fecha |
| Hechos obsoletos | servidos en silencio | reemplazados, fechados y marcados |
| Ambigüedad | devuelve una suposición plausible | te pide una pista |
| Almacenamiento | índice binario | .txt UTF-8 + JSON que puedes leer |
Inicio rápido
python3 gui.py # graphical interface: set up, browse, inspect
python3 init_ihmt.py # or from the terminal: create ./ihmt_memory
python3 main.py demo # full walkthrough in ./demo_workspace
python3 -m unittest discover -v # stdlib only; MCP tests skip without the SDK
Luego úsalo con tu propio material:
python3 main.py ingest ~/notes ~/project/src/Main.java
python3 main.py consolidate --force
python3 main.py search "how did we handle stock reservations"
python3 main.py ask "Luis" # interactive clue loop
python3 main.py conflicts # what changed over time
Como biblioteca:
from ihmt import IHMT
memory = IHMT.initialize("./workspace")
memory.ingest_file("examples/InventoryService.java")
memory.ingest_file("examples/journal_personal.txt")
memory.flush() # close the tree up to the root
answer = memory.search("reserveStock soft hold")
print(answer.best.content) # the leaf
print(answer.best.path) # ['root', 'N1-software.java-…', 'L-software.java-…']
print(answer.node_reads) # how many branch files were opened
for notice in memory.notices():
print(notice) # "On 2024-03-11 you said user location = 'Madrid', but on 2026-02-03 you updated to 'Valencia'."
Interfaz gráfica
python3 gui.py # opens a browser at 127.0.0.1
python3 gui.py --path ~/my-memory --port 8765 --no-browser
Sigue con cero dependencias — el servidor es http.server de la biblioteca estándar, escucha solo en la
interfaz de bucle local, y cada llamada /api/* necesita el token aleatorio que lleva la URL que abre.
Cuatro pantallas: Configuración (elige la carpeta de memoria con el diálogo del sistema, crea el almacén, elige entre registro por proyecto y global, previsualiza el comando exacto o JSON antes de que se escriba algo), Explorar (árbol colapsable hasta el texto almacenado, con avisos de reemplazo), Diagnóstico (una búsqueda que informa confianza, archivos abiertos vs. totales, y la ruta de descenso), y Línea de tiempo (valores activos vs. históricos y las contradicciones detectadas). La interfaz es bilingüe (ES/EN) y de solo lectura sobre la memoria: nunca elimina ni edita una hoja.
Úsalo desde Claude Code (MCP)
mcp_server.py expone el árbol a Claude Code como nueve herramientas en tres familias: memoria a largo plazo, índices
de proyecto y memoria de borrador de sesión. El núcleo sigue sin dependencias; el
SDK es un extra opcional:
python3 -m venv .venv
.venv/bin/pip install -r requirements-mcp.txt # mcp[cli]>=2.0
Para un solo proyecto, copia .mcp.json.example al .mcp.json de ese proyecto y completa las rutas
absolutas; Claude Code te pedirá aprobarlo en la próxima sesión allí (claude mcp list lo muestra como
Pending approval hasta entonces):
{
"mcpServers": {
"ihmt-memory": {
"command": "/absolute/path/to/IHMT-MEMORY/.venv/bin/python",
"args": ["/absolute/path/to/IHMT-MEMORY/mcp_server.py"],
"env": { "IHMT_HOME": "/absolute/path/to/IHMT-MEMORY" }
}
}
}
o en un solo comando:
claude mcp add ihmt-memory --scope user \
-e IHMT_HOME=/absolute/path/to/IHMT-MEMORY \
-- /absolute/path/to/IHMT-MEMORY/.venv/bin/python /absolute/path/to/IHMT-MEMORY/mcp_server.py
IHMT_HOME selecciona el almacén ($IHMT_HOME/ihmt_memory), creado en el primer uso. Apunta cada proyecto a
un directorio compartido para una única memoria entre proyectos, o dale a cada proyecto la suya propia.
| Herramienta | Comportamiento |
|---|---|
search_memory(query, clue=None, detail="compact") | Recorre el árbol. Salida compacta: la mejor memoria con su fecha y avisos OUTDATED, una línea por cada otra coincidencia; detail="full" agrega ids, rutas de árbol y extractos. Una consulta ambigua devuelve un bloque AMBIGUOUS que lista los candidatos en lugar de adivinar — llama de nuevo con clue. Una consulta que no coincide con nada lo dice, y también lo dice aquella cuya entrada más cercana comparte solo una palabra suelta con ella (NOT FOUND). |
save_memory(content, domain="general", content_type="auto", replaces="") | Clasifica, divide y almacena el texto, extrae hechos fechados, y mantiene el árbol consolidado. Informa cómo se archivó y — si el guardado contradice algo recordado antes — el aviso para transmitir al usuario. Con replaces (unas pocas palabras que describen una memoria anterior) el guardado se registra como su corrección: la memoria antigua se marca OUTDATED y las búsquedas responden primero con la nueva. |
mark_outdated(old_id, new_id) | Marca una memoria como corregida por otra, cuando save_memory encontró varios candidatos para replaces y listó sus ids. |
project_map(path, detail="files", subpath="") | Mapa compacto de un código base: archivos con su tamaño en tokens y, con detail="symbols", cada método con su rango de líneas. Construido desde el manifiesto de sincronización, sin abrir hojas. |
find_code(query, path, scope="main", subpath="", clue=None, max_tokens=1500) | Devuelve solo el símbolo que responde a la consulta, como file:first-last + código. scope es main (omitir pruebas), test o all. |
read_file(path, force=False) | Lee un archivo y recuerda lo que entregó en esta sesión: una lectura repetida responde UNCHANGED o solo un diff unificado. |
note(text) / recall(query, clue=None) | Memoria de borrador de sesión: sobrevive a una compactación de contexto, desaparece cuando la sesión termina. |
digest_output(text, label="output") | Condensa un registro largo a sus primeras líneas, errores, fallos, totales de pruebas y últimas líneas; el texto completo sigue siendo recuperable. |
Ahorro de tokens dentro de una sesión
La memoria a largo plazo ahorra tokens entre sesiones. Las herramientas de proyecto los ahorran dentro de una, donde el
costo es leer los mismos archivos una y otra vez. ProjectIndex (ihmt/project_index.py) mantiene un
almacén privado por proyecto bajo $IHMT_PROJECTS_DIR (por defecto $IHMT_HOME/ihmt_projects):
- una hoja por símbolo —
code_chunk_mode="symbol", por lo que una búsqueda devuelve un método, no un archivo; - sincronización de checksum en cada llamada — tamaño y mtime primero, SHA-256 solo para lo que cambió; los archivos modificados se re-ingieren, los eliminados se borran, y las ramas se reconstruyen. El código nunca se sirve obsoleto;
- ramas ordenadas por ruta — cada archivo está sellado con su rango en orden de ruta, por lo que cada rama cubre archivos vecinos y su resumen sigue siendo significativo para el descenso;
- clasificación consciente del código — el navegador filtra por
scope/path_prefixdel catálogo, prefiere el archivo que una consulta nombra, degrada las pruebas a menos que se pidan y degrada las líneas de importación.
Medido en un proyecto Spring Boot de 55 archivos (8 preguntas típicas): leer los archivos que contienen las
respuestas cuesta 3,613 tokens; find_code devuelve el método exacto para las 8 en 1,071. El mapa del
proyecto cuesta 660 tokens frente a 13,157 para leerlo completo.
Esos ahorros son en comparación con un agente que lee archivos completos. En una prueba A/B con 22 sesiones reales de
Claude Code sin interfaz gráfica, Claude prefirió los grep/sed -n por lotes y nunca llamó a las herramientas del proyecto por
su cuenta; forzarlas hizo que las sesiones fueran entre un 42 y un 71 % más caras. Mantener el servidor habilitado cuesta alrededor de
460 tokens por conversación, ya que Claude Code carga las herramientas MCP bajo demanda. El valor principal de IHMT es la memoria
entre sesiones; trata las herramientas del proyecto como opcionales y no las hagas obligatorias en CLAUDE.md.
El servidor es compatible de forma transparente con MCP SDK 2.x (MCPServer), 1.x (FastMCP) y el paquete
independiente fastmcp.
Las instrucciones de uso en tu ~/.claude/CLAUDE.md (plantilla) le indican a Claude Code cuándo debe recurrir a cada herramienta: buscar antes de responder cualquier cosa que
dependa de sesiones anteriores, guardar hechos duraderos con su fecha, nunca adivinar en AMBIGUOUS, siempre
transmitir OUTDATED y nunca almacenar secretos.
Estructura de almacenamiento
Todo vive en un único directorio reubicable:
ihmt_memory/
root.json # the trunk: domains, topics, top branches, counters
layer_0/<domain>/*.txt # the leaves: raw UTF-8 text + a strict JSON header
layers/1/*.json # branches: summaries of leaves
layers/2..N/*.json # branches: summaries of summaries
state/catalog.json # index: id -> path, domain, parent, timestamp
state/facts.json # the fact timeline
ihmt.config.json # branch factor, token budgets, backend
root.json vive dentro de ihmt_memory/ para que el almacén sea autocontenido: copia el directorio y la
memoria viaja con él.
Una hoja es un archivo de texto normal que se describe a sí mismo, por lo que sigue siendo significativo incluso si se pierde el catálogo:
<<<IHMT-META
{
"leaf_id": "L-software.java-0001-84130ee123",
"timestamp": "2026-09-09T17:45:00Z",
"data_type": "CODE",
"domain": "software.java",
"tags": ["method:reserveStock", "class:InventoryService", "lang:java", "type:code"],
"parent_id": "N1-software.java-5571a7baac",
"span": {"start_line": 43, "end_line": 68},
"checksum": "sha256:…",
"extra": {"context": "public final class InventoryService {"}
}
IHMT-META>>>
public Optional<String> reserveStock(Sku sku, int quantity) {
…
Un nodo rama incorpora el título, el extracto y las palabras clave de cada hijo. Ese es el detalle que hace que el descenso sea económico: una rama se puede clasificar sin abrir ninguno de sus hijos.
Los cinco componentes
1. UniversalIngestor — detectar, dividir, enriquecer, almacenar
DomainDetector clasifica cada documento por tipo (CODE, NARRATIVE, CLINICAL, PERSONAL,
PROCESS, GENERIC) y dominio (software.java, medicine.clinical, process.cooking,
personal, …) a partir de su extensión más firmas léxicas en inglés y español. Ambos se pueden
anular con --domain / --type.
El tipo selecciona el divisor, y cada divisor obedece un invariante:
Un bloque lógico nunca se corta. Si un solo bloque supera
max_tokens, se almacena completo y se marca comooversized. La corrección del bloque prevalece sobre alcanzar el presupuesto de tokens.
- Código (
chunkers/code.py) — Python mediante laastde la biblioteca estándar; Java/JS/TS/C/C++/C#/Go/Rust/Kotlin/ Swift/PHP medianteBraceScanner, un escáner a nivel de caracteres que rastrea la profundidad de llaves mientras omite comentarios, literales de cadena, literales de carácter, literales de plantilla y líneas de preprocesador. Las importaciones se fusionan; cada clase/función es un bloque; una clase de tamaño excesivo se divide por miembro, y el encabezado de la clase contenedora viaja en elextra.contextde la hoja en lugar de insertarse en el texto. Concatenar las hojas de un archivo reproduce el archivo byte por byte — así se afirma en las pruebas. - Narrativa — atómica por párrafo, con
***,---,Chapter/Capítulocomo límites estrictos. Solo un párrafo mayor quemax_tokensse divide, y entonces en los límites de las oraciones. - Temporal (diarios, chats, registros clínicos) — una entrada fechada es atómica y un cambio de fecha es un límite estricto, por lo que una hoja nunca mezcla dos encuentros. La fecha encontrada en el texto se convierte en la marca de tiempo de la hoja, que es lo que hace que la ponderación por actualidad signifique cuándo algo era cierto en lugar de cuándo se ingirió.
- Proceso (recetas, protocolos, manuales de operaciones) — los pasos y las listas de ingredientes permanecen vinculados a su encabezado.
Los identificadores de hoja se derivan de (domain, source, position, content), por lo que volver a ingerir un documento
sin cambios reescribe las mismas hojas en lugar de duplicarlas.
2. RecursiveSummarizer — el Evento de Resumen
Observa la capa 0. Cuando branch_factor hojas de un dominio no tienen padre, se activa un
Evento de Resumen: se condensan en un nodo de capa 1, se escribe el nodo y solo entonces
se marcan los hijos con su parent_id — de modo que una ejecución interrumpida vuelve a procesar un grupo en lugar de
dejarlo huérfano. La misma regla se aplica de la capa 1 a la capa 2, y así sucesivamente, hasta que el árbol converge;
entonces se reescribe root.json.
consolidate() es idempotente. flush() (--force) también promueve grupos parciales para que el árbol se cierre
por completo. Cualquier cosa que aún no se haya consolidado se referencia directamente desde el tronco, por lo que nada en el
almacén queda inalcanzable desde la raíz.
3. SemanticNavigator — descenso y el Bucle de Pistas Interactivo
La clasificación utiliza Okapi BM25 sobre el título, las palabras clave, las etiquetas y el extracto de cada candidato, con pesos de campo
y frecuencias de documento calculadas entre los hermanos del nivel actual — exactamente la
discriminación que el descenso necesita, sin costo adicional de E/S. El recorrido mantiene un haz de beam_width
ramas por nivel.
La confianza combina dos señales independientes:
confidence = 0.6 × coverage + 0.4 × margin
Cobertura pregunta "¿esta hoja contiene realmente lo que se pidió?"; margen pregunta "¿es distinguible de sus rivales?". Un nombre común obtiene una puntuación alta en la primera y casi cero en la segunda — que es precisamente cuando el sistema no debe adivinar:
$ python main.py ask "Luis"
"Luis" is ambiguous (3 memories match this query equally well, confidence 0.62).
It could belong to any of these branches:
1. [personal] journal_personal.txt · 2024-07-22 — Vacaciones en Benidorm con Luis, mi primo…
2. [personal] journal_personal.txt · 2026-08-30 — Fin de semana en la playa de El Saler con Luis…
3. [personal] journal_personal.txt · 2024-11-30 — Cierre de trimestre… Luis Marín revisó el pull request…
Give me a clue to narrow it down (e.g. a place, a date, a project):
> vacaciones en Benidorm
query: "Luis + vacaciones en Benidorm" · confidence 0.74 · 5 node reads, 3 leaf reads, depth 2
1. [personal] journal_personal.txt · 2024-07-22
path root → N2-personal-ea34db33ae → N1-personal-59bf8bc291 → L-personal-0002-a17b3c8f35
La pista activa una referencia cruzada conjunta: los candidatos que coinciden con ambos grupos de términos se potencian
(×1,6), los candidatos que coinciden solo con uno se degradan (×0,7). El bucle se ejecuta hasta max_clue_rounds
veces, se detiene antes si el usuario lo rechaza y nunca convierte silenciosamente una consulta ambigua en una
respuesta segura.
clue_provider es cualquier invocable, por lo que el bucle funciona para un humano en una terminal (input) o para un
agente que permite que el LLM proporcione su propio seguimiento.
4. ConflictResolver — ponderación por actualidad y la línea de tiempo
Los hechos son (subject, attribute, value, timestamp, source_leaf), registrados programáticamente mediante
record_fact() o extraídos en el momento de la ingesta mediante reglas de patrones (vivo en X / I live in X,
mi stack es Y, trabajo en Z, Diagnóstico:, Tratamiento:, Medicación: …; ampliable con
add_pattern).
Cada (subject, attribute) mantiene una línea de tiempo fechada. El valor más reciente es ACTIVE; cada valor anterior
se convierte en HISTORICAL con superseded_by y un intervalo valid_from/valid_to. Nada se
elimina, por lo que ambas preguntas siguen siendo respondibles:
memory.resolver.active_state()["user::location"].value # 'Valencia' (now)
memory.resolver.state_at("2024-12-31")["user::location"].value # 'Madrid' (back then)
Repetir un valor en una fecha posterior es una confirmación, no una contradicción. Un cambio genuino produce un
aviso transparente — "El 2024-03-11 dijiste que la ubicación del usuario = 'Madrid', pero el 2026-02-03 la actualizaste a
'Valencia'." — y la hoja reemplazada se anota, de modo que recuperar material obsoleto siempre llega
con su corrección adjunta (SearchResult.notices).
Una hoja en sí misma permanece ACTIVE: lo que dice era cierto en su propia fecha, y eso sigue siendo la respuesta
correcta a una pregunta histórica. Lo que cambia es que ya no puede leerse como actual.
5. Motores de resumen
class SummarizerBackend(Protocol):
name: str
def summarize(self, children, *, domain: str, layer: int) -> NodeSummary: ...
HeuristicSummarizer(predeterminado) — resumen extractivo de la biblioteca estándar: clasificación de términos TF con palabras vacías EN/ES más selección de oraciones representativas. Sin conexión, determinista, que es lo que permite que la suite de pruebas afirme sobre la forma del árbol.AnthropicSummarizer(opcional) — se usa solo cuando se selecciona y el paqueteanthropicyANTHROPIC_API_KEYestán ambos presentes. Cada ruta de fallo (SDK faltante, clave faltante, error de red, respuesta no analizable) recurre al motor heurístico, por lo que una consolidación nunca se pierde porque un modelo no estaba disponible.
python3 init_ihmt.py --backend anthropic # model set by summarizer_model in ihmt.config.json
Cualquier otro modelo o runtime local se conecta implementando el mismo protocolo y pasándolo como
IHMT(..., backend=MyBackend()).
Referencia de CLI
| Comando | Propósito |
|---|---|
init [--branch-factor N] [--target-tokens N] [--force] | crear el almacén |
ingest <paths…|-> [--domain D] [--type T] [--tag X] [--no-consolidate] | ingerir archivos, directorios o stdin |
consolidate [--force] | ejecutar Eventos de Resumen pendientes |
search <query> [--top-k N] [--full] | recorrer el árbol |
ask <query> [--clue TEXT] [--top-k N] | buscar con el bucle de pistas |
tree [--depth N] | esquema de la jerarquía |
stats, facts [--subject S], conflicts [--subject S] | inspección |
rebuild | reconstruir catálogo, línea de tiempo y tronco a partir de los archivos |
demo | recorrido de principio a fin |
--path selecciona el directorio del almacén y --json emite salida legible por máquina; ambos funcionan antes o
después del subcomando.
Configuración
ihmt_memory/ihmt.config.json:
| Clave | Predeterminado | Significado |
|---|---|---|
branch_factor | 8 | hijos por rama; la base logarítmica del costo de recuperación |
target_tokens / max_tokens | 2000 / 3000 | objetivo de tamaño de hoja y umbral de sobredimensionamiento |
beam_width | 3 | ramas mantenidas vivas por nivel |
confidence_threshold | 0,45 | por debajo de esto, pedir una pista |
ambiguity_margin | 0,18 | brecha de puntuación bajo la cual los candidatos se consideran empatados |
max_clue_rounds | 3 | iteraciones del bucle de pistas |
summarizer_backend / summarizer_model | heuristic / claude-sonnet-5 | resumen |
code_chunk_mode | pack | symbol almacena una hoja por miembro de clase (usado por los índices de proyecto) |
Los corpus pequeños merecen un factor de ramificación pequeño — la demo usa branch_factor=4, target_tokens=400 para que un
puñado de documentos aún construya un árbol real de múltiples capas.
Pruebas
python3 -m unittest discover -v # from the project root
187 pruebas — 25 de ellas para el servidor MCP, omitidas sin el SDK — que cubren: reconstrucción byte exacto e invariantes de profundidad de límites para Java y Python, atomicidad de escena/fecha/sección, manejo de bloques sobredimensionados, ida y vuelta de encabezados de hoja, recuperación de catálogo, umbrales de resumen, propagación ascendente, idempotencia, alcanzabilidad completa desde la raíz, límites de costo de descenso, el bucle de pistas, ponderación por actualidad, preservación histórica, los índices de proyecto, las herramientas MCP, la interfaz gráfica y la CLI.
Notas de diseño y límites
- La recuperación es un descenso, no un escaneo. Ese es el punto central, y significa que una rama podada en el tronco no se vuelve a visitar. La poda a nivel de dominio solo ocurre cuando una consulta tiene señal real en el tronco; si no la tiene, todos los dominios permanecen en juego y el haz se aplica un nivel más abajo. El bucle de pistas es el mecanismo de recuperación cuando el descenso se vuelve amplio.
- Léxico, no semántico. La coincidencia es BM25 sobre tokens con plegado de acentos y división de CamelCase: funciona en
cualquier idioma y no necesita modelo, pero no coincidirá con un sinónimo. Conectar un reclasificador de incrustaciones
en
BM25Rankeres la mejora natural; la estructura del árbol no cambia. - Los recuentos de tokens se estiman en aproximadamente 4 caracteres por token. Los presupuestos solo necesitan ser consistentes, no exactos.
- La extracción de hechos se basa en patrones. Las reglas incluidas cubren frases comunes en inglés/español y
encabezados clínicos;
record_fact()es la ruta confiable, yadd_pattern()extiende las reglas. - Escritor único. Las escrituras son atómicas (
tmp+os.replace) y las reconstrucciones de catálogo toman un archivo de bloqueo, pero el almacén asume un escritor a la vez. initialize(force=True)descarta solo el estado derivado — ramas, catálogo, línea de tiempo — y separa las hojas supervivientes para que la próxima consolidación reconstruya la jerarquía. El contenido de las hojas nunca se elimina.
Diseño
ihmt/
api.py IHMT facade wiring everything together
config.py IHMTConfig
models.py MemoryLeaf, BranchNode, ChildRef, RootIndex, Fact, Contradiction
storage.py MemoryStore: atomic I/O, catalog, recovery
textutils.py tokenizing, keywords, entities, extractive summary, timestamps
detectors.py DomainDetector
chunkers/ base · code · narrative · temporal · process · generic
summarizers.py SummarizerBackend · Heuristic · Anthropic
universal_ingestor.py UniversalIngestor
recursive_summarizer.py RecursiveSummarizer
semantic_navigator.py SemanticNavigator, BM25Ranker, ClueRequest, scope/path filters
project_index.py ProjectIndex: per-project code cache with checksum sync
conflict_resolver.py ConflictResolver + timeline manager
ihmt_gui/ local graphical interface (stdlib only)
mcp_server.py MCP server: the nine tools
init_ihmt.py · main.py · gui.py · examples/ · tests/
GUIDE.md installation and usage guide
INSTALL.md installation instructions for AI agents
templates/ memory-instructions.md: the usage rules agents append to CLAUDE.md / AGENTS.md
Licencia
MIT © 2026 Gonzalo Román Márquez (gonzaroman)