后端引擎 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>
21 KiB
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
三层保护:
- 后台 goroutine:不阻塞用户,答案立即返回
- 30 秒超时:
context.WithTimeout(ctx, 30*time.Second) - 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 仍是「真正的自学习引擎」,而非装饰品。
设计亮点
- 异步非阻塞:进化在后台 goroutine,不增加用户等待
- 优雅降级:LLM 失败退化为规则学习,Evolver 初始化失败 Agent 正常运行
- 四层记忆:经验 + 环境 + 事实 + 程序,覆盖从瞬时到持久的认知链路
- 会话级反馈:上次任务的经验立即注入下次 Prompt
- 线程安全:
Memories.mu sync.Mutex保护文件读写 - 零 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