# FranklinWH-ToolBox AI 助手方案 ## 1. 协议栈说明 | 协议 | 用途 | 状态 | | -------------------------------- | ---------------------- | ---------------------------------- | | **MCP** (Model Context Protocol) | AI Agent ↔ 工具/数据源 | Kiro CLI 原生支持,主力协议 | | **A2A** (Agent2Agent Protocol) | Agent ↔ Agent 协作 | Linux Foundation 维护,v1.0 已发布 | | ~~ACP~~ | ~~Agent 通信~~ | 2025.8 已合并入 A2A,不再独立存在 | **建议:** 项目内 AI 助手核心是 **Kiro CLI + 自定义 MCP Server**。如果后续需要多 Agent 协作再引入 A2A。 --- ## 2. 现有系统架构 ### 2.1 前端(src/) | 项目 | 说明 | | ---------- | ------------------------------------------------ | | 框架 | Vue 3 + TypeScript + Vite | | UI 库 | Naive UI | | 状态管理 | Pinia(自定义 defineSetupStore 模板) | | 样式 | UnoCSS (Tailwind 兼容) | | 图表 | @antv/g2 | | HTTP | 自定义 FetchClient(支持拦截器、download、blob) | | 代码编辑器 | Monaco Editor | **前端目录结构:** ``` src/ ├── composable/ # 组合式函数 ├── components/ # 公共组件 ├── event/ # 事件系统 ├── injection/ # 依赖注入 ├── router/ # 路由配置 ├── service/ # API 层(按模块分目录:feedback/, siteInfoManage/, simCardManage/ 等) ├── stores/ # Pinia Store(modules/data/, modules/views/) ├── theme/ # 主题配置 ├── types/ # 全局类型 ├── utils/ # 工具函数 └── views/ # 页面视图(按功能模块分目录) ├── feedback/ ├── site-info-manage/ ├── sites-monitor/ ├── sim-card-manage/ ├── batch-download/ └── ... ``` ### 2.2 后端(service/) | 项目 | 说明 | | -------- | ---------------------------------------------------------------------- | | 框架 | Go + Fiber v2 | | 数据库 | MongoDB + PostgreSQL + MySQL | | 缓存 | Redis(自定义 cache 层) | | 对象存储 | MinIO | | 任务队列 | Asynq (Redis-based) | | 外部 API | FranklinWH Energy API、Plane V2 Web API、Acceleronix API、SmartHub API | | 认证 | Casdoor JWT | | MQTT | FWH Pro MQTT | **后端目录结构:** ``` service/ ├── main.go # 入口(初始化所有依赖 + 启动 HTTP 服务) ├── config/ # 配置加载(Viper,从 .env 读取) ├── controllers/ # HTTP 路由处理器(Fiber Handler) ├── services/ # 业务逻辑层 ├── repositories/ # 数据访问层 │ ├── mongo/ # MongoDB 操作 │ ├── redis/ # Redis 操作 │ ├── pgsql/ # PostgreSQL 操作 │ ├── mysql/ # MySQL 操作 │ ├── api/ # 外部 API 客户端 │ │ ├── fwh_pro/ # FranklinWH Energy/OSS API │ │ ├── plane_v1/ # Plane V1 API(API Key 认证) │ │ ├── plane_v2/ # Plane V2 Web API(Session 认证) │ │ ├── acceleronix/ # Acceleronix SIM 卡 API │ │ └── smartHub/ # SmartHub SIM 卡 API │ ├── mqtt/ # MQTT 通信 │ └── events/ # 事件通信(xLog TCP) ├── routes/ # 路由注册 ├── middleware/ # 中间件(认证等) ├── domain/ # 领域模型 ├── dto/ # 数据传输对象 ├── database/ # 数据库连接初始化 ├── cache/ # Redis 缓存封装 ├── task/ # 任务队列(Asynq) ├── scheduler/ # 定时任务 ├── jobs/ # 后台作业 └── utils/ # 工具包 ``` ### 2.3 后端各层职责 ``` HTTP Request │ ▼ ┌──────────────┐ │ controllers/ │ 解析参数、校验、调用 service、返回 JSON └──────┬───────┘ │ ▼ ┌──────────────┐ │ services/ │ 业务逻辑编排、数据转换、调用多个 repository └──────┬───────┘ │ ▼ ┌──────────────┐ │repositories/ │ 数据库 CRUD、外部 API 调用、缓存操作 └──────────────┘ ``` --- ## 3. 整体架构(AI 助手视角) ``` ┌─────────────────────────────────────────────────────────────────┐ │ 开发者终端 │ │ Kiro CLI │ │ (AI Agent / Host) │ └────────────┬────────────────────────────────┬────────────────────┘ │ MCP (stdio) │ MCP (stdio) ▼ ▼ ┌────────────────────────┐ ┌──────────────────────────────┐ │ 内部工具 MCP Server │ │ 外部服务 MCP Server │ │ (Go) │ │ (社区开源) │ ├────────────────────────┤ ├──────────────────────────────┤ │• query_site_info │ │• AWS Docs 搜索 │ │• search_sites │ │• Brave 网络搜索 │ │• query_sim_card │ └──────────────────────────────┘ │• query_power_flow │ │• create_plane_issue │ │• query_plane_issues │ │• query_alarm_stats │ └────────────┬───────────┘ │ 直接 import 复用 ▼ ┌────────────────────────────────────────────────────────────────┐ │ 现有后端 service/ 代码 │ ├────────────────────────────────────────────────────────────────┤ │ services/ │ repositories/ │ domain/dto │ │ (业务逻辑) │ (数据访问+外部API) │ (数据模型) │ └─────────┬───────────┴──────────┬─────────────┴─────────────────┘ │ │ ▼ ▼ ┌──────────────┐ ┌──────────────────────────────────────────────┐ │ MongoDB │ │ 外部系统 │ │ PostgreSQL │ │ • FranklinWH Energy API │ │ Redis │ │ • Plane V2 Web API │ │ MinIO │ │ • Acceleronix / SmartHub API │ └──────────────┘ └──────────────────────────────────────────────┘ ``` ### 3.1 MCP Tools Server 与现有后端的关系 **核心原则:MCP Tools Server 是现有后端 service 层的另一个入口,不是独立系统。** | 对比 | 现有 HTTP Server (`main.go`) | MCP Tools Server | | -------- | ------------------------------------- | ---------------------------------- | | 入口 | Fiber HTTP 路由 | stdio JSON-RPC | | 调用方 | 前端浏览器 | Kiro CLI (AI Agent) | | 认证 | Casdoor JWT | 无需(本地 stdio 进程) | | 复用层 | controllers → services → repositories | **直接调用 services/repositories** | | 输出格式 | JSON HTTP Response | MCP Tool Result (text/JSON) | **具体复用方式:** ```go // MCP Tools Server 的 go.mod 中引用现有 service 模块 // 方式一:同一 module,不同 main 入口 // service/cmd/mcp/main.go (MCP Server 入口) // service/main.go (HTTP Server 入口) // 方式二:独立 module,replace 引用 // mcp-servers/tools/go.mod: // require fwh-toolbox-server v0.0.0 // replace fwh-toolbox-server => ../../service ``` **推荐方式一**(同一 module):在 `service/cmd/mcp/` 下创建 MCP Server 入口,共享整个 service 代码库。 ``` service/ ├── main.go # 现有 HTTP Server 入口 ├── cmd/ │ └── mcp/ │ └── main.go # MCP Tools Server 入口 ├── services/ # 共享:业务逻辑 ├── repositories/ # 共享:数据访问 ├── domain/ # 共享:领域模型 ├── dto/ # 共享:DTO ├── config/ # 共享:配置 ├── database/ # 共享:数据库连接 └── cache/ # 共享:缓存 ``` **MCP Server 入口初始化流程:** ```go // service/cmd/mcp/main.go func main() { // 1. 加载配置(同 HTTP Server) cfg, _ := config.LoadConfig() // 2. 初始化数据库连接(同 HTTP Server) database.Init(&cfg.App) cache.Init(cfg.App.RedisAddr) repositories.PlaneWebHttpInit(&cfg.PlaneWebApi) // 3. 创建 MCP Server(替代 Fiber HTTP 路由) s := server.NewMCPServer("fwh-tools", "1.0.0", server.WithToolCapabilities(false), server.WithRecovery(), ) // 4. 注册 Tools(调用 services/ 层函数) tools.RegisterSiteInfoTools(s) tools.RegisterSimCardTools(s) tools.RegisterPowerFlowTools(s) tools.RegisterAlarmTools(s) tools.RegisterShutdownTools(s) tools.RegisterPlaneTools(s) // 5. 启动 stdio 通信(替代 app.Listen) server.ServeStdio(s) } ``` **不需要的部分:** - 不需要 Fiber 框架 - 不需要 controllers/ 层(MCP handler 直接调用 services) - 不需要 middleware/(stdio 无需 HTTP 认证) - 不需要 routes/(MCP 用 tool 注册替代) --- ## 4. 知识库 MCP Server(独立 VitePress 项目) 知识库是一个独立的 VitePress 文档项目,通过 `vitepress-plugin-mcp` 插件将文档暴露为 MCP Server。 ### 4.1 概述 | 项目 | 说明 | | -------- | -------------------------------------------------- | | 位置 | 独立 Git 仓库(不在 FranklinWH-ToolBox 内) | | 框架 | VitePress(Markdown 文档站) | | 内容 | 团队知识:编码规范、架构模板、技术文档、最佳实践等 | | MCP 插件 | `vitepress-plugin-mcp`(npm 包) | | MCP 传输 | Streamable HTTP(远程 MCP Server) | | 配置层级 | `./.kiro/settings/mcp.json`(后端运行目录级别) | ### 4.2 VitePress 知识库项目搭建 **Step 1:创建 VitePress 项目** ```bash mkdir team-knowledge && cd team-knowledge pnpm init pnpm add -D vitepress vitepress-plugin-mcp mkdir docs ``` **Step 2:编写文档内容** ``` team-knowledge/ ├── docs/ │ ├── index.md # 首页 │ ├── coding/ │ │ ├── frontend.md # 前端编码规范 │ │ ├── backend.md # 后端编码规范 │ │ └── git.md # Git 提交规范 │ ├── architecture/ │ │ ├── vue-project.md # Vue 项目架构模板 │ │ ├── go-service.md # Go 后端架构模板 │ │ └── mcp-server.md # MCP Server 开发指南 │ ├── devops/ │ │ ├── deployment.md # 部署流程 │ │ └── monitoring.md # 监控方案 │ └── troubleshooting/ │ ├── common-issues.md # 常见问题排查 │ └── fwh-specific.md # FranklinWH 业务相关 ├── .vitepress/ │ └── config.ts # VitePress + MCP 配置 ├── package.json └── pnpm-lock.yaml ``` **Step 3:配置 VitePress + MCP 插件** ```ts // .vitepress/config.ts import { defineConfig } from 'vitepress' import { MCPPlugin } from 'vitepress-plugin-mcp' export default defineConfig({ title: 'Team Knowledge Base', description: '团队知识库', themeConfig: { search: { provider: 'local', // 必须启用 local search(插件依赖它的索引) options: {}, }, sidebar: [ { text: '编码规范', items: [ { text: '前端', link: '/coding/frontend' }, { text: '后端', link: '/coding/backend' }, { text: 'Git', link: '/coding/git' }, ], }, { text: '架构模板', items: [ { text: 'Vue 项目', link: '/architecture/vue-project' }, { text: 'Go 后端', link: '/architecture/go-service' }, ], }, ], }, vite: { plugins: [ MCPPlugin({ port: 4100 }), // MCP Server 监听端口 ], }, }) ``` **Step 4:启动** ```bash # 开发模式(同时启动 VitePress + MCP Server) pnpm vitepress dev docs # MCP Server 自动在 http://localhost:4100 启动 ``` **Step 5:验证 MCP Server** ```bash # 测试 MCP Server 是否可用 curl http://localhost:4100/mcp ``` ### 4.3 插件暴露的 Tools `vitepress-plugin-mcp` 自动提供以下工具: | Tool 名称 | 描述 | 参数 | | ---------------------- | ---------------------------------------------------- | --------------- | | `search_documentation` | 全文搜索文档内容(基于 VitePress local search 索引) | `query: string` | | `get_page_content` | 获取指定页面的完整 Markdown 内容 | `path: string` | | `list_pages` | 列出所有文档页面 | 无 | ### 4.4 在 Kiro 中配置连接 知识库是**远程 MCP Server**(HTTP),在后端运行目录的 Kiro 配置中使用 `url` 字段连接: **`./.kiro/settings/mcp.json` 中添加:** ```json { "mcpServers": { "team-knowledge": { "url": "http://lochost:4100/mcp" } } } ``` ### 4.5 部署方案 | 环境 | 方式 | 地址 | | -------- | ------------------------------------ | ------------------------------------ | | 本地开发 | `pnpm vitepress dev docs` | `http://localhost:4100/mcp` | | 团队共享 | 部署到内网服务器(PM2/systemd 常驻) | `http://knowledge.internal:4100/mcp` | | Docker | 容器化部署 | `http://knowledge:4100/mcp` | **PM2 部署示例:** ```bash # ecosystem.config.js module.exports = { apps: [{ name: 'knowledge-mcp', script: 'npx', args: 'vitepress dev docs --port 5173', cwd: '/opt/team-knowledge', env: { NODE_ENV: 'production' } }] } ``` **注意:** `vitepress-plugin-mcp` 的 MCP Server 与 VitePress dev server 绑定运行。生产环境需要保持 `vitepress dev` 常驻运行(不是 `vitepress build`)。 ### 4.6 使用效果 配置完成后,在 Kiro CLI 中可以直接使用知识库: ``` > 帮我查一下前端编码规范中关于组件命名的约定 Kiro 会自动调用 team-knowledge MCP Server 的 search_documentation 工具, 搜索知识库内容并返回相关文档片段。 ``` ### 4.7 添加新文档的流程 ``` 1. 在 team-knowledge 仓库中编写 Markdown 文件 2. git commit && git push 3. 服务器自动重启(PM2 watch 或 CI/CD) 4. MCP Server 自动索引新内容 5. Kiro 下次搜索即可获取新文档 ``` --- ## 5. 内部工具 MCP Server 详细设计 ### 5.1 技术选型 | 项目 | 选择 | 理由 | | --------- | ----------------------------- | -------------------------------------- | | 语言 | Go | 与后端一致,直接 import service 层代码 | | SDK | `github.com/mark3labs/mcp-go` | 社区最活跃的 Go MCP SDK | | Transport | stdio | Kiro CLI 本地调用,最简单 | | 构建产物 | 单二进制`fwh-mcp-server` | `go build -o fwh-mcp-server ./cmd/mcp` | ### 5.2 暴露的 Tools 清单 | Tool 名称 | 输入参数 | 描述 | 复用的 service 函数 | | --------------------------- | ------------------------------------- | ----------------------- | --------------------------------- | | `query_site_info` | `sn: string` | 查询站点设备详细信息 | `services.GetSiteInfoData` | | `search_sites` | `page, size: int, keyword?: string` | 搜索站点列表 | `services.QuerySiteInfoDataList` | | `query_sim_card` | `giccid: string` | 查询单张 SIM 卡状态 | `services.GetSimCardInfo` | | `search_sim_cards` | `page, size: int, status?, platform?` | 搜索 SIM 卡列表 | `services.GetSimCardBatchInfo` | | `query_power_flow` | `sn: string, date: string` | 查询站点功率流数据 | `services.QuerySitePowerFlow` | | `query_alarm_statistics` | `date?: string` | 查询告警统计 | `services.GetAlarmStatistics` | | `query_shutdown_statistics` | `date: string` | 查询停机统计 | `services.GetShutdownStatistics` | | `create_feedback` | `title, type, priority, description` | 创建 Plane Intake Issue | `services.SubmitFeedback` | | `query_feedback_history` | 无 | 查询 Feedback 提交历史 | `services.GetFeedbackHistory` | ### 5.3 代码结构 ``` service/ ├── cmd/ │ └── mcp/ │ ├── main.go # MCP Server 入口 │ └── tools/ │ ├── siteInfo.go # 站点信息相关 tools │ ├── simCard.go # SIM 卡相关 tools │ ├── powerFlow.go # 功率流相关 tools │ ├── alarm.go # 告警统计 tools │ ├── shutdown.go # 停机统计 tools │ └── plane.go # Plane/Feedback tools ├── main.go # 现有 HTTP Server(不改动) ├── services/ # 共享 ├── repositories/ # 共享 └── ... ``` ### 5.4 代码示例 **入口 `service/cmd/mcp/main.go`:** ```go package main import ( "fmt" "log" "os" "github.com/mark3labs/mcp-go/server" "fwh-toolbox-server/cache" "fwh-toolbox-server/config" "fwh-toolbox-server/database" "fwh-toolbox-server/repositories" "fwh-toolbox-server/cmd/mcp/tools" ) func main() { // 加载配置 cfg, err := config.LoadConfig() if err != nil { log.Fatalf("加载配置失败: %v", err) } // 初始化基础设施(与 HTTP Server 相同) if err := database.Init(&cfg.App); err != nil { log.Fatalf("数据库初始化失败: %v", err) } cache.Init(cfg.App.RedisAddr) repositories.PlaneWebHttpInit(&cfg.PlaneWebApi) // 创建 MCP Server s := server.NewMCPServer( "fwh-tools", "1.0.0", server.WithToolCapabilities(false), server.WithRecovery(), ) // 注册所有 Tools tools.RegisterSiteInfoTools(s) tools.RegisterSimCardTools(s) tools.RegisterPowerFlowTools(s) tools.RegisterAlarmTools(s) tools.RegisterShutdownTools(s) tools.RegisterPlaneTools(s) // 启动 stdio 通信 fmt.Fprintln(os.Stderr, "[fwh-mcp-server] started (stdio)") if err := server.ServeStdio(s); err != nil { log.Fatalf("[fwh-mcp-server] error: %v", err) } } ``` **工具示例 `service/cmd/mcp/tools/siteInfo.go`:** ```go package tools import ( "context" "encoding/json" "fmt" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" "fwh-toolbox-server/services" ) func RegisterSiteInfoTools(s *server.MCPServer) { // Tool: 查询站点信息 s.AddTool( mcp.NewTool("query_site_info", mcp.WithDescription("查询单个站点设备的详细信息,包括版本、网络状态、激活状态、SN、固件等"), mcp.WithString("sn", mcp.Required(), mcp.Description("站点 SN 序列号")), ), handleQuerySiteInfo, ) // Tool: 搜索站点列表 s.AddTool( mcp.NewTool("search_sites", mcp.WithDescription("搜索站点设备列表,支持分页"), mcp.WithNumber("page", mcp.Description("页码,从1开始,默认1")), mcp.WithNumber("size", mcp.Description("每页数量,默认10")), mcp.WithString("keyword", mcp.Description("搜索关键字(匹配 SN、设备名)")), ), handleSearchSites, ) } func handleQuerySiteInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { sn, err := req.RequireString("sn") if err != nil { return mcp.NewToolResultError("参数 sn 必填"), nil } data, err := services.GetSiteInfoData(sn) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("查询失败: %v", err)), nil } jsonBytes, _ := json.MarshalIndent(data, "", " ") return mcp.NewToolResultText(string(jsonBytes)), nil } func handleSearchSites(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { page := int(req.GetFloat("page", 1)) size := int(req.GetFloat("size", 10)) keyword := req.GetString("keyword", "") data, err := services.QuerySiteInfoDataList(uint(page), uint(size), keyword, nil, nil, nil) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("搜索失败: %v", err)), nil } jsonBytes, _ := json.MarshalIndent(data, "", " ") return mcp.NewToolResultText(string(jsonBytes)), nil } ``` --- ## 6. 外部服务 MCP Server 直接使用社区开源 Server,不需要自己实现。**不使用 Git MCP Server。** | Server | 用途 | 包名 | | ----------------- | ------------------------ | ------------------------------------------ | | AWS Documentation | 搜索 AWS 文档、最佳实践 | `@aws/aws-documentation-mcp-server` | | Brave Search | 网络搜索(技术问题排查) | `@modelcontextprotocol/server-bravesearch` | --- ## 7. Kiro CLI 配置 所有 Kiro 配置基于后端程序运行目录(`service/`)的相对路径 `./.kiro/`。 ### 7.1 工作区 MCP 配置 **`service/.kiro/settings/mcp.json`:** ```json { "mcpServers": { "fwh-tools": { "command": "./fwh-mcp-server", "args": [], "env": { "MONGO_DSN": "${MONGO_DSN}", "REDIS_ADDR": "${REDIS_ADDR}", "PLANE_WEB_HOST": "${PLANE_WEB_HOST}", "PLANE_WEB_EMAIL": "${PLANE_WEB_EMAIL}", "PLANE_WEB_PASSWORD": "${PLANE_WEB_PASSWORD}", "PLANE_WEB_WORKSPACE_SLUG": "${PLANE_WEB_WORKSPACE_SLUG}", "PLANE_WEB_PROJECT_ID": "${PLANE_WEB_PROJECT_ID}" } }, "team-knowledge": { "url": "http://knowledge.internal:4100/mcp" }, "aws-docs": { "command": "npx", "args": ["-y", "@aws/aws-documentation-mcp-server"] }, "web-search": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-bravesearch"], "env": { "BRAVE_API_KEY": "${BRAVE_API_KEY}" } } } } ``` ### 7.2 Steering 文件 **`service/.kiro/steering/project.md`:** ```markdown # FranklinWH-ToolBox 项目上下文 ## 项目概述 内部工具平台,前端 Vue3 + Naive UI,后端 Go Fiber + MongoDB。 ## 可用 MCP 工具 - fwh-tools: 查询站点、SIM卡、功率流、告警、Plane Issue - team-knowledge: 搜索团队知识库文档 - aws-docs: 搜索 AWS 文档 - web-search: 网络搜索 ## 注意事项 - 不要直接操作 Git - 修改代码后需 go build / npm run build 验证 - 后端分层:controllers → services → repositories ``` ### 7.3 部署目录结构 ``` /var/www/fwh-toolbox/server/ # 后端运行目录(WorkingDirectory) ├── fwh-toolbox-server # HTTP Server 二进制 ├── fwh-mcp-server # MCP Tools Server 二进制 ├── .kiro/ │ ├── settings/ │ │ └── mcp.json # MCP Server 配置(command: "./fwh-mcp-server") │ └── steering/ │ └── project.md # Agent 指令文件 └── .env # 环境变量配置 ``` **源码目录结构:** ``` service/ ├── .kiro/ # Kiro 配置(开发环境 + 部署时拷贝) │ ├── settings/ │ │ └── mcp.json │ └── steering/ │ └── project.md ├── cmd/ │ └── mcp/ │ ├── main.go # MCP Server 入口 │ └── tools/ # 各工具实现 │ ├── siteInfo.go │ ├── simCard.go │ ├── powerFlow.go │ ├── alarm.go │ ├── shutdown.go │ └── plane.go ├── main.go # HTTP Server 入口 └── ... ``` ### 7.4 后端 ACP Client 引用 ACP Client 启动 `kiro-cli acp` 时指定工作目录为后端运行目录,Kiro 自动从 `./.kiro/settings/mcp.json` 加载配置: ```go cmd := exec.Command("kiro-cli", "acp") cmd.Dir = serviceDir // 后端运行目录(含 .kiro/ 和 fwh-mcp-server) ``` --- ## 8. 构建与运行 ### 8.1 构建 MCP Tools Server ```bash cd service go build -o fwh-mcp-server ./cmd/mcp ``` ### 8.2 本地测试 ```bash # 直接运行测试 echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | ./fwh-mcp-server # 通过 Kiro CLI 测试(在 service/ 目录下) kiro chat # 然后在对话中使用工具 ``` --- ## 9. 实现步骤 | 阶段 | 内容 | 输出物 | 预估时间 | | ----------- | ------------------------------------- | ------------------------------ | -------- | | **Phase 1** | 创建`service/cmd/mcp/` 目录结构和入口 | main.go + go build 通过 | 0.5 天 | | **Phase 2** | 实现站点信息 Tools | tools/siteInfo.go | 1 天 | | **Phase 3** | 实现 SIM 卡 + 功率流 Tools | tools/simCard.go, powerFlow.go | 1 天 | | **Phase 4** | 实现告警 + Plane Tools | tools/alarm.go, plane.go | 1 天 | | **Phase 5** | 配置`.kiro/` + Steering + 联调 | 配置文件 + 端到端测试 | 0.5 天 | | **Phase 6** | 配置外部 MCP Server (AWS Docs, Brave) | npm 安装 + 环境变量配置 | 0.5 天 | **总计:约 4.5 天** --- ## 10. 前端 AI Chat 与后端通讯设计 ### 10.1 架构概览 ``` ┌────────────────────────────────┐ │ 前端 (Vue 3) │ │ src/views/ai-chat/ │ │ ┌──────────────────────────┐ │ │ │ ChatPanel 组件 │ │ │ │ • 消息列表 (Markdown) │ │ │ │ • 输入框 │ │ │ │ • 流式输出显示 │ │ │ └────────────┬─────────────┘ │ │ │ SSE / fetch │ └───────────────┼────────────────┘ │ ▼ ┌────────────────────────────────┐ │ 后端 (Go Fiber) │ │ service/controllers/ │ │ aiChatControllers.go │ │ ┌──────────────────────────┐ │ │ │ POST /ai/chat │ │ │ │ • 接收用户消息 │ │ │ │ • 调用 LLM API │ │ │ │ • SSE 流式返回 │ │ │ └────────────┬─────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────┐ │ │ │ LLM Provider │ │ │ │ • Claude API (Anthropic) │ │ │ │ • 或 OpenAI API │ │ │ │ • 或 AWS Bedrock │ │ │ └──────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────┐ │ │ │ MCP Tools (可选) │ │ │ │ • 复用 services 层 │ │ │ │ • AI 可调用内部工具 │ │ │ └──────────────────────────┘ │ └────────────────────────────────┘ ``` ### 10.2 通讯协议:SSE(Server-Sent Events) 选择 SSE 而非 WebSocket 的原因: - AI 对话是单向流(服务端推送 token 流给前端) - SSE 基于 HTTP,天然支持认证 header - 无需额外的连接管理 - 断线自动重连 ### 10.3 后端引擎:Kiro ACP Client 后端实现 ACP (Agent Client Protocol) Client,通过 JSON-RPC 2.0 与 `kiro-cli acp` 子进程结构化通讯。 **为什么选 ACP 而非直接 exec Kiro CLI:** | 对比项 | exec kiro chat | ACP Client | | -------------- | --------------------- | ------------------------------------------ | | 通讯格式 | 非结构化文本输出 | JSON-RPC 2.0 结构化 | | 会话管理 | 自己实现 | Kiro 原生支持(session/new, session/load) | | 工具调用可见性 | 无法感知 | ToolCall/ToolCallUpdate 实时事件 | | 取消操作 | kill 进程 | session/cancel 优雅取消 | | 会话持久化 | 自己实现 history.json | Kiro 自动持久化到 ~/.kiro/sessions/ | | 协议稳定性 | CLI 输出格式可能变 | ACP 是开放标准 | **架构:** ``` 前端 SSE ←── 后端 (ACP Client) ←── JSON-RPC stdin/stdout ←── kiro-cli acp │ ├── 自动调用 MCP Server ├── 自动管理会话上下文 └── 自动持久化对话历史 ``` ### 10.4 后端接口设计 **`POST /api/ai/chat`** — 发送消息 请求: ```json { "message": "帮我查一下 SN 为 ABC123 的站点信息", "sessionId": "sess_abc123" // 可选,不传则使用默认会话 } ``` 响应(SSE 流,将 ACP 事件转发给前端): ``` Content-Type: text/event-stream event: message data: {"type":"text","content":"正在查询站点信息..."} event: tool_call data: {"type":"tool_call","name":"query_site_info","status":"running"} event: tool_update data: {"type":"tool_update","name":"query_site_info","status":"completed"} event: message data: {"type":"text","content":"该站点的固件版本是 v3.2.1..."} event: done data: {"type":"done","sessionId":"sess_abc123"} ``` **SSE 事件映射:** | SSE Event | ACP Update Type | 说明 | | ------------- | ------------------- | --------------------- | | `message` | `AgentMessageChunk` | AI 文本流(逐块推送) | | `tool_call` | `ToolCall` | Kiro 调用 MCP 工具 | | `tool_update` | `ToolCallUpdate` | 工具执行进度 | | `done` | `TurnEnd` | 本轮对话结束 | | `error` | - | 错误 | **其他接口:** | 接口 | 方法 | 描述 | | ------------------ | ------ | ---------------------- | | `/ai/chat` | POST | 发送消息,SSE 流式返回 | | `/ai/sessions` | GET | 获取当前用户会话列表 | | `/ai/sessions/new` | POST | 创建新会话 | | `/ai/sessions/:id` | DELETE | 删除会话 | | `/ai/cancel` | POST | 取消当前操作 | ### 10.5 后端 ACP Client 实现 **核心数据结构:** ```go // ACPSession 单个用户的 ACP 连接 type ACPSession struct { UserId string SessionId string // Kiro ACP session ID Process *exec.Cmd // kiro-cli acp 长驻进程 Stdin io.WriteCloser // JSON-RPC 请求写入 Stdout *bufio.Reader // JSON-RPC 响应/通知读取 Mutex sync.Mutex // 同一用户串行对话 LastActive time.Time RequestId int64 // JSON-RPC 请求 ID 自增 } // ACPManager 管理所有用户的 ACP 会话 type ACPManager struct { sessions map[string]*ACPSession // userId -> session mu sync.RWMutex maxIdle time.Duration // 空闲超时 projectDir string // 项目目录 } ``` **完整通讯流程:** ```go // 1. 启动 kiro-cli acp(每用户一个长驻进程) func (m *ACPManager) GetOrCreate(userId string) (*ACPSession, error) { if sess, exists := m.sessions[userId]; exists { sess.LastActive = time.Now() return sess, nil } cmd := exec.Command("kiro-cli", "acp") cmd.Dir = m.projectDir // 含 .kiro/settings/mcp.json stdin, _ := cmd.StdinPipe() stdout, _ := cmd.StdoutPipe() cmd.Start() sess := &ACPSession{ UserId: userId, Process: cmd, Stdin: stdin, Stdout: bufio.NewReader(stdout), } // 2. initialize 握手 sess.sendRPC("initialize", map[string]any{ "protocolVersion": 1, "clientCapabilities": map[string]any{ "fs": map[string]any{"readTextFile": true, "writeTextFile": true}, }, "clientInfo": map[string]any{"name": "fwh-toolbox", "version": "1.0.0"}, }) // 3. 创建会话 result := sess.sendRPC("session/new", map[string]any{"cwd": m.projectDir}) sess.SessionId = result.SessionId m.sessions[userId] = sess return sess, nil } // 4. 发送消息并流式读取 func (sess *ACPSession) Prompt(message string, events chan<- SSEEvent) { sess.Mutex.Lock() defer sess.Mutex.Unlock() sess.sendRPC("session/prompt", map[string]any{ "sessionId": sess.SessionId, "content": []map[string]any{{"type": "text", "text": message}}, }) // 持续读取 session/notification 直到 TurnEnd for { notif := sess.readNotification() switch notif.Params.UpdateType { case "AgentMessageChunk": events <- SSEEvent{Type: "message", Content: notif.Params.Text} case "ToolCall": events <- SSEEvent{Type: "tool_call", Name: notif.Params.ToolName} case "ToolCallUpdate": events <- SSEEvent{Type: "tool_update", Name: notif.Params.ToolName} case "TurnEnd": events <- SSEEvent{Type: "done", SessionId: sess.SessionId} close(events) return } } } // 5. 取消操作 func (sess *ACPSession) Cancel() { sess.sendRPC("session/cancel", map[string]any{"sessionId": sess.SessionId}) } ``` **Controller 层:** ```go func AiChat(c *fiber.Ctx) error { userId := middleware.AuthGetUserName(c) var req struct { Message string `json:"message"` SessionId string `json:"sessionId"` } c.BodyParser(&req) sess, err := acpManager.GetOrCreate(userId) if err != nil { return Error(c, "AI 会话创建失败") } // SSE 流式响应 c.Set("Content-Type", "text/event-stream") c.Set("Cache-Control", "no-cache") c.Context().SetBodyStreamWriter(func(w *bufio.Writer) { events := make(chan SSEEvent, 50) go sess.Prompt(req.Message, events) for event := range events { fmt.Fprintf(w, "event: %s\ndata: %s\n\n", event.Type, event.JSON()) w.Flush() } }) return nil } ``` ### 10.6 多会话管理(ACP 原生能力) Kiro ACP 原生支持多会话,后端无需自己实现持久化。 **ACP 会话方法:** | 方法 | 用途 | | ---------------- | -------------------------------------- | | `session/new` | 创建新会话(Kiro 分配 ID,自动持久化) | | `session/load` | 按 ID 恢复历史会话 | | `session/prompt` | 在指定会话中对话 | | `session/cancel` | 取消当前操作 | **会话生命周期:** ``` 用户首次 → 启动 kiro-cli acp 进程 → initialize → session/new │ 用户对话 → session/prompt → 读取 notifications → SSE │ Kiro 自动 │ 持久化 用户加载历史 → session/load(sessionId) │ ▼ 空闲 30min → kill 进程 → 下次使用时重启 + session/load 恢复 ``` **Kiro 自动持久化目录:** ``` ~/.kiro/sessions/cli/ ├── .json # 会话元数据 └── .jsonl # 对话事件日志 ``` ### 10.7 限制与注意事项 | 项目 | 说明 | | ---------- | ------------------------------------------------------- | | 并发限制 | 同一用户 Mutex 串行(ACP 不支持同 session 并行 prompt) | | 最大进程数 | 限制 20 个并发 kiro-cli acp 进程 | | 空闲超时 | 30 分钟无活动 kill 进程(再次使用时自动恢复) | | 请求超时 | 单次 prompt 最多等待 120 秒 | | MCP 配置 | 所有会话共享项目 .kiro/settings/mcp.json | | Kiro 订阅 | 需确保服务器上 Kiro 已登录且订阅有效 | ### 10.8 前端实现要点 **实现位置:** `src/layout/header/Actions` 中,作为顶部栏全局操作按钮,点击展开 AI Chat 侧边抽屉。 **目录结构:** ``` src/layout/header/Actions/ ├── AiChat/ │ ├── index.vue # 入口按钮 + NDrawer 容器 │ ├── ChatPanel.vue # 聊天面板(消息列表 + 输入) │ ├── MessageItem.vue # 单条消息(支持 Markdown 渲染) │ ├── ToolCallCard.vue # 工具调用展示卡片 │ ├── StreamingText.vue # 流式文本显示组件 │ ├── useAiChat.ts # 核心逻辑:SSE 连接、消息管理 │ └── interface.ts # 类型定义 ``` **入口组件 `AiChat/index.vue`:** ```vue ``` **`useAiChat.ts` 核心逻辑:** ```typescript export function useAiChat() { const messages = ref([]) const isStreaming = ref(false) async function sendMessage(content: string) { // 添加用户消息 messages.value.push({ role: 'user', content }) isStreaming.value = true // 添加 AI 消息占位 const aiMessage: ChatMessage = { role: 'assistant', content: '', toolCalls: [] } messages.value.push(aiMessage) // 发起 SSE 请求(使用框架的 fetch,自动带 token) const response = await fetch(baseUrl + '/ai/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ message: content, history: messages.value.slice(0, -1), // 不含当前占位 }), }) // 读取 SSE 流 const reader = response.body!.getReader() const decoder = new TextDecoder() while (true) { const { done, value } = await reader.read() if (done) break const text = decoder.decode(value) // 解析 SSE 事件,逐步更新 aiMessage.content parseSSEEvents(text, aiMessage) } isStreaming.value = false } return { messages, isStreaming, sendMessage } } ``` **`ChatPanel.vue` 基本结构:** ```vue ``` ### 10.9 消息渲染 | 内容类型 | 渲染方式 | | -------- | ---------------------------------- | | 普通文本 | Markdown 渲染(支持代码高亮) | | 工具调用 | 折叠卡片,显示工具名 + 参数 + 结果 | | 表格数据 | 自动识别 JSON 数组,渲染为表格 | | 错误信息 | 红色提示卡片 | ## 11. 安全考虑 | 风险 | 措施 | | --------------- | ------------------------------------------------------------ | | AI 误操作 Git | 不配置 Git MCP Server | | 敏感数据泄露 | Tools 只返回必要字段,不返回密码/token/密钥 | | AI 误操作 Plane | create_feedback 限制只能创建 Intake,不能删除/修改已有 Issue | | MCP Server 崩溃 | 使用`server.WithRecovery()` 中间件恢复 panic | | 环境变量泄露 | 使用`${VAR}` 引用,不硬编码 | | 数据量过大 | Tools 返回结果限制最大条目数,分页查询 | | Kiro 进程泄漏 | SessionManager 定时扫描超时进程并 kill | | 用户并发冲突 | 同一用户 Mutex 锁,防止并行调用 Kiro | | 磁盘占用 | 会话目录定期清理,限制历史文件大小 | | Kiro 订阅额度 | 监控使用量,超额时返回友好提示 | --- ## 12. 关于 A2A(原 ACP) 当前阶段不需要 A2A。适合引入的场景: - 多个独立 AI Agent 协作(代码审查 Agent + 测试 Agent + 部署 Agent) - 跨团队/跨系统的 Agent 通信 如果后续需要: - A2A Go SDK: `github.com/a2aproject/a2a-go` - A2A 规范: `https://a2aprotocol.ai`