Files
aiops/docs/agent/02-整体架构设计.md
T
attackerandClaude 92a2247390 feat: 新增 Agent CLI — Web 版 AI 运维 Agent(参考 opsxcli 移植)
后端引擎 backend/pkg/agentcli/(纯引擎库,零业务依赖,剥离 CLI/TUI 外壳):
- llm: OpenAI 兼容客户端,复用 model.LLMConfig
- tools: ssh_execute(复用 pkg/remoteexec)+ analyze_output + 风险评估黑名单
- safety: 三级安全模式 + 五级风险 + WebSocket 异步确认
- prompt: 五层 System Prompt
- core: ReAct 16 轮循环 + 自适应检查点 + 循环检测
- evolver: 四层记忆 + 进化循环,data/agentcli/ 文件存储

集成层:
- controller/agent_cli.go: WebSocket 控制器(事件流推流 + 高危确认 + 会话持久化)
- model/agent_cli.go: agent_cli_sessions/messages 表 + AutoMigrate
- 路由 /ws/agent-cli + /api/v1/aiops/agent-cli/sessions
- seed 权限 aiops:agent-cli / aiops:llm-config

前端:
- AgentCLI.tsx: 对话流 + 工具块 + 高危确认 + 四模块 tips 面板
- LLMConfig.tsx: 大模型设置独立页
- api/agentCli.ts + Sidebar/App 菜单路由

文档:
- docs/agent/ 11 篇(opsxcli 同名文档适配改写 + 00-README 映射总览)

其他:
- CLAUDE.md 补充 agent-cli 模块说明
- 修正 .gitignore 根级二进制规则(前导 /,避免误伤 frontend/src/components/aiops/)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-27 09:55:17 +08:00

304 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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