Files
aiops/docs/agent/08-Evolver自进化引擎.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

21 KiB
Raw Blame History

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

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

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

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(观察)

提取工具序列:

for _, tc := range exec.ToolCalls {
    result.ToolSequence = append(result.ToolSequence, tc.ToolName)
}

输出如 ["ssh_execute", "analyze_output", "ssh_execute"]。

Step 2: CLASSIFY(任务分类)

关键词规则匹配(不调 LLM,零成本):

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 优先,失败降级规则:

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(存储经验 + 环境记忆)

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 可用时触发,异步执行不阻塞:

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 调用:

evolveCtx := a.evolver.GetContextForPrompt(query)
systemPrompt := prompt.BuildSystemPrompt(serverInfo, toolDescs, evolveCtx)

注入内容会被放入 Prompt Layer 5(详见 07 - Prompt 构建)。

四层记忆系统

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 结构

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

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(追加写入)

{"task_type":"磁盘空间","query":"检查磁盘空间","hint":"对于磁盘满问题,先检查大日志文件再清理","tool_sequence":["ssh_execute","analyze_output"],"success":true,"timestamp":1748000000}

environment.json(整体读写)

{
  "hosts": ["192.168.1.10", "192.168.1.11"]
}

factual.jsonl(追加写入)

{"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 的排序策略:

// 优先成功经验,按时间取最近 n 条
successFirst := ... // 过滤 success=true
others := ...       // 过滤 success=false
out := append(pick(successFirst), pick(others)...)

让 LLM 优先看到成功的经验。

与 Agent 的集成

初始化与优雅降级

在 agentcli.NewEngine 中:

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
通用运维 (兜底)

创建方式

// 基础引擎(无 LLM 提炼,纯规则学习)
engine, _ := evolver.NewEvolverEngine("data/agentcli", nil)

// 带 LLM 提炼的引擎(推荐)
engine, _ := evolver.NewEvolverEngine("data/agentcli", llmClient)

与 opsxcli 的差异:opsxcli 有 NewEvolverEngine 和 NewEvolverEngineWithLLM 两个工厂;aiops 合并为一个,通过 client 参数是否为 nil 区分。

统计与监控

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