十:Agent 数据分析可视化与自动化 Workflow 编排

AI Agent开发实战:从Tool到Workflow的完整构建指南 在人工智能应用开发领域,Agent架构正成为连接大语言模型与实际业务需求的关键技术范式。其核心原理在于通过模块化设计,将复杂任务分解为可执行的原子操作单元,从而实现自动化决策与执行。从技术价值角度看,这种架构显著提升了AI系统的可扩展性、可维护性和任务完成能力,使得单一模型能够处理多步骤、多工具的复杂场景。在实际工程实践中,开发者需要掌握从基础工具封装、技能组合到工作流编排的完整链路。本文聚焦于LangChain与LangGraph等主流框架,通过构建天气查询、旅行规划等具体案例,详细演示了如何实现Tool 阅读详情

系列第十篇:Agent 数据分析可视化与自动化 Workflow 编排

前言

九篇文章下来,我们的 Agent 已经能对话、能协作、能查库、能记忆、能防御。但在企业日常运营中,有两个高频场景始终缺少最后一块拼图:

场景一:领导说"给我出一份本月各楼栋能耗对比图"——Agent 能查出数据,但只能返回文字表格,不能生成图表。

场景二:运维团队说"每天早上 8 点自动巡检所有区域的能耗异常,有问题就发日报"——Agent 能力再强,也只会在用户主动提问时才能工作。

本文将补齐这两块拼图:让 Agent 具备数据分析 + 可视化图表生成能力,以及 让 Agent 脱离用户手动触发,实现定时自动化运行。


一、数据分析与可视化:让 Agent 从"说"到"画"

1.1 为什么需要可视化?

人类大脑对视觉信息的处理效率远高于纯文本。同样一组数据:

文本形式:
A栋: 45,230 kWh
B栋: 67,890 kWh  ← 哪个最高?需要逐行比较
C栋: 89,340 kWh
D栋: 32,100 kWh

图表形式:
C栋 ████████████████████ 89,340
B栋 ███████████████      67,890
A栋 ██████████           45,230
D栋 ███████              32,100

在数据分析场景中,Agent 需要能够:

  1. 分析数据:统计、聚合、对比、趋势识别
  2. 选择图表类型:根据数据特征自动选择最合适的可视化方式
  3. 生成图表:输出可嵌入报告或网页的可视化结果

1.2 三种图表生成方案对比

┌────────────────────────────────────────────────────────────────┐
│               Agent 图表生成方案对比                            │
│                                                                │
│  方案          实现方式        优点           缺点              │
│  ──────────────────────────────────────────────────────────     │
│  ECharts       前端渲染       交互性强       需要前端页面       │
│  (推荐)        生成 JSON      效果专业       Agent 输出 JSON    │
│                配置           国产开源                          │
│                                                                │
│  Matplotlib    后端 Python    独立图片       需要 Python 环境   │
│  /JFreeChart   生成 PNG       可嵌入文档     Java图表库较弱     │
│                                                                │
│  Mermaid       Markdown       纯文本        样式有限           │
│  ASCII Art     语法           无需依赖       只适合简单图表     │
│                                                                │
│  推荐策略:                                                    │
│  · Web 应用 → ECharts(生成 JSON 配置,前端渲染)              │
│  · 报告导出 → JFreeChart(生成 PNG 嵌入 PDF)                 │
│  · 纯对话   → ASCII Art / Mermaid(无前端依赖)               │
│                                                                │
└────────────────────────────────────────────────────────────────┘

二、数据分析引擎:Agent 的结构化分析能力

2.1 分析类型定义

/**
 * 数据分析类型枚举
 * 
 * Agent 需要识别用户想做什么类型的分析,
 * 然后选择对应的分析策略和可视化方式。
 */
@Getter
public enum AnalysisType {

    COMPARISON(1, "对比分析",
        "比较多个对象的指标差异",
        ChartType.BAR),

    TREND(2, "趋势分析",
        "分析指标随时间的变化趋势",
        ChartType.LINE),

    DISTRIBUTION(3, "分布分析",
        "分析数据的分布特征和占比",
        ChartType.PIE),

    RANKING(4, "排名分析",
        "按指标值排序,找出 Top/Bottom N",
        ChartType.HORIZONTAL_BAR),

    CORRELATION(5, "关联分析",
        "分析两个或多个指标之间的相关性",
        ChartType.SCATTER),

    ANOMALY(6, "异常检测",
        "识别数据中的异常点和偏离趋势",
        ChartType.LINE_WITH_MARKERS);

    private final Integer key;
    private final String value;
    private final String description;
    private final ChartType defaultChartType;

    AnalysisType(Integer key, String value,
                 String description, ChartType defaultChartType) {
        this.key = key;
        this.value = value;
        this.description = description;
        this.defaultChartType = defaultChartType;
    }
}

/**
 * 图表类型枚举
 */
@Getter
public enum ChartType {

    BAR(1, "柱状图", "适合分类对比"),
    LINE(2, "折线图", "适合时间序列趋势"),
    PIE(3, "饼图", "适合占比分析"),
    HORIZONTAL_BAR(4, "条形图", "适合排名展示"),
    SCATTER(5, "散点图", "适合关联分析"),
    LINE_WITH_MARKERS(6, "标注折线图", "适合异常点标记"),
    STACKED_BAR(7, "堆叠柱状图", "适合多维度对比"),
    HEATMAP(8, "热力图", "适合时段×区域分析");

    private final Integer key;
    private final String value;
    private final String suitableFor;

    ChartType(Integer key, String value, String suitableFor) {
        this.key = key;
        this.value = value;
        this.suitableFor = suitableFor;
    }
}

2.2 数据分析 Agent

/**
 * 数据分析 Agent
 * 
 * 职责:
 * 1. 理解用户的分析需求 → 确定分析类型
 * 2. 调用 TextToSqlTool 获取数据
 * 3. 对数据进行统计分析
 * 4. 选择合适的图表类型
 * 5. 生成 ECharts 配置 JSON
 * 6. 生成分析结论文本
 */
@Service
public class DataAnalysisAgent {

    private final ChatLanguageModel analysisModel;
    private final TextToSqlTool sqlTool;

    /**
     * 执行完整的数据分析流程
     * 
     * @param userQuery 用户的分析需求
     * @return 分析结果(含图表配置 + 文字结论)
     */
    public AnalysisResult analyze(String userQuery) {
        // Step 1: 分析意图识别
        AnalysisIntent intent = recognizeIntent(userQuery);

        log.info("分析意图: type={}, entities={}, timeRange={}",
            intent.analysisType(), intent.entities(),
            intent.timeRange());

        // Step 2: 数据获取
        String rawData = sqlTool.queryDatabase(
            intent.sqlQueryDescription());

        // Step 3: 数据结构化解析
        StructuredData data = parseStructuredData(rawData);

        // Step 4: 统计分析
        StatisticalSummary stats = computeStatistics(
            data, intent.analysisType());

        // Step 5: 图表配置生成
        EChartsConfig chartConfig = generateChartConfig(
            intent.analysisType(), intent.chartType(),
            data, stats);

        // Step 6: 分析结论生成
        String conclusion = generateConclusion(
            userQuery, intent, data, stats);

        return new AnalysisResult(
            conclusion, chartConfig, stats, data);
    }

    /**
     * 分析意图识别
     * 
     * 用 LLM 从自然语言中提取:
     * - 分析类型(对比/趋势/排名/...)
     * - 分析实体(哪些区域/设备/指标)
     * - 时间范围
     * - 推荐的图表类型
     */
    private AnalysisIntent recognizeIntent(String userQuery) {
        String prompt = String.format("""
            你是一个数据分析意图识别专家。请分析用户的需求,提取以下信息:
            
            ## 可选分析类型
            1. COMPARISON: 比较多个对象的指标(如"A栋和B栋的能耗对比")
            2. TREND: 时间序列趋势(如"最近半年每月用电量变化")
            3. DISTRIBUTION: 占比分布(如"各区域能耗占总能耗的比例")
            4. RANKING: 排名(如"能耗最高的5个区域")
            5. ANOMALY: 异常检测(如"最近有没有异常的能耗飙升")
            
            ## 可选图表类型
            BAR, LINE, PIE, HORIZONTAL_BAR, STACKED_BAR, HEATMAP,
            LINE_WITH_MARKERS
            
            按 JSON 返回:
            {
              "analysisType": "...",
              "chartType": "...",
              "entities": ["区域/设备名称"],
              "metrics": ["能耗指标"],
              "timeRange": "时间范围描述",
              "sqlQueryDescription": "转化为数据库查询的自然语言描述"
            }
            
            用户需求:%s
            """, userQuery);

        String result = analysisModel.generate(
            UserMessage.from(prompt)).content().text().trim();

        return parseIntent(result);
    }
}

2.3 ECharts 配置生成

这是核心部分——将分析数据转换为 ECharts 的 JSON 配置。前端拿到这个 JSON 后可以直接渲染出专业的图表。

/**
 * ECharts 配置生成器
 * 
 * 设计思路:
 * Agent 不生成 HTML/JS 代码,而是生成 ECharts 的 option JSON。
 * 前端有一个通用的图表渲染组件,接收 JSON 即可渲染。
 * 
 * 这样设计的好处:
 * 1. Agent 的输出是结构化 JSON,不是代码(安全性高)
 * 2. 前端统一管控样式和交互(一致性)
 * 3. JSON 可以缓存/存储/转发(灵活性)
 */
@Service
public class EChartsConfigGenerator {

    /**
     * 根据分析类型和数据生成 ECharts 配置
     */
    public EChartsConfig generate(AnalysisType analysisType,
                                   ChartType chartType,
                                   StructuredData data,
                                   StatisticalSummary stats) {
        return switch (chartType) {
            case BAR -> generateBarChart(data, stats);
            case LINE -> generateLineChart(data, stats);
            case PIE -> generatePieChart(data, stats);
            case HORIZONTAL_BAR -> generateHorizontalBar(data, stats);
            case STACKED_BAR -> generateStackedBar(data, stats);
            case HEATMAP -> generateHeatmap(data, stats);
            case LINE_WITH_MARKERS ->
                generateLineWithAnomalyMarkers(data, stats);
            default -> generateBarChart(data, stats);
        };
    }

    /**
     * 柱状图:各楼栋能耗对比
     * 
     * 示例场景:"A/B/C/D栋本月能耗对比"
     */
    private EChartsConfig generateBarChart(StructuredData data,
                                            StatisticalSummary stats) {
        Map<String, Object> option = new LinkedHashMap<>();

        // 标题
        option.put("title", Map.of(
            "text", stats.title(),
            "subtext", stats.subtitle(),
            "left", "center"
        ));

        // 提示框
        option.put("tooltip", Map.of(
            "trigger", "axis",
            "axisPointer", Map.of("type", "shadow"),
            "formatter", "{b}: {c} kWh"
        ));

        // X轴:分类(楼栋名称)
        option.put("xAxis", Map.of(
            "type", "category",
            "data", data.labels(),
            "axisLabel", Map.of(
                "rotate", data.labels().size() > 6 ? 30 : 0
            )
        ));

        // Y轴:数值
        option.put("yAxis", Map.of(
            "type", "value",
            "name", "能耗 (kWh)",
            "axisLabel", Map.of(
                "formatter", "{value}"
            )
        ));

        // 数据系列
        List<Map<String, Object>> series = List.of(
            Map.of(
                "name", "能耗",
                "type", "bar",
                "data", data.values(),
                "itemStyle", Map.of(
                    "color", Map.of(
                        "type", "linear",
                        "x", 0, "y", 0, "x2", 0, "y2", 1,
                        "colorStops", List.of(
                            Map.of("offset", 0,
                                "color", "#5470C6"),
                            Map.of("offset", 1,
                                "color", "#91CC75")
                        )
                    )
                ),
                // 标注平均线
                "markLine", Map.of(
                    "data", List.of(
                        Map.of("type", "average", "name", "平均值")
                    )
                )
            )
        );
        option.put("series", series);

        // 图例和网格
        option.put("grid", Map.of(
            "left", "10%", "right", "10%", "bottom", "15%"));

        return new EChartsConfig("bar", option);
    }

    /**
     * 折线图:能耗时间趋势
     * 
     * 示例场景:"A栋最近30天每日用电量趋势"
     */
    private EChartsConfig generateLineChart(StructuredData data,
                                             StatisticalSummary stats) {
        Map<String, Object> option = new LinkedHashMap<>();

        option.put("title", Map.of(
            "text", stats.title(),
            "subtext", stats.subtitle(),
            "left", "center"
        ));

        option.put("tooltip", Map.of(
            "trigger", "axis"
        ));

        // X轴:日期
        option.put("xAxis", Map.of(
            "type", "category",
            "data", data.labels(),
            "boundaryGap", false
        ));

        // Y轴
        option.put("yAxis", Map.of(
            "type", "value",
            "name", "能耗 (kWh)"
        ));

        // 数据系列(带面积填充)
        option.put("series", List.of(
            Map.of(
                "name", "每日能耗",
                "type", "line",
                "data", data.values(),
                "smooth", true,         // 平滑曲线
                "areaStyle", Map.of(     // 面积填充
                    "opacity", 0.3
                ),
                // 标注最大/最小值
                "markPoint", Map.of(
                    "data", List.of(
                        Map.of("type", "max", "name", "最高"),
                        Map.of("type", "min", "name", "最低")
                    )
                ),
                // 标注平均线
                "markLine", Map.of(
                    "data", List.of(
                        Map.of("type", "average", "name", "平均")
                    )
                )
            )
        ));

        // 数据缩放(支持拖拽查看局部)
        option.put("dataZoom", List.of(
            Map.of("type", "inside", "start", 0, "end", 100),
            Map.of("type", "slider", "start", 0, "end", 100)
        ));

        return new EChartsConfig("line", option);
    }

    /**
     * 饼图:能耗占比分布
     * 
     * 示例场景:"各区域能耗占总能耗的比例"
     */
    private EChartsConfig generatePieChart(StructuredData data,
                                            StatisticalSummary stats) {
        // 组装数据项
        List<Map<String, Object>> pieData = new ArrayList<>();
        for (int i = 0; i < data.labels().size(); i++) {
            pieData.add(Map.of(
                "name", data.labels().get(i),
                "value", data.values().get(i)
            ));
        }

        Map<String, Object> option = new LinkedHashMap<>();

        option.put("title", Map.of(
            "text", stats.title(),
            "left", "center"
        ));

        option.put("tooltip", Map.of(
            "trigger", "item",
            "formatter", "{b}: {c} kWh ({d}%%)"
        ));

        option.put("series", List.of(
            Map.of(
                "type", "pie",
                "radius", List.of("35%%", "65%%"),  // 环形饼图
                "data", pieData,
                "emphasis", Map.of(
                    "itemStyle", Map.of(
                        "shadowBlur", 10,
                        "shadowOffsetX", 0,
                        "shadowColor", "rgba(0, 0, 0, 0.5)"
                    )
                ),
                "label", Map.of(
                    "formatter", "{b}\n{d}%%"
                )
            )
        ));

        option.put("legend", Map.of(
            "orient", "vertical",
            "left", "left"
        ));

        return new EChartsConfig("pie", option);
    }

    /**
     * 带异常标注的折线图
     * 
     * 示例场景:"最近30天能耗趋势,标注异常点"
     * 
     * 这是最实用的图表类型之一——不仅展示趋势,
     * 还能直观地指出哪些天的数据异常。
     */
    private EChartsConfig generateLineWithAnomalyMarkers(
            StructuredData data, StatisticalSummary stats) {

        // 检测异常点(超出均值 ± 2 标准差)
        double mean = stats.mean();
        double stdDev = stats.standardDeviation();
        double upperBound = mean + 2 * stdDev;
        double lowerBound = mean - 2 * stdDev;

        List<Map<String, Object>> markPointData = new ArrayList<>();
        for (int i = 0; i < data.values().size(); i++) {
            double val = ((Number) data.values().get(i)).doubleValue();
            if (val > upperBound) {
                markPointData.add(Map.of(
                    "name", "偏高",
                    "coord", List.of(data.labels().get(i), val),
                    "value", val,
                    "itemStyle", Map.of("color", "#EE6666")
                ));
            } else if (val < lowerBound) {
                markPointData.add(Map.of(
                    "name", "偏低",
                    "coord", List.of(data.labels().get(i), val),
                    "value", val,
                    "itemStyle", Map.of("color", "#91CC75")
                ));
            }
        }

        Map<String, Object> option = new LinkedHashMap<>();

        option.put("title", Map.of(
            "text", stats.title(),
            "subtext", String.format(
                "正常范围: %.0f ~ %.0f kWh (均值 ± 2σ)",
                lowerBound, upperBound),
            "left", "center"
        ));

        option.put("xAxis", Map.of(
            "type", "category",
            "data", data.labels(),
            "boundaryGap", false
        ));

        option.put("yAxis", Map.of(
            "type", "value",
            "name", "能耗 (kWh)"
        ));

        option.put("series", List.of(
            Map.of(
                "name", "每日能耗",
                "type", "line",
                "data", data.values(),
                "smooth", true,
                // 正常范围标注
                "markArea", Map.of(
                    "silent", true,
                    "data", List.of(List.of(
                        Map.of("yAxis", lowerBound,
                            "itemStyle", Map.of(
                                "color", "rgba(145,204,117,0.1)")),
                        Map.of("yAxis", upperBound)
                    ))
                ),
                // 异常点标注
                "markPoint", Map.of(
                    "data", markPointData,
                    "symbol", "triangle",
                    "symbolSize", 15
                ),
                // 平均线
                "markLine", Map.of(
                    "data", List.of(
                        Map.of("type", "average", "name", "平均")
                    )
                )
            )
        ));

        return new EChartsConfig("line", option);
    }
}

/**
 * ECharts 配置封装
 */
public record EChartsConfig(
    String chartType,            // bar / line / pie / ...
    Map<String, Object> option   // ECharts option JSON
) {
    /**
     * 序列化为 JSON 字符串(传输给前端)
     */
    public String toJson() {
        return JsonUtils.toJson(option);
    }
}

2.4 分析结论自动生成

/**
 * 分析结论生成器
 * 
 * 将数据统计结果转化为专业的自然语言分析结论。
 * 
 * 关键原则:
 * 1. 结论必须有数据支撑(每个结论都引用具体数值)
 * 2. 区分"事实"和"推测"(推测要明确标注"可能")
 * 3. 给出可操作的建议
 */
@Service
public class ConclusionGenerator {

    private final ChatLanguageModel conclusionModel;

    public String generate(String userQuery,
                            AnalysisIntent intent,
                            StructuredData data,
                            StatisticalSummary stats) {
        String prompt = String.format("""
            你是一个专业的能源数据分析师。请根据以下数据和分析结果,
            撰写一段专业的分析结论。
            
            ## 用户需求
            %s
            
            ## 分析类型
            %s
            
            ## 数据统计摘要
            - 数据点数: %d
            - 均值: %.1f kWh
            - 最大值: %.1f kWh (%s)
            - 最小值: %.1f kWh (%s)
            - 标准差: %.1f
            - 变化率: %s
            
            ## 原始数据
            %s
            
            ## 写作要求
            1. 结构:核心发现 → 数据支撑 → 原因分析(如有) → 建议
            2. 所有数值保留单位 (kWh)
            3. 百分比保留一位小数
            4. 控制在 200 字以内
            5. 区分事实和推测(推测用"可能"标注)
            """,
            userQuery,
            intent.analysisType().getValue(),
            stats.dataPointCount(),
            stats.mean(),
            stats.max(), stats.maxLabel(),
            stats.min(), stats.minLabel(),
            stats.standardDeviation(),
            stats.changeRate(),
            data.toBriefString());

        return conclusionModel.generate(
            UserMessage.from(prompt)).content().text().trim();
    }
}

2.5 Agent 工具封装

@Component
public class ChartGenerationTool {

    private final DataAnalysisAgent analysisAgent;

    @Tool("对用户的数据分析需求生成可视化图表和分析结论。" +
          "当用户需要'对比图'、'趋势图'、'占比图'、" +
          "'排名图'等可视化分析时使用此工具。" +
          "输入分析需求的自然语言描述," +
          "返回包含图表配置(JSON)和分析结论的结果。")
    public String generateChart(String analysisRequirement) {
        AnalysisResult result = analysisAgent.analyze(
            analysisRequirement);

        return String.format("""
            ## 分析结论
            %s
            
            ## 图表配置 (ECharts JSON)
            ```
json
            %s
            ```
            
            ## 统计摘要
            - 数据点数: %d
            - 均值: %.1f kWh
            - 最大: %.1f kWh
            - 最小: %.1f kWh
            """,
            result.conclusion(),
            result.chartConfig().toJson(),
            result.stats().dataPointCount(),
            result.stats().mean(),
            result.stats().max(),
            result.stats().min());
    }
}

2.6 前端图表渲染组件

vue
<!-- Vue3 通用图表组件 -->
<template>
  <div ref="chartContainer" class="agent-chart" 
       :style="{ width: width, height: height }">
  </div>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'

const props = defineProps({
  /** ECharts option JSON(Agent 输出的配置) */
  chartOption: { type: Object, required: true },
  width: { type: String, default: '100%' },
  height: { type: String, default: '400px' }
})

const chartContainer = ref(null)
let chartInstance = null

onMounted(() => {
  chartInstance = echarts.init(chartContainer.value)
  renderChart()
})

watch(() => props.chartOption, renderChart, { deep: true })

function renderChart() {
  if (!chartInstance || !props.chartOption) return
  
  // 合并默认样式(保证一致性)
  const mergedOption = {
    ...defaultTheme,
    ...props.chartOption
  }
  
  chartInstance.setOption(mergedOption, true)
}

// 默认主题配置(统一所有图表的视觉风格)
const defaultTheme = {
  textStyle: { fontFamily: 'PingFang SC, Microsoft YaHei' },
  color: ['#5470C6', '#91CC75', '#FAC858', '#EE6666', 
          '#73C0DE', '#3BA272', '#FC8452', '#9A60B4']
}
</script>

三、自动化 Workflow:让 Agent 脱离手动触发

3.1 为什么需要自动化?

目前的 Agent 都是 “被动响应”——用户不问,Agent 就不动。但企业中有大量的 周期性、规则性 任务:

  • 每天早上 8 点:自动生成昨日能耗日报
  • 每周一上午 9 点:自动生成上周能耗周报
  • 每小时:自动巡检所有区域能耗是否异常
  • 每月 1 号:自动生成上月能耗分析报告并发送给管理层
  • 触发式:当某区域能耗超过阈值时,自动执行分析流程

这些任务不需要人类每次手动触发——Agent 应该能 自动感知、自动决策、自动执行。

3.2 Workflow 引擎架构


┌──────────────────────────────────────────────────────────────┐
│                   Agent Workflow 引擎                         │
│                                                              │
│  ┌───────────┐                                               │
│  │ 触发器    │                                               │
│  │ Trigger   │                                               │
│  │           │                                               │
│  │ · Cron    │──┐                                            │
│  │ · 事件    │  │    ┌──────────────┐    ┌──────────────┐   │
│  │ · 阈值    │──┼───→│  Workflow    │───→│  Action      │   │
│  │ · 手动    │  │    │  Pipeline    │    │  Executor    │   │
│  └───────────┘  │    │  (编排引擎)  │    │  (执行引擎)  │   │
│                 │    └──────────────┘    └──────────────┘   │
│                 │         │                      │           │
│                 │         ▼                      ▼           │
│                 │    ┌──────────┐          ┌──────────┐     │
│                 │    │ Step     │          │ 日志     │     │
│                 │    │ Manager  │          │ 审计     │     │
│                 │    └──────────┘          └──────────┘     │
│                 │                                            │
│  ┌──────────────▼────────────────────────────────────────┐  │
│  │                    XXL-JOB 调度中心                     │  │
│  └────────────────────────────────────────────────────────┘  │
│                                                              │
└──────────────────────────────────────────────────────────────┘

3.3 Workflow 定义模型

java
/**
 * Workflow 定义
 * 
 * 一个 Workflow 由多个 Step 组成,
 * 每个 Step 可以是一次 Agent 调用、一次工具调用、
 * 或一个条件判断。
 */
@Table(name = "agent_workflow")
public class WorkflowDefinition {

    @Id
    private String workflowId;
    private String name;            // "每日能耗巡检"
    private String description;
    private String cronExpression;  // "0 0 8 * * ?"
    private boolean enabled;

    @Column(columnDefinition = "json")
    private List<WorkflowStep> steps;

    private String createdBy;
    private Instant createdAt;
}

/**
 * Workflow 步骤定义
 */
public record WorkflowStep(
    int stepIndex,
    String stepName,
    StepType type,
    String config,          // JSON 格式的步骤配置
    String conditionExpr,   // 条件表达式(可选)
    String onError          // SKIP / ABORT / RETRY
) {
    public enum StepType {
        AGENT_CHAT,        // Agent 对话(分析/生成)
        TOOL_CALL,         // 工具调用
        SQL_QUERY,         // 数据库查询
        CONDITION,         // 条件判断
        NOTIFICATION,      // 发送通知
        CHART_GENERATION,  // 生成图表
        HUMAN_APPROVAL     // 等待人工审批
    }
}

3.4 预置 Workflow 模板

java
/**
 * Workflow 模板工厂
 * 
 * 提供常见的预置 Workflow 模板,
 * 用户可以在模板基础上修改参数。
 */
@Component
public class WorkflowTemplateFactory {

    /**
     * 模板1: 每日能耗日报
     * 
     * 执行流程:
     * 1. 查询昨日各区域能耗数据
     * 2. 与前日/上周同期对比
     * 3. 识别异常区域
     * 4. 生成日报文本 + 图表
     * 5. 发送给相关人员
     */
    public WorkflowDefinition dailyReportTemplate() {
        return WorkflowDefinition.builder()
            .workflowId("daily-energy-report")
            .name("每日能耗日报")
            .description("每天早上8点自动生成昨日能耗日报")
            .cronExpression("0 0 8 * * ?")
            .enabled(true)
            .steps(List.of(
                new WorkflowStep(0,
                    "查询昨日数据",
                    WorkflowStep.StepType.SQL_QUERY,
                    """
                    {
                      "query": "查询昨日各区域每日能耗数据",
                      "timeRange": "yesterday"
                    }
                    """,
                    null, "ABORT"
                ),
                new WorkflowStep(1,
                    "对比分析",
                    WorkflowStep.StepType.AGENT_CHAT,
                    """
                    {
                      "prompt": "分析昨日各区域能耗数据,与前日和上周同期对比,指出异常区域"
                    }
                    """,
                    null, "ABORT"
                ),
                new WorkflowStep(2,
                    "生成趋势图",
                    WorkflowStep.StepType.CHART_GENERATION,
                    """
                    {
                      "prompt": "生成昨日各区域能耗对比柱状图",
                      "chartType": "BAR"
                    }
                    """,
                    null, "SKIP"
                ),
                new WorkflowStep(3,
                    "生成日报",
                    WorkflowStep.StepType.AGENT_CHAT,
                    """
                    {
                      "prompt": "基于以上分析结果,生成一份简洁的能耗日报,包含:概况、异常、建议",
                      "format": "markdown"
                    }
                    """,
                    null, "ABORT"
                ),
                new WorkflowStep(4,
                    "发送日报",
                    WorkflowStep.StepType.NOTIFICATION,
                    """
                    {
                      "recipients": ["energy-managers"],
                      "channel": "email",
                      "subject": "能耗日报 - ${date}"
                    }
                    """,
                    null, "SKIP"
                )
            ))
            .build();
    }

    /**
     * 模板2: 实时异常巡检
     * 
     * 执行流程:
     * 1. 查询所有区域的当前能耗
     * 2. 与阈值配置对比
     * 3. 如果有异常:触发深度分析
     * 4. 生成告警并通知
     */
    public WorkflowDefinition anomalyPatrolTemplate() {
        return WorkflowDefinition.builder()
            .workflowId("energy-anomaly-patrol")
            .name("能耗异常巡检")
            .description("每小时自动巡检所有区域能耗异常")
            .cronExpression("0 0 * * * ?")
            .enabled(true)
            .steps(List.of(
                new WorkflowStep(0,
                    "获取实时数据",
                    WorkflowStep.StepType.SQL_QUERY,
                    """
                    {
                      "query": "查询所有区域今日实时能耗和告警阈值配置"
                    }
                    """,
                    null, "ABORT"
                ),
                new WorkflowStep(1,
                    "阈值判断",
                    WorkflowStep.StepType.CONDITION,
                    """
                    {
                      "expression": "any_area_exceeds_threshold",
                      "description": "是否有任何区域的能耗超过告警阈值"
                    }
                    """,
                    null, "ABORT"
                ),
                new WorkflowStep(2,
                    "异常深度分析",
                    WorkflowStep.StepType.AGENT_CHAT,
                    """
                    {
                      "prompt": "对超出阈值的区域进行深度分析:查近7天趋势、可能的原因、建议措施"
                    }
                    """,
                    "step[1].result == 'true'",  // 只在有异常时执行
                    "SKIP"
                ),
                new WorkflowStep(3,
                    "发送告警",
                    WorkflowStep.StepType.NOTIFICATION,
                    """
                    {
                      "recipients": ["area-managers"],
                      "channel": "sms",
                      "level": "WARNING"
                    }
                    """,
                    "step[1].result == 'true'",
                    "SKIP"
                )
            ))
            .build();
    }

    /**
     * 模板3: 月度分析报告
     * 
     * 执行流程:
     * 1. 查询上月全量数据
     * 2. 多维度统计分析(同比、环比、排名、分布)
     * 3. 生成多张图表
     * 4. 撰写完整分析报告
     * 5. 人工审批
     * 6. 发送给管理层
     */
    public WorkflowDefinition monthlyReportTemplate() {
        return WorkflowDefinition.builder()
            .workflowId("monthly-energy-report")
            .name("月度能耗分析报告")
            .description("每月1号自动生成上月能耗分析报告")
            .cronExpression("0 0 9 1 * ?")
            .enabled(true)
            .steps(List.of(
                new WorkflowStep(0, "数据采集",
                    WorkflowStep.StepType.SQL_QUERY,
                    """
                    {"query": "查询上月所有区域的每日能耗、设备能耗、告警记录"}
                    """, null, "ABORT"),

                new WorkflowStep(1, "同比环比分析",
                    WorkflowStep.StepType.AGENT_CHAT,
                    """
                    {"prompt": "进行同比(与去年同月)和环比(与上月)分析"}
                    """, null, "ABORT"),

                new WorkflowStep(2, "排名分析",
                    WorkflowStep.StepType.CHART_GENERATION,
                    """
                    {"prompt": "生成各区域月度能耗排名柱状图", "chartType": "HORIZONTAL_BAR"}
                    """, null, "SKIP"),

                new WorkflowStep(3, "趋势分析",
                    WorkflowStep.StepType.CHART_GENERATION,
                    """
                    {"prompt": "生成近6个月的月度能耗趋势折线图", "chartType": "LINE"}
                    """, null, "SKIP"),

                new WorkflowStep(4, "占比分析",
                    WorkflowStep.StepType.CHART_GENERATION,
                    """
                    {"prompt": "生成各区域能耗占比饼图", "chartType": "PIE"}
                    """, null, "SKIP"),

                new WorkflowStep(5, "撰写报告",
                    WorkflowStep.StepType.AGENT_CHAT,
                    """
                    {
                      "prompt": "综合以上分析,撰写一份完整的月度能耗分析报告。包含:执行摘要、数据分析、异常事件、优化建议、下月预测",
                      "format": "markdown"
                    }
                    """, null, "ABORT"),

                new WorkflowStep(6, "人工审批",
                    WorkflowStep.StepType.HUMAN_APPROVAL,
                    """
                    {"approvers": ["energy-director"], "timeout": "24h"}
                    """, null, "ABORT"),

                new WorkflowStep(7, "发送报告",
                    WorkflowStep.StepType.NOTIFICATION,
                    """
                    {
                      "recipients": ["management-team"],
                      "channel": "email",
                      "attachPdf": true
                    }
                    """, null, "SKIP")
            ))
            .build();
    }
}

3.5 Workflow 执行引擎

java
/**
 * Workflow 执行引擎
 * 
 * 核心设计:
 * 1. 每个 Step 的执行结果写入上下文(Context)
 * 2. 后续 Step 可以引用前序 Step 的结果
 * 3. 条件 Step 根据表达式决定是否跳过后续 Step
 * 4. 每一步都有日志和审计记录
 */
@Service
public class WorkflowExecutor {

    private final AgentService agentService;
    private final SqlQueryExecutor sqlExecutor;
    private final ChartGenerationTool chartTool;
    private final NotificationService notificationService;
    private final HumanInTheLoopManager hitlManager;
    private final WorkflowLogRepository logRepo;

    /**
     * 执行完整的 Workflow
     * 
     * @param workflow Workflow 定义
     * @param triggerContext 触发上下文(如定时触发的日期信息)
     * @return 执行结果
     */
    public WorkflowExecutionResult execute(
            WorkflowDefinition workflow,
            Map<String, Object> triggerContext) {

        String executionId = UUID.randomUUID().toString();
        log.info("开始执行 Workflow: id={}, name={}, execution={}",
            workflow.getWorkflowId(), workflow.getName(), executionId);

        // 执行上下文:存储每个 Step 的输出
        Map<String, Object> context = new HashMap<>(triggerContext);
        List<StepExecutionRecord> records = new ArrayList<>();

        for (WorkflowStep step : workflow.getSteps()) {
            long stepStart = System.currentTimeMillis();

            // 条件检查:是否需要执行此 Step
            if (step.conditionExpr() != null
                    && !evaluateCondition(step.conditionExpr(),
                                          context)) {
                records.add(StepExecutionRecord.skipped(
                    step.stepIndex(), step.stepName()));
                log.debug("Step {} 条件不满足,跳过",
                    step.stepIndex());
                continue;
            }

            try {
                // 执行 Step
                Object result = executeStep(step, context);

                // 将结果写入上下文
                context.put(
                    "step[" + step.stepIndex() + "].result",
                    result);

                long duration = System.currentTimeMillis() - stepStart;
                records.add(StepExecutionRecord.success(
                    step.stepIndex(), step.stepName(),
                    result, duration));

                log.info("Step {} 完成: {} ({}ms)",
                    step.stepIndex(), step.stepName(), duration);

            } catch (Exception e) {
                long duration = System.currentTimeMillis() - stepStart;
                records.add(StepExecutionRecord.failed(
                    step.stepIndex(), step.stepName(),
                    e.getMessage(), duration));

                log.error("Step {} 失败: {}",
                    step.stepIndex(), e.getMessage());

                // 根据错误策略决定
                switch (step.onError()) {
                    case "ABORT" -> {
                        log.error("Workflow 中止执行");
                        return WorkflowExecutionResult.failed(
                            executionId, records,
                            "Step " + step.stepIndex()
                                + " 失败: " + e.getMessage());
                    }
                    case "SKIP" -> log.warn("Step {} 失败,继续执行",
                        step.stepIndex());
                    case "RETRY" -> {
                        // 重试一次
                        try {
                            Object result = executeStep(step, context);
                            context.put(
                                "step[" + step.stepIndex() + "].result",
                                result);
                            records.add(StepExecutionRecord.success(
                                step.stepIndex(), step.stepName(),
                                result, duration));
                        } catch (Exception retryEx) {
                            return WorkflowExecutionResult.failed(
                                executionId, records,
                                "Step " + step.stepIndex()
                                    + " 重试仍失败");
                        }
                    }
                }
            }
        }

        log.info("Workflow 执行完成: {}", executionId);
        return WorkflowExecutionResult.success(executionId, records);
    }

    /**
     * 执行单个 Step(按类型分发)
     */
    private Object executeStep(WorkflowStep step,
                                Map<String, Object> context)
            throws Exception {
        Map<String, Object> config = JsonUtils.parseMap(
            step.config());

        return switch (step.type()) {
            case AGENT_CHAT -> {
                String prompt = resolveVariables(
                    (String) config.get("prompt"), context);
                yield agentService.chat(prompt);
            }
            case SQL_QUERY -> {
                String query = resolveVariables(
                    (String) config.get("query"), context);
                yield sqlExecutor.queryByNaturalLanguage(query);
            }
            case CHART_GENERATION -> {
                String prompt = resolveVariables(
                    (String) config.get("prompt"), context);
                yield chartTool.generateChart(prompt);
            }
            case NOTIFICATION -> {
                String[] recipients = ((List<String>)
                    config.get("recipients")).toArray(String[]::new);
                String lastResult = String.valueOf(
                    context.get("step[" + (step.stepIndex() - 1)
                        + "].result"));
                yield notificationService.sendBatch(
                    recipients, lastResult);
            }
            case CONDITION -> {
                String expr = (String) config.get("expression");
                yield evaluateCondition(expr, context)
                    ? "true" : "false";
            }
            case HUMAN_APPROVAL -> {
                // 阻塞等待人工审批
                yield hitlManager.requestConfirmation(
                    "workflow-approval",
                    "Workflow 需要您的审批",
                    step.stepName(),
                    PendingConfirmation.ConfirmationType.APPROVAL,
                    context,
                    Duration.parse((String)
                        config.getOrDefault("timeout", "PT1H"))
                );
            }
        };
    }

    /**
     * 变量替换:将 ${variable} 替换为上下文中的值
     * 
     * 例如: "能耗日报 - ${date}" → "能耗日报 - 2026-08-27"
     */
    private String resolveVariables(String template,
                                     Map<String, Object> context) {
        String result = template;
        for (var entry : context.entrySet()) {
            result = result.replace(
                "${" + entry.getKey() + "}",
                String.valueOf(entry.getValue()));
        }
        return result;
    }
}

3.6 XXL-JOB 集成调度

项目使用 XXL-JOB 作为任务调度中心,我们将 Workflow 引擎接入 XXL-JOB:

java
/**
 * XXL-JOB 调度任务入口
 * 
 * 每个预置的 Workflow 对应一个 XXL-JOB Job。
 * 通过 XXL-JOB 的管理后台可以:
 * - 调整 Cron 表达式
 * - 手动触发执行
 * - 查看执行日志
 * - 暂停/恢复任务
 */
@Component
public class AgentWorkflowJobs {

    private final WorkflowExecutor executor;
    private final WorkflowDefinitionRepository workflowRepo;

    /**
     * 每日能耗日报 Job
     * XXL-JOB 配置: Cron = "0 0 8 * * ?"
     */
    @XxlJob("dailyEnergyReportJob")
    public void dailyReportJob() {
        WorkflowDefinition workflow = workflowRepo
            .findById("daily-energy-report");

        Map<String, Object> triggerContext = Map.of(
            "date", LocalDate.now().minusDays(1).toString(),
            "reportType", "daily",
            "triggerTime", Instant.now().toString()
        );

        WorkflowExecutionResult result = executor.execute(
            workflow, triggerContext);

        if (result.success()) {
            XxlJobHelper.log("日报生成成功");
        } else {
            XxlJobHelper.log("日报生成失败: " + result.error());
            XxlJobHelper.handleFail(result.error());
        }
    }

    /**
     * 能耗异常巡检 Job
     * XXL-JOB 配置: Cron = "0 0 * * * ?"
     */
    @XxlJob("energyAnomalyPatrolJob")
    public void anomalyPatrolJob() {
        WorkflowDefinition workflow = workflowRepo
            .findById("energy-anomaly-patrol");

        Map<String, Object> triggerContext = Map.of(
            "date", LocalDate.now().toString(),
            "triggerTime", Instant.now().toString()
        );

        WorkflowExecutionResult result = executor.execute(
            workflow, triggerContext);

        if (!result.success()) {
            XxlJobHelper.handleFail(result.error());
        }
    }

    /**
     * 月度能耗报告 Job
     * XXL-JOB 配置: Cron = "0 0 9 1 * ?"
     */
    @XxlJob("monthlyEnergyReportJob")
    public void monthlyReportJob() {
        WorkflowDefinition workflow = workflowRepo
            .findById("monthly-energy-report");

        YearMonth lastMonth = YearMonth.now().minusMonths(1);
        Map<String, Object> triggerContext = Map.of(
            "year", lastMonth.getYear(),
            "month", lastMonth.getMonthValue(),
            "monthName", lastMonth.toString(),
            "reportType", "monthly"
        );

        WorkflowExecutionResult result = executor.execute(
            workflow, triggerContext);

        if (!result.success()) {
            XxlJobHelper.handleFail(result.error());
        }
    }

    /**
     * 通用 Workflow 触发 Job(支持自定义 Workflow)
     * 
     * XXL-JOB 参数: workflowId=xxx
     * 通过 Job 参数动态指定要执行的 Workflow
     */
    @XxlJob("genericWorkflowJob")
    public void genericWorkflowJob() {
        String workflowId = XxlJobHelper.getJobParam();
        if (workflowId == null || workflowId.isBlank()) {
            XxlJobHelper.handleFail("缺少 workflowId 参数");
            return;
        }

        WorkflowDefinition workflow = workflowRepo
            .findById(workflowId);
        if (workflow == null) {
            XxlJobHelper.handleFail(
                "Workflow 不存在: " + workflowId);
            return;
        }

        WorkflowExecutionResult result = executor.execute(
            workflow, Map.of(
                "triggerTime", Instant.now().toString()));

        if (!result.success()) {
            XxlJobHelper.handleFail(result.error());
        }
    }
}

3.7 Workflow 执行日志与监控

java
/**
 * Workflow 执行日志服务
 * 
 * 记录每次 Workflow 执行的完整过程,
 * 用于问题排查和执行审计。
 */
@Service
public class WorkflowLogService {

    private final WorkflowLogRepository logRepo;

    /**
     * 保存执行日志
     */
    @Transactional
    public void saveExecutionLog(String executionId,
                                  String workflowId,
                                  WorkflowExecutionResult result) {
        WorkflowExecutionLog log = new WorkflowExecutionLog();
        log.setExecutionId(executionId);
        log.setWorkflowId(workflowId);
        log.setStatus(result.success() ? "SUCCESS" : "FAILED");
        log.setTotalSteps(result.records().size());
        log.setSucceededSteps((int) result.records().stream()
            .filter(StepExecutionRecord::success).count());
        log.setFailedSteps((int) result.records().stream()
            .filter(r -> !r.success()).count());
        log.setSkippedSteps((int) result.records().stream()
            .filter(StepExecutionRecord::skipped).count());
        log.setTotalDurationMs(result.records().stream()
            .mapToLong(StepExecutionRecord::durationMs).sum());
        log.setErrorMessage(result.error());
        log.setStepDetails(JsonUtils.toJson(result.records()));
        log.setCreatedAt(Instant.now());

        logRepo.save(log);
    }

    /**
     * 查询执行历史
     */
    public List<WorkflowExecutionLog> getExecutionHistory(
            String workflowId, int limit) {
        return logRepo.findByWorkflowId(
            workflowId, PageRequest.of(0, limit));
    }

    /**
     * Workflow 执行统计(用于监控面板)
     */
    public WorkflowStats getStats(String workflowId,
                                   LocalDate since) {
        List<WorkflowExecutionLog> logs =
            logRepo.findByWorkflowIdSince(workflowId, since);

        return new WorkflowStats(
            logs.size(),
            (int) logs.stream()
                .filter(l -> "SUCCESS".equals(l.getStatus()))
                .count(),
            (int) logs.stream()
                .filter(l -> "FAILED".equals(l.getStatus()))
                .count(),
            logs.stream()
                .mapToLong(WorkflowExecutionLog::getTotalDurationMs)
                .average().orElse(0)
        );
    }
}

四、数据分析 + Workflow 的完整实战场景


场景:每月1号自动生成上月能耗分析报告

┌─── Workflow 执行链 ──────────────────────────────────────────────┐
│                                                                  │
│  [08:00] 定时触发                                                │
│     │                                                            │
│  Step 0: SQL查询上月全量数据                                      │
│     → 获取 30天 × 5个区域 = 150条记录                             │
│     │                                                            │
│  Step 1: Agent 同比环比分析                                      │
│     → "8月总能耗 234,500 kWh,同比增长12.3%%,环比下降5.1%%"     │
│     │                                                            │
│  Step 2: 生成排名柱状图 (ECharts JSON)                            │
│     → { title: "各区域月度能耗排名", xAxis: [...], ... }         │
│     │                                                            │
│  Step 3: 生成趋势折线图                                          │
│     → { title: "近6月能耗趋势", series: [...] }                  │
│     │                                                            │
│  Step 4: 生成占比饼图                                            │
│     → { title: "各区域能耗占比", series: [...] }                 │
│     │                                                            │
│  Step 5: Agent 撰写完整报告                                      │
│     → Markdown 格式的 2000 字分析报告                             │
│     │                                                            │
│  Step 6: [HITL] 等待能源主管审批                                  │
│     → 推送审批请求 → 主管查看报告 → 点击"批准"                   │
│     │                                                            │
│  Step 7: 发送报告                                                │
│     → 邮件发送给管理团队,附带3张图表和报告正文                   │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

五、十篇文章完整知识图谱


┌──────────────────────────────────────────────────────────────────────┐
│                 Java AI Agent 实战系列 · 完整路线图                    │
│                                                                      │
│  第一篇 ── 单体 Agent          Tool Calling + RAG + Memory           │
│  第二篇 ── Multi-Agent + MCP   LangGraph4j + MCP 工具标准化          │
│  第三篇 ── 评估 + A2A          四维评估 + LLM-Judge + 跨组织通信     │
│  第四篇 ── 记忆 + 画像          三层记忆 + 画像自动提取               │
│  第五篇 ── 可观测性 + 成本      链路追踪 + Token 精算                │
│  第六篇 ── RAG 优化             混合检索 + 重排序 + 知识同步         │
│  第七篇 ── 安全攻防             Prompt注入防御 + 权限沙箱            │
│  第八篇 ── 国产模型 + 推理      四大模型 + Plan-Execute + Reflection │
│  第九篇 ── Text-to-SQL + HITL  自然语言查库 + 人机协作              │
│  第十篇 ── 可视化 + Workflow    图表生成 + 自动化编排 (本篇)         │
│                                                                      │
│  完整能力图谱:                                                      │
│  能构建 → 能协作 → 能评估 → 能记忆 → 能运维 → 能检索              │
│  → 能防御 → 能落地 → 能查数据 → 能可视化 + 能自动化              │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
从RPA到AI Agent:Agentic Workflow如何重塑企业自动化 在数字化转型浪潮中,企业自动化正经历从传统RPA到智能体工作流的范式跃迁。传统自动化依赖预设规则,擅长线性重复任务,但面对复杂多变场景时适应性不足。而基于AI Agent的智能体工作流,则通过规划与推理引擎、工具调用、记忆管理等核心组件,实现了从流程驱动到目标驱动的转变。这种架构赋予AI自主规划、决策和动态调整能力,使其能处理非线性、多步骤的复杂任务,如客户服务全流程或市场策略自动执行。其技术价值在于将AI从被动工具升级为主动执行者,显著提升业务处理智能化水平。应用场景广泛覆盖智能客服、数据分析、营销自动化 阅读详情

相关推荐

构建现代AI Agent:LangGraph、MCP与Workflow三大核心架构实战解析

在人工智能工程化实践中,智能体(Agent)架构设计正从简单的API调用演变为复杂的系统化构建。其核心原理在于通过模块化、可编排的组件,赋予大语言模型(LLM)长期记忆、动态决策和工具调用能力,从而提升应用的自主性和实用性。这一技术价值体现在能够将AI能力无缝集成到业务流程中,实现自动化与智能化决策。典型的应用场景包括智能客服、自动化数据分析、企业工具集成以及复杂任务流程编排。本文聚焦于当前主流的三大实现形态:基于图的**LangGraph**编排框架、实现工具安全调用的**MCP协议**,以及面向流程自动

weixin_30595035的博客 368

Workflow与Agent选型指南:从概念差异到实战场景解析

在自动化与智能化系统设计中,工作流(Workflow)与智能体(Agent)是两种核心范式。工作流基于预定义的确定性流程编排,强调可靠连接与无误调度,适用于步骤清晰、规则明确的重复性任务,如数据处理管道和审批流程。其技术价值在于提供高可预测性、完善的状态管理和易于协作的可视化设计。智能体则采用目标驱动的自主规划范式,依托大语言模型(LLM)等核心技术,具备动态决策和工具调用能力,擅长处理路径不唯一或需应对不确定性的开放式任务。在AI应用开发领域,理解两者的本质差异——即“编排”与“规划”的区别——是进行技术

weixin_30740295的博客 509

LangGraph、MCP与Workflow:构建复杂AI Agent的架构指南

在AI应用开发领域,构建能够处理复杂任务、具备记忆和工具调用能力的智能体(Agent)是当前的技术热点。其核心原理在于通过状态机管理对话流程,利用标准化协议集成外部工具,并通过工作流引擎编排多步骤任务,从而突破简单API调用的功能局限。这种架构的技术价值在于实现了Agent内部逻辑、技能实现与业务流程的解耦,大幅提升了系统的可扩展性和可维护性。典型的应用场景包括需要多轮交互的智能客服、个人办公助手以及跨系统的自动化数据分析流水线。本文聚焦于LangGraph、MCP(Model Context Protoc

weixin_34347651的博客 434

不只是代码:用 Agent 构建全自动数据分析与可视化流水线

本文从核心概念、架构设计、代码实现、落地优化四个维度,完整讲解了如何用大模型Agent搭建一套全自动的数据分析与可视化流水线,从用户输入自然语言需求到输出分析结论和交互式图表,全程不需要人工干预,可以帮数据从业者减少90%的重复报表工作。你可以基于本文提供的代码,结合自己公司的业务场景,增加数据源、业务口径和权限控制,快速落地一套适合自己公司的智能分析平台。

欢迎来到我的优快云空间!这里聚焦AI大模型应用实战,分享前沿技术、实战案例与开发经验。 342

企业级智能体平台架构:RAG+Workflow+Agent三支柱解析

检索增强生成(RAG)技术通过结合实时业务数据与大模型预训练知识,有效解决了基础模型数据滞后性问题。其核心在于构建高效的知识处理流水线,涉及动态分块策略、混合检索机制等关键技术环节。工作流引擎(Workflow)则为企业流程提供了可控的自动化编排能力,通过规则引擎与LLM驱动的混合架构,平衡确定性与灵活性需求。智能体(Agent)作为业务能力载体,需要明确定义角色边界与交互模式。这三种技术的有机组合,形成了现代企业智能体平台的基础架构,广泛应用于客服自动化、知识管理、业务流程优化等场景。以微软Copilot

weixin_30730053的博客 493

hermes-agent:多智能体编排与自动化执行实战指南

多智能体协作是AI落地的关键能力,但单Agent难以应对复杂业务,而自行编写调度代码又常陷入状态管理混乱、上下文丢失等困境。Agent框架通过统一的任务编排引擎、消息路由和工具注册机制,解决了多角色分工与流程控制的痛点,让系统具备可预测、可审计的自动化执行能力。从客服工单分类到数据查询、权限重置等场景,基于工作流驱动的方式能显著降低开发与维护成本。hermes-agent作为一款轻量级多智能体编排框架,将Agent、Tool、Workflow三大概念有机结合,支持工具权限隔离、上下文传递和失败重试,帮助开发

weixin_29100399的博客 316

AI Workflow与Agent核心区别:从流程自动化到自主决策的技术选型指南

在构建现代AI应用时,流程自动化与智能代理是两种核心的技术范式。流程自动化基于预定义的步骤和确定性的数据流,通过中心化编排确保任务的可重复性与可靠性,其技术价值在于处理结构化、高容错的批量任务。而智能代理则遵循目标驱动的自主决策机制,通过感知-思考-行动循环动态规划路径,适用于需要灵活应对变化的复杂场景。理解二者的本质区别——即执行固定剧本与解决开放问题的不同——对于技术选型至关重要。本文结合AI应用架构中的常见热词如LangChain和智能运维,深入探讨了从概念原理到混合架构的实践路径,为工程师在自动化脚

cuankuangzhong6373的博客 582

微信小程序导航栏滑动一定距离后固定到顶部

接上一篇文章的内容,通常在一个页面中部会有一个导航栏查看不同的内容,这篇文章旨在实现在页面下滑的过程中导航栏自动固定到顶部以便我们阅读过程中的切换。 下滑一段距离后自动固定到顶部 首先,思考这个功能应该是怎么实现的: 在页面下滑的过程中,我们希望系统应当能识别下滑了多少距离,当原本的导航栏逐渐被遮住以后(遮住意味着下滑了特定距离),触发这个功能,执行另一种样式,将导航栏固定在页面顶端,同时如果我们将页面往上滑,系统能识别到原来的导航栏能露出来了(即距离顶端的高度小于一个特定的值),这时候又执行最初的样式

Oblivision_Geek的博客 4030

【WorkBuddy】WorkBuddy三大核心抽象:Agent、Workflow与Trigger的深度解析

文章摘要: 本文深入解析自动化任务设计的三大核心抽象:Agent、Workflow和Trigger。Agent作为执行单元,封装具体任务能力;Workflow负责流程编排,定义执行顺序;Trigger决定任务启动时机。三者形成层次化结构,将"做什么"、"按什么顺序做"、"什么时候做"解耦。文章通过销售报告自动化案例,展示三者协同机制,并重点剖析Agent的内部运作流程,包括沙箱环境、五阶段执行模型及工具调用循环。最后以客户邮件处理为例,演示Agent的具体实现方式。这种抽象设计大幅提升自动化系统的可维护性和

ruiqideng的博客 399

Visionpro 使用CogPMAlignTool工具提取匹配轮廓点

【代码】Visionpro 使用CogPMAlignTool工具提取匹配轮廓点。

weixin_43947315的博客 2091

安卓ai自动化脚本 Yyds.Auto实现 示例 纯ai图像识别自动点击脚本

纯ai识别实现自动化脚本

专注安卓前沿科技 4780

从可视化工作流到代码优先Agent编排:范式转移与实践指南

工作流自动化是AI应用落地的关键环节,早期通过可视化工作流构建器拖拽节点即可快速搭建业务流程,大幅降低了开发门槛。然而,随着大模型推理能力和工具调用能力的跃升,固定路径的图形化编排已难以满足复杂生产环境对可测试性、可维护性和迭代效率的要求。AI Agent作为目标驱动的自主决策范式,正逐步取代过程驱动的Workflow范式。其核心原理由模型运行时动态规划工具调用序列,配合代码优先的工程化实践,显著提升系统的可控性与可观测性。在智能客服、内容生产、数据分析等开放场景中,代码优先的Agent编排能更好地处理多轮

weixin_34318272的博客 300

STC8H8K64U ADC采样实战:从蜂鸣器控制到电压测量(附完整代码)

本文以STC8H8K64U单片机为核心,通过两个实战案例详细讲解ADC采样的应用。首先介绍如何利用ADC采样值动态控制蜂鸣器响度,实现模拟PWM效果;其次深入探讨高精度外部电压测量的方法,包括硬件连接、两点校准技巧以及软件滤波,旨在帮助开发者从理论到实践全面掌握ADC功能。

weixin_29159441的博客 558

终极Dify工作流自动化指南:如何用可视化编排提升10倍工作效率

你是否曾经被重复性的工作流程折磨得精疲力尽?每天花大量时间在数据整理、内容创作、翻译校对这些机械性任务上?想象一下,如果有一个工具能帮你把这些流程自动化,让你专注于真正有价值的工作,那该多好!今天我要介绍的**Awesome-Dify-Workflow**正是这样一个开源项目,它为你提供了大量现成的Dify工作流模板,让你快速实现工作流程自动化。 **Awesome-Dify-Workflow*

gitblog_01028的博客 973

【NVIDIA Jetson Orin系列 - 安装NVIDIA SDK Manager】

NVIDIA Jetson Orin - 安装NVIDIA SDK Manager

dally2的博客 2691

[图解]SysML与EA实战:住宅安全系统接口块设计全解析

本文详细解析了使用SysML和EA工具进行住宅安全系统接口块设计的全流程。从系统上下文图入门到接口块实战技巧,涵盖块定义图与内部块图的视角转换,以及多图协同建模方法论。特别分享EA工具的高效操作指南和常见问题避坑建议,帮助工程师掌握精确信号定义、端口方向设置等关键技能,提升安防系统设计效率。

weixin_29234509的博客 332

AI Workflow与Agent核心区别:架构图与代码实战解析

在人工智能应用开发中,任务编排与自主决策是两大核心范式。AI Workflow(工作流)基于确定性流程编排,通过预设步骤序列处理任务,强调可靠性与可预测性,适用于文档处理、数据ETL等固定流水线场景。其技术原理围绕有向无环图(DAG)和线性链展开,实现高效的任务调度与状态管理。相比之下,AI Agent(智能体)则具备自主决策能力,基于感知-规划-行动循环动态调用工具并调整策略,以应对路径不明确的复杂目标,如研究分析、问题诊断等场景。从技术价值看,Workflow提供稳定可控的执行框架,而Agent拓展了A

ciqiaofu0192的博客 335

TPAMI 2025 | 从缺失碎片到杰作:基于上下文自适应扩散的图像修复

图像修复是一项具有挑战性的任务,尤其是要确保生成的内容与图像的现有部分无缝融合。尽管最近的扩散模型展现出了潜力,但它们在保持已知区域和未知(缺失)区域之间的连贯性方面常常遇到困难。这个问题源于扩散过程中缺乏明确的空间和语义对齐,导致生成的内容无法与原始图像平滑融合。此外,扩散模型通常依赖全局学习分布,而非局部特征,这使得生成的图像部分与现有图像部分之间存在不一致。在这项工作中,作者提出了ConFill,这是一个新颖的框架,引入了上下文自适应差异(CAD)模型,以确保在整个扩散过程中,已知区域和未知区域的中间

小白学视觉 240

Awesome-Dify-Workflow:从零到企业级的AI工作流编排实战指南

在AI应用开发领域,从原型验证到生产部署往往存在巨大的技术鸿沟。Awesome-Dify-Workflow项目通过提供丰富的工作流模板,为开发者搭建了一座连接AI模型能力与实际业务需求的桥梁。本文将深入剖析该项目的技术实现、架构设计以及在企业级场景中的应用实践。 ## 问题驱动:传统AI应用开发的三大痛点 传统AI应用开发面临三个核心挑战:开发周期长、维护成本高、技术门槛陡峭。企业通常需要投入

gitblog_00141的博客 1139

OpenClaw进阶实战(十二):电商比价工作流(一)——数据采集与竞品监控

本文介绍了基于OpenClaw的电商比价系统实现方案。该系统可自动化监控淘宝、京东、拼多多等平台的竞品价格波动,包含以下核心功能: 多平台价格采集:通过各平台官方API(淘宝客、京东联盟、多多进宝)获取商品价格,并提供模拟数据模式便于测试 数据存储与分析:使用SQLite存储历史价格数据,支持查询最近价格和价格变化趋势 模块化设计:采用技能化架构,分离了平台适配层(淘宝/京东/拼多多)、数据存储层和核心业务逻辑 扩展性:预留了价格波动告警接口(钉钉通知),为后续动态调价提供数据支撑 系统特点: 支持真实AP

PeterPan的博客 214

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

智能代理系统Hermes Agent:从工作流自动化到AI模型编排实战

智能代理(Intelligent Agent)作为人工智能领域的重要概念,通过感知环境、制定决策和执行动作来实现自主任务处理。其核心原理在于结合知识表示、推理机制和学习能力,使系统能够理解复杂指令并自主完成目标。在技术价值层面,智能代理显著提升了任务执行的自动化程度,减少了人工干预需求。工作流自动化(Workflow Automation)作为智能代理的典型应用场景,通过将重复性任务流程化,实现了业务流程的标准化和效率提升。在实际工程实践中,AI模型编排技术让多个智能代理能够协同工作,完成从任务分配到结果交

weixin_33894640的博客 1010

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

从Markdown到多Agent:AI Workflow的四次演进与实战解析

在人工智能工程化实践中,工作流(Workflow)是实现复杂任务自动化的核心技术范式。其核心原理在于将零散、依赖人工的交互,通过预定义的步骤与逻辑编排,转化为可重复、可管理的自动化流程,从而极大提升效率与可靠性。这一技术的核心价值在于为AI应用构建了一套“操作系统”,使其能稳定、可控地处理从数据分析到内容生成等各类任务。随着应用场景从简单脚本向复杂系统演进,技术架构也经历了从静态清单、动态脚本、可视化引擎到分布式多Agent系统的深刻变革。其中,多Agent架构通过引入具有规划、工具使用与协作能力的自治智能

weixin_29038303的博客 346

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Agent、Workflow、RAG与Skill技术选型指南

在构建智能系统时,Agent、Workflow、RAG和Skill是四种常见的技术范式。Agent以其自主决策能力适用于开放环境任务,Workflow擅长处理确定性的业务流程,RAG通过检索增强生成提升专业知识问答的准确性,而Skill则以模块化设计实现特定功能扩展。理解这些技术的核心原理和适用场景,对于系统架构设计至关重要。特别是在处理复杂业务需求时,如电商智能客服系统,往往需要混合使用这些技术。例如,结合LangChain框架开发自主Agent,配合LlamaIndex构建RAG知识库,再通过标准化Sk

weixin_30650039的博客 332

5个步骤掌握AI工作流:用Awesome-Dify-Workflow实现零基础自动化办公

在数字时代,许多职场人士面临"想自动化办公却被技术门槛阻挡"的困境。Awesome-Dify-Workflow项目通过低代码AI工作流的方式,让零基础用户也能借助AI构建专业级自动化流程。本文将系统介绍如何利用这个强大的开源项目实现从创意到成果的全流程自动化,帮助读者快速掌握可视化AI工作流构建的核心方法。 ## 🔍 传统办公的痛点与AI解决方案 传统办公流程中存在三个显著障碍:重复性任务耗

gitblog_01199的博客 1168

从Agent到超级工作台:LangGraph实战构建AI自动化工作流

智能体(Agent)作为人工智能领域的重要概念,通过感知环境、自主决策和执行动作来实现特定目标。其核心原理是基于大语言模型的推理能力,结合工具调用和环境交互。在工程实践中,智能体的价值体现在将AI能力转化为实际生产力,特别是在自动化工作流场景中。随着技术发展,单一智能体逐渐向模块化、可编排的超级工作台演进,这种架构通过工作流编排和状态管理,实现了多个专用技能的协同作业。本文以代码审查为例,展示如何利用LangGraph框架构建包含安全扫描、风格检查等技能的自动化工作台,为开发者提供从概念到落地的完整实践方案

weixin_33951761的博客 496

Awesome-Dify-Workflow:Dify工作流终极指南,轻松实现AI自动化

如果你正在寻找一个**简单快速**的方法来构建AI工作流,那么**Awesome-Dify-Workflow**就是你的完美选择。这个开源项目汇集了大量实用的Dify工作流程,让你无需编码就能实现复杂的AI任务自动化,无论是个人使用还是团队学习都非常合适。 ## 🚀 三分钟快速入门指南 开始使用Awesome-Dify-Workflow非常简单,只需要三个步骤: 1. **克隆仓库**:首

gitblog_00046的博客 534
上一篇: 九:Text-to-SQL 智能数据查询与 Human-in-the-Loop 人机协作
下一篇: 从像素到汉字:基于深度学习的中文OCR系统六步全链路剖析
yychen_java
博客等级 码龄8年 1451粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值