更新
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# AI 助手(MCP)只读数据查询:实现与部署
|
||||
|
||||
日期:2026-09-24。状态:已在本地一次性测试库完成实现与测试;未部署线上、未迁移线上数据库。
|
||||
日期:2026-09-24。状态:只读查询与业绩工具已上线;AI 后台浏览器(第 3 节末、第 5 节第 7 步)已在本地一次性测试库完成实现与测试,待同步上线并执行 `2026_09_24_ai_mcp_console.sql`。
|
||||
|
||||
对接方:行知 AI 工作助手(方案见行知项目 `docs/zyt-mcp-plan.md`)。员工在行知里用甄养堂后台账号密码绑定,之后 AI 按该账号自己的权限和数据范围只读查询甄养堂数据。
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
| `server/database/migrations/2026_09_24_ai_mcp.sql` | 新表 `zyt_ai_grant`、`zyt_ai_access_log`;“AI 助手”菜单及权限点 |
|
||||
| `server/tests/AiMcp*Test.php` | 单元测试、只读保护测试、HTTP 契约测试 |
|
||||
| `admin/src/api/ai_mcp.ts`、`admin/src/views/ai_mcp/` | 后台页面:AI 授权管理、AI 访问日志、AI 数据目录 |
|
||||
| `server/app/mcp/controller/ConsoleController.php`、`service/ConsoleService.php`、`server/database/migrations/2026_09_24_ai_mcp_console.sql` | AI 后台浏览器会话(`/mcp/console/open|close`)与权限点 `ai.mcp/console`(同日增补,见第 3 节末) |
|
||||
|
||||
## 2. 工作方式
|
||||
|
||||
@@ -62,6 +63,19 @@
|
||||
- 同时修复:`params` 里写的 `page`/`page_size` 自动当作分页;限流与每日行数计数在缓存读写出错时放行并记日志(文件缓存下并发调用可能读到写了一半的文件,之前会让整次查询变成“内部错误”);内部错误提示带上异常类型,便于对照服务器日志。
|
||||
- 授权接口:`POST /mcp/auth/grant`、`POST /mcp/auth/revoke`(Bearer)、`GET /mcp/auth/whoami`(Bearer),返回与后台一致的 `{code, show, msg, data}`;失败时 `data.reason` 为 `invalid_credentials / disabled / need_change_password / need_bind_wecom / no_ai_permission / locked / feature_disabled / ip_not_allowed / invalid_request`。
|
||||
|
||||
### AI 后台浏览器(2026-09-24 增补,`controller/ConsoleController.php`、`service/ConsoleService.php`)
|
||||
|
||||
MCP 工具只能查数据目录里开放的资源。为了让 AI 也能看后台网页上才有的内容,并在成员逐次批准下代为操作,行知在自己的服务器上运行一个内置浏览器(无界面 Edge),用成员已绑定的 AI 授权免密码登录本后台。本模块只负责换取和收回这个登录:
|
||||
|
||||
- `POST /mcp/console/open`(`Authorization: Bearer zyt_ai_…`,可带 `X-Xingzhi-Task-Id`):为该账号签发“AI 浏览器”专用终端(`terminal = 8`)的后台登录,返回 `token`、`expire_time`、`local_storage`(后台前端读取的 `like_admin_token`)和 `start_path`。令牌只写进行知服务器上的浏览器,不给模型、不进任务记录、不回传网页。
|
||||
- 沿用后台原有登录体系(`AdminTokenService::setToken` 与 `zyt_admin_session`),不改任何中间件;独立终端,不会挤掉该账号在电脑、手机、企微客服端的登录(关闭“多处登录”时也一样);同一账号的 AI 浏览器共用一个会话,已有有效会话时直接沿用。
|
||||
- 有效期默认 2 小时(`CONSOLE_TTL_MINUTES`);签发时清掉登录缓存,由浏览器第一次请求按它自己的出口 IP 重建,后台的登录 IP 校验照常生效。
|
||||
- 前提:AI 授权有效(与 MCP 相同的实时校验),且账号有新权限点 `ai.mcp/console`“允许 AI 使用后台浏览器”(默认不授予任何角色,root 自动拥有)。没有该权限返回 `code = 0`、`data.reason = no_console_permission`,不建会话;授权无效返回 401。
|
||||
- `POST /mcp/console/close`:作废该会话(到期时间改为过去并清登录缓存;文件缓存下连后台应用自己的缓存目录 `runtime/adminapi/cache` 一并清理)。行知关闭浏览器、空闲 15 分钟、打开满 2 小时、成员解除或重新绑定时调用;授权已撤销、过期或失去权限时也照样注销(只要令牌确实由本系统签发过)。
|
||||
- 自动收回:AI 授权被撤销或失效(后台撤销、改密、停用、删除、过期)时,同时作废该账号的 AI 浏览器会话;账号失去 `ai.mcp/access` 或需要先绑定企微时,授权保留,但会话立即作废。
|
||||
- 审计:`zyt_ai_access_log` 记录 `console.open`(成功或拒绝及原因、行知任务号)和 `console.close`。浏览器里的操作走后台自己的接口和操作日志,登录终端为 8,可与员工本人的操作区分。
|
||||
- 这是完整的后台登录(按账号自己的菜单权限和数据范围),**不经过** MCP 的只读事务和脱敏。行知侧的保护:只允许打开本后台域名;AI 触发的每个写请求(POST/PUT/PATCH/DELETE 等)先在任务里请成员批准(只能“允许本次”);成员在实时画面里亲自操作的直接提交;无人触发的后台提交、跳到其他网站、WebSocket、下载一律拦截;组织可把后台设为只读(任何写请求都拦截);给模型的页面文字隐藏手机号和身份证号;截图只给成员看。
|
||||
|
||||
## 4. 配置(服务器私密 `server/.env`)
|
||||
|
||||
```ini
|
||||
@@ -79,6 +93,8 @@ LOG_RETENTION_DAYS = 180 ; 访问日志保留天数(《网络安全法》要
|
||||
LOCK_FAILURES = 5
|
||||
LOCK_MINUTES = 30
|
||||
REQUIRE_PASSWORD_CHANGED = true
|
||||
CONSOLE_ENABLED = true ; AI 后台浏览器总开关(另需角色勾选 ai.mcp/console);紧急停用设为 false
|
||||
CONSOLE_TTL_MINUTES = 120 ; AI 浏览器后台会话有效期(10–480 分钟)
|
||||
```
|
||||
|
||||
限流和锁定使用系统缓存;线上建议 `cache.driver = redis`(文件缓存下计数为近似值)。服务在负载均衡或 CDN 之后时,需先让 `request()->ip()` 取到真实客户端 IP,`ALLOWED_IPS` 才有意义。
|
||||
@@ -91,6 +107,7 @@ REQUIRE_PASSWORD_CHANGED = true
|
||||
4. 在预发/测试库上以 root 账号运行 `php app/mcp/cli/probe.php --admin=<root 的 ID>` 与 `php app/mcp/cli/coverage.php`,确认没有 `writes`(只读保护拦截)结果、没有未覆盖的表;有的话在对应 `review/*.php` 把该资源改为待整改或改用只读 Logic,或用 `coverage.php --write-tables` 补数据表资源。
|
||||
5. `.env` 设置 `[AI_MCP] ENABLED = true` 与 `ALLOWED_IPS`;nginx 对 `/mcp` 与 `/mcp/auth/grant` 加 `limit_req`,并确认不缓冲响应。
|
||||
6. 在行知管理员页面配置组织连接器:MCP 地址 `https://admin.zhenyangtang.com.cn/mcp`,授权/撤销/身份接口为同域的 `/mcp/auth/grant|revoke|whoami`,工具名前缀关闭,只读工具自动放行。
|
||||
7. **AI 后台浏览器(同日增补)**:同步 `server/app/mcp/`(新增 `controller/ConsoleController.php`、`service/ConsoleService.php`,改动 `service/{GrantService,McpConfig}.php`);执行 `server/database/migrations/2026_09_24_ai_mcp_console.sql`(只新增一个按钮权限点,可重复执行);在“权限管理 > 角色”给需要的角色勾选“允许 AI 使用后台浏览器”(`ai.mcp/console`)。行知侧已配置好组织连接器的“管理后台”:地址 `https://admin.zhenyangtang.com.cn/admin/`,免登录接口 `/mcp/console/open`,退出接口 `/mcp/console/close`,修改逐次审批。
|
||||
|
||||
新增后台接口或页面后:运行 `php app/mcp/cli/catalog.php --write` 重新盘点,并在 `review/` 给新资源写结论;`php server/tests/AiMcpUnitTest.php` 会报告尚未审核的只读接口数量。
|
||||
|
||||
@@ -102,6 +119,7 @@ php server/tests/AiMcpUnitTest.php
|
||||
AI_MCP_TEST_MYSQL=1 php server/tests/AiMcpReadOnlyTest.php
|
||||
AI_MCP_TEST_MYSQL=1 AI_MCP_TEST_BASE_URL=http://127.0.0.1:8099 php server/tests/AiMcpHttpContractTest.php
|
||||
AI_MCP_TEST_MYSQL=1 AI_MCP_TEST_BASE_URL=http://127.0.0.1:8099 php server/tests/AiMcpPerfTest.php # 业绩工具:口径、排名、数据范围、走势、图表、权限回退
|
||||
AI_MCP_TEST_MYSQL=1 AI_MCP_TEST_BASE_URL=http://127.0.0.1:8099 php server/tests/AiMcpConsoleTest.php # AI 后台浏览器(需先执行 2026_09_24_ai_mcp_console.sql)
|
||||
php app/mcp/cli/probe.php --admin=<ID> [--only=tcm.] # 默认不执行不开放的、会调外部接口的资源
|
||||
php app/mcp/cli/coverage.php
|
||||
```
|
||||
@@ -112,12 +130,18 @@ php app/mcp/cli/coverage.php
|
||||
|
||||
业绩工具(同日增补):`AiMcpPerfTest` 通过(医助诊金计入部分退款单、医生成交金额不计;取消单都不计;经理看两名下属医助/医生、医助只看自己;部门名称解析;按日/周/月走势;图表输出;排行榜权限点停用时回退到 Tab 权限);原有三个测试与探测脚本无回归;行知端到端(真实行知后端 + MCP 客户端 + 本模块)确认模型拿到 4 个业绩工具、图表代码块完整到达并存入回答。上线只需同步 `server/app/mcp/`(新增 `service/PerfTools.php`,改动 `service/{Tools,Catalog,Dispatcher,RateLimiter,Protocol}.php`、`catalog/review/{stats.php,README.md}`),不涉及数据库。第二次修正(指标命名、单人对账、统计明细翻页)只改 `service/{PerfTools,Tools,Protocol}.php` 与 `tests/AiMcpPerfTest.php`,四个测试全部通过。
|
||||
|
||||
AI 后台浏览器(同日增补):`AiMcpConsoleTest` 通过——无效授权 401;没有 `ai.mcp/console` 被拒且不建会话;签发 terminal=8 会话、有效期 2 小时,关闭“多处登录”时电脑端登录不受影响;换来的令牌能直接访问后台接口;再次打开沿用同一会话;关闭后令牌立即失效、重复关闭无害、重新打开换新令牌;撤销授权同时作废会话;授权失效后仍能注销,未知令牌不能;失去 `ai.mcp/access` 时会话立即作废而授权保留;审计含行知任务号和拒绝记录。原有四个测试无回归。行知联调:行知服务器上的内置浏览器(Playwright 驱动 Edge)用本接口免密码打开本地实例的后台页面并读取内容,AI 触发的写请求停在审批(本地联调时后台前端的生产构建写死了线上接口地址,用请求改写指向本地实例)。
|
||||
|
||||
## 7. 回退
|
||||
|
||||
把 `.env` 的 `[AI_MCP] ENABLED` 改为 `false`:`/mcp` 与授权接口立即返回 503,行知侧查询自动失败并提示。新表和菜单保留即可,不需要回滚数据库。需要彻底停用时,在“AI 授权管理”撤销全部授权。
|
||||
|
||||
只停用 AI 后台浏览器:`.env` 设 `[AI_MCP] CONSOLE_ENABLED = false`(不能再打开新会话),或在角色里取消“允许 AI 使用后台浏览器”。已打开的会话在行知空闲 15 分钟或到期(最长 2 小时)时收回;需要立即收回时,在“AI 授权管理”撤销相关成员的授权。
|
||||
|
||||
## 8. 已知限制与后续
|
||||
|
||||
- 后台部分接口本身缺少逐条权限校验或存在扩大范围的参数(见行知方案文档“zyt 安全前置整改”一节)。MCP 已按“默认拒绝 + 参数白名单 + 行守卫”规避,但后台网页仍受影响,建议另行修复。
|
||||
- 未登记为菜单权限点的接口在 MCP 中一律不开放;如需开放,先按 `2026_08_12_call_transcription_permissions.sql` 的做法登记权限点。
|
||||
- 阶段三可选:接入 IAM(Keycloak)授权码 + PKCE 绑定,密码不再经过行知。
|
||||
- AI 后台浏览器按请求方法区分读写:行知只拦截非 GET/HEAD/OPTIONS 请求。后台若有用 GET 修改数据的接口,无法被审批拦住,建议这类接口改为 POST(导出下载在行知浏览器里已禁用)。
|
||||
- 取消角色的 `ai.mcp/console` 只阻止新开会话,不会立刻结束已打开的会话(见第 7 节)。
|
||||
|
||||
Reference in New Issue
Block a user