AIOps 通用 Agent 探索(Simple)

供稿来自:@秦皓

背景说明

目前我们使用 Cursor 时,除了编码外在诸如需求分析、技术文档编写、代码评审、代码部署调试、线上日志分析等场景下也会借助它进行工作提效。同时在通过与开发、测试和运维团队的交流讨论之后,我们整理出了基于 AI 的研发全流程,如下图所示:

可以看到 AI 贯穿于我们的研发整个流程,但在绝大部分场景下,会因为 Cursor 局限在单机设备上,无法方便地与我们现有的系统进行集成。为每个场景开发独立的 Agent 非常耗费人力,DevOps 流程本身并不像业务流程要求特别精细(特别是偏向分析、问题排查或总结报告等场景),基于对大模型智能会持续提升的信念,通过践行 Manus 提倡的更少结构,更多智能(Less structure, more intelligence )的理念,我们进行了开发一个 AIOps 通用 Agent 的探索。

预期目标

使用 ReAct 框架参照(并简化)Manus 架构和 Cursor/Cline 开发一个 AIOps 通用 Agent,我们的目标是通过完善 Prompt + LLM(Agent)+ Tools + Workspace 的方式实现 DevOps 流程的落地。

✨✨ 实现让用户通过提示词定义具体流程,就能快速实现某个流程的 AI 自动化集成✨✨

下文我将先先对本文用到的主要相关理论进行简单阐述,然后介绍 AIOps 通用 Agent 的设计实现方案,AI MR 场景实践效果,最后对未来的形态进行展望。

相关理论及开发框架

更少结构,更多智能

Less structure, more intelligence(更少结构,更多智能)是 3 月份大火的 Manus 背后的设计理念,这一理念强调 Manus 能够自主执行复杂的多步骤任务,几乎无需人工干预,注重灵活性和智能适应,而不是依赖预设的流程或结构。根据网上对 Manus 的拆解,其核心是基于推理-行动-观察的 ReAct 模型,架构图大致如下:

ReAct 框架简介

ReAct(Reasoning and Action)是一个框架,其概念来源于一篇https://arxiv.org/pdf/2210.03629(简版:https://react-lm.github.io/),其核心思想是通过思维链的方式,引导模型将复杂问题进行拆分,一步一步地进行推理(Reasoning)和行动(Action),同时还引入了观察(Observation)环节,在每次执行之后,都会先观察当前现状,然后再进行下一步的推理。

ReAct 这个框架,就是要让开发者一步步引导 LLM 进行推理,然后根据推理的结果,判断采取哪个行动,并生成最终产物。

其伪代码如下:

while not task_finished:
    thought = model.reason(context)
    action = model.act(thought)
    observation = environment.execute(action)
    context.update(observation)

Eino ReAct Agent

Eino 框架是字节跳动开源的一个 LLM 应用开发框架,提供了一个强调简洁性、可扩展性、可靠性与有效性,且更符合 Go 语言编程惯例的 LLM 应用开发框架。Eino ReAct Agent 是实现了 ReAct 逻辑 的智能体框架,用户可以用来快速灵活地构建并调用 ReAct Agent。

AIOps  通用 Agent

仓库地址:https://codeup.aliyun.com/qimao/public/aiops/aiops。

架构设计

整体架构包括:

1. 管理平台(暂无)

  • 用户基于场景对任务进行管理
  • 对外提供 API,用于系统集成
  • 定义周期性调用的 Job
  • 任务运行的报告

2. AIOps 通用 Agent

  • 封装了核心的 LLM(ReAct Engine)
  • 提供 LLM 具体需要使用的工具
  • Agent 运行环境管理(基于 Docker)

3. 其他

  • 使用 Langfuse 进行 LLM 调用监控
  • 使用 Bashly 封装常用的依赖工具

核心流程

一次 Agent 运行的核心流程如下:

组件说明

我们从上面的架构和流程图中,抽象出以下几个组件:

本文后续将以代码 MR 报告总结为例进行展开,我们提供 Codeup 相关命令行依赖(codeup),通过使用系统提示词定义一个 MR 评审总结核心流程,来实现让 AI 进行代码评审报告的能力。

提示词(Prompt)

提示词(Prompt)是我们最终需要关注的部分,随着 LLM 大模型能力的提升,相信我们的主要工作将以编写提示词为主,以后整个 AIOps Agent 的核心部分就是提示词的编写(流程的制定)。

在合并请求(MR)场景中,我们通过提供(codeup 和其他常用个 shell 命令:gitjq 等依赖),再编写以下系统提示词,实现一个 MR 报告生产 Agent:

使用 codeup 或 git 和 jq 等常用 bash 终端命令(我已经为你完成 codeup 命令的权限认证),请按照以下步骤审查进行 Codeup 合并请求(MR)审查。你需要在指定的代码仓库 /var/aiops/repos/${{repod_id}} 中处理相关 git 命令。

<detailed_sequence_of_steps>
# Codeup MR 审查流程 - 详细步骤

## 1. 收集 MR 信息
1. 获取 MR 的明细
  ```bash
  codeup mr get ${{repo_id}} ${{local_id}}
  ```

## 2. 理解上下文
1. 获取 MR 的完整变更文件树:
  ```bash
  codeup mr tree ${{repo_id}} ${{local_id}}
  ```
2. 使用 codeup repo file 命令查看特定分支上的文件内容:
  ```bash
  codeup repo file ${{repo_id}} ${{file_path}} --ref ${{branch}} | jq -r '.content' | base64 -d
  ```

## 3. 分析变更
分析本次变更主要:
- 修改了什么(总结几个点)
- 对整体仓库的影响
- 潜在的缺陷或问题
- 概括审查结果

## 4. 追加代码评审描述
对整个代码变更进行代码评审描述的更新(**这个步骤一定要执行,描述中的内容要求保持换行**):
  ```bash
  codeup mr update ${{repo_id}} ${{local_id}} --description '【原始的代码评审描述】
  \n
  【更新时间】
  【新的代码评审总结内容】'
  ```
</detailed_sequence_of_steps>

<common_codeup_commands>

# 用于 MR 审查的常用 Codeup CLI 命令,以及对应的返回格式

##  基本命令示例及返回格式

```bash
# 获取合并请求详情
$ codeup mr get 3394535 211
{
  "id": 211,
  "title": "fix: update Add function call to lowercase add",
  "description": "1 【更新时间】2025-06-10 11:02 代码审查完成:1. 发现2处潜在边界条件问题需补充测试 2. 存在3个魔法数字需要常量化 3. 所有反馈已通过行间评论标注",
  "state": "UNDER_REVIEW",
  "source_branch": "feat/cr",
  "target_branch": "master",
  "author": "秦皓",
  "created_at": "2025-05-13T09:21:16+08:00",
  "updated_at": "2025-06-10T11:02:52+08:00",
  "conflict_status": null,
  "reviewers": [
    {
      "name": "秦皓",
      "status": null,
      "reviewed": false,
      "review_time": null
    }
  ],
  "comment_count": 8,
  "unresolved_comment_count": 8,
  "url": "https://codeup.aliyun.com/qimao/public/devops/demo/change/211"
}


# 获取合并请求变更文件树
$ codeup mr tree 3394535 211
{
  "changedTreeItems": [
    {
      "aMode": "100644",
      "addLines": 13,
      "bMode": "100644",
      "delLines": 1,
      "deletedFile": false,
      "isBinary": false,
      "newFile": false,
      "newObjectId": "33eb73b5a624d113763f5d1ac5746d9e2da241db",
      "newPath": "README.md",
      "oldObjectId": "3ff86afe4fad6809ee9e1b7f5c3352712ff821e3",
      "oldPath": "README.md",
      "readStatus": "INIT",
      "renamedFile": false
    },
    {
      "aMode": "100644",
      "addLines": 0,
      "bMode": "0",
      "delLines": 9,
      "deletedFile": true,
      "isBinary": false,
      "newFile": false,
      "newObjectId": "0000000000000000000000000000000000000000",
      "newPath": "main.go",
      "oldObjectId": "90c6869506feed59a97b60ca92bb17db95bf09d2",
      "oldPath": "main.go",
      "readStatus": "INIT",
      "renamedFile": false
    }
  ],
  "count": 2,
  "totalAddLines": 47,
  "totalDelLines": 22
}

# 获取合并请求补丁集列表
$ codeup mr patches 3394535 211
[
  {
    "version_no": 1,
    "patch_set_name": "版本1",
    "patch_set_id": "0a7c40a77e1e42fe8a4659340c48a2b9",
    "commit_id": "747053544a3f08cce4de0c8fdc976f8f1ca90d76",
    "short_commit_id": "74705354",
    "create_time": "源分支"
  },
  {
    "version_no": 1,
    "patch_set_name": "Base",
    "patch_set_id": "7a4c707968a0483fa7df52ab7a2500c2",
    "commit_id": "07ab2d3dc2c2800b5e76656c02edc1bd44f67221",
    "short_commit_id": "07ab2d3d",
    "create_time": "目标分支"
  }
]
```

## 差异和文件命令

```bash
# 查看 master 分支上的文件内容:
$ codeup repo file 3394535 main.go --ref master 
{
  "blobId": "90c6869506feed59a97b60ca92bb17db95bf09d2",
  "commitId": "07ab2d3dc2c2800b5e76656c02edc1bd44f67221",
  "content": "cGFja2FnZSBtYWluCgppbXBvcnQgKAoJImNvZGV1cC5hbGl5dW4uY29tL3FpbWFvL3B1YmxpYy9kZW1vL2FwcCIKKQoKZnVuYyBtYWluKCkgewoJYXBwLlJ1bigpCn0K",
  "encoding": "base64",
  "fileName": "main.go",
  "filePath": "main.go",
  "lastCommitId": "02989c2393215ea6b8bf68af09439b85e0ef2d69",
  "ref": "master",
  "size": "96"
}

# 读取文件具体内容
$ codeup repo file 3394535 main.go --ref master | jq -r '.content' | base64 -d
package main

import (
        "codeup.aliyun.com/qimao/public/demo/app"
)

func main() {
        app.Run()
}
```

## 审查命令(创建合并请求评论)

```bash
#  更新合并请求描述(**保持换行**)
codeup mr update 3394535 211 --description '原始评论

${{date_time}}【评审更新】
## 代码评审总结 

### 主要变更 

1. **代码结构重构**:将计算功能从 app 包分离到独立的 calc 包,提升代码模块化程度 
2. **文件重组**:app/app.go → app/calc/calc.go, app/app_test.go → app/calc/calc_test.go, 删除根目录 main.go,新增 app/main.go 
3. **函数规范化**:私有函数 divide/multiply 修改为导出函数 Divide/Multiply 
4. **功能增强**:新增命令行计算器功能,支持四则运算演示 

### 对项目的影响 
- **正面影响**:改善了代码组织结构,计算逻辑与应用逻辑分离,便于维护和测试 
- **兼容性**:删除了原有的 Run() 函数,但通过新的 main.go 提供了更实用的命令行界面 

### 发现的问题 
🚨 **严重问题**:app/main.go 第22行存在函数调用错误 
- 当前代码:calc.add(a, b) 
- 应修正为:calc.Add(a, b) 
- 影响:程序无法正常编译运行 

### 建议 
1. **必须修复**:修正 app/main.go 中的函数调用错误 
2. **代码质量**:添加错误处理逻辑,当前 strconv.Atoi 的错误被忽略 
3. **测试覆盖**:建议为新增的 main.go 添加集成测试 

### 审查结果 
- ✅ 代码架构设计合理 
- ✅ 测试用例完整迁移 
- ❌ 存在编译错误需修复 
- ⚠️ 缺少错误处理机制
'

# 创建全局评论:
codeup mr comment add 3394535 211 "代码看起来不错"

# 对于严重缺陷创建行内评论:
codeup mr comment add 3394535 211 "这里需要修改" \
  --type INLINE_COMMENT \
  --file /src/main.go \
  --line 42 \
  --from-patch abc \
  --to-patch def \
  --patch abc
```

</common_codeup_commands>

<final_result>
最终输出格式保持如下:

```markdown
# 评审总结(${{repo_id}}-${{local_id}}
${{date_time}}【评审总结】,MR 地址:${{url}}。

## 评估结论
✅ 建议合并|🧰 修复问题后合并|❌ 不建议合并

## 📋 评审流程总结

1. **✅ 收集 MR 信息**
  - MR 标题:``
  - 状态:``
  - 分支:``

2. **✅ 理解变更上下文**
  - 共涉及 5 个文件变更(47行新增,22行删除)
  - 主要是代码结构重构和模块分离

3. **✅ 分析变更内容**
  - **代码结构重构**:将计算功能分离到独立的 calc 包
  - **文件重组**:重命名和移动多个文件
  - **功能增强**:新增命令行计算器功能

4. **✅ 发现关键问题**
  - 🚨 **严重问题**:`app/main.go` 第22行函数调用错误
  - 当前:`calc.add(a, b)` 
  - 应为:`calc.Add(a, b)`

## 🎯 关键建议:
1. **必须修复**:函数调用错误,否则程序无法编译
2. **代码质量**:添加错误处理逻辑
3. **测试覆盖**:为新功能添加集成测试

✨
```

</final_result>

<requirements>
- 忽略以 "md,mdc,yaml,json" 等后缀结尾的文档

</requirements>

<params>
核心参数: 
- repo_id: 仓库 ID
- local_id: 合并请求 ID

其他参数:
- file_path: 具体需要查看的文件
- branch: 具体需要查看文件的分支
- date_time: 当前时间,格式:"2025/06/06 13:10:11",使用 date 命令获取
- url: 在 codeup mr get 中的返回值中
</params>

LLM(ReAct Engine)

我们基于字节开源的 Eino 框架实现通用的 ReAct Agent 引擎,之后便可基于它加上:

  • 系统提示词
  • 工具列表
  • 执行环境

迅速运行一个 Agent 实例。React Engine 核心代码(仅 100 行余行):

package engine

import (
        "context"
        _ "embed"
        "errors"
        "fmt"
        "io"
        "strings"

        "aiops/internal/engine/tools"

        "github.com/cloudwego/eino-ext/callbacks/langfuse"
        "github.com/cloudwego/eino-ext/components/model/qwen"
        "github.com/cloudwego/eino/callbacks"
        "github.com/cloudwego/eino/components/tool"
        "github.com/cloudwego/eino/compose"
        "github.com/cloudwego/eino/flow/agent/react"
        "github.com/cloudwego/eino/schema"
)

type Engine struct {
        model *qwen.ChatModel // LLM 大模型
        tools []tool.BaseTool // 工具

        systemPrompt string // 系统提示词

        lfCfg *langfuse.Config // Langfuse 观测 LLM 调用链路
}

func NewEngine(systemPrompt string, chatModel *qwen.ChatModel, lfCfg *langfuse.Config, _ []tool.BaseTool) (*Engine, error) {
        return &Engine{
                model:        chatModel,
                tools:        tools.GetAllTools(),
                lfCfg:        lfCfg,
                systemPrompt: systemPrompt,
        }, nil
}

func (a *Engine) Run(userPrompt string) (result string, err error) {
        cbh, flusher := langfuse.NewLangfuseHandler(a.lfCfg)
        defer flusher() // 等待所有 trace 上报完成后退出

        callbacks.AppendGlobalHandlers(cbh) // 设置 langfuse 为全局 callback

        // 生产 Agent 实例
        ctx := context.Background()
        ragent, err := react.NewAgent(ctx, &react.AgentConfig{
                ToolCallingModel: a.model,
                ToolsConfig: compose.ToolsNodeConfig{
                        Tools: a.tools,
                },
                MaxStep: 40,
        })
        if err != nil {
                return "", fmt.Errorf("failed to create agent: %w", err)
        }

        // 流式调用
        sr, err := ragent.Stream(ctx, []*schema.Message{
                {
                        Role:    schema.System,
                        Content: a.systemPrompt, // 系统提示词
                },
                {
                        Role:    schema.User,
                        Content: userPrompt, // 用户提示词
                },
        })

        if err != nil {
                return "", fmt.Errorf("failed to stream: %w", err)
        }

        defer sr.Close() // remember to close the stream

        // 获取最终返回结果
        var buf = strings.Builder{}
        for {
                msg, err := sr.Recv()
                if err != nil {
                        if errors.Is(err, io.EOF) {
                                // finish
                                break
                        }
                        // error
                        return "", fmt.Errorf("failed to recv: %w", err)
                }

                buf.WriteString(msg.Content)
        }

        // 返回结果
        return buf.String(), nil
}

ReAct Agent 实现细节可参考文档:Eino: ReAct Agent 使用手册

工具(Tools)

Tool 提供了 LLM 执行具体任务的能力,在生成 MR 报告场景中,我们在提示词提供了多个 shell 命令,所以我们仅提供一个 bash_command 的命令,用于执行 shell 命令即可:

// ToolBash Bash 命令执行工具
type ToolBash struct{}

// Info 返回工具信息
func (t *ToolBash) Info(ctx context.Context) (*schema.ToolInfo, error) {
        return &schema.ToolInfo{
                Name: "bash_command",
                Desc: "执行 Bash 命令行指令,支持执行各种 shell 命令、脚本和系统操作",
                ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
                        "command": {
                                Type:     "string",
                                Desc:     "要执行的 bash 命令,支持管道、重定向等 shell 特性",
                                Required: true,
                        },
                        "working_dir": {
                                Type:     "string",
                                Desc:     "命令执行的工作目录,可选,默认为当前目录",
                                Required: false,
                        },
                        "timeout_seconds": {
                                Type:     "number",
                                Desc:     "命令执行超时时间(秒),可选,默认为 30 秒",
                                Required: false,
                        },
                        "environment": {
                                Type:     "object",
                                Desc:     "附加的环境变量,格式为 key-value 对象,可选",
                                Required: false,
                        },
                }),
        }, nil
}
此处的工具指的是 ReAct 框架需要的需要的工具。在运行 Docker 时,我们还会提到提示词中使用的如 codeup、git、jq 等命令行工具,这些工具我们称之为运行时依赖

执行环境(Workspace)

执行环境(Workspace)是 Agent 实际运行的环境,我们使用独立 Docker 容器了隔离 Agent 运行,以保障执行环境的可靠性。在运行 Docker 时,将用户定义的运行时依赖(如 codeupgitjq 等命令行工具)、Codebase 或者文件数据(通过磁盘卷挂载的方式)安装到容器中。

核心代码:

        // 创建沙箱实例
        sandbox, err := sandbox.NewDockerSandbox(context.Background(), &sandbox.Config{
                VolumeBindings: a.volumes,             // 挂载 codebase 等数据卷
                Env:            a.envs,                // 环境变量
                Image:          "aiops-sandbox:latest", // 镜像,包含完整运行时依赖
                WorkDir:        "/workspace",           // 定义 workspace 
                MemoryLimit:    512 * 1024 * 1024,
                CPULimit:       1.0,
                NetworkEnabled: true,
                Timeout:        30 * time.Minute,
        })
        if err != nil {
                log.Printf("创建沙箱失败:%v\n", err)
                return nil, fmt.Errorf("创建沙箱失败: %w", err)
        }

        // 创建容器
        err = sandbox.Create(context.Background())
        if err != nil {
                log.Printf("创建沙箱失败:%v\n", err)
                return nil, fmt.Errorf("创建沙箱失败: %w", err)
        }
        defer sandbox.Cleanup(context.Background())

Agent 定义

暂时未开发 Web UI 管理界面,我们可以通过等价的 YAML 格式了解一个 Agent 定义可以有多简单:

// 默认的通用 Agent
- id: "default"
  name: "默认通用 Agent"
  system_prompt: "你是一个无所不知的助手,请根据用户的问题给出回答,涉及时间的问题,应该用命令行工具获取当前最新时间,如果无法访问互联网,使用你已有的知识给出回答,并最终将答案输出给用户。"
  report_path: "aiops/reports/default"

// 合并请求 Agent
- id: "code_review" 
  name: "Code Review"
  async: true
  system_prompt_doc: "task_cr.md"
  system_prompt: ""
  user_prompt: ""
  user_prompt_template: "请为我给 repo_id=${{repo_id}}, local_id=${{local_id}} 的合并请求进行代码评审"
  user_prompt_params:
    - param: "repo_id"
      value: ${{repo_id}}
    - param: "local_id"
      value: ${{local_id}}
    - param: "repo_url"
      value: ${{repo_url}}
  tools:
    - "codebase"
  envs:
    "API_KEY": "pt-xxxx"
  volumes:
    "/var/aiops/repos/${{repo_id}}": "/workspace/src"
  pre_shell: ""
  post_shell: "codeup mr comment add ${{repo_id}} ${{local_id}} 'AI 代码评审报告: ${{report_url}}'"
  report_path: "aiops/mr/reports/${{repo_id}}/${{local_id}}/"

场景效果(MR 报告)

我们通过代码合并请求(MR)总结报告为例,使用相同的提示词,AIOps 通用 Agent 得出的报告与 Cursor 得出的报告质量几乎一致。

核心价值

通过对生成合并请求(MR)报告场景的实践,我们认为 AIOps 通用 Agent 是一个值得持续探索的方向,让我们能在尽可能减少人力投入的同时,让我们方便快捷地在现有流程中集成 AI 的能力。主要表现在:

  1. 简单高效:通过使用 Markdown 提示词定义业务流程的方式,即可快速实现某个特定 DevOps 场景下的分析报告、问题排查甚至自动修复等 AI Agent 能力。
  2. 隐私安全:通过自研开发的方式,我们可以完全控制代码或数据不会被泄露。
  3. 易于集成:通过暴露标准 Open API 的方式,可以快速在第三方系统中进行集成调用,无缝接入到现有的各个系统中。
  4. 异步处理:通过暴露 API 接口共第三方系统集成,或者设置定时运行 Job,能够实现异步、周期行处理工作,减少人工操作,实现自动化。
  5. 集中管理:通过脱离 Cursor IDE 环境(云端化)并进行集中管理设置的方式,提高了团队工作成果的可复用性,一人开发,全员受益。
  6. 贴合自身:通过自研开发的方式,我们可以基于我们七猫自身团队的流程特性进行定制设计,效率更高,品质更有保障。

未来展望

  1. 搭建管理平台,实现 Web UI 管理,能够更加直接方便的管理提示词、工具、报告、参数(数据、文件或代码仓库)、依赖(Docker 中的工具)、对外暴露的 API 能力等。
  2. 随着技术的进一步提升与功能需求的不断丰富,也希望在架构上进行升级,提供如多 Agent 协同、记忆与上下文管理、交互数据和任务反馈等更丰富的能力。
  3. 通过在 AIOps 流程中的持续探索和成熟之后,我们也希望将这一套方案迁移到我们七猫的业务中,开发对应的业务通用 Agent、数据通用 Agent 等。

参考链接

  1. 95. 对Manus创始人肖弘的3小时访谈:世界不是线性外推,做博弈中的重要变量
  2. Manus 的调研与思考 | Why·Liam·Blog
  3. 04|提示工程:更好地释放LLM的能力-程序员的AI开发第一课-极客时间
  4. https://react-lm.github.io/
  5. https://www.promptingguide.ai/zh/techniques/react
  6. https://github.com/cline/cline/blob/main/.clinerules/workflows/pr-review.md
  7. Linux 文本三剑客超详细教程:grep、sed、awk
展示评论