# 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 的附件; - 现有测试继续通过。