# 7-71 统一 AI 管理面板与 Pi Lab 功能接入 — 实施检查清单 状态:partial — P0/P1/P2 完成;P3/P4/P5 管理员侧 OpenHub-like AI 管理与逐用户配置完成,用户自助偏好与 LightRAG 写管理待后续 日期:2026-07-04 > 本清单与 `design/07-ai/process/7-71-unified-ai-management-control-plane-and-pi-lab-integration-v1.md` 的 §11 进度表保持同步。 > process 文档保留架构结论;本文件记录已完成的检查项供快速验证。 ## 已通过验收项 ### P0 — 合同与只读聚合 ✅ | 检查项 | 通过 | |---|---| | `/api/ai-settings/effective` GET 端点已注册 | ✅ | | `/api/ai-settings/access-scopes` GET 端点已注册 | ✅ | | `/api/ai-admin/access-scopes` GET 端点已注册 | ✅ | | 所有 AI settings/scopes 端点无 POST/PUT/DELETE | ✅ | | `effective_settings` 使用 `load_active_directory_grants` 生成 allowed roots | ✅ | | `load_model_policy_and_quota` 不读取 `allowed_roots_json` | ✅ | | `source_of_truth` 声明为 `"directory_grants"` | ✅ | | 静态 smoke 27/27 通过 | ✅ | ### P1 — Pi Lab history 入库 ✅ | 检查项 | 通过 | |---|---| | `persist_upsert_run` 写入 `ai_runtime_runs` | ✅ | | `persist_append_event` 写入 `ai_runtime_events` | ✅ | | `GET /api/page-ai/pi/sessions` 路由注册 | ✅ | | `GET /api/page-ai/pi/sessions/{id}` 路由注册 | ✅ | | `GET /api/page-ai/pi/sessions/{id}/events` 路由注册 | ✅ | | list_sessions 按 pi_lab profile + pi acp_runtime 过滤 | ✅ | ### P2 — Receipt 与 patch 入库 ✅ | 检查项 | 通过 | |---|---| | migration v9 新增 `ai_tool_events / ai_file_patches` | ✅ | | SQLite 与 Turso/libSQL store 均实现 append/list | ✅ | | Pi tool receipt 默认写 control-plane | ✅ | | 成功 patch 关联写入 `ai_file_patches` | ✅ | | JSONL 仅在 control-plane 写失败时作为 debug fallback | ✅ | | deny reason、diff summary、file version、citation count 可查询 | ✅ | | receipt 与 patch 按当前用户隔离 | ✅ | ### P3 — AI 管理中心与管理员模型写管理 ✅ | 检查项 | 通过 | |---|---| | `/admin/ai` 路由与管理员鉴权 | ✅ | | `/user/ai` 路由与用户鉴权 | ✅ | | 账户菜单有独立 AI 管理入口 → `/admin/ai` 或 `/user/ai` | ✅ | | AI 管理页面 SSR 渲染 OpenHub-like 左侧分页与多 section | ✅ | | Access Scopes 只读展示(无 POST/PUT/DELETE、无写表单) | ✅ | | Sessions & Receipts 显示真实 Pi session、receipt、patch 数量 | ✅ | | Providers & Models 管理写操作 | ✅ | | `/user/ai` 用户可操作设置 | ❌ | ### 独立入口保留检查 | 检查项 | 通过 | |---|---| | OpenHub `/page-ai/openhub/ai` | ✅ | | OpenHub admin guard 路由 | ✅ | | OpenHub status API | ✅ | | Pi Lab shell `/page-ai/pi` | ✅ | | Pi Lab event stream `GET /api/page-ai/pi/events` | ✅ | | Pi Lab start/send/abort 路由 | ✅ | ### P4 — 工具、Skills/MCP 管理 ✅ | 检查项 | 通过 | |---|---|---| | `GET/PUT /api/ai-admin/settings` 原子策略端点 | ✅ | | 模型 provider、allowed models、角色模型与 failover 写入 | ✅ | | MNote-owned tools allow/ask/deny 写入 | ✅ | | Skill 添加、描述、启停、删除 | ✅ | | MCP transport/command/url/network/secret refs 写入 | ✅ | | MCP 强制 facade-only 与 sandbox | ✅ | | 刷新后配置持久存在 | ✅ | | effective API 投影模型、Skills、MCP | ✅ | ### P5 — 逐用户 AI 配置 ✅ | 检查项 | 通过 | |---|---| | OpenHub `UserManagement`、模型/工具/Skill 权限实现已核验 | ✅ | | `GET /api/ai-admin/users` 用户列表 | ✅ | | `GET/PUT /api/ai-admin/users/{user_id}/settings` | ✅ | | 用户可独立选择管理员允许的模型和默认模型 | ✅ | | 用户工具策略只能相对全局降权 | ✅ | | 用户 Skill/MCP 只能在全局启用目录中开关 | ✅ | | 普通用户登录后的 effective API 应用 override | ✅ | | allowed roots 仍只来自 `directory_grants` | ✅ | | 用户抽屉采用 OpenHub-like 分组:模型按 provider、工具按 risk、Skill/MCP 开关、目录只读跳统一授权页 | ✅ | ## 后续项 | 项 | 状态 | 说明 | |---|---|---| | `/user/ai` 用户可操作偏好 | 未实现 | 当前只读 effective config,不能提升管理员权限 | | LightRAG source/index/status 写管理 | 未实现 | 继续使用唯一 knowledge provider 与现有 facade | | provider/skill/MCP 专用规范化表 | 未实现 | 当前原子策略存入 `ai_policies.model_policy_json`;出现独立查询、授权或生命周期需求时迁移 | ## 浏览器证据 - Pi Lab 原生抽屉:`tmp/7-71-pi-lab-browser-final.png` - AI 管理页:`tmp/7-71-ai-admin-final.png` - 模型写管理:`tmp/7-71-ai-admin-openhub-like-ui-final/models.png` - Skills/MCP 写管理:`tmp/7-71-ai-admin-openhub-like-ui-final/skills-mcp.png` - 逐用户 AI 配置:`tmp/7-71-ai-admin-openhub-like-ui-final/users.png` - 管理页运行时摘要:会话 1、回执 3、文件补丁 1 - Pi Lab 截图可见真实 mock 回复、citation、diff、abort,且 OpenHub 默认入口仍独立存在 - `mnote-e2e` 通过 control-plane `role=admin` 进入管理员模式;浏览器验收用 dev seed 临时确保测试账号为 admin - OpenHub 的 MCP 实际为用户工作区 `.opencode/mcp` 文件展示,并非 Admin per-user override;MNote 采用更严格的管理员注册 + 用户启停 + facade/sandbox 模型 ## 验证命令 ```bash # 静态 smoke(只读代码审计,无需启动服务器) node scripts/task-ai-management-control-plane-static-smoke.js MNOTE_AI_ADMIN_OUTPUT_DIR=tmp/7-71-ai-admin-openhub-like-ui-final \ node scripts/task-ai-management-browser-smoke.js node scripts/task-pi-lab-static-smoke.js CARGO_TARGET_DIR=/tmp/mnote-7-71-target cargo check -p mnote-web CARGO_TARGET_DIR=/tmp/mnote-7-71-target cargo test -p control-plane ai_tool_events CARGO_TARGET_DIR=/tmp/mnote-7-71-target cargo test -p control-plane ai_file_patches CARGO_TARGET_DIR=/tmp/mnote-7-71-target cargo test -p control-plane migrations CARGO_TARGET_DIR=/tmp/mnote-7-71-target cargo test -p mnote-web ai_settings CARGO_TARGET_DIR=/tmp/mnote-7-71-target cargo test -p mnote-web page_ai_pi CARGO_TARGET_DIR=/tmp/mnote-7-71-target cargo test -p mnote-web ai_admin # 运行时验证(需要 mnote-web 运行于 localhost:3000) # curl -s http://localhost:3000/api/ai-settings/effective | jq '{source: .sourceOfTruth, models: [.models[].id], rootsCount: (.allowedRoots | length)}' # curl -s http://localhost:3000/api/ai-settings/access-scopes | jq '.sourceOfTruth' # curl -s http://localhost:3000/api/ai-admin/access-scopes | jq '.sourceOfTruth' # curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/admin/ai # curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/user/ai ```