面试知识库

M0 · 项目全景与架构串讲#

一句话定位: 面向 OnCall/SRE 的多智能体智能运维诊断平台——告警进来,诊断报告和处置闭环出去,中间全自动。


开场钩子#

场景#

V3 上线跑了一轮压测,300 QPS burst 灌告警,系统没崩但 LLM 配额被打爆了——75 条告警建了 75 个诊断任务同时调 LLM,百炼 TPM 限流把整条链路卡死 15 秒。更尴尬的是,这 75 条告警其实是同一台机器的同一个故障,Alertmanager 的 group_interval 重复推了 75 次。

问题根因不是”没做限流”——限流做了,但只挡住了 API 层的 HTTP 洪峰,没挡住”相同故障重复建任务”这一层。这让我意识到:告警治理不是一层能解决的,需要逐层递进收敛,从 IP 级到告警级到事件组级到任务级到并发槽,每层用不同粒度的唯一键做幂等。

这个教训直接催生了整个系统的分层架构设计:入口层只做零智能的归一入队,预处理层用纯规则降噪(不碰 LLM),调查层按三级递进自适应决定投入多少算力,处置层用资源建模+事务化保证安全。

面试官切入#

“你提到了分层递进收敛——能展开讲讲整个系统的架构吗?“


一、模块运作流程#

1.1 一句话定位#

这是一个面向 OnCall/SRE 的多智能体 AIOps 平台:Alertmanager 告警或人工故障描述进来,系统自动完成降噪→调查→处置→验证的全闭环,输出可追溯的 Markdown 诊断报告。核心设计理念是确定性优先、LLM 按需介入——能用规则解决的绝不调模型。

1.2 全景流程图(文字版)#

模块间端到端链路:

  • 故障处理主线: M1(告警入口→三级递进调查) → M2(Tier 3 深度调查) → M3(Runbook 积累) → M4(安全执行修复) → M5(事务化验证闭环)
  • 全程驱动: M6(Incident 状态机) 贯穿始终,每个关键节点触发状态迁移
  • 支撑底座: M7(RAG 知识检索) 为 Tier 2/3 提供知识、M8(队列 Worker) 提供异步执行基础、M9(Benchmark) 保证质量

1.3 分步详解#

Step 1: 告警接入 (app/api/v1/webhook.py)#

  • 做什么: 接收 Alertmanager webhook payload(一组告警),归一化成内部 NormalizedAlert,落 Postgres
  • 怎么做: 两道 Redis Lua 固定窗口限流(单 IP 50/s、单 receiver 500/min)→ 告警按 {receiver}:{groupKey}:{fingerprint}:{5min桶}:{status} 做幂等去重(ON CONFLICT 只加 seen_count)→ 用 alertmanager:{receiver}:{groupKey} 算确定性 incident_group_id(SHA256 前 24 位)→ 部分唯一索引做任务去重 → 立即 202 返回
  • 为什么这么做: API 层的职责是”零智能快入账”。75 条相同告警压到 1 个任务入队,压缩比 75:1。限流保护 API 进程,去重保护队列和 Worker,入队保护 API 响应延迟

Step 2: L2 预处理 (app/preprocessing/engine.py)#

  • 做什么: 输入 NormalizedAlert,输出 IncidentContext(L2→L3 的契约对象)
  • 怎么做: 去重(指纹窗口内已见)→ 抖动检测(短时间内反复 firing/resolved)→ 时间窗聚合(同 correlation_key 的告警归为一组)→ 抑制(维护期/静默规则)→ 拓扑级联关联(app/topology/: Tarjan SCC 缩点 → 多跳反向可达 + 因果时序门 → 静默根因)→ Impact 评分(severity × 告警数 × 拓扑扇出 × 抖动惩罚)→ Runbook 预匹配
  • 为什么这么做: 零 LLM。告警风暴下每条都调模型会直接撞百炼 RPM/TPM 限流。被判重复/抑制的告警不入队——降噪率 = 1 − M/N 的事实来源

Step 3: 队列与 Worker (app/queue/redis_streams.py, app/diagnosis_worker.py)#

  • 做什么: API 进程投任务到 Redis Streams,Worker 消费执行诊断
  • 怎么做: 4 级优先级队列(critical/high/normal/low,severity 直接映射)→ Worker 用 XREADGROUP 消费,消费前先抢分布式并发槽(Redis ZSET,上限 32)→ 成功 ACK,失败重试 3 次后进 DLQ → Postgres 记录任务生命周期(pending/running/completed/failed)
  • 为什么这么做: API 和 Worker 解耦(API P50 4ms 返回);并发槽保护下游 LLM 配额;Postgres 是事实源,Redis 是运行态缓冲——Redis 挂了丢排队顺序不丢数据

Step 4: 分层自适应调查 (app/investigation/)#

  • 做什么: 输入 IncidentContext,输出根因结论 + 证据链 + 置信度
  • 怎么做: 唯一一张 LangGraph 图 (graph.py),Tier 1→2→3 顺序执行、命中即止:
    • Tier 1 (tier1.py): Runbook 指纹精确匹配 + 前置条件校验 + 冷却期检查。命中 → 确定性处置提案,零 LLM
    • Tier 2 (tier2.py): pgvector 向量检索历史 Incident → 便宜档小模型(qwen-turbo)限定 3 轮适用性复核。命中 → 复用历史 RCA
    • Tier 3 (tier3.py): 两阶段假设驱动——LLM 生成 3-5 条根因假设排序 → 逐条派 subagent 定向取证(metric_agent/log_agent 等),CONFIRMED 且置信达标即终止;全不达标 → BEST_EFFORT + ESCALATED
  • 为什么这么做: 图里没有入口分流开关。调用方是 Alertmanager,它不具备”该花多少算力”的判断力。走多深是图的输出,不是输入。impact_score 只调门槛不改结构

Step 5: 处置决策与安全治理 (app/remediation/)#

  • 做什么: 把调查结论转化为安全的修复动作
  • 怎么做: 资源域建模 (model.py: 操作 = 资源状态机上的迁移边,不是命令) → 白名单校验 (policy.py: 保护名单/封闭 Registry,不在册的一律拒) → 意图校验 (validate_intent: 参数正则/枚举/范围) → 审批(高风险走飞书移动端一键审批,interrupt() 挂起图等响应,超时自动降级 escalate_then_deny
  • 为什么这么做: 用调用白名单工具替代生成自由 shell 命令,消除命令注入与越权风险。Registry 是封闭集合 + 默认拒绝

Step 6: 事务化执行与验证闭环 (app/remediation/transaction.py, saga.py, regression.py)#

  • 做什么: 安全执行修复操作,验证修复效果,失败自动回滚
  • 怎么做: 提案生成(快照 pre_state + 计算 Recovery Plan)→ CAS 漂移检测(执行前重探资源态,与快照不一致则拒绝)→ 执行(事务工具)→ 三级递进门禁验证(命令成功 → 服务健康 → 指标恢复 → 告警消退)→ 失败自动补偿回滚(Saga 逆序执行 recovery_plan)
  • 为什么这么做: “执行完 ≠ 修好了”。单步事务保证原子性,Saga 保证多步计划的补偿一致性,三级门禁保证修复真实生效

Step 7: Runbook 自增长飞轮 (app/runbooks/)#

  • 做什么: Tier 3 的调查成果反哺 Tier 1,持续扩充确定性处置覆盖率
  • 怎么做: Tier 3 调查经人工确认 + 三级门禁通过 → LLM 从证据链提炼前置条件+处置步骤 (generator.py) → 生成结构化 Runbook 草稿(status=pending_review)→ 审核入库转 active → 下次同类告警在 Tier 1 命中
  • 为什么这么做: 飞轮要有刹车——草稿一律 pending_review,LLM 发明的动作会被资源域 Registry 拒收。冷启动期用 data/runbooks/*.yaml 种子 Runbook 覆盖高频场景

1.4 数据流 trace(一条告警走完全程)#

MySQL replica lag > 10s on db-slave-02 为例,走完 M0 涉及的端到端流程:

① 接入

  • 输入: Alertmanager POST /api/v1/webhook/alertmanager,payload 含 alertname=MySQLReplicaLag, instance=db-slave-02, severity=warning, fingerprint=a3c7e2f0...
  • 处理: 限流通过 → 归一成 NormalizedAlert(alertname, service, severity, fingerprint, labels) → 幂等键 oncall-sre:{}/MySQLReplicaLag:a3c7e2f0:bucket_17:firingON CONFLICT 入库 → 任务去重 → 入队 level=normal
  • 输出: HTTP 202 + Redis Streams 中一条消息 {task_id, incident_id, alert_signature}

② L2 预处理

  • 输入: NormalizedAlert + Redis 里的近期告警窗口
  • 处理: 去重(首次出现,非重复) → 抖动(无近期 resolved→firing 翻转,非抖动) → 聚合(同 correlation_key 窗口内无其他告警) → 抑制(无维护窗口) → 拓扑关联(db-slave-02 runs_on host-07, depends_on db-master-01; 查 host-07 和 db-master-01 无并发告警,无级联根因) → Impact 评分(severity=warning × 单条告警 × 无拓扑扇出 = 0.35)
  • 输出: IncidentContext(impact_score=0.35, suppressed=False, is_flapping=False, matched_runbooks=[mysql_replica_lag_runbook])

③ Worker 消费

  • 输入: Redis XREADGROUP 取到消息
  • 处理: 抢并发槽成功(ZCARD < 32)→ Postgres 任务状态 pending→running → 构建 LangGraph 图输入
  • 输出: 调查图开始执行

④ Tier 1 命中

  • 输入: IncidentContext(预处理已匹配到 mysql_replica_lag_runbook
  • 处理: 指纹精确匹配 ✓ → 前置条件校验(mysql_check_replication_status 探测确认 lag > 10s)✓ → 冷却期外 ✓ → Impact 未触发强制 Tier 3 ✓ → Tier 1 命中
  • 输出: Runbook 的 action_steps 缝合成处置提案(resource=service://systemctl/mysql-replica, operation=service.restart

⑤ 处置决策

  • 输入: 处置提案
  • 处理: validate_intent 校验意图合法 → 保护名单未命中 → Runbook 处置 + severity=warning → risk=medium → 自动执行(无需审批)
  • 输出: 审批通过的 TransactionProposal

⑥ 事务化执行

  • 输入: TransactionProposal
  • 处理: 快照 pre_state={active: true, lag: 12s} → CAS 检测(当前态与快照一致) → 执行 restart → 单步事务 committed → 三级门禁:命令成功 ✓ → 服务健康(端口 3306 可连) ✓ → 指标恢复(lag < 10s, 等待 30s 后复核) ✓
  • 输出: RegressionResult(passed=True), Incident → RESOLVED

⑦ 飞轮(本例不触发)

  • Tier 1 命中走的是已有 Runbook,不产生新草稿。若本例走到 Tier 3 才解决,则 LLM 会提炼新 Runbook 草稿进审核队列

1.5 技术选型决策表(汇总级)#

组件选了什么备选方案为什么选它什么情况下换
Web 框架FastAPIFlask, Django原生 async、自动 OpenAPI 文档、Pydantic 校验一体;OnCall 场景高并发 webhook 需要 async不太会换,除非要整合到已有 Django 单体
LLM 编排LangGraph自写循环, AutoGen, CrewAIStateGraph 处理条件分支+并行 fan-out 清晰;interrupt() 原生支持审批挂起;checkpoint 支持崩溃恢复Tier 1/2 的纯规则路径不需要 LangGraph,但统一在一张图里维护成本更低
LLM 提供商DeepSeek-V4 (经百炼)GPT-4o, Claude价格低一个量级(¥0.04/次诊断);中文运维场景表现够用百炼 RPM/TPM 是真实瓶颈,如果调用量上去需要直连官方或换多供应商轮转
向量库pgvector (Postgres 扩展)Milvus, Qdrant, Chroma复用已有 Postgres 连接池,零额外运维;BM25 用 ParadeDB pg_search 也在同一个 DB;当前数据量(千级 chunk)暴力搜比 IVFFlat 还快数据量到十万级需要加 HNSW 索引;如果需要多租户隔离,Qdrant 的 collection 模型更自然
BM25ParadeDB pg_search内存 rank_bm25之前用 Python rank_bm25 全量加载到内存,数据量大了 OOM;迁到 DB 侧后内存与检索解耦pg_search 生态还年轻,如果遇到分词/索引更新问题可回退到 Elasticsearch
RerankerDashScope rerank API本地 cross-encoder, Cohere云端免部署、免 GPU;冷启动阶段数据量小,本地模型没有质量优势延迟敏感场景或断网环境需要本地 cross-encoder
消息队列Redis StreamsKafka, RabbitMQ, SQS已有 Redis 实例(限流/并发槽也用它);consumer group + PEL 原生支持恰好一次语义;运维诊断吞吐量远不到 Kafka 门槛如果需要跨机房持久化或吞吐量上万 TPS,换 Kafka
任务事实库Postgres (asyncpg)MySQL, MongoDB事务+JSONB+pgvector+pg_search 全在一个 DB 里;Incident 状态机迁移用行锁+事务保证一致性不太会换,Postgres 是整个系统的事实源
工具协议MCP (Model Context Protocol)自定义 JSON-RPC, Function CallingAnthropic 提出的标准协议,工具服务独立进程运行,与主进程解耦;工具可被多个 Agent 复用MCP 生态还在早期,如果需要更低延迟的工具调用可改为进程内函数
审批通道飞书 WebSocket 长连接飞书 HTTP 回调, Slack, 自建审批无需公网 IP(WebSocket 从内网主动连飞书服务器);移动端一键审批体验好如果企业用钉钉/企微则换对应 SDK

1.6 口述脚本#

开场(30s): “这是一个面向 OnCall 的多智能体运维诊断平台。核心理念是确定性优先——能用规则解决的绝不调模型。系统分三大层:接入预处理层做纯规则降噪,一行 LLM 都不调;调查层按三级递进自适应决定投入多少算力——已知故障走 Runbook 零 LLM 处置,相似故障检索历史案例便宜验证,只有未知故障才进 ReAct 深度调查;处置层用资源建模+事务化保证安全执行。”

[留钩子]: “举个例子,一条 MySQL 主从延迟告警进来——“(等面试官追问)

展开(60s): “告警先过 L2 纯规则预处理,去重、抖动检测、拓扑关联、Impact 评分,全部零 LLM。然后进调查图——这张图里没有入口分流开关,Tier 1 先试 Runbook 指纹匹配,命中就直接出处置提案;没命中自动递进 Tier 2 检索历史案例,再没命中才进 Tier 3 假设驱动调查。处置走资源域建模,操作是状态机上的迁移边不是命令,白名单+CAS 漂移检测+三级门禁验证,失败自动补偿回滚。”

[留钩子]: “这个三级递进最有意思的设计决策是——走多深是系统的输出而不是输入。“(引导面试官问”为什么不让调用方选模式”)


二、踩坑实录#

坑 1:限流和队列共用 Redis,Fail-open 意外变安全#

  • 现象: 设计 Redis 依赖组件的容错策略时,限流模块选了 fail-open(Redis 挂了放行所有请求)。Code review 时被质疑”限流放开不会打爆 LLM 吗”
  • 根因: 限流、队列入队、并发槽用的是同一个 Redis。Redis 挂了 → 限流 fail-open 放行 → 但 XADD 入队也失败 → 请求到不了 Worker → LLM 不会被打爆。放行的请求只是多做了一次 Postgres 落库(告警不丢),并没有雪崩风险
  • 修法: 保持 fail-open 策略,但在文档和代码注释中明确标注”安全性依赖限流与队列同 Redis 实例”这个隐式假设。如果未来拆分 Redis 实例,这个假设需要重新审视
  • 教训: Fail-open vs Fail-closed 的选择不能只看单个组件,要看保护组件挂了之后被保护资源是否还能被直接访问。同挂则 fail-open 安全,不同挂则必须 fail-closed

坑 2:V3 的 fast/deep 双模式入口选择是个错误抽象#

  • 现象: V3 让 webhook 入口预判 fast/deep 模式(severity=critical → deep,其他 → fast)。结果 warning 级的跨域故障(多个服务同时异常但每条告警只是 warning)被分配到 fast,单链调查无法关联多域证据,输出低质量报告
  • 根因: 调用方是 Alertmanager,它只有告警级别信息,不具备”该花多少算力”的判断力。fast/deep 的选择实质上是在入口处做了一个信息不充分的决策
  • 修法: V4 砍掉 fast/deep 双模式,换成 Tier 1→2→3 分层自适应。调查深度是图的输出结果而不是入口参数——入口只提交告警,图内自动递进。“走多深”由告警本身的 Runbook 匹配、历史相似度、假设验证结果决定
  • 教训: 不要在信息最贫乏的地方做最关键的决策。入口处只有告警 label,诊断深度应该由诊断过程中逐步积累的证据来决定

坑 3:百炼调 DeepSeek-V4 的真实限流瓶颈是 TPM 不是 RPM#

  • 现象: 本地令牌桶按 RPM(每分钟请求数)配了限流参数,但压测时在 RPM 远未达限的情况下就开始大量 429
  • 根因: 通过百炼(DashScope)调 DeepSeek-V4 不是直连官方 API。百炼的限流策略是 TPM(每分钟 token 数)优先,一次诊断可能消耗 5K-20K token,几次调用就能打满 TPM 配额,而此时 RPM 才用了个位数
  • 修法: 限流参数从 RPM 改为 TPM 为主、RPM 为辅的双维度控制。同时在 L2 预处理层严格降噪(被判重复/抑制的告警不入队),作为防打满 TPM 的第一道闸
  • 教训: 通过中间商调 API 时,限流策略可能与官方文档描述的不一致。要实测真实约束,不能照搬文档配参数

坑 4:pgvector IVFFlat 索引在冷启动小数据量下召回率反而低于暴力搜索#

  • 现象: 从 Milvus 迁到 pgvector 后,给向量列建了 IVFFlat 索引(习惯性优化)。结果 RAG 召回率下降了约 15%
  • 根因: IVFFlat 靠聚类中心做近似搜索,数据量小(几千条 chunk)时聚类中心不稳定,部分相关文档被分到错误的聚类而漏召。暴力搜索(无索引)在这个量级反而是精确的,且延迟也在毫秒级
  • 修法: 删掉 IVFFlat 索引,冷启动阶段用暴力搜索。在 config 中预留 HNSW 索引的切换开关,等数据量上万再启用
  • 教训: 索引是有前提条件的优化。数据量不够时加索引不但没加速,还损失了精度。“先跑起来再优化”比”上来就建索引”更务实

三、量化评估#

M0 作为全景模块,汇总各子模块的核心量化指标(详细评估方法见各子模块文档):

指标数值来源说明
告警→任务压缩比75:1 (burst 场景)scripts/loadtest.py 300QPS×8s 压测2400 条告警 → 32 个并发诊断
API P50 响应延迟4ms压测报告入队后立即 202 返回
单次诊断 LLM 成本~¥0.04 (Tier 1/2), ~¥0.20 (Tier 3)token 台账统计DeepSeek-V4 经百炼
单次诊断耗时Tier 1: <5s, Tier 2: ~15s, Tier 3: 30s-3min端到端计时Tier 1 零 LLM 调用
RAG 检索管线Vector+BM25+RRF+Rerankscripts/eval_ragas.py具体指标见 M7
测试覆盖59 个测试文件, 532 个测试函数pytest单测+集成

四、面试问答#

基础题(必问级)#

Q1: 讲讲你的项目架构?#

答: 这是一个面向 OnCall 的多智能体 AIOps 平台,核心理念是确定性优先、LLM 按需介入。架构分三大层六个阶段:

接入层(L1)零智能快入账——两道 Redis 限流 + 五层递进去重,75 条相同告警压到 1 个任务,API P50 4ms 返回。预处理层(L2)纯规则降噪——去重、抖动检测、拓扑级联关联、Impact 评分,一行 LLM 都不调,被判重复/抑制的告警直接不入队。调查层(L3)分层自适应——Tier 1 Runbook 指纹匹配零 LLM 处置,Tier 2 向量检索历史案例便宜验证,Tier 3 假设驱动 ReAct 深度调查,命中即止自动递进。处置层(L4/L5)资源建模+事务化——白名单工具替代自由命令、CAS 漂移检测、三级门禁验证、Saga 补偿回滚。最后 Runbook 飞轮(L6)把 Tier 3 的调查成果反哺 Tier 1。

Incident 状态机贯穿全程驱动状态流转:DETECTED → TRIAGING → INVESTIGATING → RCA_READY → REMEDIATING → VERIFYING → RESOLVED | ESCALATED。

追问 1: 为什么不用 LLM 做告警预处理?降噪交给模型效果不是更好吗? 答: 告警风暴下一分钟可能涌入上百条告警,每条都调 LLM 会直接撞百炼 TPM 限流把整条链路卡死。L2 的职责是降低下游负载,它自己不能成为负载源。纯规则的去重/聚合/抑制能解决 80%+ 的降噪需求(因为大部分”噪音”就是重复告警和抖动),剩下需要语义理解的部分交给 Tier 2/3 在队列消费后按需处理。这是分层卸载的设计——每层只处理自己这个粒度的噪音。

追问 2: 这个系统和市面上的 AIOps 产品比,有什么差异? 答: 最大差异是确定性三级递进取代固定模式选择。大多数 AIOps 产品要么全走 LLM(贵、慢、不确定),要么全走规则(覆盖不了未知故障)。我们的设计是:能确定的用确定性方法(Tier 1 Runbook),不确定的才用 LLM(Tier 3),中间有个低成本的过渡带(Tier 2 检索验证)。而且三级递进不是入口预设的,是调查过程中自适应决定的。另一个差异是处置的安全治理——资源域建模+事务化是借鉴 Kubernetes 的声明式资源模型,而不是让 LLM 生成 shell 命令。

Q2: 为什么选 LangGraph 而不是自己写循环?#

答: LangGraph 给了三个自己写难以做好的东西:第一,StateGraph 的条件边处理 Tier 1→2→3 的递进分支比 if-else 链清晰得多;第二,interrupt() 原生支持审批挂起——处置需要人工审批时图挂起等响应,恢复后接着跑,自己实现这套 checkpoint+resume 机制成本很高;第三,checkpoint 支持 Worker 崩溃后从断点恢复。

但 LangGraph 不是万能的——Tier 3 的 ReAct 执行循环我用了自写的 Agent Harness(app/runtime/tool_runner.py),因为需要 create_agent 给不了的并行工具编排、工具熔断、预算闸门、上下文压缩。

追问: 如果重来还会选 LangGraph 吗? 答: 会,但会控制依赖范围。目前 LangGraph 主要用在调查图的节点编排和审批挂起,这两个场景它确实比自写好。但如果只有 Tier 1 的纯规则路径,一个 async 函数就够了。代价是要自己维护与 LangChain 消息协议的兼容(TypedDict/dict 双形态、astream 失败回退 ainvoke),框架升级时这里是重点回归区。

Q3: 你的系统怎么保证高可用?#

答: 核心哲学是 Fail-open + Postgres 事实源兜底。所有 Redis 依赖组件(限流/并发槽/队列消费)Redis 挂了都 fail-open 降级,Postgres 作为事实源不丢数据。具体机制:

Worker 崩溃恢复:Redis Stream 的 PEL(Pending Entries List)记着已分发但未 ACK 的消息,其他 Worker 的 XAUTOCLAIM 超时后接管,连续 3 次失败进 DLQ。并发槽用 ZSET + TTL 心跳,Worker 崩了 90 秒后槽自动回收。

LLM 不可用时优雅降级:Tier 1(纯规则 Runbook)和 Tier 2(检索侧)不依赖 LLM 仍能工作——系统从”智能诊断”退化为”确定性自愈 + 相似案例推荐”而不是整体瘫痪。

追问: Redis 恢复后会不会流量突发? 答: 会有一个短暂窗口——限流计数器已过期,所有请求放行。但受并发槽限制(32),不会同时涌入 LLM。1 个限流窗口后(通常 1-60 秒)限流重新生效。运维平台的流量不是交易级的,这个瞬时窗口实测没有影响。

进阶题(区分度)#

Q4: 三级递进里 impact_score 怎么用?只调门槛不改结构是什么意思?#

答: impact_score 是 L2 预处理产出的影响面评分(0~1),由 severity × 告警数量 × 拓扑扇出 × 抖动惩罚加权得到。它不改变 Tier 1→2→3 的执行顺序(结构不变),只调每级的置信度门槛——Impact 越高,各 Tier 要求的确认置信度越高。

举例:Impact=0.3(单条 warning),Tier 2 检索到相似案例 confidence=0.6 就够了。Impact=0.9(多条 critical + 跨域),同样的 confidence=0.6 不达标,会继续递进 Tier 3 做更深调查。用公式表达:effective_threshold = base_threshold + impact * weight,硬顶 0.95。

追问: 为什么不让 impact 直接决定跳过 Tier 1/2 进 Tier 3? 答: 因为 Tier 1 是零成本的——Runbook 指纹匹配是微秒级纯内存操作。即使是 Impact=1.0 的灾难性故障,如果恰好有精确匹配的 Runbook,Tier 1 处置的确定性和速度远优于 Tier 3 的 LLM 推理。跳过 Tier 1 是在”确定有确定性答案”时强行走不确定性路径,是浪费。有一个例外:must_escalate_to_tier3 会在 Impact 极高 Runbook 置信度不足时强制升级。

Q5: Incident 状态机的迁移矩阵是什么意思?为什么”默认拒绝”?#

答: 状态机定义了 Incident 从 DETECTED 到 RESOLVED/ESCALATED 的所有合法路径,用 (当前状态, 事件) → 目标状态 的二元组穷举(app/incidents/state_machine.pyTRANSITIONS 字典)。不在矩阵里的 (状态, 事件) 组合一律抛 IllegalTransitionError

“默认拒绝”的意思是:新增一个事件类型时,如果忘了在矩阵里加对应的 (状态, 事件) 条目,系统会报错而不是静默跳过。这比”宽容跳转”安全得多——比如 RESOLVED 状态收到 EXECUTION_DONE 事件(不应该发生),宽容跳转可能把它当成正常流程,默认拒绝会直接报错让你排查为什么 RESOLVED 的 Incident 还在跑处置。

追问: RESOLVED 被拉回 INVESTIGATING 是什么场景? 答:correlation_key 的新告警到达时,如果关联的 Incident 已经 RESOLVED,说明修了但又复发了。状态机允许 (RESOLVED, NEW_ALERT_MERGED) → INVESTIGATING——带着之前的证据重新调查。但有个闸:同一个 Incident 被拉回超过 3 次(MAX_RESOLVED_PULLBACKS)就直接 → ESCALATED,防”处置→复发→再处置”的无限循环。

压力题(面试官挑战设计决策)#

Q6: 你这系统说到底就是个告警转报告的 wrapper,跟直接调 GPT API 然后套个 prompt 有什么本质区别?#

答: 本质区别在于确定性治理。直接调 GPT 写 prompt 能出报告,但没法解决三个生产级问题:

第一,成本和延迟不可控。直接调 GPT 没有降噪层,75 条相同告警各调一次,成本和延迟都是 75 倍。三级递进让 80%+ 的故障在 Tier 1/2 用零或极低 LLM 成本解决。

第二,处置不安全。GPT 生成”请执行 rm -rf /var/lib/mysql”你敢跑吗?资源域建模把操作限定在封闭 Registry 里,白名单+CAS+事务化+门禁验证+审批,才是”敢上生产”的前提。

第三,不闭环。报告出来了然后呢?Runbook 飞轮把 Tier 3 的调查成果提炼成 Runbook 反哺 Tier 1,系统会越来越”聪明”——不是 LLM 越来越聪明,而是确定性覆盖率越来越高。

追问: 那你这个系统真的比直接调 GPT 效果好吗?有对比数据吗? 答: 诚实说,“效果好”要分维度。诊断准确率上,Tier 3 的 LLM 推理质量和直接调 GPT 差不多(底层都是 LLM)。区别在:(1)成本低一个量级——大部分故障在 Tier 1/2 解决不进 LLM;(2)安全——直接调 GPT 的处置建议你不敢自动执行,而这套系统有完整的安全闭环可以自动执行;(3)可追溯——每一步的证据、决策、置信度都有记录,面试官或 oncall 负责人能审计整条链路。具体数据见 M9 Benchmark 文档。

Q7: 你用的 DeepSeek 不是最好的模型,为什么不用 GPT-4o 或 Claude?#

答: 两个原因。第一,成本。一次 Tier 3 诊断 15-20 次 LLM 调用,20K+ token,DeepSeek-V4 经百炼约 ¥0.04,换 GPT-4o 约 ¥2-5,差两个量级。运维诊断是高频低价值密度场景——每天可能几百次诊断,大部分是已知故障走 Tier 1/2,真正需要深度推理的占少数。

第二,中文运维能力。我们的告警、日志、SOP 文档全是中文,实测 DeepSeek 在中文运维场景的指令跟随和工具调用质量与 GPT-4o 差距不大(Tier 2 的适用性复核甚至用更便宜的 qwen-turbo)。

追问: 百炼的限流不是瓶颈吗? 答: 确实是。百炼的 TPM 是当前最大的外部约束。缓解手段:L2 预处理严格降噪减少进入 LLM 的量;Tier 1 零 LLM 分流走大部分已知故障;Tier 2 用便宜档小模型不占主模型配额;并发槽控制同时调 LLM 的 Worker 数。如果调用量继续上升,下一步是直连 DeepSeek 官方 API 或多供应商轮转。


五、前沿概念与延伸#

5.1 AIOps 成熟度模型#

是什么: Gartner 提出的 AIOps 平台成熟度分级——从 L0(纯人工 oncall)到 L5(全自主修复),描述组织在智能运维能力上的进化路径。

为什么要这么做: 面试官问”你这个系统在行业里算什么水平”时,需要一个客观框架来定位。不是每个组织都需要 L5,过度投入和投入不足一样是问题。

这么做的理由: 成熟度模型帮助区分”系统能做什么”和”系统该做什么”。我们的系统在 L2-L3 之间——有自动化诊断和半自动处置,但完全自主修复(L4+)需要更多 Runbook 覆盖和更高的处置安全置信度。

举例说明: 本系统的三级递进刚好映射到成熟度模型的三个级别:Tier 1(Runbook 自动处置)≈ L3 自动化修复;Tier 2(辅助定位)≈ L2 辅助诊断;Tier 3(深度调查)≈ L2-L3 过渡,能定位根因但处置仍需人确认。飞轮机制的价值在于:不断把 Tier 3 的成果转成 Tier 1,等价于在不增加人力的情况下提升成熟度。

延伸问答:

面试官:“你还知道其他 AIOps 框架吗?什么情况下用哪个?” 答:除了 Gartner 成熟度模型,还有 Google SRE 的运维金字塔(Toil Reduction → SLO → Error Budget → Automation),侧重”消除 toil”的视角。微软有 AIOps Challenge 的评测框架,侧重算法准确率。选型看场景——做内部运维工具用 SRE 金字塔更实用(跟 SLO 对齐),做 AIOps 产品用 Gartner 模型更好(跟客户的成熟度阶段对齐),做学术研究用 AIOps Challenge 的评测指标。

5.2 事件驱动架构(Event-Driven Architecture)#

是什么: 系统组件之间通过事件(异步消息)通信,而不是直接调用。事件的生产者和消费者解耦——生产者不知道谁会消费,消费者不知道谁会生产。

为什么要这么做: OnCall 场景的核心矛盾是”告警洪峰”vs”有限的 LLM 算力”。如果用同步 RPC,一个慢诊断会拖死 API 进程。事件驱动让接入层(生产者)和调查层(消费者)在时间和负载上完全解耦。

这么做的理由: 相比传统的请求-响应模式,事件驱动带来三个好处:(1)削峰填谷——API 毫秒级入队返回,Worker 按自己的节奏消费;(2)可恢复性——任务在 Redis Stream 里持久化,Worker 崩了换一个接着消费;(3)可观测性——每个事件都有时间戳和状态,完整审计链路自然形成。

举例说明: 本系统的事件流:Alertmanager → webhook(入账事件)→ Redis Streams(任务事件)→ Worker(消费事件)→ Postgres(状态变更事件)→ 前端 SSE(通知事件)。Incident 状态机就是一个事件驱动的 FSM——每个状态变更由一个事件触发(如 RCA_CONFIRMED),迁移结果触发下一个阶段的行为。

延伸问答:

面试官:“事件驱动和消息队列有什么区别?你为什么选 Redis Streams 而不是 Kafka?” 答:消息队列是事件驱动的一种实现手段,但事件驱动还可以用 EventEmitter、Webhook、CDC 等。选 Redis Streams 因为三个原因:已有 Redis 实例(限流/并发槽也用它)、consumer group + PEL 原生支持恰好一次语义、运维诊断的吞吐量(几百 TPS)远不到 Kafka 的门槛。Kafka 的优势在跨机房持久化和万 TPS 以上的吞吐,这个场景用不到。如果未来要做多集群联邦诊断,队列会是第一个需要升级的组件。


六、诚实边界#

  1. 没有真实生产环境验证: 系统跑在本地开发环境(OrbStack + docker-compose),所有压测数据来自模拟场景。没有经过真实告警风暴、真实故障、真实 oncall 的检验。面试时说”设计了”而不是”在生产跑了多久”。

  2. 拓扑数据靠人工+简单探测: 拓扑图的数据来自 compose/nginx 解析、ss 连接嗅探、告警共现挖掘和手写 YAML。没有对接真实的 CMDB、K8s API、Service Mesh。在真实大规模微服务环境下,拓扑发现和保鲜是比图算法难得多的问题。

  3. Runbook 冷启动覆盖率低: 种子 Runbook 只覆盖十几个高频场景(MySQL 主从延迟、Redis OOM、磁盘满等)。大部分告警第一次进来都会走到 Tier 3。飞轮需要足够多的 Tier 3 成功案例才能转起来,冷启动期体验不会很好。

  4. 单 LLM 供应商风险: 完全依赖百炼调 DeepSeek-V4,百炼出问题整条 Tier 2/3 链路就挂了(Tier 1 不受影响)。没有做多供应商轮转或本地模型降级。

  5. 处置覆盖范围有限: 资源域 Registry 目前只覆盖了 container/service/process/file/database/deployment 六种资源类型。真实运维场景还有网络策略、DNS、负载均衡、云资源(EC2/RDS/ELB)等,每种都需要新增资源定义和工具。

  6. 多租户和权限: 系统假设单租户运维团队使用。没有做租户隔离、RBAC、数据隔离。企业级部署需要补齐这些基础设施。

  7. 前端功能有限: 有基础的诊断报告查看和 Runbook 管理界面,但没有告警仪表盘、拓扑可视化、审批工作流 UI 等 oncall 日常需要的功能。