ai-assistant-plan.md 45 KB

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)

具体复用方式:

// 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 入口初始化流程:

// 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 项目

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 插件

// .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:启动

# 开发模式(同时启动 VitePress + MCP Server)
pnpm vitepress dev docs

# MCP Server 自动在 http://localhost:4100 启动

Step 5:验证 MCP Server

# 测试 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 中添加:

{
  "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 部署示例:

# 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:

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:

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:

{
  "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:

# 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 加载配置:

cmd := exec.Command("kiro-cli", "acp")
cmd.Dir = serviceDir  // 后端运行目录(含 .kiro/ 和 fwh-mcp-server)

8. 构建与运行

8.1 构建 MCP Tools Server

cd service
go build -o fwh-mcp-server ./cmd/mcp

8.2 本地测试

# 直接运行测试
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 — 发送消息

请求:

{
  "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 实现

核心数据结构:

// 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                  // 项目目录
}

完整通讯流程:

// 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 层:

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:

<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 核心逻辑:

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 基本结构:

<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