后端引擎 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>
304 lines
16 KiB
Markdown
304 lines
16 KiB
Markdown
# 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
|