diff --git a/docs/superpowers/specs/2026-06-15-memory-search-upstream-options-design.md b/docs/superpowers/specs/2026-06-15-memory-search-upstream-options-design.md new file mode 100644 index 0000000..570535d --- /dev/null +++ b/docs/superpowers/specs/2026-06-15-memory-search-upstream-options-design.md @@ -0,0 +1,145 @@ +# Memory Search 上游参数增量兼容设计 + +## 目标 + +扩展 Memory Gateway 的 `POST /memories/search`,在保留现有用户鉴权、 +`scope` 搜索编排、资源隔离、软删除和覆盖修改能力的前提下,支持上游 +搜索接口的全部请求选项。 + +本次只修改 `/memories/search`,不新增 `/memories/get`,也不提供上游路径的 +线协议兼容接口。 + +## 请求模型 + +保留现有字段: + +- `user_id`:必填,始终用于 Gateway 用户鉴权和本地数据隔离。 +- `user_key`:必填,用于 Gateway 用户鉴权。 +- `conversation_id`:可选,供 `current_chat` scope 生成 session 过滤条件。 +- `query`:必填。 +- `scope`:保留 `current_chat`、`resources`、`all_user_memory`。 +- `app_id`、`project_id`:默认 `default`。 + +新增或扩展字段: + +- `agent_id`:可选。存在时,上游搜索使用 `agent_id`;不存在时使用 + Gateway 鉴权用户的 `user_id`。请求中不会同时向上游发送两种 owner ID。 +- `method`:支持 `keyword`、`vector`、`hybrid`、`agentic`,默认 `hybrid`。 +- `top_k`:支持 `-1` 或 `1..100`,保留 Gateway 默认值 `8`。 +- `radius`:可选,范围 `0..1`。 +- `include_profile`:布尔值,默认 `true`。 +- `enable_llm_rerank`:布尔值,默认 `true`。 +- `filters`:可选对象,支持上游开放字段过滤 DSL,包括嵌套 `AND`、`OR`。 + +`agent_id` 只改变上游记忆 owner,不替代 Gateway 的 `user_id/user_key` 鉴权。 +这可以防止调用者绕过 Gateway 用户体系,同时允许同一已认证用户查询被授权 +使用的 agent memory。当前版本不新增 agent 权限表,因此仅校验 Gateway 用户 +凭据,不声明 agent 的独立所有权关系。 + +## 搜索编排与过滤器合并 + +每一次上游 search 调用都必须透传: + +- owner:`agent_id` 或 `user_id`,二选一; +- `query`; +- `method`; +- `top_k`; +- `radius`,仅在请求提供时发送; +- `include_profile`; +- `enable_llm_rerank`; +- `app_id`; +- `project_id`; +- 合并后的 `filters`。 + +现有 scope 继续生成内部 session 条件: + +- `current_chat`:`session_id = chat:{conversation_id}`; +- `resources`:按批次生成 `session_id in [...]`; +- `all_user_memory`:不生成 session 条件。 + +当请求同时提供自定义 `filters` 和 scope session 条件时,使用以下结构合并: + +```json +{ + "AND": [ + {"自定义过滤条件": "..."}, + {"session_id": "scope 生成的条件"} + ] +} +``` + +仅存在其中一个条件时直接使用该条件;两者都不存在时不发送 `filters`。 +Gateway 不解析或重写自定义过滤 DSL 的内部字段,由上游执行完整校验。 + +`agent_id` 与所有 scope 均可组合。对于没有对应数据的组合,上游自然返回空数组; +Gateway 不额外禁止这些组合,以保持接口简单并完整透传搜索能力。 + +## 响应标准化 + +继续返回 Gateway 的统一结构: + +```json +{ + "results": [] +} +``` + +每个结果新增 `memory_type`: + +| 上游数组 | `memory_type` | +|---|---| +| `episodes` | `episode` | +| `profiles` | `profile` | +| `agent_cases` | `agent_case` | +| `agent_skills` | `agent_skill` | +| `unprocessed_messages` | `unprocessed_message` | + +其余字段保持现状:`id`、`session_id`、`text`、`score`、`source_scope`、 +`resource_id`、`resource_uri` 和 `raw`。 + +profile 和 agent skill 等没有 `session_id` 的结果允许返回 `null`。资源映射只对 +能匹配当前用户资源 session 的结果生效,不泄露其他用户的内部资源 URI。 + +所有类型的结果继续按现有顺序执行: + +1. 合并各 scope 的结果; +2. 应用当前用户的 memory tombstone; +3. 按 memory ID 应用当前用户的 active override; +4. 返回统一结果。 + +## 错误处理 + +- 不合法的 `method`、`top_k` 或 `radius` 由 Gateway 请求模型返回 HTTP 422。 +- 上游过滤 DSL 错误和其他 HTTP 错误继续由现有 client 行为向外传播。 +- 不改变当前 `current_chat` 缺少 `conversation_id` 时跳过该 scope 的行为。 +- 不为 `agent_id` 引入新的数据库表或权限模型。 + +## 代码改动 + +- `core/api.py` + - 扩展 `SearchMemoriesRequest`。 + - 将新增参数传给 service。 +- `core/service.py` + - 扩展 `search_memories` 和 `_search_payload`。 + - 合并自定义 filters 与 scope filters。 + - 标准化结果时增加 `memory_type`。 +- `tests/test_gateway.py` + - 验证默认参数透传。 + - 验证全部自定义搜索选项透传。 + - 验证 agent owner 与用户鉴权身份分离。 + - 验证 filters 与 scope 条件使用 `AND` 合并。 + - 验证五类结果的 `memory_type`。 +- `README.md` + - 更新 `/memories/search` 参数和响应说明。 + +## 验收标准 + +1. 未提供新字段时,上游收到 `method=hybrid`、`include_profile=true`、 + `enable_llm_rerank=true`。 +2. 所有上游搜索选项均能通过 Gateway 请求并原样传递。 +3. `top_k=-1` 被接受,`top_k=0` 和范围外值被拒绝。 +4. 自定义 filters 不会覆盖 scope 的资源或聊天 session 隔离条件。 +5. 设置 `agent_id` 后,上游只收到 `agent_id`,Gateway 仍使用 + `user_id/user_key` 完成鉴权。 +6. 每个搜索结果包含准确的 `memory_type`。 +7. 现有 tombstone、override、资源 URI 隔离测试继续通过。