对照网址: 主仓库 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(交付时已 mergeupstream/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 非管理员隐藏「思考级别」切换控件 |
X-Access-Token位置:src/views/ventAI/dataPicker/api.ts(ssePost())
原来的行为:
headers: { 'Content-Type': 'application/json' }, // 完全没有令牌
点选解读是基座里唯一不带令牌调 TfAgents 的路径。后果:TfAgents 无法识别是谁在用,
只能把消耗记到兜底账号 click-anonymous,做不到「按登录人员记账」(决策 D5 / B1-20 的明确要求)。
现在的行为:
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}、事件解析完全没动。
位置:manageAssistent/api.ts(导出解析器)、dataPicker/api.ts(调用)、useDataPicker.ts(渲染文案)
原来的行为:
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`); // 用户只能看到「HTTP错误: 429」
}
现在的行为:三步,全部复用仓库既有能力,没有新写第二套解析器。
throwStreamHttpError 由模块内私有改为 export(函数体零改动);await throwStreamHttpError(response) —— 它解析响应体的 detail
(字符串或 { code, message, retry_after, limit_tokens, spent_tokens }),抛出带
status / retryAfter / limitTokens / spentTokens 的 StreamError;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),不会把网络中断等场景改坏。
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)。
基座四处改动:
manageAssistent/api.ts 新增 MeInfo 与 getMe():
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 弹框都会弹红色报错,会被误判为故障。AiAssistantModal.vue 新增 isAdmin / adminConfigured 两个 ref 与 fetchMe(),
并在两处触发:onMounted 一次 + visible 的 watch 里一次。
isAdmin = false:接口返回前先按非管理员渲染,避免管理入口「闪现」给非管理员。isAdmin = false(隐藏入口)。fetchModelMsg() 未做任何改动:思考级别的读取对所有人保持开放,只收敛「切换」。TaskListPanel.vue:optionItems 由普通数组改为 computed 并按 isAdmin 过滤。
computed 而非普通数组,是为了 isAdmin 异步返回后能自动重算,无需刷新页面。ChatInputArea.vue:仅给 .btn-think-wrapper(思考级别切换控件)加 v-if="isAdmin"。
handleSelectThinkLevel / thinkLevelOptions / initialThinkLevel 的 watch 均未改动。与原来的差别:
| 原来 | 现在 | |
|---|---|---|
| 非管理员看到「技能」入口 | 是 | 否 |
| 非管理员看到「子智能体」入口 | 是 | 否 |
| 非管理员看到「思考级别」切换 | 是 | 否 |
| 非管理员看到「定时任务」 | 是 | 是(不变) |
思考级别读取(getModelMsg) |
开放 | 开放(不变) |
/api/me 不可用时 |
— | 保守隐藏(不是全部放开) |
⚠️ 这是界面可见性,不是安全边界:真正的拦截在 TfAgents 后端。前端隐藏只是让非管理员 不用「点了才知道没权限」,不能替代后端鉴权。
方法:用基座自带 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从头到尾没被取过。这正是坚持做浏览器验收的价值。
vite(端口 3100,本机起、验收后已停)。182.92.126.35:8070 真实可达(/api/model 返回 200);
/api/me 尚未实现(全仓 grep 无实现,仅文档),故 M3 用请求拦截注入角色响应
——这也顺带验证了「接口未上线即保守降级」。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 变动 |
adminConfigured 已赋值但暂无模板消费:按改动清单预留(false 时可提示「管理员未配置」)。
若不需要,可删除该 ref 与 getMe 里的赋值,或补一句提示文案。.action-divider 会相邻(纯视觉问题):
改动清单明确要求「只包住思考级别块、保留分隔线」,故未顺带调整布局。getMe 会随弹框打开被调用一次,且挂载时也会调用一次:a-modal 有 destroyOnClose,
visible=false 时内容不渲染,因此不产生额外副作用;代价是比原实现多一个轻量 GET。/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 个文件 + 本说明 —— 第三节的验收结论对交付版本依然成立。
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」)。