Files
memory-gateway/docs/superpowers/specs/2026-06-15-memory-attachment-path-mapping-design.md

102 lines
3.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Memory 附件真实路径映射设计
## 目标
`/resources``/memories/add` 两种摄入方式都保存附件与 session 的映射。
`/memories/search` 返回结果时,根据结果 `session_id` 查询当前用户附件,并且只有
当附件完整文件名出现在结果 `raw` 的字符串字段中时,才返回该附件真实 URI。
## 数据模型
新增 SQLite 表 `memory_attachments`
- `id TEXT PRIMARY KEY`
- `user_id TEXT NOT NULL`
- `app_id TEXT NOT NULL DEFAULT 'default'`
- `project_id TEXT NOT NULL DEFAULT 'default'`
- `session_id TEXT NOT NULL`
- `resource_id TEXT`
- `content_type TEXT NOT NULL`
- `name TEXT NOT NULL`
- `internal_uri TEXT NOT NULL`
- `source TEXT NOT NULL`
- `sha256 TEXT`
- `created_at TIMESTAMP NOT NULL`
- `deleted_at TIMESTAMP`
`(user_id, session_id, internal_uri)` 建立唯一索引,避免幂等上传产生重复映射;
`(user_id, session_id, deleted_at)` 建立查询索引。
数据库初始化时,将现有未删除 `user_resources` 回填为附件映射。历史
`/memories/add` 请求没有保存在 Gateway 数据库中,因此无法自动回填。
## 摄入规则
### `/resources`
资源记录创建后,为保存的真实 `file://` URI 创建附件映射:
- `session_id` 使用 `resource:{user_id}:{resource_id}`
- `resource_id` 指向资源;
- `source``resource_upload`
- `content_type`、文件名、SHA256 复用资源元数据。
重复资源上传时确保已有资源对应的附件映射存在。
### `/memories/add`
API 将已鉴权的 `user_id` 一并传给 service。逐条检查 message 的 content item
- 只有字符串 content 或纯文本 item 时不创建附件;
-`uri` 时记录该 URI`source=memory_add_uri`
- 没有 `uri` 但有 `base64` 时,解码并保存到
`storage/{user_id}/memory_attachments/{attachment_id}/{safe_name}`,记录生成的
`file://` URI`source=memory_add_base64`
- 同时存在 `uri``base64` 时优先使用 `uri`,不重复落盘;
- 文件名优先使用 `name`,否则从 URI 路径或 `ext` 生成安全名称。
上游 add 调用失败时,删除本次 base64 生成的文件,不写入映射。调用成功后写入
附件映射。上游请求体保持原样,不修改现有 add 行为。
## 搜索匹配规则
对每条标准化搜索结果:
1. 根据已鉴权 `user_id` 和结果 `session_id` 查询未删除附件;
2. 递归遍历 `raw` 中 dict、list 的字符串值;
3. 跳过键名为 `base64` 的值,避免扫描大块编码数据;
4. 使用附件完整文件名做不区分大小写的子串匹配;
5. 仅命中的附件进入 `attachments`,按 `internal_uri` 去重;
6. 没有 session 或没有命中时返回 `attachments: []`
响应附件格式:
```json
{
"type": "image",
"name": "simple-multimodal-image.png",
"internal_uri": "file:///home/tom/memory-gateway/tests/simple-multimodal-image.png"
}
```
episode 是 session 级记忆,因此只能在同一 session 的附件中按文件名匹配,不能
证明具体附件是向量召回的直接来源。
## 删除与隔离
- 所有附件查询必须同时匹配 `user_id``session_id`
- 删除 `/resources` 时,对应附件映射设置 `deleted_at`
- 真实路径按用户明确要求直接出现在搜索结果中;
- 不改变资源列表和详情现有的 `resource://` 对外 URI。
## 测试
- 资源上传创建附件映射;
- 资源搜索仅在 raw 出现文件名时返回真实 URI
- raw 不含文件名时返回空附件数组;
- `/memories/add` 的 URI content 创建映射;
- `/memories/add` 的 base64 content 落盘并创建映射;
- 不扫描 raw 中的 base64 字段;
- 不返回其他用户同 session 的附件;
- 现有测试继续通过。