310 lines
16 KiB
Markdown
310 lines
16 KiB
Markdown
# /api/ai_ops 核心设计 - Essence 报告
|
||
|
||
> 生成日期:2026-05-30
|
||
> 分析透镜:Mechanical(如何工作)
|
||
> 设计模式:3-Agent Collaborative Analysis Pattern
|
||
|
||
---
|
||
|
||
## 💎 核心洞察
|
||
|
||
`/api/ai_ops` 的精华在于 **3-Agent 协同分析模式**:
|
||
|
||
1. **Planner** 负责"想"(制定计划 & 重新规划)
|
||
2. **Executor** 负责"做"(执行工具调用)
|
||
3. **Supervisor** 负责"协调"(循环调度直到完成)
|
||
|
||
这个模式解决了单 Agent 无法"边执行边调整"的痛点。
|
||
|
||
---
|
||
|
||
## 🎯 设计分析
|
||
|
||
### 问题(Problem)
|
||
|
||
传统的单 Agent 系统在处理复杂的运维场景时存在以下痛点:
|
||
|
||
1. **规划与执行混杂**:一个 Agent 既要制定计划,又要执行工具调用,导致逻辑混乱
|
||
2. **无法自适应调整**:执行失败后无法重新规划,只能从头开始
|
||
3. **调试困难**:无法清晰追踪"哪个环节失败了"
|
||
4. **输出格式不稳定**:Agent 可能在规划阶段就输出最终结果,导致流程短路
|
||
|
||
**具体场景**:
|
||
```
|
||
AI Ops 告警分析需要:
|
||
1. 先查 Prometheus 告警
|
||
2. 根据告警查对应的日志
|
||
3. 如果日志查询失败 → 重新规划(换个主题或时间范围)
|
||
4. 汇总所有数据 → 生成报告
|
||
|
||
单 Agent 无法处理"步骤 3"的重新规划
|
||
```
|
||
|
||
**代码证据**:
|
||
- `AiOpsService.java:144-235` - Planner Prompt 明确定义了 Replanner 角色
|
||
- `AiOpsService.java:241-257` - Executor Prompt 明确只执行"第一步"
|
||
|
||
---
|
||
|
||
### 模式(Pattern)
|
||
|
||
**核心思想**:将复杂任务拆分为 3 个专职 Agent,通过 Supervisor 编排协同工作。
|
||
|
||
#### 角色分工
|
||
|
||
| Agent | 职责 | 输入 | 输出 | 关键行为 |
|
||
|-------|------|------|------|---------|
|
||
| **Planner** | 制定计划 & 重新规划 | `{input}` + `{executor_feedback}` | `decision` (PLAN/EXECUTE/FINISH) + `step` 描述 | 分析告警 → 制定下一步 |
|
||
| **Executor** | 执行工具调用 | `{planner_plan}` | `executor_feedback` (JSON) | 只执行第一步 → 返回证据 |
|
||
| **Supervisor** | 调度与编排 | `taskPrompt` | `OverAllState` | Loop 调度 Planner & Executor 直到 FINISH |
|
||
|
||
#### 协同流程图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Supervisor │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────┐ │
|
||
│ │ Loop: │ │
|
||
│ │ │ │
|
||
│ │ ┌─────────────────┐ │ │
|
||
│ │ │ Planner Agent │ │ │
|
||
│ │ │ │ │ │
|
||
│ │ │ Input: │ │ │
|
||
│ │ │ - task │ │ │
|
||
│ │ │ - feedback │◄────────┐ │ │
|
||
│ │ │ │ │ │ │
|
||
│ │ │ Output: │ │ │ │
|
||
│ │ │ decision │ │ │ │
|
||
│ │ │ step │ │ │ │
|
||
│ │ └─────────────────┘ │ │ │
|
||
│ │ │ │ │ │
|
||
│ │ ├── PLAN ──────────► (记录) │ │
|
||
│ │ │ │ │ │
|
||
│ │ ├── EXECUTE ───┐ │ │ │
|
||
│ │ │ │ │ │ │
|
||
│ │ │ ▼ │ │ │
|
||
│ │ │ ┌─────────────────┐ │ │
|
||
│ │ │ │ Executor Agent │ │ │
|
||
│ │ │ │ │ │ │
|
||
│ │ │ │ Input: │ │ │
|
||
│ │ │ │ - planner_plan │ │ │
|
||
│ │ │ │ │ │ │
|
||
│ │ │ │ Output: │ │ │
|
||
│ │ │ │ - feedback │───────────────┘ │
|
||
│ │ │ │ - evidence │ │
|
||
│ │ │ └─────────────────┘ │
|
||
│ │ │ │ │
|
||
│ │ │ ▼ │
|
||
│ │ │ 调用 Tools: │
|
||
│ │ │ - QueryMetricsTools │
|
||
│ │ │ - QueryLogsTools │
|
||
│ │ │ - InternalDocsTools │
|
||
│ │ │ │
|
||
│ │ └── FINISH ───► 输出 Markdown 报告 │
|
||
│ │ │
|
||
│ └──────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 🔗 完整调用链
|
||
|
||
### HTTP → Service → Agents → Tools → SSE
|
||
|
||
```
|
||
用户点击 "AI Ops" 按钮
|
||
↓
|
||
HTTP POST /api/ai_ops (ChatController.java:280)
|
||
↓
|
||
ChatController.aiOps()
|
||
- 创建 SseEmitter (10 分钟超时)
|
||
- 异步执行任务
|
||
↓
|
||
AiOpsService.executeAiOpsAnalysis(chatModel, toolCallbacks) (Line 51)
|
||
↓
|
||
┌────────────────────────────────────────────────────────────┐
|
||
│ Step 1: 构建 3 个 Agent │
|
||
│ │
|
||
│ ① plannerAgent = buildPlannerAgent() (Line 100-109) │
|
||
│ - name: "planner_agent" │
|
||
│ - description: "负责拆解告警、规划与再规划步骤" │
|
||
│ - systemPrompt: buildPlannerPrompt() (Line 144-235) │
|
||
│ - outputKey: "planner_plan" │
|
||
│ │
|
||
│ ② executorAgent = buildExecutorAgent() (Line 115-124) │
|
||
│ - name: "executor_agent" │
|
||
│ - description: "负责执行 Planner 的首个步骤并反馈" │
|
||
│ - systemPrompt: buildExecutorPrompt() (Line 241-257) │
|
||
│ - outputKey: "executor_feedback" │
|
||
│ │
|
||
│ ③ supervisorAgent = SupervisorAgent.builder() (Line 59-65)│
|
||
│ - name: "ai_ops_supervisor" │
|
||
│ - systemPrompt: buildSupervisorSystemPrompt() │
|
||
│ - subAgents: [plannerAgent, executorAgent] │
|
||
└────────────────────────────────────────────────────────────┘
|
||
↓
|
||
┌────────────────────────────────────────────────────────────┐
|
||
│ Step 2: Supervisor 编排执行 (Line 70) │
|
||
│ │
|
||
│ supervisorAgent.invoke(taskPrompt) │
|
||
│ │
|
||
│ 编排逻辑(内置于 SupervisorAgent): │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ Loop until decision == FINISH: │ │
|
||
│ │ │ │
|
||
│ │ 1. 调用 planner_agent │ │
|
||
│ │ → 输出 decision: PLAN/EXECUTE/FINISH │ │
|
||
│ │ │ │
|
||
│ │ 2. if decision == EXECUTE: │ │
|
||
│ │ 调用 executor_agent │ │
|
||
│ │ → 执行第一步工具调用 │ │
|
||
│ │ → 返回 executor_feedback │ │
|
||
│ │ │ │
|
||
│ │ 3. 将 executor_feedback 传回 planner │ │
|
||
│ │ → planner 重新规划 (Replanner 角色) │ │
|
||
│ │ │ │
|
||
│ │ 4. if decision == FINISH: │ │
|
||
│ │ planner 输出最终 Markdown 报告 │ │
|
||
│ │ → break │ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
└────────────────────────────────────────────────────────────┘
|
||
↓
|
||
┌────────────────────────────────────────────────────────────┐
|
||
│ Step 3: 工具调用(在 Executor 阶段) │
|
||
│ │
|
||
│ Executor Agent 根据 Planner 的计划调用工具: │
|
||
│ │
|
||
│ ① QueryMetricsTools.queryPrometheusAlerts() │
|
||
│ → 查询 Prometheus 活跃告警 │
|
||
│ → Mock 模式返回模拟数据(HighCPUUsage, etc.) │
|
||
│ │
|
||
│ ② QueryLogsTools.queryLogs(告警名称, 日志主题) │
|
||
│ → 查询腾讯云 CLS 日志 │
|
||
│ → Mock 模式返回与告警关联的模拟日志 │
|
||
│ │
|
||
│ ③ InternalDocsTools.queryInternalDocs(关键字) │
|
||
│ → RAG 知识库检索 │
|
||
│ → 从 Milvus 检索相关文档 │
|
||
│ │
|
||
│ ④ DateTimeTools.getCurrentDateTime() │
|
||
│ → 获取当前时间(用于计算告警持续时间) │
|
||
└────────────────────────────────────────────────────────────┘
|
||
↓
|
||
AiOpsService.extractFinalReport(state) (Line 79-94)
|
||
- 从 state.value("planner_plan") 提取 Planner 最终输出
|
||
- 返回 Markdown 格式的告警分析报告
|
||
↓
|
||
ChatController 通过 SSE 流式返回前端 (Line 312-314)
|
||
- event: message
|
||
- data: {"type":"content","data":"# 告警分析报告\n..."}
|
||
↓
|
||
前端渲染 Markdown
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 替代方案对比
|
||
|
||
| 方案 | 优点 | 缺点 | 为什么不选 |
|
||
|------|------|------|-----------|
|
||
| **单 Agent** | 简单,易维护 | 无法重新规划,调试困难 | 无法处理"执行失败后重新规划"的场景 |
|
||
| **2-Agent (Planner + Executor)** | 角色清晰 | 需要外部循环逻辑,状态管理复杂 | 缺少 Supervisor 统一调度,状态传递困难 |
|
||
| **静态工作流(DAG)** | 确定性强 | 无法动态调整 | 告警场景不确定,无法提前定义 DAG |
|
||
| **ReAct Loop (单 Agent 循环)** | 通用性强 | 规划与执行混杂,输出格式不稳定 | 无法保证"先规划后执行"的顺序 |
|
||
|
||
---
|
||
|
||
## ⚖️ 权衡分析
|
||
|
||
**为什么选择 3-Agent 协同?**
|
||
|
||
| 维度 | 收益 | 代价 |
|
||
|------|------|------|
|
||
| **职责清晰** | ✅ 每个 Agent 只做一件事,易于调试 | ❌ 多一个 Supervisor,代码量增加 |
|
||
| **自适应能力** | ✅ Executor 失败后,Planner 可以重新规划 | ❌ 需要设计 feedback 传递机制 |
|
||
| **输出稳定性** | ✅ Supervisor 保证"只有 FINISH 才输出报告" | ❌ 需要在 Prompt 中明确约束 |
|
||
| **可扩展性** | ✅ 可以轻松添加新的 Agent(如 Reviewer) | ❌ Supervisor 逻辑会变复杂 |
|
||
|
||
---
|
||
|
||
## 📦 迁移示例(≤20 行)
|
||
|
||
```java
|
||
// 1. 定义 3 个 Agent
|
||
ReactAgent planner = ReactAgent.builder()
|
||
.name("planner")
|
||
.systemPrompt("制定计划,输出 decision: PLAN/EXECUTE/FINISH")
|
||
.outputKey("plan")
|
||
.build();
|
||
|
||
ReactAgent executor = ReactAgent.builder()
|
||
.name("executor")
|
||
.systemPrompt("执行计划的第一步,返回 feedback")
|
||
.outputKey("feedback")
|
||
.tools(yourTools) // 注入工具
|
||
.build();
|
||
|
||
SupervisorAgent supervisor = SupervisorAgent.builder()
|
||
.name("supervisor")
|
||
.systemPrompt("循环调度 planner 和 executor 直到 FINISH")
|
||
.subAgents(List.of(planner, executor))
|
||
.build();
|
||
|
||
// 2. 启动编排
|
||
OverAllState result = supervisor.invoke("分析这个问题...");
|
||
```
|
||
|
||
---
|
||
|
||
## ⚠️ 关键陷阱
|
||
|
||
| 陷阱 | 后果 | 避免方法 |
|
||
|------|------|---------|
|
||
| **Prompt 未明确"只执行第一步"** | Executor 会执行所有步骤,Planner 无法插手 | 在 Executor Prompt 中强调"只执行其中的第一步" |
|
||
| **未设置 outputKey** | 状态无法传递,Planner 无法读取 feedback | 每个 Agent 必须设置 `outputKey` |
|
||
| **Planner 在 EXECUTE 阶段输出最终报告** | 流程短路,Supervisor 无法控制 | Prompt 中明确"FINISH 时才输出 Markdown" |
|
||
| **Supervisor Prompt 缺失循环逻辑** | 只执行一轮就结束 | Supervisor Prompt 必须说明"直到 decision=FINISH" |
|
||
| **工具调用失败未反馈给 Planner** | Planner 无法重新规划,陷入死循环 | Executor 必须在 feedback 中记录失败原因 |
|
||
|
||
**代码证据**:
|
||
- `AiOpsService.java:228-233` - 防止 Planner 提前输出报告的约束
|
||
- `AiOpsService.java:245-246` - Executor 对工具失败的处理机制
|
||
|
||
---
|
||
|
||
## 📁 核心文件索引
|
||
|
||
| 文件 | 关键行 | 作用 |
|
||
|------|--------|------|
|
||
| `ChatController.java` | 280-314 | HTTP 入口 + SSE 流式返回 |
|
||
| `AiOpsService.java` | 51-70 | 3-Agent 构建与编排 |
|
||
| `AiOpsService.java` | 100-109 | Planner Agent 构建 |
|
||
| `AiOpsService.java` | 115-124 | Executor Agent 构建 |
|
||
| `AiOpsService.java` | 144-235 | Planner Prompt(含 Replanner 逻辑) |
|
||
| `AiOpsService.java` | 241-257 | Executor Prompt(只执行第一步) |
|
||
| `AiOpsService.java` | 263-277 | Supervisor Prompt(循环调度) |
|
||
| `AiOpsService.java` | 79-94 | 最终报告提取逻辑 |
|
||
|
||
---
|
||
|
||
## 🎓 学习检查点
|
||
|
||
完成本报告后,你应该能回答:
|
||
|
||
- [ ] 为什么用 3 个 Agent 而不是 1 个?
|
||
- [ ] Planner 的 Replanner 角色是什么意思?
|
||
- [ ] Executor 为什么只执行"第一步"?
|
||
- [ ] Supervisor 如何知道该调用哪个 Agent?
|
||
- [ ] 如果 Executor 执行失败会发生什么?
|
||
- [ ] outputKey 的作用是什么?
|
||
- [ ] 如何从 state 中提取最终报告?
|
||
|
||
---
|
||
|
||
> 💡 **延伸阅读**:
|
||
> - [outputKey 深度解析](./02-outputKey-深度解析.md)
|
||
> - [3个核心疑问解答](./03-核心疑问解答.md)
|