跳到主内容
创建时间:最后更新:作者:白川

上下文与记忆类包:把大输出挡在外面

拆解六个上下文与记忆类 Pi 包——pi-context-mode、pi-lean-ctx、pi-hermes-memory、pi-total-recall、gentle-engram、@samfp/pi-memory——按技术路线、存储位置、是否需要额外模型调用三个维度切开,并指出「记忆」和「上下文压缩」是两件事。

上下文与记忆类包:把大输出挡在外面

模型跑一次 npm install,输出几千行;一次 tail -f 拉日志,几 MB;一个 schema.json dump 又是几千 token。这些东西进上下文之后,后面每一轮都在为它们付钱,而模型真正需要看的可能只是最后那 20 行报错。

于是你会想到两个方向:一是让上下文小一点,二是让模型记住之前的事。但这两个方向经常被混成一件事,而生态里六个包恰好走的是三条完全不同的路。把它们分不清,就会出现"我明明装了记忆包,上下文还是爆"——因为你装的是加法包,问题是减法问题。

Pi 官方不提供上下文压缩和记忆(内置能力的边界前面章节已经讲过)。这一层完全交给生态。这篇把六个包按三个维度切开:技术路线(减法 / 网关 / 加法)、存储位置(本地 SQLite 还是外部服务端)、是否需要额外模型调用。读完你能判断自己的痛点属于哪一类,并知道该装哪个、不该装哪个。

有个前提要先说清楚:「记忆」和「上下文压缩」是两件事。 压缩是在内容已经进入上下文之后想办法让它变小;记忆是把过去会话里的信息在下一轮重新送进上下文。前者省的是"这一轮有多长",后者花的是"每轮都要多带一点"。这六个包里,有的解决前者,有的只解决后者,还有一两个两头都沾。

六个包各管什么

先看全景。这六个包没有一个是"官方推荐",它们是六个不同作者对同一个问题的六种回答:

包版本一句话路线
pi-context-mode
1.0.170
MCP + Pi 扩展,把大输出、文件、会话历史挡在上下文外
减法
pi-lean-ctx
3.12.0
LeanCTX 上下文网关,把工具输出路由到上下文选择层
网关
pi-hermes-memory
0.10.0
持久记忆 + 会话搜索 + 密钥扫描(从 Hermes agent 移植)
加法
pi-total-recall
1.9.2
记忆 + 会话历史 + 本地知识三层上下文栈,一次安装
加法
gentle-engram
0.4.2
本地或云端共享大脑的持久记忆,跨会话 / 压缩 / MCP
加法
@samfp/pi-memory
1.6.1
会话结束时 LLM 抽取事实与教训,每轮检索注入
加法

这张表里最值得注意的一行是 pi-total-recall。 它是六个里唯一做"包聚合"的——把 @samfp/pi-memory、pi-session-search、pi-knowledge-search 三个独立包打进自己的 tarball,在 manifest 里挂载三个 extension 入口。生态里其它包都是单点扩展,它是套装。

另一件值得先说的事:这六个包都不需要 API key。 存储全在本地,或者指向你自己的服务端。

三条技术路线

这是全文最需要记住的区分。减法、网关、加法是三种不同性质的操作,装错方向就是白花时间:

路线代表包机制模型视角的变化适合的痛点
减法
pi-context-mode
内容先落到本地索引,模型只拿到检索结果
工具多了一个"检索"步骤
单次输出太大,撑爆上下文
网关
pi-lean-ctx
不改模型,替换掉内置 read/bash/grep/find/ls 的实现
同样的工具名,输出被裁剪过
想要"看起来没变,实际变小"
加法
hermes-memory、total-recall、engram、@samfp/pi-memory
每轮往上下文里注入过去的信息
系统提示/上下文多了一块
模型记不住上次说过什么

减法和加法方向是相反的。 减法想让上下文变小,加法想让上下文变大——只不过多出来的是有用信息。它们的收益完全取决于你的会话形态:如果你每天跑同一个命令十几次,减法收益巨大;如果你是每天开一个新会话干一件新事,加法才有意义。

网关路线是唯一"模型无感"的。 pi-lean-ctx 不改提示词、不加新工具,它只是把 Pi 内置的那几个文件操作工具换成了 ctx_ 前缀版本。这意味着模型学会的"怎么用 read"的知识继续有效,输出先过一道上下文选择层再进对话。代价是你得接受工具名变了。

减法:pi-context-mode

它是双重身份的——同时是一个 MCP server 和一个 Pi 扩展。这个设计不是巧合:MCP 那一半让别的宿主也能用,扩展那一半让它在 Pi 里注册工具。

11 个工具:

text
# 默认可见的 6 个
ctx_execute          执行命令,结果落库而不是进上下文
ctx_execute_file     执行文件内容
ctx_batch_execute    批量执行
ctx_fetch_and_index  抓取并索引
ctx_search           检索索引
ctx_index            建立索引

# 默认隐藏的 5 个
ctx_stats    ctx_doctor    ctx_upgrade    ctx_purge    ctx_insight

后 5 个是默认隐藏的,需要时手动启用。这意味着 ctx_doctor(诊断)和 ctx_stats(统计)这类运维工具不会天天出现在模型的可选列表里占位置。

底层是一个 SQLite FTS5 知识库。全文检索靠 SQLite 自带的 FTS5 扩展实现,不依赖向量数据库,也不需要外部 embedding 服务——这是它和加法类包最大的实现差异。本地存储,所以也不需要 API key。

它没有斜杠命令,全部通过工具调用驱动。

网关:pi-lean-ctx

它是六个里路线最特别的一个。它不"减少"任何东西,它改变东西到达上下文的方式。

具体做法:把 Pi 内置的 read、bash、grep、find、ls 五个工具替换成 ctx_ 前缀版本。模型还是发起"读取文件""执行命令"这类动作,但输出先被路由到上下文选择层,再决定进对话的是哪一部分。

工具列表:

text
ctx_read    ctx_shell    ctx_grep    ctx_find    ctx_ls
lean_ctx    ctx_compose  ctx_compress  ctx_edit  ctx_expand
ctx_graph   ctx_heatmap

注意 ctx_compress 和 ctx_expand 这一对。 它是六个包里唯一把"压缩"做成显式工具的——压缩和还原都是可以调用的动作,不是设置项里的开关。加上 ctx_graph 和 ctx_heatmap 两个可视化工具,它能给你看一眼上下文选择的分布。

配置入口是 /lean-ctx 这一个斜杠命令。它对宿主的版本有要求:pi-coding-agent >=0.74.0、pi-tui、typebox ^1.0.0。

加法:三个记忆包的共同点

剩下四个包(pi-hermes-memory、pi-total-recall、gentle-engram、@samfp/pi-memory)做的是同一类事:把过去的信息重新送回当前上下文。差别在送什么、从哪取、怎么送。

pi-hermes-memory 是从 Hermes agent 移植过来的,所以它带的不只是记忆:

工具作用
memory_search
检索记忆
memory_add
追加一条记忆
memory_replace
替换一条记忆
memory_remove
删除一条记忆
session_search
搜索历史会话
skill_manage
管理记忆相关的 Skill

斜杠命令有 10 个,覆盖面很宽:

text
/memory-insights      /memory-skills        /memory-consolidate
/memory-interview     /memory-switch-project /memory-index-sessions
/memory-sync-markdown /memory-preview-context /memory-policy
/learn-memory-tool

其中最有价值的是 Secret Scanning——这是整个生态里唯一做「API key / token / SSH key 禁止落盘」的实现。 记忆系统会把对话里的东西存下来,这本身是个风险:某次对话里粘贴了一个密钥,它就进了你的长期存储。hermes-memory 明确拦这件事。

gentle-engram 的工具数量是六个包里最多的,17 个,分成几组:

text
# 存取
mem_save  mem_search  mem_context  mem_get_observation
# 固定与评审
mem_pin  mem_unpin  mem_review  mem_judge  mem_compare
# 项目维度
mem_current_project  mem_list_projects
# 会话生命周期
mem_session_start  mem_session_end  mem_session_summary
mem_capture_passive  mem_save_prompt
# 运维
mem_doctor

mem_capture_passive 这个名字说明它的设计取向——被动捕获,不要求模型显式调用"记住这个"。配合 mem_pin(固定)和 mem_session_summary(会话摘要),它覆盖了"自动记 + 手动钉 + 定期总结"三种常见诉求。

它和前几个包最大的不同是存储不在本地:它需要一个外部 Engram 服务端 v2.1.0 或更高版本,可以是本机跑的,也可以是云端。这个设计的取舍很直接——多用户共享同一份大脑,或者多台机器同步。换来的是跨会话、跨压缩、跨 MCP 都能用,因为记忆根本不在这个进程里。

@samfp/pi-memory 的机制最需要展开讲,因为它是这条路里设计最讲究的一个。它的工作分两段:

第一段,会话结束时做 consolidation。 一个 LLM 调用读完整段对话,把里面的事实和教训抽出来,结构化写进库。

第二段,每轮开始前做检索注入。 按当前 prompt 去检索相关记忆,把结果包在 <memory> 块里注入上下文。

这里有个细节值得单独说:它不改 system prompt。 记忆内容是作为普通上下文块注入的,不是改写系统提示。这么做是为了保住前缀缓存——system prompt 一旦每轮变化,KV cache 就失效,重建成本极高。注入块放在提示的后段,不影响前面那一大段稳定前缀。

存储结构和配置:

json
{
  "memory": {
    "injectionMode": "context-hook",
    "consolidationModel": "claude-sonnet-4-20250514",
    "embedding": {
      "enabled": false
    }
  }
}

injectionMode 默认是 context-hook。数据库是 SQLite(WAL 模式),路径 ~/.pi/memory/memory.db,里面三张表:semantic、lessons、events。

consolidation 依赖模型,默认是 claude-sonnet-4-20250514。 关键的一句:在非 Anthropic provider 上这个调用是 no-op——不会报错,但也不会真的抽取。包也支持 ollama/qwen3:8b 这类本地模型。

embedding 默认关闭。 关掉的时候走 FTS5 全文检索;开了但失败会静默回退到 FTS5,不抛错。所以你不会从报错里知道降级发生了——这一点排障时会用到。

@samfp/pi-memory 的包名陷阱

这个包有一个必须先讲的安装坑,而且它的 README 顶部专门为此写了提醒。

真实仓库是 samfoy/pi-memory(仓库在 samfoy 名下,不是 samfp),但 npm 上的包名是 @samfp/pi-memory——带 @samfp/ scope。

为什么必须带 scope? 因为 npm 上存在一个无关的 pi-memory 包。不加 scope 直接 npm install pi-memory,装到的是另一个东西,然后你会花几个小时排查一个根本不相干的扩展为什么行为诡异。

正确的安装命令:

bash
# 正确:必须带 @samfp/ scope
pi install npm:@samfp/pi-memory

# 错误:npm 上有一个无关的 pi-memory,会装错
pi install npm:pi-memory

这条规则在整篇里都很重要,因为 pi-total-recall 打包的就是 @samfp/pi-memory——你通过 total-recall 装它时,拿到的是同一个 scope 下的正确包。

存储、依赖与模型调用

横向对比这三件事:

包存储位置需要 API key需要额外模型调用peerDependencies
pi-context-mode
本地 SQLite(FTS5)
否
否
pi-ai pi-tui pi-agent-core pi-coding-agent typebox,全部 *
pi-lean-ctx
路由到上下文选择层
否
否
pi-tui、pi-coding-agent >=0.74.0、typebox ^1.0.0
pi-hermes-memory
本地
否
否
pi-ai >=0.80.6、pi-tui、pi-coding-agent >=0.80.6
pi-total-recall
本地 SQLite
否
否
无(改为依赖 pi-memory / pi-session-search / pi-knowledge-search)
gentle-engram
外部 Engram 服务端 v2.1.0+(本地或云端)
否
否
pi-tui >=0.74.0、pi-coding-agent、typebox
@samfp/pi-memory
本地 SQLite(WAL)~/.pi/memory/memory.db
否
是(consolidation)
—

三条读表要点:

第一,"需要 API key"这一列全是"否",但这不等于零成本。 gentle-engram 需要一个自建服务端,@samfp/pi-memory 需要额外跑一次 consolidation 模型调用。前者是基础设施成本,后者是 token 成本——都比配 API key 更隐蔽。

第二,pi-total-recall 的 peerDependencies 是空的,这不代表它没有依赖。 它把三个包打进了自己的 tarball,依赖变成了内部依赖。这是第一章讲的"其他 Pi 包如果要作为依赖,必须包含在发布的 tarball 里"的实际应用。

第三,宿主包的版本约束不一样。 pi-context-mode 全部写 *,最宽松;pi-lean-ctx 和 gentle-engram 要求 pi-coding-agent >=0.74.0;pi-hermes-memory 要求 pi-ai 和 pi-coding-agent 都要 >=0.80.6。装之前对一下你的 Pi 版本。

工具调用长什么样

不同包的调用形态不一样,但调用通道是同一个。下面几个是最典型的用法,参数名以各包自身的工具 schema 为准:

json
{
  "name": "ctx_search",
  "arguments": {
    "query": "auth middleware 配置",
    "limit": 5
  }
}
json
{
  "name": "memory_remember",
  "arguments": {
    "content": "这个项目的测试命令是 pnpm test,不是 npm test",
    "type": "fact"
  }
}
json
{
  "name": "session_search",
  "arguments": {
    "query": "上次改数据库迁移是在哪个分支"
  }
}

ctx_search 之后模型拿到的是索引里的片段,不是原始文件;memory_remember 之后还要等 consolidation 才会真正变成可检索的记忆。

谁负责什么

职责由谁承担说明
决定哪些内容值得留在上下文
你
包只提供机制,不替你判断业务重要性
减法/网关/加法的路线选择
你
装之前先分清痛点属于哪一类
索引的存储与检索实现
对应的包
本地 SQLite 或外部服务端
记忆内容的正确性
你
记忆是模型抽出来的,会错
密钥等敏感信息不落盘
包 + 你
只有 pi-hermes-memory 内置了这条防线
consolidation 的模型与成本
你
@samfp/pi-memory 默认走 Anthropic 模型
前缀缓存不被破坏
包
@samfp/pi-memory 特意不改 system prompt
Engram 服务端的部署与可用性
你
gentle-engram 的记忆在服务端,挂了就没了

三种场景的可观察结果

场景一:单次工具输出把上下文撑爆,但任务本身不需要那些输出。 这是减法问题。装 pi-context-mode,让它把大输出落到本地索引,模型改用 ctx_search 取片段。可观察结果是工具调用仍然成功,但对话历史里不再出现几万行的原始输出,上下文长度曲线变得平缓。

场景二:想让工具行为不变,但输出自动变短。 这是网关问题,别用减法包。装 pi-lean-ctx,它替换掉内置 read/bash/grep/find/ls,模型侧几乎无感。可观察结果是工具名从 read 变成了 ctx_read,而同样的调用拿回来的内容短了很多——工具名变化本身就是它生效的信号。

场景三:模型第二天完全不记得昨天调过的接口签名。 这是加法问题,前面两个都解决不了。装 @samfp/pi-memory(注意 scope),跑一次 /memory-consolidate 把已有会话抽成记忆,下一个会话开新话题时 <memory> 块里会出现那条签名。可观察结果是检索注入的内容出现在对话历史里、而 system prompt 没有变化——后者说明前缀缓存保住了。

你现在能做什么

  • 装 pi-context-mode,然后跑一次输出很大的命令,对比调用前后的上下文长度;顺便启用默认隐藏的 ctx_stats 和 ctx_doctor 看索引状态。
  • 装 pi-lean-ctx,用 /lean-ctx 走一遍它的配置,确认内置的 read/bash 已被 ctx_ 前缀版本接管,并试一次 ctx_compress 再 ctx_expand。
  • 装 @samfp/pi-memory(带 scope),在 settings.json 里加 memory 节,跑 /memory-consolidate,然后用 memory_search 确认能检索到,再开新会话验证自动注入。
  • 装 pi-total-recall,检查它的 tarball 里确实带了 pi-memory、pi-session-search、pi-knowledge-search 三个扩展入口,然后跑 /knowledge-search-setup 把本地知识接进来。
  • 装 pi-hermes-memory,确认 Secret Scanning 生效:故意在对话里粘贴一个形似 API key 的字符串,看它是否被拒绝落盘。
  • 想用 gentle-engram 的话,先起一个 v2.1.0 以上的 Engram 服务端,确认 mem_search 能返回结果再谈别的。

排障速查

现象检查点处理
装 @samfp/pi-memory 后行为和文档完全不同
npm 包名
必须用 npm:@samfp/pi-memory;npm 上的裸 pi-memory 是无关包
memory_remember 写进去了但搜不到
consolidation 模型
默认 claude-sonnet-4-20250514 在非 Anthropic provider 上是 no-op;在 settings.json 改 consolidationModel
记忆检索质量差、像是只匹配了关键词
embedding 状态
embedding 默认关闭(走 FTS5);开启失败会静默回退,不报错
记忆把 system prompt 搞乱了
注入方式
@samfp/pi-memory 的注入是 <memory> 块,不改 system prompt,前缀缓存应保持命中
ctx_doctor / ctx_stats 工具不见了
工具可见性
这 5 个工具默认隐藏,需要手动启用
索引搜不到内容
是否已建立索引
大文件要先 ctx_index / ctx_fetch_and_index 才会进库
gentle-engram 的工具全部报错
Engram 服务端
需要外部服务端 v2.1.0+,且要指向正确的地址
pi-lean-ctx 装上但工具名还是 read
版本与重载
要求 pi-coding-agent >=0.74.0;装完 /reload
加了包但上下文反而更长
装错了路线
加法包本来就往上下文注入;输出太大应该用减法或网关包
pi-total-recall 装完少了一部分功能
tarball 内容
它聚合三个包,缺哪个检查 tarball 里的 extension 入口

参考资料