← 返回文章列表

Dify 中级实验(20):综合实战——自动化报告生成流水线如何从数据到周报一步到位?

1. 业务场景

先讲一个我们实际遇到的场景。

一家公司的 CEO 每周一早上要一份上周业务运营报告:销售对比、客户数据、客服数据、异常预警、趋势分析。以前这份报告全靠分析师手工做——从各个系统导出数据、在 Excel 里对账、写分析、排版成文档,每周一早上雷打不动要耗 3 个小时,而且周一上午往往是分析师最忙的时候:数据还没整理完,CEO 已经在催了。数据要得急、口径还不统一,这个月销售算的是含税收入、下个月又不含了,报告里的数字对不上,CEO 在会上当面质疑。

我们第一次接这类需求时,第一反应也是「写个脚本定时跑不就行了」。真正梳理完整条链路才发现——脚本只能把数据采回来,报告还得有人写:对账、分析、排版、推送,每一步都卡着人工,而这条链路恰恰是流水线化改造的最佳对象。

这不是个例。任何「周期性、重复性、数据源固定的报告」场景都是这个模式:周报、月报、日报、经营分析会材料、销售战报——只要数据来源稳定、格式固定,就是流水线化改造的最佳对象

2. 场景痛点

这个流程的痛点,在分析师的周一早上体现得最直接:

本质上,「从数据采集到报告推送」是一条完全可以自动化的流水线——多路采集、聚合、清洗、分析、模板、分支、Webhook 串起来,人只负责看结果、做决策

3. 方案:为什么是「全自动报告流水线」

本实验是系列 1 的收官检验:综合运用 DIFY-102 全部进阶知识(多路采集、聚合、清洗、分析、模板、分支、Webhook),构建一条从数据采集到报告推送的全自动流水线

选它的理由:

这篇文章我们就用它搭一个「周报自动生成器」:输入 report_week,自动完成三路数据采集 → 聚合 → 清洗与异常检测 → 趋势分析 → 按格式输出 Markdown 周报或 JSON 报告,异常时自动推送告警。

4. 整体架构

graph TD start["开始:report_week"] --> sales["销售数据采集(Code)"] start --> cust["客户数据采集(Code)"] start --> support["客服数据采集(Code)"] sales --> agg["三路数据聚合(Code)"] cust --> agg support --> agg agg --> clean["数据清洗与异常检测(Code:空值/异常/环比)"] clean --> trend["趋势分析与策略建议(LLM)"] trend --> md["Markdown周报模板(模板转换)"] trend --> json["JSON报告生成(Code)"] md --> fmt_select{"格式选择(IF-ELSE)"} json --> fmt_select fmt_select --> end1["结束"] fmt_select --> webhook["Webhook推送"] webhook --> alert_cond["告警条件"] alert_cond --> alert_end["发送告警通知/结束"]

链路很清晰:入口收报告周 → 三路并行采集 → 聚合 → 清洗与异常检测 → 趋势分析 → 按格式分支输出 → 异常时 Webhook 告警。关键设计是「检查类分支必须消费判断结果」——告警条件不消费,告警就是死代码。

5. 模块设计

5.1 三路数据采集(Code)

三个采集节点并行(拓扑上从开始直接分 3 路),各自返回模拟数据——这是多路并行 + 聚合的标准形态:

def main(report_week: str) -> dict:

    # 模拟从数据库 A 拉取销售数据

    import json

    sales = [

        {"week": "上上周", "revenue": 480000, "orders": 960},

        {"week": "上周", "revenue": 520000, "orders": 1040},

    ]

    return {"sales": json.dumps(sales, ensure_ascii=False)}

💡 参数名必须与函数签名一致(report_week),code 节点按名传参——Dify 1.16 实测:参数名不一致直接报错。

5.2 数据清洗与异常检测(Code)

核心清洗节点,接收三路聚合结果 + force_refresh 开关:

def main(sales: str, customers: str, support: str, force_refresh: str) -> dict:

    import json

    # 解析 + 空值兜底

    def parse(s):

        try:

            d = json.loads(s or "[]")

            return d if isinstance(d, list) else []

        except Exception:

            return []

    sl, cu, sp = parse(sales), parse(customers), parse(support)

    # 环比计算(上周 vs 上上周)

    def change(data, key):

        if len(data) < 2:

            return 0.0

        cur, prev = data[-1].get(key, 0), data[-2].get(key, 0)

        return round((cur - prev) / prev * 100, 1) if prev else 0.0

    anomaly_count = 0

    if change(sl, "revenue") < 0: anomaly_count += 1

    if change(cu, "active_users") < 0: anomaly_count += 1

    return {

        "revenue_change": str(change(sl, "revenue")),

        "customer_change": str(change(cu, "active_users")),

        "anomaly_count": str(anomaly_count),

        "cleaned": json.dumps({"sales": sl, "customers": cu, "support": sp}, ensure_ascii=False)

    }

5.3 Markdown 周报模板(模板转换)

Jinja2 渲染周报正文(零 Token),空值保护 + 条件渲染:

# 📊 周报 {{ report_week }}

## 一、核心指标总览

{% set rv = revenue_current if revenue_current is not none else 0 %}

{% set rv_prev = revenue_previous if revenue_previous is not none else 0 %}

- 营收:{{ rv }} 元(环比 {{ revenue_change }}%)

{% if anomaly_count | int > 0 %}

## 四、异常预警

共发现 {{ anomaly_count }} 项异常,请关注:

{% for a in anomalies %}

- {{ a }}

{% endfor %}

{% endif %}

💡 模板里所有变量都要有默认值保护(is not none else 0)——否则缺失字段直接渲染失败。

5.4 格式选择 + Webhook 告警(分支 + 通知)

output_format 决定走「结束」(返回报告)还是「Webhook 推送」(推送到群/系统);告警条件(IF-ELSE)判断 anomaly_count > 0 才发通知——检查类分支必须消费判断结果,否则告警是死代码。

6. 运行验证

输入 预期 实测
report_week=上周 周报含销售/客户/客服三节 + 环比 ✅ 三节齐全,环比正确
数据含异常(营收下降) 异常预警节 + 告警推送 ✅ anomaly_count>0 触发告警分支
output_format=json JSON 报告输出 ✅ 结构化 JSON

(本实验已在 TR4 验收中 4/4 PASS:确定性/内容/语义/边界全过)

7. 实战坑

现象 修复
code 节点字符串含 "\n" 写成跨行 Python 未终止字符串 SyntaxError,应用导入即失败 字符串内的换行必须写 "\n" 转义(本实验 5 处:details_text/daily_text/top_text/cats_text/anomalies_text)
模板变量无默认值 缺字段时渲染报错 全部 is not none else 默认值 保护
告警分支不消费判断结果 异常不通知(死代码) 检查类节点后必须接 IF-ELSE 消费其输出
LLM 长输出被截断 周报分析不完整 推理模型 reasoning_format: separated + max_tokens: 8000

8. 实验文档及源码获取

文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。


系列 1(中级 20 篇)完——从参数提取到全自动流水线,Dify 工作流核心能力全部覆盖。敬请期待系列 2:高级实验(综合场景实战:智能客服、对话式 BI、企业级平台……)。

本系列 · Dify 实验 · 中级
  1. Dify 中级实验(01):参数提取器实战——如何从自然语言中提取结构化数据?
  2. Dify 中级实验(02):问题分类器——智能路由引擎如何四路分发?
  3. Dify 中级实验(03):模板转换实战——如何用零 Token 完成文本加工?
  4. Dify 中级实验(04):迭代进阶——如何批量处理数据并守住性能边界?
  5. Dify 中级实验(05):并行执行——如何让多路任务同时跑?
  6. Dify 中级实验(06):变量聚合——如何确定性合并多路分支结果?
  7. Dify 中级实验(07):子工作流——如何把公共逻辑做成可复用积木?
  8. Dify 中级实验(08):代码节点进阶——如何用标准库处理文件与数据?
  9. Dify 中级实验(09):HTTP 节点进阶——如何搞定认证、分页与错误重试?
  10. Dify 中级实验(10):知识库深度调优——如何科学评估检索质量?
  11. Dify 中级实验(11):高级 RAG 流水线——如何搭建多路检索与精排?
  12. Dify 中级实验(12):Agent 深度配置——如何让智能体自主调用工具?
  13. Dify 中级实验(13):多 Agent 协作——如何编排多个智能体分工干活?
  14. Dify 中级实验(14):对话变量与状态管理——如何让工作流记住多轮对话的状态?
  15. Dify 中级实验(15):条件分支高阶策略——多条件路由如何避免分支爆炸?
  16. Dify 中级实验(16):错误处理与降级——工作流如何有尊严地失败?
  17. Dify 中级实验(17):调试监控与性能优化——响应慢和 Token 超支如何定位?
  18. Dify 中级实验(18):插件开发入门——如何把工作流变成 Agent 可调用的工具?
  19. Dify 中级实验(19):综合实战——如何把 19 个实验串成一条生产级流水线?
  20. Dify 中级实验(20):综合实战——自动化报告生成流水线如何从数据到周报一步到位?

联系我

15088711270

手机端点击号码可直接拨打 · 桌面端可复制

微信二维码

扫码加微信 · 备注「门户」更快通过