|
|
@@ -0,0 +1,1226 @@
|
|
|
+# 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/
|
|
|
+├── <session-id>.json # 会话元数据
|
|
|
+└── <session-id>.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
|
|
|
+<template>
|
|
|
+ <!-- 顶部栏按钮 -->
|
|
|
+ <NButton text @click="drawerShow = true">
|
|
|
+ <template #icon>
|
|
|
+ <span class="i-ph:chat-circle-dots" />
|
|
|
+ </template>
|
|
|
+ </NButton>
|
|
|
+
|
|
|
+ <!-- 侧边抽屉 -->
|
|
|
+ <NDrawer v-model:show="drawerShow" :width="480" placement="right">
|
|
|
+ <NDrawerContent title="AI 助手" closable>
|
|
|
+ <ChatPanel />
|
|
|
+ </NDrawerContent>
|
|
|
+ </NDrawer>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+**`useAiChat.ts` 核心逻辑:**
|
|
|
+
|
|
|
+```typescript
|
|
|
+export function useAiChat() {
|
|
|
+ const messages = ref<ChatMessage[]>([])
|
|
|
+ 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
|
|
|
+<template>
|
|
|
+ <div class="flex h-full flex-col">
|
|
|
+ <!-- 消息列表 -->
|
|
|
+ <div class="flex-1 overflow-y-auto p-4 space-y-4">
|
|
|
+ <MessageItem v-for="msg in messages" :key="msg.id" :message="msg" />
|
|
|
+ <!-- 流式输出中的光标闪烁 -->
|
|
|
+ <span v-if="isStreaming" class="animate-pulse">▊</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 输入区域 -->
|
|
|
+ <div class="border-t p-4">
|
|
|
+ <NInput
|
|
|
+ v-model:value="inputText"
|
|
|
+ type="textarea"
|
|
|
+ :autosize="{ minRows: 1, maxRows: 4 }"
|
|
|
+ placeholder="输入消息...(Shift+Enter 换行)"
|
|
|
+ @keydown.enter.exact.prevent="handleSend"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+### 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`
|