Skip to content

Releases

v5.6.0 — 记忆网络

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 11:05

本页会随勘误就地更新。规范全文以 main 为准。

范围

这一版只回答一个问题:每条记忆,说不说得清是谁记的、从哪来、还能信多久。

现状三处说不清:同一件事被三个模型各记一遍,看起来像三件;命中只给片段,不知道是电脑会话还是网页转述;
摘要覆盖式更新,事后问「当时为什么这么定」答不上来。设计四件:

一、分身 ID:avatar = <agent>-<model>[-<version>],三段全同才算同一个分身。
不取专名只留简称——模型的出厂身份已经有了,别叠加;缺哪段写 unknown
(dsh-unknown 比孤零零的 dsh 诚实),UUID 一律不当分身名。

二、三层溯源,一路回到原文,中间不许"再总结一次":

命中片段(带 avatar / source / 时间)
   → 分索引行:这条属于哪个文件(index\L1.jsonl)
   → 原文:file:line 逐字可读

三、检索只读客观:检索回的是「事实 + 来源 + 时间」,不返回性格、不写性格——
检索改的是"知道什么",改不了"是谁"。

四、性格 = 底座原生,只记录漂移(AVATAR_SPEC §8.1):不建性格面,人格直接用模型出厂神格;
差异不判跑偏、不压制、不写回。漂移只由本分身自己的真实记忆产生(peer: 与检索片段不参与),
由 persona_observe.py 按 persona_key 每天留一行表达快照(句长 / 问句率 / 决定词密度),
diff 只并排给人看,不判正常/跑偏——底座天生带性格,没有零点,也就没有「漂移」(§8.3)。
一句话:允许不同,不许互相污染;要「塑造」人格属于改身份,得人点头。

落地

文件 做了什么
persona_avatar.py avatar 唯一入口:认两种头方言(设备/底座 与 来源/模型)、缺段写 unknown、UUID 不当名
persona_index_l1.py L1.jsonl 每行加 agent/model/avatar;L1.md 加分身列
persona_search.py 结果带分身列;--avatar 过滤;--show-source(文件:行号);--context N(原文前后 N 行)
persona_master.py 总索引加「分身分布」
impl/launcher/avatar_poc.py 补 TC-AV7 性格隔离用例:7/7 → 8/8

证据

① 三层溯源:10/10 条命中都能在 3 步内读到原文行
② 可重建 :删 index/L1.jsonl → 重建 146 → 146 行
③ 性格隔离:PoC 8/8(AV7:A 变 B 不变 / 检索命中 0 / peer 不入漂移)

分身分布(实测):网页-deepseek-web 92 · dsh-unknown 32 · hermes-deepseek-v4-flash 7 ·
hermes-qwen3-* 12 · unknown 3。

顺手修掉两个真缺陷:--device/--model 对电脑会话一直是空的(导出写「来源/模型」,检索只认「设备/底座」);
分身过滤发生在「取前 60 名之后」,导致 206 条 dsh 命中被误报 0 条——改成先过滤再取候选。

边界

  • 历史文件没有元信息头 → unknown 就写 unknown,不按内容猜模型(猜出来的来源比没有更坏);
  • 不做跨设备实时同步:同步是文件层的事,索引只负责"读得对";
  • 不为整洁删重复——重复本身是证据(同一件事被两个模型独立记下,说明它重要);
  • 性格面不建(2026-11-09 定):人格用底座出厂神格;要「塑造」某分身属于改身份,仍须人点头(5.4 红线);
  • 漂移记录只落 ops\drift.jsonl,不进记忆正文——它是观察,不是经历。

修订记录

2026-11-09 首版。同日从「设计成文」推进到「已落地」:persona_avatar.py +
--show-source / --context / --avatar;性格隔离按他的要求折进 AVATAR_SPEC §8(合并章节),不新增节号。

v5.5.0 — 云端任务调度

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 11:05

本页会随勘误就地更新。规范全文以 main 为准。

范围

这一版只回答一个问题:活儿是谁记的、谁派的、谁接了,能不能说清。

多端(电脑 / 手机 / 网页 / 别的模型)都能总结出待办。各记各的结果是两个人做同一件事,
或者都以为对方在做——这不是效率问题,是相互抹杀。所以分工写死:
生成归各端,登记与分发归服务端;本地不留私有任务池。

任一端总结 ──► 上交(离线先落 outbox)──► 服务端只做三件事
                                          合并 / 排序 / 匹配
                                              │
                          派给「最近报到过、能力匹配」的分身
                                              │
                                    ack ──► 执行 ──► done(带证据)
                                              │
                          超时未接手:expired → 改派(上限 3 次)→ failed + 告警

三条规则写死,不留「我觉得」:

  • 合并:标题归一相同,或 2-gram Jaccard ≥ 0.6 → 合成一条,source_ids 全留(丢了就不可回溯);
  • 排序:prio 1–10,同优先级先看依赖、再看创建时间;服务端只校正不发明;
  • 匹配:人的指令 > 能力标签 > 最近报到;能力不够的不派。

两个防坑设计:认领用文件锁 + 追加(抢不到就是 exit 1,不靠"应该不会撞");
状态只追加不覆盖(ops/tasks.jsonl 与 dispatch.jsonl 同构),任何一条都能倒查回去。

落地

命令 / 接口 作用
persona_tasks.py submit --offline 上交;离线落 ops/outbox.jsonl
flush / merge / pool / show / ack / done 补交、合并、看池子、追溯、认领、收工
serve --port 8787 POST /tasks · GET /tasks?agent= · POST /tasks/<id>/ack

HTTP handler 只有 persona_tasks._Handler 一处,serve() 与自检都从 make_server() 起。

证据

① 不重复:两进程抢同一条 → 退出码 [0, 1]
② 不丢 :outbox 1 条 → flush 后池子 1→2
③ 不卡 :换人次数用满 → failed + 告警
④ 可回溯:show 能追到 source_ids 与提出者
⑤ 合并 :同义标题 → 合并,来源 ['s-1905-07','s-1905-08','s-2']

三接口(随机空闲端口):POST /tasks → 201;GET /tasks?agent= → 200;
POST /tasks/<id>/ack 第一次 200、第二次 409;同标题重提 → 200 且同 id(幂等)。

边界

  • 只绑 127.0.0.1,不开公网;跨机靠文件同步或隧道,不靠这个端口;
  • 托盘「任务池」只读视图还没做(B83):现在看池子要用 pool 命令;
  • 超时改派仍交给已有的 persona_report.py reap,本模块不重复实现一套回收逻辑;
  • 任务记录进 ops/,不进 memory/——任务是活儿,不是经历。

修订记录

2026-11-09 首版。同日从「设计成文」推进到「已落地」:新增 persona_tasks.py;
HTTP handler 抽成一处(第一版自检里复制过一份,已删)。

v5.4.0 — 后台处理

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 11:05

本页会随勘误就地更新。规范全文以 main 为准。

范围

这一版只回答一个问题:上下文涨到上限之前,程序该在什么时候动手、按什么顺序动。

原来的答案是「人记得清理的时候」。他记不住,也不该记——所以把决定权从人手里拿走,只保留否决权
(删记忆仍要人点头)。设计三件事,缺一件就不成立:

一、一维水位尺,四档,顺序写死、不许倒挂。

300K 注意 ──► 600K 准备 ──► 800K 该交接 ──► 1M 断
                              └─ 先镜像 → 再出包 → 才允许压

数字只在 persona_limits.py 一处,persona_gauge 与 persona_daemon 都 import 它。
这条尺子以前被拆成三份(规范 / 量尺 / 守护各一份),还出现过 HOT 900K 压在 CLEAN 800K 上的倒挂——
85 万会报「热」而不是「该交接」。这类错只有一处定义才防得住。

二、上下文一律按「输入」算,不按缓存。

状态栏里「输入 182K · 缓存 56.2M」是两个东西:输入是这一轮真正带进上下文 的量,缓存是整个会话累计命中的量。
拿缓存定交接线,等于拿账单当体重秤。所以判定只认每轮输入(主模型、剔除 qwen* 本地小模型轮次,
它们的输入小、会把水位算小),拿不到实测才退回账本估算并标 ~。

三、四个动作顺序写死:监测 → 镜像 → 压缩 → 归档。

没镜像就压,等于拿记忆赌运气——所以镜像规范比压缩写得更细:

  • 落 archive\<日期>\<来源>\<会话id>-mirror.md,七字段头(会话 / 设备 / 底座 / 镜像于 / 触发 / sha256 / 原文);
  • 同名同内容幂等跳过;同源改了内容 → 另存带时间戳,绝不覆盖(镜像是证据,不是缓存);
  • --check 用头部记的 sha256 自证正文没被改过;
  • 头字段解析复用 persona_avatar.from_head,不写第四份正则。

落地

文件 做了什么
persona_limits.py 水位阈值唯一出处(OK/WARN/CLEAN/MIRROR/LIMIT/PACK_LIMIT)
persona_mirror.py 镜像:七字段头、幂等、不覆盖、--check 自证
persona_gauge.py 水位读数改成「每轮输入」;文案不再写死数字
persona_daemon.t_hctx 800K 分支:先导出 → 再镜像 → 结果写进任务日志

证据

created … x-mirror.md          一致      ← 首次
skipped … x-mirror.md          幂等跳过   ← 再跑一次
renamed … x-mirror-195304.md   一致      ← 源改了内容,另存不覆盖
--check → OK(1 个镜像)

水位尺实测(--ctx 造数):250K → OK / 650K → 准备 / 820K → 该交接。
真机读数:[上下文] 123K(主模型最近一轮输入),账本兜底 ~231K 只作参考、不参与判定。

边界

  • 压缩仍 PENDING:壳没有稳定接口契约(compact 在 DeepSeek 系模型上填不出嵌套参数)。
    没有契约就调,等于把「压缩」写成碰运气——所以这版只做到「镜像 + 出包」;
  • 判据①「自然触发一次」还没发生:从没真到过 800K,所以只算「实现完成」,不算「验过」;
  • 不清记忆、不改写历史:压缩只压上下文,不动 sessions\ / archive\ 的原文。

修订记录

2026-11-09 首版。同日三改:出包线 450K → 800K(他定);删掉倒挂的 HOT、level() 改一维顺序;
上下文口径改成「每轮输入」(他:「那就按输入算,我一直看的缓存」)。

v5.3.0 — 保活 + 交互代理

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 09:56

本页会随勘误就地更新。规范全文以 main 为准。

范围

新增 tools/persona/(七个脚本)与 docs/INTERACT_SPEC.md。这一版只回答一个问题:唤醒之后到底发生了什么。

  • persona_wake.py 闹钟:登记必须带 purpose(无 purpose 退出码 2);到点先摘一份总索引简讯
    (ops/wake-brief.json),再执行 purpose(环境变量 PERSONA_PURPOSE / PERSONA_BRIEF)、
    替 agent 报到(read_index=true)。
  • persona_report.py + persona_dispatch.py 派发:两道门——最近 600 秒报到过(否则 2)、
    报到时读过总索引(否则 3);reap 把超时 pending 标 expired 并改派给醒着的分身;
    台账 append-only,原记录不覆盖。重试有上限:换 3 次仍没人接就标 failed 并写
    ops/alerts.jsonl——没有上限的改派是另一种死循环,任务一直转手、看起来永远「处理中」。
  • persona_master.py 总索引速览(人读 / --json / --pending)。
  • persona_proxy.py 交互代理:12 个动词、只绑 127.0.0.1、X-Persona-Token、逐次审计;
    sys.run 默认全拒,白名单在 ops/proxy-allow.json。协议见 docs/INTERACT_SPEC.md。
  • persona_keepalive.py 保活:进程查询三态(在跑 / 没在跑 / 查不了)。默认只报不拉
    (autostart: false):进程该不该活归它自己的启动项/运行时管,保活只负责「看得见」;
    要拉得在目标里显式写 true。
  • selftest.py 自检:临时库,真跑。
  • persona_todo.py 待办备忘:把 BACKLOG 摊成 续接\待办备忘.md(等你拍板 / 我可做两段)。
  • 交接包在「指针」前固定输出开工三步:读设定 / 读总索引 / 认领检查。

守护进程接三个定时任务(wake / reap / proxy),保活并入已有的 agent 任务。
一条心跳成立:闹钟 → 摘总索引简讯 → 执行 purpose → 报到(已读索引)→ 派发(两道门)→ 超时回收/改派。

证据

  • python tools/persona/selftest.py → PASS 36 / FAIL 0(临时库,不碰真数据):
    总索引 3 条来源倒序;无 purpose 登记退出码 2;到点触发写 index_total=3 的简讯并注入
    PERSONA_PURPOSE;派发两道门分别 3 / 2;过期记录标 expired 并改派给 rg2,原行未被覆盖;
    超过重试上限标 failed、不再改派、alerts.jsonl 出现 dispatch_failed;
    代理 11 项:越界 403 / 未知动词 404 / 未授权 sys.run 403 / 加白名单后 200 / 错 token 401,
    审计 9 行且不含 token。
  • 实机守护进程:wake OK 无到点闹钟、reap OK 无超时派发、proxy OK 代理在跑、agent OK 在跑;
    persona_proxy call health 返回 version=5.3.0;壳关掉后保活只报状态、不再自动开窗口。

边界

  • 代理只绑 127.0.0.1,不做跨设备、不做公网,不当远程 shell;sys.run 默认全拒;
  • 进程查询在受限令牌下可能被过滤,故返回三态:查不了就不动手(看不见 ≠ 没在跑);
  • 告警只落盘(ops/alerts.jsonl),托盘红灯的取数留给 v5.4;
  • tools/ctxpack/persona_daemon.py 仍是本机私有部署件,接线写在 tools/persona/README.md 里,未整份入库。

修订记录

2026-11-09 首版。同日曾切 v5.3.1(重试上限)与 v5.3.2(保活只报不拉)两个补丁,
随后撤销并合并回本版——三个版本号讲的是同一件事,拆开只会让读者做版本拼图;
两个 tag 与 Release 已删除。顺手修:persona_master 读 L1.jsonl 遇 BOM 整行丢失(改 utf-8-sig);
persona_wake 的 --run 补 cwd 与 PERSONA_BRIEF 环境变量。

v5.2.0 — 托盘状态灯

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 09:09

本页会随勘误就地更新。规范全文以 main 为准。

范围

新增 tools/tray/persona_tray.py:Windows 系统托盘,只读守护进程写出的 ops/persona-status.json。
绿=正常、黄=有任务在忙、红=有任务失败或状态超过 3 分钟没更新;双击看各任务最近一次结果;
菜单可打开日志、打开记忆库、退出。只显示,不代办任何任务。

依赖:pystray + Pillow(python -m pip install pystray Pillow)。

证据

  • 实机启动 6 秒无异常退出;
  • 状态解析覆盖四态:正常 / 忙碌 / 失败 / 状态文件缺失(灰色「读取中」);
  • 字段与 persona_daemon 写出的 persona-status.json(at / tasks.*.ok / busy / msg)逐项对应。

边界

  • 不含界面注入,不含记忆写回,不替代守护进程;托盘本身不产生任何数据;
  • 仅 Windows;状态文件缺失时只显示灰色,不做重试;
  • 同时含一处抓取器修复:无虚拟列表时回退到 document.body,避免整次抓取直接报错(单独提交 1efb7ae)。

修订记录

2026-11-09 首版。托盘是「常驻程序看得见」的第一步;后续每个小功能单独切版本。

v5.1.0 — 托盘(控制台)设计

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 09:16

本页会随勘误就地更新。规范全文以 main 为准。

范围

docs/TRAY_SPEC.md:托盘(控制台)的定位与路由模型。

  • 托盘是控制台,不是智能体:管第三方 agent,不承载对话、不做推理、不跑本地模型;
  • 路由:托盘指定一个主节点(主人格或某个分身)→ 主节点按能力与可用性派给分身 → 结果回传 → 分身掉线自动切;
  • 功能清单:v5.2.0 状态灯(已发)、v5.3 启停控制、v5.4 任务池视图、v5.5 保活面板;
  • 明确否决:托盘内置小模型自己执行任务——路由权归主节点。

证据

本版无代码;判据是把四条边界写死进文档:不跑模型、不写记忆、不直连 API、状态只读 ops/persona-status.json。

边界

不含实现。v5.2.0 的状态灯是本文第一条落地;后续每版只加一个小功能。

修订记录

2026-11-09 补发:先发了 v5.2.0(实现),回头补 v5.1.0(设计)。编号低于 v5.2.0 但提交晚于它,特此明示,避免按时间线误判。

v5.0.0 — 寄生在开源壳上的记忆层

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 01:39

本页会随勘误就地更新。规范全文以 main 为准。

范围

这一版改的不是功能,是交付形态:停止自研客户端,把记忆层嫁接到开源壳上。

前面四个大版都在造自己的壳:加载、组装、路由、执行、归档,五件事都要自己兜。
结论是——壳是最不值钱的那层:它每天在变(Electron、UI、登录、模型路由),而记忆层几乎不变。
继续自研壳,等于用有限的精力去追一个一直在动的东西。

所以这一版做了三件套的分工,边界写死:

件 谁 约定
壳 Hermes Agent 中文社区桌面版(Electron + Python,本地 SQLite + FTS5) 只读不托管;记忆仍是纯文件,两边随时可拆
脑 免费档 Qwen/Qwen3.5-4B(256K 上下文);重活仍交网页端 DeepSeek 判断型任务不下放给小模型
检索 本地 bge-m3(免费、不出门) embedding 与 rerank 都在本机

两个细节是设计,不是配置:

  • 路径:C:\hermes-mem 用目录联接(Junction)指向中文长路径。为什么不用真路径?
    小模型的分词会把 AI会话检索记录 读成 AI 会话检索记录(多一个空格)→ File not found。
    绕开它的缺陷,比要求它别犯错便宜。
  • 工具面:platform_toolsets 白名单把 31 组裁到 11 组。上下文预算是产品约束:
    多留一组工具,就少留一段记忆。

证据

  • read_file 0.4 秒读出归档 1098 条,内容与 LATEST.md 逐字一致(含「上轮追加 16 条」);
  • 一轮 42.6 秒 / 38.6k 输入 token;工具面 31 组 → 11 组;
  • 免费档校验(价格页机器可读数据):Qwen3.5-4B 输入/输出均为 0;
    Qwen2.5-7B-Instruct 同为 0,但上下文仅 32K,被壳的 64K 门槛拒绝;
  • 本地 bge-m3 嵌入 2525 块:8.5 分钟 / 0 tokens;
  • 失败留档:4B 把 AI会话检索记录 输出成 AI 会话检索记录(多一个空格)→ File not found。
    这条失败比上面几条成功更有用——它决定了 Junction 那个设计。

边界

  • 数据出本地:大脑在云端,发给它的都离机;上云前必须过 mask_secrets.py,
    这条尚未接进壳的调用链——是这一版最大的未闭合项;
  • 免费档限流:RPM/TPM 打满即 429,批量活不要压在这里;
  • 壳升级会打回原形:config.yaml 之外的补丁要重放(工具白名单已落在配置里,可重放);
  • 登录态仍未解决:壳读网页会话那条链路依旧要手动扫码,与 v4.3.0 记录的同一问题同源;
  • 不含:登录态持久化;脱敏只实现未接线。

修订记录

2026-11-09 首版:新增 docs/SHELL_ADOPTION.md;README 徽章、PROJECT_MEMO §五、
DECISIONS 版本线同步到 v5.0.0。

壳会过时,记忆不会。

v4.6.0 — 技能与命名

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 04:46

本页会随勘误就地更新。规范全文以 main 为准。

范围

docs/SKILLS.md:七项技能的触发词与判据,以及**「什么经验才配变成技能」**——

值得 不值得
做成功过一次、以后还会重复 一次性的迁移、临时排障
有明确触发词 需要读长文才能判断
判据能写成是/否 "感觉更好了"

配套:9 个脚本统一 persona_ 前缀(keeper → persona_daemon、ctxguard → persona_gauge、
ctxslim → persona_slim、ctxpack → persona_pack、hermes_export → persona_bridge、
zsearch → persona_search、index_inc → persona_index、index_l1 → persona_index_l1)。

改名是真实改动(脚本立刻可用),但它不是新能力。 历史归档与旧会话不改名——那是记录,不是配置。

证据

改名后守护 11 个任务全绿;导入 / 导出 / 索引 / 备份各跑一轮无异常;persona_gauge、persona_search 冒烟通过。

边界

  • 技能只写"触发词 + 判据",不写教程——教程会过时,判据不会;
  • 改名只覆盖脚本与运行期产物,历史文件一律不动(改了就对不上账);
  • 本版不含新能力。

修订记录

2026-11-09 首版。修三处:read_watch.py、handoff_rotate.py 带 UTF-8 BOM 根本无法解析(长期静默失效);
导出无锁并发会 PermissionError(加 ops/export.lock);弃用会话仅删文件挡不住(每轮导出写回),
改用 ops/skip-sessions.txt 名单。

v4.5.0 — 索引:四层与增量

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 04:46

本页会随勘误就地更新。规范全文以 main 为准。

范围

docs/GLOBAL_INDEX.md:索引是前置放大器——模型再聪明,也答不出没被喂进去的那段。

  • 四层:L0 正文 / L1 目录(每源一行)/ 块索引 / 归档;
  • 增量:按 sha256 记账,改回旧内容也算没变;
  • 安全:先写临时文件再原子替换,重建期间旧索引照常可查;
  • 检索:关键词 + 向量 RRF 合并,断网自动降级为纯关键词。

证据

  • 索引重建:19170 块 / 121 文件 / 1.7 分钟;
  • 连续第二次跑:嵌入 0 块(哈希幂等成立);
  • L1 目录随出包自动重建,接续包 3413 字 / 6.3 KB,未超上限。

边界

项 状态
L1–L5 五级时空密度索引(B35) 仍是纸面
索引竞态 已修:列目录与算哈希之间文件被归档删除时不再整任务退出
压缩 / 兜底 不在本版范围

修订记录

2026-11-09 首版。修:FileNotFoundError 曾让索引任务每轮卡在同一处(同一批文件永远算不出哈希)。

v4.4.0 — 画像:观察即记忆

Choose a tag to compare

@MirHuang666 MirHuang666 released this 09 Nov 04:46

本页会随勘误就地更新。规范全文以 main 为准。

范围

docs/PROFILE.md:画像不是模型对人的印象,是一串可追溯的观察。

  • 采集:对话里的 记一条观察:… 由导出器收割成 - [日期] … → 证据;
  • 写入:只追加,同一句重复出现也只留一条(幂等);
  • 指针:壳侧 memories\ 只放 ≤2000 字符的指针,换壳不重写画像;
  • 分流:归纳用免费小模型,判断型任务(工具选择、审批)不得下放。

为什么强调"观察"而不是"评价":评价不可验证、会被模型的措辞带偏;观察带日期和证据,能被翻回去核对。

证据

本版是设计,唯一可测的是「幂等」:同一句观察重复十次,USER.md 只多一行。

边界

项 状态
写回链路 未验收——merge 至今只报「无新增」
画像质量 未评估;观察是句子,不是评分
删除 任何一条观察的删除都要人确认(5.4)

修订记录

2026-11-09 首版。同日曾把画像/索引/技能打成一个版本,因颗粒度不对已撤销,拆成 4.4 / 4.5 / 4.6。