Files
aiops/docs/agent/07-Prompt构建与记忆注入.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

14 KiB
Raw Blame History

07 - Prompt 构建与记忆注入

本文由 opsxcli 同名设计文档适配改写,结合 aiops Agent CLI 的 Web 实现(backend/pkg/agentcli/prompt/prompt.go)。 五层 System Prompt 架构保留;opsxcli 的「personaLayer / capabilityLayer / constraintLayer / fewShotLayer / memoryLayer」五常量在 aiops 中合并为单个 BuildSystemPrompt() 函数;角色从「opsxcli 智能运维超级助手」改写为「aiops Agent CLI 智能运维 Agent(Web 版,类似 Claude Code)」。

设计理念

Prompt 是 LLM Agent 的灵魂。aiops Agent CLI 沿用 opsxcli 的 五层 System Prompt 架构,将静态知识与动态记忆分层组装:

  • Layer 1-4 静态层:编译期确定,不随运行时变化(角色、安全、工具、工作方式)
  • Layer 5 动态层:运行时由 Evolver 注入历史经验,越用越聪明

五层 System Prompt 架构

┌──────────────────────────────────────────┐
│  Layer 1: 角色定位                        │  ← 我是谁
│  「aiops Agent CLI 智能运维 Agent」       │
├──────────────────────────────────────────┤
│  Layer 2: 安全规则                        │  ← 我必须遵守什么
│  黑名单 / 高危确认 / 优先只读             │
├──────────────────────────────────────────┤
│  Layer 3: 工具说明                        │  ← 我能用什么
│  由 Registry 动态生成(ssh_execute 等)   │
├──────────────────────────────────────────┤
│  Layer 4: 工作方式(ReAct 范式)          │  ← 我怎么工作
│  思考 → 行动 → 观察 → 循环 → 收尾        │
├──────────────────────────────────────────┤
│  Layer 5: 记忆注入                        │  ← 我学到了什么(动态)
│  Evolver 提供的历史经验                   │
└──────────────────────────────────────────┘

BuildSystemPrompt 函数

// backend/pkg/agentcli/prompt/prompt.go

func BuildSystemPrompt(serverInfo, toolDescs, evolveContext string) string {
    var sb strings.Builder

    // Layer 1: 角色
    sb.WriteString("你是 aiops 平台的 Agent CLI —— 一个企业级智能运维 Agent," +
        "类似 Claude Code / Codex,但运行在 Web 页面上,通过 SSH 工具远程操作真实服务器。\n" +
        "你的职责是:根据用户的自然语言任务描述,自主在目标服务器上排查故障、安装与配置软件、修复问题," +
        "并通过 WebSocket 把每一步思考、执行的命令和输出实时反馈给用户。\n\n")

    // 目标主机(动态片段,但不属于记忆层)
    if strings.TrimSpace(serverInfo) != "" {
        sb.WriteString("【目标主机】\n" + serverInfo + "\n\n")
    }

    // Layer 2: 安全规则
    sb.WriteString("【安全规则】\n")
    sb.WriteString("1. 破坏性命令(rm -rf /、mkfs、dd 覆写磁盘等)被严格禁止,绝不执行。\n")
    sb.WriteString("2. 高危操作(reboot、systemctl restart、kill、卸载软件等)需要用户确认后才能执行。\n")
    sb.WriteString("3. 排障时优先使用只读命令(df/ps/cat/grep/tail/systemctl status/journalctl)收集信息...\n")
    sb.WriteString("4. 不要执行任何你不确定后果的命令;宁可多问用户,也不盲目操作。\n\n")

    // Layer 3: 工具(动态生成)
    if strings.TrimSpace(toolDescs) != "" {
        sb.WriteString("【可用工具】\n" + toolDescs + "\n\n")
    }

    // Layer 4: 工作方式(ReAct)
    sb.WriteString("【工作方式(ReAct 循环)】\n")
    sb.WriteString("1. 思考:分析当前状态,决定下一步...\n")
    sb.WriteString("2. 行动:调用 ssh_execute 执行命令(一次只做关键的、可验证的一步)。\n")
    sb.WriteString("3. 观察:仔细阅读命令输出(含 exit_code,0 表示成功)。\n")
    sb.WriteString("4. 循环:如果命令失败或问题未解决,分析错误原因,换一种方法重试,直到真正解决。\n")
    sb.WriteString("5. 收尾:问题解决后,用清晰的中文总结:问题根因、你做了什么、最终结果、后续建议。\n\n")
    sb.WriteString("【重要】务必真正解决问题,而不是假装解决。每一步都要基于实际命令输出判断," +
        "禁止编造不存在的输出。\n\n")

    // Layer 5: 记忆注入(动态)
    if strings.TrimSpace(evolveContext) != "" {
        sb.WriteString("【历史经验(来自自进化引擎)】\n" + evolveContext + "\n\n")
    }

    // 输出要求
    sb.WriteString("【输出要求】使用简体中文,简洁专业。思考与命令穿插呈现,避免冗长空话。")
    return sb.String()
}

与 opsxcli 的差异:

  • opsxcli 把五层拆为五个独立常量(personaLayer / capabilityLayer / constraintLayer / fewShotLayer / memoryLayerPrefix),aiops 合并为单个函数,更紧凑
  • opsxcli 有独立的 BuilderV2 结构和 NewBuilderV2WithMemory 工厂,aiops 直接用函数(Web 场景下不需要构建器状态)
  • aiops 移除了独立的 fewShot 示例层,把 ReAct 工作方式说明合并到 Layer 4

Layer 1: 角色定位

关键改写:opsxcli 的角色是「opsxcli 智能运维超级助手」(CLI 工具语境);aiops 改写为「aiops 平台的 Agent CLI —— 企业级智能运维 Agent,类似 Claude Code / Codex,但运行在 Web 页面上」。

设计要点:

  • 明确类比:用「类似 Claude Code / Codex」让用户和 LLM 都快速理解定位
  • 强调 Web 语境:「运行在 Web 页面上」「通过 SSH 远程操作」「通过 WebSocket 反馈」
  • 明确职责边界:排障、安装配置、修复 —— 不是「无所不能的 AI」
  • 不包含版本号(与 opsxcli 一致)

目标主机片段

serverInfo 由 controller 构建,注入到 Layer 1 之后:

serverInfo := fmt.Sprintf("主机名: %s | IP: %s:%d | 用户: %s | OS: %s",
    ac.server.Hostname, ac.server.IP, ac.server.Port, ac.server.Username, ac.server.OS)

让 LLM 知道当前操作的是哪台主机、什么系统、用什么用户。

Layer 2: 安全规则

四条硬规则:

  1. 破坏性命令严格禁止(呼应代码层的黑名单硬拒)
  2. 高危操作需用户确认(呼应代码层的 ConfirmFn)
  3. 排障优先只读命令(引导 LLM 先收集信息再动手)
  4. 不确定时不盲目操作(宁可多问)

注意:Layer 2 是 Prompt 级别的「软约束引导」,与 safety.Controller.Check 的代码级「硬拦截」是不同层面的机制。两者互补:Prompt 引导 LLM 主动避免危险操作,代码层兜底防止 LLM 失控。

Layer 3: 工具说明

由 core.Agent.buildToolDescriptions() 动态生成:

func (a *Agent) buildToolDescriptions() string {
    var sb strings.Builder
    for _, t := range a.registry.List() {
        sb.WriteString(fmt.Sprintf("- %s: %s\n", t.Name(), t.Description()))
    }
    return sb.String()
}

为什么动态生成?

  • 工具集可扩展(实现 Tool 接口注册即可),硬编码 Prompt 会与代码脱节
  • 工具描述本身就是给 LLM 的「使用说明书」,由工具自己提供最准确
  • ssh_execute 的描述会包含具体的目标主机和用户信息(动态拼接)

输出示例:

【可用工具】
- ssh_execute: 在远程服务器 192.168.1.100(用户 root)上执行 shell 命令并返回合并的 stdout+stderr 输出与退出码...
- analyze_output: 分析一段(可能很长的)命令输出文本:统计行数、提取疑似错误/告警行...

Layer 4: 工作方式(ReAct 范式)

五步循环:

  1. 思考 —— 分析状态,决定下一步(产出 thought 文本)
  2. 行动 —— 调用 ssh_execute 执行命令(一次只做关键的、可验证的一步)
  3. 观察 —— 仔细阅读输出(含 exit_code,0 表示成功)
  4. 循环 —— 失败则换法重试,直到真正解决
  5. 收尾 —— 清晰中文总结(根因、做了什么、结果、建议)

关键约束(红线):

务必真正解决问题,而不是假装解决。每一步都要基于实际命令输出判断,禁止编造不存在的输出。

这条约束非常重要 —— LLM 有「编造输出」的倾向,明确禁止 + 配合 exit_code 信号,能显著降低幻觉。

Layer 5: 记忆注入(动态)

由 evolver.EvolverEngine.GetContextForPrompt(query) 提供:

func (e *EvolverEngine) GetContextForPrompt(query string) string {
    taskType := classifyTask(query, nil)
    var sb strings.Builder

    // 同类任务的历史经验(优先成功)
    if exps := e.mem.FindExperiences(taskType, 3); len(exps) > 0 {
        sb.WriteString(fmt.Sprintf("  【%s 类历史经验】\n", taskType))
        for _, exp := range exps {
            mark := "成功"
            if !exp.Success { mark = "未成功" }
            sb.WriteString(fmt.Sprintf("  - (%s) %s\n", mark, exp.Hint))
        }
    }
    // 相关事实
    if facts := e.mem.RelevantFacts(query, 3); len(facts) > 0 {
        sb.WriteString("  【已知事实】\n")
        for _, f := range facts {
            sb.WriteString(fmt.Sprintf("  - %s\n", f.Fact))
        }
    }
    // 已知主机
    if hosts := e.mem.Hosts(); len(hosts) > 0 {
        sb.WriteString(fmt.Sprintf("  【已知主机】%s\n", strings.Join(hosts, ", ")))
    }
    return sb.String()
}

注入示例:

【历史经验(来自自进化引擎)】
  【磁盘空间 类历史经验】
  - (成功) 完成「磁盘空间」类任务时,工具序列 ssh_execute → analyze_output 最终成功,可作为参考。
  - (成功) 对于磁盘满问题,先检查大日志文件再清理
  【已知事实】
  - 生产 nginx 日志目录是 /var/log/nginx
  【已知主机】192.168.1.10, 192.168.1.11

设计要点:

  • 按 query 分类后只注入同类经验,避免无关信息污染
  • 优先注入成功经验(FindExperiences 内部排序)
  • 限制条数(经验 3 条、事实 3 条),控制 token 开销

与 opsxcli 的差异:opsxcli 把记忆层细分为 5.0 事实 / 5.1 经验 / 5.1.5 技能 / 5.2 环境 / 5.3 偏好;aiops 精简为 3 段(经验 / 事实 / 已知主机),剥离了用户偏好(Web 场景下偏好由安全模式等显式配置承载)和技能层(未实现自动 Skill 提炼)。

注入流程

core.Agent.Run()
    │
    ├─ evolveCtx = evolver.GetContextForPrompt(query)   ← Layer 5 内容
    │
    ├─ toolDescs = buildToolDescriptions()              ← Layer 3 内容
    │
    ├─ systemPrompt = prompt.BuildSystemPrompt(
    │      serverInfo,    ← Layer 1 目标主机片段
    │      toolDescs,     ← Layer 3
    │      evolveCtx,     ← Layer 5
    │  )
    │
    └─ messages = [{system: systemPrompt}, {user: query}]

为什么 Layer 1-4 是静态的?

  • 角色定位和工作方式在编译期确定
  • 安全规则不应该被动态修改(防止 Evolver 污染安全约束)
  • 静态内容可以利用 LLM Provider 的 Prompt Caching(减少 token 消耗,部分 Provider 如 Anthropic 支持)
  • 只有事实/经验需要动态更新

输出要求

最后一段统一约束输出风格:

使用简体中文,简洁专业。思考与命令穿插呈现,避免冗长空话。

这与 opsxcli 一致 —— 中文运维语境的统一规范。

扩展指南

修改角色描述

直接修改 BuildSystemPrompt 中 Layer 1 的字符串。注意:

  • 不要包含版本号
  • 明确能力边界(不要吹成「无所不能」)
  • 场景化描述优于抽象形容

增加新的 Prompt 层

  1. 在 BuildSystemPrompt 中增加新的 sb.WriteString(...) 片段
  2. 在本文档更新架构图
  3. 若是动态层,需在 Agent.Run 中准备数据并传入

调整记忆注入策略

修改 evolver.GetContextForPrompt:

  • 增加新的记忆段(如「最近失败的任务」)
  • 调整条数限制
  • 改进分类匹配逻辑

常见陷阱

陷阱 解决方案
Prompt 过长导致 token 超限 Layer 1-4 精简 + Layer 5 按需注入 + 条数限制
记忆注入了无关经验 GetContextForPrompt 按 task_type 匹配,不全量注入
Layer 1-4 被 Evolver 污染 静态层硬编码在函数中,Evolver 只能影响 Layer 5
LLM 编造命令输出 Layer 4 显式禁止 + 输出附 exit_code 验证
角色描述含版本号 已清理,禁止再加版本号
工具描述与实际工具脱节 Layer 3 由 Registry 动态生成,永远一致

与 opsxcli 的差异总结

维度 opsxcli aiops
代码组织 5 个独立常量 + BuilderV2 结构 单个 BuildSystemPrompt 函数
角色 opsxcli 智能运维超级助手(CLI) aiops Agent CLI(Web 版,类比 Claude Code)
fewShot 层 独立 Layer 4(运维场景示例) 合并进 Layer 4 工作方式
记忆子层 5 个(事实/经验/技能/环境/偏好) 3 个(经验/事实/已知主机)
静态/动态分离 严格(BuilderV2.enableMemory 开关) 严格(函数内 if 判断)

思想完全保留(五层架构 + 静态动态分离 + 记忆按需注入),实现更紧凑。

作者:Logan.Li