跨会话记忆
wescode 的记忆系统基于 wesgine 的分层记忆模型,每个 workspace(Cell)拥有独立的记忆存储。
四层记忆模型
wesgine 定义了七个层,其中四个是用户可见的记忆层:
| 层 | 标识 | 说明 | 持久性 |
|---|---|---|---|
| L1 | environment | 宿主环境信息 | 进程感知,每次 boot 重建 |
| L2 | consensus | 部门/团队共识 | 持久,admin 写入 |
| L3 | about_me | 关于我的偏好 | 持久,用户交互产出 |
| L4 | agent_memory | 角色记忆 | 持久,Agent 工具写入 |
运行时层(不是记忆)
| 层 | 标识 | 说明 |
|---|---|---|
session | 会话记忆 | 当前会话内有效 |
working | 工作缓存 | 当前 Run 内有效 |
unknown | 未识别 | 兜底,不猜测归属 |
wescode 记忆特点
1 workspace = 1 Cell = 独立记忆
wescode 的每个 workspace 是独立的 Cell。切换 workspace = 换 Cell = 整个记忆面一起换。
workspace-foo (Cell ws-1234)
├── L1: foo 项目的环境信息
├── L3: 在 foo 项目中的偏好
└── L4: 关于 foo 项目的 Agent 记忆
workspace-bar (Cell ws-5678)
├── L1: bar 项目的环境信息
├── L3: 在 bar 项目中的偏好
└── L4: 关于 bar 项目的 Agent 记忆
这不是缺陷,是隔离本身。空层显示 0 而不是隐藏——「这个 workspace 还没有共识」和「共识被藏起来了」是两件不同的事。
Actor 恒为 local
wescode 未接 SSO,Actor 固定为 "local":
- 同一 Cell 内 L2 与 L3 事实上同人
- 但两层仍分开渲染(接入登录后它们会分岔)
- 合并过的界面回不去
Memory 读写 API
读取
| 端点 | 说明 |
|---|---|
GET /memory | 列出记忆(?layer=&namespace=&kind=&limit=&offset=&actor=) |
POST /memory/search | 语义搜索 |
GET /memory/counts | 每层条数(键覆盖全部层含 0) |
GET /memory/{id} | 取单条 |
POST /memory/validate | 诊断条目可召回性 |
写入
| 端点 | 说明 |
|---|---|
POST /memory | 保存记忆条目(body.layer) |
POST /memory/draft | 提交集体草案 |
POST /memory/vote | 投票集体草案 |
删除与维护
| 端点 | 说明 |
|---|---|
DELETE /memory/{id} | 删除单条 |
DELETE /memory/layer | 清空一层 |
POST /memory/gc | 触发垃圾回收 |
POST /memory/digest | 记忆摘要压缩 |
Memory 写入层(INV-MEM-53)
写入通过 SaveToLayer 按层决定存储位置,不直接操作 scope:
// 正确:按层写入
store.SaveToLayer(MemoryWrite{
Layer: "about_me",
Content: "用户偏好 dark mode",
Kind: KindPreference,
})
// 错误:直接指定 scope
// store.Save(MemoryEntry{Scope: "global", ...}) ← 禁止
层写入规则
| 层 | 可写 | 约束 |
|---|---|---|
environment | ❌ | 宿主感知器产出,不可人工写入 |
consensus | ✅ | 需 admin 权限 |
about_me | ✅ | 必须有 actor |
agent_memory | ✅ | 必须有 actor + namespace |
session | ❌ | 运行时态 |
working | ❌ | 运行时态 |
memory 工具
Agent 通过 memory 工具主动保存记忆。这是显式写入,不是隐式提取:
{
"tool": "memory",
"action": "save",
"content": "用户的 Go 项目使用 4 空格缩进",
"kind": "preference"
}
重要:M2 自动提取链路已删除(规则提取器 / LLM 提取器 /
ExtractFromTurnAPI 均已移除)。记忆写入完全由 memory 工具和 CognitiveSettlement 驱动。
GC 与 Digest
垃圾回收(GC)
GC 定期清理记忆:
- TTL 清理:过期条目回收
- 配额清理:超过
MaxEntries时按RetentionScore加权淘汰(INV-MEM-26)
RetentionScore 综合考虑:
- 召回频率(
recall_count) - 最近访问时间(
accessed_at) - 创建时间(
created_at)
Digest(语义摘要)
Digest 执行智能记忆整理:
| 操作 | 说明 |
|---|---|
| dedup | BM25 语义去重 |
| detectConflicts | 检测矛盾条目(INV-MEM-30) |
| detectSupersession | 检测新条目取代旧条目 |
| markStale | 30+ 天零召回标记 stale(INV-MEM-29) |
| promote | 晋升高价值条目 |
三面注入
记忆通过三个面注入 Agent 上下文:
| 注入面 | 内容 | 触发 |
|---|---|---|
| Eager | L1/L2 + Constraint + Instruction | 系统提示词(每轮都在) |
| Lazy | 本轮相关的 L3/L4 记忆 | 当轮 overlay(按相关性) |
| Cold-start | 跨会话摘要 | 新会话首轮 |
质量门(INV-MEM-50)
每条记忆在自动注入前经过 RecallQualityOK 检查:
- stale 条目(30+ 天零召回)被排除
- conflicted 条目(被更新条目取代)被排除
- 显式检索(
memory(search))不受此门约束
记忆中心 UI
wescode 的记忆页是 wesui MemoryCenter 的薄适配器:
- 七层芯片恒定渲染(含 0),因为空层与不存在的层必须有区别
- 分组列表只画非空层
- 按
layer提问,计数走GET /memory/counts - 产品侧不推导层名——层名由引擎
layer字段给出
常见问题
Q:切换 workspace 后记忆为什么全空了?
每个 workspace 是独立 Cell,记忆物理隔离。切回原来的 workspace 记忆就回来了。
Q:L2 共识在 wescode 里为什么总是 0?
wescode 个人版只有一个 Actor(local),没有 admin 向 L2 写入共识的场景。这是正常的。