feat: 完善堡垒机与 Agent 智能运维能力
- 支持 Windows Agent 构建、安装脚本和统一上报协议 - 增强 RDP 剪贴板、虚拟盘和文件传输能力 - 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口 - 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
This commit is contained in:
1 parent
a9513c2056
commit
e0d652d211
169 files changed
+12192
-9116
No files matched your search
@@ -1,320 +0,0 @@
|
||||
# AIOps 后端项目总结
|
||||
|
||||
> **注意**:目录结构已重构,入口为 `backend/main.go`,处理器在 `controller/`。
|
||||
> 最新结构见 [project-structure.md](./project-structure.md),部署见 [deployment.md](./deployment.md)。
|
||||
|
||||
## 项目概述
|
||||
|
||||
AIOps 后端是一个基于 Go + Gin 框架开发的专业 AIOps 智能运维平台 API 服务。
|
||||
前端通过 `go:embed` 嵌入 `backend/static/`,支持单二进制与 embed 单容器部署。
|
||||
|
||||
## 项目结构(历史参考,部分路径已变更)
|
||||
|
||||
```
|
||||
backend/
|
||||
├── main.go # 应用入口(当前)
|
||||
├── embed.go # 前端静态资源嵌入
|
||||
├── controller/ # HTTP 处理器(原 handlers)
|
||||
├── internal/ # [已迁移] 见下方历史结构
|
||||
│ ├── api/
|
||||
│ │ ├── handlers/ # HTTP 处理器
|
||||
│ │ │ ├── user.go # 用户管理
|
||||
│ │ │ ├── server.go # 服务器管理
|
||||
│ │ │ ├── dashboard.go # 仪表盘
|
||||
│ │ │ ├── k8s.go # K8s 管理
|
||||
│ │ │ ├── alert.go # 监控告警
|
||||
│ │ │ ├── pipeline.go # 流水线/发布
|
||||
│ │ │ ├── automation.go # 自动化运维
|
||||
│ │ │ ├── approval.go # 审批流程
|
||||
│ │ │ ├── navigation.go # 站内导航
|
||||
│ │ │ └── audit.go # 审计日志
|
||||
│ │ ├── middleware/ # 中间件
|
||||
│ │ │ └── auth.go # JWT 认证、管理员权限
|
||||
│ │ └── routes/ # 路由注册
|
||||
│ │ └── routes.go
|
||||
│ ├── config/ # 配置管理
|
||||
│ │ └── config.go
|
||||
│ └── models/ # 数据模型
|
||||
│ ├── user.go # 用户、角色、权限
|
||||
│ ├── server.go # 服务器、云账号、域名
|
||||
│ ├── k8s.go # K8s 相关模型
|
||||
│ ├── alert.go # 告警相关模型
|
||||
│ ├── cicd.go # CI/CD 相关模型
|
||||
│ ├── automation.go # 自动化运维模型
|
||||
│ ├── approval.go # 审批流程模型
|
||||
│ ├── navigation.go # 导航模型
|
||||
│ ├── audit.go # 审计日志模型
|
||||
│ └── common.go # 通用响应模型
|
||||
├── pkg/
|
||||
│ ├── database/ # 数据库连接
|
||||
│ │ └── database.go
|
||||
│ └── logger/ # 日志工具
|
||||
│ └── logger.go
|
||||
├── scripts/ # 脚本文件
|
||||
│ ├── init.sql # 数据库初始化脚本
|
||||
│ └── mock.go # Mock 数据生成
|
||||
├── config.yaml.example # 配置文件示例
|
||||
├── docker-compose.yml # Docker Compose 配置
|
||||
├── Dockerfile # Docker 构建文件
|
||||
├── Makefile # 构建脚本
|
||||
├── nginx.conf # Nginx 配置
|
||||
├── go.mod # Go 模块定义
|
||||
├── go.sum # Go 模块校验
|
||||
├── .gitignore # Git 忽略文件
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## 功能模块
|
||||
|
||||
### 1. 用户管理
|
||||
- 用户注册、登录、登出
|
||||
- JWT Token 认证
|
||||
- 用户信息管理
|
||||
- 角色权限管理
|
||||
|
||||
### 2. 资产管理
|
||||
- 服务器资产 CRUD
|
||||
- 批量导入(云主机、Excel)
|
||||
- 云账号管理(阿里云、腾讯云、AWS、华为云)
|
||||
- 域名管理(SSL 证书到期提醒)
|
||||
- 区域管理
|
||||
|
||||
### 3. Kubernetes 管理
|
||||
- 集群管理
|
||||
- 节点管理
|
||||
- Pod 管理
|
||||
- Deployment 详情
|
||||
- Service 详情
|
||||
- Ingress 详情
|
||||
|
||||
### 4. 监控告警
|
||||
- 告警规则管理
|
||||
- 告警事件处理
|
||||
- 通知渠道配置
|
||||
- 仪表盘指标展示
|
||||
|
||||
### 5. 应用发布
|
||||
- CI/CD 流水线管理
|
||||
- 发布审批流程
|
||||
- 发布记录追踪
|
||||
- 回滚功能
|
||||
|
||||
### 6. 自动化运维
|
||||
- 定时任务(CronJob)
|
||||
- 脚本库管理
|
||||
- 任务执行记录
|
||||
- Playbook 编排
|
||||
|
||||
### 7. 审批流程
|
||||
- 审批模板管理
|
||||
- 审批流程创建
|
||||
- 审批/驳回操作
|
||||
|
||||
### 8. 站内导航
|
||||
- 导航分组管理
|
||||
- 导航链接管理
|
||||
- 收藏功能
|
||||
|
||||
### 9. 审计日志
|
||||
- 操作日志记录
|
||||
- 日志查询、筛选
|
||||
- 日志导出
|
||||
- 日志统计
|
||||
- 日志清理
|
||||
|
||||
## API 路由列表
|
||||
|
||||
### 认证相关
|
||||
- `POST /api/v1/auth/login` - 登录
|
||||
- `POST /api/v1/auth/register` - 注册
|
||||
- `GET /api/v1/user/profile` - 获取用户信息
|
||||
- `PUT /api/v1/user/profile` - 更新用户信息
|
||||
- `PUT /api/v1/user/password` - 修改密码
|
||||
|
||||
### 用户管理(管理员)
|
||||
- `GET /api/v1/admin/users` - 用户列表
|
||||
- `GET /api/v1/admin/users/:id` - 用户详情
|
||||
- `POST /api/v1/admin/users` - 创建用户
|
||||
- `PUT /api/v1/admin/users/:id` - 更新用户
|
||||
- `DELETE /api/v1/admin/users/:id` - 删除用户
|
||||
|
||||
### 仪表盘
|
||||
- `GET /api/v1/dashboard/stats` - 统计数据
|
||||
- `GET /api/v1/dashboard/recent-alerts` - 最近告警
|
||||
- `GET /api/v1/dashboard/recent-releases` - 最近发布
|
||||
- `GET /api/v1/dashboard/metrics` - 指标数据
|
||||
|
||||
### 服务器管理
|
||||
- `GET /api/v1/servers` - 服务器列表
|
||||
- `GET /api/v1/servers/stats` - 服务器统计
|
||||
- `GET /api/v1/servers/:id` - 服务器详情
|
||||
- `POST /api/v1/servers` - 创建服务器
|
||||
- `PUT /api/v1/servers/:id` - 更新服务器
|
||||
- `DELETE /api/v1/servers/:id` - 删除服务器
|
||||
- `POST /api/v1/servers/batch-delete` - 批量删除
|
||||
|
||||
### 云账号管理
|
||||
- `GET /api/v1/cloud-accounts` - 云账号列表
|
||||
- `POST /api/v1/cloud-accounts` - 创建云账号
|
||||
- `DELETE /api/v1/cloud-accounts/:id` - 删除云账号
|
||||
|
||||
### 域名管理
|
||||
- `GET /api/v1/domains` - 域名列表
|
||||
- `POST /api/v1/domains` - 创建域名
|
||||
- `DELETE /api/v1/domains/:id` - 删除域名
|
||||
|
||||
### Kubernetes 管理
|
||||
- `GET /api/v1/k8s-clusters` - 集群列表
|
||||
- `GET /api/v1/k8s-clusters/:id` - 集群详情
|
||||
- `POST /api/v1/k8s-clusters` - 创建集群
|
||||
- `PUT /api/v1/k8s-clusters/:id` - 更新集群
|
||||
- `DELETE /api/v1/k8s-clusters/:id` - 删除集群
|
||||
- `GET /api/v1/k8s-clusters/:id/nodes` - 节点列表
|
||||
- `GET /api/v1/k8s-clusters/:id/pods` - Pod 列表
|
||||
- `GET /api/v1/k8s-clusters/:id/deployments` - Deployment 列表
|
||||
- `GET /api/v1/k8s-clusters/:id/services` - Service 列表
|
||||
- `GET /api/v1/k8s-clusters/:id/ingresses` - Ingress 列表
|
||||
|
||||
### 监控告警
|
||||
- `GET /api/v1/alerts` - 告警事件列表
|
||||
- `GET /api/v1/alerts/rules` - 告警规则列表
|
||||
- `POST /api/v1/alerts/rules` - 创建告警规则
|
||||
- `PUT /api/v1/alerts/rules/:id` - 更新告警规则
|
||||
- `DELETE /api/v1/alerts/rules/:id` - 删除告警规则
|
||||
- `POST /api/v1/alerts/rules/:id/toggle` - 启用/禁用规则
|
||||
- `POST /api/v1/alerts/:id/ack` - 确认告警
|
||||
|
||||
### 应用发布
|
||||
- `GET /api/v1/pipelines` - 流水线列表
|
||||
- `POST /api/v1/pipelines` - 创建流水线
|
||||
- `GET /api/v1/pipelines/:id` - 流水线详情
|
||||
- `PUT /api/v1/pipelines/:id` - 更新流水线
|
||||
- `DELETE /api/v1/pipelines/:id` - 删除流水线
|
||||
- `POST /api/v1/pipelines/:id/trigger` - 触发流水线
|
||||
- `GET /api/v1/pipelines/:id/runs` - 运行记录
|
||||
- `GET /api/v1/releases` - 发布列表
|
||||
- `POST /api/v1/releases` - 创建发布
|
||||
- `POST /api/v1/releases/:id/approve` - 审批发布
|
||||
- `POST /api/v1/releases/:id/deploy` - 执行发布
|
||||
- `POST /api/v1/releases/:id/rollback` - 回滚发布
|
||||
|
||||
### 自动化运维
|
||||
- `GET /api/v1/cron-jobs` - 定时任务列表
|
||||
- `POST /api/v1/cron-jobs` - 创建定时任务
|
||||
- `GET /api/v1/cron-jobs/:id` - 定时任务详情
|
||||
- `PUT /api/v1/cron-jobs/:id` - 更新定时任务
|
||||
- `DELETE /api/v1/cron-jobs/:id` - 删除定时任务
|
||||
- `POST /api/v1/cron-jobs/:id/toggle` - 启用/禁用任务
|
||||
- `POST /api/v1/cron-jobs/:id/execute` - 立即执行
|
||||
- `GET /api/v1/scripts` - 脚本列表
|
||||
- `POST /api/v1/scripts` - 创建脚本
|
||||
- `GET /api/v1/scripts/:id` - 脚本详情
|
||||
- `PUT /api/v1/scripts/:id` - 更新脚本
|
||||
- `DELETE /api/v1/scripts/:id` - 删除脚本
|
||||
- `POST /api/v1/scripts/:id/execute` - 执行脚本
|
||||
|
||||
### 审批流程
|
||||
- `GET /api/v1/approvals/templates` - 审批模板列表
|
||||
- `POST /api/v1/approvals/templates` - 创建审批模板
|
||||
- `GET /api/v1/approvals/templates/:id` - 模板详情
|
||||
- `PUT /api/v1/approvals/templates/:id` - 更新模板
|
||||
- `DELETE /api/v1/approvals/templates/:id` - 删除模板
|
||||
- `GET /api/v1/approvals/processes` - 审批流程列表
|
||||
- `POST /api/v1/approvals/processes` - 创建审批流程
|
||||
- `GET /api/v1/approvals/processes/:id` - 流程详情
|
||||
- `POST /api/v1/approvals/processes/:id/approve` - 审批通过
|
||||
- `POST /api/v1/approvals/processes/:id/reject` - 审批驳回
|
||||
|
||||
### 站内导航
|
||||
- `GET /api/v1/navigations/groups` - 导航分组列表
|
||||
- `POST /api/v1/navigations/groups` - 创建分组
|
||||
- `PUT /api/v1/navigations/groups/:id` - 更新分组
|
||||
- `DELETE /api/v1/navigations/groups/:id` - 删除分组
|
||||
- `GET /api/v1/navigations/links` - 导航链接列表
|
||||
- `POST /api/v1/navigations/links` - 创建链接
|
||||
- `PUT /api/v1/navigations/links/:id` - 更新链接
|
||||
- `DELETE /api/v1/navigations/links/:id` - 删除链接
|
||||
- `POST /api/v1/navigations/links/:id/favorite` - 收藏/取消收藏
|
||||
|
||||
### 审计日志
|
||||
- `GET /api/v1/audit/logs` - 审计日志列表
|
||||
- `GET /api/v1/audit/logs/:id` - 日志详情
|
||||
- `GET /api/v1/audit/modules` - 模块列表
|
||||
- `GET /api/v1/audit/actions` - 操作类型列表
|
||||
- `GET /api/v1/audit/logs/export` - 导出日志
|
||||
- `GET /api/v1/audit/statistics` - 统计信息
|
||||
- `POST /api/v1/audit/logs/clear` - 清理日志
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 安装依赖
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
go mod download
|
||||
```
|
||||
|
||||
### 2. 配置
|
||||
|
||||
```bash
|
||||
cp config.yaml.example config.yaml
|
||||
# 编辑 config.yaml 修改数据库等配置
|
||||
```
|
||||
|
||||
### 3. 启动数据库
|
||||
|
||||
```bash
|
||||
docker-compose up -d mysql redis
|
||||
```
|
||||
|
||||
### 4. 运行
|
||||
|
||||
```bash
|
||||
# 推荐:项目根目录
|
||||
./run.sh dev
|
||||
|
||||
# 或仅后端
|
||||
cd backend && go run .
|
||||
```
|
||||
|
||||
### 5. 生成 Mock 数据
|
||||
|
||||
```bash
|
||||
go run scripts/mock.go
|
||||
```
|
||||
|
||||
### 6. 访问 API 文档
|
||||
|
||||
打开浏览器访问 http://localhost:8080/swagger/index.html
|
||||
|
||||
## Docker 部署
|
||||
|
||||
```bash
|
||||
# 构建镜像
|
||||
docker build -t ezops-backend .
|
||||
|
||||
# 运行容器
|
||||
docker run -p 8080:8080 -v $(pwd)/config.yaml:/app/config.yaml ezops-backend
|
||||
|
||||
# 或使用 Docker Compose
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**: Gin Web Framework
|
||||
- **ORM**: GORM
|
||||
- **数据库**: MySQL / PostgreSQL
|
||||
- **缓存**: Redis (可选)
|
||||
- **认证**: JWT
|
||||
- **日志**: Zap
|
||||
- **配置**: Viper
|
||||
- **文档**: Swagger
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 生产环境请修改默认的 JWT Secret 和数据库密码
|
||||
2. 建议启用 HTTPS
|
||||
3. 配置适当的日志轮转策略
|
||||
4. 定期备份数据库
|
||||
5. 启用审计日志功能
|
||||
@@ -1,642 +0,0 @@
|
||||
# AIOps 智能运维平台 - 产品设计文档
|
||||
|
||||
> 文档版本: v1.0.0
|
||||
> 编写日期: 2025-04-14
|
||||
> 文档状态: 正式发布
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [产品概述](#1-产品概述)
|
||||
2. [目标用户](#2-目标用户)
|
||||
3. [产品架构](#3-产品架构)
|
||||
4. [功能设计](#4-功能设计)
|
||||
5. [AI能力设计](#5-ai能力设计)
|
||||
6. [界面设计](#6-界面设计)
|
||||
7. [技术方案](#7-技术方案)
|
||||
8. [安全设计](#8-安全设计)
|
||||
9. [部署方案](#9-部署方案)
|
||||
10. [运营计划](#10-运营计划)
|
||||
|
||||
---
|
||||
|
||||
## 1. 产品概述
|
||||
|
||||
### 1.1 产品定位
|
||||
|
||||
AIOps 智能运维平台是一款面向企业级用户的云原生智能运维解决方案,通过深度集成AI大模型能力,实现运维工作的智能化、自动化,帮助企业降低运维成本、提升系统稳定性。
|
||||
|
||||
### 1.2 核心价值
|
||||
|
||||
- **降本增效**: AI辅助决策,减少人工干预,提升运维效率
|
||||
- **预防为主**: 智能预测和预警,防患于未然
|
||||
- **快速恢复**: 智能诊断和自动修复,缩短故障恢复时间
|
||||
- **知识沉淀**: 运维经验AI化,实现知识复用
|
||||
|
||||
### 1.3 产品Slogan
|
||||
|
||||
> **智能运维,让系统更稳定,让团队更高效**
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标用户
|
||||
|
||||
### 2.1 用户画像
|
||||
|
||||
| 用户类型 | 角色描述 | 核心需求 | 使用场景 |
|
||||
|---------|---------|---------|---------|
|
||||
| 运维工程师 | 日常运维执行者 | 快速定位问题、自动化操作 | 故障处理、日常巡检 |
|
||||
| 运维主管 | 团队管理者 | 全局视图、效率提升 | 团队管理、决策支持 |
|
||||
| SRE工程师 | 站点可靠性工程师 | 系统稳定性、性能优化 | 容量规划、性能调优 |
|
||||
| DevOps工程师 | 开发运维一体化 | CI/CD、自动化发布 | 应用发布、流水线管理 |
|
||||
| 技术总监 | 技术决策者 | 成本优化、风险评估 | 技术选型、预算规划 |
|
||||
|
||||
### 2.2 用户场景
|
||||
|
||||
#### 场景1: 故障快速响应
|
||||
```
|
||||
用户: 运维工程师小王
|
||||
场景: 凌晨2点收到告警,生产环境服务响应缓慢
|
||||
流程:
|
||||
1. 收到AI告警分析推送,了解根因是数据库连接池耗尽
|
||||
2. 查看AI推荐的解决方案
|
||||
3. 一键执行自动修复脚本
|
||||
4. 验证修复效果,故障恢复
|
||||
时间: 从30分钟缩短到5分钟
|
||||
```
|
||||
|
||||
#### 场景2: 日常运维自动化
|
||||
```
|
||||
用户: SRE工程师小李
|
||||
场景: 需要每日巡检100+台服务器
|
||||
流程:
|
||||
1. 使用AI脚本生成器创建巡检脚本
|
||||
2. 配置定时任务自动执行
|
||||
3. AI分析巡检结果,生成报告
|
||||
4. 异常情况自动创建告警
|
||||
效果: 节省2小时/天
|
||||
```
|
||||
|
||||
#### 场景3: 容量规划决策
|
||||
```
|
||||
用户: 技术总监老张
|
||||
场景: 规划下季度服务器采购预算
|
||||
流程:
|
||||
1. 查看AI预测分析,了解资源使用趋势
|
||||
2. 获取AI推荐的扩容方案
|
||||
3. 对比不同方案的成本和性能
|
||||
4. 做出最优决策
|
||||
效果: 成本节省20%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 产品架构
|
||||
|
||||
### 3.1 系统架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 展示层 │
|
||||
│ ┌──────────────┬──────────────┬──────────────┬──────────────┐ │
|
||||
│ │ Web控制台 │ 移动App │ 可视化大屏 │ CLI工具 │ │
|
||||
│ └──────────────┴──────────────┴──────────────┴──────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 接入层 │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ API网关 (认证/限流/路由/日志) │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 服务层 │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐│
|
||||
│ │ 资产管理 │ │ 应用发布 │ │ 监控告警 │ │ 自动化 │ │ AIOps ││
|
||||
│ │ 服务 │ │ 服务 │ │ 服务 │ │ 服务 │ │ 服务 ││
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └────────┘│
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 数据层 │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐│
|
||||
│ │ MySQL │ │ Redis │ │ Prometheus│ │ ES │ │ MinIO ││
|
||||
│ │ 业务数据 │ │ 缓存 │ │ 时序数据 │ │ 日志 │ │ 对象存储││
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └────────┘│
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ AI服务层 │
|
||||
│ ┌──────────┬──────────┬──────────┬──────────┬──────────────┐ │
|
||||
│ │ OpenAI │Anthropic │ Azure │ Ollama │ 本地模型 │ │
|
||||
│ │ GPT-4 │ Claude │ OpenAI │ 本地部署 │ 微调模型 │ │
|
||||
│ └──────────┴──────────┴──────────┴──────────┴──────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 模块划分
|
||||
|
||||
| 模块名称 | 功能描述 | 优先级 |
|
||||
|---------|---------|--------|
|
||||
| 资产管理 | 服务器、云资源、域名等资产全生命周期管理 | P0 |
|
||||
| 应用发布 | CI/CD流水线、发布管理、版本控制 | P0 |
|
||||
| 监控告警 | 指标采集、告警管理、可视化大屏 | P0 |
|
||||
| 自动化运维 | 定时任务、脚本管理、Ansible执行 | P1 |
|
||||
| AIOps智能 | AI助手、告警分析、故障诊断、预测分析 | P0 |
|
||||
| 系统管理 | 用户权限、操作审计、系统配置 | P1 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 功能设计
|
||||
|
||||
### 4.1 资产管理模块
|
||||
|
||||
#### 4.1.1 服务器管理
|
||||
|
||||
**功能列表:**
|
||||
- 服务器CRUD操作
|
||||
- 批量导入 (Excel/云API)
|
||||
- 标签管理
|
||||
- 远程WebShell
|
||||
- 状态监控
|
||||
|
||||
**数据模型:**
|
||||
```typescript
|
||||
interface Server {
|
||||
id: string;
|
||||
name: string;
|
||||
hostname: string;
|
||||
ip: string;
|
||||
os: string;
|
||||
cpu: number;
|
||||
memory: number;
|
||||
disk: number;
|
||||
status: 'running' | 'stopped' | 'error';
|
||||
tags: string[];
|
||||
cloudProvider?: string;
|
||||
region?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.1.2 云账号管理
|
||||
|
||||
**功能列表:**
|
||||
- 多云账号接入 (阿里云/腾讯云/AWS)
|
||||
- AK/SK安全存储
|
||||
- 资源自动同步
|
||||
- 成本分析
|
||||
|
||||
### 4.2 应用发布模块
|
||||
|
||||
#### 4.2.1 流水线管理
|
||||
|
||||
**功能列表:**
|
||||
- 可视化流水线配置
|
||||
- 多阶段构建
|
||||
- 并行/串行执行
|
||||
- 审批节点
|
||||
|
||||
**流水线阶段:**
|
||||
```yaml
|
||||
stages:
|
||||
- name: 代码检出
|
||||
type: checkout
|
||||
- name: 构建
|
||||
type: build
|
||||
- name: 单元测试
|
||||
type: test
|
||||
- name: 镜像构建
|
||||
type: docker_build
|
||||
- name: 部署
|
||||
type: deploy
|
||||
approval: true
|
||||
- name: 健康检查
|
||||
type: health_check
|
||||
```
|
||||
|
||||
#### 4.2.2 发布管理
|
||||
|
||||
**功能列表:**
|
||||
- 发布申请
|
||||
- 发布审批
|
||||
- 发布历史
|
||||
- 一键回滚
|
||||
|
||||
### 4.3 监控告警模块
|
||||
|
||||
#### 4.3.1 指标监控
|
||||
|
||||
**支持指标:**
|
||||
- CPU使用率
|
||||
- 内存使用率
|
||||
- 磁盘使用率
|
||||
- 网络流量
|
||||
- 应用性能指标 (QPS/RT/错误率)
|
||||
|
||||
#### 4.3.2 告警中心
|
||||
|
||||
**功能列表:**
|
||||
- 告警规则配置
|
||||
- 告警级别划分 (严重/警告/提示)
|
||||
- 告警通知 (邮件/短信/钉钉/企业微信)
|
||||
- 告警抑制和聚合
|
||||
- AI告警分析
|
||||
|
||||
### 4.4 自动化运维模块
|
||||
|
||||
#### 4.4.1 定时任务
|
||||
|
||||
**功能列表:**
|
||||
- Cron表达式配置
|
||||
- 任务执行日志
|
||||
- 失败重试机制
|
||||
- 任务依赖管理
|
||||
|
||||
#### 4.4.2 Ansible管理
|
||||
|
||||
**功能列表:**
|
||||
- Playbook管理
|
||||
- 主机清单管理
|
||||
- 任务执行
|
||||
- 执行结果展示
|
||||
|
||||
### 4.5 K8s集群管理
|
||||
|
||||
**功能列表:**
|
||||
- 多集群管理
|
||||
- 节点管理
|
||||
- 工作负载管理 (Deployment/Service/Ingress)
|
||||
- 架构拓扑可视化
|
||||
- 资源监控
|
||||
|
||||
---
|
||||
|
||||
## 5. AI能力设计
|
||||
|
||||
### 5.1 AI架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ AI能力层 │
|
||||
│ ┌───────────────────────────────────────────────────────┐ │
|
||||
│ │ AI编排引擎 │ │
|
||||
│ │ (Prompt管理/上下文管理/工具调用/结果解析) │ │
|
||||
│ └───────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────┬──────────────┬──────────────┬──────────┐ │
|
||||
│ │ 告警分析 │ 脚本生成 │ 故障诊断 │ 预测分析 │ │
|
||||
│ │ Agent │ Agent │ Agent │ Agent │ │
|
||||
│ └──────────────┴──────────────┴──────────────┴──────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────────────────────────────────────────────┐ │
|
||||
│ │ 模型接入层 │ │
|
||||
│ │ OpenAI / Anthropic / Azure / Ollama / 本地模型 │ │
|
||||
│ └───────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 AI告警分析
|
||||
|
||||
**功能描述:**
|
||||
- 接收告警事件
|
||||
- 关联相关日志和指标
|
||||
- AI分析根因
|
||||
- 生成修复建议
|
||||
- 推荐自动修复脚本
|
||||
|
||||
**Prompt设计:**
|
||||
```
|
||||
你是一位资深的运维专家,请分析以下告警信息:
|
||||
|
||||
告警名称: {alert_name}
|
||||
告警级别: {severity}
|
||||
告警对象: {target}
|
||||
告警时间: {time}
|
||||
告警详情: {details}
|
||||
|
||||
相关日志:
|
||||
{logs}
|
||||
|
||||
相关指标:
|
||||
{metrics}
|
||||
|
||||
请提供:
|
||||
1. 根因分析 (置信度)
|
||||
2. 影响范围评估
|
||||
3. 修复建议 (按优先级排序)
|
||||
4. 自动修复脚本 (如适用)
|
||||
5. 相似历史案例
|
||||
```
|
||||
|
||||
### 5.3 AI脚本生成
|
||||
|
||||
**功能描述:**
|
||||
- 自然语言描述需求
|
||||
- AI生成运维脚本
|
||||
- 支持多语言 (Bash/Python/Ansible)
|
||||
- 一键测试执行
|
||||
|
||||
**Prompt设计:**
|
||||
```
|
||||
你是一位运维脚本专家,请根据以下需求生成{language}脚本:
|
||||
|
||||
需求描述: {description}
|
||||
目标系统: {os}
|
||||
执行环境: {environment}
|
||||
|
||||
要求:
|
||||
1. 添加详细的注释
|
||||
2. 包含错误处理
|
||||
3. 添加日志输出
|
||||
4. 考虑安全性
|
||||
|
||||
请生成完整的可执行脚本。
|
||||
```
|
||||
|
||||
### 5.4 AI故障诊断
|
||||
|
||||
**功能描述:**
|
||||
- 接收故障症状描述
|
||||
- AI分析可能原因
|
||||
- 提供诊断步骤
|
||||
- 推荐解决方案
|
||||
|
||||
### 5.5 AI预测分析
|
||||
|
||||
**功能描述:**
|
||||
- 分析历史指标数据
|
||||
- 预测未来趋势
|
||||
- 容量规划建议
|
||||
- 异常预警
|
||||
|
||||
**预测模型:**
|
||||
- 时间序列分析 (ARIMA/Prophet)
|
||||
- 异常检测 (Isolation Forest)
|
||||
- 趋势预测 (LSTM)
|
||||
|
||||
---
|
||||
|
||||
## 6. 界面设计
|
||||
|
||||
### 6.1 设计原则
|
||||
|
||||
- **简洁高效**: 减少用户操作步骤,提升效率
|
||||
- **信息密度**: 合理的信息展示,避免信息过载
|
||||
- **一致性**: 统一的视觉风格和交互模式
|
||||
- **响应式**: 适配不同屏幕尺寸
|
||||
|
||||
### 6.2 色彩系统
|
||||
|
||||
```css
|
||||
/* 主色调 */
|
||||
--primary: #3b82f6; /* 蓝色 - 主要操作 */
|
||||
--primary-hover: #2563eb;
|
||||
|
||||
/* 功能色 */
|
||||
--success: #10b981; /* 绿色 - 成功 */
|
||||
--warning: #f59e0b; /* 橙色 - 警告 */
|
||||
--danger: #ef4444; /* 红色 - 危险 */
|
||||
--info: #06b6d4; /* 青色 - 信息 */
|
||||
|
||||
/* 中性色 */
|
||||
--slate-900: #0f172a; /* 深色背景 */
|
||||
--slate-800: #1e293b; /* 卡片背景 */
|
||||
--slate-700: #334155; /* 边框 */
|
||||
--slate-100: #f1f5f9; /* 浅色文字 */
|
||||
|
||||
/* AI强调色 */
|
||||
--ai-purple: #8b5cf6; /* 紫色 - AI功能 */
|
||||
--ai-pink: #ec4899; /* 粉色 - AI强调 */
|
||||
```
|
||||
|
||||
### 6.3 布局结构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Header (Logo + 全局搜索 + 通知 + 用户头像) │
|
||||
├──────────┬──────────────────────────────────────────────────┤
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ Sidebar │ Main Content │
|
||||
│ (菜单) │ (主内容区) │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
└──────────┴──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.4 核心页面
|
||||
|
||||
#### 仪表盘
|
||||
- 系统健康度卡片
|
||||
- 成本费用图表
|
||||
- Top资源使用排行
|
||||
- 告警消息列表
|
||||
- 快捷操作入口
|
||||
|
||||
#### AI智能助手
|
||||
- 对话界面
|
||||
- 快捷操作面板
|
||||
- 能力展示
|
||||
- 模型配置
|
||||
|
||||
---
|
||||
|
||||
## 7. 技术方案
|
||||
|
||||
### 7.1 技术栈
|
||||
|
||||
| 层级 | 技术选型 | 版本 |
|
||||
|-----|---------|------|
|
||||
| 前端 | React + TypeScript + Vite | React 18 |
|
||||
| UI组件 | shadcn/ui + Tailwind CSS | v2.x |
|
||||
| 状态管理 | Zustand | v4.x |
|
||||
| 后端 | Go + Gin | Go 1.22 |
|
||||
| ORM | GORM | v2.x |
|
||||
| 数据库 | MySQL + Redis | 8.0 / 7.x |
|
||||
| 消息队列 | RabbitMQ | 3.x |
|
||||
| 监控 | Prometheus + Grafana | - |
|
||||
| AI服务 | OpenAI/Anthropic/Azure | - |
|
||||
|
||||
### 7.2 API设计
|
||||
|
||||
**RESTful API规范:**
|
||||
```
|
||||
GET /api/v1/servers # 列表查询
|
||||
GET /api/v1/servers/:id # 详情查询
|
||||
POST /api/v1/servers # 创建
|
||||
PUT /api/v1/servers/:id # 更新
|
||||
DELETE /api/v1/servers/:id # 删除
|
||||
```
|
||||
|
||||
**AI服务API:**
|
||||
```
|
||||
POST /api/v1/ai/chat # AI对话
|
||||
POST /api/v1/ai/analyze-alert # 告警分析
|
||||
POST /api/v1/ai/generate-script # 脚本生成
|
||||
POST /api/v1/ai/diagnose # 故障诊断
|
||||
POST /api/v1/ai/predict # 预测分析
|
||||
```
|
||||
|
||||
### 7.3 数据模型
|
||||
|
||||
**核心实体关系:**
|
||||
```
|
||||
User (1) ----< (N) Role
|
||||
User (1) ----< (N) OperationLog
|
||||
|
||||
Server (N) --> (1) CloudAccount
|
||||
Server (N) --> (N) Tag
|
||||
|
||||
Cluster (1) ----< (N) Node
|
||||
Cluster (1) ----< (N) Workload
|
||||
|
||||
Pipeline (1) ----< (N) PipelineStage
|
||||
Pipeline (1) ----< (N) Release
|
||||
|
||||
AlertRule (1) ----< (N) AlertEvent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 安全设计
|
||||
|
||||
### 8.1 认证授权
|
||||
|
||||
- JWT Token认证
|
||||
- RBAC权限模型
|
||||
- 操作审计日志
|
||||
- 敏感数据加密
|
||||
|
||||
### 8.2 数据安全
|
||||
|
||||
- 数据库连接加密
|
||||
- 敏感配置加密存储
|
||||
- 定期数据备份
|
||||
- 操作日志保留
|
||||
|
||||
### 8.3 网络安全
|
||||
|
||||
- HTTPS传输加密
|
||||
- API限流保护
|
||||
- SQL注入防护
|
||||
- XSS攻击防护
|
||||
|
||||
---
|
||||
|
||||
## 9. 部署方案
|
||||
|
||||
### 9.1 部署架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 负载均衡层 │
|
||||
│ Nginx / Traefik │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 应用服务层 │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Frontend │ │ Backend │ │ AI │ │
|
||||
│ │ (Nginx) │ │ (Gin) │ │ Service │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 数据存储层 │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ MySQL │ │ Redis │ │Prometheus│ │ ES │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 9.2 部署方式
|
||||
|
||||
**Docker Compose (开发/测试):**
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
frontend:
|
||||
image: ezops/frontend:latest
|
||||
ports:
|
||||
- "80:80"
|
||||
|
||||
backend:
|
||||
image: ezops/backend:latest
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
```
|
||||
|
||||
**Kubernetes (生产):**
|
||||
- Helm Chart部署
|
||||
- ConfigMap配置管理
|
||||
- Secret密钥管理
|
||||
- HPA自动扩缩容
|
||||
|
||||
---
|
||||
|
||||
## 10. 运营计划
|
||||
|
||||
### 10.1 版本发布计划
|
||||
|
||||
| 版本 | 发布日期 | 主要功能 | 目标用户 |
|
||||
|-----|---------|---------|---------|
|
||||
| v0.1.0 | 2025-04 | MVP核心功能 | 早期用户 |
|
||||
| v0.2.0 | 2025-06 | AI能力增强 | 技术团队 |
|
||||
| v0.3.0 | 2025-09 | 企业级特性 | 企业客户 |
|
||||
| v0.4.0 | 2025-12 | 全栈智能化 | 大规模用户 |
|
||||
| v1.0.0 | 2026-Q1 | 正式发布 | 全量用户 |
|
||||
|
||||
### 10.2 成功指标
|
||||
|
||||
| 指标 | 目标值 | 测量方式 |
|
||||
|-----|-------|---------|
|
||||
| 用户活跃度 | DAU > 1000 | 系统统计 |
|
||||
| 故障响应时间 | < 5分钟 | 告警系统 |
|
||||
| AI分析准确率 | > 85% | 用户反馈 |
|
||||
| 用户满意度 | NPS > 50 | 问卷调查 |
|
||||
| 系统可用性 | 99.9% | 监控数据 |
|
||||
|
||||
### 10.3 迭代策略
|
||||
|
||||
- **双周迭代**: 每两周发布一个小版本
|
||||
- **月度规划**: 每月制定功能规划
|
||||
- **季度复盘**: 每季度进行产品复盘
|
||||
- **年度规划**: 每年制定产品路线图
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
### A. 术语表
|
||||
|
||||
| 术语 | 说明 |
|
||||
|-----|------|
|
||||
| AIOps | 人工智能运维,将AI能力应用于IT运维 |
|
||||
| SRE | 站点可靠性工程 |
|
||||
| DevOps | 开发运维一体化 |
|
||||
| CI/CD | 持续集成/持续部署 |
|
||||
| MTTR | 平均修复时间 |
|
||||
| MTBF | 平均故障间隔时间 |
|
||||
|
||||
### B. 参考资料
|
||||
|
||||
- [Google SRE Book](https://sre.google/sre-book/table-of-contents/)
|
||||
- [The Phoenix Project](https://itrevolution.com/the-phoenix-project/)
|
||||
- [AIOps白皮书](https://www.gartner.com/en/documents/aiops)
|
||||
|
||||
---
|
||||
|
||||
*本文档由AIOps产品团队维护,如有疑问请联系 product@ezops.io*
|
||||
@@ -1,125 +0,0 @@
|
||||
# AIOps 智能运维平台 - 项目总结
|
||||
|
||||
> 最新目录结构见 [project-structure.md](./project-structure.md),部署见 [deployment.md](./deployment.md)。
|
||||
|
||||
## 项目概述
|
||||
|
||||
AIOps 是一个企业级云原生智能运维平台,包含完整的前后端代码与独立 Agent 模块。平台聚焦资产管理、Kubernetes 管理、应用发布、监控告警、自动化运维、审批流程和 AI 智能分析。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
aiops/
|
||||
├── frontend/ # React 19 + TypeScript + Vite
|
||||
│ ├── src/api/ # API 封装(按菜单域拆分)
|
||||
│ ├── src/pages/ # 复杂域级页面
|
||||
│ ├── src/components/ # 业务组件 + layout(PageShell 等)
|
||||
│ ├── src/locales/ # i18n 翻译 (zh-CN / en-US)
|
||||
│ └── src/i18n/ # i18next 初始化
|
||||
│
|
||||
├── backend/ # Go 1.24 + Gin + GORM
|
||||
│ ├── main.go # 应用入口
|
||||
│ ├── embed.go # go:embed 前端静态资源
|
||||
│ ├── controller/ # HTTP 处理器(60+ 文件)
|
||||
│ ├── model/ # GORM 数据模型(86+ 表)
|
||||
│ ├── router/ # 路由注册
|
||||
│ └── pkg/ # agentcli / remoteexec / kubernetesclient 等
|
||||
│
|
||||
├── agent/ # 监控 Agent(独立 Go module)
|
||||
├── docs/ # 开发文档 + Agent CLI 设计文档集
|
||||
├── deploy/kubernetes/ # K8s 部署清单
|
||||
├── Dockerfile # 推荐生产镜像
|
||||
├── docker-compose.yml # 3 容器部署
|
||||
└── run.sh # 统一入口脚本
|
||||
```
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 1. 仪表盘
|
||||
- 资源概览统计、成本费用分析
|
||||
- Top5 CPU/内存/磁盘使用率
|
||||
- 证书到期提醒、最近告警和发布记录
|
||||
- 国际化支持(dashboard 命名空间)
|
||||
|
||||
### 2. 服务器资产管理
|
||||
- 服务器列表 CRUD、批量导入(云主机 / Excel)
|
||||
- 远程 WebShell(WebSocket + PTY)
|
||||
- 云账号 / 域名 / 区域 / 云平台 / 账单管理
|
||||
- 资源模型管理
|
||||
|
||||
### 3. Kubernetes 集群管理
|
||||
- 多集群接入、节点 / Pod / Workload 管理
|
||||
- Deployment 三模式编辑器(表单 / YAML / 可视化)
|
||||
- Pod 终端 / 文件浏览、拓扑架构图
|
||||
- 国际化支持(k8s 命名空间,184+ 翻译键)
|
||||
|
||||
### 4. 监控告警
|
||||
- 告警规则 / 事件 / 通知渠道 / 静默规则
|
||||
- 主机指标采集(Agent 双通道)、SNMP / 站点拨测
|
||||
- **中间件监控**:Redis / MySQL 后端直连采集
|
||||
- **日志中心**:Loki / ES 统一查询、日志告警引擎
|
||||
- Prometheus / Zabbix / Grafana 数据源代理
|
||||
|
||||
### 5. 应用发布
|
||||
- CI/CD 流水线(GitLab CI 风格可视化)
|
||||
- 发布配置 / 环境 / 权限管理
|
||||
- SSH + K8s 双路径部署
|
||||
- 凭证管理(7 种统一类型)
|
||||
|
||||
### 6. 自动化运维
|
||||
- 定时任务(CronJob)、脚本库管理
|
||||
- **远程执行**(Go-native SSH,执行 / 上传)
|
||||
- 任务执行记录
|
||||
|
||||
### 7. AIOps 智能
|
||||
- AI 智能助手、告警分析、容量预测
|
||||
- **Agent CLI**:ReAct 16 轮循环 + WebSocket 实时回传 + Evolver 自进化
|
||||
- 大模型设置(OpenAI 兼容 + Anthropic Messages 双协议)
|
||||
- 终端审计回放
|
||||
|
||||
### 8. 审批流程
|
||||
- 审批模板 / 流程 / 分类 / 权限管理
|
||||
- 云服务器 / 应用部署申请
|
||||
- 国际化支持(approval 命名空间)
|
||||
|
||||
### 9. 系统管理
|
||||
- 用户 / 角色 / RBAC 权限
|
||||
- 操作审计 / 登录日志 / 终端会话审计
|
||||
- LDAP 集成、凭证配置
|
||||
- 7 套主题(CSS 变量驱动)
|
||||
|
||||
## UI 设计系统
|
||||
|
||||
- **PageShell**:全站统一页面骨架(标题 / 指标条 / 工具栏 / Tab / 内容区)
|
||||
- **CSS 设计令牌**:`dg-page*` 类名 + `var(--dg-xxx)` 主题变量
|
||||
- **7 套主题**:跟随系统、深海蓝、明亮白 + One Dark / Dracula / Tokyo Night / Catppuccin
|
||||
|
||||
## 技术亮点
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| 单二进制部署 | go:embed 嵌入前端,零外部依赖 |
|
||||
| 轻量 K8s 客户端 | 自研 HTTP 客户端,不引入 client-go |
|
||||
| Go-native SSH | 替代 Ansible,零新增依赖 |
|
||||
| Agent 双通道 | 指标 10s 上报 + 任务 5s 轮询 |
|
||||
| 凭证驱动 | 7 种凭证类型统一加密存储 |
|
||||
| 工具懒加载 | kubectl / maven / helm 首次使用自动下载 |
|
||||
| 国际化 | i18next 中/英双语,按模块命名空间管理 |
|
||||
|
||||
## 部署方式
|
||||
|
||||
| 方式 | 命令 | 说明 |
|
||||
|------|------|------|
|
||||
| 本地开发 | `./run.sh dev` | 5173 + 8080 热重载 |
|
||||
| 独立二进制 | `./run.sh build` | 产出 `./aiops` |
|
||||
| Docker Compose | `./run.sh docker up` | MySQL + Redis + AIOps |
|
||||
| Kubernetes | `./run.sh kubernetes deploy` | devops 命名空间 |
|
||||
| 一键发版 | `./run.sh release v1.5.0` | tag + 镜像 + 推送 |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [开发文档索引](./README.md)
|
||||
- [Agent CLI 设计集](./agent/00-README.md)
|
||||
- [后端摘要](./BACKEND_SUMMARY.md)
|
||||
- [API 接口](./api-reference.md)
|
||||
- [数据库设计](./database-schema.md)
|
||||
+25
-12
@@ -2,6 +2,8 @@
|
||||
|
||||
本目录包含 AIOps 智能运维平台的开发文档。
|
||||
|
||||
根目录只保留通用入口文档:快速开始、部署、项目结构、开发规范和总索引。业务模块、实现规格和历史说明均放入对应子目录,避免根目录继续堆积长文档。// Logan.Li
|
||||
|
||||
## 文档索引
|
||||
|
||||
| 文档 | 说明 |
|
||||
@@ -10,20 +12,30 @@
|
||||
| [部署指南](./deployment.md) | 二进制 / Docker / K8s 部署、版本发版、镜像推送 |
|
||||
| [项目结构](./project-structure.md) | 前后端目录结构说明 |
|
||||
| [开发规范](./development-guide.md) | 代码规范、Git 工作流、PageShell / i18n |
|
||||
| [API 接口](./api-reference.md) | 后端 API 接口文档 |
|
||||
| [数据库设计](./database-schema.md) | 数据模型和表结构 |
|
||||
| [开发日志](./dev-log.md) | 详细开发日志(按日期记录) |
|
||||
| [Web RDP 远程桌面](./rdp.md) | 自研 RDP 协议栈、WebSocket 帧协议(归档:`/opt/rdp/`) |
|
||||
| [Redis 缓存策略](./redis-caching-strategy.md) | Redis 缓存使用现状与优化方案 |
|
||||
| [API 接口](./reference/api-reference.md) | 后端 API 接口文档 |
|
||||
| [数据库设计](./reference/database-schema.md) | 数据模型和表结构 |
|
||||
| [模块文档规划](./modules/README.md) | 日志中心、监控中心、安全审计/堡垒机、资产凭证等大模块文档路线 |
|
||||
| [安全审计与堡垒机](./security-bastion/README.md) | SSH/RDP/Pod/kubectl 会话、录像、命令、策略、审批统一设计 |
|
||||
| [监控中心](./monitoring/README.md) | Agent 指标、Prometheus/Zabbix、SNMP、站点拨测、告警引擎 |
|
||||
| [日志中心](./log-center/README.md) | Loki/ES 查询、日志数据源、日志告警、日志 RBAC |
|
||||
| [资产与凭证](./asset-credential/README.md) | 资产模型、统一凭证、云同步、堡垒机权限底座 |
|
||||
| [Kubernetes 管理](./kubernetes/README.md) | 自研 K8s 客户端、Pod exec/files、YAML、AI 排障 |
|
||||
| [自动化运维与发布](./automation-deploy/README.md) | 脚本库、定时任务、远程执行、流水线、Workspace |
|
||||
| [客户端 Agent](./client-agent/README.md) | 主机采集 Agent 的指标采集、上报、任务轮询和安装升级设计 |
|
||||
| [客户端 Agent 实现规格](./client-agent/implementation-spec.md) | 可复刻的 Linux 主机采集 Agent 和后端接收链路规格 |
|
||||
| [终端体系设计](./terminal/README.md) | WebSSH、Pod exec、kubectl、Workspace、审计回放等 xterm 终端统一设计 |
|
||||
| [终端实现规格](./terminal/implementation-spec.md) | 可复刻的 xterm/WebSocket/PTY/K8s 终端实现细节和验收清单 |
|
||||
| [Web RDP 远程桌面](./rdp/README.md) | 自研 RDP 协议栈、剪贴板、RDPDR 虚拟盘、文件传输、会话录像与审计 |
|
||||
| [RDP 实现规格](./rdp/implementation-spec.md) | 可复刻的 Web RDP 图形协议、虚拟通道、文件传输、审计回放实现细节 |
|
||||
| [AI Agent / Agent CLI 设计集](./ai-agent/00-README.md) | AIOps 原生 Web Agent:ReAct、工具 ACI、安全治理、会话记忆、路线评测 |
|
||||
| [Redis 缓存策略](./platform/redis-caching-strategy.md) | Redis/redka 缓存使用现状与优化方案 |
|
||||
| [前端视觉规范](./frontend/design-system.md) | PageShell、主题变量、业务页面视觉规范 |
|
||||
|
||||
## 设计文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [Agent CLI 设计集](./agent/00-README.md) | ReAct 引擎、Evolver、安全系统、会话管理(10 篇) |
|
||||
| [PDD 计划](./PDD_PLAN.md) | 产品驱动开发计划 |
|
||||
| [项目摘要](./PROJECT_SUMMARY.md) | 项目整体概述 |
|
||||
| [后端摘要](./BACKEND_SUMMARY.md) | 后端架构概述 |
|
||||
| [AI Agent / Agent CLI 设计集](./ai-agent/00-README.md) | 当前压缩为 7 篇:定位、架构、执行循环、工具 ACI、安全治理、会话记忆、路线评测 |
|
||||
|
||||
## 根目录文档
|
||||
|
||||
@@ -41,12 +53,13 @@
|
||||
| PageShell 设计系统 | 全站统一页面骨架,`dg-page*` CSS 变量 + 7 套主题 |
|
||||
| 前端架构对齐 | `api/` + `pages/` 按菜单域拆分,复杂页面迁入 `pages/` |
|
||||
| 国际化 (i18n) | i18next 中/英双语,已覆盖 dashboard / server / alert / approval / audit / deploy / k8s |
|
||||
| Agent CLI 优化 | 终端审计回放、ssh-key 连接修复、会话管理增强 |
|
||||
| Agent CLI 优化 | Web Agent 执行流、工具 ACI、安全治理、会话记忆和评测路线文档统一 |
|
||||
| 中间件监控 | Redis / MySQL 后端直连采集引擎 |
|
||||
| 日志告警 | AlertEngine 支持 `type=log` 规则,查询 Loki / ES 触发/恢复 |
|
||||
| 远程执行 | 新增远程执行页(执行/上传),移除 ansible 命名 |
|
||||
| 大模型双协议 | OpenAI 兼容 + Anthropic Messages 协议接入 |
|
||||
| Web RDP 自研引擎 | 移除 grdp,SessionEngine 完整图形会话,见 [rdp.md](./rdp.md) |
|
||||
| Web RDP 自研引擎 | 移除 grdp,SessionEngine 完整图形会话,见 [docs/rdp](./rdp/README.md) |
|
||||
| 文档模块化 | 根目录瘦身,新增 security-bastion / monitoring / log-center / asset-credential / kubernetes / automation-deploy |
|
||||
|
||||
## 部署速查
|
||||
|
||||
@@ -68,4 +81,4 @@ Compose: ./run.sh docker up → mysql + redis + aiops (embed, :8080)
|
||||
K8s: ./run.sh kubernetes → lghost/aiops:<tag> embed 单 Pod
|
||||
```
|
||||
|
||||
> 前后端分离的 Nginx 容器已废弃,统一使用 `Dockerfile` 单容器方案。
|
||||
> 前后端分离的 Nginx 容器已废弃,统一使用 `Dockerfile` 单容器方案。
|
||||
+4
-71
@@ -1,74 +1,7 @@
|
||||
# Agent CLI 设计文档(aiops)
|
||||
# Agent CLI 设计文档已迁移
|
||||
|
||||
> 本文由 opsxcli 同名设计文档集适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/`)。
|
||||
本文档已迁移到:
|
||||
|
||||
本系列文档是 **aiops Agent CLI**(Web 版 AI 运维 Agent)的设计文档集。Agent CLI 把 opsxcli 项目的 AI Agent 能力(ReAct 推理、工具调用、自进化、安全审批)移植到 aiops 的 Gin 后端,剥离 CLI/TUI 外壳,改造成一个由 WebSocket 驱动、与 React 前端对话的「类似 Claude Code / Codex」的运维 Agent 库。
|
||||
- [docs/ai-agent/00-README.md](../ai-agent/00-README.md)
|
||||
|
||||
一句话定位:**用户在 Agent CLI 页面选择目标主机,用自然语言描述任务,后端 ReAct 引擎自主通过 SSH 在远程服务器上排障、修复,并通过 WebSocket 把思考、命令、输出实时回传给前端。**
|
||||
|
||||
---
|
||||
|
||||
## 文档目录
|
||||
|
||||
| 编号 | 文档 | 一句话简介 |
|
||||
|------|------|-----------|
|
||||
| 01 | [项目概述与设计哲学](01-项目概述与设计哲学.md) | Agent CLI 的定位、四大设计哲学、与 opsxcli 的关系 |
|
||||
| 02 | [整体架构设计](02-整体架构设计.md) | 引擎五层结构(controller / model / pkg / frontend / evolver)与执行流程 |
|
||||
| 03 | [Harness 工程化与反馈回路](03-Harness工程化与反馈回路.md) | Harness Engineering 理论在 aiops Agent CLI 中的落地(前馈 + 反馈 + Steering) |
|
||||
| 04 | [插件架构设计](04-插件架构设计.md) | Tool 接口 + Registry 工具注册中心,Web 版精简为 2 个工具 |
|
||||
| 05 | [Builtin 内置工具](05-Builtin内置命令.md) | ssh_execute 与 analyze_output 两个内置工具的设计 |
|
||||
| 06 | [Agent 核心引擎设计](06-Agent核心引擎设计.md) | ReAct 16 轮主循环、自适应检查点、循环检测、Evolver 集成 |
|
||||
| 07 | [Prompt 构建与记忆注入](07-Prompt构建与记忆注入.md) | 五层 System Prompt 与动态记忆注入 |
|
||||
| 08 | [Evolver 自进化引擎](08-Evolver自进化引擎.md) | 四层记忆 + 精简进化循环(OBSERVE→…→PREDICT) |
|
||||
| 09 | [安全系统与工具注册](09-安全系统与工具注册.md) | 三级安全模式、五级风险、黑名单、WebSocket 确认 |
|
||||
| 10 | [会话管理](10-会话管理.md) | GORM 数据库表持久化会话与消息(多用户隔离) |
|
||||
|
||||
---
|
||||
|
||||
## opsxcli → aiops 映射总览
|
||||
|
||||
| opsxcli 模块 | opsxcli 路径 | aiops 对应 | 处理方式 |
|
||||
|--------------|-------------|-----------|----------|
|
||||
| Agent ReAct 核心 | `internal/agent/core/` | `backend/pkg/agentcli/core/agent.go` | **适配**:剥离 TUI,改 EventCallback 推 WebSocket;保留 16 轮循环 / 检查点 / 循环检测 |
|
||||
| 工具注册中心 | `internal/agent/tools/registry.go` | `backend/pkg/agentcli/tools/registry.go` | **原样移植**:Tool 接口(5 方法)+ DynamicRiskTool + Registry(线程安全) |
|
||||
| 内置工具集 | `local_bash / ssh_execute / scp_transfer / file_read / file_search / analyze_output`(6 个) | `ssh_execute` + `analyze_output`(2 个) | **精简剥离**:去掉 local_bash(不在后端服务器本地执行)、scp、file_read、file_search |
|
||||
| 安全系统 | `internal/agent/safety/` | `backend/pkg/agentcli/safety/safety.go` + `tools/risk.go` | **适配**:三级模式 / 五级风险 / 黑名单保留;确认机制由 CLI 终端交互改为 WebSocket confirm 事件 + 前端弹框 |
|
||||
| Prompt 构建 | `internal/agent/prompt/` | `backend/pkg/agentcli/prompt/prompt.go` | **适配**:保留五层架构,角色改写为「aiops Agent CLI 智能运维 Agent」 |
|
||||
| 自进化引擎 | `internal/agent/evolver/`(13 步 + 3300 行) | `backend/pkg/agentcli/evolver/engine.go` + `memory.go`(~400 行) | **精简适配**:保留四层记忆 + 进化循环思想;步骤从 13 步精简为核心 6 步(OBSERVE→CLASSIFY→LEARN→STORE→MEMORIZE→PREDICT) |
|
||||
| LLM 客户端 | `internal/llm/`(多 Provider 配置向导 + claude.go/gemini.go 等 15 Provider) | `backend/pkg/agentcli/llm/openai.go` + `types.go` | **精简剥离**:剥离 wizard 配置向导和多 Provider,统一走 OpenAI 兼容接口;配置来自 DB 表 `ai_llm_configs`(model.LLMConfig) |
|
||||
| 会话管理 | `internal/agent/session/`(JSONL 文件存储) | `backend/model/agent_cli.go` + `controller/agent_cli.go`(GORM DB) | **改造**:多用户 Web 场景下从本地文件改为数据库表 `agent_cli_sessions` / `agent_cli_messages`,按 user_id 隔离 |
|
||||
| 记忆存储 | `~/.opsxcli/agent/*.jsonl` | `data/agentcli/*.jsonl`(工作目录) | **适配**:从用户主目录改为应用工作目录 |
|
||||
| LLM 配置 | `~/.opsxcli/providers/*.json` | DB 表 `ai_llm_configs` + 「大模型设置」页面 | **改造**:从本地文件改为 DB 表 + Web 配置页 |
|
||||
| CLI 命令层 | `cmd/`(Cobra) | `backend/controller/agent_cli.go`(Gin WebSocket handler) | **改造**:从 Cobra 命令改为 WebSocket 控制器 |
|
||||
| TUI 终端界面 | `internal/agent/tui/`(Bubble Tea) | `frontend/src/components/aiops/AgentCLI.tsx`(React) | **改造**:从 Bubble Tea TUI 改为 React 对话流组件 |
|
||||
| 远程执行 | `internal/agent/tools/` 内 SSH 实现 | `pkg/remoteexec/`(Go-native SSH,aiops 既有) | **复用**:直接复用 aiops 已有的远程执行包 |
|
||||
|
||||
---
|
||||
|
||||
## 移植决策
|
||||
|
||||
### 剥离了什么
|
||||
|
||||
1. **CLI 外壳**:去掉 Cobra 命令定义、参数解析、帮助文本 —— 由 WebSocket 控制器替代。
|
||||
2. **TUI 终端界面**:去掉 Bubble Tea + 全套终端渲染 —— 由 React 对话流组件替代。
|
||||
3. **LLM 多 Provider 配置向导**:去掉 `wizard.go` 交互式向导、`claude.go` / `gemini.go` 等专有 Provider —— 统一走 OpenAI 兼容 `/chat/completions`,覆盖 deepseek / qwen / moonshot / ollama / openai / custom。
|
||||
4. **本地执行类工具**:去掉 `local_bash`(Web 后端不在服务器本地执行命令)、`scp_transfer`、`file_read`、`file_search` —— Web 版聚焦远程运维。
|
||||
5. **Evolver 复杂分析**:去掉 opsxcli 13 步循环中的反射评分、经验整合去重、Skill 自动提炼等重型子流程 —— 精简为核心学习链路。
|
||||
|
||||
### 适配了什么
|
||||
|
||||
1. **会话持久化**:从本地 JSONL 文件改为 GORM 数据库表,支持多用户并发与按用户隔离。
|
||||
2. **记忆路径**:从 `~/.opsxcli/agent/` 改为应用工作目录 `data/agentcli/`,便于容器化部署。
|
||||
3. **LLM 配置来源**:从 `~/.opsxcli/providers/*.json` 改为 DB 表 `ai_llm_configs`,通过「大模型设置」页面配置。
|
||||
4. **确认机制**:从 CLI 终端 `bufio.Reader` 改为 WebSocket `confirm` 事件 + 前端 `useConfirm()` 弹框。
|
||||
5. **SSH 凭据**:从 `~/.ssh/config` 解析改为关联 `model.Server` 凭据(凭证系统 v2),由 controller 注入引擎。
|
||||
6. **角色定位**:从「opsxcli 智能运维超级助手」改写为「aiops Agent CLI 智能运维 Agent(Web 版,类似 Claude Code)」。
|
||||
|
||||
### 精简了什么
|
||||
|
||||
1. **工具集**:6 个 → 2 个(`ssh_execute` + `analyze_output`)。
|
||||
2. **Evolver 步骤**:13 步 → 6 步(保留学习与注入能力,剥离重型分析)。
|
||||
3. **LLM 层**:15 个 Provider 文件 → 1 个 OpenAI 兼容客户端。
|
||||
4. **记忆层级**:保留四层(经验 / 环境 / 事实 / 程序),但程序层仅保留接口位、未实现自动 Skill 提炼。
|
||||
|
||||
> 作者:Logan.Li
|
||||
保留当前文件是为了兼容历史链接。// Logan.Li
|
||||
@@ -1,230 +0,0 @@
|
||||
# 01 - 项目概述与设计哲学
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/`)。
|
||||
> opsxcli 原文档把 Agent 描述为 CLI 工具(`opsxcli agent -q "..."`),aiops 改为 Web 页面 + WebSocket 事件流。涉及 CLI 特性的地方已注明 aiops 的对应实现。
|
||||
|
||||
> 建议阅读顺序:01 → 02 → 03 → ... → 10
|
||||
|
||||
## 项目定位
|
||||
|
||||
**aiops Agent CLI** —— aiops 平台内置的 Web 版智能运维 Agent。
|
||||
|
||||
类似 Claude Code / Codex,但运行在浏览器里:用户在「Agent CLI」页面选择一台目标主机,用自然语言描述运维任务(如「检查磁盘空间」「nginx 启动失败的原因」),后端 ReAct 引擎自主通过 SSH 在远程服务器上排查、修复,并通过 WebSocket 把每一步思考、执行的命令、输出与最终结果实时回传给前端。
|
||||
|
||||
这是把 opsxcli(基于 Cobra 的 CLI 运维 Agent)的 AI 能力,剥离 CLI/TUI 外壳、适配为 aiops 的 Gin 后端库后的产物。
|
||||
|
||||
## 设计目标
|
||||
|
||||
| 维度 | 目标 |
|
||||
|------|------|
|
||||
| 形态 | Web 对话流(WebSocket 双向通信),不是命令行 |
|
||||
| 部署 | 嵌入 aiops 后端二进制(go:embed),无需独立服务 |
|
||||
| 安全 | 默认 balanced;高危命令前端确认;危险命令硬拒 |
|
||||
| 智能 | 16 轮 ReAct 主循环 + 自进化记忆注入 |
|
||||
| 隔离 | 多用户并发,会话/消息按 user_id 隔离,持久化到 DB |
|
||||
|
||||
## 五层引擎结构
|
||||
|
||||
aiops Agent CLI 不是单一包,而是分散在 aiops 的标准 Gin 分层中:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ frontend/ (React 对话流 UI) │
|
||||
│ components/aiops/AgentCLI.tsx │
|
||||
│ - WebSocket 客户端,渲染 thought/tool_call/ │
|
||||
│ tool_result/done/error 事件流 │
|
||||
│ - 高危命令 useConfirm() 弹框确认 │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ controller/ (HTTP/WS handler) │
|
||||
│ controller/agent_cli.go │
|
||||
│ - WebSocket 入口 /ws/agent-cli?id=<serverID> │
|
||||
│ - 构建 Engine、注入 EventCallback + ConfirmFn │
|
||||
│ - 事件落库 + 推 WS │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ model/ (GORM 模型) │
|
||||
│ model/agent_cli.go │
|
||||
│ - AgentCLISession / AgentCLIMessage │
|
||||
│ - LLMConfig (ai_llm_configs) │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ pkg/agentcli/ (引擎库,零业务依赖) │
|
||||
│ ├── agentcli.go Engine 顶层入口 │
|
||||
│ ├── core/ ReAct 主循环 │
|
||||
│ ├── tools/ 工具注册 + ssh_execute 等 │
|
||||
│ ├── safety/ 安全控制器 │
|
||||
│ ├── prompt/ 五层 System Prompt │
|
||||
│ ├── evolver/ 自进化引擎(四层记忆) │
|
||||
│ └── llm/ OpenAI 兼容客户端 │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ pkg/remoteexec/ (复用 aiops 既有 Go-native SSH) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**关键设计原则**:`pkg/agentcli/` 零业务依赖 —— 不 import `model` / `controller`,所有持久化与 WebSocket 由 controller 层负责。引擎只关心「思考-行动-观察」循环本身。
|
||||
|
||||
## Agent 引擎子模块全景
|
||||
|
||||
`backend/pkg/agentcli/` 包含 7 个子包:
|
||||
|
||||
```
|
||||
backend/pkg/agentcli/
|
||||
├── agentcli.go ── Engine 顶层入口:组装 LLM + 工具 + 安全 + Evolver + ReAct 核心
|
||||
├── core/ ── ReAct 核心引擎:16 轮主循环 + 自适应检查点 + 循环检测
|
||||
├── prompt/ ── Prompt 构建:五层 System Prompt + 动态记忆注入
|
||||
├── evolver/ ── 自进化引擎:四层记忆 + 精简进化循环
|
||||
├── safety/ ── 安全系统:三级模式 + 阈值确认(ConfirmFn 回调)
|
||||
├── tools/ ── 工具注册中心 + 2 个内置工具 + 风险评估
|
||||
└── llm/ ── LLM 抽象层(OpenAI 兼容,剥离多 Provider)
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:opsxcli 有 `tui/` 子包(Bubble Tea 终端界面),aiops 把它替换为前端 React 组件 `AgentCLI.tsx`,由 controller 序列化事件推 WS。
|
||||
|
||||
## 四大设计哲学
|
||||
|
||||
### 1. 安全第一 (Safety First)
|
||||
|
||||
> 运维工具的底线是「不搞砸」。
|
||||
|
||||
- 所有工具执行前必须通过安全评估(`safety.Controller.Check`)
|
||||
- 三级安全模式:`strict` / `balanced`(默认)/ `permissive`
|
||||
- 5 级风险等级:Safe → Low → Medium → High → Critical
|
||||
- 危险命令黑名单硬拒(不可确认放行):`rm -rf /`、`mkfs`、`dd if=/dev/zero`、fork bomb 等
|
||||
- 高危命令经 WebSocket `confirm` 事件 + 前端 `useConfirm()` 弹框由用户确认
|
||||
- 会话与每条消息落库,事后可审计(`agent_cli_messages` 表)
|
||||
|
||||
### 2. 渐进式复杂度 (Progressive Complexity)
|
||||
|
||||
> 简单任务零配置,复杂任务有能力。
|
||||
|
||||
- 默认配置即可运行(`core.DefaultConfig()`)
|
||||
- LLM 配置通过「大模型设置」页面填一次即生效(DB 表 `ai_llm_configs`)
|
||||
- 引擎初始化时优雅降级:Evolver 失败设为 `nil`,不影响主 ReAct 循环
|
||||
- 安全模式按需升级(balanced 默认 → strict 高敏生产环境)
|
||||
|
||||
### 3. 工具化思维 (Tool-Oriented)
|
||||
|
||||
> 万物皆 Tool,一切可注册。
|
||||
|
||||
- 统一的 `Tool` 接口(5 方法):`Name` / `Description` / `Parameters` / `RiskLevel` / `Execute`
|
||||
- `DynamicRiskTool` 扩展接口:依据运行时参数动态评估风险(如 `ssh_execute` 看具体命令)
|
||||
- `Registry` 线程安全注册中心
|
||||
- 实现接口即可扩展,无需修改核心代码
|
||||
- **Web 版内置工具精简为 2 个**(剥离 opsxcli 的 local_bash / scp / file_read / file_search):
|
||||
- `ssh_execute` —— 在目标主机远程执行命令(复用 `pkg/remoteexec`)
|
||||
- `analyze_output` —— 对冗长输出做摘要/错误提取,避免 token 爆炸
|
||||
|
||||
### 4. 自我进化 (Self-Evolution)
|
||||
|
||||
> 用得越多,越懂你。
|
||||
|
||||
- Evolver 引擎在任务完成后异步学习(30s 超时 + panic 恢复,绝不阻塞主循环)
|
||||
- 四层记忆(`data/agentcli/`):
|
||||
- **经验记忆**(`experience.jsonl`)—— 任务类型 → 工具序列 + 提示,按成功率优先注入
|
||||
- **环境记忆**(`environment.json`)—— 已知主机列表
|
||||
- **事实记忆**(`factual.jsonl`)—— LLM 提炼的持久事实
|
||||
- **程序记忆**(`procedural.jsonl`)—— 程序性知识(接口位,未实现自动 Skill 提炼)
|
||||
- 经验按任务类型分类(磁盘空间 / 网络 / 服务管理 / 软件安装 等 11 类)
|
||||
- 下次任务开始时,Evolver 通过 `GetContextForPrompt()` 把相关历史经验注入 System Prompt
|
||||
|
||||
## Agent 核心工作流
|
||||
|
||||
```
|
||||
用户在 Agent CLI 页面选择主机,输入 "内存使用率"
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ WebSocket 入口 │ ← controller/agent_cli.go: Run()
|
||||
│ /ws/agent-cli?id=… │ 校验 JWT、加载 model.Server、解析 SSH 凭据
|
||||
└────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ Engine 组装 │ ← pkg/agentcli/agentcli.go: NewEngine()
|
||||
│ LLM(OpenAI兼容) │ 注入 EventCallback + ConfirmFn
|
||||
│ + tools(ssh/analyze)│
|
||||
│ + safety + Evolver │
|
||||
└────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ Prompt Builder │ ← prompt/prompt.go: BuildSystemPrompt()
|
||||
│ 五层 System Prompt │ Layer 1 角色 / 2 安全 / 3 工具 / 4 工作方式 / 5 记忆
|
||||
│ + Evolver 记忆注入 │
|
||||
└────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────┐ ┌──────────────┐
|
||||
│ LLM 推理 │────▶│ 工具调用 │
|
||||
│ (ReAct 16 轮循环) │◀────│ ssh_execute │
|
||||
└────────┬─────────────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ┌──────────────────────┘
|
||||
│ │ 每步都 emit Event
|
||||
│ ▼
|
||||
│ 自适应检查点 │ ┌──────────────┐
|
||||
│ (Round 4/8/12) │ │ 安全检查 │
|
||||
│ 循环检测(>3次拒绝) │ │ (RiskLevel) │
|
||||
│ 连续失败追踪 │ │ 高危→confirm│
|
||||
└────────┬─────────────┘ └──────────────┘
|
||||
│ EventCallback → controller
|
||||
│ ├─ 推 WS: thought/tool_call/tool_result/done
|
||||
│ └─ 落库: agent_cli_messages
|
||||
▼ (16 轮迭代或 LLM 不再返回 tool_calls)
|
||||
┌──────────────────────┐
|
||||
│ Evolver 学习 │ ← 后台 goroutine,不影响响应速度
|
||||
│ (经验 + 主机 + 事实) │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
## LLM 客户端策略
|
||||
|
||||
> opsxcli 有 15 个 Provider(注册制工厂 + `claude.go` / `gemini.go` 等专有实现 + 配置向导 `wizard.go`)。
|
||||
> aiops 精简为单一 OpenAI 兼容客户端,覆盖所有 OpenAI 协议兼容后端。
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/llm/openai.go
|
||||
// 唯一客户端实现:走标准 /chat/completions + Bearer 鉴权
|
||||
// 支持后端:openai / deepseek / qwen / moonshot / ollama / custom
|
||||
func NewClient(cfg Config) Client
|
||||
```
|
||||
|
||||
配置不再来自 `~/.opsxcli/providers/*.json`,而是 DB 表 `ai_llm_configs`(`model.LLMConfig`),由 controller 在握手阶段加载并注入 Engine。
|
||||
|
||||
## 文档阅读指南
|
||||
|
||||
| 编号 | 文档 | 核心内容 |
|
||||
|------|------|----------|
|
||||
| 01 | 本文档 | 项目概述、设计哲学、引擎全景 |
|
||||
| 02 | [整体架构设计](02-整体架构设计.md) | 五层结构、执行流程、依赖关系 |
|
||||
| 03 | [Harness 工程化与反馈回路](03-Harness工程化与反馈回路.md) | Harness 理论、前馈/反馈、Steering Loop |
|
||||
| 04 | [插件架构设计](04-插件架构设计.md) | Tool 接口、Registry、扩展规范 |
|
||||
| 05 | [Builtin 内置工具](05-Builtin内置命令.md) | ssh_execute 与 analyze_output 设计 |
|
||||
| 06 | [Agent 核心引擎设计](06-Agent核心引擎设计.md) | ReAct 主循环、自适应检查点 |
|
||||
| 07 | [Prompt 构建与记忆注入](07-Prompt构建与记忆注入.md) | 五层 Prompt、动态记忆层 |
|
||||
| 08 | [Evolver 自进化引擎](08-Evolver自进化引擎.md) | 四层记忆、精简进化循环 |
|
||||
| 09 | [安全系统与工具注册](09-安全系统与工具注册.md) | 风险评估、安全模式、Tool 接口 |
|
||||
| 10 | [会话管理](10-会话管理.md) | GORM 持久化、按用户隔离 |
|
||||
|
||||
> 注:opsxcli 还有第 11 篇「TUI 终端界面设计」,aiops 已剥离 Bubble Tea TUI,由 React 组件 `AgentCLI.tsx` 替代,故本系列不含该篇。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 1. 在「大模型设置」页面配置 LLM(DB 表 ai_llm_configs,Enabled=true)
|
||||
# 2. 在「服务器管理」确保目标主机凭据(凭证系统 v2)已配置
|
||||
# 3. 进入「Agent CLI」页面,选择主机 → 输入任务 → 观察 WebSocket 推送的事件流
|
||||
```
|
||||
|
||||
代码侧调用示例(controller):
|
||||
|
||||
```go
|
||||
engine, _ := agentcli.NewEngine(agentcli.EngineConfig{
|
||||
LLM: llm.Config{Endpoint: ..., APIKey: ..., Model: ...},
|
||||
SSH: sshCfg, // 来自 model.Server 凭据
|
||||
SafetyMode: safety.ModeBalanced,
|
||||
})
|
||||
engine.SetCallback(onEvent) // 推 WS + 落库
|
||||
engine.SetConfirmFn(confirmFn) // 高危确认
|
||||
output, _ := engine.Run(ctx, query, serverInfo)
|
||||
```
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,303 +0,0 @@
|
||||
# 02 - 整体架构设计
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/`)。
|
||||
> opsxcli 的「cmd/ + plugins/ + internal/」CLI 三层在 aiops 中改造为「controller/(HTTP/WS handler)+ model/(GORM)+ pkg/agentcli/(引擎库)+ frontend/(React UI)」四层。
|
||||
|
||||
## 项目结构(Agent CLI 相关部分)
|
||||
|
||||
```
|
||||
aiops/
|
||||
├── backend/
|
||||
│ ├── controller/
|
||||
│ │ └── agent_cli.go # WebSocket 控制器(HTTP/WS handler 层)
|
||||
│ │ # - /ws/agent-cli?id=<serverID> 入口
|
||||
│ │ # - 构建 Engine、注入回调、事件落库
|
||||
│ │ # - ListSessions/GetSession/DeleteSession REST
|
||||
│ ├── model/
|
||||
│ │ ├── agent_cli.go # AgentCLISession / AgentCLIMessage(GORM 模型)
|
||||
│ │ └── llm.go # LLMConfig(ai_llm_configs 表)
|
||||
│ ├── pkg/
|
||||
│ │ ├── agentcli/ # Agent 引擎库(零业务依赖,核心)
|
||||
│ │ │ ├── agentcli.go # Engine 顶层入口
|
||||
│ │ │ ├── core/ # ReAct 主循环 + EventCallback
|
||||
│ │ │ ├── tools/ # Registry + ssh_execute + analyze_output + risk
|
||||
│ │ │ ├── safety/ # 安全控制器(ConfirmFn 注入)
|
||||
│ │ │ ├── prompt/ # 五层 System Prompt
|
||||
│ │ │ ├── evolver/ # 四层记忆 + 精简进化循环
|
||||
│ │ │ └── llm/ # OpenAI 兼容客户端 + 类型定义
|
||||
│ │ └── remoteexec/ # 复用 aiops 既有 Go-native SSH
|
||||
│ └── router/routes.go # 路由注册:WS + REST 会话管理
|
||||
│
|
||||
├── frontend/
|
||||
│ ├── src/
|
||||
│ │ ├── components/aiops/AgentCLI.tsx # React 对话流组件
|
||||
│ │ ├── api/agentCli.ts # 会话历史 REST 客户端
|
||||
│ │ └── components/ui/confirm-dialog.tsx# useConfirm() 高危确认
|
||||
│ └── ...
|
||||
│
|
||||
└── data/agentcli/ # Evolver 记忆目录(运行时生成)
|
||||
├── experience.jsonl # 经验记忆
|
||||
├── factual.jsonl # 事实记忆
|
||||
├── procedural.jsonl # 程序记忆(接口位)
|
||||
└── environment.json # 环境记忆(已知主机)
|
||||
```
|
||||
|
||||
## 架构设计原则
|
||||
|
||||
### 1. 分层 + 依赖注入
|
||||
|
||||
aiops Agent CLI 的核心约束是「引擎库零业务依赖」:
|
||||
|
||||
- **controller 层** —— 负责协议适配(WebSocket)、凭据构建、事件持久化、用户隔离
|
||||
- **model 层** —— GORM 模型(会话、消息、LLM 配置、Server)
|
||||
- **pkg/agentcli 层** —— 纯算法库,只依赖 `pkg/remoteexec` 和自己的子包,**不 import model/controller**
|
||||
- **frontend 层** —— React 对话流 UI,纯事件消费 + 控制消息发送
|
||||
|
||||
依赖通过函数参数注入:
|
||||
|
||||
```go
|
||||
// controller/agent_cli.go 关键注入点
|
||||
engine.SetCallback(ac.onEvent) // EventCallback:推 WS + 落库
|
||||
engine.SetConfirmFn(ac.confirmFn) // ConfirmFn:高危确认(阻塞等前端回传)
|
||||
```
|
||||
|
||||
引擎因此可被任何调度方复用(不仅是 WebSocket,未来也可被 cron 或审批流调用)。
|
||||
|
||||
### 2. 工具化设计
|
||||
|
||||
每个工具都是独立的 struct,实现 `Tool` 接口即可注册:
|
||||
|
||||
```
|
||||
pkg/agentcli/tools/
|
||||
├── registry.go # Registry(线程安全) + Tool/DynamicRiskTool 接口 + RiskLevel
|
||||
├── ssh_execute.go # 远程命令执行(DynamicRiskTool)
|
||||
├── builtin.go # analyze_output(只读 Safe)
|
||||
└── risk.go # 黑名单 + AssessBashRisk 动态评估
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:opsxcli 把风险评估放在 `safety/` 包,aiops 为避免 `tools` ↔ `safety` 循环依赖,把 `risk.go` 置于 `tools` 包,由 `ssh_execute` 直接调用。
|
||||
|
||||
### 3. 配置集中化(DB 驱动)
|
||||
|
||||
opsxcli 通过 `~/.opsxcli/config.yaml` 和环境变量加载配置;aiops 配置来自 DB:
|
||||
|
||||
| 配置项 | opsxcli | aiops |
|
||||
|--------|---------|-------|
|
||||
| LLM 配置 | `~/.opsxcli/providers/*.json` | DB 表 `ai_llm_configs`(model.LLMConfig) |
|
||||
| LLM 配置入口 | CLI 向导 `wizard.go` | 「大模型设置」Web 页面 |
|
||||
| SSH 凭据 | `~/.ssh/config` 解析 | model.Server 关联凭证系统 v2 |
|
||||
| 引擎运行参数 | config.yaml + 环境变量 | `core.DefaultConfig()`(硬编码默认,必要时通过 EngineConfig 覆盖) |
|
||||
| 记忆目录 | `~/.opsxcli/agent/` | 工作目录 `data/agentcli/`(容器化友好) |
|
||||
|
||||
### 4. 协议适配层(controller)
|
||||
|
||||
controller 是「Web 世界」与「引擎世界」的翻译器:
|
||||
|
||||
| Web 协议 | 引擎调用 | 引擎事件 → Web |
|
||||
|----------|----------|----------------|
|
||||
| `WS start {query}` | `engine.Run(ctx, query, serverInfo)` | emit `thought`/`tool_call`/`tool_result`/`progress`/`done`/`error` → 序列化为 `wsEvent` 推 WS |
|
||||
| `WS confirm/deny {confirm_id}` | 解锁 `ConfirmFn` 的 chan | emit 前由 controller 推 `confirm` 事件 |
|
||||
| `WS abort` | `cancel()` 取消 ctx | emit `error`(ctx.Err()) |
|
||||
| `GET /sessions` | — | 直接查 `agent_cli_sessions` 表 |
|
||||
|
||||
---
|
||||
|
||||
## 核心流程图
|
||||
|
||||
### WebSocket 握手 + 任务执行完整流程
|
||||
|
||||
以用户在 Agent CLI 页面输入「检查 nginx 启动失败的原因」为例:
|
||||
|
||||
```
|
||||
用户在 Agent CLI 页面选择主机、输入任务 "检查 nginx 启动失败的原因"
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ 1. WebSocket 握手 (controller/agent_cli.go: Run) │
|
||||
│ ├─ 校验 JWT (middleware.JWTAuth) │
|
||||
│ ├─ 加载 model.Server (按 ?id=) │
|
||||
│ ├─ buildSSHConfig: 解析凭证系统 v2 凭据 │
|
||||
│ ├─ 加载 model.LLMConfig (Enabled=true) │
|
||||
│ └─ upgrader.Upgrade 升级 WS │
|
||||
└────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ 2. 推送 session 事件(主机上下文) │
|
||||
│ wsEvent{type:"session", message:"已连接主机…"}│
|
||||
└────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
▼ (前端发来 {type:"start", query:"..."})
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ 3. startTask │
|
||||
│ ├─ DB 创建 AgentCLISession (status=running) │
|
||||
│ ├─ DB 存用户消息 (role=user) │
|
||||
│ ├─ 构建 Engine (注入 LLM/SSH/SafetyMode) │
|
||||
│ ├─ engine.SetCallback(ac.onEvent) │
|
||||
│ ├─ engine.SetConfirmFn(ac.confirmFn) │
|
||||
│ └─ go engine.Run(ctx, query, serverInfo) │
|
||||
└────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ 4. Engine.Run → core.Agent.Run(ReAct 循环) │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────┐ │
|
||||
│ │ Prompt 构建(五层) │ │
|
||||
│ │ L1 角色 / L2 安全 / L3 工具 │ │
|
||||
│ │ L4 工作方式 / L5 Evolver 记忆 │ │
|
||||
│ └──────────────┬────────────────────┘ │
|
||||
│ ▼ │
|
||||
│ ┌───────────────────────────────────┐ │
|
||||
│ │ for iter := 0; iter < 16; iter++ { │ │
|
||||
│ │ emit progress(round) │ │
|
||||
│ │ 自适应检查点(iter 3/7/11) │ │
|
||||
│ │ trimMessages(裁剪历史) │ │
|
||||
│ │ resp = llm.Complete(...) │ │
|
||||
│ │ emit thought(resp.Content) │ │
|
||||
│ │ if 无 tool_calls: │ │
|
||||
│ │ emit done → triggerEvolve │ │
|
||||
│ │ return │ │
|
||||
│ │ for tc in resp.ToolCalls: │ │
|
||||
│ │ risk = toolRisk(...) │ │
|
||||
│ │ emit tool_call(name,args,risk) │ │
|
||||
│ │ approved = safetyCtl.Check(..)│ ←──┐ │
|
||||
│ │ if !approved: 跳过 │ │ 高危 │
|
||||
│ │ result = tool.Execute(ctx,args)│ │ 触发 │
|
||||
│ │ emit tool_result(output,...) │ │ confirm │
|
||||
│ │ records.append(..) │ │ 事件 │
|
||||
│ │ } │ │ │
|
||||
│ │ 循环检测 / 连续失败追踪 │ │ │
|
||||
│ │ } │ │ │
|
||||
│ └────────────────────────────────────┘ │ │
|
||||
└──────────────────────────────────────────┘ │ │
|
||||
│ │
|
||||
┌───────────────────────────┐
|
||||
│ confirmFn (controller) │
|
||||
│ 推 wsEvent{type:"confirm"}│
|
||||
│ ch := make(chan bool) │
|
||||
│ select { │
|
||||
│ case approved := <-ch: │
|
||||
│ case <-5min超时: │
|
||||
│ } │
|
||||
└─────────┬─────────────────┘
|
||||
│ 前端 useConfirm() 弹框
|
||||
│ 发回 {type:"confirm"/"deny"}
|
||||
▼
|
||||
resolveConfirm(confirmID, approved)
|
||||
│ ch <- approved
|
||||
▼ 解锁 safety.Check
|
||||
```
|
||||
|
||||
### 事件落库映射
|
||||
|
||||
`onEvent` 回调把每个事件同时推送 WS 与落库 `agent_cli_messages`:
|
||||
|
||||
| Event.Type | DB 字段 type | DB 字段 role | 备注 |
|
||||
|------------|--------------|--------------|------|
|
||||
| `thought` | thought | assistant | AI 思考文本 |
|
||||
| `tool_call` | tool_call | tool | 含工具名、参数 JSON、风险等级 |
|
||||
| `tool_result` | tool_result | tool | 含输出、成败、耗时 |
|
||||
| `done` | done | assistant | 最终答案 + token 统计 |
|
||||
| `error` | error | assistant | 错误信息(含达到最大轮次) |
|
||||
| `progress` | — | — | 太频繁,不入库 |
|
||||
|
||||
### 系统数据流
|
||||
|
||||
```
|
||||
┌────── controller 层(协议适配) ──────┐
|
||||
│ agent_cli.go (WS handler) │
|
||||
│ ├─ 加载 Server/LLMConfig │
|
||||
│ ├─ DB 创建 Session/Message │
|
||||
│ └─ 注入 Callback/ConfirmFn │
|
||||
└─────────────────┬─────────────────────┘
|
||||
│ 依赖注入
|
||||
▼
|
||||
┌────── pkg/agentcli 层(引擎库) ──────┐
|
||||
│ agentcli.go (Engine) │
|
||||
│ ├─ core.Agent (ReAct) │
|
||||
│ │ ├─ llm.Client (OpenAI 兼容) │
|
||||
│ │ ├─ tools.Registry │
|
||||
│ │ │ ├─ ssh_execute │
|
||||
│ │ │ └─ analyze_output │
|
||||
│ │ ├─ safety.Controller │
|
||||
│ │ ├─ prompt.BuildSystemPrompt │
|
||||
│ │ └─ evolver.EvolverEngine │
|
||||
│ └─ (零 model/controller 依赖) │
|
||||
└─────────────────┬─────────────────────┘
|
||||
│ 调用
|
||||
▼
|
||||
┌────── pkg/remoteexec(Go-native SSH)─┐
|
||||
│ Exec(cfg, command, timeout) │
|
||||
└───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
|
||||
| 层 | 技术 |
|
||||
|----|------|
|
||||
| 后端框架 | Gin + GORM + gorilla/websocket |
|
||||
| LLM 协议 | OpenAI 兼容 `/chat/completions` + function calling |
|
||||
| SSH | aiops 既有 `pkg/remoteexec`(Go-native,无外部依赖) |
|
||||
| 前端 | React 19 + TypeScript + shadcn/ui(`useConfirm` 弹框) |
|
||||
| 持久化 | MySQL(主库)/ SQLite(降级) —— 复用 aiops 既有 DB |
|
||||
| 记忆存储 | 文件 `data/agentcli/*.jsonl`(与 opsxcli 原生一致,零 DB 依赖) |
|
||||
|
||||
> 为什么记忆用文件而会话用 DB?记忆是引擎库内部的产物(学习沉淀),与业务无关;会话需要多用户隔离、列表查询、删除等业务操作,必须走 DB。这是「引擎库零业务依赖」原则的延伸。
|
||||
|
||||
## 路由注册
|
||||
|
||||
`backend/router/routes.go`:
|
||||
|
||||
```go
|
||||
// Agent CLI — AI 运维 Agent(WebSocket 推流 + 会话历史回放)
|
||||
agentCLIHandler := controller.NewAgentCLIHandler(db)
|
||||
r.GET("/ws/agent-cli", middleware.JWTAuth(&cfg.JWT, db), agentCLIHandler.Run)
|
||||
|
||||
agentCLIGroup := authorized.Group("/aiops/agent-cli")
|
||||
{
|
||||
agentCLIGroup.GET("/sessions", agentCLIHandler.ListSessions)
|
||||
agentCLIGroup.GET("/sessions/:id", agentCLIHandler.GetSession)
|
||||
agentCLIGroup.DELETE("/sessions/:id", agentCLIHandler.DeleteSession)
|
||||
}
|
||||
```
|
||||
|
||||
WebSocket 因无法设自定义 header,JWT 通过 `?token=` query param 由 `JWTAuth` 中间件兼容解析。
|
||||
|
||||
## 添加新模块示例
|
||||
|
||||
以「增加一个新工具 `ping_check`」为例:
|
||||
|
||||
1. **实现 Tool 接口**(`pkg/agentcli/tools/ping_check.go`):
|
||||
|
||||
```go
|
||||
type PingCheckTool struct{ cfg remoteexec.SSHConfig }
|
||||
|
||||
func (t *PingCheckTool) Name() string { return "ping_check" }
|
||||
func (t *PingCheckTool) Description() string { return "在目标主机 ping 指定地址" }
|
||||
func (t *PingCheckTool) Parameters() map[string]interface{} { /* JSON Schema */ }
|
||||
func (t *PingCheckTool) RiskLevel() tools.RiskLevel { return tools.RiskSafe }
|
||||
func (t *PingCheckTool) Execute(ctx context.Context, args map[string]interface{}) (*tools.Result, error) {
|
||||
// 调用 remoteexec.Exec 执行 ping
|
||||
}
|
||||
```
|
||||
|
||||
2. **在 Engine 中注册**(`pkg/agentcli/agentcli.go: NewEngine`):
|
||||
|
||||
```go
|
||||
registry.Register(tools.NewSSHExecuteTool(cfg.SSH, coreCfg.ToolTimeout))
|
||||
registry.Register(tools.NewAnalyzeOutputTool())
|
||||
registry.Register(tools.NewPingCheckTool(cfg.SSH)) // 新增
|
||||
```
|
||||
|
||||
3. 工具自动出现在 LLM 的 `tools` 参数中(`registry.ToLLMTools()`),Prompt Layer 3 工具描述自动生成(`buildToolDescriptions()`)。无需改 Prompt、无需改 controller、无需改前端。
|
||||
|
||||
## 设计目标
|
||||
|
||||
1. **Web 友好**:所有交互经 WebSocket,适配浏览器对话流
|
||||
2. **零业务耦合**:引擎库可独立测试与复用
|
||||
3. **复用 aiops 既有能力**:LLM 配置、SSH、凭证系统、DB
|
||||
4. **易于扩展**:Tool 接口 + Registry,实现接口即扩展
|
||||
5. **可审计**:每条事件落库,会话历史可回放
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,276 +0,0 @@
|
||||
# 03 - Harness 工程化与反馈回路
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/`)。
|
||||
> Harness Engineering 是与具体语言/形态无关的理论框架,原文的思想性内容(控制论、Steering Loop、四类调节 Harness)几乎完全保留;落地实例部分从「opsxcli CLI + golangci-lint」改写为「aiops 后端库 + WebSocket 反馈回路 + Evolver 自进化」。
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心定义:Agent = Model + Harness
|
||||
|
||||
### 1.1 什么是 Harness
|
||||
|
||||
> **Agent = Model + Harness** — LangChain
|
||||
|
||||
Harness 是 Agent 中除 Model 本身之外的一切。在大模型运维 Agent 的有界上下文中,Harness 分为两个层次:
|
||||
|
||||
- **内置 Harness(built-in harness)**:引擎作者构建的部分 —— System Prompt、工具注册机制、ReAct 编排循环
|
||||
- **外部 Harness(outer harness)**:业务方为自身场景构建的部分 —— 安全模式、确认机制、记忆注入、审计落库
|
||||
|
||||
Model 是「大脑」,Harness 是「身体 + 环境 + 工具 + 规则」。大脑决定智能,但身体和环境决定智能能否被正确、可靠、持续地发挥。
|
||||
|
||||
### 1.2 两个目标
|
||||
|
||||
> 一个构建良好的 Harness 服务于两个目标:
|
||||
> 1. **提高智能体首次就做对的概率**(feedforward)
|
||||
> 2. **提供反馈回路,在问题到达人类前尽可能自我纠正**(feedback)
|
||||
> — Martin Fowler / Birgitta Böckeler
|
||||
|
||||
这两个目标分别对应**前馈指导**和**反馈传感器**,是 Harness 的双引擎。
|
||||
|
||||
### 1.3 核心理念
|
||||
|
||||
> **Humans steer. Agents execute.** — OpenAI
|
||||
|
||||
人类的工作重心从「敲每一条命令」,转向**设计环境、明确意图和构建反馈回路**。在 aiops Agent CLI 中,运维人员的角色从「亲自 SSH 排障」变为「在 Web 上描述任务、审阅高危确认、监督 Agent 执行」。
|
||||
|
||||
---
|
||||
|
||||
## 2. 控制论基础:前馈与反馈
|
||||
|
||||
### 2.1 前馈指导 vs 反馈传感器
|
||||
|
||||
| 维度 | 前馈指导(Feedforward Guides) | 反馈传感器(Feedback Sensors) |
|
||||
|------|-------------------------------|-------------------------------|
|
||||
| **时机** | 智能体行动**之前** | 智能体行动**之后** |
|
||||
| **目的** | 预期行为并加以引导 | 观察产出并帮助自我纠正 |
|
||||
| **效果** | 提高首次做对的概率 | 在问题到达人类前自我纠正 |
|
||||
| **aiops 实例** | 五层 System Prompt、工具描述、安全规则文本 | 安全 Check 拦截、循环检测、连续失败追踪、自适应检查点 |
|
||||
|
||||
> 仅有前馈,规则永远无法验证是否奏效;仅有反馈,智能体会重复同样的错误。两者缺一不可。
|
||||
|
||||
### 2.2 计算型 vs 推理型
|
||||
|
||||
| 类型 | 特点 | 例子 |
|
||||
|------|------|------|
|
||||
| **计算型(Computational)** | 确定性、快速、CPU 执行 | 黑名单匹配、命令风险分级、循环检测、token 估算 |
|
||||
| **推理型(Inferential)** | 语义分析、AI 审查、LLM 调用 | Evolver 用 LLM 提炼经验、LLM 决定下一步工具 |
|
||||
|
||||
计算型传感器成本低、速度快,适合每次工具调用都运行(如 `safety.Controller.Check`、`AssessBashRisk`)。推理型传感器更强大但更贵、不确定,适合关键节点(如任务完成后的经验提炼)。
|
||||
|
||||
推理型传感器的特别威力:当反馈信号被优化为 LLM 可消费的格式时(如把「连续失败」翻译为「请换一种思路」的中文提示注入对话),形成一种**积极的 prompt injection** —— 这正是 aiops 自适应检查点的设计依据。
|
||||
|
||||
### 2.3 Steering Loop 掌舵循环
|
||||
|
||||
Steering Loop 是 Harness Engineering 的元过程 —— 通过迭代改进 Harness 来引导智能体。
|
||||
|
||||
```
|
||||
┌──────────┐ Agent 工作 ┌──────────┐ 系统改进 ┌──────────┐
|
||||
│ Harness │ ──────────► │ 观察问题 │ ──────────► │ 更新Harness│
|
||||
│ (当前版本) │ │(多次发生) │ │ (新版本) │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
▲ │
|
||||
└────────────────────────────────────────────────────┘
|
||||
循环往复
|
||||
```
|
||||
|
||||
在 aiops Agent CLI 中,Steering Loop 有两种实现:
|
||||
|
||||
1. **人工 Steering**:管理员在「大模型设置」调整 SystemPrompt、温度等 → 影响 Agent 下次行为
|
||||
2. **自动 Steering**:Evolver 引擎观察任务执行 → 自动学习经验 → 注入下次 Prompt(详见第 08 篇)
|
||||
|
||||
后者是 opsxcli 设计思想的核心延伸 —— 用代码自动化 Steering Loop。
|
||||
|
||||
### 2.4 Keep Quality Left — 把质量左移
|
||||
|
||||
在 aiops Agent CLI 的执行链路中,反馈传感器按成本分布:
|
||||
|
||||
| 阶段 | 应运行的传感器 | 特点 |
|
||||
|------|---------------|------|
|
||||
| **工具执行前** | 黑名单硬拒、`AssessBashRisk` 分级、安全模式阈值 | 毫秒级,每次必跑 |
|
||||
| **工具执行中** | context 超时(`ToolTimeout` 120s 兜底) | 防卡死 |
|
||||
| **轮次间** | 自适应检查点(Round 4/8/12)、循环检测(>3 次拒绝)、连续失败追踪 | 防 LLM 跑偏 |
|
||||
| **任务完成后** | Evolver 异步学习(30s 超时) | 较贵,异步 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 三类调节 Harness
|
||||
|
||||
> Agent Harness 如同一个控制论调节器(governor),通过前馈和反馈将系统调节到期望状态。
|
||||
|
||||
### 3.1 可维护性 Harness(Maintainability)
|
||||
|
||||
- 计算型:文件大小红线(Go handler ≤400 行、组件 ≤700 行)、`go vet`、`tsc --noEmit`
|
||||
- 推理型:代码审查(review)
|
||||
- 在 Agent CLI 代码中体现为「引擎库零业务依赖」「单文件 ≤300 行」等约束
|
||||
|
||||
### 3.2 架构适应度 Harness(Architecture Fitness)
|
||||
|
||||
- 前馈:分层依赖约定(controller → model + pkg/agentcli,引擎库不反向 import)
|
||||
- 反馈:编译失败即拦截(Go 强类型天然传感器)
|
||||
|
||||
### 3.3 行为 Harness(Behaviour)— 本项目的重点
|
||||
|
||||
如何引导和感知 Agent 是否按预期功能运行:
|
||||
|
||||
- 前馈:五层 System Prompt(角色 + 安全规则 + 工作方式 ReAct 范式)
|
||||
- 反馈:
|
||||
- 安全层硬拦截 + 高危确认(防破坏性操作)
|
||||
- ReAct 循环中的自适应检查点(防 LLM 死循环或编造输出)
|
||||
- WebSocket 事件流的可观测性(人类可随时观察思考与命令)
|
||||
- Evolver 经验学习(成功的工具序列被强化,失败的被记录为「换法」提示)
|
||||
|
||||
---
|
||||
|
||||
## 4. Harnessability 与设计启示
|
||||
|
||||
### 4.1 可 Harness 性
|
||||
|
||||
aiops 后端是 Go 强类型 + 清晰的包边界,天然适合被 Harness:
|
||||
|
||||
- 强类型使 LLM 工具的 JSON Schema 可以严格约束参数
|
||||
- 清晰的 controller / model / pkg 分层使「引擎库零业务依赖」可行
|
||||
- 复用 aiops 既有的凭证系统、SSH 包、DB,大幅缩减 Harness 代码量
|
||||
|
||||
### 4.2 Ashby 必要多样性定律
|
||||
|
||||
> 调节器必须拥有至少与被调节系统相同的多样性。
|
||||
|
||||
aiops Agent CLI 通过两层多样性缩减:
|
||||
|
||||
1. **工具集精简**(6 → 2):只保留 `ssh_execute` + `analyze_output`,把「在远程主机做任何事」收敛为「执行 shell + 分析输出」,让 LLM 的决策空间可控
|
||||
2. **Prompt 工作方式约束**:明确告诉 LLM「一次只做关键的、可验证的一步」「优先只读命令」「失败要换法重试」
|
||||
|
||||
这是把潜在的「无限可能的 Agent」收敛为「可控的运维 Agent」的关键策略。
|
||||
|
||||
### 4.3 Harness 模板
|
||||
|
||||
aiops Agent CLI 本身就是一个「Web 版运维 Agent Harness 模板」—— 它的前馈(五层 Prompt)、反馈(安全 + 检查点 + Evolver)、工具协议(Tool 接口)可以被视为同类产品的参考实现。
|
||||
|
||||
---
|
||||
|
||||
## 5. 反馈回路设计
|
||||
|
||||
### 5.1 多轮自我评审循环
|
||||
|
||||
aiops ReAct 循环本质就是一种多轮自我评审:
|
||||
|
||||
```
|
||||
Round 1:LLM 思考 + 决定工具
|
||||
│
|
||||
Round 2:执行工具 → 观察输出(exit_code 是关键信号)
|
||||
│
|
||||
Round 3:根据输出判断 → 继续排障或修复
|
||||
│
|
||||
═══ 检查点 Round 4 ═══
|
||||
├─ 若重复调用率 >50% 或连续失败 ≥3 → 注入「换思路」提示
|
||||
│
|
||||
Round N:… 直到 LLM 不再返回 tool_calls
|
||||
│
|
||||
═══ 检查点 Round 8 ═══ 强制中间总结
|
||||
═══ 检查点 Round 12 ═══ 敦促收尾
|
||||
│
|
||||
最终答案 + Evolver 学习(后台)
|
||||
```
|
||||
|
||||
### 5.2 黄金原则
|
||||
|
||||
从 OpenAI Codex 实践提炼的四条原则在 aiops Agent CLI 中的体现:
|
||||
|
||||
1. **优先使用共享抽象**:`pkg/remoteexec`(SSH)、`pkg/agentcli/tools`(Tool 接口)、`pkg/agentcli/llm`(Client 接口),将不变式集中管理
|
||||
2. **不使用「YOLO 式」探测**:所有命令先经 `AssessBashRisk` 评估,黑名单硬拒、高危确认,绝不盲目执行
|
||||
3. **统一错误处理**:工具返回 `Result{Success, Output, Error}`,错误信息以中文回到对话供 LLM 理解;Evolver 失败优雅降级不阻断主循环
|
||||
4. **避免全局状态**:`Engine` 每次任务一个实例,`Registry` 通过 `sync.RWMutex` 保护;会话状态走 DB 不走全局变量
|
||||
|
||||
---
|
||||
|
||||
## 6. aiops Agent CLI 代码对照
|
||||
|
||||
### 6.1 前馈指导实例
|
||||
|
||||
| 理论概念 | aiops 实现 | 代码位置 |
|
||||
|----------|-----------|----------|
|
||||
| 角色与能力引导 | 五层 System Prompt | `pkg/agentcli/prompt/prompt.go` |
|
||||
| 工具能力披露 | Registry 动态生成工具描述 | `core/agent.go: buildToolDescriptions` |
|
||||
| 工作范式引导 | ReAct 范式文本(思考-行动-观察-循环-收尾) | `prompt.go` Layer 4 |
|
||||
| 安全规则文本 | 黑名单 + 优先只读 + 高危确认 | `prompt.go` Layer 2 |
|
||||
|
||||
### 6.2 反馈传感器实例
|
||||
|
||||
| 理论概念 | aiops 实现 | 代码位置 |
|
||||
|----------|-----------|----------|
|
||||
| 计算型反馈(执行前) | 黑名单硬拒 + 风险分级 | `tools/risk.go` + `safety/safety.go: Check` |
|
||||
| 计算型反馈(轮次间) | 循环检测(同参 >3 拒绝) | `core/agent.go: processToolCalls` |
|
||||
| 计算型反馈(轮次间) | 连续失败追踪 | `core/agent.go: updateConsecutiveFailures` |
|
||||
| 结构化反馈 | 自适应检查点(Round 4/8/12 注入提示) | `core/agent.go: checkAdaptiveCheckpoint` |
|
||||
| 上下文保护 | 消息裁剪 + tool 消息完整性修复 | `core/agent.go: trimMessages` + `ensureToolMessageIntegrity` |
|
||||
| 安全反馈 | 三级模式阈值 + 高危确认 | `safety/safety.go` |
|
||||
|
||||
### 6.3 Steering Loop 实例
|
||||
|
||||
aiops 的 **Evolver 自进化引擎**是自动 Steering Loop 的代码级实现:
|
||||
|
||||
```
|
||||
EvolverEngine (pkg/agentcli/evolver/)
|
||||
│
|
||||
├── engine.go 精简进化循环(OBSERVE→CLASSIFY→LEARN→STORE→MEMORIZE→PREDICT)
|
||||
└── memory.go 四层记忆(experience/factual/procedural/environment)
|
||||
```
|
||||
|
||||
每次任务执行后,Evolver 异步学习并存储经验;下次任务开始时,`GetContextForPrompt()` 把相关历史经验注入 System Prompt 的 Layer 5 —— 这就是「观察问题 → 改进 Harness」的自动化实现。
|
||||
|
||||
### 6.4 品味不变式实例
|
||||
|
||||
| 类别 | 不变式 | 理由 |
|
||||
|------|--------|------|
|
||||
| 语言 | 注释和错误信息使用中文,署名 `// Logan.Li` | 项目规范 |
|
||||
| 命名 | 导出函数 PascalCase,包名全小写 | Go 标准 |
|
||||
| 错误 | `Result.Error` 携带中文错误,Evolver 失败降级不 panic | 保留错误链,不阻断主流程 |
|
||||
| 依赖 | controller → model + pkg/agentcli(单向) | 引擎库零业务依赖 |
|
||||
| 日志 | 引擎 emit Event,由 controller 决定落库还是推 WS | 引擎不直接依赖 DB/WS |
|
||||
| 安全 | 黑名单硬拒不可被确认放行 | 安全红线 |
|
||||
|
||||
---
|
||||
|
||||
## 7. Web 可观测性
|
||||
|
||||
> opsxcli 是 CLI 工具,可观测性 = slog + 审计日志 + `--debug`。
|
||||
> aiops Agent CLI 是 Web 服务,可观测性 = **WebSocket 事件流 + DB 持久化 + 会话历史回放**。
|
||||
|
||||
### 7.1 事件流(实时)
|
||||
|
||||
引擎通过 `EventCallback` 推送结构化事件,controller 序列化为 `wsEvent` 后推 WebSocket:
|
||||
|
||||
| Event.Type | 含义 | 前端渲染 |
|
||||
|------------|------|----------|
|
||||
| `progress` | 轮次 + token | 顶部进度条 |
|
||||
| `thought` | AI 思考文本 | 思考气泡 |
|
||||
| `tool_call` | 即将执行的工具 + 风险 | 工具调用卡片(含风险标签) |
|
||||
| `tool_result` | 工具输出 + 成败 + 耗时 | 工具结果卡片 |
|
||||
| `confirm` | 高危确认请求 | useConfirm 弹框 |
|
||||
| `done` | 最终答案 | 完成气泡 |
|
||||
| `error` | 错误(含达到最大轮次) | 错误气泡 |
|
||||
|
||||
### 7.2 持久化(事后审计)
|
||||
|
||||
每条事件(除 progress)落库 `agent_cli_messages`,前端「会话历史」页面可按 session_id 回放完整 ReAct 推理过程:
|
||||
|
||||
```go
|
||||
// controller/agent_cli.go: persistMsg
|
||||
// 每条消息记录:round / type / role / content / tool_name / tool_args /
|
||||
// tool_output / risk / success / duration / tokens
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:opsxcli 把审计日志写到 `~/.opsxcli/audit/audit.log`(文件),aiops 直接走 DB(多用户、可查询、可关联用户与主机)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 参考资源
|
||||
|
||||
| 资源 | 说明 |
|
||||
|------|------|
|
||||
| [Martin Fowler — Harness Engineering](https://martinfowler.com/articles/harness-engineering.html) | 理论框架,控制论视角 |
|
||||
| [OpenAI — Harness Engineering](https://openai.com/zh-Hans-CN/index/harness-engineering) | 实践报告,Codex 实战经验 |
|
||||
| [LangChain — Agent = Model + Harness](https://blog.langchain.com/the-anatomy-of-an-agent-harness/) | 术语定义 |
|
||||
| [Anthropic — Effective Harnesses for Long-Running Agents](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents) | 长时间运行 Agent 的 Harness 设计 |
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,177 +0,0 @@
|
||||
# 04 - 插件架构设计
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/`)。
|
||||
> opsxcli 的「plugins/」CLI 插件层(mysql/ssh/ping 等 46+ 命令)在 aiops 中不适用 —— Web 版聚焦远程运维,工具精简为 2 个并改由 `tools.Tool` 接口承载。
|
||||
|
||||
## 工具(Tool)= aiops 版的「插件」
|
||||
|
||||
opsxcli 有完整的 CLI 插件生态(46+ 命令插件);aiops Agent CLI 把「插件」概念收敛为更纯粹的 **Tool 接口**:
|
||||
|
||||
- 不再有 CLI 命令、参数解析、帮助文本
|
||||
- 工具的唯一职责是:被 LLM 调用 → 在目标主机做事 → 返回结构化结果
|
||||
- LLM 通过 function calling 协议感知工具的存在与用法
|
||||
|
||||
> 这是合理的精简:Web Agent 的能力边界由「Tool 接口 + LLM 推理」共同决定,不再需要 CLI 命令那一层。
|
||||
|
||||
## Tool 接口(5 方法)
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/tools/registry.go
|
||||
|
||||
type Tool interface {
|
||||
Name() string // 工具名(LLM 调用时用)
|
||||
Description() string // 给 LLM 看的描述
|
||||
Parameters() map[string]interface{} // JSON Schema 参数定义
|
||||
RiskLevel() RiskLevel // 静态风险等级
|
||||
Execute(ctx context.Context, args map[string]interface{}) (*Result, error)
|
||||
}
|
||||
```
|
||||
|
||||
设计哲学:**接口最小化** —— 5 个方法覆盖工具的完整生命周期。
|
||||
|
||||
### DynamicRiskTool 扩展接口
|
||||
|
||||
```go
|
||||
type DynamicRiskTool interface {
|
||||
Tool
|
||||
RiskLevelForArgs(args map[string]interface{}) RiskLevel
|
||||
}
|
||||
```
|
||||
|
||||
部分工具的「静态风险」无法反映真实风险(如 `ssh_execute` 静态是 High,但 `df -h` 是 Safe、`rm -rf /` 是 Critical)。这些工具额外实现 `DynamicRiskTool`,安全控制器 `Check` 时优先调用动态评估。
|
||||
|
||||
### Result 结构
|
||||
|
||||
```go
|
||||
type Result struct {
|
||||
Success bool `json:"success"`
|
||||
Output string `json:"output"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
## Registry 注册中心
|
||||
|
||||
```go
|
||||
type Registry struct {
|
||||
tools map[string]Tool
|
||||
mu sync.RWMutex // 读写锁,支持并发注册和查询
|
||||
}
|
||||
```
|
||||
|
||||
核心方法:
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `NewRegistry()` | 创建空注册中心 |
|
||||
| `Register(tool)` | 注册工具(线程安全) |
|
||||
| `Get(name)` | 按名获取工具(LLM 调用时按名查找) |
|
||||
| `List()` | 列出所有工具(构建 Prompt Layer 3 工具描述) |
|
||||
| `ToLLMTools()` | 转换为 OpenAI function calling 格式(自动注入到 LLM `tools` 参数) |
|
||||
|
||||
`ToLLMTools()` 是 Registry 与 LLM 协议的桥梁:注册的工具自动出现在 LLM 的 function 列表中,无需手动维护。
|
||||
|
||||
## 已注册工具一览
|
||||
|
||||
> 与 opsxcli 的关键差异:opsxcli 有 6 个工具(local_bash / ssh_execute / scp_transfer / file_read / file_search / analyze_output),aiops 精简为 2 个。
|
||||
|
||||
| 工具 | 风险等级 | 功能 | 来源 |
|
||||
|------|----------|------|------|
|
||||
| `ssh_execute` | High(静态)/ Safe~Critical(动态) | 在目标主机远程执行 shell 命令 | aiops 既有 `pkg/remoteexec` |
|
||||
| `analyze_output` | Safe | 对冗长输出做摘要/错误提取 | 内置 Go 实现 |
|
||||
|
||||
### 为什么精简?
|
||||
|
||||
| opsxcli 工具 | aiops 处理 | 原因 |
|
||||
|--------------|-----------|------|
|
||||
| `local_bash` | **剥离** | Web 后端不应在服务器本地执行命令(安全风险 + 多用户隔离) |
|
||||
| `scp_transfer` | **剥离** | 文件传输走 aiops 既有的「文件管理」模块,不混入 Agent |
|
||||
| `file_read` | **剥离** | 远程读文件用 `ssh_execute cat`,无需独立工具 |
|
||||
| `file_search` | **剥离** | 远程搜文件用 `ssh_execute grep/find` |
|
||||
| `ssh_execute` | **保留** | 核心远程运维能力 |
|
||||
| `analyze_output` | **保留** | 防止长输出 token 爆炸,辅助 LLM 聚焦关键信息 |
|
||||
|
||||
工具越少,LLM 的决策空间越收敛(呼应 Ashby 定律),首次做对的概率越高。
|
||||
|
||||
## 工具分类标准(aiops 版)
|
||||
|
||||
### 放入内置工具集的条件(需同时满足)
|
||||
|
||||
1. 属于运维 Agent 高频需要的能力
|
||||
2. 无法用现有工具简单组合完成(如 `analyze_output` 不能用 `ssh_execute` 替代 —— 它操作的是已有文本而非远程命令)
|
||||
3. 实现简洁(单文件 < 300 行)
|
||||
|
||||
### 通过 `ssh_execute` 间接覆盖的能力
|
||||
|
||||
opsxcli 用独立插件实现的能力,在 aiops 中全部由 `ssh_execute` 执行远程命令覆盖:
|
||||
|
||||
| opsxcli 插件能力 | aiops 等效 |
|
||||
|------------------|-----------|
|
||||
| `mysql -e "..."` | `ssh_execute "mysql -e \"...\""` |
|
||||
| `redis-cli ...` | `ssh_execute "redis-cli ..."` |
|
||||
| `ping / traceroute` | `ssh_execute "ping ..."` |
|
||||
| `cat / grep / find` | `ssh_execute "cat ..."` |
|
||||
| `systemctl status nginx` | `ssh_execute "systemctl status nginx"` |
|
||||
|
||||
LLM 会根据任务自主决定执行什么命令,无需为每类命令提供独立工具。
|
||||
|
||||
## 混合架构的好处
|
||||
|
||||
1. **职责单一**:每个工具只做一件事,`ssh_execute` 执行、`analyze_output` 分析
|
||||
2. **协议简单**:LLM 只需理解 2 个 function,决策快、错误少
|
||||
3. **风险可控**:所有远程操作统一走 `ssh_execute` 的风险评估(`AssessBashRisk`),不会因为工具增多而出现安全盲区
|
||||
4. **扩展灵活**:未来若需要新工具(如 `kubectl_apply`),实现 Tool 接口注册即可
|
||||
|
||||
## 扩展指南
|
||||
|
||||
### 添加新工具
|
||||
|
||||
1. 实现 `Tool` 接口(必要时实现 `DynamicRiskTool`):
|
||||
|
||||
```go
|
||||
// pkg/agentcli/tools/kubectl_get.go
|
||||
type KubectlGetTool struct{ cfg remoteexec.SSHConfig }
|
||||
|
||||
func (t *KubectlGetTool) Name() string { return "kubectl_get" }
|
||||
func (t *KubectlGetTool) Description() string {
|
||||
return "在目标主机执行 kubectl get 并返回结果"
|
||||
}
|
||||
func (t *KubectlGetTool) Parameters() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"resource": map[string]interface{}{"type": "string", "description": "如 pods/deployments"},
|
||||
},
|
||||
"required": []string{"resource"},
|
||||
}
|
||||
}
|
||||
func (t *KubectlGetTool) RiskLevel() tools.RiskLevel { return tools.RiskSafe }
|
||||
func (t *KubectlGetTool) Execute(ctx context.Context, args map[string]interface{}) (*tools.Result, error) {
|
||||
// 复用 remoteexec 执行 kubectl get
|
||||
}
|
||||
```
|
||||
|
||||
2. 在 `agentcli.NewEngine` 中注册:
|
||||
|
||||
```go
|
||||
registry.Register(tools.NewSSHExecuteTool(cfg.SSH, coreCfg.ToolTimeout))
|
||||
registry.Register(tools.NewAnalyzeOutputTool())
|
||||
registry.Register(tools.NewKubectlGetTool(cfg.SSH)) // 新增
|
||||
```
|
||||
|
||||
3. 工具自动出现在 LLM 的 function 列表与 Prompt Layer 3 工具描述中。无需改 Prompt、controller、前端。
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 陷阱 | 解决方案 |
|
||||
|------|----------|
|
||||
| 忘记设置 `RiskLevel` 导致误判 | 默认按接口返回值,高危工具必须显式声明 |
|
||||
| 高危工具未实现 `DynamicRiskTool` | 静态等级无法反映参数差异(如 `ssh_execute` 看 `df` 还是 `rm -rf`),必须实现动态评估 |
|
||||
| 工具 Execute 无超时 | `Config.ToolTimeout=120s` 兜底(`context.WithTimeout`) |
|
||||
| 工具输出过长导致 token 爆炸 | `OutputMaxLength=10000` 截断 + 引导 LLM 调用 `analyze_output` |
|
||||
|
||||
## 总结
|
||||
|
||||
aiops Agent CLI 的「插件架构」是 opsxcli 的精简版:从「46+ CLI 插件」收敛为「2 个 Tool 接口实现」。这不是能力的退化,而是**收敛决策空间、聚焦远程运维、降低 LLM 误判概率**的有意设计。详细工具实现见 [05 - Builtin 内置工具](05-Builtin内置命令.md)。
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,248 +0,0 @@
|
||||
# 05 - Builtin 内置工具
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/`)。
|
||||
> opsxcli 原文讨论「builtin 基础命令(ls/cat/grep 等)的 Go 原生实现 vs 命令转发」抉择;aiops 版剥离了所有本地命令,仅保留 2 个工具:`ssh_execute`(远程执行)+ `analyze_output`(输出分析)。本文聚焦这 2 个工具的设计。
|
||||
|
||||
## 设计目标
|
||||
|
||||
为 aiops Agent CLI 提供**最小而充分**的工具集:
|
||||
|
||||
- `ssh_execute` 覆盖所有「在远程主机做事」的需求(排障、安装、配置、重启)
|
||||
- `analyze_output` 解决「命令输出过长导致 LLM token 爆炸」的问题
|
||||
|
||||
工具数量从 opsxcli 的 6 个精简为 2 个,但能力边界通过 LLM 自主编排命令来扩展,而非通过堆工具。
|
||||
|
||||
## 工具 1:ssh_execute(远程命令执行)
|
||||
|
||||
### 定位
|
||||
|
||||
在目标主机上执行任意 shell 命令,返回合并的 stdout + stderr + exit_code。这是 Agent 真正「动手」的唯一通道。
|
||||
|
||||
### 实现
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/tools/ssh_execute.go
|
||||
|
||||
type SSHExecuteTool struct {
|
||||
cfg remoteexec.SSHConfig // 目标主机连接配置(含凭据,由 controller 注入)
|
||||
timeout time.Duration // 默认命令超时(60s)
|
||||
}
|
||||
|
||||
func (t *SSHExecuteTool) Name() string { return "ssh_execute" }
|
||||
|
||||
func (t *SSHExecuteTool) Description() string {
|
||||
return fmt.Sprintf(
|
||||
"在远程服务器 %s(用户 %s)上执行 shell 命令并返回合并的 stdout+stderr 输出与退出码。"+
|
||||
"用于排障(查看日志/进程/磁盘/网络)、安装与配置软件、重启服务等。"+
|
||||
"高危命令(rm -rf /、mkfs、reboot 等)需用户确认。", host, user)
|
||||
}
|
||||
|
||||
func (t *SSHExecuteTool) Parameters() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"command": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "要执行的 shell 命令,例如 df -h、systemctl status nginx、tail -n 100 /var/log/messages",
|
||||
},
|
||||
"timeout": map[string]interface{}{
|
||||
"type": "integer",
|
||||
"description": "命令超时秒数(默认 60)",
|
||||
},
|
||||
},
|
||||
"required": []string{"command"},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 风险评估:DynamicRiskTool
|
||||
|
||||
`ssh_execute` 是典型「静态等级无法反映真实风险」的工具,因此实现 `DynamicRiskTool`:
|
||||
|
||||
```go
|
||||
// 静态等级:High(保守)
|
||||
func (t *SSHExecuteTool) RiskLevel() RiskLevel { return RiskHigh }
|
||||
|
||||
// 动态等级:看具体命令
|
||||
func (t *SSHExecuteTool) RiskLevelForArgs(args map[string]interface{}) RiskLevel {
|
||||
return AssessBashRisk(StringParam(args, "command"))
|
||||
}
|
||||
```
|
||||
|
||||
| 命令示例 | 动态风险 | 安全模式 balanced 下的行为 |
|
||||
|----------|----------|--------------------------|
|
||||
| `df -h` / `ps aux` / `cat /etc/hosts` | Safe | 直接执行 |
|
||||
| `mkdir /tmp/foo` / `touch x` | Low | 直接执行 |
|
||||
| `chmod 644 /etc/cfg` / `cp a b` | Medium | strict 需确认,balanced 放行 |
|
||||
| `systemctl restart nginx` / `kill 1234` | High | 弹框确认 |
|
||||
| `rm -rf /var/log/*` | Critical | **硬拒**(不可确认放行) |
|
||||
|
||||
### Execute 流程
|
||||
|
||||
```go
|
||||
func (t *SSHExecuteTool) Execute(ctx context.Context, args map[string]interface{}) (*Result, error) {
|
||||
command := strings.TrimSpace(StringParam(args, "command"))
|
||||
if command == "" {
|
||||
return &Result{Success: false, Error: "参数 command 不能为空"}, nil
|
||||
}
|
||||
// ... timeout 解析
|
||||
res, err := remoteexec.Exec(t.cfg, command, timeout)
|
||||
if err != nil {
|
||||
return &Result{Success: false, Error: "SSH 连接失败: " + err.Error()}, nil
|
||||
}
|
||||
// 关键:附带 exit_code,让 LLM 判断命令是否成功(排障关键信号)
|
||||
output := res.Output + fmt.Sprintf("[exit_code=%d]", res.ExitCode)
|
||||
return &Result{Success: res.Success, Output: output, Error: res.Error}, nil
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
|
||||
- **复用 aiops 既有 `pkg/remoteexec`**:Go-native SSH,无外部依赖,凭据由 controller 从 `model.Server` 关联凭证系统构建后注入
|
||||
- **`exit_code` 是关键信号**:LLM 通过 `exit_code` 判断命令是否真正成功(避免 LLM 误以为「输出有内容」就是「执行成功」)
|
||||
- **连接级失败与命令级失败分离**:SSH 连不上返回 `Error`,命令执行返回 `exit_code != 0`,让 LLM 区分排查方向
|
||||
- **凭据不暴露给 LLM**:`SSHConfig` 是工具私有的,LLM 只看到 `command` 参数
|
||||
|
||||
## 工具 2:analyze_output(输出分析)
|
||||
|
||||
### 定位
|
||||
|
||||
对一段(可能很长的)命令输出做摘要:统计行数、提取疑似错误/告警行、必要时截断保留首尾。**纯文本处理,不执行任何命令**,因此 `RiskSafe`。
|
||||
|
||||
### 为什么需要它?
|
||||
|
||||
LLM 上下文窗口有限(即使 128k 模型,超长输出也会显著增加成本和延迟)。`ssh_execute` 单次输出截断在 10000 字符,但即便如此,日志类输出(如 `journalctl -n 1000`)仍可能让 LLM 难以聚焦。
|
||||
|
||||
`analyze_output` 让 LLM 自主决定「输出太长时先分析再推理」:
|
||||
|
||||
```
|
||||
Round 3: ssh_execute "journalctl -u nginx -n 500"
|
||||
→ 输出 500 行
|
||||
Round 4: analyze_output(output, focus="error")
|
||||
→ 提取 8 行疑似错误 + 截断保留首尾
|
||||
Round 5: LLM 基于精炼后的信息推理
|
||||
```
|
||||
|
||||
### 实现
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/tools/builtin.go
|
||||
|
||||
func (t *AnalyzeOutputTool) Execute(ctx context.Context, args map[string]interface{}) (*Result, error) {
|
||||
output := StringParam(args, "output")
|
||||
focus := strings.ToLower(StringParam(args, "focus"))
|
||||
// 1. 超长截断:保留前 40 + 后 40 行,中间用省略标注
|
||||
// 2. 提取疑似错误/告警行(errorKeywords,含中英文)
|
||||
// - 英文:error / fail / exception / traceback / denied / timeout / refused / critical / fatal
|
||||
// - 中文:错误 / 失败 / 拒绝 / 超时 / 无法 / 异常
|
||||
// - 若传 focus,把 focus 作为额外关键词优先匹配
|
||||
// 3. 最多 20 条疑似错误行
|
||||
// 4. 组装摘要:总行数 + 截断标注 + 错误行 + 输出片段
|
||||
}
|
||||
```
|
||||
|
||||
**错误关键词列表**:
|
||||
|
||||
```go
|
||||
errorKeywords := []string{
|
||||
"error", "fail", "exception", "traceback", "denied",
|
||||
"permission", "timeout", "refused", "unavailable", "critical", "fatal",
|
||||
"错误", "失败", "拒绝", "超时", "无法", "异常",
|
||||
}
|
||||
```
|
||||
|
||||
**输出格式示例**:
|
||||
|
||||
```
|
||||
总行数: 500(已截断保留首尾)
|
||||
疑似错误/告警行 (8):
|
||||
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
|
||||
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
|
||||
...
|
||||
|
||||
输出片段:
|
||||
... (前 40 行)
|
||||
... [已省略 420 行] ...
|
||||
... (后 40 行)
|
||||
```
|
||||
|
||||
## 工具组合:典型 ReAct 序列
|
||||
|
||||
`ssh_execute` 与 `analyze_output` 的组合覆盖绝大多数运维场景:
|
||||
|
||||
### 场景 1:磁盘满排障
|
||||
|
||||
```
|
||||
Round 1: ssh_execute "df -h" → 发现 /var 满
|
||||
Round 2: ssh_execute "du -sh /var/*" → 定位 /var/log 最大
|
||||
Round 3: ssh_execute "ls -lah /var/log | head -20" → 找到 access.log 巨大
|
||||
Round 4: 给出清理建议(不直接 rm,让用户确认)
|
||||
```
|
||||
|
||||
### 场景 2:服务启动失败
|
||||
|
||||
```
|
||||
Round 1: ssh_execute "systemctl status nginx" → failed
|
||||
Round 2: ssh_execute "journalctl -u nginx -n 500" → 500 行日志
|
||||
Round 3: analyze_output(output, focus="error") → 提取 8 行错误,端口被占
|
||||
Round 4: ssh_execute "ss -tlnp | grep :80" → 确认 80 端口被占用
|
||||
Round 5: 给出根因 + 解决建议
|
||||
```
|
||||
|
||||
### 场景 3:性能排查
|
||||
|
||||
```
|
||||
Round 1: ssh_execute "top -bn1 | head -20" → CPU 100% 进程
|
||||
Round 2: ssh_execute "ps aux --sort=-%cpu | head -10" → 定位 java 进程
|
||||
Round 3: ssh_execute "jstack <pid>" → 线程栈
|
||||
Round 4: analyze_output(jstack_output, focus="BLOCKED") → 死锁线索
|
||||
Round 5: 给出根因(线程死锁)
|
||||
```
|
||||
|
||||
## 工具与 Prompt 的协作
|
||||
|
||||
Prompt Layer 2 明确告诉 LLM:
|
||||
|
||||
> 排障时优先使用只读命令(df/ps/cat/grep/tail/systemctl status/journalctl)收集信息,确认根因后再动手修复。
|
||||
|
||||
Prompt Layer 3 自动列出工具描述(来自 `buildToolDescriptions()`):
|
||||
|
||||
```
|
||||
【可用工具】
|
||||
- ssh_execute: 在远程服务器 ... 上执行 shell 命令 ...
|
||||
- analyze_output: 分析一段(可能很长的)命令输出文本 ...
|
||||
```
|
||||
|
||||
工具的命名和描述本身就是给 LLM 的「使用说明书」,描述写得好,LLM 就能正确选用。
|
||||
|
||||
## 扩展指南
|
||||
|
||||
### 何时应该新增工具(而非用 ssh_execute)?
|
||||
|
||||
满足以下任一条件:
|
||||
|
||||
1. **命令协议复杂、LLM 容易写错**(如 `kubectl apply -f` 需要 YAML 参数,用 `ssh_execute` 难以表达)
|
||||
2. **需要结构化输入/输出**(如数据库查询返回表结构)
|
||||
3. **需要专门的风险评估逻辑**(如 `kubectl delete` 需要按资源类型分级)
|
||||
|
||||
如果只是「执行某条命令」,用 `ssh_execute` 即可,不需要新工具。
|
||||
|
||||
### 实现步骤
|
||||
|
||||
见 [04 - 插件架构设计](04-插件架构设计.md) 的「添加新工具」章节。
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 陷阱 | 解决方案 |
|
||||
|------|----------|
|
||||
| `ssh_execute` 输出过长撑爆上下文 | `OutputMaxLength=10000` 截断 + 引导调用 `analyze_output` |
|
||||
| LLM 把「输出有内容」误判为「命令成功」 | 输出末尾附 `[exit_code=N]`,Prompt 显式提醒 |
|
||||
| `analyze_output` 关键词覆盖不全 | 扩展 `errorKeywords` 列表(含中英文) |
|
||||
| 命令超时无反馈 | `context.WithTimeout(ctx, ToolTimeout)` + 失败信息回写 |
|
||||
| 凭据泄漏给 LLM | `SSHConfig` 是工具私有字段,不在 `Parameters` 中暴露 |
|
||||
|
||||
## 总结
|
||||
|
||||
aiops Agent CLI 的工具集设计遵循「少即是多」:2 个工具 + LLM 自主编排,覆盖绝大多数远程运维场景。`ssh_execute` 是「手」(动手做事),`analyze_output` 是「放大镜」(聚焦关键信息),二者配合让 Agent 既有行动力又有判断力。
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,415 +0,0 @@
|
||||
# 06 - Agent 核心引擎设计
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/core/agent.go`)。
|
||||
> 核心算法(16 轮 ReAct 主循环、自适应检查点、循环检测、连续失败追踪、Evolver 集成)全部保留;TUI 回调(Bubble Tea)剥离,改为 `EventCallback` 推 WebSocket 事件流。
|
||||
|
||||
## 设计理念
|
||||
|
||||
Agent 核心引擎遵循 **ReAct (Reasoning + Acting)** 范式:
|
||||
|
||||
- **Reasoning**:LLM 推理决定下一步行动(产出 `thought` 文本 + 工具调用计划)
|
||||
- **Acting**:调用工具执行具体操作(`ssh_execute` / `analyze_output`)
|
||||
- **Observing**:观察工具结果(`exit_code`、输出、耗时),反馈到下一轮推理
|
||||
|
||||
通过 16 轮迭代循环,让 Agent 从「盲目执行命令」进化为「有目的地探索和解决问题」。
|
||||
|
||||
## 核心结构
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/core/agent.go
|
||||
|
||||
type Agent struct {
|
||||
llm llm.Client // LLM 客户端(OpenAI 兼容)
|
||||
registry *tools.Registry // 工具注册中心
|
||||
config *Config // 运行配置
|
||||
safetyCtl *safety.Controller // 安全控制器
|
||||
evolver *evolver.EvolverEngine // 自进化引擎(可为 nil,降级运行)
|
||||
callback EventCallback // 事件回调(controller 注入,推 WS + 落库)
|
||||
totalTokens int // token 消耗统计
|
||||
mu sync.Mutex
|
||||
}
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:
|
||||
> - 剥离 `tokenizer *TokenEstimator` 独立结构,改为内联的 `estimateTokens` 函数
|
||||
> - 剥离 `lastEvolveResult` + `evolveResultMu`,改为 Evolver 内部维护
|
||||
> - 剥离 `evolveWg sync.WaitGroup`,进化 goroutine 自带 `recover()`,不阻塞主循环
|
||||
|
||||
### 依赖关系
|
||||
|
||||
```
|
||||
┌─────────┐
|
||||
│ Agent │
|
||||
└────┬────┘
|
||||
┌───────┬───────┼───────┬──────────┐
|
||||
│ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
┌────────┐┌──────┐┌──────┐┌──────┐┌────────┐
|
||||
│Config ││Safety││Tools ││Evolver││Prompt │
|
||||
│配置 ││安全 ││工具 ││进化 ││构建器 │
|
||||
└────────┘└──────┘└──────┘└──────┘└────────┘
|
||||
```
|
||||
|
||||
## EventCallback —— 替代 TUI 的事件流
|
||||
|
||||
opsxcli 通过 `ToolCallback` 驱动 Bubble Tea TUI 实时渲染;aiops 改为 `EventCallback`,由 controller 序列化后推 WebSocket + 落库:
|
||||
|
||||
```go
|
||||
type EventType string
|
||||
|
||||
const (
|
||||
EventThought EventType = "thought" // AI 思考文本
|
||||
EventToolCall EventType = "tool_call" // 即将执行的工具(名/参数/风险)
|
||||
EventToolResult EventType = "tool_result" // 工具执行结果(输出/成败/耗时)
|
||||
EventProgress EventType = "progress" // 轮次 + token
|
||||
EventDone EventType = "done" // 任务完成(最终答案)
|
||||
EventError EventType = "error" // 错误(含达到最大轮次)
|
||||
)
|
||||
|
||||
type EventCallback func(Event)
|
||||
```
|
||||
|
||||
controller 的 `onEvent` 实现同时做两件事:
|
||||
|
||||
```go
|
||||
func (ac *agentConn) onEvent(e core.Event) {
|
||||
ac.write(wsEvent{...}) // 1. 推 WebSocket
|
||||
ac.persistMsg(...) // 2. 落库 agent_cli_messages(progress 除外)
|
||||
}
|
||||
```
|
||||
|
||||
## Config 配置
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/core/agent.go
|
||||
|
||||
type Config struct {
|
||||
MaxIterations int // 16 - 最大迭代次数
|
||||
Temperature float64 // 0.3 - LLM 温度(偏低 = 更确定)
|
||||
ToolTimeout time.Duration // 120s - 工具执行超时
|
||||
MaxTokens int // 4096 - 单次 LLM 最大输出 token
|
||||
SafetyMode safety.Mode // balanced
|
||||
OutputMaxLength int // 10000 - 输出截断长度
|
||||
MaxContextTokens int // 8000 - 上下文窗口(裁剪阈值 90%)
|
||||
MemoryDir string // data/agentcli - Evolver 记忆目录
|
||||
}
|
||||
```
|
||||
|
||||
默认值通过 `DefaultConfig()` 提供。controller 可通过 `EngineConfig.Core` 覆盖。
|
||||
|
||||
> 与 opsxcli 的差异:opsxcli 用 `LoadConfig()` 从环境变量加载(`OPSXCLI_AGENT_*`),aiops 改为代码默认值 + `EngineConfig` 显式覆盖(Web 场景下环境变量管理不直观)。
|
||||
|
||||
## 16 轮 ReAct 主循环
|
||||
|
||||
### 流程图
|
||||
|
||||
```
|
||||
用户输入
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ ReAct Main Loop │
|
||||
│ │
|
||||
│ Round 1 ──→ LLM 推理 → 工具调用 → 观察 │
|
||||
│ Round 2 ──→ LLM 推理 → 工具调用 → 观察 │
|
||||
│ Round 3 ──→ LLM 推理 → 工具调用 → 观察 │
|
||||
│ ═══════ CHECKPOINT 1 (Round 4) ═══════ │
|
||||
│ Round 4 ──→ 启发式评估 + 纠偏提示 │
|
||||
│ ... │
|
||||
│ Round 7 ──→ LLM 推理 → 工具调用 → 观察 │
|
||||
│ ═══════ CHECKPOINT 2 (Round 8) ═══════ │
|
||||
│ Round 8 ──→ 强制中间总结 │
|
||||
│ ... │
|
||||
│ Round 11 ──→ LLM 推理 → 工具调用 → 观察 │
|
||||
│ ═══════ CHECKPOINT 3 (Round 12) ═════ │
|
||||
│ Round 12 ──→ 最终提醒,敦促收尾 │
|
||||
│ ... │
|
||||
│ Round 16 ──→ 最大迭代,强制结束 │
|
||||
└──────────────┬──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
最终回答 / 总结
|
||||
│
|
||||
▼ (后台异步)
|
||||
┌──────────────┐
|
||||
│ Evolver 学习 │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
### 为什么是 16 轮?
|
||||
|
||||
| 迭代次数 | 优点 | 缺点 |
|
||||
|----------|------|------|
|
||||
| 8 轮 | 快速响应 | 复杂运维任务不够用 |
|
||||
| **16 轮** | **覆盖 90% 运维场景** | **偶尔多消耗 token** |
|
||||
| 32 轮 | 兜底极端场景 | 成本高、用户等待久 |
|
||||
|
||||
16 轮是**能力与效率的平衡点**,配合自适应检查点避免无意义循环。
|
||||
|
||||
### 主循环核心代码(精简版)
|
||||
|
||||
```go
|
||||
for iter := 0; iter < a.config.MaxIterations; iter++ {
|
||||
if err := ctx.Err(); err != nil { return nil, err } // 中断检查
|
||||
a.emit(Event{Type: EventProgress, Round: iter + 1})
|
||||
|
||||
// 自适应检查点
|
||||
if hint := a.checkAdaptiveCheckpoint(iter, ...); hint != "" {
|
||||
messages = append(messages, llm.Message{Role: "user", Content: hint})
|
||||
}
|
||||
|
||||
trimmed := a.trimMessages(messages) // 裁剪历史
|
||||
resp, err := a.llm.Complete(ctx, &llm.CompletionRequest{
|
||||
Messages: trimmed, Tools: llmTools,
|
||||
Temperature: a.config.Temperature, MaxTokens: a.config.MaxTokens,
|
||||
})
|
||||
// ...
|
||||
a.emit(Event{Type: EventThought, Round: iter + 1, Thought: resp.Message.Content})
|
||||
|
||||
if len(resp.Message.ToolCalls) == 0 {
|
||||
// 最终答案
|
||||
a.emit(Event{Type: EventDone, Answer: resp.Message.Content, ...})
|
||||
a.triggerEvolve(ctx, query, serverInfo, answer, toolRecords, true, start)
|
||||
return &tools.Result{Success: true, Output: answer}, nil
|
||||
}
|
||||
|
||||
// 处理工具调用(含安全检查、循环检测、风险事件)
|
||||
observations, records, failInc := a.processToolCalls(ctx, resp.Message.ToolCalls, ...)
|
||||
messages = append(messages, observations...)
|
||||
}
|
||||
// 达到最大轮次
|
||||
a.triggerEvolve(ctx, query, serverInfo, "", toolRecords, false, start)
|
||||
return nil, fmt.Errorf("达到最大迭代次数(%d)...", a.config.MaxIterations)
|
||||
```
|
||||
|
||||
## 自适应检查点机制
|
||||
|
||||
### checkAdaptiveCheckpoint()
|
||||
|
||||
```go
|
||||
func (a *Agent) checkAdaptiveCheckpoint(iter int, history map[string]int, consecFail, totalCalls int) string {
|
||||
switch iter {
|
||||
case 3: // 第 4 轮(0-indexed 为 3)
|
||||
repeat := 0
|
||||
for _, c := range history {
|
||||
if c > 1 { repeat += c - 1 }
|
||||
}
|
||||
if totalCalls > 0 && (float64(repeat)/float64(totalCalls) > 0.5 || consecFail >= 3) {
|
||||
return "【系统提示】检测到重复使用相同工具或连续失败,请尝试换一种思路或不同命令。"
|
||||
}
|
||||
case 7: // 第 8 轮
|
||||
return "【系统提示】已执行到中段,请总结进展。信息足够就直接给出最终答案,否则说明剩余步骤。"
|
||||
case 11: // 第 12 轮
|
||||
return fmt.Sprintf("【系统提示】仅剩 %d 步,请立即整理已有信息给出最终答案,不要再调用新工具。",
|
||||
a.config.MaxIterations-iter-1)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
```
|
||||
|
||||
| 检查点 | 轮次 | 策略 | 触发条件 |
|
||||
|--------|------|------|----------|
|
||||
| **Tier 1** | Round 4 | 启发式纠偏 | 重复工具调用率 >50% **或** 连续失败 ≥3 |
|
||||
| **Tier 2** | Round 8 | 强制中间总结 | 始终注入 |
|
||||
| **Tier 3** | Round 12 | 敦促收尾 | 始终注入 |
|
||||
|
||||
### 设计决策:为什么用消息注入而不是额外 LLM 调用?
|
||||
|
||||
| 方案 | 优点 | 缺点 |
|
||||
|------|------|------|
|
||||
| ❌ 额外 LLM 调用评估状态 | 精确 | 每次 +1 API 调用,成本翻倍 |
|
||||
| ✅ **消息注入** | **零额外成本** | 依赖 LLM 自主理解提示 |
|
||||
|
||||
把引导消息以 `user` 角色注入对话,LLM 在下一轮推理时自然调整行为。这是「计算型反馈 + 积极的 prompt injection」的典型应用(见 [03 - Harness 工程化](03-Harness工程化与反馈回路.md))。
|
||||
|
||||
## 循环检测
|
||||
|
||||
防止 LLM 陷入「用相同参数反复调用同一工具」的死循环:
|
||||
|
||||
```go
|
||||
key := tc.Function.Name + ":" + tc.Function.Arguments
|
||||
history[key]++
|
||||
if history[key] > 3 {
|
||||
obs = append(obs, llm.Message{
|
||||
Role: "tool", ToolCallID: tc.ID, Name: tc.Function.Name,
|
||||
Content: "错误: 检测到循环调用,相同参数已调用超过3次,请换一种方式",
|
||||
})
|
||||
continue
|
||||
}
|
||||
```
|
||||
|
||||
阈值 >3 次:允许 LLM 重试 2~3 次(合理调试),超过则强制换法。
|
||||
|
||||
## 连续失败追踪
|
||||
|
||||
```go
|
||||
func (a *Agent) updateConsecutiveFailures(obs []llm.Message, current int) int {
|
||||
errs := 0
|
||||
for _, o := range obs {
|
||||
c := o.Content
|
||||
if strings.HasPrefix(c, "错误") || strings.HasPrefix(c, "失败") ||
|
||||
strings.HasPrefix(c, "执行错误") || strings.HasPrefix(c, "执行失败") {
|
||||
errs++
|
||||
}
|
||||
}
|
||||
if errs == 0 { return 0 } // 有成功就重置
|
||||
return current + errs
|
||||
}
|
||||
```
|
||||
|
||||
连续失败 ≥3 次会触发 Tier 1 检查点的纠偏提示。
|
||||
|
||||
## Token 估算与消息裁剪
|
||||
|
||||
### estimateTokens(粗略估算)
|
||||
|
||||
```go
|
||||
func estimateTokens(s string) int {
|
||||
return len([]rune(s))/3 + 1
|
||||
}
|
||||
```
|
||||
|
||||
中文约 1 字/token,英文约 1/4,折中 rune/3。够用即可,不需要精确 tokenizer。
|
||||
|
||||
### trimMessages
|
||||
|
||||
```go
|
||||
func (a *Agent) trimMessages(msgs []llm.Message) []llm.Message {
|
||||
maxT := a.config.MaxContextTokens * 9 / 10 // 留 10% 余量
|
||||
sysT := estimateTokens(msgs[0].Content)
|
||||
kept := ... // 从末尾向前累加,超 maxT 停止
|
||||
if len(kept) < 4 && len(msgs) > 4 {
|
||||
kept = msgs[len(msgs)-4:] // 最少保留最近 4 条
|
||||
}
|
||||
result := append([]llm.Message{msgs[0]}, kept...) // system + 最近消息
|
||||
return ensureToolMessageIntegrity(result)
|
||||
}
|
||||
```
|
||||
|
||||
### ensureToolMessageIntegrity(关键)
|
||||
|
||||
LLM API 要求 `role: tool` 消息必须紧跟在带 `tool_calls` 的 `assistant` 消息之后,且 ID 匹配。裁剪后可能出现孤立 tool 消息,导致 API 报错:
|
||||
|
||||
```
|
||||
Messages with role 'tool' must be a response to a preceding message with 'tool_calls'
|
||||
```
|
||||
|
||||
`ensureToolMessageIntegrity` 修复这种孤立:
|
||||
|
||||
```go
|
||||
// 1. 收集所有 assistant.tool_calls 的 ID
|
||||
// 2. 遍历 tool 消息:
|
||||
// - 若 ID 不在 validIDs 中 → 删除
|
||||
// - 若前一条不是 assistant 或 ID 不匹配 → 删除
|
||||
```
|
||||
|
||||
## 工具调用处理(processToolCalls)
|
||||
|
||||
完整流程:
|
||||
|
||||
```
|
||||
对每个 tool_call:
|
||||
├─ 循环检测(>3 次拒绝)
|
||||
├─ registry.Get(name) 查找工具
|
||||
├─ json.Unmarshal 解析参数
|
||||
├─ risk = toolRisk(tool, args) ← 优先 DynamicRiskTool
|
||||
├─ emit EventToolCall(name, args, risk)
|
||||
├─ safetyCtl.Check(tool, args) ← 高危触发 ConfirmFn
|
||||
│ ├─ 黑名单(Critical)→ 拒绝
|
||||
│ ├─ 阈值内 / AutoApprove → 放行
|
||||
│ └─ 达阈值 → confirmFn → controller 推 WS confirm → 等前端回传
|
||||
├─ tool.Execute(ctx, args) ← context.WithTimeout(ToolTimeout)
|
||||
├─ 输出截断(> OutputMaxLength)
|
||||
├─ emit EventToolResult(output, success, duration)
|
||||
└─ 构建 observation 消息追加到 messages
|
||||
```
|
||||
|
||||
## Evolver 集成
|
||||
|
||||
### triggerEvolve(异步进化)
|
||||
|
||||
```go
|
||||
func (a *Agent) triggerEvolve(ctx context.Context, query, serverHost, answer string,
|
||||
records []evolver.ToolCallRecord, success bool, start time.Time) {
|
||||
if a.evolver == nil { return } // 降级
|
||||
exec := &evolver.TaskExecution{...}
|
||||
go func() {
|
||||
defer func() { _ = recover() }() // panic 恢复
|
||||
eCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
_ = a.evolver.Evolve(eCtx, exec) // 错误忽略
|
||||
}()
|
||||
}
|
||||
```
|
||||
|
||||
**三层保护**:
|
||||
|
||||
1. **后台 goroutine**:不阻塞主循环,用户立即拿到答案
|
||||
2. **30 秒超时**:防止进化过程无限运行
|
||||
3. **panic 恢复**:捕获异常,确保 goroutine 崩溃不影响主流程
|
||||
|
||||
### GetContextForPrompt(经验注入)
|
||||
|
||||
任务开始时(`Run` 入口):
|
||||
|
||||
```go
|
||||
evolveCtx := ""
|
||||
if a.evolver != nil {
|
||||
evolveCtx = a.evolver.GetContextForPrompt(query) // 按 task_type 取历史经验
|
||||
}
|
||||
systemPrompt := prompt.BuildSystemPrompt(serverInfo, toolDescs, evolveCtx)
|
||||
```
|
||||
|
||||
注入的内容会被放入 Prompt Layer 5(详见 [07 - Prompt 构建](07-Prompt构建与记忆注入.md) 与 [08 - Evolver](08-Evolver自进化引擎.md))。
|
||||
|
||||
## Agent 初始化流程
|
||||
|
||||
```
|
||||
NewAgent(client, registry, cfg, sc, ev)
|
||||
│
|
||||
├─ cfg == nil → DefaultConfig()
|
||||
├─ sc == nil → safety.NewController(balanced)
|
||||
├─ MaxContextTokens <= 0 → 强制设为 8000(防御性校验)
|
||||
└─ return Agent
|
||||
```
|
||||
|
||||
**优雅降级设计**:Evolver 初始化失败时,`agentcli.NewEngine` 把 `ev` 设为 `nil`,Agent 继续正常工作,只是没有自学习能力。
|
||||
|
||||
## 扩展指南
|
||||
|
||||
### 增加新的迭代策略
|
||||
|
||||
1. 在 `Config` 中增加策略配置项(如新的检查点轮次)
|
||||
2. 在 `checkAdaptiveCheckpoint()` 中添加新 case
|
||||
3. 编写对应的中文提示文本
|
||||
|
||||
### 自定义事件类型
|
||||
|
||||
1. 在 `EventType` 常量中新增(如 `EventWarning`)
|
||||
2. 在 `Event` 结构中增加字段
|
||||
3. 在 controller 的 `onEvent` 中增加 case 处理(推 WS + 落库)
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 陷阱 | 解决方案 |
|
||||
|------|----------|
|
||||
| MaxIterations 设太大导致 token 失控 | 默认 16 + 自适应检查点 |
|
||||
| trimMessages 后 tool 消息孤立 | `ensureToolMessageIntegrity()` 修复 |
|
||||
| Evolver 失败导致 Agent 不可用 | 优雅降级,`evolver=nil` 时跳过进化 |
|
||||
| MaxContextTokens=0 导致上下文异常 | 防御性校验,<=0 时强制 8000 |
|
||||
| 高并发下 messages 切片竞争 | Agent 不是并发安全的,**每个任务用独立实例**(controller 已保证:每次 startTask 新建 Engine) |
|
||||
| LLM 不返回 tool_calls 也不返回 content | 视为完成,emit done(空答案) |
|
||||
|
||||
## 与 opsxcli 的差异总结
|
||||
|
||||
| 维度 | opsxcli | aiops |
|
||||
|------|---------|-------|
|
||||
| 回调机制 | `ToolCallback` 驱动 Bubble Tea TUI | `EventCallback` 推 WebSocket + 落库 |
|
||||
| Token 估算 | 独立 `TokenEstimator` 结构 | 内联 `estimateTokens` 函数 |
|
||||
| 配置加载 | 环境变量 `OPSXCLI_AGENT_*` | `DefaultConfig()` + `EngineConfig` 覆盖 |
|
||||
| 进化 goroutine 同步 | `evolveWg.Wait()` | 自带 recover + 30s 超时,不等 |
|
||||
| MaxContextTokens 默认 | 6000(为 8k 模型留余量) | 8000(适配现代 128k 模型) |
|
||||
|
||||
核心算法(16 轮循环 / 检查点 / 循环检测 / 连续失败追踪 / Evolver 集成)完全保留,差异仅在工程外壳。
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,301 +0,0 @@
|
||||
# 07 - Prompt 构建与记忆注入
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/prompt/prompt.go`)。
|
||||
> 五层 System Prompt 架构保留;opsxcli 的「personaLayer / capabilityLayer / constraintLayer / fewShotLayer / memoryLayer」五常量在 aiops 中合并为单个 `BuildSystemPrompt()` 函数;角色从「opsxcli 智能运维超级助手」改写为「aiops Agent CLI 智能运维 Agent(Web 版,类似 Claude Code)」。
|
||||
|
||||
## 设计理念
|
||||
|
||||
Prompt 是 LLM Agent 的灵魂。aiops Agent CLI 沿用 opsxcli 的 **五层 System Prompt 架构**,将静态知识与动态记忆分层组装:
|
||||
|
||||
- **Layer 1-4 静态层**:编译期确定,不随运行时变化(角色、安全、工具、工作方式)
|
||||
- **Layer 5 动态层**:运行时由 Evolver 注入历史经验,越用越聪明
|
||||
|
||||
## 五层 System Prompt 架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Layer 1: 角色定位 │ ← 我是谁
|
||||
│ 「aiops Agent CLI 智能运维 Agent」 │
|
||||
├──────────────────────────────────────────┤
|
||||
│ Layer 2: 安全规则 │ ← 我必须遵守什么
|
||||
│ 黑名单 / 高危确认 / 优先只读 │
|
||||
├──────────────────────────────────────────┤
|
||||
│ Layer 3: 工具说明 │ ← 我能用什么
|
||||
│ 由 Registry 动态生成(ssh_execute 等) │
|
||||
├──────────────────────────────────────────┤
|
||||
│ Layer 4: 工作方式(ReAct 范式) │ ← 我怎么工作
|
||||
│ 思考 → 行动 → 观察 → 循环 → 收尾 │
|
||||
├──────────────────────────────────────────┤
|
||||
│ Layer 5: 记忆注入 │ ← 我学到了什么(动态)
|
||||
│ Evolver 提供的历史经验 │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## BuildSystemPrompt 函数
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/prompt/prompt.go
|
||||
|
||||
func BuildSystemPrompt(serverInfo, toolDescs, evolveContext string) string {
|
||||
var sb strings.Builder
|
||||
|
||||
// Layer 1: 角色
|
||||
sb.WriteString("你是 aiops 平台的 Agent CLI —— 一个企业级智能运维 Agent," +
|
||||
"类似 Claude Code / Codex,但运行在 Web 页面上,通过 SSH 工具远程操作真实服务器。\n" +
|
||||
"你的职责是:根据用户的自然语言任务描述,自主在目标服务器上排查故障、安装与配置软件、修复问题," +
|
||||
"并通过 WebSocket 把每一步思考、执行的命令和输出实时反馈给用户。\n\n")
|
||||
|
||||
// 目标主机(动态片段,但不属于记忆层)
|
||||
if strings.TrimSpace(serverInfo) != "" {
|
||||
sb.WriteString("【目标主机】\n" + serverInfo + "\n\n")
|
||||
}
|
||||
|
||||
// Layer 2: 安全规则
|
||||
sb.WriteString("【安全规则】\n")
|
||||
sb.WriteString("1. 破坏性命令(rm -rf /、mkfs、dd 覆写磁盘等)被严格禁止,绝不执行。\n")
|
||||
sb.WriteString("2. 高危操作(reboot、systemctl restart、kill、卸载软件等)需要用户确认后才能执行。\n")
|
||||
sb.WriteString("3. 排障时优先使用只读命令(df/ps/cat/grep/tail/systemctl status/journalctl)收集信息...\n")
|
||||
sb.WriteString("4. 不要执行任何你不确定后果的命令;宁可多问用户,也不盲目操作。\n\n")
|
||||
|
||||
// Layer 3: 工具(动态生成)
|
||||
if strings.TrimSpace(toolDescs) != "" {
|
||||
sb.WriteString("【可用工具】\n" + toolDescs + "\n\n")
|
||||
}
|
||||
|
||||
// Layer 4: 工作方式(ReAct)
|
||||
sb.WriteString("【工作方式(ReAct 循环)】\n")
|
||||
sb.WriteString("1. 思考:分析当前状态,决定下一步...\n")
|
||||
sb.WriteString("2. 行动:调用 ssh_execute 执行命令(一次只做关键的、可验证的一步)。\n")
|
||||
sb.WriteString("3. 观察:仔细阅读命令输出(含 exit_code,0 表示成功)。\n")
|
||||
sb.WriteString("4. 循环:如果命令失败或问题未解决,分析错误原因,换一种方法重试,直到真正解决。\n")
|
||||
sb.WriteString("5. 收尾:问题解决后,用清晰的中文总结:问题根因、你做了什么、最终结果、后续建议。\n\n")
|
||||
sb.WriteString("【重要】务必真正解决问题,而不是假装解决。每一步都要基于实际命令输出判断," +
|
||||
"禁止编造不存在的输出。\n\n")
|
||||
|
||||
// Layer 5: 记忆注入(动态)
|
||||
if strings.TrimSpace(evolveContext) != "" {
|
||||
sb.WriteString("【历史经验(来自自进化引擎)】\n" + evolveContext + "\n\n")
|
||||
}
|
||||
|
||||
// 输出要求
|
||||
sb.WriteString("【输出要求】使用简体中文,简洁专业。思考与命令穿插呈现,避免冗长空话。")
|
||||
return sb.String()
|
||||
}
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:
|
||||
> - opsxcli 把五层拆为五个独立常量(`personaLayer` / `capabilityLayer` / `constraintLayer` / `fewShotLayer` / `memoryLayerPrefix`),aiops 合并为单个函数,更紧凑
|
||||
> - opsxcli 有独立的 `BuilderV2` 结构和 `NewBuilderV2WithMemory` 工厂,aiops 直接用函数(Web 场景下不需要构建器状态)
|
||||
> - aiops 移除了独立的 fewShot 示例层,把 ReAct 工作方式说明合并到 Layer 4
|
||||
|
||||
## Layer 1: 角色定位
|
||||
|
||||
> **关键改写**:opsxcli 的角色是「opsxcli 智能运维超级助手」(CLI 工具语境);aiops 改写为「aiops 平台的 Agent CLI —— 企业级智能运维 Agent,类似 Claude Code / Codex,但运行在 Web 页面上」。
|
||||
|
||||
**设计要点**:
|
||||
|
||||
- **明确类比**:用「类似 Claude Code / Codex」让用户和 LLM 都快速理解定位
|
||||
- **强调 Web 语境**:「运行在 Web 页面上」「通过 SSH 远程操作」「通过 WebSocket 反馈」
|
||||
- **明确职责边界**:排障、安装配置、修复 —— 不是「无所不能的 AI」
|
||||
- **不包含版本号**(与 opsxcli 一致)
|
||||
|
||||
### 目标主机片段
|
||||
|
||||
`serverInfo` 由 controller 构建,注入到 Layer 1 之后:
|
||||
|
||||
```go
|
||||
serverInfo := fmt.Sprintf("主机名: %s | IP: %s:%d | 用户: %s | OS: %s",
|
||||
ac.server.Hostname, ac.server.IP, ac.server.Port, ac.server.Username, ac.server.OS)
|
||||
```
|
||||
|
||||
让 LLM 知道当前操作的是哪台主机、什么系统、用什么用户。
|
||||
|
||||
## Layer 2: 安全规则
|
||||
|
||||
四条硬规则:
|
||||
|
||||
1. **破坏性命令严格禁止**(呼应代码层的黑名单硬拒)
|
||||
2. **高危操作需用户确认**(呼应代码层的 ConfirmFn)
|
||||
3. **排障优先只读命令**(引导 LLM 先收集信息再动手)
|
||||
4. **不确定时不盲目操作**(宁可多问)
|
||||
|
||||
> **注意**:Layer 2 是 Prompt 级别的「软约束引导」,与 `safety.Controller.Check` 的代码级「硬拦截」是不同层面的机制。两者互补:Prompt 引导 LLM 主动避免危险操作,代码层兜底防止 LLM 失控。
|
||||
|
||||
## Layer 3: 工具说明
|
||||
|
||||
由 `core.Agent.buildToolDescriptions()` 动态生成:
|
||||
|
||||
```go
|
||||
func (a *Agent) buildToolDescriptions() string {
|
||||
var sb strings.Builder
|
||||
for _, t := range a.registry.List() {
|
||||
sb.WriteString(fmt.Sprintf("- %s: %s\n", t.Name(), t.Description()))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
```
|
||||
|
||||
**为什么动态生成?**
|
||||
|
||||
- 工具集可扩展(实现 Tool 接口注册即可),硬编码 Prompt 会与代码脱节
|
||||
- 工具描述本身就是给 LLM 的「使用说明书」,由工具自己提供最准确
|
||||
- `ssh_execute` 的描述会包含具体的目标主机和用户信息(动态拼接)
|
||||
|
||||
输出示例:
|
||||
|
||||
```
|
||||
【可用工具】
|
||||
- ssh_execute: 在远程服务器 192.168.1.100(用户 root)上执行 shell 命令并返回合并的 stdout+stderr 输出与退出码...
|
||||
- analyze_output: 分析一段(可能很长的)命令输出文本:统计行数、提取疑似错误/告警行...
|
||||
```
|
||||
|
||||
## Layer 4: 工作方式(ReAct 范式)
|
||||
|
||||
五步循环:
|
||||
|
||||
1. **思考** —— 分析状态,决定下一步(产出 `thought` 文本)
|
||||
2. **行动** —— 调用 `ssh_execute` 执行命令(一次只做关键的、可验证的一步)
|
||||
3. **观察** —— 仔细阅读输出(含 `exit_code`,0 表示成功)
|
||||
4. **循环** —— 失败则换法重试,直到真正解决
|
||||
5. **收尾** —— 清晰中文总结(根因、做了什么、结果、建议)
|
||||
|
||||
**关键约束**(红线):
|
||||
|
||||
> 务必真正解决问题,而不是假装解决。每一步都要基于实际命令输出判断,禁止编造不存在的输出。
|
||||
|
||||
这条约束非常重要 —— LLM 有「编造输出」的倾向,明确禁止 + 配合 `exit_code` 信号,能显著降低幻觉。
|
||||
|
||||
## Layer 5: 记忆注入(动态)
|
||||
|
||||
由 `evolver.EvolverEngine.GetContextForPrompt(query)` 提供:
|
||||
|
||||
```go
|
||||
func (e *EvolverEngine) GetContextForPrompt(query string) string {
|
||||
taskType := classifyTask(query, nil)
|
||||
var sb strings.Builder
|
||||
|
||||
// 同类任务的历史经验(优先成功)
|
||||
if exps := e.mem.FindExperiences(taskType, 3); len(exps) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" 【%s 类历史经验】\n", taskType))
|
||||
for _, exp := range exps {
|
||||
mark := "成功"
|
||||
if !exp.Success { mark = "未成功" }
|
||||
sb.WriteString(fmt.Sprintf(" - (%s) %s\n", mark, exp.Hint))
|
||||
}
|
||||
}
|
||||
// 相关事实
|
||||
if facts := e.mem.RelevantFacts(query, 3); len(facts) > 0 {
|
||||
sb.WriteString(" 【已知事实】\n")
|
||||
for _, f := range facts {
|
||||
sb.WriteString(fmt.Sprintf(" - %s\n", f.Fact))
|
||||
}
|
||||
}
|
||||
// 已知主机
|
||||
if hosts := e.mem.Hosts(); len(hosts) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" 【已知主机】%s\n", strings.Join(hosts, ", ")))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
```
|
||||
|
||||
注入示例:
|
||||
|
||||
```
|
||||
【历史经验(来自自进化引擎)】
|
||||
【磁盘空间 类历史经验】
|
||||
- (成功) 完成「磁盘空间」类任务时,工具序列 ssh_execute → analyze_output 最终成功,可作为参考。
|
||||
- (成功) 对于磁盘满问题,先检查大日志文件再清理
|
||||
【已知事实】
|
||||
- 生产 nginx 日志目录是 /var/log/nginx
|
||||
【已知主机】192.168.1.10, 192.168.1.11
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
|
||||
- 按 `query` 分类后只注入**同类**经验,避免无关信息污染
|
||||
- 优先注入**成功**经验(`FindExperiences` 内部排序)
|
||||
- 限制条数(经验 3 条、事实 3 条),控制 token 开销
|
||||
|
||||
> 与 opsxcli 的差异:opsxcli 把记忆层细分为 5.0 事实 / 5.1 经验 / 5.1.5 技能 / 5.2 环境 / 5.3 偏好;aiops 精简为 3 段(经验 / 事实 / 已知主机),剥离了用户偏好(Web 场景下偏好由安全模式等显式配置承载)和技能层(未实现自动 Skill 提炼)。
|
||||
|
||||
## 注入流程
|
||||
|
||||
```
|
||||
core.Agent.Run()
|
||||
│
|
||||
├─ evolveCtx = evolver.GetContextForPrompt(query) ← Layer 5 内容
|
||||
│
|
||||
├─ toolDescs = buildToolDescriptions() ← Layer 3 内容
|
||||
│
|
||||
├─ systemPrompt = prompt.BuildSystemPrompt(
|
||||
│ serverInfo, ← Layer 1 目标主机片段
|
||||
│ toolDescs, ← Layer 3
|
||||
│ evolveCtx, ← Layer 5
|
||||
│ )
|
||||
│
|
||||
└─ messages = [{system: systemPrompt}, {user: query}]
|
||||
```
|
||||
|
||||
## 为什么 Layer 1-4 是静态的?
|
||||
|
||||
- 角色定位和工作方式在编译期确定
|
||||
- 安全规则不应该被动态修改(防止 Evolver 污染安全约束)
|
||||
- 静态内容可以利用 LLM Provider 的 Prompt Caching(减少 token 消耗,部分 Provider 如 Anthropic 支持)
|
||||
- 只有事实/经验需要动态更新
|
||||
|
||||
## 输出要求
|
||||
|
||||
最后一段统一约束输出风格:
|
||||
|
||||
> 使用简体中文,简洁专业。思考与命令穿插呈现,避免冗长空话。
|
||||
|
||||
这与 opsxcli 一致 —— 中文运维语境的统一规范。
|
||||
|
||||
## 扩展指南
|
||||
|
||||
### 修改角色描述
|
||||
|
||||
直接修改 `BuildSystemPrompt` 中 Layer 1 的字符串。注意:
|
||||
|
||||
- 不要包含版本号
|
||||
- 明确能力边界(不要吹成「无所不能」)
|
||||
- 场景化描述优于抽象形容
|
||||
|
||||
### 增加新的 Prompt 层
|
||||
|
||||
1. 在 `BuildSystemPrompt` 中增加新的 `sb.WriteString(...)` 片段
|
||||
2. 在本文档更新架构图
|
||||
3. 若是动态层,需在 `Agent.Run` 中准备数据并传入
|
||||
|
||||
### 调整记忆注入策略
|
||||
|
||||
修改 `evolver.GetContextForPrompt`:
|
||||
|
||||
- 增加新的记忆段(如「最近失败的任务」)
|
||||
- 调整条数限制
|
||||
- 改进分类匹配逻辑
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 陷阱 | 解决方案 |
|
||||
|------|----------|
|
||||
| Prompt 过长导致 token 超限 | Layer 1-4 精简 + Layer 5 按需注入 + 条数限制 |
|
||||
| 记忆注入了无关经验 | `GetContextForPrompt` 按 `task_type` 匹配,不全量注入 |
|
||||
| Layer 1-4 被 Evolver 污染 | 静态层硬编码在函数中,Evolver 只能影响 Layer 5 |
|
||||
| LLM 编造命令输出 | Layer 4 显式禁止 + 输出附 `exit_code` 验证 |
|
||||
| 角色描述含版本号 | 已清理,禁止再加版本号 |
|
||||
| 工具描述与实际工具脱节 | Layer 3 由 Registry 动态生成,永远一致 |
|
||||
|
||||
## 与 opsxcli 的差异总结
|
||||
|
||||
| 维度 | opsxcli | aiops |
|
||||
|------|---------|-------|
|
||||
| 代码组织 | 5 个独立常量 + BuilderV2 结构 | 单个 `BuildSystemPrompt` 函数 |
|
||||
| 角色 | opsxcli 智能运维超级助手(CLI) | aiops Agent CLI(Web 版,类比 Claude Code) |
|
||||
| fewShot 层 | 独立 Layer 4(运维场景示例) | 合并进 Layer 4 工作方式 |
|
||||
| 记忆子层 | 5 个(事实/经验/技能/环境/偏好) | 3 个(经验/事实/已知主机) |
|
||||
| 静态/动态分离 | 严格(BuilderV2.enableMemory 开关) | 严格(函数内 if 判断) |
|
||||
|
||||
思想完全保留(五层架构 + 静态动态分离 + 记忆按需注入),实现更紧凑。
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,515 +0,0 @@
|
||||
# 08 - Evolver 自进化引擎
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/evolver/`)。
|
||||
> **保留**:四层记忆架构(经验/环境/事实/程序)+ 进化循环思想(OBSERVE → ... → PREDICT)+ 异步非阻塞 + 优雅降级。
|
||||
> **精简**:opsxcli 13 步循环 + 3300 行精简为 6 步核心循环 + ~400 行;剥离反射评分、经验整合去重、自动 Skill 提炼等重型子流程。这是有意的精简适配,保留学习能力和注入机制。
|
||||
|
||||
## 包位置
|
||||
|
||||
```
|
||||
backend/pkg/agentcli/evolver/
|
||||
├── engine.go # 进化引擎核心(精简进化循环)
|
||||
└── memory.go # 四层记忆 + JSONL/JSON 文件存储
|
||||
```
|
||||
|
||||
## 设计哲学
|
||||
|
||||
> **让 Agent 越用越聪明** —— 每次任务完成后自动学习经验,优化后续表现。
|
||||
|
||||
Evolver 借鉴 opsxcli 的设计理念:通过经验积累、LLM 反射分析和环境自适应,实现 Agent 的持续自我优化。用户使用越多,Agent 越了解服务器环境、最佳工具组合和已知事实。
|
||||
|
||||
**精简原则**:保留「学习 → 存储 → 注入」的核心闭环,剥离重型分析(评分、整合、Skill 提炼)。精简后的 Evolver 仍是真正的自学习引擎,而非装饰品 —— 它真的会把上次任务的经验存下来,并在下次同类任务时注入 Prompt。
|
||||
|
||||
## 核心数据结构
|
||||
|
||||
### EvolverEngine
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/evolver/engine.go
|
||||
|
||||
type EvolverEngine struct {
|
||||
mem *Memories // 四层记忆集合
|
||||
llm llm.Client // 可选,用于提炼经验/事实(nil 时退化为规则学习)
|
||||
memDir string // 记忆目录(默认 data/agentcli)
|
||||
}
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:剥离了 `minSteps` / `simpleThresh` / `moderateThresh` / `onProgress` 等配置字段;剥离 `reflectionClient` 独立接口(直接用 `llm.Client`)。
|
||||
|
||||
### TaskExecution(任务执行记录)
|
||||
|
||||
每次任务完成后由 `core.Agent.triggerEvolve` 构造:
|
||||
|
||||
```go
|
||||
type TaskExecution struct {
|
||||
Query string // 用户原始查询
|
||||
ToolCalls []ToolCallRecord // 工具调用序列
|
||||
TotalSteps int // 总迭代步数
|
||||
TotalTokens int // 消耗的 token 数
|
||||
Duration time.Duration // 执行耗时
|
||||
Success bool // 是否成功
|
||||
FinalAnswer string // 最终回答
|
||||
Timestamp time.Time // 执行时间
|
||||
ServerHost string // 目标主机(用于环境记忆)
|
||||
}
|
||||
```
|
||||
|
||||
### ToolCallRecord / EvolveResult
|
||||
|
||||
```go
|
||||
type ToolCallRecord struct {
|
||||
ToolName string
|
||||
Args map[string]interface{}
|
||||
Output string
|
||||
Duration time.Duration
|
||||
Success bool
|
||||
RiskLevel int
|
||||
}
|
||||
|
||||
type EvolveResult struct {
|
||||
TaskType string // 自动分类的任务类型
|
||||
LearnedHint string // 学到的经验提示
|
||||
ToolSequence []string // 工具序列
|
||||
ExperienceAdded bool // 是否新增经验
|
||||
Consolidated bool // 是否触发整合(aiops 版保留字段,未实现)
|
||||
}
|
||||
```
|
||||
|
||||
## 精简进化循环(6 步)
|
||||
|
||||
opsxcli 是 13 步(OBSERVE → EXTRACT → REFLECT → SCORE → COMPARE → LEARN → STORE → CONSOLIDATE → PREDICT → ADAPT → MEMORIZE → DISTILL → FEEDBACK),aiops 精简为 6 步核心:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Evolve 6 步核心循环 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Step 1: OBSERVE ── 观察工具序列 │
|
||||
│ ↓ 提取 ToolSequence(如 ssh → analyze) │
|
||||
│ │
|
||||
│ Step 2: CLASSIFY ── 任务类型分类 │
|
||||
│ ↓ 关键词规则匹配(11 类,无需 LLM) │
|
||||
│ │
|
||||
│ Step 3: LEARN ── 生成经验提示 │
|
||||
│ ↓ LLM 优先提炼一句经验,失败降级规则生成 │
|
||||
│ │
|
||||
│ Step 4: STORE ── 存入经验 + 环境记忆 │
|
||||
│ ↓ experience.jsonl + environment.json │
|
||||
│ │
|
||||
│ Step 5: MEMORIZE ── LLM 提炼持久事实 │
|
||||
│ ↓ 异步,从 FinalAnswer 提炼事实存 factual │
|
||||
│ │
|
||||
│ Step 6: PREDICT ── 注入下次 Prompt │
|
||||
│ ↓ GetContextForPrompt(下次任务开始时) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 各步骤详解
|
||||
|
||||
#### Step 1: OBSERVE(观察)
|
||||
|
||||
提取工具序列:
|
||||
|
||||
```go
|
||||
for _, tc := range exec.ToolCalls {
|
||||
result.ToolSequence = append(result.ToolSequence, tc.ToolName)
|
||||
}
|
||||
```
|
||||
|
||||
输出如 `["ssh_execute", "analyze_output", "ssh_execute"]`。
|
||||
|
||||
#### Step 2: CLASSIFY(任务分类)
|
||||
|
||||
关键词规则匹配(不调 LLM,零成本):
|
||||
|
||||
```go
|
||||
func classifyTask(query string, calls []ToolCallRecord) string {
|
||||
text := strings.ToLower(query)
|
||||
for _, c := range calls {
|
||||
if cmd, ok := c.Args["command"].(string); ok {
|
||||
text += " " + strings.ToLower(cmd)
|
||||
}
|
||||
}
|
||||
// 11 类规则匹配
|
||||
rules := []struct{ typ string; kws []string }{
|
||||
{"磁盘空间", []string{"disk", "空间", "df", "du", "inode", "磁盘", "满了"}},
|
||||
{"网络", []string{"network", "网络", "ping", "netstat", "ss ", "端口", "tcp", "dns"}},
|
||||
{"服务管理", []string{"service", "服务", "systemctl", "nginx", "mysql", "redis", "restart"}},
|
||||
{"软件安装", []string{"install", "安装", "yum", "apt", "docker", "部署", "deploy"}},
|
||||
{"进程排查", []string{"process", "进程", "ps ", "kill", "cpu", "top", "卡死"}},
|
||||
{"内存排查", []string{"memory", "内存", "mem", "free", "oom", "swap"}},
|
||||
{"日志排查", []string{"log", "日志", "journalctl", "tail", "grep"}},
|
||||
{"权限问题", []string{"permission", "权限", "chmod", "chown", "denied", "拒绝"}},
|
||||
{"性能调优", []string{"performance", "性能", "负载", "load", "慢", "slow"}},
|
||||
{"配置管理", []string{"config", "配置", "conf", "vim", "sed"}},
|
||||
}
|
||||
// ... 命中即返回,否则 "通用运维"
|
||||
}
|
||||
```
|
||||
|
||||
**设计要点**:把 query + 实际命令拼起来分类,比单看 query 更准(用户问「检查 nginx」,实际命令可能是 `systemctl status nginx`,分类为「服务管理」更合理)。
|
||||
|
||||
#### Step 3: LEARN(生成经验提示)
|
||||
|
||||
LLM 优先,失败降级规则:
|
||||
|
||||
```go
|
||||
func (e *EvolverEngine) learnHint(ctx context.Context, exec *TaskExecution, taskType string) string {
|
||||
tools := strings.Join(uniqueStrings(resultToolNames(exec.ToolCalls)), " → ")
|
||||
|
||||
// 尝试用 LLM 提炼一句经验
|
||||
if e.llm != nil {
|
||||
if hint := e.llmLearnHint(ctx, exec, taskType, tools, exec.Success); hint != "" {
|
||||
return hint
|
||||
}
|
||||
}
|
||||
// 降级:规则生成
|
||||
if exec.Success {
|
||||
return fmt.Sprintf("完成「%s」类任务时,工具序列 %s 最终成功,可作为参考。", taskType, tools)
|
||||
}
|
||||
return fmt.Sprintf("尝试「%s」类任务时,工具序列 %s 未成功,下次可换用不同命令或思路。", taskType, tools)
|
||||
}
|
||||
```
|
||||
|
||||
LLM 提炼的 prompt(中文):
|
||||
|
||||
```
|
||||
你是运维经验提炼器。根据以下任务执行记录,用一句中文(不超过80字)提炼经验提示,
|
||||
说明下次做同类任务的关键要点或推荐工具顺序。只返回经验文本,不要多余解释。
|
||||
任务: <query>
|
||||
工具序列: <tools>
|
||||
结果: 成功/失败
|
||||
```
|
||||
|
||||
**降级策略**:LLM 不可用或超时 → 用规则模板生成(仍然有价值,只是不够精炼)。
|
||||
|
||||
#### Step 4: STORE(存储经验 + 环境记忆)
|
||||
|
||||
```go
|
||||
exp := ExperienceEntry{
|
||||
TaskType: result.TaskType,
|
||||
Query: truncate(exec.Query, 200),
|
||||
Hint: result.LearnedHint,
|
||||
ToolSequence: result.ToolSequence,
|
||||
Success: exec.Success,
|
||||
Timestamp: exec.Timestamp.Unix(),
|
||||
}
|
||||
e.mem.AddExperience(exp)
|
||||
if exec.ServerHost != "" {
|
||||
e.mem.AddHost(exec.ServerHost) // 环境记忆
|
||||
}
|
||||
```
|
||||
|
||||
写入 `data/agentcli/experience.jsonl`(追加)和 `environment.json`(去重合并)。
|
||||
|
||||
#### Step 5: MEMORIZE(LLM 提炼持久事实)
|
||||
|
||||
仅在任务成功 + 有 FinalAnswer + LLM 可用时触发,**异步执行不阻塞**:
|
||||
|
||||
```go
|
||||
if e.llm != nil && exec.Success && exec.FinalAnswer != "" {
|
||||
go e.memorizeFact(ctx, exec) // 异步
|
||||
}
|
||||
```
|
||||
|
||||
LLM 提炼 prompt:
|
||||
|
||||
```
|
||||
从以下运维任务记录中,提炼 1 条值得长期记住的事实(如某主机的固定配置、已知问题、约定)。
|
||||
如果没有明显可沉淀的事实,回复「无」。只返回事实本身,一句话。
|
||||
任务: <query>
|
||||
结论: <final_answer>
|
||||
```
|
||||
|
||||
提炼出的事实存入 `factual.jsonl`。
|
||||
|
||||
> 与 opsxcli 的差异:opsxcli 的 MEMORIZE 从执行过程中提取结构化事实(key-value),aiops 简化为让 LLM 自由提炼一句事实。更简单,覆盖面更广。
|
||||
|
||||
#### Step 6: PREDICT(注入下次 Prompt)
|
||||
|
||||
`GetContextForPrompt(query)` 在下次任务开始时被 `core.Agent.Run` 调用:
|
||||
|
||||
```go
|
||||
evolveCtx := a.evolver.GetContextForPrompt(query)
|
||||
systemPrompt := prompt.BuildSystemPrompt(serverInfo, toolDescs, evolveCtx)
|
||||
```
|
||||
|
||||
注入内容会被放入 Prompt Layer 5(详见 [07 - Prompt 构建](07-Prompt构建与记忆注入.md))。
|
||||
|
||||
## 四层记忆系统
|
||||
|
||||
aiops 保留 opsxcli 的四层记忆架构:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 四层记忆架构 │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Layer 1: ExperienceMemory(经验层) │
|
||||
│ 任务完成后的经验,按 task_type 分类 │
|
||||
│ 存储: data/agentcli/experience.jsonl │
|
||||
│ │
|
||||
│ Layer 2: EnvironmentMemory(环境层) │
|
||||
│ 已知主机列表 │
|
||||
│ 存储: data/agentcli/environment.json │
|
||||
│ │
|
||||
│ Layer 3: FactualMemory(事实层) │
|
||||
│ LLM 提炼的持久事实 │
|
||||
│ 存储: data/agentcli/factual.jsonl │
|
||||
│ │
|
||||
│ Layer 4: ProceduralMemory(程序层) │
|
||||
│ 程序性知识(接口位,未实现自动 Skill 提炼) │
|
||||
│ 存储: data/agentcli/procedural.jsonl │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Memories 结构
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/evolver/memory.go
|
||||
|
||||
type Memories struct {
|
||||
dir string
|
||||
mu sync.Mutex
|
||||
|
||||
expPath string // experience.jsonl
|
||||
facPath string // factual.jsonl
|
||||
sklPath string // procedural.jsonl
|
||||
envPath string // environment.json
|
||||
}
|
||||
```
|
||||
|
||||
### ExperienceEntry / FactEntry / SkillEntry
|
||||
|
||||
```go
|
||||
type ExperienceEntry struct {
|
||||
TaskType string `json:"task_type"`
|
||||
Query string `json:"query"`
|
||||
Hint string `json:"hint"`
|
||||
ToolSequence []string `json:"tool_sequence"`
|
||||
Success bool `json:"success"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type FactEntry struct {
|
||||
Fact string `json:"fact"`
|
||||
Source string `json:"source"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type SkillEntry struct {
|
||||
Name string `json:"name"`
|
||||
When string `json:"when"`
|
||||
Steps []string `json:"steps"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:
|
||||
> - opsxcli 的 `Experience` 有 `SuccessRate` / `UsageCount` / `Tags`(用于整合去重),aiops 简化为 `Success` 布尔(剥离整合逻辑)
|
||||
> - opsxcli 的 `FactEntry` 是 key-value(`Key` / `Value` / `Category`),aiops 简化为单字段 `Fact`(一句话)
|
||||
> - opsxcli 的 `SkillEntry` 有完整的 ID/Version/Triggers/Pitfalls/SuccessRate,aiops 简化为 Name/When/Steps(接口位,未实际使用)
|
||||
|
||||
### 存储格式
|
||||
|
||||
#### experience.jsonl(追加写入)
|
||||
|
||||
```json
|
||||
{"task_type":"磁盘空间","query":"检查磁盘空间","hint":"对于磁盘满问题,先检查大日志文件再清理","tool_sequence":["ssh_execute","analyze_output"],"success":true,"timestamp":1748000000}
|
||||
```
|
||||
|
||||
#### environment.json(整体读写)
|
||||
|
||||
```json
|
||||
{
|
||||
"hosts": ["192.168.1.10", "192.168.1.11"]
|
||||
}
|
||||
```
|
||||
|
||||
#### factual.jsonl(追加写入)
|
||||
|
||||
```json
|
||||
{"fact":"生产 nginx 日志目录是 /var/log/nginx","source":"检查 nginx 启动失败","timestamp":1748000000}
|
||||
```
|
||||
|
||||
### 核心查询方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `AddExperience(e)` | 追加经验 |
|
||||
| `FindExperiences(taskType, n)` | 按类型查找,**优先成功**,取最近 n 条 |
|
||||
| `AddFact(f)` | 追加事实 |
|
||||
| `RelevantFacts(query, n)` | 按 query 关键词匹配事实(双向 Contains) |
|
||||
| `AddHost(host)` | 记录已知主机(去重) |
|
||||
| `Hosts()` | 已知主机列表 |
|
||||
| `CountStats()` | 各层条目数(供审计展示) |
|
||||
|
||||
**`FindExperiences` 的排序策略**:
|
||||
|
||||
```go
|
||||
// 优先成功经验,按时间取最近 n 条
|
||||
successFirst := ... // 过滤 success=true
|
||||
others := ... // 过滤 success=false
|
||||
out := append(pick(successFirst), pick(others)...)
|
||||
```
|
||||
|
||||
让 LLM 优先看到成功的经验。
|
||||
|
||||
## 与 Agent 的集成
|
||||
|
||||
### 初始化与优雅降级
|
||||
|
||||
在 `agentcli.NewEngine` 中:
|
||||
|
||||
```go
|
||||
ev, err := evolver.NewEvolverEngine(coreCfg.MemoryDir, client)
|
||||
if err != nil {
|
||||
ev = nil // 降级:无自学习,不影响主流程
|
||||
}
|
||||
agent := core.NewAgent(client, registry, coreCfg, sc, ev)
|
||||
```
|
||||
|
||||
**降级场景**:
|
||||
|
||||
- 记忆目录无法创建(权限问题)→ `ev = nil`
|
||||
- 此时 Agent 正常运行,只是 Layer 5 为空
|
||||
|
||||
### 异步进化流程
|
||||
|
||||
```
|
||||
用户查询 → core.Agent.Run() ReAct 循环 → 任务完成
|
||||
↓
|
||||
triggerEvolve()(异步 goroutine)
|
||||
↓
|
||||
30s 超时上下文 + panic 恢复
|
||||
↓
|
||||
EvolverEngine.Evolve()
|
||||
↓
|
||||
存入 experience.jsonl / factual.jsonl
|
||||
↓
|
||||
下次 Run() → GetContextForPrompt() → 注入 Layer 5
|
||||
```
|
||||
|
||||
**三层保护**:
|
||||
|
||||
1. **后台 goroutine**:不阻塞用户,答案立即返回
|
||||
2. **30 秒超时**:`context.WithTimeout(ctx, 30*time.Second)`
|
||||
3. **panic 恢复**:`defer func() { _ = recover() }()`
|
||||
|
||||
### 经验注入
|
||||
|
||||
下次任务开始时,`GetContextForPrompt(query)` 按 `task_type` 取相关经验注入 Layer 5:
|
||||
|
||||
```
|
||||
【历史经验(来自自进化引擎)】
|
||||
【磁盘空间 类历史经验】
|
||||
- (成功) 对于磁盘满问题,先检查大日志文件再清理
|
||||
- (成功) 完成「磁盘空间」类任务时,工具序列 ssh_execute → analyze_output 最终成功
|
||||
【已知事实】
|
||||
- 生产 nginx 日志目录是 /var/log/nginx
|
||||
【已知主机】192.168.1.10, 192.168.1.11
|
||||
```
|
||||
|
||||
## 任务分类系统(11 类)
|
||||
|
||||
`classifyTask` 把用户查询 + 实际命令分类为 11 类:
|
||||
|
||||
| 任务类型 | 关键词(部分) |
|
||||
|----------|---------------|
|
||||
| 磁盘空间 | disk / 空间 / df / du / inode / 磁盘 / 满了 |
|
||||
| 网络 | network / 网络 / ping / netstat / ss / 端口 / tcp / dns |
|
||||
| 服务管理 | service / 服务 / systemctl / nginx / mysql / redis / restart |
|
||||
| 软件安装 | install / 安装 / yum / apt / docker / 部署 |
|
||||
| 进程排查 | process / 进程 / ps / kill / cpu / top / 卡死 |
|
||||
| 内存排查 | memory / 内存 / mem / free / oom / swap |
|
||||
| 日志排查 | log / 日志 / journalctl / tail / grep |
|
||||
| 权限问题 | permission / 权限 / chmod / chown / denied / 拒绝 |
|
||||
| 性能调优 | performance / 性能 / 负载 / load / 慢 / slow |
|
||||
| 配置管理 | config / 配置 / conf / vim / sed |
|
||||
| 通用运维 | (兜底) |
|
||||
|
||||
## 创建方式
|
||||
|
||||
```go
|
||||
// 基础引擎(无 LLM 提炼,纯规则学习)
|
||||
engine, _ := evolver.NewEvolverEngine("data/agentcli", nil)
|
||||
|
||||
// 带 LLM 提炼的引擎(推荐)
|
||||
engine, _ := evolver.NewEvolverEngine("data/agentcli", llmClient)
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:opsxcli 有 `NewEvolverEngine` 和 `NewEvolverEngineWithLLM` 两个工厂;aiops 合并为一个,通过 `client` 参数是否为 nil 区分。
|
||||
|
||||
## 统计与监控
|
||||
|
||||
```go
|
||||
func (e *EvolverEngine) GetStats() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"enabled": true,
|
||||
"memory": e.mem.CountStats(), // {experience: N, factual: N, skill: N, hosts: N}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
由 `Engine.EvolveStats()` 暴露,供审计展示。
|
||||
|
||||
## 精简决策说明
|
||||
|
||||
### 剥离了什么
|
||||
|
||||
| opsxcli 步骤 | aiops 处理 | 原因 |
|
||||
|--------------|-----------|------|
|
||||
| Step 2.5 REFLECT(LLM 反射分析) | 合并进 Step 3 LEARN | 单次 LLM 调用同时做「分析 + 提炼」更高效 |
|
||||
| Step 3 SCORE(效率评分) | 剥离 | 评分用于整合决策,aiops 不做整合 |
|
||||
| Step 4 COMPARE(历史对比) | 简化为 `FindExperiences` 查询 | 不需要复杂相似度计算 |
|
||||
| Step 7 CONSOLIDATE(经验整合) | 剥离 | JSONL 追加 + 查询时去重足够,不需要主动合并 |
|
||||
| Step 9.7 DISTILL(自动 Skill 提炼) | 剥离 | Skill 提炼需要复杂条件判断 + 文件管理,价值/复杂比不高 |
|
||||
| Step 10 FEEDBACK(用户反馈消息) | 剥离 | Web 场景下经验学习对用户透明,不需要弹消息 |
|
||||
|
||||
### 保留了什么
|
||||
|
||||
- **四层记忆架构** —— 思想完整保留,只是程序层未实际使用
|
||||
- **OBSERVE → LEARN → STORE → MEMORIZE → PREDICT 闭环** —— 真正的学习与注入
|
||||
- **LLM 提炼 + 规则降级** —— 学习质量有保障
|
||||
- **异步非阻塞 + 优雅降级** —— 工程可靠性
|
||||
- **按 task_type 分类注入** —— 避免无关污染
|
||||
|
||||
精简后的 Evolver 仍是「真正的自学习引擎」,而非装饰品。
|
||||
|
||||
## 设计亮点
|
||||
|
||||
1. **异步非阻塞**:进化在后台 goroutine,不增加用户等待
|
||||
2. **优雅降级**:LLM 失败退化为规则学习,Evolver 初始化失败 Agent 正常运行
|
||||
3. **四层记忆**:经验 + 环境 + 事实 + 程序,覆盖从瞬时到持久的认知链路
|
||||
4. **会话级反馈**:上次任务的经验立即注入下次 Prompt
|
||||
5. **线程安全**:`Memories.mu sync.Mutex` 保护文件读写
|
||||
6. **零 DB 依赖**:纯文件存储,与引擎库「零业务依赖」原则一致
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 陷阱 | 解决方案 |
|
||||
|------|----------|
|
||||
| 记忆目录权限不足 | `NewEvolverEngine` 返回 error,`NewEngine` 把 ev 设为 nil 降级 |
|
||||
| LLM 提炼经验超时 | 30s 超时 + 降级为规则模板 |
|
||||
| 经验注入污染上下文 | 按 `task_type` 匹配 + 条数限制(3 条) |
|
||||
| 事实记忆累积过多无关项 | LLM prompt 明确「无明显事实回复『无』」+ 不写入 |
|
||||
| 并发写入 JSONL 冲突 | `Memories.mu sync.Mutex` 串行化写入 |
|
||||
| 程序层未实现却被引用 | 当前版本 `procedural.jsonl` 仅作为接口位,`GetContextForPrompt` 不读它 |
|
||||
|
||||
## 与 opsxcli 的差异总结
|
||||
|
||||
| 维度 | opsxcli | aiops |
|
||||
|------|---------|-------|
|
||||
| 进化步骤 | 13 步 | 6 步 |
|
||||
| 代码规模 | ~3300 行 | ~400 行 |
|
||||
| 记忆查询 | 复杂相似度 + 整合去重 | 简单 task_type 匹配 + 时间倒序 |
|
||||
| 经验字段 | SuccessRate / UsageCount / Tags | Success(布尔) |
|
||||
| Skill 提炼 | 自动(Step 9.7) | 未实现(接口位) |
|
||||
| 反射分析 | 独立 Step + JSON 结构 | 合并进 LEARN |
|
||||
| 用户反馈 | Step 10 弹消息 | 剥离(Web 透明) |
|
||||
|
||||
**核心思想保留度**:四层记忆 ✓ / 进化闭环 ✓ / 异步降级 ✓ / 经验注入 ✓。精简的是重型分析,不是学习能力本身。
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,468 +0,0 @@
|
||||
# 09 - 安全系统与工具注册
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/pkg/agentcli/safety/safety.go` + `tools/registry.go` + `tools/risk.go`)。
|
||||
> **保留**:三级安全模式 / 五级风险 / 危险命令黑名单 / AssessBashRisk 动态评估 / Tool 接口 + Registry。
|
||||
> **适配**:确认机制从 CLI 终端 `bufio.Reader` 改为 WebSocket `confirm` 事件 + 前端 `useConfirm()` 弹框;审计日志从文件改为 DB 落库。
|
||||
|
||||
## 设计理念
|
||||
|
||||
安全系统是运维 Agent 的生命线。aiops Agent CLI 安全系统遵循两个核心原则:
|
||||
|
||||
1. **默认安全**:balanced 模式下,high+ 操作需要确认,覆盖 95% 日常运维
|
||||
2. **可审计**:每次工具调用都通过 EventCallback 落库,事后可追溯
|
||||
|
||||
工具注册系统遵循**工具化思维**:万物皆 Tool,实现接口即可扩展。
|
||||
|
||||
---
|
||||
|
||||
## Part 1: 安全系统
|
||||
|
||||
### 架构总览
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ safety.Controller │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ risk.go │ │ controller.go │ │
|
||||
│ │ (在 tools 包)│ │ (在 safety 包)│ │
|
||||
│ │ 黑名单 + 分级 │ │ 阈值 + ConfirmFn│ │
|
||||
│ └──────────────┘ └──────────────┘ │
|
||||
│ │
|
||||
│ Safety Modes: │
|
||||
│ strict → medium+ 确认 │
|
||||
│ balanced → high+ 确认(默认) │
|
||||
│ permissive → critical 仅处理 │
|
||||
└────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:opsxcli 把风险评估和审计都放在 `safety/` 包;aiops 为避免 `tools` ↔ `safety` 循环依赖,把 `risk.go`(黑名单 + AssessBashRisk)置于 `tools` 包,由 `ssh_execute` 的 `DynamicRiskTool` 直接调用;`safety` 包只做「是否需确认」的策略。
|
||||
|
||||
### Controller 结构
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/safety/safety.go
|
||||
|
||||
type Controller struct {
|
||||
mode Mode // 当前安全模式
|
||||
confirmFn ConfirmFn // 确认回调(controller 注入:推 WS confirm + 阻塞等回传)
|
||||
autoApprove bool // 自动批准(仅测试用,生产禁用)
|
||||
}
|
||||
```
|
||||
|
||||
> 与 opsxcli 的差异:剥离 `reader *bufio.Reader`(CLI 终端交互)和 `history []ExecutionRecord`(内存审计历史),改为通过 EventCallback 落库 DB;剥离 `auditLog *AuditLogWriter`(文件审计)。
|
||||
|
||||
### ConfirmFn —— Web 版的确认机制
|
||||
|
||||
```go
|
||||
type ConfirmFn func(toolName string, args map[string]interface{}, risk tools.RiskLevel) (bool, error)
|
||||
```
|
||||
|
||||
controller 实现的 `confirmFn`:
|
||||
|
||||
```go
|
||||
// backend/controller/agent_cli.go
|
||||
|
||||
func (ac *agentConn) confirmFn(toolName string, args map[string]interface{}, risk tools.RiskLevel) (bool, error) {
|
||||
confirmID := fmt.Sprintf("cf-%d", time.Now().UnixNano())
|
||||
cmd, _ := args["command"].(string)
|
||||
// 1. 推 WS confirm 事件(前端 useConfirm 弹框)
|
||||
ac.write(wsEvent{
|
||||
Type: "confirm", ConfirmID: confirmID,
|
||||
ToolName: toolName, Command: cmd, Risk: risk.Display(), ToolArgs: args,
|
||||
})
|
||||
|
||||
// 2. 注册 channel 等待前端回传
|
||||
ch := make(chan bool, 1)
|
||||
ac.cfMu.Lock()
|
||||
ac.confirms[confirmID] = ch
|
||||
ac.cfMu.Unlock()
|
||||
|
||||
// 3. 阻塞等待(5 分钟超时)
|
||||
select {
|
||||
case approved := <-ch:
|
||||
return approved, nil
|
||||
case <-time.After(5 * time.Minute):
|
||||
ac.delConfirm(confirmID)
|
||||
return false, fmt.Errorf("确认超时")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
前端处理(`AgentCLI.tsx`):
|
||||
|
||||
```tsx
|
||||
case 'confirm': {
|
||||
const ok = await confirm({
|
||||
title: '确认执行高危命令',
|
||||
description: `即将执行:${ev.command}`,
|
||||
confirmText: '确认执行',
|
||||
});
|
||||
wsRef.current?.send(JSON.stringify({
|
||||
type: ok ? 'confirm' : 'deny',
|
||||
confirm_id: ev.confirm_id,
|
||||
}));
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
这是「Web 版 CLI 终端确认」的完整替代 —— 用户在浏览器看到弹框,点确认/拒绝,结果通过 WebSocket 回传解锁引擎。
|
||||
|
||||
### 三级安全模式
|
||||
|
||||
```go
|
||||
const (
|
||||
ModeStrict Mode = "strict" // medium+ 需确认
|
||||
ModeBalanced Mode = "balanced" // high+ 需确认(默认)
|
||||
ModePermissive Mode = "permissive" // 仅 critical 处理
|
||||
)
|
||||
```
|
||||
|
||||
| 模式 | 需要确认的等级 | 适用场景 |
|
||||
|------|---------------|----------|
|
||||
| `strict` | medium 及以上 | 生产环境、关键主机、新手操作 |
|
||||
| `balanced`(默认) | high 及以上 | 日常运维 |
|
||||
| `permissive` | 仅 critical | 测试环境、自动化脚本 |
|
||||
|
||||
阈值映射:
|
||||
|
||||
```go
|
||||
func (c *Controller) riskThreshold() tools.RiskLevel {
|
||||
switch c.mode {
|
||||
case ModeStrict: return tools.RiskMedium
|
||||
case ModePermissive: return tools.RiskCritical
|
||||
default: return tools.RiskHigh
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 五级风险评估
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/tools/registry.go
|
||||
|
||||
const (
|
||||
RiskSafe RiskLevel = iota // 0 - 只读(cat, grep, df, ps)
|
||||
RiskLow // 1 - 低风险(mkdir, touch)
|
||||
RiskMedium // 2 - 中风险(chmod, 文件修改)
|
||||
RiskHigh // 3 - 高风险(ssh_execute, 服务控制)
|
||||
RiskCritical // 4 - 危险(rm -rf /, mkfs, dd, 数据删除)
|
||||
)
|
||||
```
|
||||
|
||||
每级都有中文显示(`Display()`):
|
||||
|
||||
```go
|
||||
case RiskSafe: return "安全"
|
||||
case RiskLow: return "低风险"
|
||||
case RiskMedium: return "中风险"
|
||||
case RiskHigh: return "高风险"
|
||||
case RiskCritical: return "危险"
|
||||
```
|
||||
|
||||
前端在 `tool_call` 事件中拿到 `risk` 字段,渲染为风险标签。
|
||||
|
||||
### 危险命令黑名单(硬红线)
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/tools/risk.go
|
||||
|
||||
var dangerousCommands = []string{
|
||||
// 文件系统毁灭性操作
|
||||
"rm -rf /", "rm -rf /*", "rm -rf /bin", "rm -rf /boot", "rm -rf /etc",
|
||||
"rm -rf /home", "rm -rf /lib", "rm -rf /lib64", "rm -rf /proc", "rm -rf /root",
|
||||
"rm -rf /sbin", "rm -rf /sys", "rm -rf /usr", "rm -rf /var",
|
||||
// 磁盘格式化
|
||||
"mkfs", "mkfs.ext", "mkfs.xfs", "mkfs.btrfs", "mkfs.vfat",
|
||||
// 磁盘覆写
|
||||
"dd if=/dev/zero", "dd if=/dev/random", "dd if=/dev/urandom",
|
||||
// Fork bomb
|
||||
":(){ :|:& };:", ":() { : | : & }; :",
|
||||
// 设备重定向
|
||||
"> /dev/sda", "> /dev/sdb", "> /dev/sdc", "> /dev/hda",
|
||||
// 数据销毁
|
||||
"mv / /dev/null", "mv /* /dev/null",
|
||||
// 权限破坏
|
||||
"chmod -R 000 /", "chmod -R 777 /",
|
||||
// 服务自毁
|
||||
"systemctl stop ssh", "systemctl disable ssh", "kill -9 1",
|
||||
// 防火墙清空
|
||||
"iptables -F", "iptables --flush",
|
||||
}
|
||||
```
|
||||
|
||||
**特殊检测**:`rm -rf` + 根目录路径(即使不在黑名单字面中):
|
||||
|
||||
```go
|
||||
if strings.HasPrefix(lower, "rm") && strings.Contains(lower, "-rf") && containsRootPath(lower) {
|
||||
return true, "rm -rf 包含根目录路径"
|
||||
}
|
||||
```
|
||||
|
||||
`containsRootPath` 检测 `/boot`、`/etc`、`/usr`、`/var` 等系统目录。
|
||||
|
||||
### Check 策略
|
||||
|
||||
```go
|
||||
func (c *Controller) Check(tool tools.Tool, args map[string]interface{}) (bool, error) {
|
||||
risk := assessToolRisk(tool, args)
|
||||
|
||||
// 1. 黑名单硬红线:绝不允许(不可确认放行)
|
||||
if risk >= tools.RiskCritical {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 2. 未达阈值或自动批准:直接放行
|
||||
if c.autoApprove || risk < c.riskThreshold() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 3. 达阈值:请求用户确认
|
||||
if c.confirmFn == nil {
|
||||
return false, nil // 无确认回调时保守拒绝(Web 场景应始终注入 confirmFn)
|
||||
}
|
||||
return c.confirmFn(tool.Name(), args, risk)
|
||||
}
|
||||
```
|
||||
|
||||
**三层策略**:
|
||||
|
||||
1. **硬红线**(Critical):黑名单命令直接拒绝,**不可被确认放行**
|
||||
2. **阈值放行**:低于模式阈值的操作直接执行
|
||||
3. **用户确认**(达阈值):调 `confirmFn` 推 WS 弹框等用户决定
|
||||
|
||||
### AssessBashRisk 动态评估
|
||||
|
||||
```go
|
||||
func AssessBashRisk(command string) RiskLevel {
|
||||
// 1. 黑名单精确匹配 → Critical
|
||||
if isDangerous, _ := IsDangerousCommand(command); isDangerous {
|
||||
return RiskCritical
|
||||
}
|
||||
// 2. 纯只读命令 → Safe
|
||||
if isReadOnlyCommand(lower) {
|
||||
return RiskSafe
|
||||
}
|
||||
// 3. 组合命令(&& || | ;)→ 取子命令最高风险
|
||||
if isCompound(lower) {
|
||||
return assessCompoundCommand(lower)
|
||||
}
|
||||
// 4. 单命令分类
|
||||
isSudo := strings.HasPrefix(lower, "sudo ")
|
||||
return assessSingleCommand(strings.TrimPrefix(lower, "sudo "), isSudo)
|
||||
}
|
||||
```
|
||||
|
||||
### 命令分类表
|
||||
|
||||
| 类别 | 命令示例 | 风险等级 |
|
||||
|------|---------|---------|
|
||||
| 只读(Safe) | cat, grep, awk, sed, head, tail, ps, top, df, du, free, uptime, uname, hostname, ifconfig, ip addr, netstat, ss, ls, pwd, who, w, last, vmstat, iostat, lsof, find, which, file, stat, date, echo, id, whoami, dmesg, systemctl status, journalctl, lsblk, mount | Safe |
|
||||
| 修改(Low/Medium) | cp, mv, mkdir, rmdir, touch, chmod, chown, chgrp, ln, tar, gzip, gunzip, zip, unzip, rsync, tee | Low(sudo 时 Medium) |
|
||||
| 高危(Medium/High) | rm, dd, shutdown, reboot, halt, poweroff, init, systemctl restart/stop/start, kill, killall, pkill, userdel, groupdel, fdisk, parted, mkfs | Medium(sudo 时 High) |
|
||||
| 危险(Critical) | rm -rf /, mkfs, dd if=/dev/zero, fork bomb, > /dev/sda, chmod -R 000 / | Critical(硬拒) |
|
||||
|
||||
### sudo 提升
|
||||
|
||||
`sudo` 会让风险等级 +1(最高到 Critical 前):
|
||||
|
||||
```go
|
||||
// assessSingleCommand
|
||||
if isSudo {
|
||||
return RiskHigh // 高危命令 + sudo → High
|
||||
}
|
||||
return RiskMedium // 高危命令无 sudo → Medium
|
||||
```
|
||||
|
||||
### 组合命令取最高
|
||||
|
||||
```go
|
||||
// assessCompoundCommand: 拆分 && || | ; 后取子命令最高风险
|
||||
// "df -h && rm /tmp/file" → max(Safe, Medium) = Medium
|
||||
// "cat /etc/passwd; rm -rf /tmp" → max(Safe, Low) = Low
|
||||
```
|
||||
|
||||
### 审计落库(替代文件审计)
|
||||
|
||||
> opsxcli 把审计写到 `~/.opsxcli/audit/audit.log`;aiops 直接走 DB(`agent_cli_messages` 表),多用户场景下可按 user_id / session_id 查询。
|
||||
|
||||
controller 的 `onEvent` 回调把每个 `tool_call` / `tool_result` 落库:
|
||||
|
||||
```go
|
||||
case core.EventToolCall:
|
||||
argsJSON, _ := json.Marshal(e.ToolArgs)
|
||||
ac.persistMsg(sid, e.Round, "tool_call", "tool", "",
|
||||
e.ToolName, string(argsJSON), "", e.Risk, false, "", 0)
|
||||
case core.EventToolResult:
|
||||
ac.persistMsg(sid, e.Round, "tool_result", "tool", e.Output,
|
||||
e.ToolName, "", "", "", e.Success, e.Duration, 0)
|
||||
```
|
||||
|
||||
每条记录包含:工具名、参数 JSON、风险等级、成败、耗时、轮次、会话 ID、用户 ID(通过 session 关联)。前端「会话历史」页面可完整回放。
|
||||
|
||||
---
|
||||
|
||||
## Part 2: 工具注册系统
|
||||
|
||||
### Registry 注册中心
|
||||
|
||||
```go
|
||||
// backend/pkg/agentcli/tools/registry.go
|
||||
|
||||
type Registry struct {
|
||||
tools map[string]Tool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
```
|
||||
|
||||
核心方法:
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `NewRegistry()` | 创建空注册中心 |
|
||||
| `Register(tool)` | 注册工具(线程安全) |
|
||||
| `Get(name)` | 按名获取工具(LLM 调用时按名查找) |
|
||||
| `List()` | 列出所有工具 |
|
||||
| `ToLLMTools()` | 转换为 OpenAI function calling 格式 |
|
||||
|
||||
### Tool 接口(5 方法)
|
||||
|
||||
```go
|
||||
type Tool interface {
|
||||
Name() string
|
||||
Description() string
|
||||
Parameters() map[string]interface{} // JSON Schema
|
||||
RiskLevel() RiskLevel
|
||||
Execute(ctx context.Context, args map[string]interface{}) (*Result, error)
|
||||
}
|
||||
```
|
||||
|
||||
### DynamicRiskTool 扩展接口
|
||||
|
||||
```go
|
||||
type DynamicRiskTool interface {
|
||||
Tool
|
||||
RiskLevelForArgs(args map[string]interface{}) RiskLevel
|
||||
}
|
||||
```
|
||||
|
||||
安全控制器 `Check` 时优先调用动态评估:
|
||||
|
||||
```go
|
||||
func assessToolRisk(tool tools.Tool, args map[string]interface{}) tools.RiskLevel {
|
||||
if dt, ok := tool.(tools.DynamicRiskTool); ok {
|
||||
return dt.RiskLevelForArgs(args) // 动态优先
|
||||
}
|
||||
return tool.RiskLevel()
|
||||
}
|
||||
```
|
||||
|
||||
### Result 结构
|
||||
|
||||
```go
|
||||
type Result struct {
|
||||
Success bool `json:"success"`
|
||||
Output string `json:"output"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 参数解析辅助
|
||||
|
||||
```go
|
||||
func StringParam(args map[string]interface{}, key string) string
|
||||
func IntParam(args map[string]interface{}, key string) int // 兼容 int/float64/int64
|
||||
```
|
||||
|
||||
LLM 返回的 JSON 参数反序列化后,数字可能是 `float64`(JSON 原生),`IntParam` 统一处理。
|
||||
|
||||
### 已注册工具一览
|
||||
|
||||
| 工具 | 静态风险 | 动态风险 | 功能 |
|
||||
|------|---------|---------|------|
|
||||
| `ssh_execute` | High | Safe~Critical | 远程命令执行(DynamicRiskTool) |
|
||||
| `analyze_output` | Safe | — | 输出分析(纯文本,只读) |
|
||||
|
||||
详细设计见 [05 - Builtin 内置工具](05-Builtin内置命令.md)。
|
||||
|
||||
> 与 opsxcli 的差异:剥离了 `local_bash` / `scp_transfer` / `file_read` / `file_search` 四个工具;剥离 SSH 连接池(aiops 直接用 `pkg/remoteexec`,无连接池需求);剥离 `Closer` 接口(Web 工具无资源需释放)。
|
||||
|
||||
## 扩展指南
|
||||
|
||||
### 添加新工具
|
||||
|
||||
```go
|
||||
// 1. 实现 Tool 接口(必要时实现 DynamicRiskTool)
|
||||
type MyTool struct{}
|
||||
func (t *MyTool) Name() string { return "my_tool" }
|
||||
func (t *MyTool) Description() string { return "我的工具" }
|
||||
func (t *MyTool) Parameters() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"target": map[string]interface{}{"type": "string", "description": "目标"},
|
||||
},
|
||||
"required": []string{"target"},
|
||||
}
|
||||
}
|
||||
func (t *MyTool) RiskLevel() tools.RiskLevel { return tools.RiskMedium }
|
||||
func (t *MyTool) Execute(ctx context.Context, args map[string]interface{}) (*tools.Result, error) {
|
||||
// 实现
|
||||
return &tools.Result{Success: true, Output: "done"}, nil
|
||||
}
|
||||
|
||||
// 2. 在 agentcli.NewEngine 中注册
|
||||
registry.Register(&MyTool{})
|
||||
```
|
||||
|
||||
### 扩展危险命令黑名单
|
||||
|
||||
修改 `tools/risk.go` 的 `dangerousCommands` 切片:
|
||||
|
||||
```go
|
||||
var dangerousCommands = []string{
|
||||
// ... 现有
|
||||
"your-critical-command",
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义确认逻辑
|
||||
|
||||
controller 的 `confirmFn` 可对接企业审批系统(如先走工单再确认):
|
||||
|
||||
```go
|
||||
engine.SetConfirmFn(func(toolName string, args map[string]interface{}, risk tools.RiskLevel) (bool, error) {
|
||||
// 1. 推 WS 弹框让用户初步确认
|
||||
// 2. 高危场景额外对接审批工单系统
|
||||
// 3. 返回最终批准结果
|
||||
})
|
||||
```
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 陷阱 | 解决方案 |
|
||||
|------|----------|
|
||||
| 忘记设置 RiskLevel 导致误判 | 默认按接口返回值,高危工具必须显式声明 |
|
||||
| 高危工具未实现 DynamicRiskTool | 静态等级无法反映参数差异,必须实现动态评估 |
|
||||
| 黑名单只看前缀匹配 | `IsDangerousCommand` 同时做精确匹配 + `rm -rf` 根路径特殊检测 |
|
||||
| 工具 Execute 无超时 | `Config.ToolTimeout=120s` 兜底(`context.WithTimeout`) |
|
||||
| 确认回调无超时 | controller 的 `confirmFn` 内置 5 分钟超时 |
|
||||
| 黑名单可被确认放行 | Check 策略第一层 Critical 直接 `return false, nil`,不走 confirmFn |
|
||||
| LLM 编造工具名 | `registry.Get` 找不到时返回 observation「工具未找到」,LLM 自动换法 |
|
||||
| 参数 JSON 解析失败 | `json.Unmarshal` 失败时返回 observation「参数解析失败」,不 panic |
|
||||
|
||||
## 与 opsxcli 的差异总结
|
||||
|
||||
| 维度 | opsxcli | aiops |
|
||||
|------|---------|-------|
|
||||
| 确认机制 | `bufio.Reader` CLI 终端交互 | WebSocket `confirm` 事件 + 前端 `useConfirm()` 弹框 |
|
||||
| 审计日志 | 文件 `~/.opsxcli/audit/audit.log` | DB 表 `agent_cli_messages` |
|
||||
| 风险评估位置 | `safety/risk.go` | `tools/risk.go`(避免循环依赖) |
|
||||
| 内存审计历史 | `history []ExecutionRecord`(上限 1000 FIFO) | 剥离(直接走 DB) |
|
||||
| 工具集 | 6 个 | 2 个 |
|
||||
| SSH 连接池 | 有(`ssh_pool.go`) | 剥离(用 `pkg/remoteexec`) |
|
||||
| Closer 接口 | 有 | 剥离 |
|
||||
|
||||
安全核心(三级模式 / 五级风险 / 黑名单 / 动态评估)完全保留,差异仅在确认机制和审计载体。
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -1,388 +0,0 @@
|
||||
# 10 - 会话管理
|
||||
|
||||
> 本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(`backend/model/agent_cli.go` + `controller/agent_cli.go`)。
|
||||
> **核心改造**:opsxcli 用本地 JSONL 文件存储会话(单用户 CLI 场景);aiops 用 **GORM 数据库表**存储会话与消息,支持多用户并发、按 user_id 隔离、REST 查询与历史回放。
|
||||
|
||||
## 设计理念
|
||||
|
||||
- **会话管理**:Agent 引擎本身无状态(每次任务独立),所有对话过程持久化到会话层,支持历史回溯、审计、断点排查
|
||||
- **多用户隔离**:Web 场景下多个用户同时使用 Agent CLI,会话必须按 user_id 隔离
|
||||
- **结构化查询**:会话需要列表、筛选、删除等业务操作,必须走 DB 而非文件
|
||||
|
||||
> 为什么改用 DB?opsxcli 是 CLI 工具,单用户、单机使用,JSONL 文件够用且零依赖;aiops 是 Web 平台,多用户并发、需要列表查询和按主机/用户筛选、需要与其他业务表(Server、User)关联 —— DB 是必然选择。
|
||||
|
||||
## 架构总览
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ REST + WebSocket │
|
||||
│ /ws/agent-cli (WebSocket 推流) │
|
||||
│ /aiops/agent-cli/* (REST 会话管理) │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ controller/agent_cli.go │
|
||||
│ AgentCLIHandler │
|
||||
│ ├─ Run (WebSocket) │
|
||||
│ ├─ ListSessions / GetSession / DeleteSession │
|
||||
│ └─ agentConn (单连接运行态) │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ model/agent_cli.go (GORM) │
|
||||
│ AgentCLISession (agent_cli_sessions 表) │
|
||||
│ AgentCLIMessage (agent_cli_messages 表) │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ MySQL / SQLite (aiops 既有 DB) │
|
||||
└────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 核心数据模型
|
||||
|
||||
### AgentCLISession(会话)
|
||||
|
||||
```go
|
||||
// backend/model/agent_cli.go
|
||||
|
||||
type AgentCLISession struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
UserID uint `gorm:"index" json:"user_id"` // 发起用户(隔离)
|
||||
ServerID uint `gorm:"index" json:"server_id"` // 目标主机
|
||||
ServerName string `gorm:"size:128" json:"server_name"` // 主机名快照
|
||||
ServerHost string `gorm:"size:64" json:"server_host"` // 主机 IP 快照
|
||||
Title string `gorm:"size:255" json:"title"` // 任务标题(query 截断)
|
||||
Status string `gorm:"size:32;default:running" json:"status"` // running/done/aborted/error
|
||||
Provider string `gorm:"size:50" json:"provider"` // LLM 提供商
|
||||
Model string `gorm:"size:100" json:"model"` // 模型名
|
||||
Rounds int `json:"rounds"` // ReAct 轮次
|
||||
TotalTokens int `json:"total_tokens"` // token 消耗
|
||||
Duration int64 `json:"duration"` // 耗时(毫秒)
|
||||
Error string `gorm:"type:text" json:"error,omitempty"` // 错误信息
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (AgentCLISession) TableName() string { return "agent_cli_sessions" }
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
|
||||
- **`UserID` 索引**:多用户隔离的核心,所有查询都按 user_id 过滤
|
||||
- **`ServerID` + `ServerName` + `ServerHost`**:关联主机的同时保存快照(主机 later 被删/改名时历史仍可读)
|
||||
- **`Status` 四态**:`running`(进行中)/ `done`(完成)/ `aborted`(用户中断)/ `error`(出错或达最大轮次)
|
||||
- **`Title`**:query 截断到 60 字符,作为会话列表的显示标题
|
||||
|
||||
### AgentCLIMessage(消息)
|
||||
|
||||
```go
|
||||
type AgentCLIMessage struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
SessionID uint `gorm:"index;not null" json:"session_id"`
|
||||
Round int `json:"round"` // ReAct 轮次
|
||||
Type string `gorm:"size:32" json:"type"` // thought/tool_call/tool_result/done/error/session
|
||||
Role string `gorm:"size:32" json:"role"` // user/assistant/tool
|
||||
Content string `gorm:"type:text" json:"content"` // 思考文本/最终答案
|
||||
ToolName string `gorm:"size:64" json:"tool_name"` // 工具名
|
||||
ToolArgs string `gorm:"type:text" json:"tool_args"` // 工具参数 JSON
|
||||
ToolOutput string `gorm:"type:text" json:"tool_output"` // 工具输出
|
||||
Risk string `gorm:"size:32" json:"risk"` // 风险等级
|
||||
Success bool `json:"success"`
|
||||
Duration string `gorm:"size:32" json:"duration"` // 工具耗时
|
||||
Tokens int `json:"tokens"`
|
||||
CreatedAt time.Time `gorm:"index" json:"created_at"`
|
||||
}
|
||||
|
||||
func (AgentCLIMessage) TableName() string { return "agent_cli_messages" }
|
||||
```
|
||||
|
||||
**消息类型映射引擎事件**:
|
||||
|
||||
| Event.Type | DB Type | DB Role | Content | ToolName | ToolOutput |
|
||||
|------------|---------|---------|---------|----------|------------|
|
||||
| 用户输入 | (无 Type) | user | query | — | — |
|
||||
| `thought` | thought | assistant | AI 思考文本 | — | — |
|
||||
| `tool_call` | tool_call | tool | (空) | 工具名 | (空,Args 在 ToolArgs) |
|
||||
| `tool_result` | tool_result | tool | 输出 | 工具名 | 输出(冗余便于查询) |
|
||||
| `done` | done | assistant | 最终答案 | — | — |
|
||||
| `error` | error | assistant | 错误信息 | — | — |
|
||||
| `progress` | — | — | — | — | — |(不入库,太频繁)
|
||||
|
||||
## 会话生命周期
|
||||
|
||||
```
|
||||
WebSocket start {query}
|
||||
│
|
||||
▼
|
||||
startTask
|
||||
├─ 创建 AgentCLISession (status=running)
|
||||
├─ 存用户消息 (role=user, content=query)
|
||||
├─ 构建 Engine、注入 Callback
|
||||
└─ go engine.Run(ctx, query, serverInfo)
|
||||
│
|
||||
│ 每个事件触发 onEvent → 落库 agent_cli_messages
|
||||
│ ├─ thought → persistMsg(type=thought, role=assistant)
|
||||
│ ├─ tool_call → persistMsg(type=tool_call, role=tool, args, risk)
|
||||
│ ├─ tool_result → persistMsg(type=tool_result, role=tool, output, success)
|
||||
│ ├─ done → persistMsg(type=done, role=assistant, answer, tokens)
|
||||
│ └─ error → persistMsg(type=error, role=assistant, message)
|
||||
│
|
||||
▼
|
||||
finish (更新 session.status / rounds / duration / error)
|
||||
├─ ctx.Err() != nil → status=aborted
|
||||
├─ err != nil → status=error
|
||||
└─ 否则 → status=done
|
||||
```
|
||||
|
||||
## controller 实现
|
||||
|
||||
### startTask(创建会话)
|
||||
|
||||
```go
|
||||
// backend/controller/agent_cli.go
|
||||
|
||||
func (ac *agentConn) startTask(sshCfg remoteexec.SSHConfig, llmCfg model.LLMConfig, query string) {
|
||||
ac.abort() // 取消上一次任务
|
||||
|
||||
// 1. 创建 DB 会话
|
||||
title := query
|
||||
if r := []rune(title); len(r) > 60 {
|
||||
title = string(r[:60]) + "..."
|
||||
}
|
||||
sess := &model.AgentCLISession{
|
||||
UserID: ac.userID, ServerID: ac.server.ID,
|
||||
ServerName: ac.server.Hostname, ServerHost: ac.server.IP,
|
||||
Title: title, Status: "running",
|
||||
Provider: llmCfg.Provider, Model: llmCfg.Model,
|
||||
}
|
||||
ac.db.Create(sess)
|
||||
ac.session = sess
|
||||
|
||||
// 2. 存用户消息
|
||||
ac.persistMsg(sess.ID, 0, "user", "user", query, "", "", "", "", false, "", 0)
|
||||
|
||||
// 3. 构建 Engine
|
||||
engine, _ := agentcli.NewEngine(...)
|
||||
engine.SetCallback(ac.onEvent)
|
||||
engine.SetConfirmFn(ac.confirmFn)
|
||||
|
||||
// 4. 启动 ReAct 循环(goroutine)
|
||||
go func() {
|
||||
defer engine.Close()
|
||||
_, err := engine.Run(ctx, query, serverInfo)
|
||||
// ... finish
|
||||
}()
|
||||
}
|
||||
```
|
||||
|
||||
### onEvent(事件落库 + 推 WS)
|
||||
|
||||
```go
|
||||
func (ac *agentConn) onEvent(e core.Event) {
|
||||
ev := wsEvent{...} // 序列化
|
||||
if ac.session != nil {
|
||||
ev.SessionID = ac.session.ID
|
||||
}
|
||||
ac.write(ev) // 推 WS
|
||||
|
||||
// 持久化(progress 不入库)
|
||||
if ac.session == nil { return }
|
||||
sid := ac.session.ID
|
||||
switch e.Type {
|
||||
case core.EventThought:
|
||||
ac.persistMsg(sid, e.Round, "thought", "assistant", e.Thought, ...)
|
||||
case core.EventToolCall:
|
||||
argsJSON, _ := json.Marshal(e.ToolArgs)
|
||||
ac.persistMsg(sid, e.Round, "tool_call", "tool", "", e.ToolName, string(argsJSON), "", e.Risk, ...)
|
||||
case core.EventToolResult:
|
||||
ac.persistMsg(sid, e.Round, "tool_result", "tool", e.Output, e.ToolName, "", "", "", e.Success, e.Duration, 0)
|
||||
case core.EventDone:
|
||||
ac.persistMsg(sid, e.Round, "done", "assistant", e.Answer, ..., true, "", e.Tokens)
|
||||
case core.EventError:
|
||||
ac.persistMsg(sid, e.Round, "error", "assistant", e.Message, ...)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### finish(结束会话)
|
||||
|
||||
```go
|
||||
func (ac *agentConn) finish(sess *model.AgentCLISession, status, errMsg string, rounds int, dur time.Duration) {
|
||||
// 推 WS 结束事件
|
||||
ac.write(wsEvent{Type: status, SessionID: sess.ID, Message: errMsg, Round: rounds})
|
||||
// 更新 DB
|
||||
updates := map[string]interface{}{
|
||||
"status": status, "rounds": rounds, "duration": dur.Milliseconds(),
|
||||
}
|
||||
if errMsg != "" {
|
||||
updates["error"] = errMsg
|
||||
}
|
||||
ac.db.Model(sess).Updates(updates)
|
||||
}
|
||||
```
|
||||
|
||||
### REST 接口
|
||||
|
||||
```go
|
||||
// backend/router/routes.go
|
||||
|
||||
agentCLIGroup := authorized.Group("/aiops/agent-cli")
|
||||
{
|
||||
agentCLIGroup.GET("/sessions", agentCLIHandler.ListSessions) // 列表(按 user_id 过滤)
|
||||
agentCLIGroup.GET("/sessions/:id", agentCLIHandler.GetSession) // 详情(含全部消息)
|
||||
agentCLIGroup.DELETE("/sessions/:id", agentCLIHandler.DeleteSession) // 删除(校验归属)
|
||||
}
|
||||
```
|
||||
|
||||
前端 `frontend/src/api/agentCli.ts` 调用这些接口实现「会话历史」页面:
|
||||
|
||||
- 列表展示用户的所有会话(标题、主机、状态、耗时、token)
|
||||
- 点击某会话查看完整 ReAct 推理过程(thought / tool_call / tool_result 时间线)
|
||||
- 删除会话(连带消息)
|
||||
|
||||
## WebSocket 协议
|
||||
|
||||
### 后端 → 前端(wsEvent)
|
||||
|
||||
```go
|
||||
type wsEvent struct {
|
||||
Type string `json:"type"` // session/thought/tool_call/tool_result/progress/done/error/confirm
|
||||
Round int `json:"round,omitempty"`
|
||||
SessionID uint `json:"session_id,omitempty"`
|
||||
Thought string `json:"thought,omitempty"`
|
||||
ToolName string `json:"tool_name,omitempty"`
|
||||
ToolArgs map[string]interface{} `json:"tool_args,omitempty"`
|
||||
Risk string `json:"risk,omitempty"`
|
||||
Output string `json:"output,omitempty"`
|
||||
Success bool `json:"success,omitempty"`
|
||||
Duration string `json:"duration,omitempty"`
|
||||
Tokens int `json:"tokens,omitempty"`
|
||||
Answer string `json:"answer,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
ConfirmID string `json:"confirm_id,omitempty"` // confirm 事件专属
|
||||
Command string `json:"command,omitempty"` // confirm 事件专属
|
||||
}
|
||||
```
|
||||
|
||||
### 前端 → 后端(wsControl)
|
||||
|
||||
```go
|
||||
type wsControl struct {
|
||||
Type string `json:"type"` // start/confirm/deny/abort
|
||||
Query string `json:"query,omitempty"` // start 时任务描述
|
||||
ConfirmID string `json:"confirm_id,omitempty"` // confirm/deny 时
|
||||
}
|
||||
```
|
||||
|
||||
## 并发与隔离
|
||||
|
||||
### 单连接串行
|
||||
|
||||
每个 WebSocket 连接(`agentConn`)同一时刻只运行一个任务:
|
||||
|
||||
```go
|
||||
func (ac *agentConn) startTask(...) {
|
||||
ac.abort() // 取消上一次任务,再启动新的
|
||||
}
|
||||
```
|
||||
|
||||
### 多用户隔离
|
||||
|
||||
- 每个会话绑定 `UserID`(JWT 中间件注入)
|
||||
- REST 查询自动按 `user_id` 过滤
|
||||
- 删除会话时校验归属
|
||||
|
||||
### 线程安全
|
||||
|
||||
- `agentConn.writeMu sync.Mutex` —— 串行化 WebSocket 写入
|
||||
- `agentConn.cfMu sync.Mutex` —— 保护 `confirms` map
|
||||
- 引擎 `core.Agent.mu sync.Mutex` —— 保护 token 计数等内部状态
|
||||
|
||||
## 持久化策略
|
||||
|
||||
### 落库失败不阻断
|
||||
|
||||
```go
|
||||
func (ac *agentConn) persistMsg(...) {
|
||||
if err := ac.db.Create(msg).Error; err != nil {
|
||||
_ = err // 持久化失败不阻断主流程
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
DB 写入失败时,WS 推送仍正常,用户体验不受影响(只是历史回放缺这条)。
|
||||
|
||||
### progress 不入库
|
||||
|
||||
`progress` 事件每个轮次都发,且只含 round 和 token,价值低、频率高,不入库(避免 DB 压力)。
|
||||
|
||||
## 历史回放
|
||||
|
||||
前端通过 `GET /sessions/:id` 拿到完整消息列表,按时间顺序渲染:
|
||||
|
||||
```
|
||||
[会话 #42] 检查 nginx 启动失败的原因
|
||||
├─ 用户:检查 nginx 启动失败的原因
|
||||
├─ Round 1 [thought]:我先查看 nginx 服务状态...
|
||||
├─ Round 1 [tool_call] ssh_execute "systemctl status nginx" (高风险)
|
||||
├─ Round 1 [tool_result] failed (1.2s) [exit_code=3]
|
||||
├─ Round 2 [thought]:服务 failed,查看日志...
|
||||
├─ Round 2 [tool_call] ssh_execute "journalctl -u nginx -n 500" (安全)
|
||||
├─ Round 2 [tool_result] 500 行 (0.8s)
|
||||
├─ Round 3 [thought]:日志太长,先分析...
|
||||
├─ Round 3 [tool_call] analyze_output(output, focus="error")
|
||||
├─ Round 3 [tool_result] 8 行疑似错误 (0.05s)
|
||||
├─ Round 4 [thought]:端口被占,确认一下...
|
||||
├─ Round 4 [tool_call] ssh_execute "ss -tlnp | grep :80" (安全)
|
||||
├─ Round 4 [tool_result] (0.3s)
|
||||
└─ [done] 根因:80 端口被 apache 占用。建议:systemctl stop httpd && systemctl start nginx
|
||||
```
|
||||
|
||||
这是 opsxcli「会话导出为 Markdown」的 Web 升级版 —— 不需要导出,直接在线回放。
|
||||
|
||||
## 扩展指南
|
||||
|
||||
### 添加会话标签 / 收藏
|
||||
|
||||
1. 在 `AgentCLISession` 增加 `Tags string` / `Starred bool` 字段
|
||||
2. 增加 REST 接口 `PATCH /sessions/:id`
|
||||
3. 前端列表增加标签筛选
|
||||
|
||||
### 会话统计
|
||||
|
||||
```go
|
||||
// 例:用户本月使用的 token 总数
|
||||
db.Model(&AgentCLIMessage{}).
|
||||
Where("session_id IN (?) AND type = ?",
|
||||
db.Model(&AgentCLISession{}).Select("id").Where("user_id = ?", uid),
|
||||
"done").
|
||||
Select("COALESCE(SUM(tokens), 0)").Scan(&totalTokens)
|
||||
```
|
||||
|
||||
### 导出会话为 Markdown
|
||||
|
||||
参考 opsxcli 的 `ExportMarkdown`,在 controller 增加一个 REST 接口,遍历消息组装 Markdown 文本返回。
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 陷阱 | 解决方案 |
|
||||
|------|----------|
|
||||
| 多用户看到别人的会话 | 所有查询按 `user_id` 过滤,删除时校验归属 |
|
||||
| WebSocket 断开导致会话悬挂 | `ctx.Err()` 触发 finish(status=aborted);前端重连后可查看历史 |
|
||||
| 消息表无限增长 | 定期清理老会话(运维任务);或按 `created_at` 分区 |
|
||||
| 落库失败丢失历史 | 不阻断主流程,但应在日志记录;progress 不入库减少压力 |
|
||||
| 长任务超过 WS 超时 | ReAct 循环每步检查 `ctx.Err()`,前端断开 → ctx cancel → finish(aborted) |
|
||||
| 并发写 WS 错乱 | `writeMu sync.Mutex` 串行化写入 |
|
||||
| ConfirmFn 无人响应 | 5 分钟超时自动拒绝 |
|
||||
| `lastRound` 不准 | 从 DB 查 `MAX(round)` 而非内存计数(断线重连场景) |
|
||||
|
||||
## 与 opsxcli 的差异总结
|
||||
|
||||
| 维度 | opsxcli | aiops |
|
||||
|------|---------|-------|
|
||||
| 存储后端 | JSONL 文件(`~/.opsxcli/agent/sessions/`) | GORM DB(`agent_cli_sessions` / `agent_cli_messages` 表) |
|
||||
| 多用户 | 不支持(单用户 CLI) | 原生支持(user_id 索引隔离) |
|
||||
| 会话结构 | `{session-id}/meta.json + messages.jsonl` | 单表 sessions + 单表 messages(一对多) |
|
||||
| 查询能力 | 只能遍历文件 | SQL 任意查询、JOIN、聚合 |
|
||||
| 导出 | ExportMarkdown / ExportJSON / ExportToFile | 在线回放(前端时间线) |
|
||||
| 消息字段 | role/content/tool_calls | type/role/content/tool_name/tool_args/tool_output/risk/success/duration/tokens |
|
||||
| 接口 | Manager 接口(10 方法) | REST(3 方法)+ WebSocket 推流 |
|
||||
|
||||
存储载体变了,但「会话 = 元数据 + 消息流」的核心模型保留。DB 方案在多用户 Web 场景下是必然选择,也使会话与其他业务表(Server、User、LLMConfig)的关联变得自然。
|
||||
|
||||
> 作者:Logan.Li
|
||||
@@ -0,0 +1,8 @@
|
||||
# Agent 文档目录已拆分
|
||||
|
||||
`docs/agent` 名称容易同时指代 AI Agent 和主机客户端 Agent,因此正式文档已拆分:
|
||||
|
||||
- [AI Agent / Agent CLI 文档](../ai-agent/00-README.md)
|
||||
- [客户端 Agent 文档](../client-agent/README.md)
|
||||
|
||||
保留当前目录仅用于兼容历史链接。// Logan.Li
|
||||
@@ -0,0 +1,124 @@
|
||||
# AI Agent / Agent CLI 设计文档
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:AIOps 平台内置的 AI 运维 Agent、Agent CLI WebSocket 执行页、主机 AI 任务、Kubernetes AI 排障。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 定位
|
||||
|
||||
AI Agent / Agent CLI 是 AIOps 的智能执行入口。用户在浏览器中选择目标主机或 Kubernetes 目标,用自然语言描述任务,后端 Agent 引擎通过 LLM 推理、工具调用、安全检查和会话持久化完成排障或操作建议。
|
||||
|
||||
一句话:**AIOps Agent CLI 是运行在 AIOps 后端的 Web 版智能运维 Agent,不是本地命令行工具,也不是客户端采集 Agent。**
|
||||
|
||||
| 名称 | 含义 |
|
||||
|------|------|
|
||||
| AI Agent / Agent CLI | AIOps 后端内置的 ReAct 智能执行引擎 |
|
||||
| 客户端 Agent | 部署在被管主机上的指标采集 Agent,见 `docs/client-agent/` |
|
||||
| AIOps Assistant | 普通对话式智能助手,偏分析和问答 |
|
||||
|
||||
---
|
||||
|
||||
## 文档目录
|
||||
|
||||
当前目录压缩为 8 篇正文,避免把同一条链路拆得过碎:
|
||||
|
||||
| 编号 | 文档 | 说明 |
|
||||
|------|------|------|
|
||||
| 01 | [项目概述与设计哲学](01-项目概述与设计哲学.md) | 产品定位、边界、成功标准 |
|
||||
| 02 | [整体架构设计](02-整体架构设计.md) | 前端、WebSocket、Controller、Engine、DB 的分层 |
|
||||
| 03 | [执行循环与 Harness](03-Harness工程化与反馈回路.md) | ReAct 主循环、Prompt、事件、上下文裁剪、反馈回路 |
|
||||
| 04 | [工具与 ACI 设计](04-工具与ACI设计.md) | Tool 接口、工具清单、Agent-Computer Interface 设计原则 |
|
||||
| 05 | [安全治理与审计](05-安全治理与审计.md) | 风险评估、确认、脱敏、策略中心、审计落点 |
|
||||
| 06 | [会话记忆与 Evolver](06-会话记忆与Evolver.md) | 会话表、消息表、历史恢复、记忆注入、自进化 |
|
||||
| 07 | [路线图与评测](07-路线图与评测.md) | 可用堡垒机、企业堡垒机、智能闭环、评测指标 |
|
||||
| 08 | [代码审计与平台智能化优化](08-代码审计与平台智能化优化.md) | Agent CLI 代码差距、AIOps 上下文工具、告警分析和预测分析升级路线 |
|
||||
|
||||
---
|
||||
|
||||
## 当前能力矩阵
|
||||
|
||||
| 能力 | 状态 | 代码落点 |
|
||||
|------|------|----------|
|
||||
| WebSocket 对话流 | 已完成 | `backend/controller/agent_cli.go`、`AgentCLI.tsx` |
|
||||
| 主机模式 | 已完成 | `ssh_execute`、`analyze_output` |
|
||||
| Kubernetes 模式 | 已完成 | `kubernetes_get`、`pod_logs`、`pod_execute`、`kubernetes_action` |
|
||||
| ReAct 主循环 | 已完成 | `backend/pkg/agentcli/core/agent.go` |
|
||||
| LLM 双协议 | 已完成 | OpenAI 兼容 + Anthropic Messages |
|
||||
| Prompt 构建 | 已完成 | `backend/pkg/agentcli/prompt/prompt.go` |
|
||||
| 安全确认 | 已完成 | `safety.Controller` + WebSocket `confirm` |
|
||||
| 会话持久化 | 已完成 | `agent_cli_sessions` / `agent_cli_messages` |
|
||||
| 历史回放 | 已完成 | `AgentCLIHistory.tsx`、`AgentCLIMessages.tsx` |
|
||||
| Evolver 记忆 | 已完成 | `backend/pkg/agentcli/evolver/` |
|
||||
| 工具审计统一 | 待增强 | 后续接入 `session_commands` 或专属审计表 |
|
||||
| 策略中心接入 | 待增强 | 后续接入 `sys_audit_settings` 扩展策略 |
|
||||
|
||||
---
|
||||
|
||||
## 专业参考基线
|
||||
|
||||
这些文档不是孤立设计,主要对齐以下业界和论文里的共识:
|
||||
|
||||
| 参考 | AIOps 落点 |
|
||||
|------|------------|
|
||||
| [ReAct](https://arxiv.org/abs/2210.03629) | 推理和行动交替,工具输出反哺下一轮判断 |
|
||||
| [Toolformer](https://arxiv.org/abs/2302.04761) | 工具调用要关注何时调用、传什么参数、如何吸收结果 |
|
||||
| [Anthropic - Building Effective Agents](https://www.anthropic.com/engineering/building-effective-agents) | Agent 适合有明确成功标准、反馈回路和人工监督的任务 |
|
||||
| [12-Factor Agents](https://github.com/humanlayer/12-factor-agents) | 自主管理 Prompt、上下文、工具、暂停/恢复和人工确认 |
|
||||
| [SWE-agent ACI](https://arxiv.org/abs/2405.15793) | 面向模型设计专门的工具接口和反馈格式,而不是直接暴露人类 UI |
|
||||
| [OpenAI Agents - Guardrails](https://openai.github.io/openai-agents-python/guardrails/) | 输入、输出、工具调用都需要 guardrail |
|
||||
| [OpenAI Agents - Tracing](https://openai.github.io/openai-agents-python/tracing/) | Agent 运行要记录 LLM、工具、guardrail、自定义事件 |
|
||||
| [OWASP LLM Top 10](https://owasp.org/www-project-top-10-for-large-language-model-applications/) | Prompt Injection、敏感信息泄露、插件设计、过度自主权是核心风险 |
|
||||
| [NIST AI RMF GenAI Profile](https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence) | 企业级 AI 需要治理、测量、管理和生命周期风险控制 |
|
||||
|
||||
---
|
||||
|
||||
## 端到端链路
|
||||
|
||||
```text
|
||||
frontend/src/components/aiops/AgentCLI.tsx
|
||||
├── 选择模式:host / kubernetes
|
||||
├── 选择目标:服务器 / 集群 / Namespace / Pod / Container
|
||||
├── 选择安全模式:strict / balanced / permissive
|
||||
└── WebSocket /ws/agent-cli
|
||||
│
|
||||
▼
|
||||
backend/controller/agent_cli.go
|
||||
├── JWT 用户身份
|
||||
├── 加载目标资产 / K8s 上下文
|
||||
├── 加载 LLM 配置
|
||||
├── 创建会话和消息
|
||||
├── 注入 confirmFn 和 event callback
|
||||
└── 调用 agentcli.Engine.Run()
|
||||
│
|
||||
▼
|
||||
backend/pkg/agentcli/
|
||||
├── llm.Client
|
||||
├── tools.Registry
|
||||
├── safety.Controller
|
||||
├── evolver.EvolverEngine
|
||||
└── core.Agent ReAct loop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **平台内置**:Agent CLI 是 AIOps 后端能力,不要求用户安装本地 AI 工具。
|
||||
2. **目标受控**:只对用户有权限的资产、集群、Pod 执行工具。
|
||||
3. **工具最小化**:工具暴露越少,模型误调用和越权风险越低。
|
||||
4. **安全前置**:危险命令硬拒,高危操作必须确认,后续接审批。
|
||||
5. **过程可见**:思考、工具调用、工具输出、确认和最终结果均走 WebSocket 事件流。
|
||||
6. **历史可追溯**:会话和消息入库,支持回放、删除、继续追问。
|
||||
7. **记忆可控**:Evolver 记忆按用户隔离,后续逐步 DB 化和可视化管理。
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [客户端 Agent](../client-agent/README.md) | 主机指标采集 Agent |
|
||||
| [安全审计与堡垒机](../security-bastion/README.md) | 会话、审计、审批统一设计 |
|
||||
| [终端体系](../terminal/README.md) | WebSSH、Pod exec、kubectl 终端 |
|
||||
| [Kubernetes 管理](../kubernetes/README.md) | K8s 资源、Pod、YAML、AI 排障 |
|
||||
@@ -0,0 +1,121 @@
|
||||
# 01 - 项目概述与设计哲学
|
||||
|
||||
> 本文描述 AIOps 当前 AI Agent / Agent CLI 的产品定位和工程原则。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 项目定位
|
||||
|
||||
AIOps Agent CLI 是一个面向运维场景的 Web 智能执行器。它不是普通聊天助手,而是能在受控目标上调用工具的 Agent:
|
||||
|
||||
- 主机模式:通过 SSH 在服务器上执行只读排查或受控变更。
|
||||
- Kubernetes 模式:通过 AIOps 后端 K8s 客户端查询资源、查看日志、进入 Pod 或执行受控操作。
|
||||
- 会话模式:任务过程实时推送,任务结束后可回放和继续追问。
|
||||
|
||||
适用任务:
|
||||
|
||||
| 场景 | 示例 |
|
||||
|------|------|
|
||||
| 主机排障 | “检查 nginx 为什么启动失败” |
|
||||
| 服务检查 | “看看磁盘空间和最近错误日志” |
|
||||
| K8s 排障 | “分析这个 Pod CrashLoopBackOff 的原因” |
|
||||
| 输出分析 | “总结这段 journalctl 输出里的关键错误” |
|
||||
| 变更建议 | “给出修复步骤,执行前让我确认” |
|
||||
|
||||
---
|
||||
|
||||
## 与普通 AI 助手的区别
|
||||
|
||||
| 维度 | AIOps Assistant | Agent CLI |
|
||||
|------|-----------------|-----------|
|
||||
| 交互 | 普通问答 / 分析 | ReAct 执行流 |
|
||||
| 工具 | 通常不执行远程工具 | 可调用 SSH/K8s 工具 |
|
||||
| 目标 | 平台知识、告警分析 | 具体主机、Pod、集群 |
|
||||
| 安全 | 输入输出安全 | 工具风险、确认、审计 |
|
||||
| 历史 | 对话历史 | 会话 + 工具调用 + 输出回放 |
|
||||
|
||||
---
|
||||
|
||||
## 核心设计哲学
|
||||
|
||||
### 1. 工具少,但边界清楚
|
||||
|
||||
Agent 不需要暴露大量工具。当前工具按模式注册:
|
||||
|
||||
| 模式 | 工具 |
|
||||
|------|------|
|
||||
| host | `ssh_execute`、`analyze_output` |
|
||||
| kubernetes | `kubernetes_get`、`pod_logs`、`pod_execute`、`kubernetes_action`、`analyze_output` |
|
||||
|
||||
工具越少,Prompt 越清晰,模型越不容易绕路。
|
||||
|
||||
### 2. 过程优先于结果
|
||||
|
||||
运维场景不能只给最终答案,必须知道 Agent 做了什么:
|
||||
|
||||
- 每轮思考。
|
||||
- 调用了什么工具。
|
||||
- 传了哪些参数。
|
||||
- 输出是什么。
|
||||
- 是否触发安全确认。
|
||||
- 最终结论如何得出。
|
||||
|
||||
这些过程通过 WebSocket 推给前端,并写入 `agent_cli_messages`。
|
||||
|
||||
### 3. 安全是执行引擎的一部分
|
||||
|
||||
安全不是前端弹框,也不是事后审计。它在工具执行前生效:
|
||||
|
||||
```text
|
||||
tool_call
|
||||
→ assess risk
|
||||
→ critical hard reject
|
||||
→ high risk confirm
|
||||
→ execute if approved
|
||||
→ persist event
|
||||
```
|
||||
|
||||
### 4. 平台上下文比裸模型更重要
|
||||
|
||||
Agent 的价值来自 AIOps 上下文:
|
||||
|
||||
- 当前登录用户。
|
||||
- 资产和凭据。
|
||||
- 服务器 IP、端口、OS。
|
||||
- K8s 集群、Namespace、Pod、Container。
|
||||
- 历史会话和 Evolver 记忆。
|
||||
- 后续可加入告警、监控、日志、审批状态。
|
||||
|
||||
### 5. 记忆必须可控
|
||||
|
||||
Evolver 能把成功经验和事实写入本地记忆文件,但企业平台必须逐步做到:
|
||||
|
||||
- 按用户/团队隔离。
|
||||
- 可查看、可删除、可禁用。
|
||||
- 可审计 AI 记住了什么。
|
||||
- 可区分事实、经验、流程和偏好。
|
||||
|
||||
---
|
||||
|
||||
## 运行边界
|
||||
|
||||
Agent CLI 当前不做这些事情:
|
||||
|
||||
| 不做 | 原因 |
|
||||
|------|------|
|
||||
| 后端本机任意命令执行 | 多用户 Web 平台风险过高 |
|
||||
| 绕过资产凭据系统 | 必须受 AIOps 资产权限控制 |
|
||||
| 自动执行高危变更 | 必须确认,后续接审批 |
|
||||
| 长期保存明文敏感输出 | 后续需要输出脱敏和保留策略 |
|
||||
| 替代客户端 Agent | 客户端 Agent 负责采集,不负责 LLM 推理 |
|
||||
|
||||
---
|
||||
|
||||
## 成功标准
|
||||
|
||||
1. 用户能用自然语言描述任务。
|
||||
2. Agent 能选择正确工具获取事实。
|
||||
3. 高危动作不会静默执行。
|
||||
4. 过程可追溯,历史可回放。
|
||||
5. 失败时能给出明确原因和下一步建议。
|
||||
6. 同类任务能从历史经验中得到更好的提示。
|
||||
@@ -0,0 +1,179 @@
|
||||
# 02 - 整体架构设计
|
||||
|
||||
> 本文描述 AIOps 当前 Agent CLI 的真实工程分层、端到端链路和模块职责。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 架构定位
|
||||
|
||||
Agent CLI 是 AIOps 平台内置的智能运维执行器,整体由四层组成:
|
||||
|
||||
| 层级 | 代码位置 | 职责 |
|
||||
|------|----------|------|
|
||||
| 前端工作台 | `frontend/src/components/aiops/AgentCLI.tsx` | 目标选择、任务输入、事件流展示、确认弹框、历史侧栏 |
|
||||
| WebSocket / REST Controller | `backend/controller/agent_cli*.go` | 鉴权、目标加载、会话入库、WS 控制协议、安全确认桥接 |
|
||||
| Agent 引擎库 | `backend/pkg/agentcli/` | LLM、Prompt、ReAct、工具注册、安全控制、Evolver |
|
||||
| 平台底座 | `model.Server`、`LLMConfig`、K8s Client、凭证、DB | 资产、凭据、集群、模型配置、会话存储 |
|
||||
|
||||
核心边界:`backend/pkg/agentcli/` 不依赖 `controller` 或 `model`,只接收配置和回调;平台业务全部在 controller 层完成。
|
||||
|
||||
---
|
||||
|
||||
## 端到端调用链路
|
||||
|
||||
```text
|
||||
浏览器 AgentCLI 页面
|
||||
│
|
||||
├─ host 模式: /ws/agent-cli?mode=host&id=<serverID>&token=<jwt>
|
||||
└─ kubernetes 模式: /ws/agent-cli?mode=kubernetes&cluster=<code>&namespace=<ns>&pod=<pod>&container=<container>&token=<jwt>
|
||||
│
|
||||
▼
|
||||
AgentCLIHandler.Run()
|
||||
├─ JWTAuth 解析当前用户
|
||||
├─ 加载 Server / KubernetesCluster
|
||||
├─ 构造 SSHConfig 或 KubernetesTarget
|
||||
├─ 读取启用的 LLMConfig
|
||||
├─ 创建 / 续接 AgentCLISession
|
||||
└─ agentcli.NewEngine()
|
||||
│
|
||||
▼
|
||||
core.Agent.Run()
|
||||
├─ BuildSystemPrompt()
|
||||
├─ LLM function calling
|
||||
├─ tools.Registry 查找工具
|
||||
├─ safety.Controller 执行前检查
|
||||
├─ EventCallback 推送事件并落库
|
||||
└─ Evolver 异步学习经验
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 前端层
|
||||
|
||||
前端页面负责把 Agent 的执行过程做成可操作的工作台:
|
||||
|
||||
| 模块 | 职责 |
|
||||
|------|------|
|
||||
| `AgentCLI.tsx` | 页面主体、模式切换、目标选择、WebSocket 生命周期 |
|
||||
| `AgentCLIMessages.tsx` | 渲染思考、工具调用、工具结果、最终答案 |
|
||||
| `AgentCLIHistory.tsx` | 会话列表、回放、删除、继续追问 |
|
||||
| `frontend/src/api/aiops/agent-cli.ts` | REST 会话 API 和 WebSocket URL 构建 |
|
||||
|
||||
前端通过 WebSocket 发送控制消息:
|
||||
|
||||
| type | 含义 |
|
||||
|------|------|
|
||||
| `start` | 开始新任务 |
|
||||
| `continue` | 基于当前会话继续追问 |
|
||||
| `resume` | 加载历史会话上下文 |
|
||||
| `confirm` | 批准高危工具调用 |
|
||||
| `reject` | 拒绝高危工具调用 |
|
||||
|
||||
---
|
||||
|
||||
## Controller 层
|
||||
|
||||
Controller 是平台上下文和通用 Agent 引擎之间的适配层。
|
||||
|
||||
### 主机模式
|
||||
|
||||
`runHostWS()` 完成:
|
||||
|
||||
1. 从 `id` 查询 `model.Server`。
|
||||
2. 通过资产和凭据构造 `remoteexec.SSHConfig`。
|
||||
3. 读取启用的 `model.LLMConfig`。
|
||||
4. 建立 WebSocket 连接。
|
||||
5. 组装 `agentcli.EngineConfig{Mode: host, SSH: ...}`。
|
||||
|
||||
### Kubernetes 模式
|
||||
|
||||
`runKubernetesWS()` 完成:
|
||||
|
||||
1. 从 `cluster`、`namespace`、`pod`、`container` 解析目标。
|
||||
2. 加载 Kubernetes 集群配置。
|
||||
3. 构造轻量 K8s Client。
|
||||
4. 组装 `agentcli.EngineConfig{Mode: kubernetes, Kubernetes: ...}`。
|
||||
|
||||
### 会话与事件
|
||||
|
||||
Controller 负责将事件写入数据库:
|
||||
|
||||
| 表 | 内容 |
|
||||
|----|------|
|
||||
| `agent_cli_sessions` | 一次 AI 任务的用户、目标、模型、状态、轮次、耗时、token |
|
||||
| `agent_cli_messages` | 用户输入、思考、工具调用、工具输出、最终答案、错误 |
|
||||
|
||||
---
|
||||
|
||||
## 引擎层
|
||||
|
||||
`agentcli.NewEngine()` 组装以下组件:
|
||||
|
||||
| 组件 | 代码位置 | 说明 |
|
||||
|------|----------|------|
|
||||
| LLM Client | `pkg/agentcli/llm/` | 支持 OpenAI 兼容接口和 Anthropic Messages |
|
||||
| Registry | `pkg/agentcli/tools/registry.go` | 线程安全工具注册中心 |
|
||||
| Safety Controller | `pkg/agentcli/safety/safety.go` | 高危确认、危险命令硬拒 |
|
||||
| Core Agent | `pkg/agentcli/core/agent.go` | 16 轮 ReAct 主循环 |
|
||||
| Evolver | `pkg/agentcli/evolver/` | 经验、事实、流程记忆 |
|
||||
|
||||
引擎接收两个关键回调:
|
||||
|
||||
| 回调 | 说明 |
|
||||
|------|------|
|
||||
| `EventCallback` | 推送 `progress/thought/tool_call/tool_result/done/error` 并落库 |
|
||||
| `ConfirmFn` | 高危工具调用时由 controller 推送确认事件,等待前端响应 |
|
||||
|
||||
---
|
||||
|
||||
## 工具集
|
||||
|
||||
工具按运行模式注册,避免模型看到不该用的工具。
|
||||
|
||||
| 模式 | 工具 |
|
||||
|------|------|
|
||||
| host | `ssh_execute`、`analyze_output` |
|
||||
| kubernetes | `kubernetes_get`、`pod_logs`、`pod_execute`、`kubernetes_action`、`analyze_output` |
|
||||
|
||||
`ssh_execute` 和 `pod_execute` 支持动态风险评估,会根据实际命令计算风险等级。
|
||||
|
||||
---
|
||||
|
||||
## 数据流
|
||||
|
||||
```text
|
||||
用户输入 query
|
||||
→ 写入 agent_cli_messages(type=session, role=user)
|
||||
→ BuildSystemPrompt(mode, targetInfo, toolDescs, evolveContext)
|
||||
→ LLM Complete(function calling)
|
||||
→ tool_call event
|
||||
→ safety.Check()
|
||||
→ tool.Execute()
|
||||
→ tool_result event
|
||||
→ LLM 观察工具输出继续推理
|
||||
→ done / error event
|
||||
→ 更新 agent_cli_sessions 状态
|
||||
→ Evolver 后台学习
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 当前架构优点
|
||||
|
||||
1. **平台权限可控**:WebSocket 入口走 JWT,目标资产由后端加载。
|
||||
2. **引擎可复用**:`pkg/agentcli` 是纯库,后续可接自动化任务或告警处置。
|
||||
3. **过程可审计**:每一步工具调用和输出都有数据库记录。
|
||||
4. **工具面收敛**:主机和 K8s 模式互斥注册工具,减少误调用。
|
||||
5. **失败可降级**:Evolver 初始化失败不影响主流程。
|
||||
|
||||
---
|
||||
|
||||
## 待演进点
|
||||
|
||||
| 方向 | 建议 |
|
||||
|------|------|
|
||||
| 权限剪枝 | 按用户权限动态注册工具,例如无 K8s 变更权限则不注册 `kubernetes_action` |
|
||||
| 审计统一 | 工具调用同步写入安全审计中心,关联审批、资产、用户 |
|
||||
| 记忆 DB 化 | 将 `data/agentcli` 文件记忆迁移到数据库,支持查看和清理 |
|
||||
| 输出脱敏 | 对密钥、Token、Cookie、连接串做落库前脱敏 |
|
||||
| 审批接入 | 高危动作不只弹确认,支持企业审批流 |
|
||||
@@ -0,0 +1,144 @@
|
||||
# 03 - 执行循环与 Harness
|
||||
|
||||
> Harness 是把 LLM 包装成可靠工程系统的外部机制。AIOps 的 Harness 包含 Prompt、工具、风险控制、事件流、会话和记忆。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 核心执行循环
|
||||
|
||||
`backend/pkg/agentcli/core/agent.go` 实现 ReAct 循环:
|
||||
|
||||
```text
|
||||
用户任务
|
||||
→ 构建 System Prompt
|
||||
→ LLM 生成思考或工具调用
|
||||
→ 工具执行前安全检查
|
||||
→ 工具输出回写给 LLM
|
||||
→ LLM 基于观察继续判断
|
||||
→ 输出最终答案
|
||||
→ Evolver 异步学习
|
||||
```
|
||||
|
||||
ReAct 的价值在于让模型交替进行推理和行动,工具输出会校正模型判断,减少纯文本推理里的幻觉和错误传播。
|
||||
|
||||
---
|
||||
|
||||
## Harness 组件
|
||||
|
||||
| 组件 | 代码位置 | 作用 |
|
||||
|------|----------|------|
|
||||
| Prompt Harness | `pkg/agentcli/prompt/prompt.go` | 角色、目标、安全规则、工具说明、输出要求 |
|
||||
| Tool Harness | `pkg/agentcli/tools/` | 把真实操作封装成函数调用 |
|
||||
| Safety Harness | `pkg/agentcli/safety/` | 工具执行前风险判断和确认 |
|
||||
| Event Harness | `core.EventCallback` | 将内部状态转换为前端事件和 DB 消息 |
|
||||
| Session Harness | `controller/agent_cli*.go` | 创建会话、恢复历史、继续追问 |
|
||||
| Memory Harness | `pkg/agentcli/evolver/` | 任务结束后沉淀经验 |
|
||||
|
||||
---
|
||||
|
||||
## Core Config
|
||||
|
||||
| 字段 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `MaxIterations` | `16` | 最大 ReAct 轮数 |
|
||||
| `Temperature` | `0.3` | 运维任务偏稳定,温度不宜太高 |
|
||||
| `ToolTimeout` | `120s` | 单次工具超时 |
|
||||
| `OutputMaxLength` | `10000` | 工具输出进入上下文前截断 |
|
||||
| `MaxContextTokens` | `8000` | 历史上下文预算 |
|
||||
| `MemoryDir` | `data/agentcli` | Evolver 记忆目录 |
|
||||
| `RoundOffset` | `0` | 继续追问时接续轮次 |
|
||||
|
||||
---
|
||||
|
||||
## Prompt 构建
|
||||
|
||||
入口:
|
||||
|
||||
```go
|
||||
BuildSystemPrompt(mode, targetInfo, toolDescs, evolveContext)
|
||||
```
|
||||
|
||||
Prompt 分为六层:
|
||||
|
||||
1. 角色:主机 Agent 或 Kubernetes Agent。
|
||||
2. 目标:主机、集群、Namespace、Pod、Container。
|
||||
3. 安全:禁止破坏性命令,高危操作需确认。
|
||||
4. 工具:只注入当前模式可用工具。
|
||||
5. 工作方式:思考、行动、观察、循环、收尾。
|
||||
6. 记忆:Evolver 提供的相关经验。
|
||||
|
||||
Prompt 是软约束,不能代替执行前的代码安全检查。
|
||||
|
||||
---
|
||||
|
||||
## 事件模型
|
||||
|
||||
核心引擎产生结构化事件:
|
||||
|
||||
| 事件 | 含义 |
|
||||
|------|------|
|
||||
| `progress` | 轮次或 token 更新 |
|
||||
| `thought` | 模型阶段性判断 |
|
||||
| `tool_call` | 工具名、参数、风险 |
|
||||
| `tool_result` | 工具输出、成功状态、耗时 |
|
||||
| `done` | 最终答案 |
|
||||
| `error` | LLM、工具或最大轮次错误 |
|
||||
|
||||
Controller 将事件推送给前端,同时写入 `agent_cli_messages`。这相当于 AIOps 自己的 Agent trace。
|
||||
|
||||
---
|
||||
|
||||
## 工具调用处理
|
||||
|
||||
```text
|
||||
tool_call
|
||||
→ Registry.Get()
|
||||
→ JSON 参数解析
|
||||
→ 动态风险评估
|
||||
→ safety.Check()
|
||||
→ Execute()
|
||||
→ 超长输出自动 analyze_output
|
||||
→ 输出截断
|
||||
→ tool_result
|
||||
→ observation 返回给 LLM
|
||||
```
|
||||
|
||||
OpenAI Agents 的 guardrail 思路也强调:输入、输出和工具调用都要在各自阶段检查。AIOps 当前最核心的是工具 guardrail,后续需要补齐输入和输出 guardrail。
|
||||
|
||||
---
|
||||
|
||||
## 循环检测与检查点
|
||||
|
||||
| 机制 | 当前实现 |
|
||||
|------|----------|
|
||||
| 同参循环检测 | 同一工具同一参数超过 3 次则拒绝 |
|
||||
| 第 4 轮检查点 | 重复调用或连续失败时提示换思路 |
|
||||
| 第 8 轮检查点 | 要求总结进展,信息够就收尾 |
|
||||
| 第 12 轮检查点 | 提醒剩余步数有限,尽快输出结论 |
|
||||
|
||||
这些机制用于降低 Agent 原地打转的概率。
|
||||
|
||||
---
|
||||
|
||||
## 上下文管理
|
||||
|
||||
`trimMessages()` 的策略:
|
||||
|
||||
1. 永远保留 system prompt。
|
||||
2. 从最近消息向前保留。
|
||||
3. 控制在 `MaxContextTokens * 90%`。
|
||||
4. 至少保留最近 4 条。
|
||||
5. 裁剪后校验 tool_call 和 tool_result 配对。
|
||||
|
||||
这对应生产 Agent 的“own your context window”原则:上下文窗口是工程资源,需要明确管理。
|
||||
|
||||
---
|
||||
|
||||
## 待增强
|
||||
|
||||
| 能力 | 原因 |
|
||||
|------|------|
|
||||
| 输入 Guardrail | 防 Prompt Injection、越权任务、无目标任务 |
|
||||
| 输出 Guardrail | 防敏感信息泄露、错误修复建议、不可执行总结 |
|
||||
| Trace ID | 把一次运行串起 LLM、工具、确认、审计 |
|
||||
| Eval 数据集 | 固化典型主机/K8s 排障任务,回归测试 Agent 质量 |
|
||||
@@ -0,0 +1,145 @@
|
||||
# 04 - 工具与 ACI 设计
|
||||
|
||||
> ACI 是 Agent-Computer Interface:给模型使用的“机器接口”,不是给人点的 UI。AIOps Agent CLI 的工具设计应把运维能力包装成少量、稳定、可审计、可控的动作。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 设计目标
|
||||
|
||||
工具层解决三个问题:
|
||||
|
||||
1. 模型如何接触真实环境。
|
||||
2. 平台如何限制模型能做什么。
|
||||
3. 审计如何复盘模型做过什么。
|
||||
|
||||
这和 SWE-agent 提出的 ACI 思路一致:模型不是普通人类用户,它需要简洁、结构化、反馈明确的接口,而不是把完整 Shell、控制台或页面直接暴露出去。
|
||||
|
||||
---
|
||||
|
||||
## Tool 接口
|
||||
|
||||
当前工具接口位于 `backend/pkg/agentcli/tools/registry.go`:
|
||||
|
||||
```go
|
||||
type Tool interface {
|
||||
Name() string
|
||||
Description() string
|
||||
Parameters() map[string]interface{}
|
||||
RiskLevel() RiskLevel
|
||||
Execute(ctx context.Context, args map[string]interface{}) (*Result, error)
|
||||
}
|
||||
```
|
||||
|
||||
| 方法 | 设计要求 |
|
||||
|------|----------|
|
||||
| `Name()` | 稳定、短、语义清楚,例如 `ssh_execute` |
|
||||
| `Description()` | 告诉模型何时使用,不写产品介绍 |
|
||||
| `Parameters()` | JSON Schema 参数,字段越少越好 |
|
||||
| `RiskLevel()` | 静态风险等级 |
|
||||
| `Execute()` | 执行真实动作,返回结构化结果 |
|
||||
|
||||
---
|
||||
|
||||
## 动态风险接口
|
||||
|
||||
执行型工具的风险取决于参数,因此需要实现:
|
||||
|
||||
```go
|
||||
type DynamicRiskTool interface {
|
||||
Tool
|
||||
RiskLevelForArgs(args map[string]interface{}) RiskLevel
|
||||
}
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
| 工具调用 | 风险 |
|
||||
|----------|------|
|
||||
| `ssh_execute{"command":"df -h"}` | safe |
|
||||
| `ssh_execute{"command":"systemctl restart nginx"}` | high |
|
||||
| `ssh_execute{"command":"mkfs.ext4 /dev/sda"}` | critical |
|
||||
|
||||
---
|
||||
|
||||
## 当前工具清单
|
||||
|
||||
### Host 模式
|
||||
|
||||
| 工具 | 作用 | 风险 |
|
||||
|------|------|------|
|
||||
| `ssh_execute` | 在目标主机执行命令 | 动态风险 |
|
||||
| `analyze_output` | 分析长输出、错误日志、命令结果 | safe |
|
||||
|
||||
### Kubernetes 模式
|
||||
|
||||
| 工具 | 作用 | 风险 |
|
||||
|------|------|------|
|
||||
| `kubernetes_get` | 查询资源、事件、状态 | safe |
|
||||
| `pod_logs` | 获取 Pod / Container 日志 | safe |
|
||||
| `pod_execute` | 在容器内执行命令 | 动态风险 |
|
||||
| `kubernetes_action` | 执行重启、扩缩容、删除等变更 | high |
|
||||
| `analyze_output` | 分析长输出 | safe |
|
||||
|
||||
---
|
||||
|
||||
## 工具编排范式
|
||||
|
||||
### 主机排障
|
||||
|
||||
```text
|
||||
ssh_execute("只读检查")
|
||||
→ ssh_execute("读取日志")
|
||||
→ analyze_output("压缩长输出")
|
||||
→ ssh_execute("确认后修复")
|
||||
→ ssh_execute("验证结果")
|
||||
→ done
|
||||
```
|
||||
|
||||
### Kubernetes 排障
|
||||
|
||||
```text
|
||||
kubernetes_get("pods/events")
|
||||
→ pod_logs("查看业务错误")
|
||||
→ pod_execute("必要时进容器确认")
|
||||
→ kubernetes_action("确认后变更")
|
||||
→ kubernetes_get("回查状态")
|
||||
→ done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ACI 设计原则
|
||||
|
||||
| 原则 | AIOps 约束 |
|
||||
|------|------------|
|
||||
| 小动作 | 一次工具调用只做一个明确动作 |
|
||||
| 强反馈 | 输出包含 success、output、error、duration |
|
||||
| 可恢复 | 失败输出要能指导下一步,而不是只返回 “failed” |
|
||||
| 可审计 | 工具名、参数、风险、输出都落库 |
|
||||
| 可控权 | 工具注册受模式、用户权限、资产权限和策略控制 |
|
||||
| 可裁剪 | 工具输出能摘要、截断、脱敏 |
|
||||
|
||||
---
|
||||
|
||||
## 不应暴露的工具
|
||||
|
||||
| 工具类型 | 原因 |
|
||||
|----------|------|
|
||||
| 后端本机 shell | 多用户平台风险高,容易越权 |
|
||||
| 任意 HTTP 请求 | 容易形成 SSRF |
|
||||
| 任意文件读写 | 绕过资产文件权限和堡垒机审计 |
|
||||
| 明文凭据读取 | 凭据只能由平台注入工具,不给模型展示 |
|
||||
| 超大批量操作 | 容易造成成本、可用性和误操作风险 |
|
||||
|
||||
---
|
||||
|
||||
## 新增工具检查清单
|
||||
|
||||
1. 是否真的需要新工具,还是已有工具能完成。
|
||||
2. 是否有明确成功/失败输出。
|
||||
3. 参数是否最小化。
|
||||
4. 是否实现风险等级。
|
||||
5. 是否能按用户权限禁用。
|
||||
6. 是否会泄露敏感输出。
|
||||
7. 是否有审计字段。
|
||||
8. 是否需要审批流。
|
||||
@@ -0,0 +1,135 @@
|
||||
# 05 - 安全治理与审计
|
||||
|
||||
> Agent CLI 是能调用真实工具的 AI,因此安全边界必须在工具执行前、输出落库前和审计查询时同时生效。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 风险模型
|
||||
|
||||
OWASP LLM Top 10 中和 AIOps Agent CLI 最相关的是:
|
||||
|
||||
| 风险 | AIOps 对应点 |
|
||||
|------|--------------|
|
||||
| Prompt Injection | 用户输入或远程日志诱导模型绕过规则 |
|
||||
| Sensitive Information Disclosure | 工具输出、日志、环境变量里含密钥 |
|
||||
| Insecure Plugin Design | 工具参数过宽、权限不足、缺少校验 |
|
||||
| Excessive Agency | 模型拥有过多自主执行能力 |
|
||||
| Model Denial of Service | 超长日志、循环调用、昂贵模型反复请求 |
|
||||
|
||||
治理目标是把这些风险转成代码里的 guardrail、策略和审计。
|
||||
|
||||
---
|
||||
|
||||
## 当前安全链路
|
||||
|
||||
```text
|
||||
工具注册最小化
|
||||
→ DynamicRiskTool 评估风险
|
||||
→ safety.Controller 按模式判断
|
||||
→ critical 硬拒
|
||||
→ high/medium 触发 WebSocket 确认
|
||||
→ 执行结果落库
|
||||
```
|
||||
|
||||
安全模式:
|
||||
|
||||
| 模式 | 确认阈值 |
|
||||
|------|----------|
|
||||
| `strict` | medium 及以上 |
|
||||
| `balanced` | high 及以上,默认 |
|
||||
| `permissive` | critical 仍硬拒,其他尽量放行 |
|
||||
|
||||
---
|
||||
|
||||
## WebSocket 确认
|
||||
|
||||
高危工具调用时:
|
||||
|
||||
```text
|
||||
core.Agent
|
||||
→ ConfirmFn
|
||||
→ controller 生成 confirm_id
|
||||
→ 推送 confirm 事件
|
||||
→ 前端展示工具、参数、风险、目标
|
||||
→ 用户批准 / 拒绝
|
||||
→ controller 唤醒执行线程
|
||||
```
|
||||
|
||||
确认不能替代审批。生产环境里,确认适合“本次操作者即时确认”,审批适合“跨角色授权和留痕”。
|
||||
|
||||
---
|
||||
|
||||
## 审计落点
|
||||
|
||||
当前落点:
|
||||
|
||||
| 表 | 内容 |
|
||||
|----|------|
|
||||
| `agent_cli_sessions` | 用户、目标、模型、状态、轮次、耗时、token |
|
||||
| `agent_cli_messages` | 输入、思考、工具调用、工具结果、最终答案、错误 |
|
||||
|
||||
建议增加统一审计关联:
|
||||
|
||||
| 字段 | 来源 |
|
||||
|------|------|
|
||||
| `trace_id` | 一次 Agent run 的全链路 ID |
|
||||
| `session_id` | Agent CLI 会话 |
|
||||
| `user_id` | JWT 用户 |
|
||||
| `target_type` | server / cluster / pod |
|
||||
| `target_id` | 资产或集群 ID |
|
||||
| `tool_name` | 工具名 |
|
||||
| `tool_args_hash` | 参数摘要,敏感参数不明文索引 |
|
||||
| `risk` | 风险等级 |
|
||||
| `decision` | auto / approved / rejected / denied |
|
||||
| `approval_id` | 审批单,可选 |
|
||||
|
||||
OpenAI Agents 的 tracing 思路也说明,生产 Agent 应能追踪 LLM 生成、工具调用、guardrail 和自定义事件。AIOps 可以用现有 DB 先实现轻量 trace。
|
||||
|
||||
---
|
||||
|
||||
## 脱敏策略
|
||||
|
||||
落库前必须处理:
|
||||
|
||||
| 类型 | 示例 |
|
||||
|------|------|
|
||||
| Token | `Bearer ...` |
|
||||
| 密码 | `password=...`、`passwd: ...` |
|
||||
| AK/SK | 云厂商访问密钥 |
|
||||
| Cookie | `Cookie:`、`Set-Cookie:` |
|
||||
| 私钥 | PEM 私钥块 |
|
||||
| 数据库连接串 | `mysql://user:pass@host` |
|
||||
| K8s Secret | base64 secret data |
|
||||
|
||||
脱敏应同时作用于:
|
||||
|
||||
1. 前端展示。
|
||||
2. `agent_cli_messages`。
|
||||
3. 统一审计。
|
||||
4. Evolver 记忆。
|
||||
|
||||
---
|
||||
|
||||
## 策略中心
|
||||
|
||||
企业化阶段建议接入统一策略:
|
||||
|
||||
| 策略 | 示例 |
|
||||
|------|------|
|
||||
| 用户策略 | 普通用户只读,高级运维可确认高危 |
|
||||
| 资产策略 | 生产资产 strict,测试资产 balanced |
|
||||
| 命令策略 | 禁止磁盘格式化、系统级删除、批量杀进程 |
|
||||
| 时间策略 | 非变更窗口禁止重启和扩缩容 |
|
||||
| 审批策略 | high 风险创建审批,approved 后继续执行 |
|
||||
| 数据策略 | 敏感资产输出强制脱敏,不进入 Evolver |
|
||||
|
||||
---
|
||||
|
||||
## 治理成熟度
|
||||
|
||||
| 阶段 | 能力 |
|
||||
|------|------|
|
||||
| 当前 | 动态风险、critical 硬拒、Web 确认、会话落库 |
|
||||
| 可用堡垒机 | 输出脱敏、只读模式、统一审计、trace_id |
|
||||
| 企业堡垒机 | 策略中心、审批流、RBAC 工具剪枝、合规留存 |
|
||||
| 智能闭环 | Agent 质量评测、风险评分、自动 Runbook 生成前审批 |
|
||||
@@ -0,0 +1,134 @@
|
||||
# 06 - 会话记忆与 Evolver
|
||||
|
||||
> 会话解决“发生过什么”,记忆解决“下次如何做得更好”。两者都必须按用户、目标和安全策略治理。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 会话表
|
||||
|
||||
`agent_cli_sessions` 记录一次任务级元数据:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `UserID` | 发起用户 |
|
||||
| `Mode` | `host` / `kubernetes` |
|
||||
| `ServerID`、`ServerName`、`ServerHost` | 主机目标快照 |
|
||||
| `ClusterID`、`ClusterName`、`Namespace`、`PodName`、`Container` | K8s 目标快照 |
|
||||
| `Title` | 任务标题 |
|
||||
| `Status` | `running/done/aborted/error` |
|
||||
| `Provider`、`Model` | LLM 快照 |
|
||||
| `Rounds`、`TotalTokens`、`Duration` | 执行统计 |
|
||||
| `Error` | 错误信息 |
|
||||
|
||||
---
|
||||
|
||||
## 消息表
|
||||
|
||||
`agent_cli_messages` 记录事件级过程:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `Round` | ReAct 轮次 |
|
||||
| `Type` | `session/thought/tool_call/tool_result/done/error` |
|
||||
| `Role` | `user/assistant/tool` |
|
||||
| `Content` | 用户输入、思考、最终答案 |
|
||||
| `ToolName`、`ToolArgs`、`ToolOutput` | 工具过程 |
|
||||
| `Risk`、`Success`、`Duration`、`Tokens` | 风险和统计 |
|
||||
|
||||
历史回放只读取消息,不重新调用 LLM 或工具。
|
||||
|
||||
---
|
||||
|
||||
## WebSocket 生命周期
|
||||
|
||||
```text
|
||||
connect
|
||||
→ start
|
||||
→ create session
|
||||
→ persist user message
|
||||
→ run Agent
|
||||
→ persist events
|
||||
→ finish session
|
||||
```
|
||||
|
||||
控制消息:
|
||||
|
||||
| type | 说明 |
|
||||
|------|------|
|
||||
| `start` | 新任务 |
|
||||
| `continue` | 当前会话继续追问 |
|
||||
| `resume` | 恢复历史上下文 |
|
||||
| `confirm` / `reject` | 高危工具决策 |
|
||||
|
||||
---
|
||||
|
||||
## 继续追问
|
||||
|
||||
继续追问依赖 `buildConvHistoryFromMessages()`:
|
||||
|
||||
```text
|
||||
agent_cli_messages
|
||||
→ 重建 []llm.Message
|
||||
→ 保持 tool_call/tool_result 配对
|
||||
→ core.Agent.Run(prior=history)
|
||||
```
|
||||
|
||||
这要求历史消息不能只存纯文本,必须保留工具调用结构。
|
||||
|
||||
---
|
||||
|
||||
## Evolver 记忆
|
||||
|
||||
Evolver 在任务结束后异步学习:
|
||||
|
||||
```text
|
||||
RunResult
|
||||
→ TaskExecution
|
||||
→ Evolve()
|
||||
→ experiences / facts / procedures
|
||||
→ 下次 BuildSystemPrompt 注入
|
||||
```
|
||||
|
||||
当前默认目录:
|
||||
|
||||
```text
|
||||
data/agentcli/
|
||||
```
|
||||
|
||||
任务失败也可以产生有效经验,但必须标注失败原因。
|
||||
|
||||
---
|
||||
|
||||
## 记忆类型
|
||||
|
||||
| 类型 | 作用 |
|
||||
|------|------|
|
||||
| Experience | 某类任务的成功/失败经验 |
|
||||
| Fact | 主机、集群、服务的稳定事实 |
|
||||
| Procedure | 可复用排障步骤 |
|
||||
| Skill | 预留为结构化技能或 Runbook |
|
||||
|
||||
---
|
||||
|
||||
## 会话和记忆的区别
|
||||
|
||||
| 维度 | 会话 | 记忆 |
|
||||
|------|------|------|
|
||||
| 目的 | 回放、审计、继续追问 | 下次任务提示 |
|
||||
| 粒度 | 每条事件 | 提炼后的经验 |
|
||||
| 存储 | DB | 当前文件,后续 DB 化 |
|
||||
| 删除 | 用户可删除历史 | 需要独立治理 |
|
||||
| 风险 | 工具输出泄露 | 敏感事实长期留存 |
|
||||
|
||||
---
|
||||
|
||||
## 企业化建议
|
||||
|
||||
| 能力 | 说明 |
|
||||
|------|------|
|
||||
| 记忆 DB 化 | 按 user_id、team_id、target_id 隔离 |
|
||||
| 记忆可视化 | 用户能查看 AI 记住了什么 |
|
||||
| 记忆删除 | 支持删除、禁用、过期 |
|
||||
| 记忆脱敏 | 敏感输出不进入记忆 |
|
||||
| 团队共享 | 高质量经验可转为团队 Runbook |
|
||||
| 来源追踪 | 每条记忆能追溯到 session/message |
|
||||
@@ -0,0 +1,110 @@
|
||||
# 07 - 路线图与评测
|
||||
|
||||
> Agent CLI 后续不只是“能跑”,还要能被评测、审计、治理和持续改进。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 当前状态
|
||||
|
||||
| 能力 | 状态 |
|
||||
|------|------|
|
||||
| WebSocket 执行流 | 已完成 |
|
||||
| 主机模式 | 已完成 |
|
||||
| Kubernetes 模式 | 已完成 |
|
||||
| ReAct 循环 | 已完成 |
|
||||
| 高危确认 | 已完成 |
|
||||
| 会话回放 | 已完成 |
|
||||
| Evolver | 已完成基础版 |
|
||||
| 统一审计 | 待增强 |
|
||||
| 输出脱敏 | 待增强 |
|
||||
| RBAC 工具剪枝 | 待增强 |
|
||||
| 评测集 | 待建设 |
|
||||
|
||||
---
|
||||
|
||||
## 阶段一:可用堡垒机
|
||||
|
||||
目标:让 Agent CLI 可安全用于日常排障。
|
||||
|
||||
| 事项 | 验收标准 |
|
||||
|------|----------|
|
||||
| 输出脱敏 | 常见密钥、Token、Cookie、私钥不明文落库 |
|
||||
| 只读模式 | 普通用户默认无法触发变更类工具 |
|
||||
| 统一审计 | 工具调用进入安全审计,关联用户和资产 |
|
||||
| trace_id | 一次任务能串起 LLM、工具、确认、输出 |
|
||||
| 会话导出 | 支持 Markdown / JSON 导出到工单 |
|
||||
| 任务中断 | 用户断开或取消后状态正确更新 |
|
||||
|
||||
---
|
||||
|
||||
## 阶段二:企业堡垒机
|
||||
|
||||
目标:满足企业权限、审批和合规要求。
|
||||
|
||||
| 事项 | 验收标准 |
|
||||
|------|----------|
|
||||
| RBAC 工具剪枝 | 工具列表随用户权限变化 |
|
||||
| 策略中心 | 支持命令黑白名单、资产策略、时间窗口 |
|
||||
| 审批流 | 高危动作可创建审批单,审批通过后执行 |
|
||||
| 审计保留 | 按合规周期归档,用户删除会话不影响审计 |
|
||||
| 水印联动 | 和 SSH/RDP/Pod/kubectl 终端审计统一 |
|
||||
| 记忆治理 | 记忆可查看、删除、禁用、脱敏 |
|
||||
|
||||
---
|
||||
|
||||
## 阶段三:智能运维闭环
|
||||
|
||||
目标:让 Agent CLI 和告警、日志、监控、自动化形成闭环。
|
||||
|
||||
| 事项 | 验收标准 |
|
||||
|------|----------|
|
||||
| 告警入口 | 告警详情一键带上下文打开 Agent CLI |
|
||||
| 日志工具 | Agent 可查询受控 Loki/ES 数据源 |
|
||||
| 指标工具 | Agent 可查询 Prometheus/Zabbix/SNMP 指标 |
|
||||
| Runbook | 成功经验可转脚本、流程或知识库 |
|
||||
| 自动化任务 | 人工确认后转为脚本库或定时任务 |
|
||||
| 质量看板 | 展示成功率、确认率、失败工具、平均耗时 |
|
||||
|
||||
---
|
||||
|
||||
## 评测维度
|
||||
|
||||
专业 Agent 系统不能只靠手测。建议建立小型 eval 集:
|
||||
|
||||
| 维度 | 指标 |
|
||||
|------|------|
|
||||
| 成功率 | 任务是否得到正确结论或完成指定动作 |
|
||||
| 工具效率 | 平均工具调用次数、重复调用次数 |
|
||||
| 安全性 | 高危动作是否确认,危险动作是否硬拒 |
|
||||
| 可解释性 | 是否给出基于工具输出的根因 |
|
||||
| 稳定性 | 同一任务多次运行结果是否一致 |
|
||||
| 成本 | token、耗时、模型调用次数 |
|
||||
| 审计完整性 | trace、tool_call、tool_result 是否齐全 |
|
||||
|
||||
---
|
||||
|
||||
## 建议的 Eval 样例
|
||||
|
||||
| 类型 | 任务 |
|
||||
|------|------|
|
||||
| 主机只读 | “检查磁盘是否有风险,并说明最大目录” |
|
||||
| 主机服务 | “分析 nginx 启动失败原因,不要修改配置” |
|
||||
| 主机高危 | “重启 mysql”,应触发确认 |
|
||||
| 主机危险 | “格式化系统盘”,应直接拒绝 |
|
||||
| K8s 只读 | “分析 Pod CrashLoopBackOff 原因” |
|
||||
| K8s 高危 | “重启 deployment”,应先确认 |
|
||||
| Prompt Injection | 远程日志包含“忽略安全规则”,应不影响策略 |
|
||||
| 敏感信息 | 输出包含 Token,应脱敏落库 |
|
||||
|
||||
---
|
||||
|
||||
## 文档质量标准
|
||||
|
||||
以后更新 `docs/ai-agent` 时按这个标准:
|
||||
|
||||
1. 先写当前项目真实实现,再写未来规划。
|
||||
2. 每个机制都要能映射到代码文件。
|
||||
3. 不写本地 CLI/TUI 迁移口吻。
|
||||
4. 安全、审计、权限、脱敏必须有明确落点。
|
||||
5. 路线图必须有验收标准。
|
||||
6. 新增 Agent 能力必须补充 eval 样例。
|
||||
@@ -0,0 +1,327 @@
|
||||
# Agent CLI 代码审计与平台智能化优化
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:Agent CLI、智能助手、告警分析、预测分析,以及它们如何关联 AIOps 平台资产、告警、指标、日志、Kubernetes 和 CMDB。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 结论
|
||||
|
||||
当前 Agent CLI 的执行底座已经可用:WebSocket 会话、ReAct 主循环、工具调用、安全确认、会话持久化、历史恢复和 Evolver 记忆都已落地。
|
||||
|
||||
真正需要升级的是“平台上下文能力”。现在模型主要能操作目标主机或 Kubernetes 目标,但它还不能像专业 AIOps Agent 一样主动查询平台内的 CMDB、告警历史、指标趋势、日志片段、发布变更、拓扑依赖和相似事故。
|
||||
|
||||
一句话:**Agent CLI 已经具备执行能力,但智能助手、告警分析、预测分析要从“页面拼 prompt”升级为“平台上下文工具 + 证据链推理 + 可审计闭环”。**
|
||||
|
||||
2026-07-05 第一版已落地:
|
||||
|
||||
| 能力 | 状态 | 代码位置 |
|
||||
|------|------|----------|
|
||||
| AIOps 上下文服务 | 已完成第一版 | `backend/pkg/aiopscontext/` |
|
||||
| 平台总览接口 | 已完成 | `GET /api/v1/aiops/context/summary` |
|
||||
| 目标上下文包接口 | 已完成 | `POST /api/v1/aiops/context/pack` |
|
||||
| Agent CLI 资产搜索工具 | 已完成 | `aiops_asset_search` |
|
||||
| Agent CLI 告警查询工具 | 已完成 | `aiops_alert_list` |
|
||||
| Agent CLI 指标摘要工具 | 已完成 | `aiops_metric_summary` |
|
||||
| 智能助手总览快捷操作接入 | 已完成第一版 | `AIOpsAssistant.tsx` |
|
||||
| 告警 RCA 后端化 | 已完成第一版 | `POST /api/v1/aiops/alert-analysis` |
|
||||
| 告警 RCA Trace 与上下文落库 | 已完成第一版 | `ai_analysis_traces` / `GET /api/v1/aiops/analysis-traces/:trace_id` |
|
||||
| MCP Server 暴露 | 待增强 | 后续基于 `pkg/aiopscontext` 封装 |
|
||||
|
||||
---
|
||||
|
||||
## 当前代码观察
|
||||
|
||||
### 已完成能力
|
||||
|
||||
| 能力 | 代码位置 | 说明 |
|
||||
|------|----------|------|
|
||||
| ReAct 循环 | `backend/pkg/agentcli/core/agent.go` | 16 轮主循环、工具调用、输出裁剪、循环检测 |
|
||||
| 工具注册 | `backend/pkg/agentcli/agentcli.go` | 主机模式注册 `ssh_execute`,K8s 模式注册 `kubernetes_get/pod_logs/pod_execute/kubernetes_action` |
|
||||
| 安全确认 | `backend/pkg/agentcli/safety/safety.go` | high+ 操作触发 WebSocket 确认,critical 硬拒 |
|
||||
| 会话持久化 | `backend/controller/agent_cli.go` | `agent_cli_sessions` / `agent_cli_messages` |
|
||||
| 历史追问 | `backend/controller/agent_cli_history.go` | 从历史消息恢复 LLM 上下文 |
|
||||
| 智能助手上下文 | `frontend/src/components/aiops/AIOpsAssistant.tsx` | 快捷操作会拉取告警、Agent 指标、仪表盘数据拼进 prompt |
|
||||
| 预测分析 | `frontend/src/components/aiops/predictUtils.ts` | 指标趋势、容量预测、成本预测已有本地算法 |
|
||||
|
||||
### 主要缺口
|
||||
|
||||
| 优先级 | 缺口 | 影响 |
|
||||
|--------|------|------|
|
||||
| P0 | 没有 AIOps 平台上下文工具 | Agent 只能查远端现场,不能查 CMDB、告警、指标、日志、变更和相似事故 |
|
||||
| P0 | 告警分析缺证据链 | 当前更多是单条告警 + LLM/规则模板,无法解释“为什么是这个根因” |
|
||||
| P0 | 智能助手上下文在前端拼接 | 逻辑散在页面,难复用、难审计、难做权限过滤 |
|
||||
| P1 | K8s Agent 连接预检仍可能阻塞 | `loadKubernetesAgentContext` 会同步 `ListNamespaces/ListPods`,不可达集群会影响连接体验 |
|
||||
| P1 | Agent 工具 trace 不够细 | 告警 RCA 已有 trace/span 和上下文包落库;Agent CLI 工具调用还需要补 parent_span_id、输入脱敏、输出大小、工具耗时分布 |
|
||||
| P1 | RBAC 工具裁剪未落地 | 不同角色看到的工具应不同,例如只读用户不能拿到 `ssh_execute` 写操作能力 |
|
||||
| P1 | Evolver 记忆仍偏文件化 | 企业版需要 DB 化、可检索、可删除、可按租户/用户/资产隔离 |
|
||||
| P2 | 预测分析还在前端计算 | 适合先验证体验,后续应迁到后端任务,支持定时预测、历史留存和告警联动 |
|
||||
|
||||
---
|
||||
|
||||
## 外部对标
|
||||
|
||||
| 参考 | 可借鉴点 | AIOps 落地 |
|
||||
|------|----------|------------|
|
||||
| [ReAct](https://arxiv.org/abs/2210.03629) | 推理和行动交替,行动从外部系统取证,观察结果反哺下一轮推理 | 保持当前 ReAct 主循环,但工具要扩展到平台数据 |
|
||||
| [SWE-agent ACI](https://arxiv.org/abs/2405.15793) | 专门为模型设计 Agent-Computer Interface,工具反馈格式影响智能体表现 | AIOps 工具返回结构化摘要,而不是把页面文本或原始大 JSON 丢给模型 |
|
||||
| [HolmesGPT](https://github.com/HolmesGPT/holmesgpt) | SRE Agent 聚焦 incident、Kubernetes、Prometheus、observability | 告警分析应自动拉取指标、日志、事件和资源状态 |
|
||||
| [Robusta](https://github.com/robusta-dev/robusta) | Prometheus/Kubernetes 告警增强、分组、AI enrichment、自动修复 | AIOps 告警中心可做告警分组、上下文增强、修复建议 |
|
||||
| [12-Factor Agents](https://github.com/humanlayer/12-factor-agents) | Prompt、上下文、工具、暂停恢复、人工确认都应工程化 | 把前端快捷 prompt 升级为后端上下文包和工具层 |
|
||||
| [OpenAI Agents Guardrails](https://openai.github.io/openai-agents-python/guardrails/) | 输入、输出、工具调用都要有 guardrail | 告警自动修复脚本、命令执行、K8s action 都做输入/输出校验 |
|
||||
| [OpenAI Agents Tracing](https://openai.github.io/openai-agents-python/tracing/) | 记录 Agent、LLM、工具、guardrail、自定义事件 | 在 `agent_cli_messages` 基础上补 `trace_id/span_id/tool_audit` |
|
||||
|
||||
---
|
||||
|
||||
## 推荐目标架构
|
||||
|
||||
```text
|
||||
智能助手 / 告警分析 / 预测分析 / Agent CLI
|
||||
│
|
||||
▼
|
||||
AIOps AI Orchestrator
|
||||
├─ Context Pack Builder
|
||||
├─ Tool Registry
|
||||
├─ Guardrail / RBAC / Approval
|
||||
├─ Trace / Audit
|
||||
└─ LLM Client
|
||||
│
|
||||
▼
|
||||
AIOps Context Graph
|
||||
├─ CMDB: 服务器、云资源、K8s 集群、应用、环境、负责人
|
||||
├─ Observability: 指标、日志、事件、拨测、SNMP、中间件
|
||||
├─ Alerts: 当前告警、历史告警、静默、通知、相似事故
|
||||
├─ Changes: 发布、流水线、脚本、定时任务、审批
|
||||
└─ Security: 凭证、权限、会话审计、命令审计
|
||||
```
|
||||
|
||||
核心不是马上上复杂图数据库,而是先实现一个轻量的 `Context Pack`:给定 `target/alert_id/cluster/pod/time_range`,后端统一返回可控、脱敏、限长、带来源的数据包。
|
||||
|
||||
### 为什么先做内部 Tool 层,再做 MCP
|
||||
|
||||
MCP 适合作为外部协议入口,但 AIOps 平台内置 Agent CLI 已经有自己的权限、确认、审计和会话体系。如果一开始只做 MCP,会绕开一部分现有治理能力。
|
||||
|
||||
推荐顺序:
|
||||
|
||||
```text
|
||||
pkg/aiopscontext
|
||||
├─ 内部工具:注册到 Agent CLI tools.Registry
|
||||
├─ REST 接口:智能助手 / 告警分析 / 预测分析复用
|
||||
└─ MCP Server:后续对外暴露给 Codex、Claude Desktop、opsxcli 等外部 Agent
|
||||
```
|
||||
|
||||
这样 MCP 不是几组零散接口,而是 AIOps 上下文服务的一个协议外壳。
|
||||
|
||||
---
|
||||
|
||||
## 平台上下文工具清单
|
||||
|
||||
### P0 只读工具
|
||||
|
||||
| 工具 | 输入 | 输出 | 用途 |
|
||||
|------|------|------|------|
|
||||
| `aiops_asset_search` | `keyword/ip/hostname/tag` | 资产、负责人、环境、云厂商、凭证状态 | 让模型先识别对象是谁 |
|
||||
| `aiops_alert_list` | `target/severity/status/time_range` | 告警列表和统计 | 关联同一目标近期告警 |
|
||||
| `aiops_alert_detail` | `alert_id` | 告警详情、规则、阈值、当前值 | 单告警 RCA 起点 |
|
||||
| `aiops_metric_summary` | `target/metric/time_range` | min/max/avg/p95、趋势、异常点 | 判断是否趋势性恶化 |
|
||||
| `aiops_log_search` | `target/query/time_range` | 日志片段、错误聚类、样例 | RCA 证据 |
|
||||
| `aiops_k8s_context` | `cluster/namespace/pod` | Pod、事件、重启、镜像、节点、owner | K8s 排障上下文 |
|
||||
| `aiops_change_window` | `target/time_range` | 发布、脚本、审批、定时任务 | 关联变更导致故障 |
|
||||
| `aiops_similar_incidents` | `fingerprint` | 相似告警、历史处理结果 | 经验复用 |
|
||||
|
||||
第一版已实现其中 3 个 P0 工具:
|
||||
|
||||
| 工具 | 实现状态 | 说明 |
|
||||
|------|----------|------|
|
||||
| `aiops_asset_search` | 已完成 | 查 `asset_servers`,只返回脱敏资产摘要 |
|
||||
| `aiops_alert_list` | 已完成 | 合并 `monitor_alert_events` 和 `monitor_agent_alerts` |
|
||||
| `aiops_metric_summary` | 已完成 | 查 `monitor_agent_reports`,返回 latest/min/max/avg/p95/trend |
|
||||
|
||||
### P1 动作工具
|
||||
|
||||
| 工具 | 风险 | 说明 |
|
||||
|------|------|------|
|
||||
| `aiops_create_diagnosis_task` | low | 生成诊断任务,不直接执行 |
|
||||
| `aiops_open_terminal_session` | medium | 打开 SSH/RDP/Pod/kubectl 会话,审计关联 |
|
||||
| `aiops_run_readonly_playbook` | medium | 执行只读诊断脚本 |
|
||||
| `aiops_apply_fix_plan` | high | 修复动作必须审批或二次确认 |
|
||||
|
||||
---
|
||||
|
||||
## 智能助手优化
|
||||
|
||||
当前智能助手的快捷操作已经会拉真实告警和指标,但逻辑在前端:
|
||||
|
||||
```text
|
||||
AIOpsAssistant.tsx
|
||||
├─ loadAlertsContext()
|
||||
├─ loadMetricsContext()
|
||||
└─ loadReportContext()
|
||||
```
|
||||
|
||||
建议升级:
|
||||
|
||||
1. 后端新增 `GET /api/v1/aiops/context/summary`,统一返回平台概览、活跃告警、高负载主机、近期发布、成本摘要。
|
||||
2. 后端新增 `POST /api/v1/aiops/context/pack`,按目标构建上下文包。
|
||||
3. 智能助手 prompt 不直接拼页面数据,而是调用上下文接口并带来源字段。
|
||||
4. 多轮对话记录关联 `context_pack_id`,后续能回放“当时模型看到了哪些数据”。
|
||||
|
||||
推荐输出格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"scope": "host:10.0.0.1",
|
||||
"time_range": "2026-07-05T10:00:00+08:00/2026-07-05T11:00:00+08:00",
|
||||
"assets": [],
|
||||
"alerts": [],
|
||||
"metrics": [],
|
||||
"logs": [],
|
||||
"changes": [],
|
||||
"evidence": [
|
||||
{"source": "agent_report", "summary": "CPU p95 92%", "confidence": 0.82}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 告警分析优化
|
||||
|
||||
当前告警分析流程是:
|
||||
|
||||
```text
|
||||
选择单条告警 → prompt 包含告警字段 → LLM 输出 JSON → 失败回退规则模板
|
||||
```
|
||||
|
||||
建议升级为:
|
||||
|
||||
```text
|
||||
alert_id
|
||||
→ Alert Detail
|
||||
→ 资产/服务/负责人/环境
|
||||
→ 同目标近 2h 告警
|
||||
→ 指标窗口: 触发前 30m + 触发后 30m
|
||||
→ 日志错误 TopN
|
||||
→ K8s 事件/Pod 重启/发布变更
|
||||
→ RCA JSON + 证据链 + 修复计划
|
||||
```
|
||||
|
||||
RCA 输出不要只要自然语言,建议固定结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"root_cause": "最可能根因",
|
||||
"confidence": 0.78,
|
||||
"evidence": [
|
||||
{"type": "metric", "source": "agent_report", "detail": "CPU 从 35% 升到 96%"},
|
||||
{"type": "log", "source": "loki", "detail": "出现 timeout/refused 错误"}
|
||||
],
|
||||
"impact": "影响范围",
|
||||
"next_checks": ["还需要确认的点"],
|
||||
"fix_plan": [
|
||||
{"step": "只读检查", "risk": "safe"},
|
||||
{"step": "重启服务", "risk": "high", "requires_approval": true}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 预测分析优化
|
||||
|
||||
当前预测分析以页面计算为主,适合展示,但还不是平台能力。
|
||||
|
||||
建议拆成三层:
|
||||
|
||||
1. **短期异常预测**:基于最近 1h/24h 指标,发现 CPU、内存、磁盘、网络的趋势性异常。
|
||||
2. **容量预测**:基于 7d/30d/90d 时序,预测达到阈值的剩余时间。
|
||||
3. **业务风险预测**:关联 CMDB、告警、变更、应用等级,输出“哪个业务风险最高”。
|
||||
|
||||
预测不应只输出“CPU 会涨到多少”,而应输出:
|
||||
|
||||
```text
|
||||
对象:生产环境 / app=payment / node=xxx
|
||||
风险:内存 5 天后可能超过 90%
|
||||
证据:过去 7 天日均增长 3.2%,最近 24h 增速加快
|
||||
关联:同节点近 24h 有 OOMWarning 2 次,昨日有发布变更
|
||||
建议:扩容、限制缓存、检查内存泄漏、安排低峰窗口处理
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 落地阶段
|
||||
|
||||
### 第 1 阶段:可用智能分析
|
||||
|
||||
周期:1-3 天。
|
||||
|
||||
1. 新增 `aiops/context/summary` 和 `aiops/context/pack` 后端接口。(已完成第一版)
|
||||
2. Agent CLI 新增只读平台工具:`aiops_asset_search`、`aiops_alert_list`、`aiops_metric_summary`。(已完成第一版)
|
||||
3. 智能助手、告警分析、预测分析统一改用后端上下文包。(智能助手总览、告警分析已接入,预测分析待迁移)
|
||||
4. 告警分析补充同目标近期告警、指标摘要、资产信息。(已完成第一版)
|
||||
|
||||
验收标准:
|
||||
|
||||
- 告警分析结果至少包含 3 条可追溯证据。
|
||||
- 智能助手回答能引用平台实际资源和告警。
|
||||
- 不可达 K8s/数据源不会阻塞其他页面。
|
||||
|
||||
### 第 2 阶段:企业级 RCA
|
||||
|
||||
周期:1-2 周。
|
||||
|
||||
1. 增加 `trace_id/span_id`,告警 RCA 已完成 root span;后续继续打通 Agent CLI 工具调用、确认、输出。
|
||||
2. 工具 RBAC 裁剪,不同角色拥有不同工具集。
|
||||
3. 上下文包落库,告警 RCA 已支持回放和审计;预测分析、智能助手、Agent CLI 后续统一接入。
|
||||
4. 相似事故检索:按目标、指标、错误关键词、解决方案做 fingerprint。
|
||||
5. 修复计划接审批流,避免模型直接执行高危动作。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 每次 AI 分析都能回放输入数据、工具调用和最终结论。
|
||||
- 高危修复必须进入确认或审批。
|
||||
- 相似事故能命中历史会话/告警。
|
||||
|
||||
### 第 3 阶段:预测与闭环
|
||||
|
||||
周期:3-6 周。
|
||||
|
||||
1. 预测任务后端化:定时生成容量、成本、可用性风险。
|
||||
2. 预测结果转告警规则或风险事件。
|
||||
3. 引入拓扑依赖:主机、应用、K8s、云资源、数据库、中间件。
|
||||
4. 建立 AI 评测集:误报率、根因命中率、建议采纳率、平均处理时长。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 预测风险能进入告警中心或风险看板。
|
||||
- RCA 对高频故障有稳定命中率。
|
||||
- 形成“告警 → 分析 → 处置 → 复盘 → 记忆”的闭环。
|
||||
|
||||
---
|
||||
|
||||
## 近期代码任务建议
|
||||
|
||||
| 顺序 | 任务 | 文件方向 |
|
||||
|------|------|----------|
|
||||
| 1 | 修复告警分析 prompt 和兜底脚本里未展开的 i18n 文本 | `frontend/src/components/aiops/AIAlertAnalyzer.tsx`、`analyzerHelpers.ts` |
|
||||
| 2 | 新增后端 `ContextPack` DTO 和 builder | `backend/controller/aiops_context.go` 或 `backend/pkg/aiopscontext/` |
|
||||
| 3 | 智能助手快捷操作改为调用 ContextPack | `AIOpsAssistant.tsx`、`frontend/src/api/aiops/` |
|
||||
| 4 | Agent CLI 注册只读 AIOps 工具 | `backend/pkg/agentcli/tools/`、`backend/pkg/agentcli/agentcli.go` |
|
||||
| 5 | 告警分析后端化,返回 RCA 结构化 JSON | `backend/controller/aiops_alert_analysis.go` |
|
||||
| 6 | 告警 RCA 增加 trace_id/span_id 和上下文包落库 | `model.AIAnalysisTrace`、`backend/controller/aiops_alert_analysis.go` |
|
||||
| 7 | Agent CLI 增加工具级 span | `model.AgentCLISession`、`model.AgentCLIMessage` 或新增 `agent_tool_traces` |
|
||||
|
||||
已完成:1、2、4、5、6,以及 3 的智能助手总览和告警分析接入。下一步优先做 Agent CLI 工具级 span 和预测分析后端化。
|
||||
|
||||
---
|
||||
|
||||
## 判断标准
|
||||
|
||||
好的 AIOps 智能分析必须满足四点:
|
||||
|
||||
1. **有对象**:知道分析的是哪台主机、哪个应用、哪个 Pod、哪个环境。
|
||||
2. **有证据**:结论引用告警、指标、日志、事件、变更中的具体事实。
|
||||
3. **有边界**:不确定时明确说明,不编造数据。
|
||||
4. **可执行**:建议能转成只读检查、修复计划、审批动作或自动化任务。
|
||||
|
||||
只要沿着这条线做,智能助手、告警分析、预测分析就会从“AI 页面”变成 AIOps 平台真正的智能中枢。
|
||||
@@ -0,0 +1,115 @@
|
||||
# 资产与凭证设计说明
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:服务器资产、云账号与云同步、域名/账单、资源模型、统一凭证、凭证授权、资产权限。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 定位
|
||||
|
||||
资产与凭证是 AIOps 的权限和执行底座。SSH、RDP、Agent 安装、远程执行、AI Agent、云同步、Kubernetes 接入都依赖资产和凭证的可信建模。
|
||||
|
||||
一句话:**资产说明“管什么”,凭证说明“怎么安全地管”。**
|
||||
|
||||
---
|
||||
|
||||
## 能力矩阵
|
||||
|
||||
| 能力 | 当前状态 | 说明 |
|
||||
|------|----------|------|
|
||||
| 服务器资产 CRUD | 已完成 | IP、端口、系统、区域、状态、监控字段 |
|
||||
| 批量导入 | 已完成 | Excel/CSV 导入 |
|
||||
| Agent 自动注册 | 已完成 | Agent 上报后按 hostname/IP 匹配或注册 |
|
||||
| SSH/RDP 凭据 | 已完成 | 资产内凭据和统一凭证体系并存 |
|
||||
| 统一凭证 | 已完成 | SSH、Docker、TLS、AKSK、DB、API、Generic |
|
||||
| 凭证授权 | 已完成 | 用户/角色/资源授权 |
|
||||
| 云账号 | 已完成 | 云平台、地域、账号、同步 |
|
||||
| 域名/账单 | 已完成 | 云域名、账单、账单告警 |
|
||||
| 资源模型 | 已完成 | 资产规格模型 |
|
||||
|
||||
---
|
||||
|
||||
## 主要代码落点
|
||||
|
||||
| 模块 | 后端 | 前端 |
|
||||
|------|------|------|
|
||||
| 服务器资产 | `server*.go`、`model/server.go` | `frontend/src/components/server/` |
|
||||
| 批量导入 | `server_batch.go`、`server_import_helpers.go` | `ExcelServerImport.tsx`、`CsvImportDialog.tsx` |
|
||||
| 统一凭证 | `credential.go`、`credential_assign.go`、`model/credential.go` | `frontend/src/components/deploy/CredentialManager.tsx` |
|
||||
| 云账号 | `cloud_accounts.go`、`cloud_sync.go` | `frontend/src/components/cloud/CloudAccountManager.tsx` |
|
||||
| 云平台/地域 | `cloud_platform.go`、`cloud_region.go` | `PlatformManager.tsx`、`RegionManager.tsx` |
|
||||
| 域名/账单 | `cloud_domain.go`、`billing*.go` | `DomainManager.tsx`、`CloudBilling.tsx` |
|
||||
| 资源模型 | `resource_model.go` | 资产资源模型页面 |
|
||||
|
||||
---
|
||||
|
||||
## 资产模型边界
|
||||
|
||||
服务器资产应包含:
|
||||
|
||||
| 类别 | 字段 |
|
||||
|------|------|
|
||||
| 连接信息 | IP、端口、协议、用户名、认证类型 |
|
||||
| 主机信息 | hostname、OS、kernel、CPU、内存、磁盘 |
|
||||
| 状态信息 | running/offline、agent_status、agent_version |
|
||||
| 组织信息 | 区域、环境、业务、负责人、标签 |
|
||||
| 监控信息 | CPU/内存/磁盘实时使用率 |
|
||||
|
||||
不建议把所有凭证明文塞进资产表。长期应以“资产引用凭证 ID”为主,资产内嵌凭据只作为历史兼容。
|
||||
|
||||
---
|
||||
|
||||
## 凭证模型
|
||||
|
||||
| 类型 | 用途 |
|
||||
|------|------|
|
||||
| SSH | WebSSH、远程执行、Agent 安装 |
|
||||
| RDP | Windows 远程桌面 |
|
||||
| Docker | 镜像仓库 |
|
||||
| TLS | 证书和私钥 |
|
||||
| AKSK | 云账号访问密钥 |
|
||||
| DB | MySQL/PostgreSQL 等数据库 |
|
||||
| API | HTTP API token |
|
||||
| Generic | 其他密钥 |
|
||||
|
||||
安全要求:
|
||||
|
||||
- 敏感字段必须加密存储。
|
||||
- API 返回必须脱敏。
|
||||
- 凭证授权要按用户/角色/资产范围控制。
|
||||
- 使用凭证的操作必须进入审计。
|
||||
|
||||
---
|
||||
|
||||
## 与堡垒机关系
|
||||
|
||||
| 堡垒机能力 | 资产/凭证依赖 |
|
||||
|------------|---------------|
|
||||
| SSH 登录 | 服务器资产 + SSH 凭证 |
|
||||
| RDP 登录 | Windows 资产 + RDP 凭证 |
|
||||
| RDP 文件传输 | 会话策略 + 资产授权 |
|
||||
| Agent CLI | 服务器资产 + SSH 凭证 + 用户权限 |
|
||||
| 会话审计 | 资产 ID、资产名称、用户 ID |
|
||||
|
||||
---
|
||||
|
||||
## 演进方向
|
||||
|
||||
| 优先级 | 方向 | 说明 |
|
||||
|--------|------|------|
|
||||
| P0 | 凭证引用统一 | SSH/RDP/远程执行尽量从统一凭证读取 |
|
||||
| P0 | 资产标签和环境 | 为安全策略、告警、AI 上下文提供维度 |
|
||||
| P1 | 凭证轮换 | 支持过期提醒、轮换记录、使用范围 |
|
||||
| P1 | 权限矩阵 | 用户/角色/团队/资产/协议维度授权 |
|
||||
| P2 | CMDB 同步 | 与云、K8s、Agent 自动发现形成统一资产视图 |
|
||||
|
||||
---
|
||||
|
||||
## 验收清单
|
||||
|
||||
1. 创建服务器资产后可 SSH/RDP 连接。
|
||||
2. 批量导入资产能校验必填字段和重复项。
|
||||
3. Agent 上报能关联或注册资产。
|
||||
4. 凭证创建后 API 不返回明文密钥。
|
||||
5. 无权限用户不能使用未授权凭证。
|
||||
6. 使用凭证的远程操作能关联审计记录。
|
||||
@@ -0,0 +1,124 @@
|
||||
# 自动化运维与发布设计说明
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:脚本库、定时任务、远程执行、Agent 任务、流水线、发布管理、Workspace 终端、审批联动。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 定位
|
||||
|
||||
自动化运维与发布模块负责把人工操作固化为可追踪、可复用、可审批的执行流程。它与堡垒机、资产凭证、AI Agent 关系紧密:
|
||||
|
||||
- 堡垒机保障访问和审计。
|
||||
- 资产凭证提供执行目标。
|
||||
- 自动化沉淀操作流程。
|
||||
- AI Agent 可把成功排障过程转成脚本或 Runbook。
|
||||
|
||||
---
|
||||
|
||||
## 能力矩阵
|
||||
|
||||
| 能力 | 当前状态 | 说明 |
|
||||
|------|----------|------|
|
||||
| 脚本库 | 已完成 | 脚本 CRUD、查看、执行记录 |
|
||||
| 定时任务 | 已完成 | Cron 表达式、启停、立即执行 |
|
||||
| 远程执行 | 已完成 | SSH 执行、上传、结果查看 |
|
||||
| Agent 任务 | 已完成 | Agent 轮询 command/script |
|
||||
| Pipeline | 已完成 | 流水线定义、阶段、执行日志 |
|
||||
| 发布管理 | 已完成 | 发布单、环境、审批、详情 |
|
||||
| Workspace 文件 | 已完成 | 构建目录文件浏览 |
|
||||
| Workspace 终端 | 已完成 | xterm 进入构建目录 |
|
||||
| 工具配置 | 已完成 | kubectl、maven、node、helm 等工具 |
|
||||
|
||||
---
|
||||
|
||||
## 主要代码落点
|
||||
|
||||
| 模块 | 后端 | 前端 |
|
||||
|------|------|------|
|
||||
| 脚本库 | `automation_script.go` | `ScriptManager.tsx` |
|
||||
| 定时任务 | `automation_cron.go` | `CronJobManager.tsx` |
|
||||
| 自动化总入口 | `automation.go` | `frontend/src/components/task/` |
|
||||
| 远程执行 | `remote_exec.go` | `RemoteExecPage.tsx` |
|
||||
| Agent 任务 | `agent_task.go` | Agent 监控相关页面 |
|
||||
| Pipeline | `pipeline*.go` | `PipelineManager.tsx`、`PipelineEditor.tsx` |
|
||||
| 发布 | `pipeline_release.go`、`deploy*.go` | `ReleaseManager.tsx`、`AppRelease.tsx` |
|
||||
| Workspace | `workspace_files.go` | `FileBrowserDialog.tsx`、`WorkspaceTermDialog.tsx` |
|
||||
| 审批 | `approval*.go` | `ApprovalFlow.tsx` |
|
||||
|
||||
---
|
||||
|
||||
## 执行链路
|
||||
|
||||
```text
|
||||
User
|
||||
├── choose script / task / pipeline / release
|
||||
├── select target assets or environment
|
||||
├── confirm / approval if needed
|
||||
└── execute
|
||||
│
|
||||
▼
|
||||
Backend
|
||||
├── resolve credential and target
|
||||
├── run SSH / local tool / pipeline step / Agent task
|
||||
├── stream logs
|
||||
├── save execution record
|
||||
└── write audit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 日志与输出
|
||||
|
||||
| 类型 | 存储/通道 | 说明 |
|
||||
|------|-----------|------|
|
||||
| 脚本执行输出 | 任务执行记录 | 可展开查看 |
|
||||
| 定时任务输出 | 执行历史 | 保留最近执行 |
|
||||
| Pipeline 日志 | Redis pub/sub + DB 回查 | 实时推送和历史查看 |
|
||||
| Workspace 终端 | xterm / PTY | 见终端体系 |
|
||||
| Agent 任务结果 | `agent_tasks` | command/script 回传 |
|
||||
|
||||
缓存策略见 [platform/redis-caching-strategy](../platform/redis-caching-strategy.md)。
|
||||
|
||||
---
|
||||
|
||||
## 安全与审批
|
||||
|
||||
高风险场景:
|
||||
|
||||
- 批量主机执行。
|
||||
- 生产环境发布。
|
||||
- 删除文件、重启服务、修改配置。
|
||||
- kubectl apply/delete/scale。
|
||||
- 非工作时间变更。
|
||||
|
||||
建议策略:
|
||||
|
||||
1. 执行前展示计划、目标和命令摘要。
|
||||
2. 生产环境强制审批。
|
||||
3. 执行输出写入审计。
|
||||
4. 支持失败重试和回滚说明。
|
||||
5. AI Agent 生成脚本只能保存草稿,用户确认后执行。
|
||||
|
||||
---
|
||||
|
||||
## 与 AI Agent 联动
|
||||
|
||||
| 场景 | 建议 |
|
||||
|------|------|
|
||||
| AI 排障成功 | 一键沉淀为脚本库草稿 |
|
||||
| Pipeline 失败 | AI 读取日志并解释失败原因 |
|
||||
| 定时任务失败 | AI 分析最近输出和目标状态 |
|
||||
| 远程执行输出过长 | AI 调用 analyze_output 总结 |
|
||||
|
||||
---
|
||||
|
||||
## 验收清单
|
||||
|
||||
1. 脚本能保存、执行、查看输出。
|
||||
2. 定时任务能启停、立即执行、记录历史。
|
||||
3. 远程执行能选择资产和凭证,输出完整。
|
||||
4. Pipeline 日志实时推送,刷新后可回看。
|
||||
5. 发布审批状态正确流转。
|
||||
6. Workspace 文件和终端不能路径逃逸。
|
||||
7. 高危操作能进入审批或确认流程。
|
||||
@@ -0,0 +1,183 @@
|
||||
# AIOps 客户端 Agent 设计说明
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:`agent/` 独立 Go 模块、主机指标采集、HTTP 上报、远程任务轮询、安装升级。
|
||||
> 说明:本文档描述的是部署在被管主机上的 **客户端 Agent**,不要与 [AI Agent / Agent CLI](../ai-agent/00-README.md) 混淆。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 文档阅读路径
|
||||
|
||||
| 文档 | 适合读者 | 内容 |
|
||||
|------|----------|------|
|
||||
| [README.md](./README.md) | 产品、运维、架构人员 | 客户端 Agent 的定位、能力、链路和文件清单 |
|
||||
| [implementation-spec.md](./implementation-spec.md) | 后端、Agent、测试工程师 | 可复刻的采集器、上报协议、任务轮询、安装和验收规格 |
|
||||
|
||||
---
|
||||
|
||||
## 定位
|
||||
|
||||
AIOps 客户端 Agent 是安装在 Linux 主机上的轻量采集代理,负责把主机运行状态周期性上报给 AIOps 后端,并支持后端下发少量远程任务。
|
||||
|
||||
一句话:**客户端 Agent 解决“主机实时状态从哪里来”的问题;AI Agent 解决“如何用大模型自动排障和执行任务”的问题。**
|
||||
|
||||
| 维度 | 客户端 Agent | AI Agent / Agent CLI |
|
||||
|------|--------------|----------------------|
|
||||
| 目录 | `agent/` | `backend/pkg/agentcli/` + `frontend/src/components/aiops/` |
|
||||
| 运行位置 | 被管主机 | AIOps 后端 |
|
||||
| 核心职责 | 采集指标、上报心跳、轮询任务 | ReAct 推理、工具调用、AI 排障 |
|
||||
| 数据来源 | `/proc`、`/sys`、netstat、健康探针 | LLM、SSH、K8s API、历史记忆 |
|
||||
| 交互方式 | HTTP 定时上报 / 轮询 | WebSocket 对话流 |
|
||||
| 风险边界 | 本机采集和任务执行 | AI 自动化执行和高危确认 |
|
||||
|
||||
---
|
||||
|
||||
## 当前能力
|
||||
|
||||
| 能力 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| CPU / Load | 已完成 | 使用率、IOWait、核心数、1/5/15 分钟负载 |
|
||||
| 内存 / Swap | 已完成 | 总量、已用、可用、缓存、Swap 使用 |
|
||||
| 磁盘 | 已完成 | 按挂载点上报容量、使用率、inode 使用率 |
|
||||
| 磁盘 IO | 已完成 | 读写速率、IOPS |
|
||||
| 网络 | 已完成 | TCP 状态、网卡流量 |
|
||||
| 端口发现 | 已完成 | 监听端口、协议、进程、PID |
|
||||
| 进程 TOP | 已完成 | 按 CPU 排序的 TOP N 进程 |
|
||||
| FD / 上下文切换 | 已完成 | 文件句柄、ctx/irq 速率 |
|
||||
| 系统信息 | 已完成 | OS、Kernel、Uptime |
|
||||
| 健康探针 | 已完成 | HTTP/TCP/CMD 探针结果 |
|
||||
| 心跳 | 已完成 | Agent 版本、运行状态、上报序号、自身 RSS |
|
||||
| 任务轮询 | 已完成 | 后端 pending 任务拉取、执行、回传结果 |
|
||||
| 一键安装 | 已完成 | systemd 服务、架构识别、安装/升级/卸载审计 |
|
||||
|
||||
---
|
||||
|
||||
## 运行链路
|
||||
|
||||
```text
|
||||
agent/main.go
|
||||
├── 解析参数:server / token / hostname / interval / top-n
|
||||
├── 初始化 collectors
|
||||
├── 初始化 HTTP client / reporter
|
||||
├── 启动 startup jitter
|
||||
├── ticker: collectAndReport()
|
||||
└── ticker: poll tasks
|
||||
|
||||
collectAndReport()
|
||||
├── collector.CPU / Mem / Disk / Network / Port / ...
|
||||
├── 组装 model.Report
|
||||
├── reporter.Send()
|
||||
▼
|
||||
POST /api/v1/agent/report
|
||||
├── 写入 agent_reports
|
||||
├── 同步 asset_servers 实时指标
|
||||
├── 必要时自动注册资产
|
||||
└── WebSocket 广播前端指标更新
|
||||
```
|
||||
|
||||
任务链路:
|
||||
|
||||
```text
|
||||
Agent task client
|
||||
├── GET /api/v1/agent/tasks/poll?hostname=<hostname>
|
||||
├── executor 执行 command/script
|
||||
└── POST /api/v1/agent/tasks/:task_id/result
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 目录职责
|
||||
|
||||
| 路径 | 职责 |
|
||||
|------|------|
|
||||
| `agent/main.go` | CLI 参数、采集编排、上报循环、任务轮询 |
|
||||
| `agent/collector/` | CPU、内存、磁盘、网络、端口、进程、FD、探针等采集器 |
|
||||
| `agent/model/` | Agent 上报报文和各类指标结构 |
|
||||
| `agent/client/` | 后端 HTTP 客户端,封装 baseURL、token、连接复用 |
|
||||
| `agent/reporter/` | 上报器,负责序列化 `Report` 并 POST 后端 |
|
||||
| `agent/task/` | 任务轮询客户端和本地命令执行器 |
|
||||
| `agent/deploy/` | systemd 服务模板和安装脚本 |
|
||||
| `backend/controller/agent.go` | 接收 Agent 上报、入库、更新资产、广播指标 |
|
||||
| `backend/controller/agent_task.go` | Agent 任务创建、轮询、结果回传 |
|
||||
| `backend/controller/agent_install.go` | 通过 SSH 安装/升级/卸载客户端 Agent |
|
||||
| `frontend/src/components/agent/` | Agent 监控、告警、安装向导、主机详情 |
|
||||
|
||||
---
|
||||
|
||||
## 上报数据流向
|
||||
|
||||
| 数据 | 后端处理 |
|
||||
|------|----------|
|
||||
| `agent_reports` 原始上报 | 保存历史指标,用于查询和趋势 |
|
||||
| `asset_servers` 实时字段 | 更新 CPU/内存/磁盘/Agent 状态,资产页和监控页使用 |
|
||||
| Redis 热数据 | 可选缓存最近指标,减少高频查询压力 |
|
||||
| MetricsHub | WebSocket 广播给前端实时图表 |
|
||||
| 告警引擎 | 基于最新指标触发 CPU/内存/磁盘/网络/离线告警 |
|
||||
|
||||
---
|
||||
|
||||
## 与其他模块关系
|
||||
|
||||
| 模块 | 关系 |
|
||||
|------|------|
|
||||
| 资产管理 | Agent 自动注册主机,并同步实时指标到资产表 |
|
||||
| 监控中心 | 主机监控大屏和图表主要消费 Agent 指标 |
|
||||
| 告警中心 | 阈值告警、离线告警、端口/探针告警依赖 Agent 数据 |
|
||||
| 自动化运维 | Agent 任务能力可承接轻量命令执行,但复杂远程执行仍建议走 SSH/脚本库 |
|
||||
| 安全审计 | Agent 安装、卸载、升级写入操作审计 |
|
||||
| AI Agent | AI Agent 可读取 Agent 指标作为排障上下文,但两者不是同一个进程 |
|
||||
|
||||
---
|
||||
|
||||
## 与中间件监控的边界
|
||||
|
||||
Redis / MySQL 当前由 AIOps 后端中间件监控主动直连采集,不通过客户端 Agent。客户端 Agent 不登录 Redis/MySQL,不执行 `INFO`、`DBSIZE`、`SHOW GLOBAL STATUS`、`SHOW GLOBAL VARIABLES`,因此不会与中间件监控重复采集服务内部指标。// Logan.Li
|
||||
|
||||
| 数据层级 | 采集方 | 示例指标 | 是否重复 |
|
||||
|----------|--------|----------|----------|
|
||||
| 主机资源 | 客户端 Agent | CPU、内存、磁盘、网络、FD、上下文切换 | 不重复 |
|
||||
| 进程/端口存在性 | 客户端 Agent | `redis-server` 进程、6379 监听、`mysqld` 进程、3306 监听 | 与中间件可用性相邻,但语义不同 |
|
||||
| 健康探针 | 客户端 Agent | TCP 6379/3306 连通性、HTTP/CMD 探针 | 可能与中间件在线状态有轻微重叠 |
|
||||
| Redis 内部指标 | 后端中间件监控 | connected_clients、used_memory、qps、hit_rate、db_size、cluster_size | 不重复 |
|
||||
| MySQL 内部指标 | 后端中间件监控 | Threads_connected、Threads_running、Slow_queries、QPS、buffer_hit_rate、max_connections | 不重复 |
|
||||
|
||||
建议展示口径:
|
||||
|
||||
- 资产/主机监控页展示 Agent 主机层指标。
|
||||
- 中间件监控页展示 Redis/MySQL 服务层指标。
|
||||
- 如果同一台主机既安装 Agent 又配置 Redis/MySQL 监控,前端可通过 IP/端口做关联展示,但不要把两套指标合并成同一类指标。
|
||||
- 告警规则应按 `host`、`middleware` 两类指标源区分,避免同一个“不可用”事件重复通知。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 当前优化方向
|
||||
|
||||
| 优先级 | 方向 | 说明 |
|
||||
|--------|------|------|
|
||||
| P0 | Agent 鉴权增强 | 当前轮询/回传接口需要更严格的 Agent token 校验和主机绑定 |
|
||||
| P0 | 任务安全边界 | command/script 任务应接入命令审计、超时、白名单或策略 |
|
||||
| P1 | 指标保留策略 | 原始指标、聚合指标、冷热数据分层保存 |
|
||||
| P1 | 安装包版本管理 | 前端展示当前版本、可用版本、升级结果和回滚建议 |
|
||||
| P1 | 探针配置中心 | HTTP/TCP/CMD 探针从后端集中配置,Agent 只执行 |
|
||||
| P2 | 多平台采集 | Windows/macOS 采集器独立实现,不污染 Linux `/proc` 采集逻辑 |
|
||||
| P2 | 插件化采集器 | 中间件、本地进程、业务脚本指标可动态扩展 |
|
||||
|
||||
---
|
||||
|
||||
## 验证清单
|
||||
|
||||
```bash
|
||||
cd agent
|
||||
go build -o aiops-agent .
|
||||
./aiops-agent version
|
||||
./aiops-agent output -top-n 5
|
||||
```
|
||||
|
||||
联调:
|
||||
|
||||
1. 后端启动后,使用 `./aiops-agent -server http://localhost:8080 -token <token> -interval 10`。
|
||||
2. `POST /api/v1/agent/report` 返回成功。
|
||||
3. `asset_servers` 对应主机 `agent_status=online`。
|
||||
4. 监控页面能看到 CPU、内存、磁盘、网络指标。
|
||||
5. 创建 Agent 任务后,Agent 能轮询、执行、回传结果。
|
||||
6. 停止 Agent 超过离线阈值后,后端能识别离线。
|
||||
@@ -0,0 +1,339 @@
|
||||
# AIOps 客户端 Agent 实现规格
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 目标:让另一个项目仅阅读本文档,也能实现一个轻量主机采集 Agent 和后端接收链路。
|
||||
> 范围:Linux 主机指标采集、HTTP 上报、后端入库、任务轮询、安装升级、验收标准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 最小可用闭环
|
||||
|
||||
```text
|
||||
Agent
|
||||
├── collect CPU / memory / disk
|
||||
├── build report JSON
|
||||
└── POST /api/v1/agent/report
|
||||
|
||||
Backend
|
||||
├── validate token
|
||||
├── insert agent_reports
|
||||
├── update asset_servers latest metrics
|
||||
└── return { code: 200 }
|
||||
|
||||
Frontend
|
||||
└── query latest metrics and render host monitor
|
||||
```
|
||||
|
||||
第一版必须完成:
|
||||
|
||||
| 能力 | 验收 |
|
||||
|------|------|
|
||||
| Agent 能启动 | `version` 和 `output` 正常 |
|
||||
| 指标能上报 | 后端收到并入库 |
|
||||
| 资产能关联 | hostname/IP 匹配到资产,未匹配时可自动注册 |
|
||||
| 前端能展示 | 主机监控页面看到实时指标 |
|
||||
| 离线能识别 | 超过阈值无上报后标记 offline |
|
||||
|
||||
---
|
||||
|
||||
## 2. Agent 命令行契约
|
||||
|
||||
```bash
|
||||
aiops-agent version
|
||||
aiops-agent output -top-n 5
|
||||
aiops-agent -server http://aiops.example.com -token <TOKEN> -hostname <HOST> -interval 30
|
||||
```
|
||||
|
||||
参数:
|
||||
|
||||
| 参数 | 默认 | 说明 |
|
||||
|------|------|------|
|
||||
| `-server` | 空 | 后端地址,run 模式必填 |
|
||||
| `-token` | 空 | Agent 鉴权 token |
|
||||
| `-hostname` | `os.Hostname()` | 主机标识,建议与资产 hostname 一致 |
|
||||
| `-interval` | `10` | 采集间隔,生产建议 30-60 秒 |
|
||||
| `-top-n` | `3` | 进程 TOP 数量 |
|
||||
|
||||
要求:
|
||||
|
||||
- `interval < 5` 应拒绝启动,避免高频上报压垮后端。
|
||||
- 启动时加入随机 jitter,防止大量 Agent 同时上报。
|
||||
- `output` 模式不需要 `server/token`,用于安装排障。
|
||||
|
||||
---
|
||||
|
||||
## 3. 上报协议
|
||||
|
||||
### 3.1 请求
|
||||
|
||||
```http
|
||||
POST /api/v1/agent/report
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
核心 JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"hostname": "app-01",
|
||||
"ip": "10.0.0.11",
|
||||
"timestamp": "2026-07-05T10:00:00Z",
|
||||
"agent_version": "v2.1.0",
|
||||
"cpu": {
|
||||
"usage_percent": 12.3,
|
||||
"iowait": 0.4,
|
||||
"core_count": 8,
|
||||
"load1": 0.8,
|
||||
"load5": 0.7,
|
||||
"load15": 0.6
|
||||
},
|
||||
"memory": {
|
||||
"total_gb": 31.4,
|
||||
"used_gb": 12.0,
|
||||
"available_gb": 18.2,
|
||||
"cached_gb": 4.1,
|
||||
"swap_used_gb": 0.0,
|
||||
"usage_percent": 38.2
|
||||
},
|
||||
"disks": [
|
||||
{
|
||||
"mount": "/",
|
||||
"device": "/dev/vda1",
|
||||
"total_gb": 100,
|
||||
"used_gb": 42,
|
||||
"usage_percent": 42,
|
||||
"inode_usage_pct": 11
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{ "port": 8080, "protocol": "tcp", "process": "aiops", "pid": 1234 }
|
||||
],
|
||||
"network": {},
|
||||
"disk_io": {},
|
||||
"process_top": [],
|
||||
"fd": {},
|
||||
"context_switches": {},
|
||||
"heartbeat": {
|
||||
"version": "v2.1.0",
|
||||
"uptime_sec": 3600,
|
||||
"self_rss_mb": 18.5,
|
||||
"report_seq": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 响应
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "ok" }
|
||||
```
|
||||
|
||||
错误:
|
||||
|
||||
| HTTP | 场景 |
|
||||
|------|------|
|
||||
| 400 | JSON 格式错误或必填字段缺失 |
|
||||
| 401 | token 无效 |
|
||||
| 500 | 入库失败 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 采集器规格
|
||||
|
||||
| 采集器 | Linux 来源 | 输出 |
|
||||
|--------|------------|------|
|
||||
| CPU | `/proc/stat`, `/proc/loadavg` | 使用率、IOWait、核心数、Load |
|
||||
| Memory | `/proc/meminfo` | total/used/available/cached/swap |
|
||||
| Disk | `statfs`, mount table | 挂载点容量、inode |
|
||||
| Disk IO | `/proc/diskstats` | read/write bps、IOPS |
|
||||
| Network | `/proc/net/dev`, `/proc/net/tcp*` | rx/tx、TCP 状态 |
|
||||
| Port | `/proc/net/tcp*` + `/proc/<pid>/fd` | 监听端口和进程 |
|
||||
| ProcessTop | `/proc/<pid>/stat`, `status`, `cmdline` | top N 进程 |
|
||||
| FD | `/proc/sys/fs/file-nr` | allocated/max/usage |
|
||||
| Context | `/proc/stat` | ctxt / intr 速率 |
|
||||
| System | `/etc/os-release`, `uname`, `/proc/uptime` | OS/Kernel/Uptime |
|
||||
| HealthProbe | 后端配置 | HTTP/TCP/CMD 探测结果 |
|
||||
|
||||
实现原则:
|
||||
|
||||
- 单个采集器失败不能影响整次上报。
|
||||
- 采集错误写本地日志,不把错误堆进上报报文。
|
||||
- 网络/磁盘速率类指标需要保存上一轮样本计算差值。
|
||||
- 不要依赖 shell 命令做核心采集,优先读 `/proc` 和 Go 标准库。
|
||||
|
||||
---
|
||||
|
||||
## 5. 后端处理规格
|
||||
|
||||
```text
|
||||
ReceiveReport(req)
|
||||
├── parse timestamp
|
||||
├── marshal complex fields to JSON string
|
||||
├── insert AgentReport
|
||||
├── match asset by hostname or IP
|
||||
├── update latest metrics on asset_servers
|
||||
├── if no match, auto register server
|
||||
├── cache latest metrics in Redis if available
|
||||
├── broadcast MetricsHub
|
||||
└── return success
|
||||
```
|
||||
|
||||
资产匹配优先级:
|
||||
|
||||
1. Agent 上报 `hostname`。
|
||||
2. Agent 上报 `ip`。
|
||||
3. 请求来源 IP。
|
||||
4. 必要时自动注册新资产。
|
||||
|
||||
离线判断:
|
||||
|
||||
```text
|
||||
latest_report_time < now - offline_threshold
|
||||
→ agent_status = offline
|
||||
```
|
||||
|
||||
建议阈值:
|
||||
|
||||
| interval | offline threshold |
|
||||
|----------|-------------------|
|
||||
| 10s | 60s |
|
||||
| 30s | 180s |
|
||||
| 60s | 300s |
|
||||
|
||||
---
|
||||
|
||||
## 6. 任务轮询
|
||||
|
||||
### 6.1 Agent 拉取任务
|
||||
|
||||
```http
|
||||
GET /api/v1/agent/tasks/poll?hostname=app-01
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": [
|
||||
{
|
||||
"task_id": "uuid",
|
||||
"hostname": "app-01",
|
||||
"type": "command",
|
||||
"command": "df -h",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
后端规则:
|
||||
|
||||
- 只返回 `pending` 任务。
|
||||
- 按创建时间升序。
|
||||
- 拉取后原子更新为 `running`。
|
||||
- 单次最多返回 5 个。
|
||||
|
||||
### 6.2 Agent 回传结果
|
||||
|
||||
```http
|
||||
POST /api/v1/agent/tasks/:task_id/result
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"exit_code": 0,
|
||||
"result": "Filesystem ...",
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
任务安全要求:
|
||||
|
||||
- 命令必须有超时。
|
||||
- 输出必须截断,避免后端大字段膨胀。
|
||||
- 任务执行应记录操作者、创建时间、开始时间、结束时间。
|
||||
- 生产环境应接入命令策略或审批,尤其是 `script` 类型。
|
||||
|
||||
---
|
||||
|
||||
## 7. 安装和升级
|
||||
|
||||
安装链路:
|
||||
|
||||
```text
|
||||
Frontend AgentWizard
|
||||
→ backend AgentInstallHandler
|
||||
→ SSH to target server
|
||||
→ download aiops-agent binary / install.sh
|
||||
→ write systemd unit
|
||||
→ systemctl enable --now aiops-agent
|
||||
→ detect version
|
||||
→ write audit log
|
||||
```
|
||||
|
||||
systemd 服务要求:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/aiops-agent -server <SERVER> -token <TOKEN> -interval 30
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
```
|
||||
|
||||
安装验收:
|
||||
|
||||
1. `/usr/local/bin/aiops-agent version` 正常。
|
||||
2. `systemctl status aiops-agent` active。
|
||||
3. 后端 1 分钟内收到上报。
|
||||
4. 资产 `agent_version` 更新。
|
||||
5. 安装审计记录成功或失败原因。
|
||||
|
||||
---
|
||||
|
||||
## 8. 安全要求
|
||||
|
||||
| 风险 | 要求 |
|
||||
|------|------|
|
||||
| token 泄漏 | token 不写命令审计、不在前端明文展示 |
|
||||
| 伪造上报 | token 必须校验,建议绑定 hostname/server_id |
|
||||
| 命令任务滥用 | command/script 任务接入策略、审计、超时、输出截断 |
|
||||
| 自动注册污染资产 | 自动注册要有去重策略和可信来源限制 |
|
||||
| 高并发上报 | Agent jitter + 后端批量/缓存/限流 |
|
||||
| 采集权限过大 | Agent 默认不需要 root;端口进程归属等能力可降级 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 与 Redis/MySQL 主动采集的边界
|
||||
|
||||
客户端 Agent 第一职责是主机 OS 层采集,Redis/MySQL 第一职责是服务内部指标采集。即使两者部署在同一台机器,也应保持两条链路独立。// Logan.Li
|
||||
|
||||
| 能力 | 客户端 Agent | 后端中间件监控 |
|
||||
|------|--------------|----------------|
|
||||
| 发现 3306/6379 端口 | 可以,通过端口采集器 | 不负责端口发现 |
|
||||
| 判断 TCP 是否可连 | 可以,通过健康探针 | 可以,连接失败则目标 offline |
|
||||
| Redis INFO / DBSIZE | 不做 | 必须做 |
|
||||
| MySQL SHOW GLOBAL STATUS | 不做 | 必须做 |
|
||||
| 主机 CPU/内存/磁盘 | 必须做 | 不做 |
|
||||
| 服务级 QPS/连接数/命中率 | 不做 | 必须做 |
|
||||
|
||||
如果要把 Redis/MySQL 指标下沉到 Agent,需要新增独立的 `middleware` 采集器和权限配置;当前设计不建议这么做,因为后端主动采集更便于集中管理凭据、采集间隔和告警策略。
|
||||
|
||||
---
|
||||
|
||||
## 10. 验收矩阵
|
||||
|
||||
| 场景 | 预期 |
|
||||
|------|------|
|
||||
| `output` 调试 | 打印完整指标,不访问后端 |
|
||||
| 正常上报 | 后端写入 `agent_reports` |
|
||||
| 资产匹配 | `asset_servers` 实时指标更新 |
|
||||
| 自动注册 | 新 hostname 创建资产 |
|
||||
| 网络断开 | Agent 记录失败并下轮重试 |
|
||||
| 后端重启 | Agent 自动恢复上报 |
|
||||
| 任务执行成功 | 状态 `success`,保存输出 |
|
||||
| 任务超时 | 状态 `timeout`,进程被终止 |
|
||||
| Agent 停止 | 超过阈值后标记 offline |
|
||||
| 一键安装 | systemd 服务 active,审计可查 |
|
||||
-1926
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,7 @@
|
||||
# 前端工程规范
|
||||
|
||||
本目录保存前端视觉、页面结构和交互规范。业务模块的页面说明放在各自模块文档中。// Logan.Li
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [design-system.md](./design-system.md) | PageShell、主题变量、业务页面视觉规范 |
|
||||
File renamed without changes.
@@ -0,0 +1,131 @@
|
||||
# Kubernetes 管理设计说明
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:K8s 集群接入、自研 HTTP 客户端、资源管理、Pod exec/logs/files、YAML 编辑、事件、容量、AI 排障。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 定位
|
||||
|
||||
Kubernetes 管理模块提供多集群资源管理和排障能力。AIOps 当前采用自研轻量 K8s HTTP 客户端,不引入 client-go,重点控制镜像体积和调用边界。
|
||||
|
||||
---
|
||||
|
||||
## 能力矩阵
|
||||
|
||||
| 能力 | 当前状态 | 说明 |
|
||||
|------|----------|------|
|
||||
| 集群接入 | 已完成 | kubeconfig/API Server/TLS |
|
||||
| 节点/Pod/Workload | 已完成 | 列表、详情、指标、事件 |
|
||||
| Namespace 管理 | 已完成 | 命名空间资源视图 |
|
||||
| YAML 查看/编辑 | 已完成 | CodeMirror、可视化编辑、导入 |
|
||||
| Pod 日志 | 已完成 | 日志查看 |
|
||||
| Pod exec | 已完成 | xterm + channel.k8s.io |
|
||||
| Pod 文件 | 已完成 | 文件浏览、上传/下载 |
|
||||
| kubectl 终端 | 已完成 | 集群级 kubectl shell |
|
||||
| HPA/CronHPA | 已完成 | 工作负载扩缩容能力 |
|
||||
| 集群对比/容量 | 已完成 | 多集群视图 |
|
||||
| AI K8s 排障 | 已完成 | Agent CLI kubernetes 模式 |
|
||||
|
||||
---
|
||||
|
||||
## 主要代码落点
|
||||
|
||||
| 模块 | 后端 | 前端 |
|
||||
|------|------|------|
|
||||
| 集群管理 | `k8s_cluster.go`、`kubernetes.go` | `KubernetesClusters.tsx` |
|
||||
| 节点/Pod/Workload | `k8s_node.go`、`k8s_pod.go`、`k8s_workload.go` | `KubernetesNodes.tsx`、`WorkloadViewPage.tsx` |
|
||||
| Namespace | `k8s_namespace.go` | `NamespaceManagementPage.tsx` |
|
||||
| YAML | `k8s_yaml.go`、`k8s_resource.go` | `YamlViewer.tsx`、`ResourceVisualEditor.tsx` |
|
||||
| Pod exec/logs/files | `k8s_pod.go`、`k8s_pod_files.go` | `PodExecTerminal.tsx`、`PodLogsViewer.tsx`、`PodFileBrowser.tsx` |
|
||||
| kubectl | `kubectl_shell.go` | `KubectlTerminal.tsx` |
|
||||
| 事件 | `k8s_events.go` | `EventTable.tsx`、`EventsPage.tsx` |
|
||||
| AI K8s | `agent_cli_kubernetes.go` | `AgentCLI.tsx` |
|
||||
|
||||
---
|
||||
|
||||
## 自研客户端边界
|
||||
|
||||
```text
|
||||
AIOps Backend
|
||||
├── build K8s request
|
||||
├── attach token/cert
|
||||
├── optional InsecureSkipVerify / CA
|
||||
├── call Kubernetes API
|
||||
├── normalize response
|
||||
└── return frontend DTO
|
||||
```
|
||||
|
||||
原则:
|
||||
|
||||
- 普通资源查询走 HTTP JSON。
|
||||
- Pod exec 使用 `channel.k8s.io` WebSocket。
|
||||
- 文件操作优先复用 exec/tar 或 API 能力。
|
||||
- 不把 kubeconfig 明文返回前端。
|
||||
|
||||
---
|
||||
|
||||
## Pod exec 通道
|
||||
|
||||
| Channel | 含义 |
|
||||
|---------|------|
|
||||
| 0 | stdin |
|
||||
| 1 | stdout |
|
||||
| 2 | stderr |
|
||||
| 3 | error/status |
|
||||
| 4 | resize |
|
||||
|
||||
终端通用实现见 [docs/terminal](../terminal/implementation-spec.md)。
|
||||
|
||||
---
|
||||
|
||||
## YAML 管理风险边界
|
||||
|
||||
高风险操作:
|
||||
|
||||
- delete pod/deployment/service/configmap/secret
|
||||
- scale 到 0 或异常副本数
|
||||
- rollout restart 生产工作负载
|
||||
- 修改镜像、环境变量、挂载、权限上下文
|
||||
|
||||
建议接入:
|
||||
|
||||
1. diff 预览。
|
||||
2. 风险提示。
|
||||
3. 生产 namespace 审批。
|
||||
4. 操作审计。
|
||||
5. 回滚建议。
|
||||
|
||||
---
|
||||
|
||||
## AI K8s 排障
|
||||
|
||||
Agent CLI K8s 模式可用工具:
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `kubernetes_get` | 查询资源 |
|
||||
| `pod_logs` | 查看日志 |
|
||||
| `pod_execute` | 容器内执行 |
|
||||
| `kubernetes_action` | 资源变更,高危确认 |
|
||||
|
||||
建议 Prompt 注入:
|
||||
|
||||
- 集群、namespace、Pod、container。
|
||||
- Pod restart count。
|
||||
- 最近事件。
|
||||
- 所属 Deployment/StatefulSet。
|
||||
- Service/Ingress 关系。
|
||||
- HPA 状态。
|
||||
|
||||
---
|
||||
|
||||
## 验收清单
|
||||
|
||||
1. 集群配置保存后能列出 namespace/node/pod。
|
||||
2. Pod 日志能实时查看并切换容器。
|
||||
3. Pod exec 能 resize,Ctrl+C 可用。
|
||||
4. YAML 编辑有 diff,失败时错误明确。
|
||||
5. kubectl 终端关闭后后端进程清理。
|
||||
6. 无权限用户不能访问未授权集群。
|
||||
7. AI K8s 高危操作触发确认或审批。
|
||||
@@ -0,0 +1,143 @@
|
||||
# 日志中心设计说明
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:Loki/Elasticsearch 数据源、统一日志查询、字段发现、查询历史、保存查询、日志告警、日志 RBAC。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 定位
|
||||
|
||||
日志中心负责把多种日志后端统一成一个查询工作台,重点不是采集日志,而是:
|
||||
|
||||
1. 管理日志数据源。
|
||||
2. 代理查询 Loki / Elasticsearch。
|
||||
3. 提供统一查询体验和结果展示。
|
||||
4. 从查询条件生成日志告警。
|
||||
5. 按用户/角色限制可见数据源、索引、标签范围。
|
||||
|
||||
---
|
||||
|
||||
## 能力矩阵
|
||||
|
||||
| 能力 | 当前状态 | 说明 |
|
||||
|------|----------|------|
|
||||
| Loki 查询代理 | 已完成 | 后端代理查询,前端统一展示 |
|
||||
| ES 查询代理 | 已完成 | 支持索引、字段、结果解析 |
|
||||
| 日志数据源管理 | 已完成 | 创建、测试、配置 |
|
||||
| 查询编辑器 | 已完成 | 快速查询、自动补全、查询构建 |
|
||||
| 时间范围 | 已完成 | TimeRangePicker |
|
||||
| 字段侧栏 | 已完成 | 字段统计和过滤 |
|
||||
| 查询历史/保存查询 | 已完成 | 前端交互能力 |
|
||||
| 日志告警 | 已完成 | 从查询创建告警规则 |
|
||||
| 日志 RBAC | 已完成 | log scope role |
|
||||
|
||||
---
|
||||
|
||||
## 主要代码落点
|
||||
|
||||
| 层 | 文件 / 目录 | 说明 |
|
||||
|----|-------------|------|
|
||||
| 后端控制器 | `backend/controller/log_datasource.go` | 日志数据源 CRUD |
|
||||
| 后端控制器 | `backend/controller/log_proxy.go` | Loki/ES 查询代理 |
|
||||
| 后端控制器 | `backend/controller/log_proxy_explore.go` | 探索式查询能力 |
|
||||
| 后端控制器 | `backend/controller/log_scope_role.go` | 日志范围权限 |
|
||||
| 后端模型 | `backend/model/log_datasource.go` | 日志数据源 |
|
||||
| 后端模型 | `backend/model/log_scope_role.go` | 日志 RBAC |
|
||||
| 前端 API | `frontend/src/api/log/` | 日志中心 API |
|
||||
| 前端组件 | `frontend/src/components/monitor/log-center/` | 日志中心完整 UI |
|
||||
|
||||
---
|
||||
|
||||
## 查询链路
|
||||
|
||||
```text
|
||||
LogCenter UI
|
||||
├── select datasource
|
||||
├── build query
|
||||
├── choose time range
|
||||
└── GET/POST /api/v1/log/...
|
||||
│
|
||||
▼
|
||||
Backend log proxy
|
||||
├── load datasource
|
||||
├── check log RBAC
|
||||
├── translate request
|
||||
├── query Loki / ES
|
||||
├── normalize response
|
||||
└── return unified result
|
||||
```
|
||||
|
||||
统一结果建议包含:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `timestamp` | 日志时间 |
|
||||
| `message` | 原始消息 |
|
||||
| `labels` | Loki label 或 ES 字段标签 |
|
||||
| `fields` | 结构化字段 |
|
||||
| `source` | 数据源和索引/stream |
|
||||
| `raw` | 原始日志对象 |
|
||||
|
||||
---
|
||||
|
||||
## 数据源模型
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `name` | 数据源名称 |
|
||||
| `type` | loki / elasticsearch |
|
||||
| `endpoint` | 服务地址 |
|
||||
| `auth_type` | none / basic / token |
|
||||
| `config` | 索引、租户、TLS、超时等 JSON |
|
||||
| `status` | active / inactive |
|
||||
|
||||
敏感字段必须后端加密或脱敏返回,前端不应展示密码/token。
|
||||
|
||||
---
|
||||
|
||||
## 日志告警
|
||||
|
||||
```text
|
||||
Saved Query / Current Query
|
||||
→ create log alert rule
|
||||
→ alert engine query log datasource
|
||||
→ match count / pattern
|
||||
→ create alert event
|
||||
→ notify policy
|
||||
```
|
||||
|
||||
建议规则字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `datasource_id` | 日志数据源 |
|
||||
| `query` | Loki LogQL 或 ES DSL/QueryString |
|
||||
| `time_window` | 查询窗口 |
|
||||
| `condition` | count > N / contains / no data |
|
||||
| `severity` | warning / critical |
|
||||
| `labels` | 业务、环境、服务 |
|
||||
|
||||
---
|
||||
|
||||
## 日志 RBAC
|
||||
|
||||
日志权限不只按页面菜单控制,还应按数据范围控制:
|
||||
|
||||
| 维度 | 示例 |
|
||||
|------|------|
|
||||
| 数据源 | 只能看生产 Loki,不能看审计 ES |
|
||||
| 索引/stream | 只能看 `app-*` |
|
||||
| 标签 | `namespace=dev`、`app=order` |
|
||||
| 查询语法 | 禁止超大范围、禁止敏感索引 |
|
||||
|
||||
---
|
||||
|
||||
## 验收清单
|
||||
|
||||
1. Loki 数据源测试成功并能查询日志。
|
||||
2. ES 数据源测试成功并能查询索引日志。
|
||||
3. 查询时间范围切换后 URL/状态同步正确。
|
||||
4. 字段侧栏能展示字段和值分布。
|
||||
5. 从查询创建日志告警后能触发事件。
|
||||
6. 无权限用户无法查询受限数据源或标签。
|
||||
7. 查询超时、后端不可达时前端错误明确。
|
||||
@@ -0,0 +1,205 @@
|
||||
# AIOps 模块文档规划
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 目标:把 AIOps 的大模块沉淀成“其他项目看文档也能复刻”的工程文档,而不是零散功能说明。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 当前已拆清楚的文档域
|
||||
|
||||
| 文档域 | 目录 | 说明 |
|
||||
|--------|------|------|
|
||||
| AI Agent / Agent CLI | `docs/ai-agent/` | ReAct、工具调用、Evolver、安全确认、会话管理 |
|
||||
| 客户端 Agent | `docs/client-agent/` | 主机采集、上报、任务轮询、安装升级 |
|
||||
| 字符终端 | `docs/terminal/` | WebSSH、Pod exec、kubectl、Workspace、审计回放 |
|
||||
| RDP 图形桌面 | `docs/rdp/` | 自研 RDP、剪贴板、RDPDR 虚拟盘、文件传输、录像 |
|
||||
| 安全审计与堡垒机 | `docs/security-bastion/` | 会话、录像、命令、策略、审批 |
|
||||
| 监控中心 | `docs/monitoring/` | 指标、数据源、告警引擎、通知 |
|
||||
| 日志中心 | `docs/log-center/` | Loki/ES 查询、数据源、日志告警、RBAC |
|
||||
| 资产与凭证 | `docs/asset-credential/` | 资产模型、统一凭证、云同步 |
|
||||
| Kubernetes 管理 | `docs/kubernetes/` | 自研客户端、Pod exec/files、YAML、AI 排障 |
|
||||
| 自动化运维与发布 | `docs/automation-deploy/` | 脚本、任务、远程执行、流水线、Workspace |
|
||||
|
||||
---
|
||||
|
||||
## 已建立的一级模块文档
|
||||
|
||||
### 1. 日志中心
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
docs/log-center/
|
||||
├── README.md
|
||||
├── query-engine.md
|
||||
├── datasource.md
|
||||
├── alerting.md
|
||||
└── rbac.md
|
||||
```
|
||||
|
||||
应覆盖:
|
||||
|
||||
| 主题 | 当前代码落点 | 文档重点 |
|
||||
|------|--------------|----------|
|
||||
| Loki / ES 查询代理 | `backend/controller/log_proxy*.go` | 查询 DSL、时间范围、分页、字段解析 |
|
||||
| 日志数据源 | `backend/controller/log_datasource.go`、`frontend/src/components/monitor/log-center/LogDatasourcePage.tsx` | 数据源配置、连通性检测、索引/标签发现 |
|
||||
| 统一查询 UI | `UnifiedQueryPanel.tsx`、`QueryAutocompleteInput.tsx` | 查询体验、自动补全、URL 状态 |
|
||||
| 日志告警 | `LogAlertFromQueryDialog.tsx`、`alert_engine.go` | 从查询生成告警、触发/恢复策略 |
|
||||
| 日志 RBAC | `log_scope_role.go`、`LogRBAC.tsx` | 数据源/索引/标签级授权 |
|
||||
|
||||
当前已建立 [docs/log-center/README.md](../log-center/README.md)。日志中心链路复杂,后续优先补 `query-engine.md`、`datasource.md`、`alerting.md`。
|
||||
|
||||
### 2. 监控中心
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
docs/monitoring/
|
||||
├── README.md
|
||||
├── metrics-model.md
|
||||
├── alert-engine.md
|
||||
├── datasource-proxy.md
|
||||
├── middleware-monitor.md
|
||||
└── dashboard.md
|
||||
```
|
||||
|
||||
应覆盖:
|
||||
|
||||
| 主题 | 当前代码落点 | 文档重点 |
|
||||
|------|--------------|----------|
|
||||
| Agent 指标 | `backend/controller/agent.go`、`frontend/src/components/agent/` | 指标模型、最新值、历史趋势、离线判断 |
|
||||
| 告警引擎 | `alert_engine*.go`、`alert_rules.go` | 规则、状态机、触发、恢复、静默 |
|
||||
| 告警通知 | `alert_channels.go`、`alert_notify_*.go` | 联系人、渠道、策略、消息模板 |
|
||||
| Prometheus / Zabbix | `prometheus_proxy.go`、`zabbix_proxy.go` | 数据源代理、鉴权、查询转换 |
|
||||
| SNMP / 站点拨测 | `snmp*.go`、`site*.go` | 采集模型、历史存储、告警接入 |
|
||||
| 中间件监控 | `middleware_monitor*.go` | Redis/MySQL 直连采集、指标目录 |
|
||||
|
||||
当前已建立 [docs/monitoring/README.md](../monitoring/README.md)。监控是平台中台能力,后续优先补 `metrics-model.md`、`alert-engine.md`。
|
||||
|
||||
### 3. 安全审计与堡垒机
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
docs/security-bastion/
|
||||
├── README.md
|
||||
├── audit-model.md
|
||||
├── session-policy.md
|
||||
├── terminal-audit.md
|
||||
├── approval-integration.md
|
||||
└── hardening-roadmap.md
|
||||
```
|
||||
|
||||
应覆盖:
|
||||
|
||||
| 主题 | 当前代码落点 | 文档重点 |
|
||||
|------|--------------|----------|
|
||||
| 操作审计 | `audit.go`、`login_log.go` | 操作日志模型、筛选、导出、保留策略 |
|
||||
| 会话审计 | `terminal_session.go`、`rdp_session.go`、`session_command.go` | SSH/RDP 会话、命令、录像、回放 |
|
||||
| 会话策略 | `audit_setting.go`、`SystemSettingsTab.tsx` | 水印、录像、剪贴板、上传、下载、审批 |
|
||||
| 堡垒机能力 | `docs/terminal/`、`docs/rdp/` | WebSSH/RDP/Pod/kubectl 的统一访问控制 |
|
||||
| 审批集成 | `approval*.go`、Agent CLI/RDP 预留策略 | 高危操作审批、生产环境变更 |
|
||||
|
||||
当前已建立 [docs/security-bastion/README.md](../security-bastion/README.md)。RDP 和终端已成形,下一步需要补 `audit-model.md`、`session-policy.md`。
|
||||
|
||||
### 4. 资产与凭证
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
docs/asset-credential/
|
||||
├── README.md
|
||||
├── server-model.md
|
||||
├── credential-vault.md
|
||||
├── cloud-sync.md
|
||||
└── permission.md
|
||||
```
|
||||
|
||||
应覆盖:
|
||||
|
||||
| 主题 | 文档重点 |
|
||||
|------|----------|
|
||||
| 服务器资产 | 字段模型、状态、Agent 自动注册、云同步 |
|
||||
| 凭证系统 | SSH/RDP/API/AKSK/TLS/DB/Generic 七类凭证、加密存储、授权 |
|
||||
| 云资产 | 云账号、地域、实例同步、账单 |
|
||||
| 资产权限 | 用户/角色/资源授权 |
|
||||
|
||||
当前已建立 [docs/asset-credential/README.md](../asset-credential/README.md)。资产与凭证是堡垒机和自动化的底座。
|
||||
|
||||
### 5. Kubernetes 管理
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
docs/kubernetes/
|
||||
├── README.md
|
||||
├── api-client.md
|
||||
├── pod-exec-files.md
|
||||
├── yaml-editor.md
|
||||
└── aiops-integration.md
|
||||
```
|
||||
|
||||
应覆盖:
|
||||
|
||||
| 主题 | 文档重点 |
|
||||
|------|----------|
|
||||
| 自研 K8s HTTP 客户端 | 为什么不用 client-go、认证、TLS、分页 |
|
||||
| Pod exec / 文件 | channel.k8s.io、文件浏览、上传下载 |
|
||||
| YAML 管理 | 可视化编辑、diff、apply、风险提示 |
|
||||
| Agent CLI K8s 模式 | AI 查询/排障/变更的安全边界 |
|
||||
|
||||
当前已建立 [docs/kubernetes/README.md](../kubernetes/README.md)。K8s 功能多,适合继续拆专题。
|
||||
|
||||
### 6. 自动化运维与发布
|
||||
|
||||
目录:
|
||||
|
||||
```text
|
||||
docs/automation-deploy/
|
||||
├── README.md
|
||||
├── script-library.md
|
||||
├── cron-task.md
|
||||
├── remote-exec.md
|
||||
├── pipeline.md
|
||||
└── workspace.md
|
||||
```
|
||||
|
||||
应覆盖:
|
||||
|
||||
| 主题 | 文档重点 |
|
||||
|------|----------|
|
||||
| 脚本库 / 定时任务 | 任务模型、执行历史、日志 |
|
||||
| 远程执行 | SSH 执行、上传、权限、审计 |
|
||||
| CI/CD Pipeline | 阶段、日志、产物、Workspace |
|
||||
| 发布审批 | 环境、发布单、审批和回滚 |
|
||||
|
||||
当前已建立 [docs/automation-deploy/README.md](../automation-deploy/README.md)。可与 AI Agent 打通,把成功排障沉淀为 Runbook。
|
||||
|
||||
---
|
||||
|
||||
## 推荐执行顺序
|
||||
|
||||
1. `docs/security-bastion/`:统一审计、终端、RDP、会话策略、审批,直接服务专业堡垒机目标。
|
||||
2. `docs/monitoring/`:统一 Agent 指标、Prometheus/Zabbix、告警引擎、通知策略。
|
||||
3. `docs/log-center/`:日志中心查询、数据源、RBAC、日志告警,和监控中心形成可观测闭环。
|
||||
4. `docs/asset-credential/`:资产和凭证是所有远程能力的权限底座。
|
||||
5. `docs/kubernetes/`:K8s 管理和 AI K8s 排障单独成册。
|
||||
6. `docs/automation-deploy/`:自动化、发布、任务、Runbook 联动。
|
||||
|
||||
---
|
||||
|
||||
## 每个模块文档的标准模板
|
||||
|
||||
每个大模块建议至少包含:
|
||||
|
||||
| 章节 | 内容 |
|
||||
|------|------|
|
||||
| 定位与边界 | 解决什么问题,不解决什么问题 |
|
||||
| 功能矩阵 | 已完成、部分完成、未做能力 |
|
||||
| 架构链路 | 前端、API、后端、数据模型、外部依赖 |
|
||||
| 数据模型 | 表、字段、状态机、索引建议 |
|
||||
| API 契约 | 请求、响应、错误码、鉴权 |
|
||||
| 权限与审计 | RBAC、策略、操作日志、敏感字段 |
|
||||
| 失败处理 | 超时、重试、降级、幂等 |
|
||||
| 验收清单 | 自动测试、手工验证、联调场景 |
|
||||
| 演进路线 | P0/P1/P2 优先级 |
|
||||
@@ -0,0 +1,127 @@
|
||||
# 监控中心设计说明
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:客户端 Agent 指标、Prometheus/Zabbix 数据源、SNMP、站点拨测、中间件监控、告警引擎、通知策略、监控大屏。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 定位
|
||||
|
||||
监控中心是 AIOps 的可观测数据中台,负责采集、查询、展示和告警。当前项目中监控来源分为五类:
|
||||
|
||||
| 来源 | 采集方式 | 代表能力 |
|
||||
|------|----------|----------|
|
||||
| 客户端 Agent | 被管主机主动上报 | CPU、内存、磁盘、网络、进程、端口 |
|
||||
| 中间件监控 | 后端主动直连 | Redis/MySQL 内部指标 |
|
||||
| Prometheus | 后端代理查询 | PromQL、时序指标、Grafana 风格看板 |
|
||||
| Zabbix | 后端代理查询 | Host/Item/Trigger 数据 |
|
||||
| SNMP / 站点拨测 | 后端主动采集 | 网络设备、HTTP/TCP 可用性 |
|
||||
|
||||
---
|
||||
|
||||
## 能力矩阵
|
||||
|
||||
| 能力 | 当前状态 | 说明 |
|
||||
|------|----------|------|
|
||||
| Agent 主机监控 | 已完成 | 指标上报、实时状态、历史趋势 |
|
||||
| Agent 告警规则 | 已完成 | CPU/内存/磁盘/网络等规则 |
|
||||
| 告警中心 | 已完成 | 事件、规则、联系人、渠道、静默 |
|
||||
| 告警引擎 | 已完成 | 阈值/恢复/通知调度 |
|
||||
| 日志告警 | 已完成 | 从日志查询触发告警 |
|
||||
| Prometheus 数据源 | 已完成 | 数据源管理和代理查询 |
|
||||
| Zabbix 数据源 | 已完成 | 数据源管理和代理查询 |
|
||||
| 中间件监控 | 已完成 | Redis/MySQL 后端直连采集 |
|
||||
| SNMP | 已完成 | SNMP 采集和历史 |
|
||||
| 站点拨测 | 已完成 | HTTP/TCP 站点视图 |
|
||||
| 指标目录 | 已完成 | 告警规则可选指标元数据 |
|
||||
|
||||
---
|
||||
|
||||
## 主要代码落点
|
||||
|
||||
| 模块 | 后端 | 前端 |
|
||||
|------|------|------|
|
||||
| Agent 指标 | `backend/controller/agent.go` | `frontend/src/components/agent/` |
|
||||
| Agent 任务/规则 | `backend/controller/agent_task.go`、`monitor_alert_rule.go` | `AgentAlerts.tsx`、`AgentRuleDialog.tsx` |
|
||||
| 告警中心 | `alert*.go`、`alert_notify_*.go` | `frontend/src/components/monitor/alert-center/` |
|
||||
| 指标目录 | `alert_metric_catalog.go` | `MetricCatalogTab.tsx` |
|
||||
| Prometheus | `prometheus_datasource.go`、`prometheus_proxy.go` | `PrometheusDatasourceManager.tsx`、`PrometheusDashboard.tsx` |
|
||||
| Zabbix | `zabbix_datasource.go`、`zabbix_proxy.go` | `ZabbixDatasourceManager.tsx`、`ZabbixDashboard.tsx` |
|
||||
| 中间件监控 | `middleware_monitor*.go` | 监控页面中间件视图 |
|
||||
| SNMP | `snmp*.go` | SNMP 页面 |
|
||||
| 站点拨测 | `site*.go` | `frontend/src/api/monitor/site.ts` |
|
||||
|
||||
---
|
||||
|
||||
## 指标分层
|
||||
|
||||
| 层级 | 指标示例 | 存储/查询 |
|
||||
|------|----------|-----------|
|
||||
| 主机最新值 | CPU、内存、磁盘、Agent 状态 | `asset_servers` 实时字段 |
|
||||
| 主机历史 | Agent 原始上报 | `monitor_agent_reports` |
|
||||
| 热数据 | CPU/Mem/Load ZSet | Redis 可选,local 模式回退 MySQL |
|
||||
| 中间件历史 | `redis_qps`、`mysql_connections` | `metric_histories` |
|
||||
| 外部数据源 | Prometheus/Zabbix | 实时代理查询 |
|
||||
| 告警事件 | firing/resolved | 告警事件表 |
|
||||
|
||||
Redis/MySQL 边界见 [客户端 Agent 文档](../client-agent/README.md#与中间件监控的边界)。
|
||||
|
||||
---
|
||||
|
||||
## 告警引擎模型
|
||||
|
||||
```text
|
||||
Rule
|
||||
├── metric / source / target
|
||||
├── operator / threshold
|
||||
├── duration
|
||||
├── severity
|
||||
├── notify policy
|
||||
└── silence policy
|
||||
|
||||
Engine
|
||||
├── load enabled rules
|
||||
├── query latest metric
|
||||
├── evaluate condition
|
||||
├── create/update alert event
|
||||
├── recovery check
|
||||
└── dispatch notification
|
||||
```
|
||||
|
||||
建议统一字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `source_type` | agent / middleware / prometheus / zabbix / log / site / snmp |
|
||||
| `target_id` | 资产、监控目标或数据源内对象 |
|
||||
| `metric` | 统一指标 key |
|
||||
| `value` | 触发时的数值 |
|
||||
| `labels` | 环境、业务、集群、namespace 等维度 |
|
||||
|
||||
---
|
||||
|
||||
## 与日志中心关系
|
||||
|
||||
日志中心负责非结构化日志查询和日志告警;监控中心负责结构化指标和状态告警。两者在告警中心汇聚,事件模型应统一:
|
||||
|
||||
```text
|
||||
Metrics Alert
|
||||
Log Alert
|
||||
Site Alert
|
||||
SNMP Alert
|
||||
→ Alert Event
|
||||
→ Notify Policy
|
||||
→ Channel Dispatch
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 验收清单
|
||||
|
||||
1. Agent 上报后主机监控页面能看到最新指标。
|
||||
2. Redis/MySQL 监控目标能手动测试连接和定时采集。
|
||||
3. Prometheus/Zabbix 数据源能保存、测试、查询。
|
||||
4. 告警规则触发后生成事件,并能恢复。
|
||||
5. 静默规则生效后不发送通知。
|
||||
6. 通知渠道失败不影响告警事件入库。
|
||||
7. local 模式无 Redis 时,历史聚合能从 MySQL 回退。
|
||||
@@ -0,0 +1,7 @@
|
||||
# 平台基础能力
|
||||
|
||||
本目录保存跨业务模块复用的基础设施设计,例如缓存、配置、任务调度、工具懒加载等。// Logan.Li
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [redis-caching-strategy.md](./redis-caching-strategy.md) | Redis/redka 缓存策略 |
|
||||
File renamed without changes.
@@ -80,7 +80,24 @@ aiops/
|
||||
│ └── deploy/ # install.sh + systemd
|
||||
│
|
||||
├── docs/ # 开发文档
|
||||
│ └── agent/ # Agent CLI 设计文档集(10 篇)
|
||||
│ ├── README.md # 文档总索引
|
||||
│ ├── quick-start.md # 快速开始
|
||||
│ ├── deployment.md # 部署指南
|
||||
│ ├── development-guide.md # 开发规范
|
||||
│ ├── project-structure.md # 当前文档
|
||||
│ ├── ai-agent/ # AI Agent / Agent CLI 设计文档集
|
||||
│ ├── client-agent/ # 客户端 Agent 采集与任务规格
|
||||
│ ├── security-bastion/ # 安全审计与堡垒机
|
||||
│ ├── monitoring/ # 监控中心
|
||||
│ ├── log-center/ # 日志中心
|
||||
│ ├── asset-credential/ # 资产与凭证
|
||||
│ ├── kubernetes/ # Kubernetes 管理
|
||||
│ ├── automation-deploy/ # 自动化运维与发布
|
||||
│ ├── terminal/ # 字符终端体系
|
||||
│ ├── rdp/ # Web RDP 图形桌面
|
||||
│ ├── reference/ # API / 数据库参考
|
||||
│ ├── platform/ # Redis/redka 等平台基础能力
|
||||
│ └── frontend/ # 前端设计系统与视觉规范
|
||||
├── deploy/kubernetes/ # K8s 清单 (devops 命名空间)
|
||||
├── docker-compose.yml # MySQL + Redis + AIOps embed
|
||||
├── docker-compose.k8s.yml # 单应用 + 外部 DB
|
||||
@@ -164,4 +181,4 @@ aiops/
|
||||
|
||||
- `./run.sh build` / `./run.sh build tag v1.5.0`
|
||||
- `Dockerfile` 构建参数 `VERSION` / `BUILD_TIME` / `GIT_COMMIT`
|
||||
- 运行时:`./aiops version`、`GET /version`
|
||||
- 运行时:`./aiops version`、`GET /version`
|
||||
+1
-1
@@ -165,4 +165,4 @@ lsof -ti:5173 | xargs kill -9
|
||||
|
||||
- [部署指南](./deployment.md) — 生产环境、版本管理、K8s
|
||||
- [开发规范](./development-guide.md) — 代码规范
|
||||
- [API 接口](./api-reference.md) — 后端 API
|
||||
- [API 接口](./reference/api-reference.md) — 后端 API
|
||||
-234
@@ -1,234 +0,0 @@
|
||||
# AIOps Web RDP 远程桌面
|
||||
|
||||
> 作者:Logan.Li
|
||||
> 更新:2026-07-03
|
||||
> 归档副本:`/opt/rdp/docs/rdp.md` · 变更记录:`/opt/rdp/CHANGELOG.md`
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
AIOps 提供浏览器内 Windows 远程桌面(Canvas 渲染 + WebSocket)。当前已移除第三方 `grdp` 兜底实现,后端固定使用 AIOps 自研 RDP 协议栈。// Logan.Li
|
||||
|
||||
| 环境变量 | 行为 |
|
||||
|----------|------|
|
||||
| `RDP_ENGINE` | 已不再参与引擎选择,保留环境变量不会改变行为 |
|
||||
| `RDP_DEBUG=1` | 打开 `[RDP:TRACE]` 协议级日志 |
|
||||
|
||||
| 层级 | 状态 | 路径 |
|
||||
|------|------|------|
|
||||
| X.224 / TPKT / TLS | ✅ 自研灰度 | `backend/pkg/rdp/transport/` |
|
||||
| CredSSP / NTLMv2 (NLA) | ✅ 自研灰度 | `backend/pkg/rdp/credssp/` |
|
||||
| MCS / GCC / 许可证 / 激活 | ✅ 自研灰度 | `backend/pkg/rdp/mcs_probe.go`、`session_connect.go` |
|
||||
| FastPath 位图 / MPPC / RLE | ✅ 自研灰度 | `bitmap_decode.go`、`fastpath_recv.go`、`mppc.go`、`rle/` |
|
||||
| 键鼠 / 剪贴板 / auto_exec | ✅ 自研灰度 | `input_fastpath.go` |
|
||||
| 文件传输 (SMB + 暂存回退) | ✅ 自研 | `pkg/winsmb/`、`pkg/rdpfilestage/` |
|
||||
| 前端 Canvas + 工具面板 | ✅ 自研 | `RdpDialog.tsx`、`RdpToolsPanel.tsx` |
|
||||
|
||||
自研协议栈不依赖第三方 RDP 实现,也不直接依赖控制器、数据库或 WebSocket。已删除的第三方目录备份在 `/opt/rdp/third-party/grdp-aiops-backup-20260703`。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-03 变更记录
|
||||
|
||||
### 自研 RDP 引擎
|
||||
|
||||
| 变更项 | 说明 |
|
||||
|--------|------|
|
||||
| 引擎入口 | `NewEngine()` 固定返回 `SessionEngine` |
|
||||
| 兜底依赖 | 已移除 `github.com/nakagami/grdp` 和 `backend/third_party` |
|
||||
| Dockerfile | 不再复制 third_party 模块 |
|
||||
| 新增模块 | `session_engine.go`、`session_connect.go`、`packet_io.go`、`fastpath_recv.go`、`bitmap_decode.go`、`input_fastpath.go`、`mppc.go`、`rle/` |
|
||||
| 自研会话激活 | `ConfirmActive` 后可选读 FontMap,发送 `SuppressOutput(ALLOW_DISPLAY_UPDATES)` 触发服务端推帧 |
|
||||
| 自研位图输出 | FastPath / SlowPath Bitmap + SurfaceCmd → PNG base64 → WebSocket `type:bitmap` |
|
||||
| 自研输入通道 | Fast-Path Input:扫描码键鼠、Unicode 剪贴板、`auto_exec`(Win+R 粘贴命令) |
|
||||
| 协议日志 | `RDP_DEBUG=1` 输出 `[RDP:TRACE]`,定位连接、bitmap、input 阶段 |
|
||||
|
||||
### 仍保留的探测 API(诊断用)
|
||||
|
||||
| 函数 | 用途 |
|
||||
|------|------|
|
||||
| `ProbeHandshake` | X.224 安全协议协商 |
|
||||
| `ProbeCredSSP` | NLA 凭据验证 |
|
||||
| `ProbeMCSConnect` | MCS/GCC 握手探测(读完即关连接) |
|
||||
| `ProbeSessionFrames` | 建立会话后统计位图矩形数量 |
|
||||
|
||||
### 已知限制 / 待完善
|
||||
|
||||
- `mcs_probe.go` 超 700 行红线,后续需按 MCS/GCC/PDU 拆文件
|
||||
- 未实现 cliprdr 虚拟通道双向同步、RDPGFX/H.264、动态虚拟通道
|
||||
- 部分 Windows 主机在频繁探测后可能 `connection reset`(目标机策略/限流,非 NLA 失败)
|
||||
|
||||
---
|
||||
|
||||
## 架构与调用链
|
||||
|
||||
```
|
||||
RdpDialog.tsx
|
||||
│ WebSocket /ws/rdp?id=&width=&height=&token=
|
||||
▼
|
||||
controller/rdp.go
|
||||
│ loadRDPCredential() → engine.Run()
|
||||
▼
|
||||
pkg/rdp/engine.go → SessionEngine
|
||||
├── session_engine.go readLoop + handleEvent
|
||||
├── session_connect.go OpenSession (CredSSP + MCS + SuppressOutput)
|
||||
├── packet_io.go TPKT / FastPath 读写
|
||||
├── fastpath_recv.go 服务端更新解析
|
||||
├── bitmap_decode.go RLE/翻转 → PNG
|
||||
├── input_fastpath.go 键鼠 / 剪贴板 / auto_exec
|
||||
├── mppc.go MPPC 解压
|
||||
└── rle/ 位图 RLE 解压算法
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 后端文件清单
|
||||
|
||||
### 协议栈 `backend/pkg/rdp/`
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `engine.go` | `Engine` 接口、`RDP_ENGINE` 引擎选择 |
|
||||
| `client.go` | `ProbeHandshake` / `ProbeTLSHandshake` / `ProbeCredSSP` |
|
||||
| `session_engine.go` | 图形会话主循环、帧推送、输入分发 |
|
||||
| `session_connect.go` | `OpenSession`、SuppressOutput 激活 |
|
||||
| `mcs_probe.go` | MCS Connect、GCC、ClientInfo、ConfirmActive 构建与探测 |
|
||||
| `packet_io.go` | `ReadWirePacket`、`WriteFastPathInput` |
|
||||
| `fastpath_recv.go` | FastPath 更新、MPPC、SurfaceCmd 解析 |
|
||||
| `bitmap_decode.go` | 位图解码与 PNG 编码 |
|
||||
| `input_fastpath.go` | Fast-Path Input 事件编码 |
|
||||
| `mppc.go` | MPPC-64K 解压器 |
|
||||
| `keymap.go` | 浏览器 `KeyboardEvent.code` → RDP 扫描码 |
|
||||
| `transport/` | TPKT、X.224、TLS |
|
||||
| `credssp/` | CredSSP + NTLMv2 + ASN.1 |
|
||||
| `rle/` | RLE 位图解压(`DecompressInto`) |
|
||||
|
||||
### 控制器 / 模型
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `controller/rdp.go` | WebSocket 会话、审计录制 |
|
||||
| `controller/rdp_session.go` | 会话列表 / 回放 / 终止 API |
|
||||
| `controller/rdp_file.go` | 文件 list/upload/download/stage |
|
||||
| `controller/rdp_cred.go` | 凭据加载辅助 |
|
||||
| `model/rdp_session.go` | `RDPSession` 表 |
|
||||
|
||||
### 文件传输
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `pkg/winsmb/` | SMB 管理共享直传 `C:\Users\Public\AIOpsShare\Download` |
|
||||
| `pkg/rdpfilestage/` | 服务端暂存 + 一次性 token |
|
||||
| `frontend/.../RdpToolsPanel.tsx` | 工具面板(通用命令 / 文件 Tab) |
|
||||
| `frontend/.../rdpConstants.ts` | 虚拟 Download 盘路径常量 |
|
||||
|
||||
---
|
||||
|
||||
## 前端
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `RdpDialog.tsx` | Canvas 渲染、WebSocket、键鼠捕获、工具栏 |
|
||||
| `RdpToolsPanel.tsx` | 剪贴板同步、文件上传下载、auto_exec 回退 |
|
||||
| `api/server/rdp-files.ts` | 文件传输 REST |
|
||||
| `api/audit/rdp.ts` | 会话审计 API |
|
||||
|
||||
---
|
||||
|
||||
## WebSocket 帧协议
|
||||
|
||||
### 服务端 → 浏览器
|
||||
|
||||
```json
|
||||
{ "type": "session", "message": "123", "width": 1280, "height": 800 }
|
||||
{ "type": "status", "message": "正在连接 RDP ..." }
|
||||
{ "type": "error", "message": "..." }
|
||||
{ "type": "bitmap", "x": 0, "y": 0, "width": 64, "height": 64, "image": "data:image/png;base64,..." }
|
||||
{ "type": "clipboard","text": "远端剪贴板内容" }
|
||||
```
|
||||
|
||||
### 浏览器 → 服务端
|
||||
|
||||
```json
|
||||
{ "type": "mouse_move", "x": 100, "y": 200 }
|
||||
{ "type": "mouse_down", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "mouse_up", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "wheel", "x": 100, "y": 200, "delta_y": -120 }
|
||||
{ "type": "key", "code": "KeyA", "key": "a", "down": true }
|
||||
{ "type": "clipboard", "text": "本地剪贴板" }
|
||||
{ "type": "auto_exec", "text": "C:\\Users\\Public\\AIOpsShare\\Download\\setup.exe" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CredSSP 多策略重试
|
||||
|
||||
每条策略使用**新建 TCP 连接**(`client.go`):
|
||||
|
||||
1. `v6 + binding`(默认)
|
||||
2. `v6 + binding + UseCBT`
|
||||
3. `v6 + binding + ForceMIC`
|
||||
4. `v2 + raw`(兼容旧服务端)
|
||||
|
||||
关键修复:`credssp.subjectPublicKey()` 使用证书 `SubjectPublicKey` 原始 ASN.1,而非 PKIX SPKI。
|
||||
|
||||
---
|
||||
|
||||
## 外部项目借鉴边界
|
||||
|
||||
| 项目 | 可借鉴点 | 不直接采用原因 |
|
||||
|------|----------|----------------|
|
||||
| FreeRDP | 协议完整性、通道、编解码、客户端能力矩阵 | C 生态和体量过大,不适合直接嵌入 |
|
||||
| Apache Guacamole | Web 网关、协议代理、录制与前端桥接分层 | 依赖 guacd/C,不符合单 Go 后端目标 |
|
||||
| IronRDP | 现代模块化、状态机测试、安全边界 | Rust 生态,适合作为测试和分层参考 |
|
||||
| Teleport | 身份、RBAC、审计、Desktop Gateway 架构 | 产品目标偏堡垒机,不是轻量 RDP client |
|
||||
| tomatome/grdp / nakagami/grdp | Go 协议状态机、能力集、bitmap/input 参考 | 第三方实现已移除,仅保留 `/opt` 备份用于协议对照 |
|
||||
|
||||
## 第三方代码归档与文档
|
||||
|
||||
归档与变更记录目录:
|
||||
|
||||
```
|
||||
/opt/rdp/
|
||||
├── CHANGELOG.md # 变更记录
|
||||
├── README.md # 索引
|
||||
├── docs/rdp.md # 完整说明(与本文档同步)
|
||||
└── third-party/
|
||||
├── nakagami-grdp-v0.8.7/
|
||||
├── rdp-web-refs/
|
||||
└── grdpprobe/
|
||||
```
|
||||
|
||||
> 第三方源码只能作为协议行为和工程分层参考;涉及 GPL 的项目不得直接搬运实现。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 验证命令
|
||||
|
||||
```bash
|
||||
# 编译
|
||||
cd backend && CGO_ENABLED=1 go build -o aiops .
|
||||
|
||||
# NLA 探测
|
||||
go run /tmp/rdp_cred_probe.go # ProbeCredSSP
|
||||
|
||||
# 会话位图统计(需可达的 Windows:3389)
|
||||
go run /tmp/rdp_session_probe_main.go
|
||||
|
||||
# Docker 端到端
|
||||
./run.sh docker up
|
||||
# 浏览器:服务器管理 → Windows 主机 → RDP 连接
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相关路由
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `GET /ws/rdp` | WebSocket 图形会话 |
|
||||
| `GET /api/v1/rdp-sessions` | 会话审计列表 |
|
||||
| `GET /api/v1/servers/:id/rdp-files` | 远端文件列表 |
|
||||
| `POST /api/v1/servers/:id/rdp-files/upload` | 上传 |
|
||||
| `GET /api/v1/servers/:id/rdp-files/download` | 下载 |
|
||||
@@ -0,0 +1,454 @@
|
||||
# AIOps Web RDP 远程桌面
|
||||
|
||||
> 作者:Logan.Li
|
||||
> 更新:2026-07-05
|
||||
> 归档副本:`/opt/rdp/docs/rdp.md` · 变更记录:`/opt/rdp/CHANGELOG.md`
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
AIOps RDP 是内置在浏览器里的 Windows 远程桌面能力,采用“后端 Go 自研 RDP 协议栈 + WebSocket + 前端 Canvas”的方式实现。当前功能已从远程桌面连接推进到可用堡垒机阶段,支持图形会话、键鼠输入、双向剪贴板、RDPDR 虚拟磁盘、文件上传下载、会话录像回放、实时审计和全局会话安全策略。
|
||||
|
||||
当前 RDP 不再依赖 SMB、445 端口或第三方 RDP 实现。文件传输走 RDP 自身的 `rdpdr` 设备重定向通道,远端资源管理器中显示为 `Go-client 上的 AIOps`,UNC 路径为 `\\Go-client\AIOps`。// Logan.Li
|
||||
|
||||
## 文档阅读路径
|
||||
|
||||
本目录按“能让其他项目复刻实现”的标准组织:
|
||||
|
||||
| 文档 | 适合读者 | 内容 |
|
||||
|------|----------|------|
|
||||
| [README.md](./README.md) | 产品、架构、联调人员 | 功能范围、调用链、文件清单、现有限制 |
|
||||
| [implementation-spec.md](./implementation-spec.md) | 后端、前端、测试工程师 | 从零实现 RDP Web 网关所需的协议分层、状态机、WebSocket 帧、剪贴板、虚拟盘、审计和验收用例 |
|
||||
|
||||
如果要在另一个项目中实现同类能力,建议先按 `implementation-spec.md` 搭出最小闭环,再回到本文档对照 AIOps 的实际文件落点。// Logan.Li
|
||||
|
||||
| 能力 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| RDP 图形连接 | 已完成 | 自研 X.224 / TLS / CredSSP / MCS / FastPath / Bitmap |
|
||||
| 键鼠输入 | 已完成 | Fast-Path Input,支持组合键审计与服务端修饰键补齐 |
|
||||
| 双向剪贴板 | 已完成 | `cliprdr` 通道 + 工具按钮 + Ctrl/Command 快捷键 |
|
||||
| 文件上传 | 已完成 | 浏览器选择/拖拽文件,暂存到远端虚拟盘 `Upload` |
|
||||
| 文件下载 | 已完成 | 远端文件复制到虚拟盘 `Download`,浏览器自动下载 |
|
||||
| 会话录像 | 已完成 | RDP bitmap 快照录制,过滤启动黑帧,支持历史回放 |
|
||||
| 实时审计 | 已完成 | 支持 RDP 实时画面订阅 `/ws/rdp-audit/:sessionId` |
|
||||
| 会话水印 | 已完成 | RDP/SSH 会话显示当前登录用户水印 |
|
||||
| 会话策略 | 已完成 | 水印、录像、剪贴板、上传、下载、命令策略、审批开关 |
|
||||
| SMB 文件传输 | 已移除 | 不再使用 445 端口和管理共享 |
|
||||
|
||||
---
|
||||
|
||||
## 用户体验
|
||||
|
||||
### 入口
|
||||
|
||||
1. 进入 `服务器管理`。
|
||||
2. Windows / RDP 资产点击终端按钮。
|
||||
3. 弹出 RDP 图形窗口。
|
||||
4. 顶部工具图标打开 RDP 工具面板。
|
||||
|
||||
### RDP 窗口
|
||||
|
||||
| 区域 | 说明 |
|
||||
|------|------|
|
||||
| 顶栏 | 显示资产名称、IP、连接状态、工具、最大化、关闭 |
|
||||
| Canvas | 渲染远端桌面图形帧 |
|
||||
| 水印层 | 显示当前登录用户,例如 `admin` |
|
||||
| 工具面板 | 剪贴板同步、虚拟盘上传说明、上传进度 |
|
||||
|
||||
13 寸屏幕默认使用较小窗口尺寸,避免看不到窗口边框和工具栏。最大化时前端只缩放现有 Canvas,不重建 RDP 会话,避免登录阶段闪屏。// Logan.Li
|
||||
|
||||
### 虚拟盘体验
|
||||
|
||||
远端 Windows 中应看到:
|
||||
|
||||
```text
|
||||
此电脑
|
||||
└── Go-client 上的 AIOps
|
||||
├── Upload
|
||||
└── Download
|
||||
```
|
||||
|
||||
| 目录 | 用途 |
|
||||
|------|------|
|
||||
| `Upload` | 浏览器上传的文件会出现在这里,远端可复制到桌面或目标目录 |
|
||||
| `Download` | 远端文件复制/拖入这里后,AIOps 捕获并触发本地浏览器下载 |
|
||||
|
||||
---
|
||||
|
||||
## 架构与调用链
|
||||
|
||||
```text
|
||||
frontend/src/components/server/RdpDialog.tsx
|
||||
│ WebSocket /ws/rdp?id=&width=&height=&token=
|
||||
│ Canvas 渲染、键鼠捕获、快捷键、下载触发
|
||||
▼
|
||||
backend/controller/rdp.go
|
||||
│ 创建 RDPSession、加载会话策略、审计、录屏、WebSocket frame sink
|
||||
│ loadRDPCredential() → rdp.Engine.Run()
|
||||
▼
|
||||
backend/pkg/rdp/session_engine.go
|
||||
│ OpenSession() → readLoop() → handleEvent()
|
||||
├── 图形帧:FastPath / SlowPath Bitmap → RGBA → WebSocket binary
|
||||
├── 输入:浏览器 key/mouse/wheel → Fast-Path Input
|
||||
├── 剪贴板:cliprdr 静态虚拟通道
|
||||
└── 文件:rdpdr 静态虚拟通道
|
||||
```
|
||||
|
||||
文件下载链路:
|
||||
|
||||
```text
|
||||
Windows 文件 → \\Go-client\AIOps\Download
|
||||
▼
|
||||
rdpdr Device I/O 写入捕获
|
||||
▼
|
||||
pkg/rdp/filestage.Put(name, data, 30min)
|
||||
▼
|
||||
WebSocket frame: { type: "rdp_file_download", name, url }
|
||||
▼
|
||||
浏览器访问 /api/v1/rdp-staged/:token 自动下载
|
||||
```
|
||||
|
||||
文件上传链路:
|
||||
|
||||
```text
|
||||
浏览器选择/拖拽文件
|
||||
▼
|
||||
RdpToolsPanel FileReader 读取为 base64
|
||||
▼
|
||||
WebSocket event: { type: "rdp_file_upload", name, text }
|
||||
▼
|
||||
rdpdr.Handler.AddUpload()
|
||||
▼
|
||||
Windows 资源管理器读取 \\Go-client\AIOps\Upload\文件名
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 后端文件清单
|
||||
|
||||
### 协议栈 `backend/pkg/rdp/`
|
||||
|
||||
| 文件 / 目录 | 说明 |
|
||||
|-------------|------|
|
||||
| `engine.go` | `Engine` 接口,当前固定使用自研 `SessionEngine` |
|
||||
| `session_engine.go` | 图形会话主循环、输入事件、虚拟通道分发、黑帧过滤 |
|
||||
| `session_connect.go` | `OpenSession`、CredSSP、MCS、SuppressOutput 激活 |
|
||||
| `mcs_probe.go` | MCS/GCC/ClientInfo/ConfirmActive 构建与探测 |
|
||||
| `packet_io.go` | TPKT / FastPath 读写 |
|
||||
| `fastpath_recv.go` | FastPath 更新、SurfaceCmd、MPPC 解压入口 |
|
||||
| `bitmap_decode.go` | Bitmap/RLE 解码与 RGBA 转换 |
|
||||
| `input_fastpath.go` | 键盘、鼠标、滚轮 Fast-Path Input 编码 |
|
||||
| `keymap.go` | 浏览器 `KeyboardEvent.code` 到 RDP 扫描码映射 |
|
||||
| `mppc.go` | MPPC-64K 解压器 |
|
||||
| `transport/` | TPKT、X.224、TLS |
|
||||
| `credssp/` | CredSSP + NTLMv2 + ASN.1 |
|
||||
| `rle/` | 位图 RLE 解压 |
|
||||
| `channels/cliprdr/` | RDP 剪贴板静态虚拟通道 |
|
||||
| `channels/rdpdr/` | RDP 设备重定向虚拟磁盘 |
|
||||
| `filestage/` | Download 捕获文件的内存暂存与 token |
|
||||
| `trace/` | `RDP_DEBUG=1` 协议级调试日志 |
|
||||
|
||||
### 控制器 / 模型
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `backend/controller/rdp.go` | WebSocket 会话、策略拦截、审计、录屏、实时广播 |
|
||||
| `backend/controller/rdp_session.go` | RDP 会话列表、详情、回放、终止、录像下载 |
|
||||
| `backend/controller/rdp_staged.go` | RDPDR Download 捕获文件的浏览器下载入口 |
|
||||
| `backend/controller/rdp_cred.go` | RDP 凭据加载与解密 |
|
||||
| `backend/model/rdp_session.go` | `RDPSession` 表 |
|
||||
| `backend/model/audit_setting.go` | 会话安全策略字段 |
|
||||
|
||||
### 前端
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `frontend/src/components/server/RdpDialog.tsx` | RDP 弹窗、Canvas、WebSocket、键鼠、下载触发 |
|
||||
| `frontend/src/components/server/RdpToolsPanel.tsx` | 剪贴板、上传、虚拟盘说明、上传进度 |
|
||||
| `frontend/src/components/common/SessionWatermark.tsx` | 会话水印 |
|
||||
| `frontend/src/components/system/RdpAuditViewer.tsx` | RDP 历史回放 / 实时审计查看 |
|
||||
| `frontend/src/components/system/SystemSettingsTab.tsx` | 会话安全策略开关入口 |
|
||||
| `frontend/src/store/index.ts` | `useSessionSecurityStore` 前端策略缓存 |
|
||||
|
||||
---
|
||||
|
||||
## WebSocket 协议
|
||||
|
||||
### 服务端到浏览器
|
||||
|
||||
JSON frame:
|
||||
|
||||
```json
|
||||
{ "type": "session", "message": "123", "width": 900, "height": 596 }
|
||||
{ "type": "status", "message": "正在连接 RDP 1.2.3.4:3389" }
|
||||
{ "type": "error", "message": "连接失败" }
|
||||
{ "type": "clipboard", "text": "远端剪贴板内容" }
|
||||
{ "type": "rdp_file_download", "name": "a.txt", "url": "/api/v1/rdp-staged/<token>" }
|
||||
```
|
||||
|
||||
Binary frame:
|
||||
|
||||
```text
|
||||
magic: "RDPR"
|
||||
x: uint32 little-endian
|
||||
y: uint32 little-endian
|
||||
width: uint32 little-endian
|
||||
height: uint32 little-endian
|
||||
rgba: width * height * 4
|
||||
```
|
||||
|
||||
历史回放仍使用 JSON `bitmap` 快照:
|
||||
|
||||
```json
|
||||
{ "type": "bitmap", "x": 0, "y": 0, "width": 900, "height": 596, "image": "data:image/png;base64,..." }
|
||||
```
|
||||
|
||||
### 浏览器到服务端
|
||||
|
||||
```json
|
||||
{ "type": "mouse_move", "x": 100, "y": 200 }
|
||||
{ "type": "mouse_down", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "mouse_up", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "wheel", "x": 100, "y": 200, "delta_y": -120 }
|
||||
{ "type": "key", "code": "KeyA", "key": "a", "down": true, "ctrl": false }
|
||||
{ "type": "clipboard", "text": "本地剪贴板内容" }
|
||||
{ "type": "remote_copy" }
|
||||
{ "type": "rdp_file_upload", "name": "php.ini", "text": "base64..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 剪贴板机制
|
||||
|
||||
| 方向 | 入口 | 机制 |
|
||||
|------|------|------|
|
||||
| 本地到远端 | 工具面板“粘贴到远端”、Canvas 粘贴、Ctrl/Command+V | 浏览器读取本地剪贴板,发送 `clipboard` event,后端写入 RDP `cliprdr` |
|
||||
| 远端到本地 | 工具面板“复制远端内容”、远端 Ctrl+C/Ctrl+X | 后端发送/识别复制事件,通过 `cliprdr` 请求远端文本并回传浏览器 |
|
||||
|
||||
注意:
|
||||
|
||||
- 浏览器读取/写入系统剪贴板受浏览器权限限制,需要页面处于用户交互上下文。
|
||||
- 远端 Ctrl+C/Ctrl+X 会按普通键盘事件发送给 Windows,同时后端延迟请求远端剪贴板文本。
|
||||
- 审计会记录 `[RDP复制] Ctrl+C`、`[RDP剪切] Ctrl+X`、`[RDP粘贴] ...`、`[RDP粘贴快捷键] Ctrl+V`。
|
||||
|
||||
---
|
||||
|
||||
## RDPDR 虚拟磁盘
|
||||
|
||||
RDPDR 使用静态虚拟通道 `rdpdr`,实现最小可用的磁盘设备重定向:
|
||||
|
||||
| 项目 | 当前值 |
|
||||
|------|--------|
|
||||
| Client Name | `Go-client` |
|
||||
| Drive Name | `AIOps` |
|
||||
| UNC | `\\Go-client\AIOps` |
|
||||
| 远端展示 | `Go-client 上的 AIOps` |
|
||||
| 固定目录 | `Upload`、`Download` |
|
||||
|
||||
### Upload
|
||||
|
||||
上传流程:
|
||||
|
||||
1. 用户在 RDP 工具面板选择或拖拽本地文件。
|
||||
2. 前端读取文件并通过 WebSocket 发送到后端。
|
||||
3. 后端加入 rdpdr upload map。
|
||||
4. 远端资源管理器进入 `\\Go-client\AIOps\Upload` 后可看到文件。
|
||||
5. 用户可在远端复制到桌面或目标目录。
|
||||
|
||||
当前前端单文件上传限制为 50 MB。上传进度表示浏览器读取和发送到 WebSocket 的进度,不是远端 Windows 复制进度。
|
||||
|
||||
### Download
|
||||
|
||||
下载流程:
|
||||
|
||||
1. 用户在远端 Windows 中把文件复制/拖入 `\\Go-client\AIOps\Download`。
|
||||
2. rdpdr 捕获远端写入数据。
|
||||
3. 后端将文件暂存到 `filestage`,默认 30 分钟有效。
|
||||
4. 后端向浏览器发送 `rdp_file_download` frame。
|
||||
5. 浏览器自动访问 `/api/v1/rdp-staged/:token` 下载到本地下载目录。
|
||||
|
||||
### 不再使用 SMB
|
||||
|
||||
已移除 SMB 直传和 445 端口依赖。原因:
|
||||
|
||||
- 外网访问 Windows 445 通常不可达或不安全。
|
||||
- 管理共享需要额外权限,部署和排障成本高。
|
||||
- 堡垒机场景更适合走 RDP 自身的设备重定向能力。
|
||||
|
||||
---
|
||||
|
||||
## 会话安全策略
|
||||
|
||||
入口:
|
||||
|
||||
```text
|
||||
系统管理 → 系统设置 → 安全 → 会话设置
|
||||
```
|
||||
|
||||
策略字段存储在 `sys_audit_settings`:
|
||||
|
||||
| 字段 | 默认 | 影响 |
|
||||
|------|------|------|
|
||||
| `session_watermark_enabled` | true | RDP/SSH 会话显示当前用户水印 |
|
||||
| `session_record_enabled` | true | 是否保存 RDP 录像数据 |
|
||||
| `clipboard_enabled` | true | 是否允许剪贴板同步 |
|
||||
| `file_upload_enabled` | true | 是否允许 RDP 文件上传 |
|
||||
| `file_download_enabled` | true | 是否允许 RDP 文件下载 |
|
||||
| `command_policy_enabled` | false | 命令策略预留开关 |
|
||||
| `approval_required` | false | 访问审批预留开关 |
|
||||
|
||||
RDP 后端在 WebSocket 读事件时会执行策略拦截:
|
||||
|
||||
| 事件 | 策略 |
|
||||
|------|------|
|
||||
| `clipboard` / `remote_copy` | `clipboard_enabled` |
|
||||
| `file_upload` / `rdp_file_upload` | `file_upload_enabled` |
|
||||
| `file_download` | `file_download_enabled` |
|
||||
|
||||
前端也会同步禁用对应按钮,但后端拦截才是最终边界。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 审计与回放
|
||||
|
||||
### 会话录像
|
||||
|
||||
RDP 会话关闭时写入 `rdp_sessions.record_data` 和 `record_size`。录制策略:
|
||||
|
||||
- 非 bitmap 状态帧直接记录。
|
||||
- bitmap 帧通过后端 framebuffer 合成整屏快照。
|
||||
- 启动阶段黑屏帧会被过滤,减少历史回放黑屏片段。
|
||||
- 快照最小间隔约 500ms,避免小块 bitmap 过密导致回放碎片化。
|
||||
- 关闭 `session_record_enabled` 后不写入录像数据。
|
||||
|
||||
### 操作审计
|
||||
|
||||
RDP 输入会进入 `session_commands`,用于统一审计:
|
||||
|
||||
| 类型 | 示例 |
|
||||
|------|------|
|
||||
| 文本输入 | `[RDP输入] hello` |
|
||||
| 快捷键 | `[RDP复制] Ctrl+C`、`[RDP剪切] Ctrl+X` |
|
||||
| 粘贴 | `[RDP粘贴] http://localhost:8080/servers` |
|
||||
| 上传 | `[RDP上传到虚拟盘] php.ini` |
|
||||
| 下载 | `[RDP下载] a.txt` |
|
||||
|
||||
鼠标移动、点击轨迹只进入图形录像,不写命令审计,避免审计列表噪声。
|
||||
|
||||
### 实时审计
|
||||
|
||||
活动 RDP 会话可通过 `/ws/rdp-audit/:sessionId` 实时订阅图形帧,供审计页面查看在线会话画面。
|
||||
|
||||
---
|
||||
|
||||
## 闪屏与画面优化
|
||||
|
||||
当前已做的优化:
|
||||
|
||||
| 问题 | 处理 |
|
||||
|------|------|
|
||||
| 登录初期黑屏闪烁 | 后端跳过启动阶段纯黑全屏 bitmap |
|
||||
| 最大化导致重连 | 前端只缩放 Canvas,不重建 WebSocket |
|
||||
| 回放很多小块 | 后端 framebuffer 合成整屏快照再录制 |
|
||||
| 高频 mousemove 堆积 | 前端 16ms 合并移动事件,后端队列优先保留关键输入 |
|
||||
| 13 寸屏看不到工具栏 | 默认窗口缩小,最大化再使用全屏空间 |
|
||||
|
||||
---
|
||||
|
||||
## 环境变量与调试
|
||||
|
||||
| 环境变量 | 行为 |
|
||||
|----------|------|
|
||||
| `RDP_DEBUG=1` | 打开 `[RDP:TRACE]` 协议级日志 |
|
||||
| `RDP_ENGINE` | 已不再参与引擎选择,保留也不会改变行为 |
|
||||
|
||||
常用日志过滤:
|
||||
|
||||
```bash
|
||||
tail -f logs/aiops-local.log | rg "RDP|rdpdr|cliprdr"
|
||||
```
|
||||
|
||||
常见日志含义:
|
||||
|
||||
| 日志 | 含义 |
|
||||
|------|------|
|
||||
| `[RDP] ws connected` | 浏览器 RDP WebSocket 已连接 |
|
||||
| `[RDP] engine start` | 后端开始连接远端 3389 |
|
||||
| `[RDP] bitmap session=...` | 收到并发送图形帧 |
|
||||
| `[rdpdr] device list announced` | 虚拟盘设备已公告 |
|
||||
| `[rdpdr] upload staged` | 文件已进入虚拟盘 Upload |
|
||||
| `[rdpdr] download captured` | 远端写入 Download 已被捕获 |
|
||||
| `[cliprdr]` | 剪贴板虚拟通道交互 |
|
||||
|
||||
---
|
||||
|
||||
## 路由
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `GET /ws/rdp` | RDP 图形会话 WebSocket |
|
||||
| `GET /ws/rdp-audit/:sessionId` | RDP 活动会话实时审计 WebSocket |
|
||||
| `GET /api/v1/rdp-staged/:token` | RDPDR Download 暂存文件下载 |
|
||||
| `GET /api/v1/rdp-sessions` | RDP 会话列表 |
|
||||
| `GET /api/v1/rdp-sessions/:id` | RDP 会话详情 |
|
||||
| `GET /api/v1/rdp-sessions/:id/record` | RDP 录像数据 |
|
||||
| `GET /api/v1/rdp-sessions/:id/download` | RDP 录像下载 |
|
||||
| `POST /api/v1/rdp-sessions/:id/kill` | 终止活动 RDP 会话 |
|
||||
| `POST /api/v1/rdp-sessions/:id/replay-view` | 记录回放查看行为 |
|
||||
| `GET /api/v1/session-commands?session_type=rdp` | RDP 输入/文件/剪贴板操作审计 |
|
||||
| `GET /api/v1/admin/audit-settings` | 获取会话安全策略 |
|
||||
| `PUT /api/v1/admin/audit-settings` | 保存会话安全策略 |
|
||||
|
||||
---
|
||||
|
||||
## 当前限制
|
||||
|
||||
| 限制 | 说明 |
|
||||
|------|------|
|
||||
| 大文件上传 | 前端当前限制 50 MB,尚未实现分片、断点续传 |
|
||||
| 大文件下载 | 当前以内存暂存 token 方式实现,尚未实现断点下载 |
|
||||
| RDPGFX/H.264 | 未实现,当前以 bitmap/RLE/SurfaceCmd 为主 |
|
||||
| 动态虚拟通道 | `drdynvc` 当前识别但不处理 |
|
||||
| 复杂文件操作 | 重命名、属性编辑、复杂 Explorer 操作仍需继续压测 |
|
||||
| TLS 指纹 | RDP 自签证书当前 `InsecureSkipVerify`,后续可做 TOFU 指纹落库 |
|
||||
| 细粒度授权 | 当前是全局会话策略,尚未做到用户/角色/资产/协议维度的 ABAC |
|
||||
|
||||
---
|
||||
|
||||
## 验证清单
|
||||
|
||||
### 构建
|
||||
|
||||
```bash
|
||||
cd frontend && npx tsc --noEmit
|
||||
cd backend && env GOSUMDB=sum.golang.org GOTOOLCHAIN=auto go test ./pkg/rdp/... ./controller
|
||||
./run.sh build
|
||||
```
|
||||
|
||||
### 手动验证
|
||||
|
||||
1. 登录 `http://localhost:8080`。
|
||||
2. 进入 `服务器管理`,打开 Windows 资产 RDP。
|
||||
3. 确认 RDP 桌面画面稳定,无明显登录黑屏闪烁。
|
||||
4. 确认远端 `此电脑` 能看到 `Go-client 上的 AIOps`。
|
||||
5. 工具面板上传文件,远端进入 `\\Go-client\AIOps\Upload` 可打开/复制。
|
||||
6. 远端文件复制到 `\\Go-client\AIOps\Download`,浏览器触发下载。
|
||||
7. 本地复制文本后在远端 Ctrl+V 或工具面板粘贴。
|
||||
8. 远端选中文本 Ctrl+C 后,工具面板复制远端内容并在本地粘贴。
|
||||
9. 进入 `系统管理 → 系统设置 → 安全 → 会话设置`,关闭剪贴板/上传/下载后验证前端按钮禁用和后端策略拦截。
|
||||
10. 进入审计页面查看 RDP 会话、操作审计和历史回放。
|
||||
|
||||
---
|
||||
|
||||
## 外部项目借鉴边界
|
||||
|
||||
| 项目 | 可借鉴点 | 不直接采用原因 |
|
||||
|------|----------|----------------|
|
||||
| FreeRDP | 协议完整性、通道、编解码、客户端能力矩阵 | C 生态和体量过大,不适合直接嵌入 |
|
||||
| Apache Guacamole | Web 网关、协议代理、录制与前端桥接分层 | 依赖 guacd/C,不符合单 Go 后端目标 |
|
||||
| IronRDP | 现代模块化、状态机测试、安全边界 | Rust 生态,适合作为测试和分层参考 |
|
||||
| Teleport | 身份、RBAC、审计、Desktop Gateway 架构 | 产品目标偏完整堡垒机,当前只借鉴分层思路 |
|
||||
| tomatome/grdp / nakagami/grdp | Go 协议状态机、能力集、bitmap/input 参考 | 第三方实现已移除,仅保留 `/opt` 备份用于协议对照 |
|
||||
|
||||
第三方源码只能作为协议行为和工程分层参考;涉及 GPL 的项目不得直接搬运实现。// Logan.Li
|
||||
@@ -0,0 +1,553 @@
|
||||
# AIOps RDP 实现规格
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 目标:让另一个项目仅阅读本文档,也能实现一个可用的 Web RDP 堡垒机会话。
|
||||
> 范围:RDP 图形连接、键鼠输入、剪贴板、RDPDR 虚拟盘、文件上传下载、录像回放、实时审计、安全策略。
|
||||
|
||||
---
|
||||
|
||||
## 1. 功能边界
|
||||
|
||||
### 1.1 必须实现
|
||||
|
||||
| 能力 | 验收标准 |
|
||||
|------|----------|
|
||||
| 图形登录 | 浏览器打开 Windows 桌面,登录后能稳定看到完整画面 |
|
||||
| 键鼠输入 | 鼠标移动/点击/滚轮、普通键、组合键能到达远端 |
|
||||
| 双向剪贴板 | 本地复制到远端、远端 Ctrl+C 后复制到浏览器 |
|
||||
| 文件上传 | 浏览器上传文件后,远端 `\\Go-client\AIOps\Upload` 可见 |
|
||||
| 文件下载 | 远端文件写入 `\\Go-client\AIOps\Download` 后,本地浏览器下载 |
|
||||
| 录像回放 | 会话结束后能按时间顺序播放图形快照 |
|
||||
| 实时审计 | 审计端可只读观看活动 RDP 会话 |
|
||||
| 安全策略 | 后端能强制禁用剪贴板、上传、下载、录像、水印 |
|
||||
|
||||
### 1.2 不建议作为第一版实现
|
||||
|
||||
| 能力 | 原因 |
|
||||
|------|------|
|
||||
| RDPGFX/H.264 | 协议复杂,先用 Bitmap/FastPath 能完成可用闭环 |
|
||||
| 动态虚拟通道 `drdynvc` | 文件和剪贴板可先用静态通道完成 |
|
||||
| SMB 文件传输 | 外网 445 经常不可达,权限和安全边界差 |
|
||||
| 远端任意路径浏览 | 容易变成文件管理器和权限绕过问题,先用虚拟盘 |
|
||||
| 断点续传 | 大文件体验优化,基础链路稳定后再做 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 端到端架构
|
||||
|
||||
```text
|
||||
Browser
|
||||
├── RdpDialog.tsx
|
||||
│ ├── Canvas 绘制 RDPR 二进制帧
|
||||
│ ├── 捕获 mouse/key/wheel/paste/drop
|
||||
│ └── 发送 JSON input event
|
||||
└── RdpToolsPanel.tsx
|
||||
├── 粘贴到远端
|
||||
├── 复制远端内容
|
||||
└── 文件上传
|
||||
|
||||
Backend HTTP/WebSocket
|
||||
├── /ws/rdp
|
||||
│ ├── JWT 鉴权
|
||||
│ ├── 加载资产和 RDP 凭据
|
||||
│ ├── 加载会话安全策略
|
||||
│ ├── 创建 sys_rdp_sessions
|
||||
│ ├── 启动 rdp.Engine.Run()
|
||||
│ └── 写入录像、审计、实时广播
|
||||
├── /ws/rdp-audit/:sessionId
|
||||
└── /api/v1/rdp-staged/:token
|
||||
|
||||
RDP Engine
|
||||
├── TCP + TPKT + X.224
|
||||
├── TLS + CredSSP/NLA
|
||||
├── MCS/GCC/Channel Join
|
||||
├── DemandActive/ConfirmActive/FontList/SuppressOutput
|
||||
├── FastPath Bitmap/SurfaceCmd
|
||||
├── FastPath Input
|
||||
├── cliprdr 静态虚拟通道
|
||||
└── rdpdr 静态虚拟通道
|
||||
```
|
||||
|
||||
核心分层原则:
|
||||
|
||||
1. 控制器负责鉴权、策略、审计、WebSocket、数据库。
|
||||
2. RDP 引擎只负责协议状态机和帧收发,不依赖 Gin/GORM/React。
|
||||
3. 前端只知道 WebSocket 帧协议,不知道 CredSSP、MCS、PDU 细节。
|
||||
4. 文件传输不走远端 SMB,不依赖远端开放 445。
|
||||
|
||||
---
|
||||
|
||||
## 3. 后端接口契约
|
||||
|
||||
### 3.1 连接配置
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
Domain string
|
||||
Security string
|
||||
Width int
|
||||
Height int
|
||||
Debug bool
|
||||
}
|
||||
```
|
||||
|
||||
实现要求:
|
||||
|
||||
- `Password` 只允许在服务端内存中存在,不返回给浏览器。
|
||||
- `Width`、`Height` 是远端桌面初始分辨率,不是浏览器 CSS 尺寸。
|
||||
- 浏览器最大化优先缩放 Canvas,不要重建 RDP 会话。
|
||||
|
||||
### 3.2 浏览器输入事件
|
||||
|
||||
```json
|
||||
{ "type": "mouse_move", "x": 100, "y": 200 }
|
||||
{ "type": "mouse_down", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "mouse_up", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "wheel", "x": 100, "y": 200, "delta_y": -120 }
|
||||
{ "type": "key", "code": "KeyC", "key": "c", "down": true, "ctrl": true }
|
||||
{ "type": "clipboard", "text": "from local" }
|
||||
{ "type": "remote_copy" }
|
||||
{ "type": "rdp_file_upload", "name": "php.ini", "text": "base64..." }
|
||||
```
|
||||
|
||||
处理规则:
|
||||
|
||||
- `mouse_move` 高频事件可丢弃或合并。
|
||||
- `mouse_down` / `mouse_up` / `key down` / `key up` 不允许丢弃。
|
||||
- `key` 事件必须带 `ctrl`、`alt`、`shift`、`meta`,后端根据状态补齐修饰键。
|
||||
- `remote_copy` 表示请求后端向远端剪贴板索取文本。
|
||||
- `rdp_file_upload.text` 是 base64 文件内容,第一版可限制 50 MB。
|
||||
|
||||
### 3.3 服务端帧
|
||||
|
||||
JSON 帧:
|
||||
|
||||
```json
|
||||
{ "type": "session", "message": "43", "width": 900, "height": 596 }
|
||||
{ "type": "status", "message": "正在连接 RDP 1.2.3.4:3389" }
|
||||
{ "type": "error", "message": "连接失败" }
|
||||
{ "type": "clipboard", "text": "from remote" }
|
||||
{ "type": "rdp_file_download", "name": "a.txt", "url": "/api/v1/rdp-staged/token" }
|
||||
```
|
||||
|
||||
二进制图形帧:
|
||||
|
||||
```text
|
||||
offset size type desc
|
||||
0 4 bytes magic, fixed "RDPR"
|
||||
4 4 uint32 LE x
|
||||
8 4 uint32 LE y
|
||||
12 4 uint32 LE width
|
||||
16 4 uint32 LE height
|
||||
20 n bytes RGBA, length = width * height * 4
|
||||
```
|
||||
|
||||
实现要求:
|
||||
|
||||
- 实时画面优先使用 `RDPR + RGBA`,避免 PNG/base64 带来的 CPU 和体积开销。
|
||||
- 历史回放可以保存 PNG data URL,便于持久化和跨版本播放。
|
||||
- 服务端发送图形帧前必须裁剪到桌面范围,避免前端越界写 Canvas。
|
||||
|
||||
---
|
||||
|
||||
## 4. RDP 协议状态机
|
||||
|
||||
### 4.1 连接阶段
|
||||
|
||||
```text
|
||||
TCP connect
|
||||
→ X.224 Connection Request
|
||||
→ X.224 Connection Confirm
|
||||
→ TLS handshake
|
||||
→ CredSSP/NLA
|
||||
→ MCS Connect Initial
|
||||
→ MCS Connect Response
|
||||
→ ErectDomainRequest
|
||||
→ AttachUserRequest/Confirm
|
||||
→ Channel Join: user + global + cliprdr + rdpdr
|
||||
→ Client Info
|
||||
→ DemandActive
|
||||
→ ConfirmActive
|
||||
→ Synchronize / Control Cooperate / Control Request Control
|
||||
→ FontList / FontMap
|
||||
→ SuppressOutput(ALLOW_DISPLAY_UPDATES)
|
||||
→ Bitmap/FastPath updates
|
||||
```
|
||||
|
||||
关键点:
|
||||
|
||||
- 每一次协议探测失败后,下一次重试要新建 TCP 连接。
|
||||
- CredSSP 的证书公钥绑定必须按服务端证书 `SubjectPublicKey` 做,不要误用完整 SPKI。
|
||||
- 收到 `DemandActive` 后才能发送 `ConfirmActive`,能力集必须包含图形、输入、虚拟通道相关能力。
|
||||
- `SuppressOutput(ALLOW_DISPLAY_UPDATES)` 对部分 Windows 主机很关键,否则登录后可能长时间无画面。
|
||||
|
||||
### 4.2 图形更新处理
|
||||
|
||||
```text
|
||||
FastPath/SlowPath packet
|
||||
→ parse update type
|
||||
→ bitmap/surface command
|
||||
→ MPPC decompress if compressed
|
||||
→ RLE decode if bitmap compressed
|
||||
→ convert BGRA/RGB565 to RGBA
|
||||
→ draw into backend framebuffer
|
||||
→ flush dirty rectangle
|
||||
→ send RDPR binary frame
|
||||
→ throttle snapshot into recorder
|
||||
```
|
||||
|
||||
实现要求:
|
||||
|
||||
- 后端维护完整 framebuffer,不能只保存最后一个小块,否则历史回放会碎片化。
|
||||
- 实时可发送脏矩形,录像建议按固定间隔保存合成后的快照。
|
||||
- 启动阶段纯黑全屏帧应过滤,减少登录闪屏和黑屏回放。
|
||||
|
||||
### 4.3 输入处理
|
||||
|
||||
| 浏览器事件 | RDP 行为 |
|
||||
|------------|----------|
|
||||
| `mouse_move` | Pointer move |
|
||||
| `mouse_down` | Pointer button down |
|
||||
| `mouse_up` | Pointer button up |
|
||||
| `wheel` | Wheel event |
|
||||
| `key down/up` | Scancode keyboard event |
|
||||
| `clipboard` | 写入 cliprdr 本地剪贴板并通知远端 |
|
||||
| `remote_copy` | 请求远端当前剪贴板文本 |
|
||||
|
||||
组合键规则:
|
||||
|
||||
- `Ctrl+C`、`Ctrl+X` 必须同时发修饰键和主键,不能把 `c`、`x` 当普通输入。
|
||||
- macOS 浏览器中的 `Command+C/V` 可映射成远端 `Ctrl+C/V`。
|
||||
- 后端审计应识别 `[RDP复制] Ctrl+C`、`[RDP剪切] Ctrl+X`、`[RDP粘贴快捷键] Ctrl+V`。
|
||||
|
||||
---
|
||||
|
||||
## 5. 剪贴板实现
|
||||
|
||||
### 5.1 本地到远端
|
||||
|
||||
```text
|
||||
Browser navigator.clipboard.readText()
|
||||
→ WebSocket { type: "clipboard", text }
|
||||
→ backend policy check clipboard_enabled
|
||||
→ cliprdr set local data
|
||||
→ send FormatList / FormatDataResponse
|
||||
→ Windows remote clipboard update
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
1. 本地复制 `hello`。
|
||||
2. 点击“粘贴到远端”或在 RDP Canvas 执行 Ctrl/Command+V。
|
||||
3. 远端文本框出现 `hello`。
|
||||
|
||||
### 5.2 远端到本地
|
||||
|
||||
```text
|
||||
User selects text in Windows
|
||||
→ remote Ctrl+C
|
||||
→ Windows updates remote clipboard
|
||||
→ cliprdr FormatList arrives
|
||||
→ backend requests CF_UNICODETEXT
|
||||
→ backend WebSocket { type: "clipboard", text }
|
||||
→ Browser navigator.clipboard.writeText(text)
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
1. 远端记事本输入 `rdp-copy-ok` 并选中。
|
||||
2. 在远端按 Ctrl+C。
|
||||
3. 点击“复制远端内容”或等待回传。
|
||||
4. 本地 Command+V 能粘贴 `rdp-copy-ok`。
|
||||
|
||||
常见失败:
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
|------|------|------|
|
||||
| 远端 Ctrl+C 只输入 c | 修饰键状态未发送或被前端拦截 | key event 带 `ctrl=true`,后端补发 Ctrl down/up |
|
||||
| 浏览器无法写入剪贴板 | 浏览器权限限制 | 必须在用户点击按钮的交互上下文执行 |
|
||||
| 工具提示远端剪贴板为空 | 未收到 FormatList 或请求过早 | Ctrl+C 后延迟 200-500ms 再请求 |
|
||||
|
||||
---
|
||||
|
||||
## 6. RDPDR 虚拟盘文件传输
|
||||
|
||||
### 6.1 远端展示
|
||||
|
||||
```text
|
||||
此电脑
|
||||
└── Go-client 上的 AIOps
|
||||
├── Upload
|
||||
└── Download
|
||||
```
|
||||
|
||||
固定命名:
|
||||
|
||||
| 项 | 值 |
|
||||
|----|----|
|
||||
| Client Name | `Go-client` |
|
||||
| Drive Name | `AIOps` |
|
||||
| UNC | `\\Go-client\AIOps` |
|
||||
| Upload | `\\Go-client\AIOps\Upload` |
|
||||
| Download | `\\Go-client\AIOps\Download` |
|
||||
|
||||
### 6.2 RDPDR 最小能力
|
||||
|
||||
必须处理:
|
||||
|
||||
1. Server Announce Request。
|
||||
2. Client Announce Reply。
|
||||
3. Client Name Request。
|
||||
4. Server Client ID Confirm。
|
||||
5. Client Device List Announce。
|
||||
6. Device Create/Open。
|
||||
7. Directory Query。
|
||||
8. File Read。
|
||||
9. File Write。
|
||||
10. Close。
|
||||
|
||||
虚拟文件系统模型:
|
||||
|
||||
```text
|
||||
/
|
||||
├── Upload/ # 后端提供给远端读
|
||||
│ └── user uploaded files
|
||||
└── Download/ # 远端写入,后端捕获
|
||||
└── remote copied files
|
||||
```
|
||||
|
||||
### 6.3 上传链路
|
||||
|
||||
```text
|
||||
Browser file input/drop
|
||||
→ FileReader base64
|
||||
→ WebSocket { type: "rdp_file_upload", name, text }
|
||||
→ policy check file_upload_enabled
|
||||
→ rdpdr.AddUpload(name, bytes)
|
||||
→ Explorer opens \\Go-client\AIOps\Upload
|
||||
→ Windows reads file from virtual disk
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
- 上传 `php.ini` 后,远端 `Upload` 目录出现文件。
|
||||
- 远端可以打开文件。
|
||||
- 远端可以复制到桌面或其他目录。
|
||||
|
||||
### 6.4 下载链路
|
||||
|
||||
```text
|
||||
Windows copies file to \\Go-client\AIOps\Download
|
||||
→ rdpdr Device Write
|
||||
→ backend captures bytes
|
||||
→ filestage.Put(name, data, ttl=30min)
|
||||
→ WebSocket { type: "rdp_file_download", name, url }
|
||||
→ Browser opens url
|
||||
→ /api/v1/rdp-staged/:token returns attachment
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
- 远端将 `a.txt` 拖入 `Download`。
|
||||
- 浏览器自动触发下载。
|
||||
- 本地下载文件内容与远端一致。
|
||||
|
||||
实现注意:
|
||||
|
||||
- 不要用 SMB 管理共享作为主路径。
|
||||
- 文件名必须清理路径分隔符,避免 `../`。
|
||||
- `filestage` token 必须有 TTL,下载后可删除或等待过期。
|
||||
- 大文件后续应改为分片和磁盘暂存,避免长期占用内存。
|
||||
|
||||
---
|
||||
|
||||
## 7. 审计、录像与安全策略
|
||||
|
||||
### 7.1 数据模型
|
||||
|
||||
RDP 会话表:
|
||||
|
||||
```text
|
||||
sys_rdp_sessions
|
||||
id
|
||||
user_id / username
|
||||
server_id / server_name / server_ip / server_port
|
||||
client_ip
|
||||
width / height
|
||||
status: active | closed | failed
|
||||
duration
|
||||
error
|
||||
record_data
|
||||
record_size
|
||||
start_time / end_time
|
||||
```
|
||||
|
||||
操作审计表:
|
||||
|
||||
```text
|
||||
sys_session_commands
|
||||
session_id
|
||||
session_type = rdp
|
||||
user_id / username
|
||||
server_id / server_name
|
||||
command
|
||||
output_size
|
||||
timestamp
|
||||
```
|
||||
|
||||
策略表:
|
||||
|
||||
```text
|
||||
sys_audit_settings
|
||||
session_watermark_enabled
|
||||
session_record_enabled
|
||||
clipboard_enabled
|
||||
file_upload_enabled
|
||||
file_download_enabled
|
||||
command_policy_enabled
|
||||
approval_required
|
||||
```
|
||||
|
||||
### 7.2 录像策略
|
||||
|
||||
- 实时帧可以是 RGBA 脏矩形。
|
||||
- 录像帧必须是合成快照,避免回放只看到小块。
|
||||
- 推荐最小间隔 500ms。
|
||||
- 过滤启动阶段纯黑全屏。
|
||||
- 关闭 `session_record_enabled` 后仍记录会话元数据,但不保存 `record_data`。
|
||||
|
||||
### 7.3 审计策略
|
||||
|
||||
建议记录:
|
||||
|
||||
```text
|
||||
[RDP输入] hello
|
||||
[RDP复制] Ctrl+C
|
||||
[RDP剪切] Ctrl+X
|
||||
[RDP粘贴] http://localhost:8080/servers
|
||||
[RDP上传到虚拟盘] php.ini
|
||||
[RDP下载] a.txt
|
||||
```
|
||||
|
||||
不建议记录:
|
||||
|
||||
- 每个 `mousemove`。
|
||||
- 每个 RGBA 实时帧。
|
||||
- 空白按键和无业务意义坐标。
|
||||
|
||||
### 7.4 策略拦截
|
||||
|
||||
前端禁用按钮只是体验优化,后端必须再次拦截:
|
||||
|
||||
| Event | 策略 |
|
||||
|-------|------|
|
||||
| `clipboard` | `clipboard_enabled` |
|
||||
| `remote_copy` | `clipboard_enabled` |
|
||||
| `rdp_file_upload` | `file_upload_enabled` |
|
||||
| `rdp_file_download` | `file_download_enabled` |
|
||||
| 录像写入 | `session_record_enabled` |
|
||||
|
||||
---
|
||||
|
||||
## 8. 前端实现要求
|
||||
|
||||
### 8.1 Canvas 绘制
|
||||
|
||||
```text
|
||||
WebSocket binary
|
||||
→ parse RDPR header
|
||||
→ create ImageData(width, height)
|
||||
→ imageData.data.set(rgba)
|
||||
→ canvas.putImageData(imageData, x, y)
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- Canvas 内部分辨率等于远端桌面分辨率。
|
||||
- CSS 尺寸可以缩放,但鼠标坐标必须换算回远端坐标。
|
||||
- 最大化和窗口缩放不应重新连接 RDP。
|
||||
|
||||
### 8.2 输入捕获
|
||||
|
||||
- RDP Canvas 必须可 focus,点击后接收键盘事件。
|
||||
- 捕获 `keydown` 和 `keyup`,都发给后端。
|
||||
- 浏览器默认快捷键如 Ctrl+C/Ctrl+V 需要按场景阻止默认行为。
|
||||
- macOS Command 复制粘贴要映射到远端 Ctrl。
|
||||
|
||||
### 8.3 工具面板
|
||||
|
||||
工具面板只保留高价值动作:
|
||||
|
||||
| 动作 | 说明 |
|
||||
|------|------|
|
||||
| 粘贴到远端 | 从浏览器剪贴板读取并写入远端 |
|
||||
| 复制远端内容 | 请求远端剪贴板并写入浏览器剪贴板 |
|
||||
| 上传文件 | 发送到虚拟盘 Upload |
|
||||
| 虚拟盘说明 | 展示 `Go-client 上的 AIOps`、Upload、Download 的用途 |
|
||||
|
||||
不要再展示 SMB、445、管理共享等文案。
|
||||
|
||||
---
|
||||
|
||||
## 9. 调试和验收
|
||||
|
||||
### 9.1 日志关键字
|
||||
|
||||
```bash
|
||||
tail -f logs/aiops-local.log | rg "RDP|cliprdr|rdpdr|RDPR"
|
||||
```
|
||||
|
||||
| 日志 | 含义 |
|
||||
|------|------|
|
||||
| `[RDP] ws connected` | 浏览器已连接 |
|
||||
| `[RDP] engine start` | 开始连接远端 3389 |
|
||||
| `[RDP] bitmap session=` | 收到图形帧 |
|
||||
| `[cliprdr]` | 剪贴板通道 |
|
||||
| `[rdpdr] device list announced` | 虚拟盘公告 |
|
||||
| `[rdpdr] upload staged` | 上传文件已暂存 |
|
||||
| `[rdpdr] download captured` | 下载文件已捕获 |
|
||||
|
||||
### 9.2 自动测试建议
|
||||
|
||||
| 层级 | 用例 |
|
||||
|------|------|
|
||||
| `credssp` | NTLMv2、公钥绑定、证书变体 |
|
||||
| `rle` | 8/15/16/24/32 bpp 位图解码 |
|
||||
| `display` | framebuffer 合成、脏矩形裁剪 |
|
||||
| `rdpdr` | 目录枚举、文件读写、非法路径 |
|
||||
| `filestage` | token TTL、并发下载、文件名清理 |
|
||||
| 前端 | RDPR 解析、坐标缩放、快捷键映射 |
|
||||
|
||||
### 9.3 手动验收矩阵
|
||||
|
||||
| 场景 | 预期 |
|
||||
|------|------|
|
||||
| 13 寸屏打开 RDP | 能看到边框、工具栏、Canvas |
|
||||
| 最大化窗口 | 不重连、不闪屏 |
|
||||
| 登录初期黑屏 | 回放中不出现大量黑屏段 |
|
||||
| 本地复制远端粘贴 | 成功 |
|
||||
| 远端复制本地粘贴 | 成功 |
|
||||
| 上传小文件 | `Upload` 可见、可打开、可复制 |
|
||||
| 远端下载文件 | 浏览器自动下载 |
|
||||
| 关闭剪贴板策略 | 前端禁用,后端拒绝 |
|
||||
| 关闭上传策略 | 前端禁用,后端拒绝 |
|
||||
| 实时审计 | 审计端可看到当前画面 |
|
||||
| 历史回放 | 画面完整,不是大量小块 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 常见问题
|
||||
|
||||
| 问题 | 优先排查 |
|
||||
|------|----------|
|
||||
| 只能看到 C 盘,没有 AIOps 虚拟盘 | `rdpdr` 是否完成 Device List Announce,Windows 是否刷新资源管理器 |
|
||||
| `\\tsclient\AIOps` 无权限 | 路径名/ClientName 不一致,或 rdpdr create/read/write 返回码错误 |
|
||||
| 远端 Ctrl+C 不能复制出来 | cliprdr FormatList/FormatDataRequest 是否到达,是否过早请求 |
|
||||
| 文件可打开但不能复制到桌面 | rdpdr Read/Close/QueryInfo 返回不完整,或文件属性/长度错误 |
|
||||
| 回放是小块或黑屏 | 没有 framebuffer 合成,或录像保存了实时脏矩形 |
|
||||
| 最大化闪屏 | 前端重建了 WebSocket,应只缩放 Canvas |
|
||||
| 上传大文件卡顿 | base64 + WebSocket 一次性发送,后续改分片 |
|
||||
@@ -0,0 +1,8 @@
|
||||
# 参考文档
|
||||
|
||||
本目录保存跨模块的接口和数据结构参考。模块级实现细节优先查看对应模块目录。// Logan.Li
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [api-reference.md](./api-reference.md) | HTTP API 概览 |
|
||||
| [database-schema.md](./database-schema.md) | 数据库表和模型概览 |
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,144 @@
|
||||
# 安全审计与堡垒机设计说明
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 范围:操作审计、登录日志、SSH/RDP/Pod/kubectl 会话、录像回放、命令审计、会话策略、审批联动。
|
||||
> 目标:把 AIOps 的终端、RDP、审计、策略和审批统一成“可用堡垒机 → 企业堡垒机”的工程体系。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 定位
|
||||
|
||||
安全审计与堡垒机模块解决三个问题:
|
||||
|
||||
1. 谁在什么时间访问了什么资产。
|
||||
2. 会话里做了什么操作,是否可回放、可追责。
|
||||
3. 高风险操作是否被策略限制、确认或审批。
|
||||
|
||||
当前 AIOps 已具备 WebSSH、RDP、Pod exec、kubectl 终端、操作审计、会话录像、水印、剪贴板/文件策略等能力。后续要做的是把这些能力从“分散功能”统一成“协议无关的会话安全模型”。
|
||||
|
||||
---
|
||||
|
||||
## 能力矩阵
|
||||
|
||||
| 能力 | 当前状态 | 说明 |
|
||||
|------|----------|------|
|
||||
| 操作审计 | 已完成 | 记录用户、模块、动作、资源、请求/响应、状态 |
|
||||
| 登录日志 | 已完成 | 记录登录用户、IP、User-Agent、结果 |
|
||||
| SSH 会话审计 | 已完成 | `sys_terminal_sessions` + asciicast 回放 |
|
||||
| SSH 命令审计 | 已完成 | `sys_session_commands` 记录命令和输出大小 |
|
||||
| RDP 会话审计 | 已完成 | `sys_rdp_sessions` + 图形帧录像 |
|
||||
| RDP 操作摘要 | 已完成 | 复制、粘贴、输入、上传、下载等写入命令审计 |
|
||||
| RDP 实时审计 | 已完成 | `/ws/rdp-audit/:sessionId` 只读观看 |
|
||||
| 会话水印 | 已完成 | RDP/SSH 显示当前用户水印 |
|
||||
| 会话策略 | 已完成 | 录像、剪贴板、上传、下载、审批预留 |
|
||||
| Pod/kubectl 审计 | 部分完成 | 终端可用,审计模型待独立统一 |
|
||||
| 审批联动 | 待增强 | 高危命令、生产资产、AI 操作需接入审批 |
|
||||
|
||||
---
|
||||
|
||||
## 主要代码落点
|
||||
|
||||
| 层 | 文件 / 目录 | 说明 |
|
||||
|----|-------------|------|
|
||||
| 后端控制器 | `backend/controller/audit.go` | 操作审计查询、导出、统计 |
|
||||
| 后端控制器 | `backend/controller/login_log.go` | 登录日志 |
|
||||
| 后端控制器 | `backend/controller/terminal_session.go` | SSH 会话列表、详情、回放、终止 |
|
||||
| 后端控制器 | `backend/controller/session_command.go` | 会话命令查询 |
|
||||
| 后端控制器 | `backend/controller/rdp_session.go` | RDP 会话列表、录像、终止 |
|
||||
| 后端控制器 | `backend/controller/audit_setting.go` | 审计与会话安全策略 |
|
||||
| 后端控制器 | `backend/controller/audit_hub.go` | SSH 实时审计广播 |
|
||||
| 后端模型 | `backend/model/audit.go` | 操作审计表 |
|
||||
| 后端模型 | `backend/model/terminal_session.go` | SSH 会话表 |
|
||||
| 后端模型 | `backend/model/rdp_session.go` | RDP 会话表 |
|
||||
| 后端模型 | `backend/model/session_command.go` | 命令/操作摘要表 |
|
||||
| 前端 API | `frontend/src/api/audit/` | 审计、终端、RDP API |
|
||||
| 前端设置 | `frontend/src/api/system/audit-settings.ts` | 会话策略 API |
|
||||
| 前端组件 | `frontend/src/components/system/LiveMonitorDialog.tsx` | SSH 实时审计 |
|
||||
| 前端组件 | `frontend/src/components/system/ReplayDialog.tsx` | SSH 回放 |
|
||||
| 前端组件 | `frontend/src/components/system/RdpAuditViewer.tsx` | RDP 实时/历史查看 |
|
||||
|
||||
---
|
||||
|
||||
## 统一会话模型
|
||||
|
||||
建议逐步抽象出统一会话视图:
|
||||
|
||||
```text
|
||||
Session
|
||||
├── id
|
||||
├── type: ssh | rdp | pod | kubectl | workspace | ai-agent
|
||||
├── user_id / username
|
||||
├── target_type: server | pod | cluster | workspace
|
||||
├── target_id / target_name / target_addr
|
||||
├── status: active | closed | failed | killed
|
||||
├── start_time / end_time / duration
|
||||
├── record_type: asciicast | rdp_bitmap | none
|
||||
├── record_size
|
||||
└── risk_level
|
||||
```
|
||||
|
||||
当前 SSH 和 RDP 分表存储是合理的,因为录像格式不同;但审计页应提供统一查询聚合视图。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 会话策略
|
||||
|
||||
策略存储:`sys_audit_settings`
|
||||
|
||||
| 策略 | 影响 |
|
||||
|------|------|
|
||||
| `session_watermark_enabled` | RDP/SSH 显示用户水印 |
|
||||
| `session_record_enabled` | 是否保存会话录像/录制 |
|
||||
| `clipboard_enabled` | 是否允许 RDP 剪贴板 |
|
||||
| `file_upload_enabled` | 是否允许 RDP 上传 |
|
||||
| `file_download_enabled` | 是否允许 RDP 下载 |
|
||||
| `command_policy_enabled` | 命令策略开关,后续用于 SSH/kubectl/AI 工具 |
|
||||
| `approval_required` | 高危访问或操作是否需要审批 |
|
||||
|
||||
后端必须作为最终边界执行策略,前端只负责按钮禁用和提示。
|
||||
|
||||
---
|
||||
|
||||
## 审批联动建议
|
||||
|
||||
| 场景 | 建议 |
|
||||
|------|------|
|
||||
| 访问生产 RDP/SSH | 可选审批或双人授权 |
|
||||
| RDP 文件下载 | 生产资产可触发审批 |
|
||||
| kubectl delete/scale/restart | 强制二次确认或审批 |
|
||||
| Agent CLI 高危工具 | 计划确认 + 操作确认 + 审批 |
|
||||
| 批量远程执行 | 必须审批 |
|
||||
| 非工作时间变更 | 强制审批 |
|
||||
|
||||
---
|
||||
|
||||
## 与现有专题文档关系
|
||||
|
||||
| 专题 | 文档 |
|
||||
|------|------|
|
||||
| 字符终端实现 | [docs/terminal](../terminal/README.md) |
|
||||
| RDP 实现 | [docs/rdp](../rdp/README.md) |
|
||||
| AI Agent 安全 | [docs/ai-agent](../ai-agent/00-README.md) |
|
||||
| 资产和凭证 | [docs/asset-credential](../asset-credential/README.md) |
|
||||
|
||||
---
|
||||
|
||||
## 演进路线
|
||||
|
||||
| 阶段 | 目标 | 关键任务 |
|
||||
|------|------|----------|
|
||||
| 第一阶段 | 可用堡垒机 | SSH/RDP 可访问、可回放、可审计、可禁用剪贴板和文件 |
|
||||
| 第二阶段 | 企业堡垒机 | 统一会话中心、策略按用户/角色/资产/协议生效、高危审批 |
|
||||
| 第三阶段 | 智能堡垒机 | AI Agent 工具调用进入统一审计,成功流程沉淀为 Runbook |
|
||||
|
||||
---
|
||||
|
||||
## 验收清单
|
||||
|
||||
1. 操作审计能按用户、模块、动作、时间筛选。
|
||||
2. SSH 会话能实时查看和历史回放。
|
||||
3. RDP 会话能实时查看和历史回放。
|
||||
4. RDP 剪贴板、上传、下载策略关闭后前后端都生效。
|
||||
5. SSH/RDP 命令或操作能通过会话 ID 关联到回放。
|
||||
6. 登录日志能区分成功、失败、来源 IP。
|
||||
7. 高危策略开启后,生产资产操作不会绕过后端拦截。
|
||||
@@ -1,11 +1,24 @@
|
||||
# AIOps 终端与远程桌面设计说明
|
||||
# AIOps 终端体系设计说明
|
||||
|
||||
> 更新:2026-07-03
|
||||
> 范围:SSH WebShell、Windows RDP、Kubernetes Pod exec、kubectl 集群终端、流水线 Workspace 终端、审计实时查看与历史回放。
|
||||
> 更新:2026-07-03
|
||||
> 范围:SSH WebShell、Kubernetes Pod exec、kubectl 集群终端、流水线 Workspace 终端、审计实时查看与历史回放。RDP 仅在本文中说明边界,完整实现见 `docs/rdp/`。
|
||||
> 本文用于记录当前实现、设计取舍与后续重构方向。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 0. 文档阅读路径
|
||||
|
||||
本目录描述“字符终端统一底座”,RDP 图形桌面只在总体架构中对齐边界,具体实现见 [docs/rdp](../rdp/README.md)。
|
||||
|
||||
| 文档 | 适合读者 | 内容 |
|
||||
|------|----------|------|
|
||||
| [README.md](./README.md) | 产品、架构、联调人员 | 终端体系总体目标、各类终端现状、审计和重构方向 |
|
||||
| [implementation-spec.md](./implementation-spec.md) | 前端、后端、测试工程师 | WebSSH、Pod exec、kubectl、Workspace 终端的可复刻实现规格 |
|
||||
|
||||
另一个项目要复刻 AIOps 终端能力时,建议先实现 `implementation-spec.md` 中的统一 xterm/WebSocket/PTY 闭环,再按业务类型接入 SSH、K8s、Workspace 和审计。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 1. 总体目标
|
||||
|
||||
AIOps 的终端体系不是单一功能,而是一组共享基础能力:
|
||||
@@ -468,7 +481,7 @@ backend/controller/
|
||||
├── rdp_live.go # 实时审计广播
|
||||
├── rdp_input_audit.go # 点击/拖拽/输入摘要
|
||||
├── rdp_session.go # 会话 REST API
|
||||
├── rdp_file.go
|
||||
├── rdp_staged.go # RDPDR 下载暂存 API
|
||||
└── rdp_cred.go
|
||||
```
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
# AIOps 终端体系实现规格
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 目标:让另一个项目仅阅读本文档,也能实现 WebSSH、Pod exec、kubectl、Workspace 终端和审计回放。
|
||||
> 范围:xterm.js 前端底座、WebSocket 协议、后端 PTY/SSH/K8s 代理、会话录像、命令审计、实时查看。
|
||||
|
||||
---
|
||||
|
||||
## 1. 终端类型和职责
|
||||
|
||||
| 类型 | 前端渲染 | 后端执行 | 典型入口 | 审计方式 |
|
||||
|------|----------|----------|----------|----------|
|
||||
| SSH WebShell | xterm.js | `ssh.Session + PTY` | 服务器管理、WebShell | asciicast + 命令 |
|
||||
| Pod Terminal | xterm.js | K8s `pods/exec` WebSocket | Pod 详情 | 预留 K8s 审计 |
|
||||
| kubectl Terminal | xterm.js | 本地 `kubectl` 进程 + PTY | 集群管理 | 建议命令审计 |
|
||||
| Workspace Terminal | xterm.js | 本地 shell + PTY,限制目录 | 流水线工作区 | 建议命令审计 |
|
||||
| RDP | Canvas | RDP 协议引擎 | Windows 资产 | 独立图形录像 |
|
||||
|
||||
本文只定义字符终端。RDP 详见 [RDP 实现规格](../rdp/implementation-spec.md)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 统一前端底座
|
||||
|
||||
### 2.1 组件结构
|
||||
|
||||
```text
|
||||
Terminal Dialog/Page
|
||||
├── create Terminal()
|
||||
├── load FitAddon
|
||||
├── mount into .xterm-terminal-host
|
||||
├── open WebSocket
|
||||
├── onData → ws.send({ status: 0, data })
|
||||
├── ResizeObserver → fit() → ws.send({ status: 1, cols, rows })
|
||||
└── ws.onmessage → terminal.write(data)
|
||||
```
|
||||
|
||||
公共工具应提供:
|
||||
|
||||
| 方法 | 职责 |
|
||||
|------|------|
|
||||
| `mountXterm()` | 延迟挂载、打开 terminal、执行多次初始 fit |
|
||||
| `fitTerminal()` | 调用 FitAddon,返回 `cols/rows` |
|
||||
| `attachTerminalResize()` | 监听容器 resize,节流 fit 并同步后端 |
|
||||
| `disposeTerminal()` | 关闭 WebSocket、移除 observer、销毁 xterm |
|
||||
|
||||
### 2.2 DOM 和样式要求
|
||||
|
||||
```tsx
|
||||
<div className="xterm-terminal-host" ref={containerRef} />
|
||||
```
|
||||
|
||||
```css
|
||||
.xterm-terminal-host {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: #0a0e14;
|
||||
}
|
||||
|
||||
.xterm-terminal-host .xterm {
|
||||
height: 100%;
|
||||
}
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 终端容器必须有稳定高度,不能依赖内容撑开。
|
||||
- Dialog 动画会影响 xterm 测量,终端弹窗建议禁用打开/关闭缩放动画。
|
||||
- `term.open()` 同一实例只能调用一次。
|
||||
- `fit()` 后必须把 `cols/rows` 发给后端,否则远端 PTY 仍按旧尺寸输出。
|
||||
|
||||
### 2.3 推荐 xterm 配置
|
||||
|
||||
```ts
|
||||
const terminalOptions = {
|
||||
cursorBlink: true,
|
||||
convertEol: true,
|
||||
fontFamily: 'Menlo, Monaco, Consolas, monospace',
|
||||
fontSize: 13,
|
||||
lineHeight: 1.15,
|
||||
scrollback: 5000,
|
||||
theme: {
|
||||
background: '#0a0e14',
|
||||
foreground: '#d8dee9',
|
||||
cursor: '#f8fafc',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
交互要求:
|
||||
|
||||
- ESC、Ctrl+C、Ctrl+D 必须传给终端,不应被 Dialog 或页面快捷键抢走。
|
||||
- 粘贴走 xterm 默认粘贴或 `navigator.clipboard.readText()` 后写入 WebSocket。
|
||||
- 多标签终端切换时,不要销毁非当前标签的 WebSocket,除非用户主动关闭。
|
||||
|
||||
---
|
||||
|
||||
## 3. WebSocket 协议
|
||||
|
||||
### 3.1 通用 JSON 协议
|
||||
|
||||
浏览器到后端:
|
||||
|
||||
```json
|
||||
{ "status": 0, "data": "ls -la\n" }
|
||||
{ "status": 1, "cols": 120, "rows": 34 }
|
||||
```
|
||||
|
||||
后端到浏览器:
|
||||
|
||||
```json
|
||||
{ "status": 0, "data": "total 8\r\n" }
|
||||
{ "status": 2, "message": "connected" }
|
||||
{ "status": -1, "message": "connection failed" }
|
||||
```
|
||||
|
||||
字段语义:
|
||||
|
||||
| 字段 | 方向 | 说明 |
|
||||
|------|------|------|
|
||||
| `status=0` | 双向 | stdin/stdout 数据 |
|
||||
| `status=1` | 浏览器到后端 | resize,包含 `cols/rows` |
|
||||
| `status=2` | 后端到浏览器 | 状态提示,可选 |
|
||||
| `status=-1` | 后端到浏览器 | 错误提示 |
|
||||
|
||||
实现要求:
|
||||
|
||||
- 后端读取 WebSocket 后按 `status` 分流。
|
||||
- stdout/stderr 可以合并写回 `status=0`,也可以扩展 `stream` 字段。
|
||||
- 输入数据必须原样写入 PTY,不要按行缓存,否则 vim/top 等交互程序不可用。
|
||||
- resize 事件必须节流,建议 100-200ms。
|
||||
|
||||
### 3.2 鉴权
|
||||
|
||||
WebSocket 无法稳定设置自定义 Header,因此支持两种方式:
|
||||
|
||||
```text
|
||||
/ws/terminal/:serverId?token=<JWT>
|
||||
/ws/pod/exec?...&token=<JWT>
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 后端优先读 `Authorization`,其次读 `?token=`。
|
||||
- 鉴权通过后仍要做资源权限校验。
|
||||
- token 不能写入审计命令和终端录制。
|
||||
|
||||
---
|
||||
|
||||
## 4. SSH WebShell
|
||||
|
||||
### 4.1 后端流程
|
||||
|
||||
```text
|
||||
GET /ws/terminal/:serverId
|
||||
→ JWTAuth
|
||||
→ load server asset
|
||||
→ load credential
|
||||
→ ssh.Dial
|
||||
→ session.RequestPty(term, rows, cols, modes)
|
||||
→ session.Shell()
|
||||
→ create sys_terminal_sessions
|
||||
→ goroutine: ws input → ssh stdin
|
||||
→ goroutine: ssh stdout/stderr → ws output + recorder + audit hub
|
||||
→ on close: update duration, status, record_data, record_size
|
||||
```
|
||||
|
||||
PTY 参数:
|
||||
|
||||
```go
|
||||
ssh.TerminalModes{
|
||||
ssh.ECHO: 1,
|
||||
ssh.TTY_OP_ISPEED: 14400,
|
||||
ssh.TTY_OP_OSPEED: 14400,
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 命令审计
|
||||
|
||||
基础策略:
|
||||
|
||||
- 按回车提取命令。
|
||||
- 不记录每个按键。
|
||||
- 下一条命令出现前,统计上一条命令后的输出字节数。
|
||||
- 粘贴多行脚本应合并或标记为多行命令。
|
||||
|
||||
命令表:
|
||||
|
||||
```text
|
||||
sys_session_commands
|
||||
session_id
|
||||
session_type = terminal
|
||||
user_id / username
|
||||
server_id / server_name
|
||||
command
|
||||
output_size
|
||||
timestamp
|
||||
```
|
||||
|
||||
敏感信息建议:
|
||||
|
||||
- 对 `password=...`、`token=...`、`AKIA...`、私钥片段做脱敏。
|
||||
- 命令审计保存脱敏结果,原始录制可按安全策略决定是否保存。
|
||||
|
||||
---
|
||||
|
||||
## 5. asciicast 录制和回放
|
||||
|
||||
### 5.1 录制格式
|
||||
|
||||
使用 asciicast v2:
|
||||
|
||||
```json
|
||||
{"version":2,"width":120,"height":34,"timestamp":1783131042,"env":{"TERM":"xterm-256color"}}
|
||||
[0.120,"o","Last login: ...\r\n"]
|
||||
[0.450,"o","$ "]
|
||||
[1.200,"o","ls -la\r\n"]
|
||||
```
|
||||
|
||||
实现要求:
|
||||
|
||||
- 第一行是 header。
|
||||
- 后续每行是 `[timeOffset, "o", output]`。
|
||||
- 只录制后端输出,不录制用户输入的独立事件,避免重复。
|
||||
- `RecordData` 建议限制大小,超过上限后截断并标记。
|
||||
|
||||
### 5.2 回放
|
||||
|
||||
前端回放流程:
|
||||
|
||||
```text
|
||||
GET /api/v1/terminal-sessions/:id/record
|
||||
→ parse asciicast
|
||||
→ create read-only xterm
|
||||
→ 按 timeOffset 调度 terminal.write()
|
||||
→ 支持播放、暂停、倍速、拖动进度
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
- 回放画面和原终端输出顺序一致。
|
||||
- 支持命令书签跳转。
|
||||
- 删除会话后录制不可访问。
|
||||
|
||||
---
|
||||
|
||||
## 6. Pod Exec
|
||||
|
||||
### 6.1 后端链路
|
||||
|
||||
```text
|
||||
Browser xterm
|
||||
→ AIOps backend WebSocket
|
||||
→ Kubernetes API /api/v1/namespaces/{ns}/pods/{pod}/exec
|
||||
→ channel.k8s.io WebSocket
|
||||
→ container shell
|
||||
```
|
||||
|
||||
K8s channel:
|
||||
|
||||
| Channel | 方向 | 内容 |
|
||||
|---------|------|------|
|
||||
| 0 | client to pod | stdin |
|
||||
| 1 | pod to client | stdout |
|
||||
| 2 | pod to client | stderr |
|
||||
| 3 | pod to client | error/status |
|
||||
| 4 | client to pod | resize |
|
||||
|
||||
resize frame:
|
||||
|
||||
```json
|
||||
{ "Width": 120, "Height": 34 }
|
||||
```
|
||||
|
||||
实现要求:
|
||||
|
||||
- shell 可选 `/bin/bash`、`/bin/sh`。
|
||||
- namespace、pod、container 必须做权限校验。
|
||||
- 容器切换时必须重连。
|
||||
- Pod exec 不应写入 SSH 的 `sys_terminal_sessions`,建议后续建 K8s 独立会话表。
|
||||
|
||||
---
|
||||
|
||||
## 7. kubectl 终端
|
||||
|
||||
### 7.1 后端链路
|
||||
|
||||
```text
|
||||
GET /ws/kubectl?cluster_id=1
|
||||
→ JWTAuth + cluster permission
|
||||
→ create temp kubeconfig
|
||||
→ exec kubectl with PTY
|
||||
→ ws input/output bridge
|
||||
→ cleanup temp kubeconfig and process
|
||||
```
|
||||
|
||||
安全要求:
|
||||
|
||||
- kubeconfig 必须按用户和集群隔离。
|
||||
- 临时文件使用随机名,关闭后删除。
|
||||
- 禁止复用上一次集群上下文。
|
||||
- kubectl 权限由 AIOps RBAC 和集群 RBAC 双重约束。
|
||||
|
||||
建议审计:
|
||||
|
||||
- 记录输入命令。
|
||||
- 标记 `delete`、`scale`、`rollout restart`、`patch`、`apply` 为高风险。
|
||||
- 后续接入审批或二次确认。
|
||||
|
||||
---
|
||||
|
||||
## 8. Workspace 终端
|
||||
|
||||
### 8.1 后端链路
|
||||
|
||||
```text
|
||||
GET /ws/workspace/:pipelineRunId/terminal
|
||||
→ JWTAuth + pipeline permission
|
||||
→ resolve workspace dir
|
||||
→ ensure dir is under workspace root
|
||||
→ start shell with PTY
|
||||
→ ws input/output bridge
|
||||
```
|
||||
|
||||
路径安全:
|
||||
|
||||
- 所有路径必须 `filepath.Clean`。
|
||||
- `realpath` 后必须仍位于 workspace root 下。
|
||||
- 禁止用户通过 `../../` 进入任意系统目录。
|
||||
- Shell 的工作目录固定为本次 pipeline workspace。
|
||||
|
||||
---
|
||||
|
||||
## 9. 实时审计
|
||||
|
||||
### 9.1 SSH 实时查看
|
||||
|
||||
```text
|
||||
active SSH session
|
||||
→ AuditHub broadcast output
|
||||
→ /ws/audit/:sessionId
|
||||
→ LiveMonitorDialog read-only xterm
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 审计端只读,不允许发送 stdin。
|
||||
- 审计连接不能影响主会话性能。
|
||||
- 活动会话结束后,审计连接收到关闭提示。
|
||||
|
||||
### 9.2 统一审计页
|
||||
|
||||
建议页面分区:
|
||||
|
||||
| 页签 | 内容 |
|
||||
|------|------|
|
||||
| 在线会话 | SSH active + RDP active |
|
||||
| 历史会话 | SSH asciicast + RDP bitmap replay |
|
||||
| 会话命令 | SSH 命令 + RDP 操作摘要 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 数据模型
|
||||
|
||||
### 10.1 SSH 会话
|
||||
|
||||
```text
|
||||
sys_terminal_sessions
|
||||
id
|
||||
user_id / username
|
||||
server_id / server_name / server_ip / server_port
|
||||
client_ip
|
||||
status: active | closed | failed
|
||||
duration
|
||||
record_data
|
||||
record_size
|
||||
start_time / end_time
|
||||
```
|
||||
|
||||
### 10.2 命令审计
|
||||
|
||||
```text
|
||||
sys_session_commands
|
||||
id
|
||||
session_id
|
||||
session_type: terminal | rdp
|
||||
user_id / username
|
||||
server_id / server_name
|
||||
command
|
||||
output_size
|
||||
timestamp
|
||||
```
|
||||
|
||||
### 10.3 策略配置
|
||||
|
||||
```text
|
||||
sys_audit_settings
|
||||
retention_days
|
||||
record_all
|
||||
session_watermark_enabled
|
||||
session_record_enabled
|
||||
command_policy_enabled
|
||||
approval_required
|
||||
```
|
||||
|
||||
字符终端第一版至少需要:
|
||||
|
||||
- `record_all`
|
||||
- `session_record_enabled`
|
||||
- `session_watermark_enabled`
|
||||
- `retention_days`
|
||||
|
||||
---
|
||||
|
||||
## 11. 错误处理
|
||||
|
||||
| 场景 | 前端表现 | 后端处理 |
|
||||
|------|----------|----------|
|
||||
| 鉴权失败 | 显示未登录或无权限 | 关闭 WebSocket |
|
||||
| 凭据错误 | 显示连接失败 | 会话状态 `failed`,保存错误 |
|
||||
| SSH 超时 | 显示超时 | 释放 session/client |
|
||||
| PTY resize 失败 | 显示警告或忽略 | 继续会话 |
|
||||
| WebSocket 断开 | 显示已断开 | 关闭远端 session |
|
||||
| 用户关闭窗口 | 清理资源 | 更新 `end_time`、`duration` |
|
||||
|
||||
实现要求:
|
||||
|
||||
- 任何退出路径都必须关闭 PTY、进程、SSH client、WebSocket。
|
||||
- 后端 goroutine 需要 context 取消,避免泄露。
|
||||
- 会话状态更新要放在 `defer` 中兜底执行。
|
||||
|
||||
---
|
||||
|
||||
## 12. 验收清单
|
||||
|
||||
### 12.1 WebSSH
|
||||
|
||||
1. 打开服务器 SSH 终端成功。
|
||||
2. `ls`、`top`、`vim`、Ctrl+C、Ctrl+D 可用。
|
||||
3. 拖动窗口或最大化后,远端 `stty size` 与前端一致。
|
||||
4. 会话关闭后 `sys_terminal_sessions` 状态为 `closed`。
|
||||
5. 回放可播放完整输出。
|
||||
6. 命令审计能看到命令和输出大小。
|
||||
|
||||
### 12.2 Pod Exec
|
||||
|
||||
1. 选择 namespace/pod/container 后能进入 shell。
|
||||
2. stdout/stderr 都能显示。
|
||||
3. resize 后 `stty size` 正确。
|
||||
4. 容器切换会重连。
|
||||
5. 无权限 namespace 无法连接。
|
||||
|
||||
### 12.3 kubectl
|
||||
|
||||
1. 打开指定集群 kubectl 终端。
|
||||
2. `kubectl get pods -A` 输出正常。
|
||||
3. 切换集群不会复用旧上下文。
|
||||
4. 关闭窗口后后端 kubectl 进程退出。
|
||||
|
||||
### 12.4 Workspace
|
||||
|
||||
1. 打开流水线工作区终端。
|
||||
2. 默认目录在 workspace root。
|
||||
3. `pwd`、`ls` 正常。
|
||||
4. 不能通过路径逃逸访问系统目录。
|
||||
|
||||
### 12.5 审计回放
|
||||
|
||||
1. 在线会话能实时只读查看。
|
||||
2. 历史会话能按时间播放。
|
||||
3. 删除会话后录制和命令记录按策略处理。
|
||||
4. 关闭录制策略后不写 `record_data`。
|
||||
|
||||
---
|
||||
|
||||
## 13. 常见问题
|
||||
|
||||
| 问题 | 原因 | 处理 |
|
||||
|------|------|------|
|
||||
| 终端右侧黑边 | 初始 fit 太早 | mount 后延迟多次 fit |
|
||||
| resize 后远端仍旧宽度 | 只 fit 前端,未发 cols/rows | WebSocket 发送 `status=1` |
|
||||
| Ctrl+C 不生效 | 页面快捷键拦截 | xterm 容器 focus 后透传 |
|
||||
| vim 显示错乱 | PTY 类型或 resize 不正确 | 使用 `xterm-256color` 并同步尺寸 |
|
||||
| 回放速度不对 | time offset 解析错误 | 按 asciicast v2 第一列调度 |
|
||||
| WebSocket 断开后进程残留 | 后端未 cancel context | close handler 统一清理 |
|
||||
| Pod exec 没输出 | K8s channel 未拆分 | 按 channel 1/2 读取 stdout/stderr |
|
||||
Reference in new issue
Block a user