banana-memory
v0.4.0
Published
Local, source-grounded project memory over Streamable HTTP MCP
Maintainers
Readme
Banana Memory
在本机运行的项目记忆服务。Claude Code 通过一个全局 Agent Skill 决定何时召回和记录,通过 Streamable HTTP MCP 访问本机的 Qwen3、llama.cpp 和 LanceDB。
当前提供 macOS Apple Silicon 本地试用版。生成使用 Qwen3-8B Q4_K_M,首次模型和运行时下载约 5.68 GB,请预留至少 10 GB 可用空间。8B 模型小样本已在 M4 / 32 GiB 上验证;16 GB 机器验证仍是正式发布门槛,详细证据见 验收记录。
安装与使用
需要 Node.js 22.23.1 或更高版本,以及 Codex 或 Claude Code。在第一个终端启动前台服务:
npx -y banana-memory@latest start服务只监听 127.0.0.1:3927。终端会持续显示模型准备状态,并输出本地管理页地址、Codex 配置和一条带本机访问 token 的 claude mcp add 命令。管理页会显示实际使用的生成模型、向量模型和 llama.cpp 运行时,也可以筛选所有项目的记忆,查看状态、时间退化、来源、版本历史以及合并和派生关系。保持该终端运行;按 Ctrl+C 会安全关闭模型与数据库,已记录的数据和下载进度保留在 ~/.banana-memory。
在管理页左侧点击某个项目会打开该项目的“时间线”:一张按时间倒序的表格,显示收到记录、新增事实或经历、晋升、进入审核、被取代、归档、用户修正、归纳经验或归纳失败、生成摘要、反馈验证、出现反例、删除以及暂停和恢复,并给出该次变更的类型、内容与原因;点击内容会跳到下方对应的记忆。时间线全部由已存储的事件、记忆版本、版本历史、反馈、墓碑和维护指令推导,服务不会为此写入任何新记录,也只显示真正写入过的状态变化——按时间衰减或环境不匹配导致的读取期降级不在其中。表格每次读取 100 条,“加载更早”继续向前翻页;它只在打开、点“重新加载”或翻页时请求数据,不参与 30 秒一次的自动刷新。
如果服务已经运行,再执行一次 start 会重新显示现有 UI、MCP 地址和添加命令,然后正常退出,不会启动第二套模型进程。UI 地址把 token 放在 URL fragment 中;浏览器收到后会移除 fragment,并仅在当前标签页会话中保存凭据。
一次性安装全局 Skill:
npx skills add Kilerd/banana-memory \
--skill banana-memory \
--global \
--agent codex claude-code \
--yesCodex 用户把服务输出的配置加入 ~/.codex/config.toml:
[mcp_servers.banana-memory]
url = "http://127.0.0.1:3927/mcp"
http_headers_helper = "npx -y banana-memory@latest codex-headers 'file:///Users/you/.banana-memory'"请使用服务实际输出的路径,不要照抄示例中的 you。这个 helper 从本机私有数据目录读取访问 token,并把当前 Codex 进程的规范化工作目录作为受信任请求头发送。它不会把工作目录交给模型填写;因此 Remote Hand 等每轮创建新 MCP 连接的宿主仍会绑定到同一个项目。
Claude Code 用户复制服务启动时输出的命令,一次性添加全局 MCP:
claude mcp add \
--transport http \
--scope user \
--header "Authorization: Bearer <本机生成的 token>" \
-- \
banana-memory \
http://127.0.0.1:3927/mcp之后在项目中启动 Claude Code 并照常工作。Skill 会在实质性任务开始时召回相关记忆,在可靠结论形成后记录精简的项目事实、偏好和结果。用 /mcp 查看连接状态;直接询问 Claude“查看 Banana Memory 状态”可以检查模型下载、队列和当前项目。
如需从源码运行和验证同一路径:
npm ci
npm run build
node dist/src/cli.js start可用 BANANA_MEMORY_HOME 将数据目录改为另一个绝对路径,也可用 --port 更改监听端口:
BANANA_MEMORY_HOME=/absolute/path node dist/src/cli.js start --port 4927工作方式与边界
HTTP MCP 优先使用已认证的本机 Codex helper 请求头或客户端提供的 roots/list 绑定当前项目。服务不会接受模型参数指定的工作区;无法取得可信文件根目录时,会退化到当前 MCP 会话独立的只读作用域,避免跨项目读取;record 和 feedback 返回 workspace_required,要求配置宿主工作区后重新连接,不再创建新的会话项目。Agent 可以在 record 中提供人类可读的 projectName,但它只改变管理页显示,不参与项目身份或读取授权。只读的 recall 和 inspect 不会创建项目;首次实际写入才会原子创建项目,旧的无事件、无记忆空项目也不会显示。
Git 仓库的子目录和 linked worktree 使用同一个主仓库根目录作为项目身份;宿主文件访问检查仍使用实际工作目录。管理页会标记未绑定工作区的会话项目。已有会话项目不会仅凭同名自动合并,可通过下面的本机迁移命令显式指定归属。
每条新记忆都有 project 或 global 作用域。默认是当前项目;只有证据明确说明它是用户通用偏好、跨项目规则或跨项目事实时,事实和偏好才可标为 global。任务经历以及由多个经历合并出的经验始终留在原项目。跨项目召回会同时考虑当前项目记忆和全局记忆,并优先排列当前项目内容;全局记忆仍保留它的原始项目、来源和候选状态。旧版本创建的记忆按项目内处理。
没有生命周期 hooks 时,记录由 Skill 驱动,而不是由宿主强制采集。通过 HTTP MCP record 写入的材料与插件 hook 事件同样可信:置信度足够、未截断且未过滤的抽取结果直接进入 active。Agent 用 role 说明材料来源——user 表示这是用户自己的明确陈述(偏好、指示、纠正或用户断定的事实),默认的 assistant 表示 agent 观察到的结果、已验证事实与任务经历;MCP 不能声明 system 或 tool 角色。仍标记 candidate 的是不确定的抽取、归纳总结以及尚未取得验证证据的经验。当前插件模式仍保留更强的自动事件采集和一次性维护凭据,作为 Claude Code 专用的增强接入。
记忆正文由模型改写:简洁、自包含、可复用,使用记录本身的语言,代码标识符、路径与 API 名称保持原样,逐字引用只作为溯源证据保存。每条记忆还带一个持久度:reusable 是未来其他任务仍会用到的稳定知识,task_detail 是只对这一次任务有意义的细节(某次提交、某次耗时、当天进度)。任务细节照样保存,但按经历的半衰期衰减、同分时排在可复用知识之后,并且不参与归纳总结与合并经验。
后台每 30 秒处理新材料,也会整理已有记忆。同一项目至少有 3 条相关原始事实、偏好或经历,且涵盖至少 2 个独立原始来源时,可以生成 summary(归纳总结)。每组最多 6 条,模型可拒绝归纳;输入不截断,每条成员都必须有可核对的原文引用。总结始终是项目内的候选,保留成员版本、各自适用条件和原始来源,不作为自我佐证再次归纳。原始记忆修改会使总结待复核;来源删除也会删除总结与对应任务。失败任务最多尝试 3 次,inspect 和管理页显示处理状态。
合并经验同样由模型归纳改写,而不是把成员正文拼接起来:一组经历被判定可合并后先写入一条待归纳的 experience,再由同一套后台任务队列调用模型生成正文与逐句引用;归纳完成前这条经验不会被召回。成员正文被改写或纠正后会重新归纳。
归纳与经验晋升相互独立:experience 的激活仍要求相同条件下至少 3 个独立任务、跨 2 个会话的可信成功证据,且没有反例。单纯积累候选或生成总结不会提高可信度。
配置云端生成模型(可选)
默认全部在本机推理:生成用 Qwen3-8B Q4_K_M,向量用 Qwen3-Embedding-0.6B。抽取与归纳可以改到任意 OpenAI 兼容的云端端点:
BANANA_MEMORY_GENERATION_URL=https://llm.example.com/v1 \
BANANA_MEMORY_GENERATION_KEY=sk-... \
BANANA_MEMORY_GENERATION_MODEL=openai/gpt-5.4-mini \
banana-memory start- 只有同时设置
BANANA_MEMORY_GENERATION_URL和BANANA_MEMORY_GENERATION_KEY才启用;BANANA_MEMORY_GENERATION_MODEL默认openai/gpt-5.4-mini。 - 启用后本机完全不再下载或启动 5 GB 的本地生成模型,只保留向量模型;向量与向量索引身份不变,始终本地。
- 离开本机的内容只有:待抽取的那一条记录正文,或待归纳的几条记忆正文与它们的适用条件。事件 ID、来源 ID、向量、数据库和文件路径不会外传。
- 密钥只作为请求头出现,不写日志、不写数据库、不进管理页;启动日志只有一行
{"component":"generation","phase":"remote","model":"...","host":"..."}。 - 每条记忆都会记下写它的模型(
modelVersion为remote:<模型名>),管理页的生成模型标注为“云端”。 - 请求固定
response_format: json_object与reasoning: {enabled:false}(网关拒绝该字段时自动重试一次不带它),60 秒超时,429/5xx/网络错误退避重试一次。返回的 JSON 一律在本机校验:引用必须逐字出现在原始记录中,否则整条候选丢弃。 - 本地 8B 现在使用同一套自由改写提示词。它的自由抽取质量明显弱于云端模型(见
docs/model-validation.md);把错误输出挡在外面的是本机校验,而不是模型自觉。
云端判断(可选)
默认关闭。关闭时的行为与过去完全一致:合并与冲突判断只使用本机的精确规则。
显式开启后,少量记忆正文会离开本机,交给 TypeSafe 的 Jev 模型(POST https://api.typesafe.ai/v1/systemone,仅文本、不生成内容)做判断:
BANANA_MEMORY_JUDGE=jev TYPESAFE_API_KEY=sk-... banana-memory start只有同时设置 BANANA_MEMORY_JUDGE=jev 和 TYPESAFE_API_KEY 才会启用;TYPESAFE_BASE_URL 可以改写接口地址(仅用于测试)。启用时启动日志会输出一行 {"component":"judge","phase":"enabled","name":"jev"}。
离开本机的内容只有:项目显示名、成对记忆的正文与适用条件、以及它们的创建日期(精确到天);判断项目身份时另加每个项目最多 4 条记忆正文,各截断到 200 字。原始事件、来源 ID、向量和数据库都不会外传。只有通过相似度预筛的同项目、同类型记忆对才会被询问,判断结果按 (记忆 ID, 版本) 在进程内缓存,不会重复计费。
它参与三个判断:
- 两条
episode是否是同一套可复用流程的不同实例(>= 0.75才合并成experience)。本机规则要求条件、版本、否定和数字完全一致,因此现实数据几乎从不合并。 - 两条
fact或preference是否互相矛盾(选项为contradiction、置信度>= 0.6且独立的冲突问题>= 0.6时,两条都转入review);若判断为newer_value_supersedes且后一条更晚(置信度>= 0.6),则只把较旧的一条标为superseded,recall的history模式仍可查到。 - 两个显示名不同的项目是否其实是同一个项目(
>= 0.8时在管理页列为合并建议)。只是建议,永不自动合并。
判断只能撤回或重组已有材料,永远不会因为模型认可而提升任何记忆的状态。任何失败——超时、网络错误、429/529、非 2xx 响应——都会退回本机规则(429/529 与网络错误会退避重试一次,单次请求 10 秒超时),写入路径不会因此报错。管理页在模型信息旁显示判断来源名称与调用、失败、输入 token 计数。
迁移已有会话项目
先停止使用该数据目录的实例并备份 memory.lance,根据原始会话的工作区确认映射,创建本机 JSON 文件:
[{ "projectIds": ["s:原项目ID", "s:另一个原项目ID"], "workspace": "/absolute/path/to/repository", "name": "项目名称" }]banana-memory migrate-projects /absolute/path/plan.json
banana-memory migrate-projects /absolute/path/plan.json --apply第一条命令只预览,第二条在独占锁下原子迁移。迁移保留事件和记忆 ID、状态、来源和历史;旧会话凭据失效,旧项目留下迁移映射记录。重复应用同一映射不再写入。迁移不会改变 MCP 的工作区授权,仍须配置 helper 或 MCP roots 才能避免后续会话继续产生碎片。
晋升策略变更前写入的候选记忆
2026-09-21 之前,通过 HTTP MCP 写入的材料被标记为不可信,抽取出的记忆只能停留在 candidate。一次性本机命令会按新策略回填已有数据:
banana-memory promote-mcp-candidates
banana-memory promote-mcp-candidates --apply第一条只预览并输出 JSON 报告,第二条在独占锁下写入,因此必须先停止使用该数据目录的服务;应用前会把 memory.lance 备份到 <数据目录>/backups/<时间戳>-promote-mcp/。命令把 trusted: false 的事件标记为可信,并把来源全部为事件的 fact、preference、episode 候选记忆置为 active,同时写入历史版本行;总结、经验、来源被截断或过滤的记忆以及已过期的记忆保持原状。
召回内容永远是带来源的历史材料,不是宿主指令。Skill 不应记录计划、猜测、普通进度、凭据、密钥或复制来的第三方指令。识别到的密钥字段会被过滤,但过滤无法识别所有敏感信息。
记忆抽取、聚合、向量生成和数据库均在本机。权重下载完成后,Banana Memory 可以离线处理;召回片段仍会交给 Claude Code,进入 Claude 的处理链路。
HTTP 服务使用持久随机 bearer token,并将其以仅当前用户可读的权限保存在 ~/.banana-memory/http-token。MCP 和管理页数据接口都需要该凭据,并且只接受 loopback Host 和可信的本地 Origin。管理页 HTML 本身不包含记忆或 token;/health 仅返回服务是否运行,不泄露项目或模型信息。
开发与验证
npm run typecheck
npm test
npm pack --dry-run
npx skills add . --list测试覆盖真实本地 LanceDB、HTTP MCP 握手与项目根绑定、Bearer 鉴权、Origin 拒绝、可控时钟、可重放模型输出和进程生命周期。独立的真实本地模型与 Claude 插件评测命令仍保留:
npm run probe:storage
npm run evaluate:replay
npm run evaluate:models
npm run evaluate:pipeline
npm run benchmark -- --models-dir .runtime/models主要实现:src/host/http.ts 提供前台 Streamable HTTP MCP;src/service.ts 是业务入口;src/store.ts 提供单表原子发布和物理清理;src/models/ 管理固定本地资源。旧的 Claude 插件、stdio MCP 与 hooks 位于 plugin/ 和 src/host/,继续用于增强模式验证。
