Dify 高级实验(08):智能告警——如何用 AI 替代固定阈值监控?
1. 业务场景
先讲一个我们实际遇到的场景。
一家 SaaS 公司的运维团队,凌晨两点被告警电话吵醒——监控系统报「数据库连接超时」,值班同学爬起来一看,日志里就那么几条 ERROR,等他想进一步排查时,故障已经自己恢复了。第二天复盘才发现:那只是网络抖动,系统自动重连成功了。可同样的告警,上周真的把数据库搞挂了,那次因为响应慢了十分钟,线上服务中断了半小时。固定阈值监控的尴尬就在这里:同一个告警,有时候是虚惊,有时候是真灾——阈值规则根本分不清。
我们第一次接手监控告警时,第一反应也是「把阈值调准一点不就行了」。后来才发现——阈值规则理解不了上下文,而「数据库连接超时」到底是网络抖动还是数据库挂了,恰恰需要看上下文才能判断。这是规则的死角,却是 LLM 的主场:把日志喂给它,它能说出「这是抖动,不用管」还是「这是故障,立刻升级」。
这不是个例。任何「靠固定阈值做监控」的团队都是这个模式:CPU 超过 90% 就告警、日志出现 ERROR 就告警——阈值定低了,告警风暴淹没真故障;阈值定高了,真故障被漏掉。规则理解不了上下文,而「数据库连接超时」是网络抖动还是数据库挂了,恰恰需要看上下文才能判断。
2. 场景痛点
这个流程的痛点,在运维值班同学身上体现得最直接:
- 告警风暴:固定阈值误报率高,深夜被无关告警吵醒是常态——狼来了喊多了,真故障来了反而没人第一时间响应。
- 上下文缺失:告警只给「CPU 95%」这种孤立数字,不说前因后果——值班同学还得手动翻日志、查指标,故障定位慢,MTTR 居高不下。
- 误报漏报并存:同样是 ERROR,网络抖动和数据库宕机在阈值规则眼里一样——该升级的没升级,不该升级的疯狂打扰,分级处置形同虚设。
- 处置经验不沉淀:每个故障怎么判断、怎么处理,都靠老运维的经验——人走了经验就没了,新人值班只能边翻文档边猜。
本质上,固定阈值的瓶颈是「规则理解不了上下文」——监控的价值不在「报不报」,而在「报得准不准、报完能不能直接定位根因」。
3. 方案:为什么是这套智能告警范式
Dify 工作流里的「解析 → AI 分析 → 分级路由 → 汇聚报告」链路,把监控从「阈值报警」升级成「AI 判断 + 分级处置」。
选它的理由,我们实际对比过:
- LLM 理解上下文,区分真假异常:把解析后的日志喂给 LLM,让它判断「是否真异常、等级、根因、建议措施」——同样是 ERROR,能区分网络抖动和数据库挂了,这是阈值规则做不到的;
- JSON 输出 + 代码展平,分级路由可靠:LLM 输出 JSON,代码节点解析并展平为可见字符串字段,IF/ELSE 按 critical/warning/info 三级分流——紧急告警建工单、一般告警发通知、正常仅记录;
- 汇聚报告沉淀处置经验:三个分支最终统一生成结构化诊断报告,根因、影响模块、建议措施都在——值班同学拿到的是结论,不是一堆日志。
这篇文章我们就用它搭一个「智能日志告警系统」:粘贴一段系统日志,AI 判断是否异常、按等级分流处置,输出结构化诊断报告。
4. 整体架构
链路很清晰:入口收原始日志 → 结构化解析 → AI 分析 → 分级路由 → 汇聚诊断报告。分级路由是这个架构的处置中枢——critical 建工单、warning 发通知、info 只记录,三级分流互不干扰,最后统一汇聚成一份诊断报告。
5. 模块设计
5.1 解析日志(cd_parse_logs)
输出 parsed_entries(array[object],供下游代码消费)与
parsed_json(string,供 LLM 引用——array[object]
在变量选择器不可见):
def main(raw_logs: str) -> dict:
import json
lines = (raw_logs or "").strip().split("\n")
parsed = []
for line in lines:
if not line.strip():
continue
entry = {"raw": line, "level": "info", "module": "", "message": ""}
if "ERROR" in line or "FATAL" in line:
entry["level"] = "error"
elif "WARN" in line:
entry["level"] = "warn"
if "]" in line:
entry["module"] = line.split("]")[0].strip("[")
entry["message"] = line.split("]")[-1].strip()
else:
entry["message"] = line
parsed.append(entry)
return {"parsed_entries": parsed,
"parsed_json": json.dumps(parsed, ensure_ascii=False),
"total": len(lines),
"errors": sum(1 for e in parsed if e["level"] == "error"),
"warnings": sum(1 for e in parsed if e["level"] == "warn")}5.2 异常分析 LLM(lm_analyze)
枚举字段必须给判定规则(只写 critical/warning/info 不给规则,LLM 会随意输出,下游分支全走错):
你是一个系统运维工程师。分析以下系统日志,判断是否存在异常。
解析后的日志(共 {{#cd_parse_logs.total#}} 条,{{#cd_parse_logs.errors#}} 个错误,{{#cd_parse_logs.warnings#}} 个警告):
{{#cd_parse_logs.parsed_json#}}
输出 JSON(只输出 JSON,不要输出其他任何内容):
{"has_anomaly": true/false, "severity": "critical/warning/info",
"root_cause": "根因分析", "affected_modules": ["模块1", "模块2"],
"suggested_action": "建议措施", "auto_resolvable": true/false}
判定规则:只有 critical 或 warning 才算异常;正常日志 severity 为 info。
5.3 解析异常等级(cd_parse_analysis)
对 LLM 的 text 做 re.search(r"\{.*\}") +
json.loads——这要求 lm_analyze 必须
reasoning_format: separated,否则思考混入 text
解析必失败。severity 白名单兜底,boolean 展平为字符串:
def main(llm_text: str) -> dict:
import json, re
text = (llm_text or "").strip()
analysis = {}
m = re.search(r"\{.*\}", text, re.DOTALL)
if m:
try:
parsed = json.loads(m.group(0))
if isinstance(parsed, dict):
analysis = parsed
except Exception:
analysis = {}
severity = str(analysis.get("severity", "info")).lower()
if severity not in ("critical", "warning", "info"):
severity = "info" # 白名单兜底
affected = analysis.get("affected_modules", []) or []
affected_text = "、".join(str(x) for x in affected) if isinstance(affected, list) else str(affected)
return {"severity": severity,
"has_anomaly": "true" if analysis.get("has_anomaly") else "false",
"root_cause": str(analysis.get("root_cause", "")) or "无明显异常",
"suggested_action": str(analysis.get("suggested_action", "")) or "无需处理",
"affected_modules": affected_text,
"auto_resolvable": "true" if analysis.get("auto_resolvable") else "false",
"analysis_json": json.dumps(analysis, ensure_ascii=False)}5.4 等级分支(cond_severity)
三个 case 全部 is
字符串比较;每个分支一个代码节点(cd_critical/cd_warning/cd_info),输出同名字段(alert_message/alert_type/notify_result/ticket_id)。
5.5 汇聚诊断报告(lm_report)
三分支汇聚共享一个 LLM——prompt 只引用分支前的字段(cd_parse_logs / cd_parse_analysis),不引用分支输出,避免三分支同名字段歧义;长报告节点同样要求「直接输出报告正文」:
你是一个系统运维专家。基于以下日志解析结果和异常分析,生成一份完整的诊断报告。
日志概况:共 {{#cd_parse_logs.total#}} 条日志,{{#cd_parse_logs.errors#}} 个错误,{{#cd_parse_logs.warnings#}} 个警告。
异常分析结论:
- 是否异常:{{#cd_parse_analysis.has_anomaly#}}
- 异常等级:{{#cd_parse_analysis.severity#}}
- 根因分析:{{#cd_parse_analysis.root_cause#}}
- 影响模块:{{#cd_parse_analysis.affected_modules#}}
- 建议措施:{{#cd_parse_analysis.suggested_action#}}
输出一份结构化的诊断报告,包含:1. 摘要 2. 异常详情 3. 根因分析 4. 建议措施 5. 后续监控建议。直接输出报告正文。
6. 运行验证
| 输入(raw_logs) | 期望行为 | 实测 |
|---|---|---|
| 正常日志(INFO 为主) | severity=info,走「记录日志」分支,诊断报告标注无异常 | 与预期一致 |
| 含 ERROR 的日志 | severity=warning,一般告警通知 + 根因分析 | 与预期一致 |
| 含多次 CRITICAL/FATAL 的日志 | severity=critical,紧急告警 + 创建工单(ticket_id 形如 TK-xxxxxx) | 与预期一致 |
| LLM 输出非法 JSON / 无 severity 字段 | 白名单兜底 severity=info,流程不中断 | 与预期一致(cd_parse_analysis 防御) |
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
LLM 输出 JSON 被代码节点
json.loads 解析失败 |
未启用推理标签分离时,思考过程混入
text,re.search 取到含
<think> 的片段、json.loads
抛异常,流程卡住 |
lm_analyze 显式
reasoning_format: separated(dify103_02
实测:解析失败流程卡在步骤 0) |
| 枚举字段 severity 不给判定规则 | LLM 随意输出 critical/warning/info,告警分支全走错(该紧急告警的只发了普通通知) | prompt 给显式规则(「只有 critical 或 warning 才算异常」)+ 代码白名单兜底 default info(dify102_08_01 实测缺陷同源) |
has_anomaly/auto_resolvable
用 boolean 输出 |
boolean 在变量选择器不可见,下游引用取不到;旧写法曾把拦截信息转述成「无法获取数据」 | 展平为
"true"/"false" 字符串,if-else 用
is 比较(2026-07-31 dify-103 实测) |
诊断报告长输出 text 为空 |
思考与输出共享 max_tokens 预算(默认 2000)被挤空,或内容写进思考部分 | lm_report
max_tokens 提到 4000-8000 +
prompt「直接输出报告正文,不要输出任何解释性文字」(dify103_09 lm_format
实测) |
| 多分支汇聚共享下游 LLM | 三个分支代码输出字段同名(alert_message 等),汇聚 prompt 引用分支输出会歧义、取不到 | 汇聚 LLM 只引用分支前的字段(cd_parse_logs/cd_parse_analysis),分支输出不进 prompt(dify103_08 交付实证) |
8. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-103-08:系统监控与智能告警.md
- 源码(可直接导入):dify103_08_系统监控与智能告警.yml
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
- Dify 高级实验(01):智能文档处理——如何把非结构化文档变成结构化数据?
- Dify 高级实验(02):对话式 BI 分析——如何让业务人员用自然语言直接查数?
- Dify 高级实验(03):智能客服——如何让 AI 自动应答并把疑难问题转人工?
- Dify 高级实验(04):RAG 增强问答——如何让知识库问答更准还能多轮追问?
- Dify 高级实验(05):多步推理——如何把复杂问题拆解成子问题逐个击破?
- Dify 高级实验(06):自动化报告——如何让系统定时自动生成周报?
- Dify 高级实验(07):智能审批——如何让机器自动完成流程审批?
- Dify 高级实验(08):智能告警——如何用 AI 替代固定阈值监控?
- Dify 高级实验(09):测试用例生成——如何让 AI 自动产出测试用例?
- Dify 高级实验(10):综合实战——如何从零搭建一个企业级智能客服平台?