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

16 KiB
Raw Blame History

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,纯事件消费 + 控制消息发送

依赖通过函数参数注入:

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

// 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):
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
}
  1. 在 Engine 中注册(pkg/agentcli/agentcli.go: NewEngine):
registry.Register(tools.NewSSHExecuteTool(cfg.SSH, coreCfg.ToolTimeout))
registry.Register(tools.NewAnalyzeOutputTool())
registry.Register(tools.NewPingCheckTool(cfg.SSH))  // 新增
  1. 工具自动出现在 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