technical-design.md 14 KB

新 AI 助手:技术设计

状态:设计初稿,2026-10-24。需求见 requirements.md,FWH 业务工具见 agent-mcp.md。AgentServer 接口与事件以其仓库 docs/design/04-api.md 和后台“API 协议”页为准,第三方 MCP 接入见其 ADR-0021。

1. 总体架构

新助手是 ToolBox 中独立的新模块:独立的入口、前后端代码、数据集合和 MCP 服务;旧 Kiro 助手(legacy-kiro-assistant.md)原样保留,两者互不调用、互不共享数据。

浏览器(ToolBox)
  ├─ 旧:顶栏 AiChat → /ai/*(不改动)
  └─ 新:菜单「AI 助手」页面(/ai-assistant)→ 会话栏 + <AgentChat> + 文件栏
         │ @agent-server/client:baseUrl = <ToolBox API>/agent,Authorization = ToolBox 登录令牌
         ▼
ToolBox Go 后端(新模块 agent)
  ├─ /agent/sessions/*:会话列表、创建、改名、删除(AgentSessions)
  └─ /agent/v1/*:AgentServer 代理(鉴权 → 归属校验 → 换 API Key → 转发,SSE 透传)
         ▼
AgentServer /v1
  Profile fwh-assistant(或按角色的其他 Profile):Claude、技能、资料库、MCP 服务
  MCP Gateway ── Bearer AGENT_MCP_TOKEN(固定请求头,无会话信息)──┐
                                                                     ▼
ToolBox fwh-agent-mcp(新服务,无用户状态,只读工具集)
         ▼
FWH 云 API / PostgreSQL / MongoDB / Redis / Plane / SIM 平台

职责划分:

组件 负责
ToolBox 前端(新模块) 独立页面:会话栏、<AgentChat>、文件栏、主题适配(ui-design.md)
ToolBox 后端(新模块) 用户鉴权、按角色选择 Profile、会话归属、AgentServer 代理
AgentServer Agent 运行、事件落库与续传、工作区与文件、审批、沙箱、技能、资料库、MCP Gateway 与审计
fwh-agent-mcp 业务数据查询(无用户)、PII 脱敏、业务凭证

2. 关键决策

编号 决策 理由
D-01 新助手与旧助手完全分开:新路由前缀 /agent、新集合、新前端目录、新 MCP 服务 旧助手可以继续使用到新助手验收完成,下线时直接删除整块旧代码,没有交叉依赖
D-02 业务工具用无用户状态的新 MCP(agent-mcp.md) AgentServer Gateway 以固定请求头连接 MCP、多会话共用连接,不传会话信息;身份由上层控制更简单可靠
D-03 用户隔离在 ToolBox 后端实现:浏览器只持有 ToolBox 登录令牌,所有 AgentServer 请求经代理并校验会话归属 AgentServer 不区分使用者,同一 API Key 下的会话彼此可见;API Key 不出后端
D-04 用户能力差异通过 Profile 表达:ToolBox 创建会话时按用户角色选择 Profile,Profile 决定技能、资料库和勾选的 MCP 服务 MCP 无用户后,权限边界上移到“会话用哪个 Profile”
D-05 前端使用 @agent-server/chat-ui 的 <AgentChat> 流式、工具卡片、审批/提问、附件、产出文件、中断、断线续传都已具备

3. ToolBox 后端

3.1 代码位置

文件 内容
service/config/config.go 新增 Agent 段
service/utils/agentserver/client.go AgentServer HTTP 客户端(会话创建、改名、删除、子资源反查)
service/utils/agentserver/proxy.go /agent/v1/* 代理:白名单、转发、SSE 透传、事件解析
service/services/agentServices.go 会话与归属、Profile 选择
service/repositories/mongo/agentSessionRepo.go AgentSessions、AgentResources
service/controllers/agentControllers.go HTTP 处理
service/routes/agentRoutes.go 路由注册(routes.go 中调用 AgentSetup)

不引用 utils/acp、services/aiChatServices.go、utils/filestore 等旧助手代码。

3.2 配置

AGENT_ENABLED=true
AGENT_SERVER_URL=http://127.0.0.1:8787
AGENT_SERVER_API_KEY=ask_xxx                    # 只在后端
AGENT_PROFILE_DEFAULT=prf_xxx                   # 默认 Profile(fwh-assistant)
AGENT_PROFILE_BY_ROLE=ops:prf_yyy,admin:prf_zzz # 可选:角色 → Profile,按顺序取第一个命中的
AGENT_ALLOWED_ROLES=                            # 可选:为空表示所有登录用户可用

API Key 在 AgentServer 后台签发时,勾选上述全部 Profile 为可用。

3.3 数据

AgentSessions(新集合,不复用 AiChatSessions):

字段 说明
sessionId AgentServer 会话 ID(ses_…),唯一索引
userId ToolBox 用户名(AuthGetUserName)
profileId 创建时选用的 Profile
title、createdAt、lastActive
deletedAt 软删除

索引:{ userId, deletedAt, lastActive }。

AgentResources(代理校验不带会话 ID 的请求用):resourceId(run_ / itx_ / file_,唯一)、sessionId、createdAt(TTL 与会话保留期一致)。

3.4 会话接口

接口 行为
GET /agent/sessions 本人未删除的会话,按 lastActive 倒序,上限 50
POST /agent/sessions 按角色选 Profile → POST /v1/sessions { profileId } → 写 AgentSessions → 返回 { sessionId, title }
PATCH /agent/sessions/:id 校验归属 → PATCH /v1/sessions/:id { title } → 更新
DELETE /agent/sessions/:id 校验归属 → DELETE /v1/sessions/:id → 软删除

用户角色变化后,已有会话继续使用创建时的 Profile;新建会话使用新角色对应的 Profile。

3.5 AgentServer 代理

路由 ALL /agent/v1/*,先经 ToolBox 鉴权中间件,然后:

  1. 白名单(其余返回 403):

| 方法 | 路径 | 归属校验 | | --- | --- | --- | | GET | /v1/sessions/:id、/events、/files、/stream(SSE) | 路径中的会话 | | POST | /v1/sessions/:id/messages、/v1/sessions/:id/files | 路径中的会话 | | PATCH | /v1/sessions/:id(只保留 title、model) | 路径中的会话 | | GET、POST | /v1/runs/:id、/v1/runs/:id/interrupt | AgentResources | | GET | /v1/interactions?sessionId= | 查询参数中的会话 | | POST | /v1/interactions/:id | AgentResources | | GET | /v1/files/:fileId | AgentResources | | GET | /v1/profiles、/v1/providers | 无(只读元数据) |

不放行 GET /v1/sessions(会列出所有用户的会话)、POST /v1/sessions、DELETE、/v1/usage、/v1/browser-tokens。

  1. 归属校验:会话属于当前用户且未删除;子资源先查 AgentResources,查不到时反查 AgentServer(GET /v1/runs/:id 与文件列表都带 sessionId;交互在本人最近活跃的会话中用 GET /v1/interactions?sessionId= 查找),确认后补写。不符统一返回 404。

  2. 转发:Authorization 换成 API Key,去掉 Cookie 与 ToolBox 专有头,保留 Last-Event-ID;multipart 原样转发(Fiber BodyLimit 按 AgentServer 上传上限调整)。

  3. 记录子资源:POST /messages 响应中的 runId、POST /files 响应中的文件 ID;SSE 与 /events 透传时解析 run.started、interaction.required、file.changed,异步批量写入,不阻塞转发;发消息时刷新 lastActive。

  4. SSE:逐块写出并立即 Flush,客户端断开时取消上游请求,加 X-Accel-Buffering: no;Caddy/nginx 对 /agent/v1/sessions/*/stream 关闭缓冲。断线续传由 SDK 的 Last-Event-ID 完成,代理不重试。

4. ToolBox 前端

4.1 页面

新助手是菜单路由 /ai-assistant 下的独立大页面(左侧会话栏、中间对话、右侧文件栏),不放在顶栏。路由、布局、交互与代码结构见 ui-design.md;代码在 src/views/ai-assistant/ 与 src/service/agent/。

不修改顶栏的 Actions/AiChat/ 和 src/service/aiChat/,旧助手入口保持不变。

4.2 客户端

const client = new AgentClient({
  baseUrl: `${getBaseUrl()}/agent`,         // SDK 请求 /agent/v1/...
  getToken: async () => getToolBoxToken(),  // 与 serverReq.ts 相同的登录令牌
})

依赖按 AgentServer docs/integration.md 安装三个 SDK 包(URL tgz,锁定版本),main.ts 引入 @agent-server/chat-ui/style.css。同源请求 ToolBox 后端,不需要 AgentServer 的 CORS 与浏览器令牌。

主题:ToolBox 切换明暗时同步设置 data-theme(若组件只认 <html> 上的属性,需确认不影响 NaiveUI);必要时覆盖 --app-* 变量对齐 ToolBox 主色。

5. AgentServer 配置

均在 AgentServer 后台完成,不改 AgentServer 代码(agent-mcp.md 5.2 的结果处理改动除外)。

项 配置
MCP 服务 fwh:远程 HTTP,http://127.0.0.1:8091/mcp/readonly,请求头 Authorization: Bearer <AGENT_MCP_TOKEN>;二期 fwhops:同一端口 http://127.0.0.1:8091/mcp/ops
技能 新写 fwh-assistant(第 6 节),源码在 ToolBox deploy/agent-assistant/skills/fwh-assistant/。AgentServer 暂无自定义技能入口,需求见 AgentServer 仓库 docs/design/12-custom-skills.md;实现后作为技能源导入
资料库 fwh-wiki:/srv/agent-server/knowledge/fwh-wiki(手动运行同步脚本从 FranklinWH-Wiki client/docs 拷贝,排除 .vitepress、public、casdoor;home 下的路径受 ProtectHome 限制,不能直接引用);fwh-data-knowledge:同样方式同步 docs/knowledge
Profile fwh-assistant 引擎 Claude;权限模式“需审批”;技能 report-conventions、custom-skills:fwh-assistant(AgentServer 支持后);资料库 fwh-wiki、fwh-data-knowledge(有内容后);MCP 服务 fwh;文件访问 restricted;按需开放 WebSearch / WebFetch
Profile fwh-assistant-ops(二期) 在上一个基础上加勾 fwhops
API Key 平台 fwh-toolbox,可用 Profile 为上述全部,默认 fwh-assistant

6. 技能 fwh-assistant

从头编写,旧的 deploy/kiro/skills/、deploy/kiro/steering/ 只作为素材,不迁移、不改动。内容:

  • 业务背景:产品线(aGate、aPower 等)、站点与设备的关系、SN 规则、时区与单位约定;
  • 工具使用:每类问题的推荐工具组合(例如“站点故障排查:site_get → alarm_events → mqtt_log_search → fault_record_list”);何时用 *_export 并在工作区用 pandas 分析;
  • 数据防护:不把大批量明细读进上下文(原 mcp-data-guard.md 的规则,改写为面向新工具);工具不足时说明缺口,不用其他方式拼凑;
  • 产出规范:报告结构、图表、公司 PPT 模版 templates/company-template.pptx(随技能分发);产出写入 outputs/;
  • references/:告警码与事件说明、TOU 模版字段(来自调度配置文档)、功率流字段解读等,按需读取。

工具名在技能中写引擎侧可见的名称(mcp__gateway__fwh__site_get 等)。

7. 实施步骤

阶段 内容 依赖 完成标志
S1 Agent MCP 框架(kit、HTTP、约束测试)+ 站点、告警两个领域 — go test ./agentmcp/... 通过;AgentServer 后台“测试连接”能列出工具
S2 Agent MCP 其余领域工具与导出工具 S1 单测通过
S3 AgentServer:resource 结果落盘(agent-mcp.md 5.2) — AgentServer 测试通过
S4 技能 fwh-assistant、资料库同步、Profile、API Key;在 Playground 回归第 8 节业务用例 S1、S2 Playground 通过
S5 ToolBox 后端新模块:配置、集合、会话接口、代理 — 接口与代理单测通过
S6 ToolBox 前端新页面(ui-design.md) S5 本地联调通过
S7 测试环境上线,新页面与旧助手并存,用户验收第 8 节用例 S4~S6 验收通过
S8 决定旧助手下线时间;下线时删除旧助手全部代码、cmd/mcp、deploy/kiro/、Kiro 配置与部署脚本 S7 单独评审

S1~S4(MCP 与 AgentServer 侧)和 S5~S6(ToolBox 前后端)可以并行。

8. 验收用例

编号 用例 覆盖
T-01 问“站点 XXXX 的概况和最近 7 天告警”,回答含表格;工具卡片显示中文标题,没有弹审批 只读工具直接调用
T-02 分析某站点 30 天功率流:Agent 调用导出工具,数据落在工作区文件,用 Python 统计并出图;上下文中没有明细 导出与落盘
T-03 问一个 Wiki 中的产品问题(如某告警码含义),回答引用文档路径 资料库
T-04 粘贴截图 + 上传 xlsx,生成带中文图表的 Word 报告和公司模版 PPT,文件卡片可下载、PDF 可预览 上传与产出
T-05 运行中刷新页面,回到同一会话且输出继续;运行中切换到其他标签页再回来,输出未中断;运行中点击中断 续传与中断
T-11 宽屏三栏、1024~1279px 文件栏收起、< 1024px 两侧都收起为抽屉,均可正常使用 页面布局
T-06 用户 A 用自己的令牌请求用户 B 的会话(stream / events / files / messages)及 B 的 runId / interactionId / fileId,全部 404;GET /agent/v1/sessions 返回 403 用户隔离
T-07 配置了角色映射时,不同角色的用户新建会话使用不同 Profile;无权使用的用户看不到“AI 助手”菜单、接口返回 403 Profile 选择
T-08 让 Agent 读取 ToolBox service/.env、~/.ssh:被拦截并写入 AgentServer 审计 沙箱
T-09 重启 ToolBox 后端、AgentServer、fwh-agent-mcp 后,历史会话时间线完整且可继续对话;fwh-agent-mcp 停止时,工具返回“暂不可用”而不是挂起 可恢复
T-10 旧 AI 助手在新模块上线后功能不受影响 相互独立