SRE incident Co-Pilot 
当告警触发时,工程师通常是从零开始,寻找运维手册(runbook)、检查指标、弄清楚首先该尝试什么。这个工具试图缩短这个过程。
它包含一个 Streamlit agent 聊天界面,可以通过告警链接打开,并预加载了上下文。它会建议运维手册的步骤和诊断命令,还可以提出 K8s manifest 的修改建议。
灵感来源于 Inbar Rose 的 [高效 Agent 系统的七个习惯](https://agent-habits.github.io/habits/) 以及 [Splunk 2025 年可观测性现状](https://www.splunk.com/en_us/blog/observability/state-of-observability-2025.html)。
## 架构
```
%%{init: {'theme': 'base', 'themeVariables': {'background': '#e0f7fa', 'primaryBorderColor': '#37474f', 'lineColor': '#2c3e50', 'tertiaryColor': '#e0f7fa'}}}%%
flowchart TB
subgraph k8s[K8s Cluster]
api[eg. Inference API
/v1/chat /metrics]
end
api -- /metrics --> prom[Prometheus
scrape and evaluate rules]
prom -- firing alert --> am[Alertmanager
route to Slack]
am -- webhook --> slack[Slack
Troubleshoot with CoPilot agent link]
slack -- engineer clicks --> ui
subgraph ui[Streamlit UI]
agent[ReActAgent
alert context in system prompt]
user[Engineer query] --> agent
agent --> direct[get_runbook_steps
direct file lookup by metric]
direct --> sm[get_next_step
workflow constraint: which steps are valid?]
sm --> agent
agent --> user
end
style k8s fill:#2e7d32,color:#ffffff,stroke:#1b5e20,stroke-width:2px
style prom fill:#e65100,color:#ffffff,stroke:#bf360c,stroke-width:2px
style am fill:#c62828,color:#ffffff,stroke:#b71c1c,stroke-width:2px
style slack fill:#1565c0,color:#ffffff,stroke:#0d47a1,stroke-width:2px
style ui fill:#00838f,color:#ffffff,stroke:#006064,stroke-width:2px
linkStyle default stroke-width:4px,stroke:#1a237e
```
## 快速开始
```
# 1. 安装依赖
pip install -r requirements.txt
# 2. 启动 chatbot
streamlit run alert_app.py --server.port 8501
# 3. 在浏览器中打开
open http://localhost:8501/?alert_id=demo-001&service=inference-api&metric=p99_latency&severity=critical
```
### 结合 Prometheus + Slack 的完整 E2E 流程
```
# 构建 inference API 镜像并部署到 Kind
docker build -t inference-api:latest inference-api/
kind create cluster --config k8s/kind-config.yaml
kind load docker-image inference-api:latest
kubectl apply -f k8s/
# Port-forward 服务
kubectl port-forward svc/inference-api 8081:8000 &
kubectl port-forward svc/prometheus 9091:9090 &
kubectl port-forward svc/slack-mock 5000:5000 &
# 触发告警
curl -X POST http://localhost:8081/debug/set-latency \
-H "Content-Type: application/json" -d '{"latency_ms": 2000}'
# 发送流量,然后检查 http://localhost:9091/alerts
# 模拟 Slack 通知
python simulate_alert.py
```
## 项目结构
```
alert-chatbot/
├── alert_chatbot.py # ReActAgent + 5 read-only FunctionTools
├── alert_app.py # Streamlit UI with alert context pre-loading
├── telemetry.py # Structured JSON audit logger
├── simulate_alert.py # Send a mock Slack alert with chatbot link
├── slack_webhook_server.py # Local Slack webhook mock for testing
├── test_e2e.sh # Automated end-to-end test script
├── requirements.txt
├── Makefile
├── data/runbooks/*.md # 6 sample runbook documents
├── inference-api/
│ ├── app.py # FastAPI with Prometheus metrics + debug endpoints
│ └── Dockerfile
├── prometheus/
│ ├── alert.rules.yml # Alert rules for inference API
│ └── alertmanager.yml # Config for Slack routing
└── k8s/
├── kind-config.yaml
├── inference-api-deploy.yaml
├── prometheus-deploy.yaml
├── prometheus-rbac.yaml
└── alertmanager-deploy.yaml
```
## 工具与依赖
| 组件 | 技术 |
|---|---|
| Agent 框架 | 带有 `ReActAgent` 的 `llama-index-core` |
| LLM | Ollama (`llama3.1`) |
| 运维手册检索 | 根据指标名称直接查找文件 |
| Web UI | Streamlit |
| Inference API | FastAPI + `prometheus_client` |
| Kubernetes | Kind (本地集群) |
| 监控 | Prometheus + Alertmanager |
| 仪表板 | Grafana (可导入的仪表板位于 `config/sre-grafana-dashboard.json`) |
## 7 个习惯如何应用
### 习惯 1 - 明确界定的角色
Agent 应该具有明确、狭窄的角色,由它们能做什么、不能做什么决定以及它们绝不拥有的权限来界定,从而使它们保持可信、可审计且可组合。
该 Agent 负责:
- 为触发的指标加载运维手册
- 运行诊断工作流约束 (`get_next_step`) 以确定有效的后续步骤
- 建议只读的诊断命令 (`suggest_diagnostic_command`)
- 提出修复方案 (`assess_options`) 和 manifest 更改 (`propose_manifest`) 供工程师审查
该 Agent 明确**不**负责:
- 执行任何命令或变更操作,所有工具仅返回文本/JSON
- 决定诊断顺序,`get_next_step` 在代码中限制了有效步骤
- 在诊断完成前进行升级,`assess_options`/`propose_manifest` 上的代码级防护会拒绝过早的升级调用
- 应用更改,`propose_manifest` 仅在 UI 批准后写入磁盘;没有任何工具会调用 `kubectl`、`curl` 或改变状态
这种有边界的角色使得 Agent 在压力下具有可预测性,在事后复盘中可审计,并且能够安全地与现有的待命 pipeline 结合,而不会产生意想不到的副作用。
### 习惯 2 - 嵌入工作流
一个 Agent 的价值不是由它独立操作的程度来衡量的,而是由它融入的程度来衡量的。这个 Agent 不会创建自己的工作流;它会插入到现有的待命告警 pipeline 中。
```
Prometheus fires → Alertmanager routes to Slack → engineer clicks link → agent appears with alert context pre-loaded
```
Agent 在自然的决策点(当工程师打开告警时)接收输入,产生适合现有界面(聊天)的文本输出,并顺利交接给决定是否采取行动的工程师。它在不重新定义工作流的前提下对其进行增强,将 Prometheus 指标和运维手册内容转化为可操作的建议,然后退居幕后。采用这种方式很自然,因为告警工作流没有任何改变;Agent 只是让找到正确的下一步变得更快。
### 习惯 3 - 显式约束
约束不是事后添加的护栏,它们是 Agent 接口的一部分。它们定义了默认允许哪些操作、哪些需要推迟以及哪些完全被禁止。而且它们必须由系统强制执行,而不仅仅是在 prompt 中描述。
| 约束 | 如何执行 |
|---|---|
| 无直接变更 | `FunctionTool` 仅封装返回文本的 Python 函数,不存在用于重启、故障转移或任何状态更改的工具 |
| 无 shell 执行 | `suggest_diagnostic_command` 返回字符串,系统从不调用 `os.system()` 或 `subprocess` |
| 来自 URL 参数的告警上下文 | 告警数据从 URL query string 中解析,无需 API 调用,无需网络权限 |
| `max_iterations=12` | `MAX_ITERATIONS=12` 涵盖了完整的诊断循环(运维手册 + 3 步迭代 + 评估)并带有缓冲周期 |
| 拒绝幻觉工具调用 | `_extract()` 防护会丢弃格式错误的工具调用,并将列表类型的参数合并为逗号分隔的字符串;当传递未知的步骤 ID 时,`get_next_step` 会发出警告 |
| 步骤 ID 强制执行 | 如果 Agent 传递了虚构的步骤名称而不是来自 `valid_next_steps` 的确切 ID,`get_next_step` 将返回一个列出被忽略 ID 的 `warning` 字段 |
| 强制步骤防护 | `assess_options` 和 `propose_manifest` 在内部针对诊断工作流检查 `completed_steps`;如果强制性诊断步骤未完成,则返回错误字典 |
| 不可逆操作标记 | `_assess_irreversibility()` 确定性地扫描 Agent 的输出以查找关键字(`scale`、`restart`、`delete`、`failover` 等)并设置 `has_irreversible_suggestion`;由代码强制执行,而不是留给 LLM |
| Manifest 提案受控 | `propose_manifest` 返回 YAML 文本;manifest 仅在 UI 批准后写入文件,绝不通过 Agent 写入。更新后的 manifest 绝不由 Agent 部署。 |
这些约束使权限变得清晰可见。检查代码库的工程师可以准确地看到 Agent 能做什么和不能做什么。权限可以被审计,行为在压力下是可预测的,并且故障模式是有边界的。
### 习惯 4 - 推迟不可逆性
**推迟(Deferral)** 是 Agent 确认不确定性的机制。它在保留人类对不易撤销操作的决定权的同时,贡献了智能。没有任何破坏性操作会被自动化 —— Agent 可以建议重启或提出 manifest 更改,但它从不执行或应用任何东西。对于任何重要的事情,工程师仍然是决策者。
**不可逆性** 是在显示给用户之前,对 Agent 的输出文本使用关键字扫描来确定性地检测的。这比要求模型自我评估更可靠,因为小型 LLM(3B-9B)生成结构化不可逆性声明的一致性较差。
`AgentResponse` Pydantic 模型携带结果:
```
class AgentResponse(BaseModel):
reasoning: str # narrative shown to the engineer
has_irreversible_suggestion: bool # determined post-hoc
irreversible_reason: str | None # one sentence explaining why
confidence: float # 0.0-1.0, derived from workflow step completeness
manifest_yaml: str # extracted YAML if agent proposes a manifest
```
**置信度(Confidence)** 是根据所有强制性诊断步骤是否完成(来自 `get_next_step().all_mandatory_complete`)来设置的,而不是来自模型输出:
- 如果所有强制性诊断步骤均已完成,则为 `0.8`
- 如果强制性步骤仍未完成,则为 `0.3`
当建议了不可逆操作时,**UI 会以显眼的红色错误横幅向工程师发出警告**,显示确切原因(例如,“⚠️ 检测到不可逆操作:建议涉及:upgrade”)。如果建议包含 manifest 更新,则会显示一个带有复选框和“批准”按钮的批准关卡。对于不包含 manifest 的信息性建议(例如,升级决定),会显示红色警告 —— 由工程师独立评估并采取行动。
**示例:事件期间的配置更改:**
Agent 建议更新 K8s ConfigMap 以缓解数据库瓶颈。它调用 `propose_manifest(service="inference-api", change_type="config_update", params='{"key":"DB_POOL_SIZE","value":"50"}')`,返回确切的 YAML manifest。UI 在带有红色警告的代码块中显示提议的 manifest:“检测到不可逆操作:针对 inference-api 的提议 manifest 更改”。工程师审查 YAML,确认复选框(“我已经审查了 manifest 并批准此更改”),然后点击“批准”。批准后,manifest 将被写入 `/tmp/
_manifest.yaml` —— 准备好执行 `kubectl apply`。Agent 从不直接应用更改。
这被标记为不可逆是因为,配置错误的 manifest(例如,将 `DB_POOL_SIZE` 设置得过高或过低)可能导致服务崩溃或无响应,从而导致面向用户的停机。一旦用户无法访问服务,他们可能会迁移到替代方案,这种信任和采用的丧失实际上是不可逆的。该关卡确保工程师在应用之前停下来验证 YAML。糟糕部署的影响不仅仅是技术回滚 —— 它是一个无法撤销的业务后果。
**示例:Kubernetes 升级决定:**
`inference-api` 上反复出现的 pod 崩溃循环已被追踪到 1.26.3 中已知的 Kubernetes bug(在 1.27.x 中修复)。Agent 无法升级集群 —— 它展示了权衡并将决定推迟。输出显示了两个选项及其风险,然后明确声明该决定被推迟给工程师,因为 Agent 无法评估业务风险承受能力。
**Agent 输出:**
UI 将其显示为没有批准复选框的红色信息警告 —— 工程师独立评估并采取行动。没有提出 manifest,因为没有安全的 manifest 可以写入。
### 习惯 5 - 为系统结果进行优化
成功是通过 MTTR 和升级率来衡量的,而不仅仅是 Agent 的准确性。Agent 的存在是为了比搜索文档更快地将相关的运维手册步骤展示给工程师。系统级结果通过内置的反馈循环进行跟踪:
**解决后反馈** - 在每次会话之后,工程师会对建议是否有帮助(是 / 部分 / 否)以及他们实际做了什么来解决它进行评分。这将作为 `resolution_feedback` 事件记录在审计跟踪中,并附带完整的会话上下文。
**工单重开跟踪** - 每次会话都会检查同一指标先前的审计日志。如果相同的告警以前触发过,则重开计数将显示在告警上下文中,并记录在 `session_start` 事件中。指标重开计数的上升导致积压工作增加,这表明之前的解决方案不完整 —— 从而促使对运维手册或修复步骤进行更深入的审查。
**关闭工单** - 告警将保持触发状态,直到工程师点击“关闭工单”按钮,该按钮记录解决时间并计算 MTTR 为 `close_time - alert_start_time`。这确保了工程师必须在工具中采取明确的行动,而不是在外部将其关闭,从而保持反馈循环完整。结果显示在 UI 中,并作为带有 `mttr_seconds` 的 `ticket_closed` 事件记录在审计跟踪中,从而实现针对单个事件和总体 MTTR 的分析。然后,将遵循 Agent 建议的会话的 MTTR 与 Agent 建议没有帮助的会话的 MTTR 进行比较 —— 从而可以量化 Agent 是真的缩短了事件处理时间还是仅仅增加了噪音。
**Grafana 仪表板** - 指标导出器(`metrics_exporter.py`)在端口 `9100` 上的 `/metrics` 路径下将审计数据作为 Prometheus 指标提供服务。Prometheus 通过 `host.docker.internal:9100` 对其进行抓取。一个可导入的 Grafana 仪表板(`config/sre-grafana-dashboard.json`)可视化总会话数、按指标划分的重开计数、每个事件的 MTTR、工单积压(随时间推移的已打开与已关闭工单对比)以及有帮助与无帮助的反馈 —— 使得 Agent 的有效性一目了然。
这些机制闭合了 Agent 建议与真实事件结果之间的循环,将主观的“它有帮助吗”转变为可审计、可指标追踪的信号。通过将反馈记录与告警时间戳相关联,可以将 Agent 辅助解决时的 MTTR 与工程师报告 Agent 没有帮助时的 MTTR 进行比较 —— 从而提供一种直接的、汇总的方法来衡量 Agent 是否提供了帮助。
### 习惯 6 - 通过结构取得进展
Agent 系统的成熟度并不意味着更多的自主性,而是意味着更多的可预测性。这里的每一个结构决策都是将复杂性从 prompt 中移出并转移到代码中。
| 原则 | 在本项目中的实现方式 |
|---|---|
| **是工具,而非知识** | `get_runbook_steps(metric)` 通过 `RUNBOOK_MAP` 直接根据指标名读取运维手册文件。没有相似性搜索,没有 embedding 赌博。当运维手册更新时,Agent 会立即获取它,无需重新索引。 |
| **是状态机,而非对话状态** | `DIAGNOSTIC_WORKFLOWS` 在代码中定义了每个指标的步骤顺序(例如 `p99_latency`:检查健康状况 → 检查延迟指标 → 检查 pod 资源 → 评估选项 → 提出 manifest)。Agent 调用 `get_next_step(metric, completed_steps)` 以找出下一步该做什么。返回 `valid_next_steps`(受限集合,当强制性诊断未完成时排除升级步骤)、`all_mandatory_complete` 和已跟踪的 `completed` 步骤。如果 Agent 传递了未知的步骤 ID(例如,编造的章节名称),`warning` 字段会列出被忽略的 ID。Agent 从列表中进行选择,而不是遵循固定的顺序。它从不跟踪自己在哪里,而是由系统来做。状态存在于工具参数中,而不是对话历史中。 |
| **严格的接口** | `propose_manifest` 接受 `change_type` 枚举(`scale_replicas`、`config_update`、`env_update`、`resource_limits`、`rollback`),而不是自由格式的文本。每个工具都返回带有 `irreversible` 和 `reason` 字段的类型化 JSON。`AgentResponse` 是一个 Pydantic模型,下游消费者(UI、审计日志、升级关卡)依赖于已知的字段名称,而不是叙述性文本。 |
| **代码胜过模型** | `_assess_irreversibility()` 使用正则表达式扫描不可逆关键字(`upgrade`、`scale`、`restart`、`delete`、`rollback`),而不是要求 LLM 进行自我评估。 |
| **枯燥的失败模式** | 如果该指标不存在运维手册,`get_runbook_steps` 将返回一个明确的错误原因,并且 UI 将显示预定义的“无可用运维手册”消息,而不是让 Agent 产生幻觉。没有创造性的谎言,只有可预测的降级。 |
| **有边界的推理和上下文** | `MAX_ITERATIONS=12` 涵盖了完整的诊断循环(运维手册 + 3 步迭代 + 评估)并带有缓冲周期。主要路径使用直接文件读取,从而完全避免了主诊断流程的上下文窗口风险。 |
| **精简的 prompt** | 系统 prompt 大约有 5 行。所有行为都存在于具有类型化 schema 的 5 个工具函数中。逻辑从 prompt 中移出到代码中,这正是结构所指明的方向。 |
**示例 1:通过 `get_runbook_steps` 直接查找运维手册**
inference-api 服务上触发了 p99 延迟为 520ms 的告警。Agent 调用 `get_runbook_steps(metric="p99_latency")`。`RUNBOOK_MAP` 字典将指标名直接映射到 `data/runbooks/high-latency.md`。Python 的 `Path.read_text()` 读取文件的每一个字节。运维手册上写着:“步骤 1:`curl -s http://inference-api:8000/metrics | grep inference_latency`,检查 p99 是否超过 500ms。”Agent 报告确切的命令。没有分块,没有 embedding,没有相似性赌博。运维手册在 30 秒前被编辑过,Agent 会立即获取它。
**示例 2:通过 `get_next_step` 进行诊断状态机操作**
工程师正在进行 p99 延迟诊断,并且已经检查了健康端点。Agent 调用 `get_next_step(metric="p99_latency", completed_steps="check_health_endpoint")`。`DIAGNOSTIC_WORKFLOWS` 在代码中定义了顺序:`["check_health_endpoint", "check_latency_metrics", "check_pod_resources", "assess_options", "propose_manifest"]`。系统回复 `{"valid_next_steps": ["check_latency_metrics", "check_pod_resources"], "mandatory_before_escalation": ["check_latency_metrics", "check_pod_resources"], "completed": ["check_health_endpoint"], "all_mandatory_complete": false}`。Agent 从列表中进行选择,它并不决定顺序。在所有强制性诊断完成之前,升级步骤(`assess_options`、`propose_manifest`)将被排除。状态存在于工具参数中,而不是对话记忆中。
### 习惯 7 - 可见的问责制
如果你不能解释为什么会做出某个决定,你就不拥有这个系统,你只是在旁观它。日志仅仅是记录;问责制需要追踪*意图*,而不仅仅是*输出*。这里的每一个结构决策都确保了“为什么”跟随着“是什么”。
| 原则 | 在本项目中的实现方式 |
|---|---|
| **决策追踪,而不仅仅是日志** | 每次运行 Agent 都会产生一个 `decision_trace` 事件,记录 `intent`(根据告警指标 URL 参数硬编码为 `"diagnose_{metric}"`,而不是由 LLM 生成)、`context_retrieved`(查找了哪本运维手册)、`constraint_checks`(评估的指标阈值)、`policies_applied`(该指标的所有诊断工作流步骤)和 `tool_chain`(实际调用的工具的有序列表)。这无需重放模型即可回答“Agent 为什么那样做”。 |
| **追踪 ID 将因果联系起来** | 每次 Agent 运行都会生成一个由所有 `tool_call`、`decision_trace` 和 `interaction` 事件共享的 `trace_id`(UUID)。通过 `trace_id` 过滤事件可以重建完整的因果链,从用户查询到工具调用再到最终响应。 |
| **每个工具都有归属团队** | 每个工具都在 `TOOL_OWNERS` 中标记了一个人类团队。如果 `propose_manifest` 产生了错误的 YAML,工单将路由到 **Platform Team**,而不是“AI 团队”。这将模型故障转化为路由到正确团队的过程改进信号。 |
| **在响应中要求引用策略** | 系统 prompt 指示 Agent 按名称引用运维手册章节(例如“根据 [章节:诊断步骤]...”)。`cited_sections` 字段是从诊断工作流定义中确定性地派生出来的,每次 `get_next_step(metric, completed_steps)` 调用都会记录 Agent 完成了哪些步骤。这些步骤 ID 通过 `_derive_cited_sections()` 映射为人类可读的标签。无需文本扫描,无需启发式匹配,也没有模型合规性风险。 |
| **每个操作都有决策元数据** | 每次工具调用都带有一个从工具名称和参数派生出来的确定性 `reason_code`,LLL 对此无能为力。`suggest_diagnostic_command(service="inference-api", symptom="latency")` → `reason_code="diagnostic_command_for_latency"`。`propose_manifest(service="inference-api", change_type="scale_replicas")` → `reason_code="propose_scale_replicas_change"`。`get_next_step(metric="p99_latency", completed_steps="")` → `reason_code="query_first_diagnostic_step"`。在遥测包装层通过 `_derive_reason_code()` 生成,而不是由模型生成。模型合规性风险为零。 |
### 示例:使用不支持的 change_type 调用 propose_manifest
**它不会这样工作:**
Agent:“我无法生成该 manifest。”
**它会这样工作:**
Agent:“根据 [allowed-manifest-changes.yaml],不支持更改类型 'drain_node';接受的值为:config_update, scale_replicas, env_update, resource_limits, rollback。
请选择支持的 change_type,或者如果需要添加新的更改类型,请根据 [tool-owners.yaml] 升级给 [Platform Team]。”
完整的事件目录:
| 事件 | 关键字段 |
|---|---|
| `session_start` | `session_id`, `alert_id`, `metric`, `repeat_count` |
| `tool_call` | `trace_id`, `tool_name`, `owner_team`, `reason_code`, `args`, `result_preview`, `duration_ms` |
| `decision_trace` | `trace_id`, `intent`, `confidence`, `context_retrieved`, `constraint_checks`, `policies_applied`, `tool_chain` |
| `interaction` | `trace_id`, `user_query`, `agent_response`, `cited_runbooks`, `cited_sections`, `irreversibility fields` |
| `approval_requested` | `suggestion_text`, `irreversible_reason`, `user_confirmed` |
| `resolution_feedback` | `helped`, `actual_fix` |
| `ticket_closed` | `session_start`, `close_time`, `mttr_seconds` |
**追踪后示例**:当工程师询问“帮我排查 inference-api”后,审计跟踪的样子如下:
```
# decision_trace(每次 agent 运行一个)
trace_id=abc-123 intent=diagnose_p99_latency confidence=1.0
context_retrieved=["data/runbooks/high-latency.md"]
constraint_checks=[{"metric":"p99_latency","threshold":"<500ms p99","unit":"ms","source":"alert_context"}]
policies_applied=["Check Health Endpoint","Check Latency Metrics","Assess Remediation Options"]
# tool_call 事件(全部共享 trace_id=abc-123)
trace_id=abc-123 tool=get_runbook_steps owner=SRE Runbook Team
trace_id=abc-123 tool=get_next_step owner=SRE Runbook Team
trace_id=abc-123 tool=suggest_diagnostic_command owner=SRE Engineering reason_code="diagnostic_command_for_latency"
trace_id=abc-123 tool=get_next_step owner=SRE Runbook Team completed_steps="check_health_endpoint"
# interaction(链接到相同的 trace_id)
trace_id=abc-123 cited_runbooks=["data/runbooks/high-latency.md"]
cited_sections=["Check Health Endpoint"]
irreversibility={"has_irreversible_suggestion": false}
```
每个事件都带有共享的 `trace_id`。通过它进行过滤以重建完整的链条:意图、查阅了哪本运维手册、按顺序调用了哪些工具(具有归属团队和确定性 `reason_code`),以及 Agent 在其响应中实际引用了哪些章节。
## 示例会话
**工程师点击告警链接 → 聊天机器人加载:**
```
Alert ID: PD-abc123
Service: inference-api
Metric: p99_latency
Severity: critical
Time: 2026-06-02T10:32:00Z
```
**工程师输入:** *“帮我排查 inference-api”*
**Agent 工具追踪(详细日志):**
```
get_runbook_steps(metric="p99_latency")
→ Runbook: High Inference Latency ... (full file content)
get_next_step(metric="p99_latency", completed_steps="")
→ {"valid_next_steps": ["check_health_endpoint", "check_latency_metrics", "check_pod_resources"], "all_mandatory_complete": false}
suggest_diagnostic_command(service="inference-api", symptom="health")
→ curl -s http://inference-api:8000/health
get_next_step(metric="p99_latency", completed_steps="check_health_endpoint")
→ {"valid_next_steps": ["check_latency_metrics", "check_pod_resources"], "all_mandatory_complete": false}
suggest_diagnostic_command(service="inference-api", symptom="latency")
→ curl -s http://inference-api:8000/metrics | grep inference_latency
get_next_step(metric="p99_latency", completed_steps="check_health_endpoint,check_latency_metrics")
→ {"valid_next_steps": ["check_pod_resources"], "all_mandatory_complete": false}
suggest_diagnostic_command(service="inference-api", symptom="resources")
→ kubectl top pods -l app=inference-api
get_next_step(metric="p99_latency", completed_steps="check_health_endpoint,check_latency_metrics,check_pod_resources")
→ {"valid_next_steps": [], "all_mandatory_complete": true}
```
**工程师在 UI 中看到的内容:**
```
✅ Suggestions - you must execute them manually
# 1. 检查 inference API 健康状态
curl -s http://inference-api:8000/health
# 2. 检查当前延迟指标
curl -s http://inference-api:8000/metrics | grep inference_latency
# 3. 检查处理中的请求
curl -s http://inference-api:8000/metrics | grep inference_requests_in_flight
# 4. 检查 pod 资源使用情况
kubectl top pods -l app=inference-api
# 5. 检查 pod 日志以获取错误
kubectl logs -l app=inference-api --tail=50
# 6. 验证 model 是否已正确加载
kubectl exec deploy/inference-api -- curl -s http://localhost:8000/health
```