# 新 AI 助手:技术设计 > 状态:设计初稿,2026-10-24。需求见 [requirements.md](requirements.md),FWH 业务工具见 [agent-mcp.md](agent-mcp.md)。AgentServer 接口与事件以其仓库 `docs/design/04-api.md` 和后台“API 协议”页为准,第三方 MCP 接入见其 ADR-0021。 ## 1. 总体架构 新助手是 ToolBox 中**独立的新模块**:独立的入口、前后端代码、数据集合和 MCP 服务;旧 Kiro 助手([legacy-kiro-assistant.md](legacy-kiro-assistant.md))原样保留,两者互不调用、互不共享数据。 ``` 浏览器(ToolBox) ├─ 旧:顶栏 AiChat → /ai/*(不改动) └─ 新:菜单「AI 助手」页面(/ai-assistant)→ 会话栏 + + 文件栏 │ @agent-server/client:baseUrl = /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 前端(新模块) | 独立页面:会话栏、``、文件栏、主题适配([ui-design.md](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` 的 `` | 流式、工具卡片、审批/提问、附件、产出文件、中断、断线续传都已具备 | ## 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`。 2. **归属校验**:会话属于当前用户且未删除;子资源先查 `AgentResources`,查不到时反查 AgentServer(`GET /v1/runs/:id` 与文件列表都带 `sessionId`;交互在本人最近活跃的会话中用 `GET /v1/interactions?sessionId=` 查找),确认后补写。不符统一返回 404。 3. **转发**:`Authorization` 换成 API Key,去掉 Cookie 与 ToolBox 专有头,保留 `Last-Event-ID`;multipart 原样转发(Fiber `BodyLimit` 按 AgentServer 上传上限调整)。 4. **记录子资源**:`POST /messages` 响应中的 `runId`、`POST /files` 响应中的文件 ID;SSE 与 `/events` 透传时解析 `run.started`、`interaction.required`、`file.changed`,异步批量写入,不阻塞转发;发消息时刷新 `lastActive`。 5. **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](ui-design.md);代码在 `src/views/ai-assistant/` 与 `src/service/agent/`。 不修改顶栏的 `Actions/AiChat/` 和 `src/service/aiChat/`,旧助手入口保持不变。 ### 4.2 客户端 ```ts 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`(若组件只认 `` 上的属性,需确认不影响 NaiveUI);必要时覆盖 `--app-*` 变量对齐 ToolBox 主色。 ## 5. AgentServer 配置 均在 AgentServer 后台完成,不改 AgentServer 代码([agent-mcp.md](agent-mcp.md) 5.2 的结果处理改动除外)。 | 项 | 配置 | | --- | --- | | MCP 服务 | `fwh`:远程 HTTP,`http://127.0.0.1:8091/mcp`,请求头 `Authorization: Bearer `;二期 `fwhops` | | 技能 | 新写 `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 助手在新模块上线后功能不受影响 | 相互独立 |