IHMT:面向 AI 编码智能体的无限层级记忆树开源发布
Show HN: IHMT – Infinite Hierarchical Memory Tree for AI Agents
作者发布 IHMT(Infinite Hierarchical Memory Tree),一个以本地纯文本文件树存储的 LLM 长期记忆系统,作为 stdio MCP server 接入 Claude Code、Codex 和 opencode,多个智能体可共享同一份记忆。
IHMT — 无限层级记忆树
为你的 AI 编程代理提供长期记忆。只需告诉你的代理一次——某个决定、你的环境如何运作、某个更正——它就会在未来的每个会话、任何项目、任何代理中记住它。
IHMT 的功能
- 跨会话记忆。决定及其原因、你的环境、你的偏好、人物和项目、更正。你的代理在回答前会搜索记忆,并保存持久的内容,这样你就不必反复重复。
- 所有代理和模型共享一份记忆。Claude Code、Codex 和 opencode 可以共享同一份记忆:一个保存的内容,其他都能找到。已用来自 Anthropic、OpenAI、Google 和 Meta 的模型测试。
- 节省 token。无需每个会话都粘贴你的笔记或重新解释上下文,代理只检索问题所需的内容——通常 200–900 个 token,无论记忆中有 50 条还是 50,000 条,因为搜索是遍历树而不是读取全部内容。 诚实的数字,包括它不节省的情况。
- 理解时间。当某些内容发生变化(“我搬到了 Valencia”,“staging 现在用的是 PostgreSQL 17”)时,旧记忆会作为历史保留,标记为
OUTDATED,搜索时会优先用当前记忆回答。你居住的地方、工作的地方和技术栈会被自动跟踪;任何其他更正会在代理用replaces保存时被关联。当问题有歧义时(“Luis”——哪一个?),它会询问而不是猜测。 - 可移植。你的记忆就是一个纯文本文件夹。把它复制到另一台电脑、备份,或纳入版本控制——放在哪里都能用。
- 本地、私密、可读。没有云、没有数据库、没有账户:IHMT 把所有内容存在你的磁盘上,不向任何地方发送任何东西。(你的代理检索到的记忆会像其他上下文一样到达其模型。)每条记忆都是你可以打开的文本文件,每个安装 IHMT 的人都从自己的空记忆开始。
兼容性。官方支持:Claude Code。也已测试:Codex(CLI 和 ChatGPT 桌面应用——设置)和 opencode (设置)。IHMT 是一个标准的 stdio MCP 服务器,所以任何支持本地 MCP 服务器的代理都应该能用——GitHub Copilot、Antigravity、Cursor、Windsurf、Gemini CLI、Claude Desktop……——并且
INSTALL.md知道如何配置它们,但我们尚未测试这些。它们都可以共享一份记忆。
安装
开始之前
| 你需要 | 为什么 |
|---|---|
| Python 3.10 或更新版本 | IHMT 是用 Python 编写的 |
| git | 用于下载 IHMT 并保持更新 |
| 一个能运行命令的 AI 代理(Claude Code、Codex、opencode、agent 模式下的 Copilot……) | 它安装 IHMT 然后使用记忆 |
| 安装期间需要联网 | 用于下载代码和 MCP 包;之后不再需要 |
缺少 Python 或 git?你的 AI 代理会为你安装(它被指示先询问你)。 Python 会安装到你的用户文件夹,无需管理员密码,因此不会更改系统范围内的任何内容。在全新的 Mac 上,git 可能需要点一下: Apple 会显示一个窗口,要求安装其命令行工具。
在 Mac 上,请注意 macOS 自带的 python3 是 3.9 版本,太旧了——这就是为什么即使 python3 存在,你的代理也可能说缺少 Python。
你不需要管理员权限、数据库、账户,也不需要除你的 agent 之外的任何付费服务。已在 macOS 和 Linux(Ubuntu)上测试;在 Windows 上包含说明但尚未测试。详情:GUIDE.md §3。
让你的 AI agent 来安装它
把这段内容粘贴到你想要赋予记忆的 AI agent 中——Claude Code、Codex 和 opencode 已测试;GitHub Copilot、Antigravity、Cursor、Windsurf、Gemini CLI、Claude Desktop 以及其他 MCP 客户端应该也能用:
Install the IHMT memory MCP server for me from https://github.com/gonzaroman/IHMT-MEMORY — follow the instructions in its INSTALL.md.
agent 会遵循 INSTALL.md:它将 IHMT 下载到 ~/IHMT-MEMORY,把你的记忆保存在 ~/.ihmt,仅向自身注册该服务器,添加使用说明,并告诉你它做了什么。然后启动一个新会话,以便加载记忆工具。
使用多个 agent? 随时把同样的提示词粘贴到每一个中。如果 IHMT 已经安装——比如你已经用 Claude 用了几个月,现在想在 Codex 中使用——agent 会找到那个安装,在能安全操作时更新它,并连接到同一份记忆,因此它从第一天起就知道你告诉过其他 agent 的内容。
它需要一个能运行终端命令或编辑文件的 agent;浏览器中仅能聊天的助手无法安装任何东西。
手动安装
要求:Python 3.10+、git,以及你的 agent 的 CLI。
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
服务器名称必须位于 -e 之前。然后将 templates/memory-instructions.md 追加到 ~/.claude/CLAUDE.md。
Codex — codex mcp add ihmt-memory --env IHMT_HOME="$HOME/.ihmt" -- "$PWD/.venv/bin/python" "$PWD/mcp_server.py",然后将 default_tools_approval_mode = "approve" 添加到 ~/.codex/config.toml 中的 [mcp_servers.ihmt-memory] 表(位于其 env 表之上),并将模板追加到 ~/.codex/AGENTS.md。详情。
opencode — 向 ~/.config/opencode/opencode.json 的 "mcp" 对象添加一个 "ihmt-memory" 条目("type": "local"、"command": [<python>, <mcp_server.py>]、"environment": {"IHMT_HOME": <memory folder>}),并将模板追加到 ~/.config/opencode/AGENTS.md。详情。
Windows、项目范围、图形化设置和故障排除都在指南中。
新手?请阅读 GUIDE.md——日常使用,一步一步,附真实输出。本 README 的其余部分是技术参考。
工作原理
一种通用、与领域无关的 LLM 长期记忆,以本地磁盘上的纯文件递归树形式存储。没有向量数据库,没有服务器,没有第三方依赖——只需 Python 3.10+ 和标准库。
IHMT 不是把所有内容嵌入到一个扁平索引中并扫描它,而是将知识组织成一棵树:底部是原始文本叶子,其上是递归的 JSON 摘要,顶部是一个单一的 root.json 主干。查询会沿着这棵树行走——根 → 分支 → 分支 → 叶子——因此打开的文件数量随树的深度(≈ beam × log_B(n))增长,而不是随存储量增长。
| 扁平 RAG | IHMT | |
|---|---|---|
| 检索成本 | 对所有 n 个块进行扫描 / ANN | beam × log_B(n) 次文件读取 |
| 结构 | 无——一堆向量 | 显式层级,可检查 |
| 分块 | 固定字符窗口 | 语法、场景和日期感知 |
| 过时事实 | 静默提供 | 被取代、标注日期并标记 |
| 歧义 | 返回一个看似合理的猜测 | 向你询问线索 |
| 存储 | 二进制索引 | UTF-8 .txt + 可读的 JSON |
快速开始
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
然后在你自己的材料上使用它:
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
作为库:
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'."
图形界面
python3 gui.py # opens a browser at 127.0.0.1 python3 gui.py --path ~/my-memory --port 8765 --no-browser
仍然零依赖——服务器是标准库中的 http.server,它只监听回环接口,并且每次 /api/* 调用都需要它打开的 URL 中携带的随机令牌。
四个界面:设置(用系统对话框选择记忆文件夹,创建存储,在按项目注册和全局注册之间选择,在写入任何内容之前预览确切的命令或 JSON)、浏览(可折叠的树,向下到存储的文本,带有取代通知)、诊断(一个报告置信度、已打开文件数与总数、以及下降路径的搜索),以及时间线(活跃值与历史值以及检测到的矛盾)。界面是双语的(ES/EN),并且对记忆是只读的:它从不删除或编辑叶子。
从 Claude Code(MCP)使用它
mcp_server.py 将树作为三个家族中的九个工具暴露给 Claude Code:长期记忆、项目索引和会话暂存记忆。核心保持无依赖;SDK 是可选的附加项:
python3 -m venv .venv
.venv/bin/pip install -r requirements-mcp.txt # mcp[cli]>=2.0对于单个项目,将 .mcp.json.example 复制到该项目的 .mcp.json 并填写绝对路径;Claude Code 会在那里的下一次会话时要求你批准它(在此之前,claude mcp list 将其显示为待批准):
{
"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" }
}
}
}或者用一条命令:
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 选择存储($IHMT_HOME/ihmt_memory),在首次使用时创建。将所有项目指向一个共享目录以获得单一的跨项目记忆,或者给每个项目自己的目录。
| 工具 | 行为 |
|---|---|
search_memory(query, clue=None, detail="compact") |
遍历树。紧凑输出:最佳记忆及其日期和 OUTDATED 通知,其他匹配每行一个;detail="full" 添加 id、树路径和摘录。模糊查询返回一个 AMBIGUOUS 块列出候选,而不是猜测——用 clue 再次调用。没有匹配的查询会说明这一点,最接近的条目只与它共享一个无关词的查询也会说明这一点(NOT FOUND)。 |
save_memory(content, domain="general", content_type="auto", replaces="") |
对文本进行分类、拆分和存储,提取带日期的事实,并保持树合并。报告它是如何归档的,以及——如果保存与之前记住的内容矛盾——要转达给用户的通知。使用 replaces(几个词描述一个较早的记忆)时,保存被记录为它的更正:旧记忆被标记为 OUTDATED,搜索首先用新记忆回答。 |
mark_outdated(old_id, new_id) |
将一个记忆标记为被另一个更正,当 save_memory 为 replaces 找到多个候选并列出它们的 id 时。 |
project_map(path, detail="files", subpath="") |
代码库的紧凑映射:文件及其以 token 计的大小,以及使用 detail="symbols" 时,每个方法及其行范围。从同步清单构建,不打开叶子。 |
find_code(query, path, scope="main", subpath="", clue=None, max_tokens=1500) |
只返回回答查询的符号,作为 file:first-last + 代码。scope 是 main(跳过测试)、test 或 all。 |
read_file(path, force=False) |
读取一个文件并记住它本次会话分发了什么:重复读取回答 UNCHANGED 或仅一个统一 diff。 |
note(text) / recall(query, clue=None) |
会话暂存记忆:在上下文压缩后存活,在会话结束时消失。 |
digest_output(text, label="output") |
将长日志浓缩为其首行、错误、失败、测试总计和末行;全文仍可召回。 |
在会话内节省 token
长期记忆在会话之间节省 token。项目工具在会话内节省它们,其中成本是反复读取相同的文件。ProjectIndex(ihmt/project_index.py)在 $IHMT_PROJECTS_DIR 下为每个项目保留一个私有存储(默认 $IHMT_HOME/ihmt_projects):
- 每个符号一个叶子——
code_chunk_mode="symbol",因此查找返回一个方法,而不是一个文件; - 每次调用时校验和同步——先看大小和 mtime,仅对移动过的内容做 SHA-256;更改的文件被重新摄取,删除的文件被删除,分支被重建。代码从不提供过时的;
- 路径排序的分支——每个文件被标记其在路径顺序中的排名,因此每个分支覆盖相邻文件,其摘要对下降保持有意义;
- 代码感知排序 — 导航器根据目录中的
scope/path_prefix进行过滤,优先选择查询中提到的文件,除非被要求否则降低测试的优先级,并降低 import 行的优先级。
在一个 55 个文件的 Spring Boot 项目上测量(8 个典型问题):读取包含答案的文件需要 3,613 个 token;find_code 在 1,071 个 token 内为全部 8 个问题返回确切的方法。项目地图需要 660 个 token,而完整读取它需要 13,157 个 token。
这些节省是相对于读取整个文件的 agent 而言的。在一项包含 22 个真实无头 Claude Code 会话的 A/B 测试中,Claude 更偏好批量 grep/sed -n,并且从不自行调用项目工具;强制调用它们会使会话成本增加 42–71%。保持服务器启用每轮对话约花费 460 个 token,因为 Claude Code 按需加载 MCP 工具。IHMT 的主要价值在于会话之间的记忆;将项目工具视为可选,不要在 CLAUDE.md 中强制要求它们。
该服务器透明地支持 MCP SDK 2.x(MCPServer)、1.x(FastMCP)以及独立的 fastmcp 包。
你的 ~/.claude/CLAUDE.md(模板)中的使用说明告诉 Claude Code 何时使用每个工具:在回答任何依赖先前会话的问题之前先搜索,保存持久事实时附带其日期,在 AMBIGUOUS 上绝不猜测,始终转发 OUTDATED,并且绝不存储机密。
存储布局
所有内容都存放在一个可重定位的目录中:
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 位于 ihmt_memory/ 内部,因此存储是自包含的:复制该目录,记忆也随之迁移。
叶子节点是一个描述自身的普通文本文件,因此即使目录丢失它仍然有意义:
<<<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) {
…
分支节点嵌入每个子节点的标题、摘录和关键词。正是这一细节使得下降过程成本低廉:一个分支可以在不打开任何子节点的情况下被排序。
五个组件
1. UniversalIngestor — 检测、拆分、丰富、存储
DomainDetector 根据扩展名以及英语和西班牙语的词汇特征,将每个文档按类型(CODE、NARRATIVE、CLINICAL、PERSONAL、PROCESS、GENERIC)和领域(software.java、medicine.clinical、process.cooking、personal、……)进行分类。两者都可以用 --domain / --type 覆盖。
类型决定拆分器,而每个拆分器都遵循一个不变式:
逻辑块绝不被切断。如果单个块超过
max_tokens,则整体存储并标记为oversized。块的正确性优先于达到 token 预算。
- 代码(
chunkers/code.py)— Python 通过标准库ast;Java/JS/TS/C/C++/C#/Go/Rust/Kotlin/Swift/PHP 通过BraceScanner,这是一个字符级扫描器,在跳过注释、字符串字面量、字符字面量、模板字面量和预处理器行的同时跟踪花括号深度。import 会被合并;每个类/函数是一个块;过大的类按每个成员拆分,而外层类头随叶子的extra.context一起传递,而不是被拼接到文本中。将文件的叶子节点拼接起来可以逐字节重现该文件——这一点在测试中已断言。 - 叙述性文本 — 段落原子化,以
***、---、Chapter/Capítulo作为硬边界。只有大于max_tokens的段落才会被拆分,且按句子边界拆分。 - 时间性(日志、聊天、临床记录)— 一条带日期的条目是原子的,日期变化是硬边界,因此一个叶子节点绝不会混合两次相遇。文本中找到的日期成为叶子节点的时间戳,这正是使新近度加权意味着某事何时为真而非何时被摄取的原因。
- 流程(配方、协议、操作手册)——步骤和配料清单始终与其标题保持关联。
叶子 id 派生自 (domain, source, position, content),因此重新摄取未更改的文档会重写相同的叶子,而不是复制它们。
2. RecursiveSummarizer —— 摘要事件
它监视第 0 层。当一个域中的 branch_factor 个叶子没有父节点时,它会触发一个
摘要事件:它们被压缩为一个第 1 层节点,该节点被写入,只有在这之后
子节点才会被标记上它们的 parent_id —— 因此中断的运行会重新处理一个组,
而不是将其孤立。同样的规则适用于从第 1 层到第 2 层,依此类推,直到树收敛;
然后 root.json 被重写。
consolidate() 是幂等的。flush()(--force)也会提升部分组,以便树完全闭合。
任何仍未合并的内容都由主干直接引用,因此存储中的任何内容
都不会从根节点不可达。
3. SemanticNavigator —— 下降与交互式线索循环
排序使用 Okapi BM25 对每个候选的标题、关键词、标签和摘要进行评分,字段权重
和文档频率在当前层的兄弟节点之间计算 —— 这正是
下降所需的区分度,且没有额外的 I/O 成本。遍历在每一层保持一个 beam_width
分支的束。
置信度融合了两个独立的信号:
confidence = 0.6 × coverage + 0.4 × margin
覆盖率问的是“这个叶子是否真的包含所询问的内容?”;边际问的是“它 是否能与竞争对手区分开来?”。一个常见的名字在第一项上得分很高,而在第二项上 接近零 —— 这正是系统绝不能猜测的时候:
$ 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
线索触发一个联合交叉引用:同时匹配两个词组的候选被提升
(×1.6),只匹配一个的候选被降级(×0.7)。循环最多运行 max_clue_rounds
次,如果用户拒绝则提前停止,并且绝不会默默地将一个模糊查询转换为一个
自信的答案。
clue_provider 是任何可调用对象,因此该循环适用于终端前的人类(input)或
让 LLM 提供自己的后续问题的代理。
4. ConflictResolver —— 新近度加权与时间线
事实是 (subject, attribute, value, timestamp, source_leaf),通过
record_fact() 以编程方式记录,或在摄取时通过模式规则提取(vivo en X / I live in X,
mi stack es Y,trabajo en Z,Diagnóstico:,Tratamiento:,Medicación: …;用
add_pattern 扩展)。
每个 (subject, attribute) 保留一个带日期的时间线。最新的值是 ACTIVE;每个更早的值
变成 HISTORICAL,带有 superseded_by 和一个 valid_from/valid_to 区间。没有任何内容被
删除,因此两个问题都保持可回答:
memory.resolver.active_state()["user::location"].value # 'Valencia' (now) memory.resolver.state_at("2024-12-31")["user::location"].value # 'Madrid' (back then)
在较晚的日期重复一个值是确认,而不是矛盾。真正的变化会产生一个
透明的通知 —— “在 2024-03-11 你说用户位置 = 'Madrid',但在 2026-02-03 你更新为
'Valencia'。” —— 并且被取代的叶子会被标注,因此检索过时的材料时总是
附带其更正(SearchResult.notices)。
叶子本身保持 ACTIVE:它所说的在其自身日期是真实的,并且这仍然是
历史问题的正确答案。变化的是它不能再被解读为当前信息。
5. 摘要后端
class SummarizerBackend(Protocol): name: str def summarize(self, children, *, domain: str, layer: int) -> NodeSummary: ...
HeuristicSummarizer(默认)—— 标准库提取式摘要:使用 EN/ES 停用词的 TF 词项排名 加上代表性句子选择。离线、确定性,这正是测试 套件能够对树形结构进行断言的原因。AnthropicSummarizer(可选)—— 仅在选中且anthropic包和ANTHROPIC_API_KEY都存在时使用。每条失败路径(缺少 SDK、缺少密钥、网络错误、 无法解析的回复)都会回退到启发式后端,因此合并绝不会因为 模型不可达而丢失。
python3 init_ihmt.py --backend anthropic # model set by summarizer_model in ihmt.config.json任何其他模型或本地运行时只要实现同一协议并把它作为 IHMT(..., backend=MyBackend()) 传入,就能接入。
CLI 参考
| 命令 | 用途 |
|---|---|
init [--branch-factor N] [--target-tokens N] [--force] |
创建存储 |
ingest <paths…|-> [--domain D] [--type T] [--tag X] [--no-consolidate] |
摄取文件、目录或标准输入 |
consolidate [--force] |
运行待处理的摘要事件 |
search <query> [--top-k N] [--full] |
遍历树 |
ask <query> [--clue TEXT] [--top-k N] |
用线索循环进行搜索 |
tree [--depth N] |
层级大纲 |
stats、facts [--subject S]、conflicts [--subject S] |
检查 |
rebuild |
从文件重建目录、时间线和主干 |
demo |
端到端演练 |
--path 选择存储目录,--json 输出机器可读的结果;两者在子命令之前或之后都可用。
配置
ihmt_memory/ihmt.config.json:
| 键 | 默认值 | 含义 |
|---|---|---|
branch_factor |
8 | 每个分支的子节点数;检索开销的对数底数 |
target_tokens / max_tokens |
2000 / 3000 | 叶节点大小目标和超大阈值 |
beam_width |
3 | 每层保持存活的分支数 |
confidence_threshold |
0.45 | 低于此值,就请求线索 |
ambiguity_margin |
0.18 | 低于此分数差距的候选视为并列 |
max_clue_rounds |
3 | 线索循环迭代次数 |
summarizer_backend / summarizer_model |
heuristic / claude-sonnet-5 |
摘要 |
code_chunk_mode |
pack |
symbol 为每个类成员存储一个叶节点(供项目索引使用) |
小语料库适合小的分支因子——演示使用 branch_factor=4, target_tokens=400,这样少量文档仍能构建出真正的多层树。
测试
python3 -m unittest discover -v # from the project root187 项测试——其中 25 项针对 MCP 服务器,在没有 SDK 时跳过——覆盖:Java 和 Python 的字节级精确重建与边界深度不变量、场景/日期/章节原子性、超大块处理、叶节点头部往返、目录恢复、摘要阈值、向上传播、幂等性、从根节点的完全可达性、下降成本界限、线索循环、新近度加权、历史保留、项目索引、MCP 工具、图形界面和 CLI。
设计说明与限制
- 检索是下降,不是扫描。这正是关键所在,这意味着在主干处被剪掉的分支不会被重新访问。只有当查询在主干处有实际信号时,才会进行领域级剪枝;如果没有信号,所有领域都保持参与,束搜索在下一层应用。当下降范围过宽时,线索循环就是恢复机制。
- 词法匹配,而非语义匹配。匹配是在去除重音、按 CamelCase 拆分后的词元上运行 BM25:它适用于任何语言且无需模型,但无法匹配同义词。将嵌入重排序器接入
BM25Ranker是自然的升级方式;树结构不会改变。 - 词元数是估算的,约为每词元 4 个字符。预算只需保持一致,不必精确。
- 事实提取基于模式。内置规则覆盖常见的英语/西班牙语表述和临床标题;
record_fact()是可靠的路径,add_pattern()可扩展规则。 - 单写入者。写入是原子的(
tmp+os.replace),目录重建会获取锁文件,但存储假定一次只有一个写入者。 initialize(force=True)只丢弃派生状态——分支、目录、时间线——并分离存活的叶节点,以便下一次合并重建层级。叶节点内容永远不会被删除。
布局
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
许可证
MIT © 2026 Gonzalo Román Márquez (gonzaroman)
来源:Hacker News · github.com