diff --git a/xiaoqu-agent-service/doc/xiaoqu-mcp-multi-product-agent-design.md b/xiaoqu-agent-service/doc/xiaoqu-mcp-multi-product-agent-design.md index cfd146c..1805370 100644 --- a/xiaoqu-agent-service/doc/xiaoqu-mcp-multi-product-agent-design.md +++ b/xiaoqu-agent-service/doc/xiaoqu-mcp-multi-product-agent-design.md @@ -10,7 +10,7 @@ xiaoqu-ai iOS / Android / Web ▼ xiaoqu-ai Gateway / Agent Runtime ───┐ │ 用户 MCP Token(仅服务端持有) -xiaoqu-operations-ai iOS / Android │ +xiaoqu-operations-ai iOS / Android / Web │ 产品登录 Token │ ▼ │ ops-assistant-service ───────────────┤ @@ -28,7 +28,7 @@ ops-assistant-service ───────────────┤ |---|---|---|---| | 总体 | xiaoqu-mcp | 当前仓库 `xiaoqu-agent-service` | 本文 | | 详细设计 | xiaoqu-ai iOS、Android、Web、服务端 Agent | `/Users/tonylai/xiaoquai/xiaoqu-ai`、`/Users/tonylai/jiaxk/xiaoqu-ai-android` | [`xiaoqu-ai-mcp-integration-design.md`](./xiaoqu-ai-mcp-integration-design.md) | -| 详细设计 | xiaoqu-operations-ai iOS、Android、服务端 Agent | `/Users/tonylai/jiaxk/xiaoqu-operations-ai` | [`xiaoqu-operations-ai-mcp-integration-design.md`](./xiaoqu-operations-ai-mcp-integration-design.md) | +| 详细设计 | xiaoqu-operations-ai iOS、Android、Web、服务端 Agent | `/Users/tonylai/wangheng/xiaoqu-operations-ai` | [`xiaoqu-operations-ai-mcp-integration-design.md`](./xiaoqu-operations-ai-mcp-integration-design.md) | 本文只定义两个产品必须共同遵守的架构和安全契约。项目代码落点、配置、Capability 映射和项目级验收只写在对应详细设计中。 @@ -124,7 +124,7 @@ Accept: application/json, text/event-stream |---|---|---| | 1 | 确认两个产品的用户映射与实际 scope | scope 已存在并绑定所需工具,不依赖客户端身份字段 | | 2 | 完成 xiaoqu-ai 服务端接入 | iOS、Android、Web 经同一服务端适配器调用 | -| 3 | 完成 xiaoqu-operations-ai 服务端接入 | 运营 iOS、Android 经 ops-assistant-service 调用 | +| 3 | 完成 xiaoqu-operations-ai 服务端接入 | 运营 iOS、Android、Web 经 ops-assistant-service 调用 | | 4 | 跨用户、跨租户和跨产品回归 | 无 Token 泄露、串号、越权或副作用自动重放 | ## 8. 总体验收与回退 diff --git a/xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md b/xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md index e055a1d..534f730 100644 --- a/xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md +++ b/xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md @@ -2,111 +2,180 @@ ## 1. 目标与边界 -本文是 [`xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计`](./xiaoqu-mcp-multi-product-agent-design.md) 的运营项目落地设计。 +本文是 [`xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计`](./xiaoqu-mcp-multi-product-agent-design.md) 的运营项目落地设计,代码基线为 `/Users/tonylai/wangheng/xiaoqu-operations-ai`。 -`ops-assistant-service` 是运营项目唯一 MCP Client,同时服务运营 iOS 和 Android,并保留现有编排: +运营 iOS、Android、Web 共用 `ops-assistant-service` 中的一套 MCP Client: ```text -运营 iOS / Android +iOS / Android / Web │ 产品登录 Token ▼ -Router → Planner → Executor → Answer - │ - ▼ - xiaoqu-mcp +ops-assistant-service REST + │ + ▼ +Router → Resolver → Planner → Executor → Answer + │ + ▼ + Capability Registry + │ 固定 adapter_id + ▼ + xiaoqu-mcp Adapter + │ 用户 MCP Token(仅服务端持有) + ▼ + xiaoqu-agent-service /mcp ``` -客户端不新增 MCP 代码。项目目录:`/Users/tonylai/jiaxk/xiaoqu-operations-ai`。 +WebSocket 只负责会话进度和完成通知,不承载 MCP 调用。客户端不新增 MCP 代码,不保存用户 MCP Token、MCP Session ID 或 `X-Open-Key`。 ## 2. 当前代码依据 -- `services/ops-assistant-service` 是 FastAPI + Python 3.12 服务。 -- 当前依赖中没有 MCP SDK,也没有 `tools/list` / `tools/call` 客户端。 -- `src/ops_assistant_service/capability_registry.py` 维护静态 `CAPABILITY_REGISTRY`。 -- `src/ops_assistant_service/executor.py` 当前执行 `customer_issues.*`、`smartclean.query` 等本地 Capability。 -- 用户身份来自 Gateway JWT,并通过 `ops_identity_mappings` 解析租户、用户和 `scope_tags`。 -- 最小接入点是 Executor 的工具适配层,不新增第二套编排器。 +- `web/ops-assistant/src/api.ts` 通过 `/api/v1/ops-assistant/*` REST 提交消息和读取业务数据。 +- `web/ops-assistant/src/ConversationPage.tsx` 使用 WebSocket 订阅会话进度,消息提交仍走 REST。 +- `services/ops-assistant-service` 是 FastAPI + Python 3.12 服务,现有 `httpx` 足够实现 MCP HTTP Client。 +- `worker.py` 同时运行旧流水线和 `contextual_fulfillment_v2`;新流水线包含 Router、Resolver、Planner、Recovery、Executor、Answer。 +- `capability_registry.py` 维护静态 `CAPABILITY_REGISTRY`、冻结的 Capability Contract 和 `adapter_id` 白名单。 +- 旧 `executor.py` 与新 `contextual_execution.py` 最终都调用 `execute_registered_tool()`。 +- 当前 Registry 包含 `customer_issues.*`、`vision_analyze`、`work_items.list`、`read_only_todos.list`、`knowledge_entries.search`,已不包含 `smartclean.query`。 +- Gateway JWT 在入口解析为共享身份,再通过 `ops_identity_mappings` 映射为运营 `tenant_id`、`user_id` 和 DataScope;后台 Worker 执行时使用运营 UUID。 -## 3. 配置设计 +因此不在 Web、路由层或某一条流水线中增加专用 MCP 分支。唯一接入点是两套流水线共用的 Capability Adapter 执行边界。 -在 `src/ops_assistant_service/config.py` 的 `Settings` 中增加以下 `OPS_ASSISTANT_` 配置: +## 3. 设计决策 + +### 3.1 统一 Adapter + +在 `CAPABILITY_REGISTRY` 中注册稳定的本地 Capability 和固定 `adapter_id`,由 `execute_registered_tool()` 将 MCP Adapter ID 路由到 `XiaoquMcpAdapter`。 + +```text +Planner 选择本地 Capability + → 校验冻结的 Capability Contract + → 校验输入 schema、scope_tags、DataScope + → execute_registered_tool(adapter_id) + → XiaoquMcpAdapter + → MCP tools/call +``` + +不得把远端 `tools/list` 原样注册给 Planner。`tools/list` 只用于启动或联调时验证远端工具是否与本地冻结契约一致。 + +### 3.2 显式依赖传递 + +`XiaoquMcpRuntime` 由 `worker.build_pipeline_runtime()` 创建,并通过显式参数传给旧 `run_executor_tick` 和新 `run_contextual_executor_tick`。禁止用模块级全局变量或从请求上下文隐式读取用户。 + +`PipelineRuntime.close()` 负责关闭 MCP HTTP Client 和清理进程内 Session 缓存。未启用 MCP 时传入 `None`,现有本地 Capability 保持原行为。 + +## 4. 配置设计 + +在 `src/ops_assistant_service/config.py` 的 `Settings` 增加以下 `OPS_ASSISTANT_` 配置: | 变量 | 默认值 | 说明 | |---|---|---| | `XQ_MCP_ENABLED` | `false` | 是否启用 MCP Capability | | `XQ_MCP_URL` | unset | xiaoqu-mcp 内网地址 | -| `XQ_MCP_OPEN_KEY` | unset | Token 初始化凭证,类型使用 `SecretStr` | -| `XQ_MCP_SCOPE_CODE` | `operation` | 服务端固定 scope,禁止由 Planner 或客户端选择 | +| `XQ_MCP_OPEN_KEY` | unset | Token 初始化凭证,使用 `SecretStr` | +| `XQ_MCP_SCOPE_CODE` | `operation` | 服务端固定 scope,禁止由客户端或模型选择 | | `XQ_MCP_TIMEOUT_SECONDS` | `10` | MCP 请求超时 | -启用时必须同时提供 URL、Open Key 和 scope。`operation` 已存在于 dev 种子,其他环境启用前仍需核对实际 scope 和工具绑定;生产配置不得写入仓库。 +启用时 URL、Open Key 和 scope 必填。`operation` 已存在于 xiaoqu-mcp dev 种子;development、test、staging、production 均须分别核对真实 scope 和工具绑定。凭证只由部署 Secret 注入。 -## 4. 最小模块 +## 5. 最小模块 -只新增两个模块: +### 5.1 `mcp_token_provider.py` -- `src/ops_assistant_service/mcp_client.py` - - 使用现有 `httpx` 完成 JSON-RPC、Session ID、Bearer Header、超时和错误映射。 - - 按租户、`user_id` 和 scope 隔离 MCP Session。 -- `src/ops_assistant_service/mcp_token_provider.py` - - 接收已验证的 `SharedIdentity`。 - - 从受信任用户资料源取得 mobile/email 并调用 `ensure`。 - - 按租户、`user_id` 和 scope 缓存 Token;401 时只刷新当前用户一次。 - - 不接受客户端或 Planner 传入的身份字段作为覆盖参数。 +输入为 Worker 已持有的运营 `tenant_id`、`user_id` 和固定 scope: -不引入通用 MCP 框架、不新增 Agent,也不直连 xiaoqu-mcp 数据库。 +1. 从 `ops_identity_mappings` 反查可信的 `shared_tenant_id`、`shared_user_id`。 +2. 通过现有只读共享用户 Port 获取权威 mobile;权威数据源存在 email 时可作为备用。 +3. 使用固定 `scopeCode` 调用 `/api/open/token/ensure`。 +4. 按运营租户、运营用户、共享用户和 scope 缓存 Token。 -## 5. Capability 映射 +现有 `SharedUserPort` 尚未暴露 mobile/email,需要最小扩展其只读 DTO 和 PostgreSQL Adapter;不得复制用户表,也不得使用 Web、Planner、工具参数或模型输出中的身份字段。 -在 `capability_registry.py` 注册固定的本地 Capability,不把远端 `tools/list` 原样暴露给 Planner: +### 5.2 `mcp_client.py` -| 本地 Capability | MCP 工具 | 首期权限 | -|---|---|---| -| `xiaoqu.agent_count` | `agent.count` | read | -| `xiaoqu.agent_list` | `agent.list.query` | read | -| `xiaoqu.property_list` | `property.list.query` | read | +使用现有 `httpx.Client` 管理: -Planner 只能选择注册表中的名称和 schema。Executor 在调用前执行本地 Capability、用户 `scope_tags`、租户/DataScope 校验;xiaoqu-mcp 再执行 Token scope 和服务端数据权限校验。 +- `initialize`、`notifications/initialized`、`tools/list`、`tools/call`。 +- Bearer Header、`Mcp-Session-Id`、JSON-RPC ID 和响应校验。 +- 连接、读取、写入和总超时。 +- 按运营租户、运营用户和 scope 隔离 Session。 +- 关闭时释放 HTTP Client,不持久化 MCP Session。 -远端结果经过字段白名单投影后再进入 Answer。Token、Authorization Header 和未授权字段不得进入 execution trace。 +### 5.3 `mcp_adapter.py` -## 6. Executor 接入 +`XiaoquMcpAdapter` 只接收已经验证的 Capability Contract、运营身份和参数: -在 `executor.py` 增加 MCP 只读分支: +- 根据固定映射选择远端 MCP 工具。 +- 调用 Token Provider 和 MCP Client。 +- 校验远端响应并执行字段白名单投影。 +- 返回现有 Executor/Answer 可消费的字典,不返回 Token、Header 或原始未授权字段。 + +不引入通用 MCP 框架或第二套 Agent 编排器。 + +## 6. Capability 映射 + +首期只注册只读 Capability: + +| 本地 Capability | `adapter_id` | MCP 工具 | 权限 | +|---|---|---|---| +| `xiaoqu.agent_count` | `mcp.xiaoqu.agent_count.v1` | `agent.count` | read | +| `xiaoqu.agent_list` | `mcp.xiaoqu.agent_list.v1` | `agent.list.query` | read | +| `xiaoqu.property_list` | `mcp.xiaoqu.property_list.v1` | `property.list.query` | read | + +每项必须在 `CAPABILITY_REGISTRY` 中声明输入/输出 schema、风险、审批、超时重试、审计事件和结果模板,并将精确 `adapter_id` 加入 `EXECUTION_ADAPTER_IDS`。已有本地 Capability 不改名、不改 Adapter、不改变执行语义。 + +## 7. 身份与权限 + +最终授权取交集: ```text -validate_tool_input - → 校验本地 Capability 和参数白名单 - → 读取 SharedIdentity / DataScope - → mcp_token_provider 获取当前用户 Token - → mcp_client 调用 tools/call - → 结果字段投影 - → 现有 execution_trace / Answer +本地 Capability allowlist + ∩ 当前运营用户 scope_tags / DataScope + ∩ MCP Token scope + ∩ xiaoqu-mcp 用户、租户和行级数据范围 ``` -现有 `customer_issues.*` 和 `smartclean.query` 保持不变。MCP 不可用时返回稳定的 `TOOL_UNAVAILABLE`,不得回退公共 Token 或数据库直连。 +- REST 和 WebSocket 只负责验证产品登录态并建立运营身份。 +- Planner 只能选择 Registry 中的 Capability,不决定用户、租户或 scope。 +- Worker 必须在每次执行前确认 `ops_identity_mappings` 仍有效,不能仅依赖消息入库时的旧快照。 +- `tools/list` 与 `tools/call` 使用同一用户 Token 和 MCP Session;服务端在两个入口都强制鉴权。 -## 7. 开发顺序 +## 8. 异步执行与生命周期 -1. 核对目标环境中的 `operation` scope、工具绑定和用户资料映射。 -2. 增加 Settings 配置及启用校验。 -3. 实现 `mcp_token_provider.py` 与 `mcp_client.py`,覆盖 JSON-RPC 和 Session 单元测试。 -4. 注册 3 个只读 Capability 并接入 Executor。 -5. 增加用户隔离、scope 拒绝、401 重连、Token 脱敏和 MCP 不可用测试。 -6. 在 development/test 环境联调,通过后再进入 staging。 +Web、iOS、Android 提交消息后获得 `202`;Worker 在后台完成 Agent 和工具执行,再由消息查询或 WebSocket 返回进度与结果。 -## 8. 项目验收 +- `build_pipeline_runtime()` 创建一个进程级 `XiaoquMcpRuntime`,但 Token 和 Session 按用户隔离。 +- Worker 重启后 Session 不恢复;首次调用重新 `ensure` 和 `initialize`。 +- 用户映射失效、scope 版本变化或 MCP 401 时,只清理该用户缓存。 +- MCP 401:重新 `ensure` 和连接一次;只读调用最多重试一次。 +- MCP 403:直接返回权限不足,不刷新 Token。 +- 超时、协议错误或服务不可用:映射为稳定的 `TOOL_UNAVAILABLE`,并让当前 Execution Attempt 进入可审计失败状态。 +- 不自动重放写操作;首期不接写工具。 -- 运营 iOS、Android 继续只调用 `ops-assistant-service`,客户端无 MCP Token。 +## 9. 开发顺序 + +1. 核对各环境 `operation` scope、三项工具绑定和用户映射数据源。 +2. 扩展只读 SharedUser Port,完成运营 UUID 到 mobile/email 的可信解析测试。 +3. 增加 Settings 配置、启用校验和 `XiaoquMcpRuntime` 生命周期。 +4. 实现 Token Provider、MCP Client 和 Adapter 的协议单元测试。 +5. 注册三项本地 Capability,并让旧 Executor 与 contextual v2 共用同一 Adapter。 +6. 覆盖用户隔离、租户隔离、scope 拒绝、401、403、超时、脱敏和 Worker 重启测试。 +7. 分别从 iOS、Android、Web 提交对话请求,验证异步进度和最终结果一致。 + +## 10. 项目验收 + +- iOS、Android、Web 均只调用 `ops-assistant-service`,客户端没有 MCP Token、Session ID 或 Open Key。 +- Web 消息走 REST,WebSocket 仅订阅进度/完成通知。 +- 旧流水线和 `contextual_fulfillment_v2` 对同一 MCP Capability 使用同一个 Adapter。 - 用户 A/B、不同租户和 scope 的 Token 与 MCP Session 不互用。 -- Planner 不能选择未注册的远程工具,Executor 不能绕过 `scope_tags` 和 DataScope。 -- `tools/list`、`tools/call` 使用同一用户 Session 和 Bearer Token。 -- MCP 401 只刷新当前用户;只读请求最多重试一次。 -- 测试、日志、trace、配置和 Git 历史中均无凭证明文。 +- Planner 不能选择未注册工具;冻结 Contract、`scope_tags`、DataScope 或 MCP scope 任一不满足即拒绝。 +- 现有七项本地 Capability 的契约和行为不受影响。 +- MCP 401 只刷新当前用户;MCP 不可用时不回退公共 Token、数据库直连或 xiaoqu-ai Agent。 +- 测试、日志、trace、配置和 Git 历史中无凭证明文。 -## 9. 当前不做 +## 11. 当前不做 -- 不把 MCP Client 放进运营 iOS 或 Android。 +- 不在运营 iOS、Android、Web 中实现 MCP Client。 - 不动态注册全部远程工具。 -- 不在首期引入写工具;写工具需另行设计审批、幂等和补偿协议。 -- 不复制 xiaoqu-mcp 数据库,不调用 xiaoqu-ai Agent 代查数据。 +- 不恢复或假定已删除的 `smartclean.query`。 +- 不复制 xiaoqu-mcp 或共享用户数据库。 +- 不在首期引入写工具;写工具需另行审批幂等、确认和补偿协议。