← 返回文章列表

Dify 高级实验(06):自动化报告——如何让系统定时自动生成周报?

1. 业务场景

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

一家电商公司的运营部,每周一早上 9 点都要开周会,会前必须有一份上周运营周报——营收、订单、客服满意度、活跃用户,还要算环比、标异常。这份周报一直由数据分析师手动做:从三个后台系统分别导出数据,粘到 Excel 里算环比,再复制进 Word 模板,最后手动发到群里。一套流程下来大半天,而且经常漏算某个指标,或者把上周的数据标成本周。运营总监最常说的一句话是:「周报能不能周一早上我睁眼就能看到?」

我们第一次接这个需求时,第一反应也是「写个 Python 脚本定时跑不就行了」。后来把流程拆开一看才发现——取数、算数、送数,每一段都有自己的坑:三个系统数据格式不一、环比计算会除零、推送要对接各渠道。脚本越写越像一锅粥,而 Dify 工作流把这些环节都做成了可视化节点,改一处、重跑一次,全流程跟着变。

这不是个例。任何「定期要出报表」的场景都是这个模式:运营周报、财务日报、销售月度复盘、SRE 巡检报告——数据来源多、格式固定、周期重复,人工做既慢又容易错,还挤占了分析师真正该做的专题分析。

2. 场景痛点

这个流程的痛点,在数据分析师身上体现得最直接:

本质上,周报的痛点不在「写报告」,而在「取数、算数、送数」这三段机械环节——数据、计算、推送都是确定性的,唯独靠人每天重复,这是自动化收益最直接的场景。

3. 方案:为什么是这套自动化报告流水线

Dify 工作流里的「并行数据采集 → 变量聚合 → 指标计算与异常检测 → LLM 分析结论 → 模板转换生成报告 → Webhook 推送」链路,把周报全流程搬上自动化。

选它的理由,我们实际对比过:

这篇文章我们就用它搭一个「运营周报自动生成器」:三路数据并行采集,自动算环比、检异常、生成报告,一键推送到群。

4. 整体架构

graph TD start["开始:report_week / output_format / push_enabled"] --> c1["销售数据采集(Code)"] start --> c2["客服数据采集(Code)"] start --> c3["用户数据采集(Code)"] c1 --> va["变量聚合:合并三路 JSON"] c2 --> va c3 --> va va --> ca["Code 指标计算与异常检测:环比/满意度/活跃用户 + anomalies"] ca --> la["LLM 分析结论:300 字以内,直接输出正文"] la --> tr["模板转换:Markdown/JSON 双格式报告"] tr --> pw["Code Webhook 推送:模拟 HTTP 200"] pw --> e1["结束"]

链路很清晰:三路并行采集 → 聚合 → 计算检测 → LLM 分析 → 模板出报告 → 推送。变量聚合和模板转换是这个架构的关键——聚合把三路数据确定性合并,模板把同一份数据渲染成多种交付格式。

5. 模块设计

5.1 三路采集(cd_sales / cd_customer / cd_support)

每个采集节点同时输出两份object 结构数据(sales)给下游代码节点与模板消费,json 字符串(sales_json)给变量聚合器合并:

def main():

    return {

        "sales": {"current": {"total_revenue": 1285000, "total_orders": 342, "avg_order": 3757},

                  "previous": {"total_revenue": 1150000, "total_orders": 310, "avg_order": 3709},

                  "daily": [{"date": "周一", "revenue": 185000, "orders": 48}]},

        "sales_json": '{"current": {...}, "previous": {...}}'   # json.dumps 序列化

    }

5.2 变量聚合(va_merge)

variables[[节点id, 字段], ...] 嵌套数组格式,output_type: string

- data:

    output_type: string

    title: 变量聚合(合并数据)

    type: variable-aggregator

    variables:

    - [cd_sales, sales_json]

    - [cd_customer, customer_json]

    - [cd_support, support_json]

5.3 指标计算与异常检测(cd_analyze)

环比计算必须除零保护has_anomaly 展平为字符串(boolean 在变量选择器不可见):

def main(sales, customer, support):

    issues = []

    sales = sales or {}; customer = customer or {}; support = support or {}

    cur = sales.get("current") or {}; prev = sales.get("previous") or {}

    prev_rev = prev.get("total_revenue") or 0

    rev_change = (cur.get("total_revenue", 0) - prev_rev) / prev_rev * 100 if prev_rev else 0

    if rev_change < -10:

        issues.append(f"营收环比下降 {abs(rev_change):.1f}%,需关注")

    # 客服满意度 < 4.0、活跃用户环比 < -5% 同理检测……

    return {"revenue_change_pct": round(rev_change, 1), "anomalies": issues,

            "has_anomaly": "true" if issues else "false", "summary": "...", "analysis": {...}}

5.4 分析结论 LLM(lm_analysis)

你是一个运营数据分析师。根据以下本周({{#start.report_week#}})运营数据,生成中文分析结论。

核心指标摘要:{{#cd_analyze.summary#}}

环比变化:营收 {{#cd_analyze.revenue_change_pct#}}%,客服工单 {{#cd_analyze.ticket_change_pct#}}%,活跃用户 {{#cd_analyze.user_change_pct#}}%

异常项列表:{{#cd_analyze.anomalies#}}

要求:

1. 分析营收、订单、客服、用户四个维度的环比变化趋势

2. 指出异常项(如有)

3. 给出 2-3 条可执行的行动建议

4. 控制在 300 字以内

5. 直接输出正文,不要输出任何解释性文字

5.5 模板转换(tmpl_report)

模板开头一组 {% set %} 做空值保护,计算处全部 if prev else 0 除零保护;所有输入走 variables 映射,模板内用 {{ var }} 引用:

{% set s = sales if sales else {} %}

{% set cur = s.current if s.current else {} %}

{% set prev = s.previous if s.previous else {} %}

{% set issues = analysis.anomalies if analysis and analysis.anomalies else [] %}

{% set rev_pct = analysis.revenue_change_pct if analysis and analysis.revenue_change_pct is defined else 0 %}

{% if output_format == 'json' %}

{ "report_week": "{{ report_week }}", "revenue": {{ cur.total_revenue | default(0) }},

  "anomalies": [{% for issue in issues %}"{{ issue }}"{% if not loop.last %}, {% endif %}{% endfor %}] }

{% else %}

# 运营周报({{ report_week }})

## 核心指标

- 营收:¥{{ cur.total_revenue | default(0) }}(环比 {{ rev_pct }}%)

- 订单:{{ cur.total_orders | default(0) }} 单

## 趋势分析

{{ llm_analysis }}

## 异常预警

{% if issues | length > 0 %}{% for issue in issues %}- ⚠️ {{ issue }}{% endfor %}

{% else %}- 无异常,各项指标正常 ✅{% endif %}

{% endif %}

5.6 推送(cd_push)

push_enabled 为 true 时模拟调用企业微信机器人(真实场景换 http-request 节点,URL 指向机器人 Webhook)。

6. 运行验证

输入 期望行为 实测
report_week=上周,output_format=markdown,push_enabled=关 输出完整 Markdown 周报(核心指标/趋势分析/异常预警三节) 与预期一致
output_format=json 输出 JSON 结构报告(report_week/revenue/anomalies/analysis 字段) 与预期一致
push_enabled=开 push_status = 已推送到企业微信机器人(模拟 Webhook 调用成功,HTTP 200) 与预期一致
三路数据置空(冒烟测试) 模板空值保护生效,输出空报告而不是运行报错 与预期一致({% set %} 兜底)

7. 实战坑

现象 修复
模板变量为 null/空时直接计算 {{ x | length }}x | map(...) | sum / (x | length) 运行报错(0/0 除零、None 切片),冒烟输入空数据直接崩溃 模板开头 {% set %} 兜底默认值 + 计算处 (sum / length) if (length) > 0 else 0(dify102 全量验证实测)
长报告 text 返回空字符串 DeepSeek 把内容写进思考部分,或思考与输出共享 max_tokens 预算被挤空 报告类节点 max_tokens 提到 4000-8000 + prompt 明确「直接输出正文,不要输出任何解释性文字」(dify103_09 lm_format 实测)
has_anomaly 用 boolean 类型输出 下游 LLM 模板 {{#cd_analyze.has_anomaly#}} 取不到值(boolean 变量选择器不可见) 代码输出 "true" if x else "false" 字符串,模板语义不变(2026-07-31 dify-103 实测)
模板里直接写 {{#node.field#}} 引用 code 的 object 字段 validate_dsl.py 模板正则扫描报错,或跨节点引用不可见字段 所有输入走 variables 映射(value_selector),模板内用 {{ var }} 引用(dify102-2 21 实证)

8. 实验文档及源码获取

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

联系我

15088711270

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

微信二维码

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