# Dynamics-memory > 状态:现行 · N48–N52 已汇合(真实 CLI 首验、可观测性、L2/L3 抽检工具链与审计修正);决策登记见 [analysis/target-architecture.md §9.3](analysis/target-architecture.md);PENDING-08 待人工验收。 > 本页是行为摘要;机械验证入口在 [analysis/acceptance_check.py](analysis/acceptance_check.py)。 **LLM agent 的有界长期记忆层** 把连续、嘈杂、前后矛盾的项目交互,蒸馏成一个不会堆成"史山"的记忆库。
新会话里一句"接着搞",agent 就知道进行到哪、上次卡在哪。 ![engine](https://img.shields.io/badge/engine-LLM--free-blue?style=flat-square) ![protocol](https://img.shields.io/badge/agents-Hauler%20%C2%B7%20Selector%20%C2%B7%20Reviewer-8E44AD?style=flat-square) ![storage](https://img.shields.io/badge/storage-SQLite%20%C3%973-16A085?style=flat-square) ![status](https://img.shields.io/badge/status-research%20preview-orange?style=flat-square) [全景](#全景一条对话的旅程) · [架构分层](#架构分层) · [记忆动力学](#记忆动力学) · [可靠性](#可靠性骨架) · [运行](#运行) · [配置](#配置)
--- ## 30 秒上手 ```bash pip install numpy pytest cp .env.example .env # ZAI_API_KEY=... python -m pytest tests/ # 本地测试;不是远程验证 python -m hybrid_memory.server --project # 或被 opencode 插件自动拉起 ``` 接入 opencode(官方 CLI 即可,无需源码): ```bash npm i -g opencode-ai cd <本仓库> && opencode # .opencode/plugin/memory-bridge.ts 自动加载并拉起 sidecar ``` 给别的项目用:把 `memory-bridge.ts` 软链到 `<项目>/.opencode/plugin/`。插件的 `@opencode-ai/plugin` 依赖按软链的真实路径解析,所以要先在本仓库里跑一次 opencode (或 `cd .opencode && npm i @opencode-ai/plugin`)让依赖装好。 ## 为什么不用现成的两条路 | 路线 | 死因 | |---|---| | 全量 log 塞 context | 每轮付 O(log) 的 prefill+KV 扫描,随项目历史线性变贵 | | 原文入向量库 | 冗余、过期、互相矛盾的副本堆成史山,旧状态压过新状态 | **log 不是记忆,log 是证据。** 记忆是蒸馏产物,进池要过动力学与置信门控; 日志留在 L0 冷层,只在需要时按计量回展——永不整段进 prompt。 --- ## 全景:一条对话的旅程 从主 agent 的一句"以后统一用 bun",到它成为一条可检索、可归因、可结算的记忆: ```mermaid flowchart LR U("主 Agent
OpenCode") -->|"① 会话事件"| BR["memory-bridge.ts
插件桥"] BR -->|"POST /observe"| SVC["sidecar
:17872"] SVC --> L0[("L0 证据库
只增不删 · FTS")] SVC -.->|"② 信号入队"| Q[["任务机
SQLite 持久交接"]] Q -->|"③ --file 文件通道"| H["Hauler
抽候选"] H -->|"④ 候选 + 来源"| S["Selector
五路裁决"] U -.->|"表达不满"| REV["Reviewer
诊断 + 版本化规则"] REV -.->|"持久规则回喂"| H S -->|"CREATE"| ENG["记忆引擎
三池动力学"] S -->|"CONFLICT"| HR{"human_reviews
待人工审核"} HR -->|"accept_new / keep_old"| ENG S -->|"REJECT"| X(("不入库")) BR -->|"⑤ GET /recall"| SVC SVC -->|"⑥ relevant-memories 注入"| U BR -->|"⑦ POST /feedback"| SVC SVC -->|"useful-hit → V 值结算"| ENG classDef agent fill:#FFF3E0,stroke:#E8963C,color:#7A4A00 classDef core fill:#E8F4FD,stroke:#2B8CBF,color:#0B4F6C classDef store fill:#F5F5F5,stroke:#9E9E9E,color:#424242 classDef risk fill:#FDECEF,stroke:#D65F7F,color:#8A2444 class H,S,REV agent class ENG,SVC core class L0,Q store class HR,X risk ``` 七步读法:① 每轮对话先落 L0(幂等回执,`request_id` 重放不重复入库); ② 证据到位才发 `hauler_due` 任务;③ Hauler 在**真 OpenCode 进程**里跑(Windows 默认常驻 `serve` + `run --attach`,其余平台默认 `run` 自举;`OPENCODE_SPAWN_MODE` 可覆盖),输入经 `--file` 私有附件传递、调用前封存上下文;④ 候选必须引用它见过的窗口内证据, Selector 逐条裁决;⑤ 检索时相似度 + 压制 + 因果界过滤;⑥ 命中记忆按 token 预算 注入;⑦ 反馈经 recognizer 归因,只有**真被答案用上**的记忆拿到 useful-hit。 信号只负责持久交接、租约和幂等;三份 `.opencode/agent/*.md` 定义负责语义判断。 重叠滑窗按 L0 来源单元去重,冲突须人审。默认模式下主 agent 不能绕过 Selector 直接调用 `/propose` 或 `/resolve`。 完整协议、审核入口、恢复及已知边界见 [OpenCode 三 Agent 文档](docs/opencode-trio.md)。 旧的单轮 candgen 和 sidecar 内调查员是**待退役的兼容路径**(仅 `MEMORY_PIPELINE=legacy` 显式启用;删除条件见 PENDING-05),其设计记录见[衔尾蛇说明](docs/ouroboros.md), 不能把旧记录当作默认模式的验证。 ## 架构分层 依赖严格单向向下;模型调用与向量计算永远在服务锁外: ```mermaid flowchart TB subgraph T["transport/ · 传输"] HTTP["HTTP 服务 · auth · dto
bootstrap 组装 · review CLI"] end subgraph SV["service/ · 门面"] SVC["MemoryService
observe · recall · feedback · propose · resolve · 人审
预算准入 · 因果界 · telemetry"] end subgraph DP["dispatch/ · 调度"] W["DispatchWorker
+ 语义循环"] EF["effects 效果事务
容量收口 · 完整回滚"] POL["policy 策略表
SEM 5/5 · WF 5/5 · INV 2/3"] end subgraph AG["agents/ · 角色"] TRIO["Hauler · Selector · Reviewer"] OCR["OpenCodeRunner 文件通道
+ 调用上下文封存"] end subgraph SEM["semantics/ · 语义"] SP["SemanticsProvider 双通路
judge · relevant_set · consolidate"] end subgraph CO["core/ · 引擎(无 LLM)"] ENG["三池动力学 · ingest 去重
张力与裁决 · 巩固蒸馏 · plan_capacity"] end subgraph ST["store/ · 存储"] EDB[("L0 证据库")] TDB[("任务库 + checkpoint")] SCH["schema 三库身份版本"] end EXT["OpenCode CLI 真进程"] LLM["LLM / Embedding"] HTTP --> SVC SVC --> W W --> TRIO TRIO --> OCR --> EXT W --> EF EF --> ENG ENG --> SP --> LLM SVC --> EDB EF --> TDB SCH -.- EDB SCH -.- TDB classDef layer fill:#FAFAFA,stroke:#BDBDBD,color:#424242 class T,SV,DP,AG,SEM,CO,ST layer ``` 单进程内一条铁律:**锁内不做慢事**——模型、向量、文件 IO 全在锁外完成, 锁内只剩纯内存变更与事务提交(见 `prepare_effect` 的锁外预计算设计)。 ## 记忆动力学 记忆 = 有版本的信念。每条携带 `src`(溯源到 L0 单元)与 `superseded_by / aggregated_into`(版本谱系),在三个池之间流动: ```mermaid stateDiagram-v2 direction TB [*] --> C: CREATE / reflection 入池 state "C 候选池(cap 200)" as C state "M 常用池(cap 8 · 检索主力)" as M state "A 冷归档池(cap 2000)" as A C --> M: V 超过 θp 且有容量 M --> C: V 跌破 θd(θp>θd 滞回防抖) C --> A: 闲置 / 容量收口迁出 A --> C: EXIST 复活 / 低先验召回 M --> A: 旧版退役(加指针 + 时间戳) A --> [*]: 无保护版本 FIFO 物理回收 ``` | 机制 | 一句话 | |---|---| | 价值 × 置信分离 | `V ← V·e^(−λ) + η·hit + η_s·shadow`;高价值定半衰期,置信达标才服务——高价值低置信"活着但不用" | | 滞回 + 驱逐 + 归档 | θp>θd 防抖,容量封顶,闲置进冷层——M 池有界且自我更新 | | 压制即检测 | 过相似未入选对自动进 tension backlog,冲突检索时自然暴露 | | 矛盾不静默 | 同实体矛盾聚合收编(全版本+时间戳),未决 conflict 不许自信出场(contested co-serve) | | 容量背压 | 非退役版本总数超 `cap_context` 时按 FIFO 收口;全 pin 收不了口 → 503 拒收,绝不静默超限或解除保护 | | 可溯源 | 每条记忆携带源单元 id,可回 L0 查证;`origin` 纯审计不给 V 加成 | ## 可靠性骨架 **一切变更走持久任务机**——崩溃后 kill -9 重启,已提交的证据不丢、租约过期的 任务重跑、快照落后以日志为准: ```mermaid stateDiagram-v2 direction LR [*] --> pending: enqueue(同 key 合并去重) pending --> running: claim(租约 + 版本 CAS) running --> ready: 产物落库(可重放) running --> pending: 失败退避(计数保持) ready --> applying: 领取(apply_attempts+1) applying --> done: 效果事务提交 applying --> ready: 应用失败退避(产物保留) running --> dead: 重试耗尽(上限见策略表) pending --> dead: 过期恢复后耗尽 done --> [*] ``` 效果事务是唯一写入口:内存引擎变更、任务状态、checkpoint 与回执**同一笔 SQLite 事务**,失败深备份完整回滚。模型产物(ready 的 result)在应用失败时 保留退避,只有应用耗尽才 dead——绝不因收口失败清空合法产物重跑模型。 冲突裁决不删数据:CONFLICT 落 `human_reviews` 队列并 pin 保护目标, 人工持独立 token 二选一,其余待审自动标 stale——记忆不接受单方面改写。 ```mermaid flowchart LR S["Selector 裁 CONFLICT"] --> PIN["目标 pin 保护
落 human_reviews"] PIN --> HUM{"人工审核
独立 token"} HUM -->|"accept_new"| NEW["新版本入库
旧版退役指向新版本"] HUM -->|"keep_old"| OLD["候选拒绝
旧版保留解除 pin"] NEW --> T["张力消解"] OLD --> T classDef risk fill:#FDECEF,stroke:#D65F7F,color:#8A2444 classDef ok fill:#E8F5E9,stroke:#43A047,color:#1B5E20 class S,PIN,HUM risk class NEW,OLD,T ok ``` ## 评测 评测平台 **TIDE** 与引擎完全分离(只经 HTTP 协议通信,真值不越过边界)。 设计见 [`docs/benchmark-design.md`](docs/benchmark-design.md), 第一版可运行实现在 [`eval/`](eval/README.md)(场景驱动器 + 离线 mock + 基线报告)。 sidecar 为评测提供的两个能力: | 参数 | 端点 | 作用 | |---|---|---| | `passive` | `GET /recall?passive=1` / `POST /search {"passive": true}` | 在引擎副本上检索:不改任何状态、不登记 retrieval_id(评测探针用) | | `budget_tokens` | 同上 | 上下文按 `approx_tokens` 计数截断整行(CJK 单字 / ASCII 词 / 符号各记 1) | 旧的 experiments/ 评测已整体移除:其仿真与引擎共用真值对象、口径无法识别聚合输出、 样本量不足以支持结论,数字不再引用。 ## 配置 | 环境变量 | 默认 | 作用 | |---|---|---| | `MEMORY_PIPELINE` | opencode | `legacy` 启用旧单轮 candgen 和进程内调查员;**其他值拒绝启动** | | `MEMORY_AGENT` | on | `off` 暂停后台 agent,任务仍持久等待 | | `MEMORY_AGENT_DAILY_CAP` | 200 | 仅 legacy 调查员的日调用上限 | | `MEMORY_AGENT_MODEL` | glm-5.3-flash | 仅 legacy 调查员模型 | | `MEMORY_OPENCODE_BIN` | opencode | OpenCode CLI 路径(需配置自己的模型 provider) | | `OPENCODE_SPAWN_MODE` | 按平台 | OpenCode 调用模式:Windows 默认 `serve+attach`(常驻 serve + `run --attach`),其余平台默认 `bootstrap`(`run` 自举);非法值拒绝启动 | | `MEMORY_BRIDGE_PORT` | 17872 | sidecar 端口 | | `ZAI_API_KEY` | — | 原引擎向量编码等依赖;三 Agent 使用 OpenCode 自身 provider 配置 | | `ZAI_BASE_URL` | `https://open.bigmodel.cn/api/paas/v4` | OpenAI 兼容端点(chat / embeddings),可换代理或本地 mock | `MEMORY_PIPELINE=legacy` 下,旧调查员才在有 API key 时自动消费旧信号; trio 队列、结果及 Reviewer 规则保存在 `tasks.sqlite`。 ## 验证状态 Python 与 Bun 测试覆盖服务端、插件和 mock HTTP 不变量;手工 smoke 用真实 OpenCode CLI + 本地假模型完成三 Agent 任务交接;端到端穿透回归覆盖"observe→Hauler→Selector→效果落库→recall→feedback→信用结清"、"CONFLICT→人审双分支"与"kill -9 两库崩溃恢复"三条链。N48 已用真实 OpenCode CLI + 智谱 glm-5.3-flash 验证小样本 Hauler→Selector→落库。N51 五链实际走 inline runner;L3 全为开发日志引述,100 行评分是 Agent 单评,不能等同于真实会话与人工验收;[抽检报告](eval/l2l3/REPORT.md)记载证据与限制;独立人工的真实会话 L3 验收尚未做,PENDING-08 仍保留。`GET /health` 的 `validation` 固定为 `unverified`:进程在听,不等于已经验证。 当前本机门禁:pytest **534 passed / 1 skipped**(平台条件用例);checker **170 模块 / 1602 符号 / 0 failures**;acceptance `--full` **25 PASS / 0 SKIP / 0 FAIL**;Bun **26 pass**;L2/L3 mock 全链自检贯通(194 轮喂入 → 导出 → 抽样 → 结算)。 `ok: true` 只表示没有 checkpoint fault,插件可以复用这个进程。它不表示快照干净,也不表示逐单元效果已经补齐。看 `snapshot` 和 `units_pending`。`snapshot=quarantined` 表示坏快照已被隔离成 `state.corrupt`,服务空启动,记忆没有从那份快照恢复。 ## 运行 停机后再备份整个状态目录:`log.sqlite`、`tasks.sqlite`、它们的 WAL、`state.pkl`,以及同目录的 `bridge-outbox.json`。不要只回滚其中一个库。本批没有检测这种不配套,也不能靠旧副本找回备份之后的新数据。回滚要插件和 sidecar 一起回到旧版本。 坏的 `state.pkl` 会被隔离,服务仍会起来。这是为了不让桥瘫痪,不是数据还在。`state.corrupt` 留在目录里,启动声明不会把它说成合法空库。
目录结构 ```text hybrid_memory/ ├── core/ 引擎本体:types · engine · ingest · retrieval · dynamics · │ consolidation · maintenance · signals(动力学全在锁内纯计算) ├── store/ 三库与恢复:evidence(L0) · tasks(任务机) · schema(三库身份版本) │ · state(快照受限反序列化) ├── dispatch/ 调度:worker(两路径) · effects(效果事务+容量收口) · policy(策略表) ├── agents/ 三角色:hauler · selector · reviewer · payload(封存上下文) │ · opencode(文件通道) · protocol ├── semantics/ 语义判断:real · llm · provider(SemanticsProvider 双通路+健康观测) ├── guards/ 输入防线:provenance(溯源) · grounding(落地校验) · bounds ├── service/ MemoryService 门面:observe · recall · feedback · operate · │ review · tools · budgets · lifecycle · telemetry · context ├── transport/ 传输:http · auth · dto · bootstrap · review_cli ├── embed/ 向量:cache(SQLite) · zhipu ├── legacy/ 旧调查员回路(兼容路径,仅 MEMORY_PIPELINE=legacy;待退役) ├── config.py Cfg / Settings / resolve_pipeline(未知值拒绝启动) ├── errors.py 错误域:Rejected / Degraded / Fatal + HTTP 码表 └── telemetry.py 健康与信号观测(地基层) .opencode/ plugin/memory-bridge.ts · agent/{hauler,selector,reviewer}.md analysis/ target-architecture.md(唯一设计)· architecture_contract.py(冻结契约) · check_architecture.py · acceptance_check.py · docs/ docs/ opencode-trio.md · ouroboros.md · benchmark-design.md · architecture-inventory.md eval/tide/ TIDE 评测(与引擎经 HTTP 分离) eval/l2l3/ L2/L3 抽检工具链(跑链·导出·盲序·结算;报告见 REPORT.md) tests/ pytest 回归:unit / integration / characterization / … ```
## OpenCode 三 Agent 协议 默认的 Hauler → Selector / 不满触发 Reviewer 协议、人工冲突审核、部署与恢复边界见 [docs/opencode-trio.md](docs/opencode-trio.md)。