日志易 + Dify 改造实战:千条异常日志也能稳定分析
每天早上看告警,你是不是也遇到过这种场景:
- 日志一多,LLM 直接超限
- 报告看起来“很聪明”,但时间是编的
- 重复告警一大堆,真正要处理的问题反而不清楚
我这次在生产环境做的改造,核心目标只有一个:
在资源受限的前提下,稳定产出可执行的异常报告。
这篇文章我把完整思路拆开讲,包含工作流设计、聚合算法、提示词约束和落地效果。
一、为什么“直接把日志喂给大模型”会失败?
我原来的流程很直接:
- HTTP 拉取前一天异常日志
- 把日志原文直接给模型
- 让模型生成异常报告
- 推送钉钉并写入 Elasticsearch
日志量几百条时还能跑;一旦到几千条,问题会集中爆发:
- 上下文超限
- 输入过长触发 400
- 报告内容重复
- 时间字段不准确
结论很明确:
大模型擅长“总结”,不擅长“吞原始海量日志”。
二、原始 Dify 工作流问题在哪?
我最初的 Dify 工作流(简化)是:
开始
→ HTTP 请求(拉取日志)
→ 日志格式化
→ LLM 分析
→ 提取报告内容
→ 推送钉钉
→ 写入 ES
→ 结束
HTTP 请求参数(脱敏):
domain=opstime_range=-1d/d,now/dquery=(hostname:10.* AND appname:syslog)size=1000fields=false
核心问题是:
模型直接面对 rows 原始数组。
当 rows=1000 且每条 raw_message 都较长时,输入会迅速膨胀到几十万字符,超限几乎是必然。
三、改造目标:从“日志堆叠”切到“结构压缩”
我把目标重写成 5 条硬约束:
- 模型不再处理原始日志
- 模型只处理聚合后的摘要
- 时间范围必须来自真实数据
- 报告必须体现聚合数量
- 输出必须能直接指导运维动作
一句话总结:
先压缩、再分析。
四、关键改造:日志聚合压缩节点
我新增了一个代码节点:日志聚合压缩。
每条日志先抽取关键字段:
device_ipdevice_nametimestampraw_message
然后对 raw_message 做归一化,生成 signature 分组。
归一化函数示例:
def normalize(msg):
s = msg.lower()
s = re.sub(r"\b[0-9a-f-]{36}\b", "<uuid>", s)
s = re.sub(r"0x[0-9a-f]+", "<hex>", s)
s = re.sub(r"\b\d+\b", "<n>", s)
s = re.sub(r"\s+", " ", s)
return s[:160]
signature 生成逻辑:
signature = log_type + "|" + normalize(raw)
每个 group 输出这些信息:
countdevice_ipsdevice_namesfirst_seenlast_seenexamples
聚合结果结构示例:
{
"summary": {
"total_logs": 1000,
"unique_groups": 42,
"output_groups": 20
},
"groups": [
{
"count": 314,
"devices": ["10.0.1.1"],
"device_names": ["CORE-SW-01"],
"first_seen": "2026-02-26 10:13:28",
"last_seen": "2026-02-26 11:59:50",
"examples": [...]
}
]
}
我这里还加了 3 个限流规则:
- 只保留
count最高的前 20 个异常组 - 每组最多保留 1 条 example
- 每组最多保留 8 台设备
结果是:
原始 1000 条日志,会被压缩成 20 组可分析对象,模型负担直接降维。
五、为什么我最终只分析 1000 条?
我也尝试过分页拉取 6000+ 日志,但现实很骨感:
- 日志易 API 深分页有约束
-
page参数在特定模式下失效 - 深分页可能返回字段统计结构
- 额外日志带来的分析增益很有限
最后我做了工程化取舍:
每天稳定分析前 1000 条异常日志。
原因有 5 个:
- 真异常通常呈集中爆发
- 高频异常优先处理更有价值
- 1000 条足够覆盖主要问题
- 模型稳定性显著提升
- 工作流复杂度可控
这不是妥协,而是面向生产可用性的选择。
六、提示词改造:给模型“硬边界”
我把提示词规则改成了强约束:
- 禁止逐条分析日志
- 必须基于
groups - 必须使用
first_seen/last_seen - 异常描述必须包含
count
核心片段示例:
角色:
你是一名网络日志分析专家,负责基于聚合日志生成准确、简洁、可执行的网络异常分析报告。
时间段提取规则(严格):
必须使用:
group.first_seen
group.last_seen
禁止使用示例时间或虚构时间。
异常描述必须包含:
- 异常类型
- 日志聚合数量
- 涉及设备数量
示例格式:
存在问题: LLDP邻居信息频繁变更,共产生 314 条告警日志,涉及 2 台设备
建议必须包含明确命令,例如:
display interfacedisplay logbufferdisplay lldp neighbordisplay bfd session
七、模型输出后的自动化落地
模型输出后,我保留了 3 个后处理节点:
- 提取日志分析结果
- 发送钉钉机器人
- 写入 Elasticsearch
ES 写入结构示例:
document = {
"序号": anomaly["序号"],
"组名称": group_name,
"提交时间": utc_time.isoformat(),
"设备地址": anomaly["设备地址"],
"设备名称": anomaly["设备名称"],
"时间段": anomaly["时间段"],
"优先级": anomaly["优先级"],
"存在问题": anomaly["存在问题"],
"建议": anomaly["建议"],
"备注": anomaly["备注"]
}
这样异常数据能长期沉淀,后续可以做趋势统计和治理闭环。
八、最终架构(稳定版)
最终工作流:
开始
→ HTTP 拉取日志(1000 条)
→ 日志聚合压缩
→ LLM 分析聚合结果
→ 报告提取
→ 推送钉钉
→ 写入 ES
→ 结束
这套架构的关键特征:
- 模型永远不碰原始海量日志
- 时间范围来自真实数据字段
- 输出数量、设备范围都可控
- token 风险稳定在安全区
- 整个流程可解释、可复盘
九、改造前后对比
改造前:
- 模型偶发超限
- 报告重复
- 时间字段错误
- 稳定性差
改造后:
- 稳定分析 1000 条日志
- 自动完成异常归类
- 报告结构统一
- 时间段准确
- 每条异常都带聚合数量
- 处置命令可直接执行
十、结论
这次改造给我最大的启发是:
大模型不是日志处理器,而是决策摘要生成器。
真正决定效果的,不是“喂多少日志”,而是你在前面做了多少结构化。
如果你也在做日志智能分析,建议先做这一步:
把原始日志变成“可分析对象”,再交给模型。
这样你得到的不是“看起来聪明”的报告,而是每天都能稳定落地的运维结果。
CTA:资料包领取
如果你也在做日志智能分析,我把这次实战里最核心的可复用资料整理好了:
- Dify DSL 工作流文件(可直接导入)
- 日志聚合压缩算法代码(可按你的日志格式改造)
- LLM 分析提示词模板(含时间约束与输出结构)
关注【数字卢语】公众号后,私信我关键词:日志改造。
我会把上述资料包发给你,帮你少走一轮从“能跑”到“稳定可用”的弯路。