|
|
@@ -0,0 +1,265 @@
|
|
|
+# AI 改动说明 · mky-vent-base · 点选令牌 + 结构化错误 + 管理入口收敛
|
|
|
+
|
|
|
+> **对照网址**:
|
|
|
+> 主仓库 <http://39.97.59.228:8013/hrx/mky-vent-base/compare/master...fix/base-agent-token>
|
|
|
+> fork <http://39.97.59.228:8013/lizuo/mky-vent-base/compare/master...fix/base-agent-token>
|
|
|
+>
|
|
|
+> 依据:`/root/TFAgents/docs/基座(mky-vent-base)配合改动清单.md`(M1 / M2 / M3 三项必改)
|
|
|
+> 分支:`fix/base-agent-token` 基线:`b3ec299b`(交付时已 merge `upstream/master` → `f25e5319`)
|
|
|
+> 日期:2026-09-15 代码交付版本:`0b3c1857`;本说明随后追加(仓库 `docs/ai-changes/`),最终分支 tip 见提交清单
|
|
|
+> 状态:真实浏览器验收通过(M1 3/3、M2 5/5、M3 9/9);已交付主仓库同名分支,待人工审核合并;**未部署**
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 一、这次改了什么(提交清单)
|
|
|
+
|
|
|
+```
|
|
|
+(本提交) docs(ai-changes): 补本分支改动说明(本文件)
|
|
|
+0b3c1857 Merge upstream/master(b3ec299b..f25e5319)
|
|
|
+29d9be9e fix(ventAI): 验收发现并修复 is_admin 仅 watch 拉取导致管理员也看不到入口 + 注释补全
|
|
|
+aef76ebb feat(ventAI): 非管理员隐藏技能/子智能体/思考级别等全局变更入口 (M3)
|
|
|
+2c2673eb fix(ventAI): 点击解读路径复用 StreamError 结构化错误并转可读文案 (M2)
|
|
|
+21b2fc53 fix(ventAI): 点击解读 SSE 请求携带 X-Access-Token 鉴权头 (M1)
|
|
|
+b3ec299b ← 基线(当时的 upstream/master)
|
|
|
+```
|
|
|
+
|
|
|
+**改动面:6 个文件,+166 / −13**(其中约 100 行为审核注释,功能代码约 60 行;本说明文件不计入)。
|
|
|
+
|
|
|
+| 文件 | 改了什么 |
|
|
|
+|---|---|
|
|
|
+| `src/views/ventAI/dataPicker/api.ts` | M1 请求头补令牌;M2 复用结构化错误解析 |
|
|
|
+| `src/views/ventAI/dataPicker/useDataPicker.ts` | M2 新增 `formatClickError()`,把 429/413/404 转中文文案 |
|
|
|
+| `src/views/ventAI/manageAssistent/api.ts` | M2 导出 `throwStreamHttpError`;M3 新增 `MeInfo` 与 `getMe()` |
|
|
|
+| `.../manageAssistent/components/AiAssistantModal.vue` | M3 拉取并持有 `isAdmin`,透传给两个子组件 |
|
|
|
+| `.../components/chatModal/TaskListPanel.vue` | M3 按 `isAdmin` 过滤「技能 / 子智能体」入口 |
|
|
|
+| `.../components/chatModal/ChatInputArea.vue` | M3 非管理员隐藏「思考级别」切换控件 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 二、逐项说明:改了什么、为什么这么改、与原来的差别
|
|
|
+
|
|
|
+### M1 · 点选解读补 `X-Access-Token`
|
|
|
+
|
|
|
+**位置**:`src/views/ventAI/dataPicker/api.ts`(`ssePost()`)
|
|
|
+
|
|
|
+**原来的行为**:
|
|
|
+```ts
|
|
|
+headers: { 'Content-Type': 'application/json' }, // 完全没有令牌
|
|
|
+```
|
|
|
+点选解读是基座里**唯一**不带令牌调 TfAgents 的路径。后果:TfAgents 无法识别是谁在用,
|
|
|
+只能把消耗记到兜底账号 `click-anonymous`,**做不到「按登录人员记账」**(决策 D5 / B1-20 的明确要求)。
|
|
|
+
|
|
|
+**现在的行为**:
|
|
|
+```ts
|
|
|
+headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ 'X-Access-Token': getToken(), // 与对话链路 manageAssistent/api.ts:215 同名同源
|
|
|
+},
|
|
|
+```
|
|
|
+
|
|
|
+**关键设计点(审核重点)**:
|
|
|
+- 令牌是**可选**语义:`getToken()` 为空时**仍照常发请求**,没有加任何「未登录就拦截 / 跳登录」的判断。
|
|
|
+ TfAgents 侧刻意保留 `click-anonymous` 兜底,若在此强制登录,会把未登录/令牌过期场景直接变成不可用。
|
|
|
+- 头名 `X-Access-Token` 与仓库既有写法一致;TfAgents 读小写 `x-access-token`,HTTP 头不区分大小写。
|
|
|
+- 该请求用 `fetch` + `ReadableStream`(非 `EventSource`),故可自定义请求头,无技术障碍。
|
|
|
+
|
|
|
+**与原来的差别**:请求头由「只有 Content-Type」变为「Content-Type + X-Access-Token」;
|
|
|
+请求体、返回值 `{thread_id, session_id}`、事件解析**完全没动**。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### M2 · 点选错误改用结构化解析
|
|
|
+
|
|
|
+**位置**:`manageAssistent/api.ts`(导出解析器)、`dataPicker/api.ts`(调用)、`useDataPicker.ts`(渲染文案)
|
|
|
+
|
|
|
+**原来的行为**:
|
|
|
+```ts
|
|
|
+if (!response.ok) {
|
|
|
+ throw new Error(`HTTP错误: ${response.status}`); // 用户只能看到「HTTP错误: 429」
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**现在的行为**:三步,全部复用仓库既有能力,**没有新写第二套解析器**。
|
|
|
+1. `throwStreamHttpError` 由模块内私有改为 `export`(**函数体零改动**);
|
|
|
+2. 点选链路改为 `await throwStreamHttpError(response)` —— 它解析响应体的 `detail`
|
|
|
+ (字符串或 `{ code, message, retry_after, limit_tokens, spent_tokens }`),抛出带
|
|
|
+ `status / retryAfter / limitTokens / spentTokens` 的 `StreamError`;
|
|
|
+3. `useDataPicker.ts` 新增本地 `formatClickError()`,把结构化错误渲染成中文:
|
|
|
+
|
|
|
+| 场景 | 展示文案 |
|
|
|
+|---|---|
|
|
|
+| 429 且带 `retry_after` | 服务端 message + `(约15分钟后恢复)`(不足 1 分钟显示「不到 1 分钟」) |
|
|
|
+| 429 无 `retry_after` | 服务端 message + `(请稍后再试)` |
|
|
|
+| 413 且服务端无文案 | `附件体积超过上限,请减小文件后重试` |
|
|
|
+| 404 且服务端无文案 | `会话不存在或无权访问` |
|
|
|
+| 其它 / 普通 Error | 保持原样 `e?.message \|\| '请求失败'` |
|
|
|
+
|
|
|
+**为什么必须改**:TfAgents 之后对 429(额度/并发闸)、413(体积超限)、404(会话归属)返回结构化错误体
|
|
|
+(D6 / B1-15 / B1-19 / B1-08),前端必须能翻译成人话。
|
|
|
+
|
|
|
+**与原来的差别**:错误提示由「HTTP错误: 429」变为可读中文;**兜底路径不变**
|
|
|
+(非 `StreamError` 的异常仍原样透出 message),不会把网络中断等场景改坏。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### M3 · 管理入口按 `is_admin` 收敛(已定案「方案 B」)
|
|
|
+
|
|
|
+**背景**:TfAgents 按决策 D1 / D9 把 9 个「全局变更」端点收归管理员(白名单在其 `.env`,先留空)。
|
|
|
+基座原先对这些入口**没有任何角色判断**,非管理员点了才吃 403。
|
|
|
+
|
|
|
+**后端契约**(TfAgents 侧,B2-01 追加需求):
|
|
|
+```
|
|
|
+GET /api/me (需认证:X-Access-Token)
|
|
|
+→ 200 { "username": "...", "is_admin": true|false, "admin_configured": true|false }
|
|
|
+→ 401 未登录 / 令牌无效
|
|
|
+```
|
|
|
+基座侧对应 URL 为 `/ventAI/api/me`(`/ventAI` 反代到 TfAgents)。
|
|
|
+
|
|
|
+**基座四处改动**:
|
|
|
+
|
|
|
+1. **`manageAssistent/api.ts`** 新增 `MeInfo` 与 `getMe()`:
|
|
|
+ ```ts
|
|
|
+ export const getMe = () => defHttp.get<MeInfo>(
|
|
|
+ { url: Api.me },
|
|
|
+ { isTransformResponse: false, errorMessageMode: 'none' }
|
|
|
+ );
|
|
|
+ ```
|
|
|
+ - **不手写请求头**:走 `defHttp` 时 axios 拦截器自动附 `Authorization` 与 `X-Access-Token`
|
|
|
+ (`src/utils/http/axios/index.ts:209-216`)。
|
|
|
+ - `errorMessageMode: 'none'` 是**刻意加的**:`/api/me` 在 TfAgents 上线前返回 404,
|
|
|
+ 若用默认错误模式,**每次打开 AI 弹框都会弹红色报错**,会被误判为故障。
|
|
|
+
|
|
|
+2. **`AiAssistantModal.vue`** 新增 `isAdmin` / `adminConfigured` 两个 ref 与 `fetchMe()`,
|
|
|
+ 并在**两处**触发:`onMounted` 一次 + `visible` 的 watch 里一次。
|
|
|
+ - 初值 `isAdmin = false`:接口返回前先按非管理员渲染,避免管理入口「闪现」给非管理员。
|
|
|
+ - 失败降级为**保守失败**:未登录 / 接口未上线 / 网络失败 → 一律 `isAdmin = false`(隐藏入口)。
|
|
|
+ - **`fetchModelMsg()` 未做任何改动**:思考级别的**读取**对所有人保持开放,只收敛「切换」。
|
|
|
+
|
|
|
+3. **`TaskListPanel.vue`**:`optionItems` 由普通数组改为 `computed` 并按 `isAdmin` 过滤。
|
|
|
+ - 「技能」「子智能体」→ 仅管理员可见;「新建任务」「定时任务」→ 对所有人开放
|
|
|
+ (定时任务属个人功能,不在收敛范围内)。
|
|
|
+ - 用 `computed` 而非普通数组,是为了 `isAdmin` 异步返回后能**自动重算**,无需刷新页面。
|
|
|
+
|
|
|
+4. **`ChatInputArea.vue`**:仅给 `.btn-think-wrapper`(思考级别切换控件)加 `v-if="isAdmin"`。
|
|
|
+ - **只包住这一个块**:左右分隔线与发送按钮保持原样,不改按钮区布局。
|
|
|
+ - `handleSelectThinkLevel` / `thinkLevelOptions` / `initialThinkLevel` 的 watch 均未改动。
|
|
|
+
|
|
|
+**与原来的差别**:
|
|
|
+
|
|
|
+| | 原来 | 现在 |
|
|
|
+|---|---|---|
|
|
|
+| 非管理员看到「技能」入口 | 是 | **否** |
|
|
|
+| 非管理员看到「子智能体」入口 | 是 | **否** |
|
|
|
+| 非管理员看到「思考级别」切换 | 是 | **否** |
|
|
|
+| 非管理员看到「定时任务」 | 是 | 是(不变) |
|
|
|
+| 思考级别读取(`getModelMsg`) | 开放 | 开放(不变) |
|
|
|
+| `/api/me` 不可用时 | — | **保守隐藏**(不是全部放开) |
|
|
|
+
|
|
|
+> ⚠️ **这是界面可见性,不是安全边界**:真正的拦截在 TfAgents 后端。前端隐藏只是让非管理员
|
|
|
+> 不用「点了才知道没权限」,不能替代后端鉴权。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 三、浏览器真实验收(Playwright + 真实 Chromium)
|
|
|
+
|
|
|
+**方法**:用基座自带 **mock 登录**(`/?mock-login=1`)取得**真实登录令牌**,再驱动真实页面。
|
|
|
+点选链路用临时验收页挂载**真实的 `v-data-picker` 指令**(`mode: 'click'`),
|
|
|
+AI 弹框用临时验收页挂载**真实的 `AiAssistantModal` 组件**;`/api/me` 用请求拦截注入不同角色响应。
|
|
|
+**临时页与临时路由已在验收后全部删除**,工作区只剩 6 个目标文件(本说明文件除外)。
|
|
|
+
|
|
|
+### 验收结果
|
|
|
+
|
|
|
+| 项 | 断言 | 结果 |
|
|
|
+|---|---|---|
|
|
|
+| M1-1 | 点击后确实向点选解读接口发出请求(`/ventAI/api/interpret/click/tun`) | ✅ |
|
|
|
+| M1-2 | 请求头含 `X-Access-Token` 且非空 | ✅ |
|
|
|
+| M1-3 | 该令牌与登录态令牌**同源一致** | ✅ |
|
|
|
+| M2-1 | 429 显示服务端结构化中文文案 | ✅ `今日额度已用完,明日 00:00 恢复(约15分钟后恢复)` |
|
|
|
+| M2-2 | 不再出现旧的「HTTP错误: 429」 | ✅ |
|
|
|
+| M2-3 | 429 附加「约 X 分钟后恢复」 | ✅ |
|
|
|
+| M2-413 | 413 显示中文文案且不含「HTTP错误」 | ✅ `上传体积超过上限` |
|
|
|
+| M2-404 | 404 显示中文文案且不含「HTTP错误」 | ✅ `会话不存在或无权访问` |
|
|
|
+| M3-1~3 | 非管理员看不到技能 / 子智能体 / 思考级别 | ✅ 计数 0/0/0 |
|
|
|
+| M3-4~6 | 管理员能看到技能 / 子智能体 / 思考级别 | ✅ 计数 1/1/1 |
|
|
|
+| M3-7 | `/api/me` 未上线(404)时保守降级隐藏 | ✅ 计数 0/0/0 |
|
|
|
+
|
|
|
+M1+M2 合计 8/8 通过;M3 合计 9/9 通过(含前置项)。
|
|
|
+
|
|
|
+### 🔴 验收中发现并已修复的真实缺陷(`29d9be9e`)
|
|
|
+
|
|
|
+M3 初版把 `fetchMe()` **只**放在 `visible` 的 `watch` 里。但本组件存在「**挂载即显示**」的用法
|
|
|
+(它在 `layouts/default/index.vue` 与 `homeAI/index.vue` 中由页面持有 `visible`),此时 watch 不触发
|
|
|
+→ **`getMe` 从未被调用** → `isAdmin` 恒为 `false` → **管理员同样看不到管理入口**。
|
|
|
+
|
|
|
+- **实测证据**:拦截 `/api/me` 计数为 **0**;管理员可见性 `skill=0 / subAgents=0 / think=0`。
|
|
|
+- **修复**:增加 `onMounted(() => fetchMe())`,挂载即拉取一次;watch 内保留原有调用
|
|
|
+ (用于「每次打开都刷新」,管理员名单可能在服务端 `.env` 变更)。
|
|
|
+- **修复后**:管理员 `1/1/1`,非管理员 `0/0/0`,404 时 `0/0/0`。
|
|
|
+
|
|
|
+> 该缺陷**纯静态审查(lint / 类型检查)发现不了**——初版代码能编译、能跑、lint 也干净,
|
|
|
+> 只是 `is_admin` 从头到尾没被取过。这正是坚持做浏览器验收的价值。
|
|
|
+
|
|
|
+### 验收环境说明
|
|
|
+
|
|
|
+- dev server:`vite`(端口 3100,本机起、验收后已停)。
|
|
|
+- TfAgents 后端:`182.92.126.35:8070` 真实可达(`/api/model` 返回 200);
|
|
|
+ **`/api/me` 尚未实现**(全仓 grep 无实现,仅文档),故 M3 用请求拦截注入角色响应
|
|
|
+ ——这也顺带验证了「接口未上线即保守降级」。
|
|
|
+- 已知无关噪声(非本次改动引入):VentModelWeb 子应用卸载报错 `P$.a.off is not a function`
|
|
|
+ (既有问题)、dev 环境 `/js/libs/adapter.min.js` 等静态资源 404、Jeecg WebSocket 连不上。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 四、静态检查
|
|
|
+
|
|
|
+| 检查 | 命令 | 结果 |
|
|
|
+|---|---|---|
|
|
|
+| ESLint | `node_modules/.bin/eslint <6 个文件>` | 8 个错误,**全在 `ChatInputArea.vue` 的既有未使用变量**(`handleSelectModel` 等);在基线 `b3ec299b` 复跑得到**同样 8 个**(仅行号偏移)→ **无新增问题** |
|
|
|
+| TypeScript | `node_modules/.bin/vue-tsc --noEmit -p tsconfig.json` | ⚠️ **无法完成**:默认堆 OOM(exit 134),加大堆后 `RangeError: Maximum call stack size exceeded`(无诊断输出);**基线同样崩** → 属既有环境缺陷,与本改动无关 |
|
|
|
+| 替代类型核验 | 受限 tsconfig 只覆盖 6 个改动文件 | 基线 1103 错误 / 分支 1103 错误,改动文件内错误行号偏移、**无一条涉及新标识符**(`getMe`/`isAdmin`/`formatClickError`/`throwStreamHttpError` 等) → **无新增类型错误** |
|
|
|
+| 工作区 | `git status --porcelain` | ✅ 只剩 6 个目标文件(+ 本说明);临时验收页/临时路由已删除;无 `package-lock.json` 变动 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 五、已知取舍(均不影响功能)
|
|
|
+
|
|
|
+1. **`adminConfigured` 已赋值但暂无模板消费**:按改动清单预留(`false` 时可提示「管理员未配置」)。
|
|
|
+ 若不需要,可删除该 ref 与 `getMe` 里的赋值,或补一句提示文案。
|
|
|
+2. **隐藏思考级别后,它左右两个 `.action-divider` 会相邻**(纯视觉问题):
|
|
|
+ 改动清单明确要求「只包住思考级别块、保留分隔线」,故未顺带调整布局。
|
|
|
+3. **`getMe` 会随弹框打开被调用一次,且挂载时也会调用一次**:`a-modal` 有 `destroyOnClose`,
|
|
|
+ `visible=false` 时内容不渲染,因此不产生额外副作用;代价是比原实现多一个轻量 GET。
|
|
|
+4. **M1 的「计费归属」端到端核对本次未做**:需 TfAgents 上线可选令牌 + 可按用户查用量,
|
|
|
+ 当前只验证到「请求头确实带了与登录态一致的令牌」。**建议上线后补一次端到端记账核对。**
|
|
|
+5. **`/api/me` 上线前,管理员也看不到管理入口**(404 → 保守降级)。
|
|
|
+ 这意味着**基座与 TfAgents 的 B2-01 必须同批上线**,否则管理功能无人可用。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 六、交付状态与上游同步
|
|
|
+
|
|
|
+| 项 | 值 |
|
|
|
+|---|---|
|
|
|
+| 本地分支 | `fix/base-agent-token` |
|
|
|
+| 我的 fork(`origin` = `lizuo/mky-vent-base`) | ✅ 已推送 `fix/base-agent-token` |
|
|
|
+| 主仓库(`upstream` = `hrx/mky-vent-base`) | ✅ 已交付同名分支(**人工审核合并,未动 master**) |
|
|
|
+| 上游基线同步 | ✅ 已 merge `upstream/master`(`b3ec299b..f25e5319`,2 提交,仅涉及 `home/configurable/*`、`gasPumpMonitor/*`,与本改动**零文件重叠**),落后 0 |
|
|
|
+| 部署 | ❌ 未部署(需另行下令) |
|
|
|
+
|
|
|
+merge 后已确认:本次 6 个文件与验收时**逐字节一致**(`git diff 29d9be9e HEAD -- <6 文件>` 为空),
|
|
|
+且 `git diff upstream/master..HEAD` 只含这 6 个文件 + 本说明 —— 第三节的验收结论对交付版本依然成立。
|
|
|
+
|
|
|
+### 复现 / 后续命令
|
|
|
+
|
|
|
+```bash
|
|
|
+cd /root/VentAnaly60/mky-vent-base
|
|
|
+
|
|
|
+# 拉取该分支查看
|
|
|
+git fetch upstream && git checkout -b review/base-agent-token upstream/fix/base-agent-token
|
|
|
+
|
|
|
+# 若后续还需更新(改了代码后)
|
|
|
+git push origin fix/base-agent-token && git deliver fix/base-agent-token
|
|
|
+```
|
|
|
+
|
|
|
+> **前置依赖**:TfAgents 侧的 `GET /api/me`(返回 `is_admin` / `admin_configured`)需与其**同批上线**,
|
|
|
+> 否则管理员也会失去管理入口(见「五、5」)。
|