# 连续记忆第一阶段:实现与验证 状态:2026-10-09,源码分支 `feat/supervisor-memory-v1`。涉及 `deskpaw_agent` 和 `deskpaw-memory`;尚未替换或重启正式服务。本文描述已实现边界,不把架构目标当作已有能力。 ## 执行路径 ```text Runtime 持久事件 ├─ 上层收件箱:终态、阻塞、需要输入 → 下次决策上下文(只读) └─ MemoryBridge 后台同步 → Memory 事件表 → 观察批次租约 → 模型提案 → 校验并原子发布 用户消息 → 限时索引召回/缓存 → 上层自然语言+控制工具 → Runtime 校验与执行 └─ 迟到结果持久化 → 上层收件箱/按请求 ID 查询 ``` 上层是主要消费者。Pi 在派发后的执行准备阶段也能读取有限工作包,并通过 `memory_evidence_get` 按 `source_refs` 展开证据;证据读取没有电脑操作权限。 上层不等待观察模型、记忆写入或 Pi 停止完成。本地只读检索有 **75 毫秒等待预算**; 超时继续后台处理,上层得到 `pending`,不能据此回答“没有记忆”。这是共享 Node 进程中的 等待上限设计,不是 CPU 饱和时的硬实时保证。执行器已发出的原子操作仍需合作式停止。 ## 谁在什么时候处理什么 | 部分 | 输入与触发 | 模型与限制 | | --- | --- | --- | | 事件筛选和任务投影 | 每秒检查持久事件;任务结束主动唤醒 | 程序,不调用模型 | | 新观察 Worker | 最多 20 条事件;最早事件等约 30 秒;结束、失败、阻塞、需输入提前触发 | 独立记忆角色,`reasoning=low`、输出上限 3000、90 秒超时、并发 1 | | 旧话题/事实整理 | 新观察队列没有可处理批次时;每分钟检查;闲置或夜间复核 | 同一记忆角色;保留旧协议,90 秒超时;不占 Pi FIFO | | 上层决策 | 用户消息 | 当前上层角色;自然语言正文和一次原生控制调用分开 | | 召回与来源展开 | 用户问题、Pi 任务准备、显式证据读取 | 中文/英文索引、ID 与 RRF,不另开模型 | 本机检查到的执行和上层模型配置均为 `deepseek / deepseek-v4-flash`;本次不修改运行配置。 新观察角色默认回退到执行角色,允许独立指定进程环境: ```text DESKPAW_MEMORY_MODEL_PROVIDER=deepseek DESKPAW_MEMORY_MODEL=deepseek-v4-flash DESKPAW_SUPERVISOR_CONTEXT_BYTES=32000 ``` 这些是 **Agent 进程**使用的变量,不是要求 Memory SQLite 服务直接连接模型。 新变量暂未加入设备配置工具的可编辑白名单。现有上层模型/reasoning/timeout 变量继续有效。 是否换更小的观察模型,应以带来源提取、遗漏率、错误归属率的离线样本评估决定;本轮没有调用付费模型做质量测评。 ## 事件与持久性 来源包括已完成的用户交互及上层回应、任务启动与终态、Pi 的失败/改策略/里程碑/阻塞/需输入报告。 没有把 `agent_message_end`、隐藏推理、普通点击、图片和原始工具大对象交给观察模型。 无变化的日程终态和日程启动被过滤。重复内容在同一批次内合并计数,仍保留每个原始事件 ID。 敏感文本做关键词防线式脱敏;这不是能识别任意未标注凭据的完整 DLP 系统。 Runtime 的 `events.seq` 是发送位置;`memory_event_cursor_v1` 只在 Memory 确认所有 ID 后推进。 Memory 接入以 `event_id` 幂等,重复 ID 不同内容会被拒绝。旧任务卡投影也会跳过完全相同的重复写入。 事件每条正文上限 2400 UTF-8 字节,观察工作包选取预算 12000 字节。队列状态为: ```text 待处理事件 → 带 token 的 120 秒租约 → 成功事务:经历+索引+来源已消费+回执 └→ 失败:保留来源,退避重试 ``` 租约过期可重领同一批;新 token 会拒绝旧 Worker 的迟到提交。提交成功而响应丢失时, 同一租约重试返回原回执,不重复发布。模型必须引用或明确丢弃所有本批事件,程序核对完整覆盖。 来源不在批次、对象无相应来源、候选偏好缺少用户逐字证据的提案均不能消费批次。 首版经历为只追加记录,带设备发布序号;未实现可覆盖事实的对象级 CAS 和自动冲突消解。 反复失败的批次会持续退避,不会默默丢弃;死信队列和人工重放界面是后续管理工作。 ## 召回、回调和安全边界 新经历使用倒排表查找,不限于最近 100 次任务。融合正文、召回线索和显式对象 ID;正文证据的 排序权重高于模型生成的线索。召回结果携带 `reported`/`candidate` 状态,不能被当作已验证事实或已批准规则。 新经历与旧资料库并存;旧话题仍有全表排序成本,后续需要迁移,不宣称所有旧数据已完成索引化。 上层总文本预算默认 32000 UTF-8 字节,先扣除固定提示词和工具模式,再保留完整当前请求与实时状态。 日志、历史和记忆有单独裁剪及省略标记。过长的当前请求拒绝派发,不静默截断后执行。 这不是供应商精确 token 计数;中文不能用“4 字符等于 1 token”来保证预算。 暂停返回“已暂停队列、正在停止”,恢复返回“已接收,待检查”。改向/取消当前任务在模型调用前 捕获任务 ID 和控制修订号,持久化决定后再核对;A 完成、B 接替或 A 已被另一路修改时,旧指令不执行。 关键执行事件和召回结果进入 `supervisor_inbox`,重复阻塞按任务控制版本合并,用户交互优先消费。 `interaction_seq` 区分迟到召回;迟到结果保留,但不自动操作新的任务。 新增只读 Agent 接口: - `GET /v2/attention`:未消费的上层注意事项。 - `GET /v2/memory/requests/`:召回状态及有界结果。 **这一步没有新增主动聊天通知,也没有让回调自行调用模型继续执行。** 现有任务结果仍走原来的 Gateway 输出渠道;新记忆的迟到查询结果进入缓存和收件箱, 在后续上层交互中使用,也可按 ID 查询。下一步才加入可中断的只读回调评估、通知去重和渠道投递确认。 首次查询超过等待预算时,当前回复可能只说明查询中,不承诺已自动补发最终回答。 当前 `owner=device:` 约束新索引与证据接口。旧全局资料仍按单设备共享会话使用。 这不是多租户身份认证;本地 socket 的受信边界没有改变。 ## 开源复用落点 - T-Mem:实际移植 RRF 算法到 `src/rank-fusion.js`,保留 MIT 声明及版本;增加通道内去重和正文加权。 - Mastra:借鉴观察批次、显式来源引用、准备/发布分离与异步结果归属;没有把整个框架或其可能等待后台工作的激活路径嵌进上层。 - Letta Code:借鉴小型常驻区、按需证据展开、提案成功落地后才消费输入,以及偏好/经历/操作技能分开;没有复制 Git 工作树管理来替代 SQLite。 Mastra 与 Letta 本阶段属于协议和流程适配,不是宣称原样导入它们的模块。 ## 后续顺序 1. 对象注册和关联:责任领域、跨月项目、软件资产与多个日程共用稳定 ID;目前适配器自动归属仅到会话/任务/日程,未自动识别所有项目。 2. 只读回调评估及主动通知:用户消息能抢占,过期回调归档,通知有去重和送达确认;操作仍须满足原授权。 3. 候选复核、纠正取代、遗忘/删除、保留期限与人工重放;然后迁移旧话题索引和补充向量通道。 4. 将观察 Worker/Pi 隔离到独立进程,做持续负载和模型限流测试,保证 H618 上的调度公平性。 财务示例中,发票日常整理与月度提交应归同一报销领域,月度提交使用每月实例;维护软件则绑定 资产和独立修复实例,休眠时只保留索引,相关报错再次出现才展开经历。这些对象关系是下一步,而不是在 首版按时间摘要上贴一个“项目记忆”名称。 ## 验证 2026-10-09 本机结果:Memory 25 项通过;Agent 上述四个测试文件共 54 项通过; Agent 完整 `npm run check` 通过;两个仓库的 `git diff --check` 通过。 Memory:`npm test`,隔离数据库与临时 Unix socket,覆盖幂等、租约恢复、来源校验、中文跨窗口检索、 重复观察、敏感文本、跨日复核,以及真实服务进程重启后的回执与证据读取。 Agent:仓库根目录 `npm run check`(包括格式、锁文件、TS、浏览器冒烟检查); 在 `packages/coding-agent` 执行: ```bash node ../../node_modules/vitest/dist/cli.js --run test/device-continuity.test.ts test/device-runtime.test.ts test/device-memory.test.ts test/device-session.test.ts ``` 这些测试使用离线模型与假 Executor,不操作真实桌面,不访问真实模型 API。覆盖慢记忆服务、 后台观察与前台解耦、暂停回执、过期目标、天然语言与控制分离、会话恢复、日程以及证据筛选。 正式环境验证和部署仍需单独进行;部署前备份 Runtime/Memory SQLite(含 WAL 一致性),先更新 Memory 协议再更新 Agent。