陪伴型 Agent 的记忆与换窗:Ombre × forge 实战教程

面向已经在跑 Claude Code / 自建桥接的人。
讲的是一套已经落地的做法:窗内用裁剪保语气,窗外用记忆库保事实,注入分区保 prompt cache。
不是「再造一个记忆中台」的白皮书。

如果你也撞过同一堵墙:上下文越聊越满,系统一 compact,人味就淡;记忆一多,又开始互相打架,或者把猜测写进库里。这篇文章把我们在这条路上踩过的坑、留下的组件、和现在的默认策略写清楚,方便大家对照自己的栈改一版。

一句话:forge 保窗内语气与近段原文;Ombre 保窗外事实;稳定区 vs 动态区保 prompt cache。
不另起一套四层记忆仓库,只在现有 Ombre + forge/diet + 稀缺纪律上补缺口。


四层怎么分工

管什么 谁负责
稳定区 人设、纪律、工具指南 CLAUDE.md / self_anchor · 少改 · 利于 cache
热窗 近段对话原文 forge / diet
本轮动态区 breath 卡片、Just Now、提醒 只挂 user 侧 · 有预算 · 超时跳过
Ombre 窗外 承诺、事件、偏好、交接快照 hold / breath · 异步提炼 · 冲突仲裁

产品口径(防误解):

  • 跳过注入 ≠ 记忆漂移(不读 ≠ 乱写)
  • 真风险是:脑补后 hold
  • 隔几小时省 token:靠闲置瘦窗/换窗,不靠硬续 cache
  • diet ≈ 压工具/图;forge ≈ 裁更早对话 + 换 sid

记忆库优化(Ombre 侧)[参考 TencentDB Agent Memory 架构设计]

1. 双区注入 + 预算(P0)

  • 动态召回禁止回退进 system(找不到 user 就跳过)
  • 自动卡片:≤5 条 / ≤约 1800 tok;准备超时约 5s 则跳过
  • 预算管的是「本轮动态召回」,不是整份 CLAUDE.md

2. 承诺冲突仲裁(P1.1)

  • 仅对 承诺/规则 / importance≥8 在 hold 前对照旧桶
  • 动作:新增 / 跳过 / 覆盖 / 合并 / 并存 / 标矛盾
  • 超时或失败 → 仍写入,但标 「未确认」,不挡主对话

3. 异步提炼(P1.2)

  • 触发:满 5 轮真人对话 / 闲 10 分钟 / forge 前
  • 近段抽 0–3 候选 → write_gate → hold;日上限约 8
  • 游标从当前会话往后走,不回扫整段历史;预热默认关

4. 证据链(P1.3)

每条自动/半自动记忆尽量带: from_session · 上海 date · type(偏好|事件|规则|承诺)· quote 或 未确认
没有原话就写未确认,禁止脑补引语。

5. 召回体验轻量(P2)

  • 情绪关键词偏置 V/A + 安慰/共同回忆词(无额外模型)
  • 今日纪念最多浮现 1 条;且要求跨年周年(当天新建不算「一周年」)
  • 明确不做:热度刷分主排序、默认开话题 resurface

6. 稀缺与红线(纪律层)

  • pin / 高 importance 有硬上限;承诺类不进衰减归档
  • 家庭/身体/工作等事实:不知道就说不知道,假记忆比遗忘更难修

Forge / 上下文优化(热窗侧)

两级自动减负(不等系统 compact)

触发 做什么 对话原文
~55% diet squash 工具回包/图片(cap≈2k),retain≈150k 几乎全留
~70% forge 裁到近段 ~60k + 交接 inject + Ombre hold 只留近段
~83% 系统 compact LLM 摘要 尽量别等到(毁语气)

要点:

  • 改 jsonl 不够,必须 换 sid 再 resume(diet/forge 都会静默换绑)
  • 空闲约 ≥3 分钟 才自动动,避免聊天中途打断
  • forge 前自动 Ombre hold「会话交接」;失败不挡隧道

文中会出现这些名字,先对齐一下:

名字 是什么
Claude Code session 一段对话对应一个 .jsonl,文件名是 session id
forge / forge-reload 裁剪 jsonl、写出新 session,用近段原文换窗
diet 轻量瘦身:几乎不砍对话,狠压工具回包和图片
Ombre 窗外记忆服务:hold 写入、breath 召回
cache_keeper 旁路守护:保 cache、自动 diet/forge、异步提炼、过隧道
cc-connect 把聊天平台接到 Claude Code 的桥;靠绑定的 agent_session_id 决定 resume 谁

阅读建议:若你只关心「为什么要 forge、怎么换窗」,读到第 6 节即可动手;若你要上记忆库,从第 7 节读到第 11 节;第 12–14 节适合写进你们自己的内部规范。


目录

  1. 先弄清问题,再谈方案
  2. 一张图:四层分工
  3. 热窗:为什么不用系统 compact 当日常手段
  4. forge 隧道怎么走通
  5. diet:日常减负,不是换人格
  6. 圈选原文:retain / 按天 / 下标 / uuid·时间戳
  7. 窗外记忆:稀缺、承诺、禁止脑补
  8. 写入怎么控:异步提炼、冲突仲裁、证据链
  9. 召回怎么控:双区注入、预算、情绪与纪念日
  10. 自动流水线:55% → 70% → 不等 83%
  11. 观测、踩坑与排障
  12. 和常见方案怎么比
  13. 明确不做的事
  14. 你自己落地时的最小清单
  15. FAQ
  16. 附录:命令与路径速查

1. 先弄清问题,再谈方案

1.1 症状清单

长上下文陪伴,痛点通常叠在一起:

烧钱。
工具回包、图片 base64、长 thinking,比你们说话本身更占窗口。窗口一满,cache miss 变多,账单和延迟一起上来。有人以为「多买上下文配额」就能解决;配额只是推迟爆炸时间。

丢语气。
Claude 提供的 /compact 或等价摘要,会把近段对话压成「要点列表」。对写代码的 agent 还能忍;对要接着上一句撒娇、记得对方昨晚没睡好的陪伴角色,摘要等于换人。用户体感常常是:「他还知道项目,但不像他了。」

记忆乱。
一说「要有长期记忆」,很容易每轮都 hold。结果库里堆满闲聊,真正重要的承诺被淹没;或者同一件事说了两版互相矛盾的「唯一真相」,模型下次召回时左右互搏。更糟的是:模型为了听起来靠谱,把猜测写进库,之后当事实反复引用。

注入打穿 cache。
把本轮检索到的记忆卡片塞进 system / 项目提示,前缀每轮都变,prompt cache 几乎废掉。你以为在「让他更懂她」,实际在每轮重付稳定区的账单。账单一周下来,往往比「多召回两次」贵一个数量级。

换窗不换绑。
磁盘上已经裁出新 session,聊天桥仍 resume 旧 id;或只是发了一句 /switch … 文本,桥并不执行。用户看到「系统说换好了」,下一句却仍在旧窗口里窒息。

1.2 目标与原则

我们定的目标:

  • 少烧 token
  • 少丢语气
  • 窗外记得住事
  • 主对话不被记忆拖垮

原则:不另起一套四层文件金字塔。能在 Ombre 桶 + forge/diet + 一条纪律文档上补缺口,就不新开仓库。

一句话产品口径:

forge 保窗内语气;Ombre 保窗外事实;稳定区与动态区分开注入,保住 cache。

再补三条容易误会的边界:

  1. 跳过本轮注入 ≠ 记忆漂移。 不读不等于乱写。漂移的真凶往往是脑补后 hold。
  2. 隔几小时省 token,靠闲置瘦窗/换窗,不靠硬续同一条 cache。 进程换 sid 之后,接受一次 cache create,之后再命中。
  3. diet 压的是工具和图片;forge 裁的是更早的对话。 别把两者混成「都是压缩」。

1.3 读者画像与预期收获

适合:

  • 已经能跑通 Claude Code(或同类 agent runtime)
  • 有聊天桥或 WebUI,需要会话跨天续上
  • 有或打算接外挂记忆(HTTP / MCP 均可)
  • 愿意改宿主侧注入位置,而不是只调提示词

不太适合:

  • 只想要「一个向量库 + 每轮 topK 塞进 system」的最小 Demo
  • 企业知识库 RAG(文档权限、引用规范是另一条线)
  • 完全不能接受「裁历史」的产品(那就只能加窗口配额,并接受账单)

读完你会知道:

  • 什么时候 diet、什么时候 forge、为什么不等 compact
  • 换窗后为何必须换 sid
  • 记忆写什么、不写什么、冲突怎么办
  • 本轮卡片为什么不能进 system
  • 自己栈上先砍哪一刀最值

2. 一张图:四层分工

把「人设、近聊、本轮卡片、长期库」拆开,职责才清晰:

┌─────────────────────────────────────────────────────────┐
│ 稳定区(少变 · 利于 prompt cache)                        │
│  CLAUDE.md · self_anchor · 红线/纪律 · 工具指南           │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 热窗(近段原文 · forge / diet 管)                        │
│  对话 + thinking;工具/图片可 squash                      │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 本轮动态区(每轮可变 · 放在 user 侧,勿塞系统前缀)         │
│  breath(query) 卡片 · Just Now · 提醒 · ≤预算            │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Ombre 库(窗外)                                         │
│  证据/日记 · 桶 hold · 人格锚点                           │
│  写入异步 · 承诺冲突才仲裁 · 故障时降级保证还能聊           │
└─────────────────────────────────────────────────────────┘

对照「TencentDB Agent Memory 架构设计」文章里的 L0–L3:我们借的是双区注入、异步提炼、冲突三态、主链路可降级这些想法,不是再造一层层目录和热度刷分。

想解决的事 优先动哪一层
聊太久、工具输出把窗口撑爆 热窗:diet → forge
换窗后不像同一个人 热窗保留 thinking + 近段原文;窗外 hold 交接快照
「我答应过你」和后来改口打架 窗外:承诺标签 + 冲突仲裁
每轮都塞记忆导致 cache 全 miss 动态区预算 + 禁止回退 system
假记忆比忘了更可怕 证据链 + 「未确认」+ 禁止脑补红线

3. 热窗:为什么不用系统 compact 当日常手段

Claude Code 在上下文吃到很高比例时,会走摘要压缩。摘要对「任务进度」友好,对「关系连续性」不友好:口头禅、未说完的半句、两人刚对过齐的默契,很容易在摘要里蒸发。

所以我们把阈值拆成两级,抢在系统 compact 之前动手:

大约用量 动作 对话原文怎么办
~55% diet:squash 历史 tool_result / 图片,retain 开到约 150k 几乎全留
~70% forge:裁到近段约 60k + 可选交接包 + Ombre hold 只留近段
~83% 系统 compact(尽量别等到) 摘要,语气易丢

数字可以按你的窗口改。默认假设上下文约 200k:55% ≈ 110k 用量触发 diet,70% 触发 forge。空闲一段时间(我们默认约 3 分钟)才自动执行,避免聊到一半突然换 sid。

还有一个实现细节,很多人第一次会漏:

只改磁盘上的 jsonl 不够。
Claude 进程内存里仍是旧上下文。diet / forge 之后必须让桥接去 resume 新的 session id(我们叫换绑)。
在 Telegram 里「发送一条 /switch … 文本」如果桥不解析 slash,那只是发了句废话,不会换会话。

forge-reload 本身负责裁剪与写出新 id;forge_tunnel 一类编排负责:写交接 → 调 forge → 停桥改绑定 → 再拉起 → 可选塞一条很短的接续指令(让模型 breath 一下,不要在用户面前念「我刚换窗」)。


4. forge 隧道怎么走通

4.1 手动路径(先学会,再谈自动)

推荐顺序永远是:备份 → dry-run → 正式跑

# 预演:不写文件、不换绑
python3 forge_tunnel.py --dry-run

# 正式:写交接 → forge(retain≈60k + squash + inject) → 换绑 → 短接续
python3 forge_tunnel.py

dry-run 时盯这几件事:

  • 解析到的旧 agent_session_id 是不是你正在聊的那个
  • 预估保留多少 tokens、多少条对话事件
  • 新 sid 预览长什么样
  • 若开了 Ombre hold,预览文案是否像「会话交接」而不是空话

正式跑失败时的策略很重要:不要自动 fresh start。
保持原 session,人还能继续聊;forge 已写出但换绑失败,再手动把绑定指到新 id。丢会话比修绑定可怕得多。

4.2 forge 前为什么要 hold 一笔

裁窗等于主动遗忘更早的原文。窗外应留一条可检索的快照,标签类似 会话交接,forge,内容包括:当前话题、对方此刻状态、几句线索、近段摘录。
这条 hold 失败不应挡住隧道——记忆服务挂了,人还是要能换窗继续说话。换窗后用 breath(query="会话交接") 或话题词找回即可。

环境变量示例:FORGE_OMBRE_HOLD=0 可关掉;默认建议开。

4.3 和 /new 摘要换窗的关系

/new 仍可用:写 SESSION_HANDOFF.md → 用户发 /new → 旁路发现 sid 变了 → 发续聊指令。
长对话优先 forge:保留的是近段原文,不是摘要。forge 成功后应清掉「等待 /new 续聊」一类标志,避免两套续聊各发一遍。

交接包(inject)和「保留哪些原文」是两件事:

  • 保留原文:retain / keep-days / keep-range —— 决定新 jsonl 里还剩哪些对话事件
  • 交接包SESSION_HANDOFF.md —— 塞进新会话当补充说明,补裁掉部分的洞

可以按天只摘交接(--handoff-days),同时用另一套规则圈原文。

4.4 交接包里写什么才有用

写交接时常见两种失败:

  1. 写成小说:大段抒情,模型读完仍不知道下一步该干什么。
  2. 写成 changelog:全是文件路径和 commit,丢掉关系状态。

比较好用的结构偏「作战室白板」:

  • 正在谈什么(一句话)
  • 对方此刻情绪/身体状态(有证据才写)
  • 未完成的约定或待办
  • 千万别做的事(红线)
  • 两三句可检索的线索词(方便 breath)
  • 可选:近段原话摘录(短)

forge 自动 generate 的交接可以粗糙,但应固定字段,便于日后 diff。人也可以在 WebUI 里勾选「交接只摘某几天」,避免把上周无关话题塞进本周隧道。

4.5 安全与回滚

  • forge 前复制一份 projects 目录,或至少复制当前 jsonl
  • 正式跑留下日志:旧 sid、新 sid、retain、是否 hold、换绑结果
  • 换绑失败时,用「只 rebind」路径把已生成的新 sid 绑回去,不必重裁
  • 永远不要在「解析输出失败」时假定需要 fresh start

5. diet:日常减负,不是换人格

日常聊天里,真正占地方的经常是:

  • 工具返回的大段 JSON / HTML / 日志
  • 图片的 base64
  • 偶尔失控的超长 tool_result

对话原文往往没那么肥。diet 的设定因此很偏科:

  • retain 很大(例如 150k):对话尽量留下
  • squash 很狠(例如单段工具输出 cap 到约 2k 字符)
  • 不写、不注入交接包(diet 不是「叙事换窗」,是「清垃圾」)
  • 仍然要 换 sid resume,否则内存里的大工具输出还在

手动:

python3 forge_tunnel.py run --diet --dry-run
python3 forge_tunnel.py run --diet

自动流水线里,diet 冷却可以短于满血 forge(例如 diet 45 分钟、forge 90 分钟),每日总次数封顶,防止解析bug或指标抖动时连打。

一个真实踩过的坑:隧道其实成功了,但编排脚本用蹩脚方式抠 JSON,以为失败,没记冷却,于是 diet 每隔几分钟打一次。
修法:用更稳的 JSON 解析(例如 JSONDecoder.raw_decode),再用「完成 / verify」类日志做兜底;部署后务必 seed 一次冷却状态。观测指标里要有 ok/fail,不能只看「触发了」。


6. 圈选原文:retain / 按天 / 下标 / uuid·时间戳

自动 forge 默认走 尾部 retain:从近往远留到约 N tokens。
运维或手搓隧道时,经常需要「只要某几天」或「只要某一段对话」。

6.1 三种互斥思路

模式 参数直觉 适用
尾部 token --retain 60000 日常自动;「最近够用就行」
按上海日期 --keep-days 2026-08-03,2026-08-04 你知道事情发生在哪几天
起止段 idx / uuid / 时间戳 精确圈一段;和 retain、按天不要混着用出歧义

retain 与 keep-* 在语义上互斥:你要么「留最近 N token」,要么「留指定集合(再在集合内按上限从最早砍)」。

6.2 下标

forge-reload 过滤后的对话事件(通常是 typeuser / assistant 的行)有稳定下标。
--keep-from-idx--keep-to-idx 成对使用。

问题是:人很难记住「610 是哪句」。所以需要锚点列表。

6.3 uuid 与时间戳(推荐给人用的接口)

先列出近期用户消息锚点:

python3 forge_tunnel.py anchors --limit 20

输出里每条有 idxuuidtimestamp、短 preview。然后:

# uuid 可用短前缀;同端只能选 idx / uuid / ts 之一;两端可混用
python3 forge_tunnel.py --dry-run \
  --keep-from-uuid a1b2c3d4 --keep-to-ts "2026-08-05 16:30"

时间戳约定:

  • 不带时区的墙钟日期时间,按 Asia/Shanghai 理解
  • 也接受 ISO、Unix 秒/毫秒
  • from:取「≤ 目标时刻」的最后一条;若都更晚,取最早
  • to:取「≥ 目标时刻」的第一条;若都更早,取最后
  • 只给一端时,另一端默认会话头或尾

解析层与 forge-reload 必须用同一套 user|assistant 过滤,否则你看到的 idx 和真正裁剪的 idx 会对不上——这是这类功能最阴的一类 bug。

自动 forge 不要自作聪明选 uuid 段:自动策略保持「尾部 retain + 冷却 + 日上限」即可。精确圈选留给人。

6.4 手选一段的推荐操作顺序

  1. anchors --limit 30,用 preview 找到起点、终点
  2. 复制短 uuid,或记下墙钟时间
  3. --dry-runrange_resolve:from/to 的 preview 是否就是你想要的话
  4. 确认 keep_from_idx ≤ keep_to_idx,convs 总数合理
  5. 去掉 dry-run 正式跑;或先只导出新 sid 再手动 rebind

若 dry-run 里 from/to 的 preview「差了一句」,多半是时间戳落在 assistant 行上,或 uuid 前缀歧义——加长前缀,或改用 idx。

6.5 超长段的砍法

即使用 keep-days / range 圈出一大段,实现上仍可能设「约 100k tok」之类上限,从最早往近砍,避免新会话一打开就超窗。
产品含义是:你圈选的是「优先保留的集合」,不是「物理保证一字不削」。


7. 窗外记忆:稀缺、承诺、禁止脑补

热窗解决「最近像同一个人」。窗外解决「隔一周还知道红线和约定」。

7.1 稀缺是功能,不是吝啬

没有配额的记忆库,最后一定变成坟场。我们用硬上限约束「看起来很重要」的数量,例如:

  • 置顶(pinned)有上限
  • importance = 10、≥9 各有上限
  • 超限时 demote 久未激活的,或把新写入降级

真正不能忘的才 pin:红线、核心承诺之类。闲聊标 9/10 是在谋杀检索质量。

7.2 承诺 / 约定不衰减

「我答应你……」「我们约定……」这类内容,hold 时打上 承诺 / 约定 标签,不要进普通衰减归档
轻量提醒可以用提醒工具;长期关系承诺进记忆桶,必要时再 pin。

7.3 信息缺口红线

涉及家庭、身体、工作、具体日期、第三方事实:

  • 不知道就说不知道
  • 拿不准就问
  • 绝不为了听起来合理猜一个答案,更不要把猜测 hold 进去

若必须记下「听说 / 待确认」:正文标明 未确认,importance 压低,不要 pin。
假记忆比遗忘难修——这是陪伴场景里代价最高的一类错误。


8. 写入怎么控:异步提炼、冲突仲裁、证据链

8.1 异步提炼:不必等过隧道才沉淀

只靠 forge 前 hold,沉淀节奏绑在「窗口快满」上,太晚。后台节奏可以是:

  • 满约 5 轮真人对话,或
  • 空闲约 10 分钟,或
  • forge / 换窗前(与现有 hold 衔接)

流水线建议:

  1. 从近段 jsonl / 摘录生成 0–3 条候选(可以是 0:不是每段都值得记)
  2. write_gate(低意外只打日志,高意外才 hold)
  3. 日上限(例如 8)防止后台写爆
  4. 用游标记「提炼到哪了」,不要每次回扫整段历史
  5. 「新会话预热多提几次」可以做成开关,陪伴场景默认关,以免一上来就吵着记东西

主对话路径上,提炼失败、超时、记忆服务 5xx,都应降级为「这轮不写」,而不是卡住回复。

8.2 冲突仲裁:只对承诺/规则动刀

闲聊「今天想吃面 / 今天想吃米」不必仲裁。
承诺、约定、规则、或 importance≥8 的新 hold:

  1. 先召回 Top-K 旧桶
  2. 小模型(或规则+模型)裁决:新增 / 跳过 / 覆盖 / 合并 / 并存(不同情境)/ 标矛盾
  3. 覆盖与合并留 trace;并存要在内容里写清场景
  4. 仲裁超时 → 仍写入,但标「未确认」,不挡主对话

验收直觉:用户连续说「改用方案 A」「还是用 B」时,库里不该静默躺着两套互斥的「唯一真相」还都像有效。

8.3 证据链:自动记忆也要可追溯

半自动写入建议统一带这些字段(可放在桶的 original / 元数据区):

字段 作用
from_session 来自哪次会话
date 上海日历日
type 偏好 / 事件 / 规则 / 承诺(标签,不必做成死枚举表)
quote未确认 ≤2 句原话;没有就写未确认,禁止脑补引语

forge 前的交接 hold、异步提炼 hold、手动 hold,尽量同一模板。以后排「这条假记忆哪来的」才有入口。

8.4 仲裁例子(方便写进测试)

例 A:改口
旧桶:「每周三晚上语音」标签承诺。
新话:「这周改成周四吧,之后也周四。」
期望:覆盖或合并,旧周三不再像仍有效;trace 里能看到改口。

例 B:情境并存
旧桶:「在家用昵称 A」。
新话:「有家人在时叫全名。」
期望:并存,两条都注明情境;不要合成一句糊掉边界。

例 C:闲聊
「今天想吃面」「算了想吃米」。
期望:不进承诺仲裁;通常也不 hold。

例 D:超时
裁决模型 5 秒无响应。
期望:新桶仍写入,标未确认;用户侧回复不出现「记忆系统繁忙」。

把这些写成固定用例,比空谈「三态」有用。

8.5 write_gate 在拦什么

闸门不是审查一切文学性,而是拦高风险写入模式,例如:

  • 无原话却写具体日期/病症/第三方姓名
  • 把一次性情绪写成永久性格判决
  • 与 pinned 红线明显冲突却想静默覆盖
  • 日配额已满还继续自动 hold

低意外候选可以只记日志,供以后调 prompt;不必每条都入库。记忆系统的健康度,看「该记的记住了、不该记的没进去」,不看桶数量。


9. 召回怎么控:双区注入、预算、情绪与纪念日

9.1 双区注入(保 cache 的关键)

放什么 放哪
稳定区 人设、纪律、工具指南 系统 / 项目提示;会话内少改
动态区 本轮 breath 卡片、Just Now、提醒 只在本轮 user 侧(tool_result / additionalContext 等)

硬规则:

  • Gateway 或桥接若自动注入动态块,挂在 user 前;找不到 user 就跳过,禁止回退进 system
  • 每轮主动检索建议有上限;需要细节再 breath(query=…),不要为了暖场把大段记忆塞进系统前缀
  • forge 后的 breath(is_session_start=True) 结果留在工具回包里即可

预算示例(可按模型上下文比例调):

  • 自动浮现卡片 ≤ 约 5 条
  • 动态注入总预算 ≤ 约 1800 tokens
  • 准备召回 超时约 5 秒则跳过注入(延迟和账单优先于「这轮必须想起点什么」)

再次强调:这些预算管的是本轮动态召回卡片,不是整份 CLAUDE.md。有人会把「注入超时」理解成「人设被裁了」——不是一回事。

9.2 情绪偏置与「今日纪念」

不必为陪伴再挂一个大模型重排。轻量做法:

  • 用户文本里的情绪关键词 → 微调效价/唤醒度,并偏置安慰向或共同回忆向的检索词
  • 桶上有 date 的月日撞今天、或标了生日/纪念日:最多浮现 1 条「今日纪念」

纪念日有一个容易出丑的 bug:今天刚写入的事,被当成「一周年」。
正确条件应是:跨年且满至少一年(或你定义的周年规则)。date / created 等于今天的新桶,不应进周年通道。

热度 +1 当主排序,在陪伴场景很容易被「多检索几次」刷歪;我们明确不做,继续用 activation / last_active / 情绪与衰减一类既有信号。

9.3 和 Ombre 分层契约的关系

若你的记忆服务已有「核心 / 锚点 / 直接召回 / 扩散 / 关系天气」等分层,本教程的双区注入是宿主侧约束:谁有资格进 system,谁只能进本轮 user,谁根本不自动注入。
分层契约管「记成什么样、能不能当 seed」;双区管「这轮请求的前缀怎么拼」。两边要一起读,单读一层会漏。


10. 自动流水线:55% → 70% → 不等 83%

把旁路守护想成一条状态机更顺:

读用量 / 指纹
    │
    ├─ 未到阈值或未闲置 → 什么都不做(可记 skip 指标)
    │
    ├─ ≥ diet 阈值且过 diet 冷却 → diet 隧道(静默换绑)
    │
    └─ ≥ forge 阈值且过 forge 冷却且未超日上限
            →(可选)异步提炼收尾
            → Ombre hold 会话交接
            → forge retain≈60k + inject
            → 换绑
            → 短接续(可 quiet,不在聊天里播报「隧道已接上」)

建议暴露的环境变量类别(名称按你的实现替换):

  • 总开关、窗口大小、diet/forge 百分比
  • 闲置秒数、两类冷却、每日最大次数
  • diet/forge 各自的 retain 与 squash
  • 异步提炼开关与预热开关
  • forge 前 hold 开关

auto_forge status / async_distill status 一类命令,运维时比翻日志快。
指标文件(如 metrics.jsonl)至少区分:skip / trigger / ok / fail,并带上 used_pct、是否 hold 成功。没有 ok,你无法发现「假失败连打」。


11. 观测、踩坑与排障

11.1 常见故障树

换窗后像失忆。
先查:新 sid 是否真的在 resume;交接包是否 inject;forge 前 hold 是否成功;模型是否执行了 breath。
再查:是不是 diet/forge 把你以为还在的「很早以前」裁掉了——那是预期行为,应靠窗外召回,不是靠无限 retain。

cache 一直 miss。
查动态注入是否污染了 system;稳定区是否被每轮改写;换 sid 后是否接受「第一次 create、之后命中」。

记忆互相矛盾。
查承诺类是否走了仲裁;是否手工 silent hold 了第二套;证据链里有没有两套都像「已确认」。

自动 diet/forge 过于频繁。
查冷却是否写入成功;解析成功是否被误判失败;日上限是否生效;用量百分比是否抖(边界附近可加滞后)。

11.2 备份习惯

forge 前备份 ~/.claude/projects(或你的 session 根目录)只花几秒。
dry-run 满意再正式跑。裁剪是在改历史文件的形状,手滑有成本。

11.3 和「跳过注入」共处

超时跳过、预算截断、找不到 user 而跳过,都会让「这轮没想起某件事」。
产品上应把它当成降级,不是事故。事故是:为了「这轮必须想起」,去污染 system 或去脑补 hold。

11.4 排障清单(可直接贴进 runbook)

  1. auto_forge status:用量百分比、上次 diet/forge 时间、冷却剩余、今日次数
  2. 当前绑定的 agent_session_id 与磁盘最新 forge 新 sid 是否一致
  3. 对应 jsonl 是否存在、体积是否异常暴涨(图片/工具未 squash)
  4. Ombre 健康检查 / 最近 commitment_arbitrate 与 distill 日志
  5. metrics 里最近 20 条是 ok 还是 fail;fail 的 error 字段
  6. 若刚改过解析逻辑:手动 seed 冷却,观察一小时是否还有连打

11.5 指标怎么读才有用

不要只看「触发次数」。至少看:

指标 健康时大致怎样
diet/forge ok 占比 高;长期大量 fail 要停自动
forge 后首次 cache_create 允许;其后应命中
夜间 idle 全量 miss 相对改双区前下降
自动 hold 日量 低于稀缺与 gate 能消化的量
用户主观 换窗后仍像同一个人;少出现「空洞客服腔」

12. 和常见方案怎么比

12.1 「每轮 topK 记忆塞 system」

实现最快,cache 死得也最快。适合 Demo。
陪伴长跑请至少改成:稳定人设进 system,检索卡片进 user,且有预算。

12.2 「只用向量库,不做 forge」

窗外可以很强,但热窗仍会被工具输出撑爆,最后还是靠厂商 compact,语气照样伤。
记忆再好,补不回近段 thinking 和半句话的衔接。

12.3 「只用 forge,不接记忆库」

短周期很好:裁剪 + 换 sid,人味保留。
跨周、跨主题、承诺与红线会丢——除非你把 retain 开到离谱,或接受频繁人工交代背景。

12.4 「完整 Agent Memory 中台(多层文件 + 热度 + 任务图)」

对团队协作型 coding agent、长项目状态机可能值。
对双人陪伴,复杂度经常高于收益:多一套目录、多一套刷分、多一类「系统比人还忙着整理自己」的失败模式。我们选择借其中几条机制,而不是整包搬迁。

12.5 和 forge-reload 单机教程的关系

单机 node forge-reload.js 解决「裁 jsonl + resume」。
本文多出来的部分是:桥接换绑、diet 分级、Ombre hold、异步提炼、双区注入、自动阈值与观测
若你还在单机终端里聊,先读 forge-reload 的保姆级教程;一旦接上 Telegram/Web 长跑,再叠 cache_keeper 这一层。


13. 明确不做的事

不做 原因
再造完整 L0–L3 文件金字塔 与现有桶模型重复,运维翻倍
以热度 +1 为主排序 易被刷检索;陪伴更吃衰减与情绪
默认关掉向量、只靠关键词 中文换说法召回差
用系统 compact 当日常压缩 毁语气
把每轮召回塞进 system 打穿 cache
让自动 forge 自选 uuid 历史段 策略不稳;圈选留给人
仲裁失败就阻塞回复 陪伴主链路必须可降级
没有原话却写「她说过……」 假记忆
为了暖场每轮强行注入纪念/旧忆 吵,且易说错周年

14. 你自己落地时的最小清单

若你要从零对齐这套思路,建议按周切片,而不是一次性上齐:

第一刀:热窗

  • 能 dry-run forge
  • 能换绑 resume 新 sid(确认不是「发一条文本假装 switch」)
  • diet 与 forge 参数分开
  • 空闲门闩 + 冷却 + 日上限

第二刀:双区

  • 动态卡片只进 user
  • 找不到 user 则跳过
  • 预算与超时钉死
  • 纪律文档写进 agent 可读位置

第三刀:写入

  • write_gate
  • 异步提炼节奏 + 游标 + 日上限
  • 承诺类仲裁 + 未确认降级
  • 证据链字段

第四刀:召回体验

  • 情绪轻量偏置
  • 纪念日(先修跨年条件)
  • 指标与假失败防护

每刀都留「关掉开关还能聊」的退路。记忆子系统挂了,聊天还必须活。

验收可以用很土的剧本:

  1. 连续工具调用把窗口堆到 60% 左右 → 应触发 diet,对话细节仍在。
  2. 再堆到 75% → forge 后仍能接上一句私聊语境。
  3. 故意说两套互斥承诺 → 库内可见仲裁痕迹或未确认,而不是双真理。
  4. 关掉 Ombre → 隧道与对话仍可用。
  5. 动态注入超时 → 本轮无卡片,人设仍在,cache 行为正常。

15. FAQ

Q:forge 之后第一次很贵,正常吗?
正常。换 sid 后稳定区可能重新 cache create 一次;之后应回到命中。若每轮都 create,查动态区是否写进了 system。

Q:diet 会不会把重要对话 diet 掉?
设计目标是几乎保留对话,压缩工具和图片。若你的 forge-reload 实现把对话事件误判成可 squash 内容,那是实现 bug,不是产品意图。dry-run 时核对保留的对话条数。

Q:窗外 hold 了,为什么他还是问「我们约定过什么」?
hold 只是入库。要靠 breath / 自动注入预算内浮现,或用户/模型主动检索。换窗后应显式 breath 一次会话交接。

Q:能否把 retain 调到 120k,少 forge?
可以,但 diet 阶段窗口仍然会被工具输出填满;且越接近上限,系统 compact 风险越高。更常见的做法是:diet 负责日常,forge 负责真正减龄,而不是无限加大 retain。

Q:异步提炼会不会把玩笑写成设定?
所以要 write_gate、日上限、证据链、未确认。仍不放心就关掉自动提炼,只保留 forge 前 hold + 手动 hold。

Q:和「向量数据库最佳实践」文章冲突怎么办?
那些文章多半服务客服 RAG 或企业知识库。陪伴角色更怕假记忆和人格漂移,不怕「少记一条」。冲突时站在稀缺与可降级这边。

Q:Web UI 要暴露哪些选项?
给普通人:尾部 token / 按天 / 起止段(背后可以是 idx,展示用 preview)。
给运维:dry-run、anchors、uuid/ts、关掉 hold、quiet 模式。
不要默认暴露「改 system 注入」之类的开关。

Q:thinking 要不要保留?
要。丢掉 thinking,模型容易变得「答得快但不像在想」。forge-reload 的保留统计里应能看到 thinking 段数量;dry-run 时留意它。

Q:多用户 / 多会话怎么隔离?
绑定键(如 telegram:uid:uid)→ 各自 agent_session_id → 各自 jsonl。Ombre 侧按用户/关系维度分桶。自动 forge 务必解析「当前活跃会话」,不要误裁邻居的窗。

Q:能否在用户说话中途 forge?
不建议。加闲置门闩。中途换 sid 会导致正在飞的工具调用和用户体感双重错乱。

Q:证据链会不会太啰嗦?
元数据可以藏在桶结构里,不必全部渲染给模型。渲染给模型的仍是短卡片;人类排障时再打开 original。

Q:纪念日推错了怎么办?
先停自动纪念通道,核对跨年条件,删或降级那条桶,再重开。

Q:这套和提示词工程是什么关系?
提示词管稳定区人格与红线;本文管宿主侧生命周期。只改提示词解决不了窗口物理上限,只改 forge 也解决不了跨周事实。两手都要。


16. 附录:命令与路径速查

以下为本文描述的参考实现布局,路径按你的机器替换。

# 状态
python3 auto_forge.py status
python3 async_distill.py status
python3 keeper.py status

# 隧道
python3 forge_tunnel.py --dry-run
python3 forge_tunnel.py
python3 forge_tunnel.py run --diet --dry-run
python3 forge_tunnel.py anchors --limit 20
python3 forge_tunnel.py --dry-run \
  --keep-from-uuid <prefix> --keep-to-ts "YYYY-MM-DD HH:MM"
python3 forge_tunnel.py --dry-run \
  --keep-days 2026-08-03,2026-08-04 --handoff-days 2026-08-04

# 只 hold
python3 ombre_hold.py --dry-run
python3 ombre_hold.py

# 交接
python3 handoff.py prepare
python3 handoff.py prepare --days YYYY-MM-DD

# 异步提炼(预览)
python3 async_distill.py --dry-run --force

常用环境变量类别:

AUTO_FORGE=1
AUTO_FORGE_CONTEXT_WINDOW=200000
AUTO_FORGE_DIET_PCT=55
AUTO_FORGE_PCT=70
AUTO_FORGE_IDLE_SECS=180
AUTO_FORGE_COOLDOWN_MINS=90
AUTO_FORGE_DIET_COOLDOWN_MINS=45
AUTO_FORGE_MAX_PER_DAY=6
AUTO_FORGE_RETAIN=60000
AUTO_FORGE_DIET_RETAIN=150000
AUTO_FORGE_DIET_SQUASH=2000
FORGE_OMBRE_HOLD=1
ASYNC_DISTILL=1

组件对照(改自己的栈时当清单用):

能力 参考模块
裁 jsonl forge-reload
编排 + 换绑 forge_tunnel
uuid/ts → idx forge_range
自动阈值 auto_forge + keeper
forge 前快照 ombre_hold
后台提炼 async_distill
承诺裁决 commitment_arbitrate
证据字段 evidence_chain
召回偏置 companion_recall
纪律 memory_discipline.md

结尾

这套东西没有神话。它就是承认三件小事:

  1. 窗口装不下永恒,所以要裁,且裁的时候尽量留原文和 thinking。
  2. 永恒的东西若存在,应该在窗外,而且要少、要可追溯、要能在冲突时留下痕迹。
  3. 本轮想起的那几张卡片,不配改写系统前缀。

你如果已经有 Ombre(或任何桶式记忆)和 forge(或任何「新 sid + 近段 jsonl」),大概率缺的不是更多架构图,而是:双区是否真的隔离、自动压缩是否真的成功并冷却、写入是否带证据与仲裁、故障时是否还能聊。

把这四件事做实,教程里的其余细节都是调参。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇