IHMT Memory
Memória de longo prazo para agentes de codificação de IA como uma árvore de arquivos locais simples, compartilhada por Claude Code, Codex e opencode.
Documentação
IHMT — Árvore de Memória Hierárquica Infinita
Memória de longo prazo para seus agentes de IA de codificação. Diga algo ao seu agente uma vez — uma decisão, como sua configuração funciona, uma correção — e ele se lembrará disso em todas as sessões futuras, em qualquer projeto, com qualquer um dos seus agentes.
O que o IHMT faz
- Lembra entre sessões. Decisões e seus motivos, seu ambiente, suas preferências, pessoas e projetos, correções. Seu agente pesquisa a memória antes de responder e salva o que dura, para que você pare de se repetir.
- Uma memória para todos os seus agentes e modelos. Claude Code, Codex e opencode podem compartilhar a mesma memória: o que um salva, os outros encontram. Testado com modelos da Anthropic, OpenAI, Google e Meta.
- Economiza tokens. Em vez de colar suas anotações ou reexplicar o contexto a cada sessão, o agente recupera apenas o que a pergunta precisa — normalmente 200–900 tokens, quer a memória contenha 50 entradas ou 50.000, porque a busca percorre uma árvore em vez de ler tudo. Números honestos, incluindo onde não economiza.
- Entende o tempo. Quando algo muda ("mudei para Valência", "o staging agora está no PostgreSQL 17"),
a memória antiga é mantida como histórico, sinalizada como
OUTDATED, e as buscas respondem primeiro com a atual. Onde você mora, onde trabalha e sua stack são rastreados automaticamente; qualquer outra correção é vinculada quando o agente a salva comreplaces. Quando uma pergunta é ambígua ("Luis" — qual deles?), ele pergunta em vez de adivinhar. - Portátil. Sua memória é uma pasta de arquivos de texto simples. Copie para outro computador, faça backup ou coloque sob controle de versão — funciona onde quer que você coloque.
- Local, privado e legível. Sem nuvem, sem banco de dados, sem conta: o IHMT armazena tudo no seu disco e não envia nada para lugar nenhum. (As memórias que seu agente recupera chegam ao modelo dele como qualquer outro contexto.) Cada memória é um arquivo de texto que você pode abrir, e cada pessoa que instala o IHMT começa com a própria memória, vazia.
Compatibilidade. Suportado oficialmente: Claude Code. Também testado: Codex (CLI e o aplicativo de desktop do ChatGPT — configuração) e opencode (configuração). O IHMT é um servidor MCP stdio padrão, então qualquer agente que suporte servidores MCP locais deve funcionar — GitHub Copilot, Antigravity, Cursor, Windsurf, Gemini CLI, Claude Desktop… — e
INSTALL.mdsabe como configurá-los, mas ainda não testamos esses. Todos eles podem compartilhar uma memória.
Instalação
Antes de começar
| Você precisa | Por quê |
|---|---|
| Python 3.10 ou mais recente | O IHMT é escrito em Python |
| git | para baixar o IHMT e mantê-lo atualizado |
| Um agente de IA que possa executar comandos (Claude Code, Codex, opencode, Copilot no modo agente…) | ele instala o IHMT e depois usa a memória |
| Internet, durante a instalação | para baixar o código e o pacote MCP; não é necessário depois |
Sem Python ou git? Seu agente de IA os instala para você (ele é instruído a perguntar primeiro). O Python vai para a sua pasta de usuário, sem senha de administrador, então nada muda no sistema. Em um Mac novo, o git pode precisar de um clique: a Apple mostra uma janela pedindo para instalar as ferramentas de linha de comando.
Em um Mac, observe que o python3 que vem com o macOS é a versão 3.9, que é muito antiga — é por isso
que seu agente pode dizer que o Python está ausente mesmo que o python3 exista.
Você não precisa de direitos de administrador, banco de dados, conta ou qualquer serviço pago além do seu agente. Testado em macOS e Linux (Ubuntu); no Windows, as instruções estão incluídas, mas ainda não testadas. Detalhes: GUIDE.md §3.
Deixe seu agente de IA instalar
Cole isto no agente de IA ao qual você quer dar uma memória — Claude Code, Codex e opencode são testados; GitHub Copilot, Antigravity, Cursor, Windsurf, Gemini CLI, Claude Desktop e outros clientes MCP devem funcionar também:
Install the IHMT memory MCP server for me from https://github.com/gonzaroman/IHMT-MEMORY — follow the instructions in its INSTALL.md.
O agente segue INSTALL.md: ele baixa o IHMT para ~/IHMT-MEMORY, mantém sua
memória em ~/.ihmt, registra o servidor apenas consigo mesmo, adiciona as instruções de uso e
informa o que fez. Depois, inicie uma nova sessão para que as ferramentas de memória sejam carregadas.
Usando vários agentes? Cole o mesmo prompt em cada um, sempre que quiser. Se o IHMT já estiver instalado — digamos que você o usou com Claude por meses e agora quer no Codex — o agente encontra essa instalação, atualiza-a se puder com segurança e conecta-se à mesma memória, então ele sabe o que você disse aos outros desde o primeiro dia.
Ele precisa de um agente que possa executar comandos de terminal ou editar arquivos; assistentes apenas de chat em um navegador não podem instalar nada.
Instalação manual
Requisitos: Python 3.10+, git e a CLI do seu 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
O nome do servidor deve vir antes de -e. Em seguida, anexe
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",
depois adicione default_tools_approval_mode = "approve" à tabela [mcp_servers.ihmt-memory] em
~/.codex/config.toml (acima da tabela env) e anexe o modelo a ~/.codex/AGENTS.md.
Detalhes.
opencode — adicione uma entrada "ihmt-memory" ("type": "local", "command": [<python>, <mcp_server.py>],
"environment": {"IHMT_HOME": <memory folder>}) ao objeto "mcp" de
~/.config/opencode/opencode.json, e anexe o modelo a ~/.config/opencode/AGENTS.md.
Detalhes.
Windows, o escopo do projeto, a configuração gráfica e a solução de problemas estão todos no guia.
Novo aqui? Leia GUIDE.md — uso diário, passo a passo, com saídas reais. O restante
deste README é a referência técnica.
Como funciona
Uma memória de longo prazo universal e independente de domínio para LLMs, armazenada como uma árvore recursiva de arquivos simples no disco local. Sem banco de dados vetorial, sem servidor, sem dependências de terceiros — Python 3.10+ e a biblioteca padrão.
Em vez de incorporar tudo em um índice plano e escaneá-lo, o IHMT organiza o conhecimento em uma
árvore: folhas de texto bruto na base, resumos JSON recursivos acima delas e um único tronco root.json
no topo. Uma consulta percorre essa árvore — raiz → ramo → ramo → folha — então o número de arquivos
abertos cresce com a profundidade da árvore (≈ beam × log_B(n)), não com a quantidade armazenada.
| RAG plano | IHMT | |
|---|---|---|
| Custo de recuperação | varredura / ANN sobre todos os n blocos | beam × log_B(n) leituras de arquivo |
| Estrutura | nenhuma — um saco de vetores | hierarquia explícita, inspecionável |
| Fragmentação | janelas fixas de caracteres | ciente de sintaxe, cena e data |
| Fatos desatualizados | servidos silenciosamente | substituídos, datados e sinalizados |
| Ambiguidade | retorna um palpite plausível | pergunta a você por uma pista |
| Armazenamento | índice binário | .txt UTF-8 + JSON que você pode ler |
Início 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
Depois use no seu próprio 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'."
Interface gráfica
python3 gui.py # opens a browser at 127.0.0.1
python3 gui.py --path ~/my-memory --port 8765 --no-browser
Ainda zero dependências — o servidor é http.server da biblioteca padrão, ele escuta apenas na
interface de loopback, e cada chamada /api/* precisa do token aleatório carregado na URL que ele abre.
Quatro telas: Configuração (escolha a pasta de memória com o diálogo do sistema, crie o armazenamento, escolha entre registro por projeto e global, visualize o comando exato ou JSON antes de qualquer coisa ser gravada), Explorar (árvore recolhível até o texto armazenado, com avisos de substituição), Diagnosticar (uma busca que relata confiança, arquivos abertos vs. total e o caminho de descida) e Linha do tempo (valores ativos vs. históricos e as contradições detectadas). A interface é bilíngue (ES/EN) e somente leitura sobre a memória: ela nunca exclui ou edita uma folha.
Use no Claude Code (MCP)
mcp_server.py expõe a árvore ao Claude Code como nove ferramentas em três famílias: memória de longo prazo, índices
de projeto e memória temporária de sessão. O núcleo permanece sem dependências; o
SDK é um extra opcional:
python3 -m venv .venv
.venv/bin/pip install -r requirements-mcp.txt # mcp[cli]>=2.0
Para um único projeto, copie .mcp.json.example para o .mcp.json desse projeto e preencha os caminhos
absolutos; o Claude Code pede sua aprovação na próxima sessão lá (claude mcp list mostra como
Pending approval até então):
{
"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" }
}
}
}
ou em um único 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 seleciona o armazenamento ($IHMT_HOME/ihmt_memory), criado no primeiro uso. Aponte cada projeto para
um diretório compartilhado para uma única memória entre projetos, ou dê a cada projeto o seu próprio.
| Ferramenta | Comportamento |
|---|---|
search_memory(query, clue=None, detail="compact") | Percorre a árvore. Saída compacta: a melhor memória com sua data e avisos OUTDATED, uma linha por outra correspondência; detail="full" adiciona ids, caminhos de árvore e trechos. Uma consulta ambígua retorna um bloco AMBIGUOUS listando os candidatos em vez de adivinhar — chame novamente com clue. Uma consulta que não corresponde a nada diz isso, e também diz uma cuja entrada mais próxima compartilha apenas uma palavra solta com ela (NOT FOUND). |
save_memory(content, domain="general", content_type="auto", replaces="") | Classifica, divide e armazena o texto, extrai fatos datados e mantém a árvore consolidada. Relata como foi arquivado e — se o salvamento contradisser algo lembrado antes — o aviso a ser repassado ao usuário. Com replaces (algumas palavras descrevendo uma memória anterior), o salvamento é registrado como sua correção: a memória antiga é sinalizada como OUTDATED e as buscas respondem primeiro com a nova. |
mark_outdated(old_id, new_id) | Sinaliza uma memória como corrigida por outra, quando save_memory encontrou vários candidatos para replaces e listou seus ids. |
project_map(path, detail="files", subpath="") | Mapa compacto de uma base de código: arquivos com seu tamanho em tokens e, com detail="symbols", cada método com seu intervalo de linhas. Construído a partir do manifesto de sincronização, sem abrir folhas. |
find_code(query, path, scope="main", subpath="", clue=None, max_tokens=1500) | Retorna apenas o símbolo que responde à consulta, como file:first-last + código. scope é main (pular testes), test ou all. |
read_file(path, force=False) | Lê um arquivo e lembra o que entregou nesta sessão: uma leitura repetida responde UNCHANGED ou apenas um diff unificado. |
note(text) / recall(query, clue=None) | Memória temporária de sessão: sobrevive a uma compactação de contexto, desaparece quando a sessão termina. |
digest_output(text, label="output") | Condensa um log longo às suas primeiras linhas, erros, falhas, totais de teste e últimas linhas; o texto completo permanece recuperável. |
Economizando tokens dentro de uma sessão
A memória de longo prazo economiza tokens entre sessões. As ferramentas de projeto economizam dentro de uma, onde o
custo é ler os mesmos arquivos repetidamente. ProjectIndex (ihmt/project_index.py) mantém um
armazenamento privado por projeto em $IHMT_PROJECTS_DIR (padrão $IHMT_HOME/ihmt_projects):
- uma folha por símbolo —
code_chunk_mode="symbol", então uma busca retorna um método, não um arquivo; - sincronização de checksum em cada chamada — tamanho e mtime primeiro, SHA-256 apenas para o que mudou; arquivos alterados são reingeridos, removidos são excluídos e os ramos reconstruídos. O código nunca é servido desatualizado;
- ramos ordenados por caminho — cada arquivo é carimbado com sua posição na ordem de caminho, então cada ramo cobre arquivos vizinhos e seu resumo permanece significativo para a descida;
- classificação ciente de código — o navegador filtra por
scope/path_prefixdo catálogo, prefere o arquivo que uma consulta nomeia, rebaixa testes a menos que solicitado e rebaixa linhas de importação.
Medido em um projeto Spring Boot de 55 arquivos (8 perguntas típicas): ler os arquivos que contêm as
respostas custa 3.613 tokens; find_code retorna o método exato para todos os 8 em 1.071. O mapa do
projeto custa 660 tokens contra 13.157 para lê-lo inteiro.
Essas economias são em relação a um agente que lê arquivos inteiros. Em um teste A/B com 22 sessões reais headless
do Claude Code, o Claude preferiu grep/sed -n em lote e nunca chamou as ferramentas de projeto por
conta própria; forçá-las tornou as sessões 42–71% mais caras. Manter o servidor habilitado custa cerca de
460 tokens por conversa, já que o Claude Code carrega as ferramentas MCP sob demanda. O principal valor do IHMT é a memória
entre sessões; trate as ferramentas de projeto como opcionais e não as torne obrigatórias em CLAUDE.md.
O servidor suporta de forma transparente o MCP SDK 2.x (MCPServer), 1.x (FastMCP) e o pacote
independente fastmcp.
As instruções de uso no seu ~/.claude/CLAUDE.md (modelo) dizem ao Claude Code quando usar cada ferramenta: buscar antes de responder qualquer coisa que
dependa de sessões anteriores, salvar fatos duráveis com sua data, nunca adivinhar em AMBIGUOUS, sempre
encaminhar OUTDATED e nunca armazenar segredos.
Estrutura de armazenamento
Tudo vive em um único diretório realocável:
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 fica dentro de ihmt_memory/ para que o armazenamento seja autocontido: copie o diretório e a
memória viaja junto.
Uma folha é um arquivo de texto normal que se descreve, então permanece significativo mesmo se o catálogo for perdido:
<<<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) {
…
Um nó de ramo incorpora o título, o trecho e as palavras-chave de cada filho. Esse é o detalhe que torna a descida barata: um ramo pode ser classificado sem abrir nenhum de seus filhos.
Os cinco componentes
1. UniversalIngestor — detectar, dividir, enriquecer, armazenar
DomainDetector classifica cada documento por tipo (CODE, NARRATIVE, CLINICAL, PERSONAL,
PROCESS, GENERIC) e domínio (software.java, medicine.clinical, process.cooking,
personal, …) a partir da extensão e de assinaturas lexicais em inglês e espanhol. Ambos podem ser
substituídos com --domain / --type.
O tipo seleciona o divisor, e todo divisor obedece a um invariante:
Um bloco lógico nunca é cortado. Se um único bloco exceder
max_tokens, ele é armazenado inteiro e sinalizado comooversized. A correção do bloco supera o orçamento de tokens.
- Código (
chunkers/code.py) — Python via stdlibast; Java/JS/TS/C/C++/C#/Go/Rust/Kotlin/ Swift/PHP viaBraceScanner, um scanner em nível de caractere que rastreia a profundidade de chaves enquanto ignora comentários, literais de string, literais de caractere, literais de template e linhas de pré-processador. Imports são agrupados; cada classe/função é um bloco; uma classe superdimensionada é dividida por membro, e o cabeçalho da classe envolvente viaja naextra.contextda folha, em vez de ser inserido no texto. Concatenar as folhas de um arquivo reproduz o arquivo byte por byte — garantido nos testes. - Narrativa — atômico por parágrafo, com
***,---,Chapter/Capítulocomo limites rígidos. Apenas um parágrafo maior quemax_tokensé dividido, e então nos limites de frases. - Temporal (diários, chats, registros clínicos) — uma entrada datada é atômica e uma mudança de data é um limite rígido, então uma folha nunca mistura dois atendimentos. A data encontrada no texto se torna o timestamp da folha, que é o que faz a ponderação por recência significar quando algo era verdade em vez de quando foi ingerido.
- Processo (receitas, protocolos, runbooks) — etapas e listas de ingredientes permanecem vinculadas ao seu título.
Os IDs das folhas são derivados de (domain, source, position, content), então reingerir um documento
inalterado reescreve as mesmas folhas em vez de duplicá-las.
2. RecursiveSummarizer — o Evento de Sumarização
Ele observa a camada 0. Quando branch_factor folhas de um domínio não têm pai, ele dispara um
Evento de Sumarização: elas são condensadas em um nó de camada 1, o nó é gravado e somente então
os filhos são carimbados com seu parent_id — para que uma execução interrompida reprocesse um grupo em vez de
orfaná-lo. A mesma regra se aplica da camada 1 para a camada 2, e assim por diante, até a árvore convergir;
então root.json é reescrito.
consolidate() é idempotente. flush() (--force) também promove grupos parciais para que a árvore feche
completamente. Qualquer coisa ainda não consolidada é referenciada diretamente pelo tronco, então nada no
armazenamento fica inacessível a partir da raiz.
3. SemanticNavigator — descida e o Loop de Pista Interativo
A classificação usa Okapi BM25 sobre o título, palavras-chave, tags e trecho de cada candidato, com pesos de campo
e frequências de documentos calculadas entre os irmãos do nível atual — exatamente a
discriminação que a descida precisa, sem custo extra de I/O. A caminhada mantém um feixe de beam_width
ramos por nível.
A confiança combina dois sinais independentes:
confidence = 0.6 × coverage + 0.4 × margin
Cobertura pergunta "essa folha realmente contém o que foi perguntado?"; margem pergunta "ela é distinguível de suas concorrentes?". Um primeiro nome comum pontua alto no primeiro e quase zero no segundo — que é precisamente quando o sistema não deve adivinhar:
$ 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
A pista dispara uma referência cruzada conjunta: candidatos que correspondem a ambos os grupos de termos são
impulsionados (×1,6), candidatos que correspondem a apenas um são rebaixados (×0,7). O loop roda até max_clue_rounds
vezes, para cedo se o usuário recusar e nunca converte silenciosamente uma consulta ambígua em uma
resposta confiante.
clue_provider é qualquer chamável, então o loop funciona para um humano em um terminal (input) ou para um
agente que permite que o LLM forneça seu próprio acompanhamento.
4. ConflictResolver — ponderação por recência e a linha do tempo
Os fatos são (subject, attribute, value, timestamp, source_leaf), registrados programaticamente via
record_fact() ou extraídos na ingestão por regras de padrão (vivo en X / I live in X,
mi stack es Y, trabajo en Z, Diagnóstico:, Tratamiento:, Medicación: …; estenda com
add_pattern).
Cada (subject, attribute) mantém uma linha do tempo datada. O valor mais recente é ACTIVE; cada um anterior
torna-se HISTORICAL com superseded_by e um intervalo valid_from/valid_to. Nada é
excluído, então ambas as perguntas permanecem respondíveis:
memory.resolver.active_state()["user::location"].value # 'Valencia' (now)
memory.resolver.state_at("2024-12-31")["user::location"].value # 'Madrid' (back then)
Repetir um valor em uma data posterior é uma confirmação, não uma contradição. Uma mudança genuína produz um
aviso transparente — "Em 2024-03-11 você disse localização do usuário = 'Madrid', mas em 2026-02-03 você atualizou para
'Valencia'." — e a folha substituída é anotada, então recuperar material desatualizado sempre chega
com sua correção anexada (SearchResult.notices).
Uma folha em si permanece ACTIVE: o que ela diz era verdade na sua própria data, e isso continua sendo a resposta
correta para uma pergunta histórica. O que muda é que ela não pode mais ser lida como atual.
5. Backends de sumarização
class SummarizerBackend(Protocol):
name: str
def summarize(self, children, *, domain: str, layer: int) -> NodeSummary: ...
HeuristicSummarizer(padrão) — sumarização extrativa da stdlib: classificação de termos TF com palavras de parada EN/ES e seleção de sentenças representativas. Offline, determinística, que é o que permite que a suíte de testes afirme sobre a forma da árvore.AnthropicSummarizer(opcional) — usado apenas quando selecionado e o pacoteanthropiceANTHROPIC_API_KEYestão ambos presentes. Todo caminho de falha (SDK ausente, chave ausente, erro de rede, resposta não analisável) recai no backend heurístico, então uma consolidação nunca é perdida porque um modelo estava inacessível.
python3 init_ihmt.py --backend anthropic # model set by summarizer_model in ihmt.config.json
Qualquer outro modelo ou runtime local se conecta implementando o mesmo protocolo e passando-o como
IHMT(..., backend=MyBackend()).
Referência da CLI
| Comando | Finalidade |
|---|---|
init [--branch-factor N] [--target-tokens N] [--force] | criar o armazenamento |
ingest <paths…|-> [--domain D] [--type T] [--tag X] [--no-consolidate] | ingerir arquivos, diretórios ou stdin |
consolidate [--force] | executar Eventos de Sumarização pendentes |
search <query> [--top-k N] [--full] | percorrer a árvore |
ask <query> [--clue TEXT] [--top-k N] | buscar com o loop de pista |
tree [--depth N] | esboço da hierarquia |
stats, facts [--subject S], conflicts [--subject S] | inspeção |
rebuild | reconstruir catálogo, linha do tempo e tronco a partir dos arquivos |
demo | passo a passo de ponta a ponta |
--path seleciona o diretório do armazenamento e --json emite saída legível por máquina; ambos funcionam antes ou
depois do subcomando.
Configuração
ihmt_memory/ihmt.config.json:
| Chave | Padrão | Significado |
|---|---|---|
branch_factor | 8 | filhos por ramo; a base logarítmica do custo de recuperação |
target_tokens / max_tokens | 2000 / 3000 | alvo de tamanho da folha e limite de superdimensionamento |
beam_width | 3 | ramos mantidos vivos por nível |
confidence_threshold | 0,45 | abaixo disso, peça uma pista |
ambiguity_margin | 0,18 | diferença de pontuação abaixo da qual os candidatos são considerados empatados |
max_clue_rounds | 3 | iterações do loop de pista |
summarizer_backend / summarizer_model | heuristic / claude-sonnet-5 | sumarização |
code_chunk_mode | pack | symbol armazena uma folha por membro de classe (usado pelos índices de projeto) |
Corpora pequenos merecem um fator de ramo pequeno — a demonstração usa branch_factor=4, target_tokens=400 para que um
punhado de documentos ainda construa uma árvore genuína de múltiplas camadas.
Testes
python3 -m unittest discover -v # from the project root
187 testes — 25 deles para o servidor MCP, ignorados sem o SDK — cobrindo: reconstrução byte-exata e invariantes de profundidade de limite para Java e Python, atomicidade de cena/data/seção, tratamento de blocos superdimensionados, idas e voltas de cabeçalho de folha, recuperação de catálogo, limites de sumarização, propagação ascendente, idempotência, acessibilidade total a partir da raiz, limites de custo de descida, o loop de pista, ponderação por recência, preservação histórica, os índices de projeto, as ferramentas MCP, a interface gráfica e a CLI.
Notas de design e limites
- A recuperação é uma descida, não uma varredura. Esse é o ponto central, e significa que um ramo podado no tronco não é revisitado. A poda em nível de domínio só acontece quando uma consulta tem sinal real no tronco; se não tiver, todos os domínios permanecem em jogo e o feixe se aplica um nível abaixo. O loop de pista é o mecanismo de recuperação quando a descida se torna ampla.
- Lexical, não semântico. A correspondência é BM25 sobre tokens com dobra de acentos e divisão em CamelCase: funciona em
qualquer idioma e não precisa de modelo, mas não corresponderá a um sinônimo. Conectar um re-classificador de embeddings
em
BM25Rankeré a atualização natural; a estrutura da árvore não muda. - As contagens de tokens são estimadas em ~4 caracteres por token. Os orçamentos só precisam ser consistentes, não exatos.
- A extração de fatos é baseada em padrões. As regras incluídas cobrem frases comuns em inglês/espanhol e
cabeçalhos clínicos;
record_fact()é o caminho confiável, eadd_pattern()estende as regras. - Escrita única. As gravações são atômicas (
tmp+os.replace) e as reconstruções de catálogo usam um arquivo de bloqueio, mas o armazenamento assume um escritor por vez. initialize(force=True)descarta apenas o estado derivado — ramos, catálogo, linha do tempo — e desanexa as folhas sobreviventes para que a próxima consolidação reconstrua a hierarquia. O conteúdo das folhas nunca é excluído.
Layout
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
Licença
MIT © 2026 Gonzalo Román Márquez (gonzaroman)