关于 IHMT Memory
IHMT Memory 是面向 AI 编码 Agent 的本地长期记忆 MCP 服务,将记忆保存为可读文本文件树,供不同会话和 Agent 共用。它支持按时间标记过时信息,并通过树状检索避免读取全部内容。
主要功能
- 01跨会话保存决策与偏好
- 02多个 Agent 共用一份记忆
- 03树状文件结构与检索
- 04标记过时信息并保留历史
- 05本地纯文本存储,无云端依赖
适用场景
- 01避免在新会话重复解释项目背景
- 02让不同编码 Agent 共享个人设置
- 03查找某项决策及其变化历史
MCP 相关
通过标准 stdio MCP 服务向客户端提供长期记忆、项目索引和会话暂存记忆工具。README 提到提供九个工具,分为三类。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
9收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| clue | any | 否 | |
| query | string | 是 | |
| detail | string | 否 |
详细介绍
IHMT — 无限层级记忆树
为 AI 编码 Agent 提供长期记忆。 告诉 Agent 一次某件事——一个决定、你的环境如何运行、一个更正——它就会在之后的每次会话、每个项目中,为你的所有 Agent 记住它。
IHMT 的功能
- 跨会话记忆。 记录决定及其原因、你的环境、偏好、相关人员和项目,以及更正。Agent 会在回答前搜索记忆,并保存值得长期保留的信息,让你不必反复说明。
- 所有 Agent 和模型共享同一份记忆。 Claude Code、Codex 和 opencode 可以共享同一份记忆:一个保存的内容,其他 Agent 也能找到。已使用 Anthropic、OpenAI、Google 和 Meta 的模型进行测试。
- 节省 token。 Agent 无需每次都粘贴笔记或重新解释上下文,而只会检索问题所需的信息——通常为 200–900 个 token,无论记忆中有 50 条还是 50,000 条,因为搜索会遍历树,而非读取所有内容。 真实数据,包括它无法节省 token 的情况。
- 理解时间变化。 当某些信息发生变化(“我搬到了 Valencia”“staging 现在使用 PostgreSQL 17”)时,旧记忆会作为历史记录保留,并标记为
OUTDATED,搜索会优先返回当前信息。你的居住地、工作地点和技术栈会自动跟踪;其他更正则会在 Agent 保存时通过replaces关联起来。当问题有歧义时(“Luis”——是哪一位?),它会先询问,而不是猜测。 - 可移植。 你的记忆就是一个包含纯文本文件的文件夹。你可以将它复制到另一台电脑、备份,或纳入版本控制——无论放在哪里都能使用。
- 本地、私密且易读。 无云服务、无数据库、无账号:IHMT 会将所有内容保存在你的磁盘上,不会将任何内容发送到其他地方。(Agent 检索到的记忆会像其他上下文一样传给其模型。)每条记忆都是可打开的文本文件,每位安装 IHMT 的用户都有自己的空白记忆。
兼容性。 官方支持:Claude Code。也已测试:Codex(CLI 和 ChatGPT 桌面应用——设置方法)以及 opencode (设置方法)。IHMT 是标准的 stdio MCP 服务,因此任何支持本地 MCP 服务的 Agent 都应该能使用——GitHub Copilot、Antigravity、Cursor、Windsurf、Gemini CLI、Claude Desktop……——而
INSTALL.md也提供了配置方法,但我们尚未测试这些工具。它们都可以共享同一份记忆。
安装
开始之前
| 你需要 | 原因 |
|---|---|
| Python 3.10 或更高版本 | IHMT 使用 Python 编写 |
| git | 用于下载 IHMT 并保持更新 |
| 能够运行命令的 AI Agent(Claude Code、Codex、opencode、Agent 模式下的 Copilot……) | 它会安装 IHMT,然后使用其中的记忆 |
| 安装期间需要互联网连接 | 用于下载代码和 MCP 软件包;安装完成后不再需要 |
缺少 Python 或 git?你的 AI Agent 会为你安装(它会先征求你的同意)。 Python 会安装在你的用户文件夹中,无需管理员密码,因此不会对系统进行任何更改。在全新 Mac 上,git 可能需要你点击一次: Apple 会显示一个窗口,提示安装其命令行工具。
请注意,在 Mac 上,macOS 自带的 python3 是 3.9 版本,版本太旧——因此即使 python3 已存在,你的 Agent 仍可能提示缺少 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? 想用的时候,可以在每个 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",
然后在 ~/.codex/config.toml 的 [mcp_servers.ihmt-memory] 表中添加
default_tools_approval_mode = "approve"(放在其 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、项目级安装、图形化设置和故障排除均见指南。
从 PyPI 安装(uvx)
无需克隆,也无需创建 venv:uv 会下载并运行
ihmt-memory 包。记忆会保存到 ~/.ihmt;设置
IHMT_HOME 可使用其他文件夹(例如,已有的记忆文件夹)。
Claude Code
claude mcp add ihmt-memory -s user -- uvx ihmt-memory
claude mcp list # ihmt-memory … ✔ Connected
uvx ihmt-memory --instructions >> ~/.claude/CLAUDE.md
Codex — codex mcp add ihmt-memory -- uvx ihmt-memory,在
~/.codex/config.toml 的 [mcp_servers.ihmt-memory] 表中添加
default_tools_approval_mode = "approve",并运行 uvx ihmt-memory --instructions >> ~/.codex/AGENTS.md。
opencode — 在 ~/.config/opencode/opencode.json 的 "mcp" 对象中添加一个 "ihmt-memory" 条目("type": "local"、"command": ["uvx", "ihmt-memory"]),并运行
uvx ihmt-memory --instructions >> ~/.config/opencode/AGENTS.md。
最后一步适用于任何安装方式,至关重要:这些说明能让 Agent 在回答前先搜索,并保存更正。也可以使用 pipx install ihmt-memory,将 uvx ihmt-memory 替换为 ihmt-memory。
刚来?请阅读 GUIDE.md——通过真实输出,一步步了解日常使用。README 的其余部分是技术参考。
工作原理
为大模型提供通用、与领域无关的长期记忆,以本地磁盘上的纯文件递归树形式存储。无需向量数据库、服务器,也无需第三方依赖——仅需 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 会将其显示为 Pending approval):
{
"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 或仅返回统一差异。 |
note(text) / recall(query, clue=None) | 会话临时记忆:上下文压缩后仍会保留,会话结束时消失。 |
digest_output(text, label="output") | 将长日志精简为开头几行、错误、失败项、测试总数和末尾几行;完整文本仍可供回忆。 |
在会话内节省令牌
长期记忆可在会话之间节省令牌。项目工具则可在单个会话内节省令牌,避免反复读取相同文件。ProjectIndex(ihmt/project_index.py)会在 $IHMT_PROJECTS_DIR 下为每个项目维护独立存储(默认路径为 $IHMT_HOME/ihmt_projects):
- 每个符号对应一个叶节点——使用
code_chunk_mode="symbol",因此查找结果是一个方法,而不是整个文件; - 每次调用都会同步校验和——先检查大小和修改时间,仅对发生变动的文件计算 SHA-256;有变更的文件会重新导入,已删除的文件会被移除,分支也会重建。代码绝不会过时;
- 分支按路径排序——每个文件都会标记其在路径顺序中的排名,因此每个分支都涵盖相邻文件,其摘要也能为逐层查找提供有意义的信息;
- 代码感知排序——导航器会根据目录中的
scope/path_prefix进行筛选,优先选择查询中提到的文件;除非用户明确要求,否则会降低测试文件和导入行的排名。
在一个有 55 个文件的 Spring Boot 项目中进行测量(8 个典型问题):读取包含答案的文件需消耗 3,613 个令牌;find_code 对全部 8 个问题都能返回准确的方法,只需 1,071 个令牌。查看项目地图需消耗 660 个令牌,而读取整个项目需 13,157 个令牌。
这些节省是相对于会读取整个文件的 Agent 而言。在一项包含 22 个真实无头 Claude Code 会话的 A/B 测试中,Claude 更倾向于批量使用 grep/sed -n,从未主动调用项目工具;强制使用这些工具反而让会话成本增加了 42–71 %。由于 Claude Code 会按需加载 MCP 工具,保持服务启用每次对话约需 460 个令牌。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)——通过标准库ast处理 Python;通过BraceScanner处理 Java/JS/TS/C/C++/C#/Go/Rust/Kotlin/Swift/PHP。BraceScanner是一种逐字符扫描器,会跟踪花括号深度,同时跳过注释、字符串字面量、字符字面量、模板字面量和预处理器行。导入语句会合并;每个类或函数都是一个逻辑块;超大的类会按成员拆分,而外围类的头部会放在叶节点的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),也适用于让大模型提供后续问题的 Agent。
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(默认)——使用标准库的抽取式摘要:基于 TF 词项排名(含 EN/ES 停用词),并选择有代表性的句子。离线且结果确定,因此测试套件可以断言树的结构。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 root
共 187 项测试,其中 25 项用于 MCP 服务,未安装 SDK 时会跳过。测试涵盖:Java 和 Python 的字节级精确重建与边界深度不变性、场景/日期/章节的原子性、超大区块处理、叶节点标头往返、目录恢复、摘要阈值、向上传播、幂等性、从根节点出发的完整可达性、下行成本边界、线索循环、时效性加权、历史记录保留、项目索引、MCP 工具、图形界面和 CLI。
设计说明与限制
- 检索是下行遍历,而非扫描。 这正是设计要点,也意味着在主干处被剪枝的分支不会再被访问。只有当查询在主干处包含实际信号时,才会进行领域级剪枝;如果没有信号,所有领域都会保留在候选范围内,并在下一层应用束搜索。线索循环是在下行遍历范围过宽时的恢复机制。
- 基于词法,而非语义。 匹配使用 BM25,对重音符号进行折叠并拆分 CamelCase 词元:适用于任何语言,无需模型,但无法匹配同义词。将嵌入式重排序器接入
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
ihmt_launcher.py the `ihmt-memory` command of the PyPI package (~/.ihmt by default)
pyproject.toml · server.json PyPI package · MCP Registry entry
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)
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。