1. 项目概述:基于Spring AI构建类Dify风格的大模型工作流引擎
这个项目实现了一个模拟Dify核心功能的AI工作流引擎,主要解决传统AI应用开发中存在的几个痛点问题:首先,业务逻辑与AI能力耦合过紧导致迭代困难;其次,缺乏可视化的流程编排能力使得非技术人员难以参与;最后,复杂的参数传递和条件分支处理需要重复编写样板代码。
通过将AI能力封装为标准化的流程节点(如问答节点、分析节点),配合条件分支和参数传递机制,我们实现了以下核心价值:
- 可视化编排:通过拖拽方式组合AI能力,降低使用门槛
- 模块化设计:每个节点独立运行,便于单独测试和替换
- 上下文共享:节点间通过统一上下文传递参数,避免重复处理
- 灵活扩展:支持自定义节点类型,适应不同业务场景
技术栈选择上,采用Spring Boot作为基础框架,主要基于以下考虑:
- Spring AI提供了统一的大模型接入抽象层,避免绑定特定厂商
- Spring生态的成熟度保障了系统稳定性
- Java类型系统有助于构建严谨的流程定义模型
- Thymeleaf模板引擎快速实现基础前端界面
2. 核心架构设计解析
2.1 分层架构设计
系统采用典型的分层架构,各层职责明确:
code复制┌───────────────────────────────────────┐
│ 表现层 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Controller │ │ 前端页面 │ │
│ └─────────────┘ └─────────────┘ │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ 业务层 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 流程引擎核心 │ │ 节点处理器 │ │
│ └─────────────┘ └─────────────┘ │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ 持久层 │
│ ┌─────────────────────────────────┐ │
│ │ 内存存储模型 │ │
│ └─────────────────────────────────┘ │
└───────────────────────────────────────┘
2.2 关键模型设计
2.2.1 流程上下文(WorkflowContext)
作为流程执行的"记忆体",采用双Map结构存储数据:
java复制@Data
public class WorkflowContext {
// 全局参数存储(流程级别)
private Map<String, Object> globalParams = new HashMap<>();
// 节点输出存储(节点级别)
private Map<String, Object> nodeOutputs = new HashMap<>();
// 操作方法省略...
}
设计考量:
- 分离全局参数和节点输出,避免命名冲突
- 采用泛型Object存储值,兼容不同类型数据
- 线程安全考虑:每个流程实例独立上下文
2.2.2 流程定义(WorkflowDefinition)
java复制@Data
public class WorkflowDefinition {
private String workflowId; // 流程唯一标识
private String workflowName; // 业务语义名称
private String startNodeId; // 入口节点
private Map<String, BaseNode> nodes; // 节点集合
}
节点采用继承体系设计:
code复制BaseNode
├── AiChatNode (AI问答)
├── ConditionNode (条件分支)
└── EndNode (流程终止)
3. 核心实现细节
3.1 参数传递机制
实现类Dify的${}表达式语法,关键代码:
java复制public static String parseExpression(String template, WorkflowContext context) {
// 构建EL上下文
ELContext elContext = new EvaluationContext(null, null, null);
elContext.getELResolver().setValue(elContext, null, "global", context.getGlobalParams());
elContext.getELResolver().setValue(elContext, null, "node", context.getNodeOutputs());
// 解析所有${expr}片段
while (exprStart != -1) {
String expr = template.substring(exprStart + 2, exprEnd);
ValueExpression valueExpr = EXPRESSION_FACTORY.createValueExpression(elContext, expr, Object.class);
Object value = valueExpr.getValue(elContext);
result.append(value != null ? value : "");
}
return result.toString();
}
使用示例:
java复制// 节点配置
node.setPromptTemplate("分析请假原因:${global.user_input},参考历史:${node.node_1.output}");
// 执行时自动替换为实际值
3.2 流程引擎核心
WorkflowEngine.execute()方法的关键流程:
- 初始化上下文
- 从startNodeId开始执行
- 根据节点类型路由处理:
java复制if (node instanceof AiChatNode) { aiChatNodeHandler.handle((AiChatNode) node, context); } else if (node instanceof ConditionNode) { nextNodeId = conditionNodeHandler.handle((ConditionNode) node, context); } - 直到遇到EndNode或异常退出
3.3 AI节点实现
AiChatNodeHandler的核心处理逻辑:
java复制public void handle(AiChatNode node, WorkflowContext context) {
// 1. 解析模板
String finalPrompt = ElExpressionUtil.parseExpression(node.getPromptTemplate(), context);
// 2. 调用AI
String response = chatClient.call(new Prompt(finalPrompt))
.getResult().getOutput().getContent();
// 3. 存储结果
context.setNodeOutput(node.getNodeId(), response);
if (node.getOutputKey() != null) {
context.setGlobalParam(node.getOutputKey(), response);
}
}
4. 典型应用场景实现
4.1 请假审批流程示例
java复制// 节点1:AI分析
AiChatNode node1 = new AiChatNode();
node1.setNodeId("node_1");
node1.setPromptTemplate("分析请假申请合理性:${global.user_input}");
node1.setOutputKey("ai_analysis_result");
// 节点2:条件分支
ConditionNode node2 = new ConditionNode();
node2.setConditionExpression("${global.ai_analysis_result} contains '合理'");
Map<String, String> branches = new HashMap<>();
branches.put("true", "node_3"); // 通过分支
branches.put("false", "node_4"); // 拒绝分支
node2.setBranchConfig(branches);
// 节点3/4:生成审批意见
AiChatNode node3 = new AiChatNode();
node3.setPromptTemplate("生成通过意见:${global.user_input}");
AiChatNode node4 = new AiChatNode();
node4.setPromptTemplate("生成拒绝意见:${global.user_input}");
// 节点5:结束
EndNode node5 = new EndNode();
node5.setEndMessage("流程结束");
4.2 客户服务场景
可扩展实现:
- 意图识别节点
- 知识库查询节点
- 多轮对话管理节点
- 满意度评价节点
5. 部署与运行指南
5.1 环境准备
必要条件:
- JDK 1.8+
- Maven 3.6+
- OpenAI API Key
5.2 启动配置
application.yml关键配置:
yaml复制spring:
ai:
openai:
api-key: sk-your-key-here
chat:
options:
model: gpt-3.5-turbo
temperature: 0.2
5.3 运行与测试
启动命令:
bash复制mvn spring-boot:run
访问入口:
- 流程测试页:http://localhost:8080/dify-workflow/index
- 编排设计器:http://localhost:8080/dify-workflow/editor
6. 扩展与优化方向
6.1 性能优化建议
- 上下文缓存:对频繁访问的参数增加缓存层
- 批量AI调用:合并多个节点的AI请求
- 异步执行:非依赖节点并行处理
6.2 功能扩展思路
-
新增节点类型:
- 数据库查询节点
- HTTP调用节点
- 代码执行节点
-
增强特性:
- 版本管理
- 权限控制
- 执行历史追溯
-
可视化增强:
- 连线编辑
- 节点属性面板
- 实时调试
7. 常见问题排查
7.1 表达式解析失败
症状:${node_1.output}未被替换
检查:
- 节点ID是否正确
- 上游节点是否已设置输出
- 表达式语法是否正确
7.2 AI节点无响应
排查步骤:
- 检查API Key配置
- 查看网络连接
- 打印最终prompt确认
- 测试直接调用ChatClient
7.3 流程循环执行
预防措施:
- 增加执行步数限制
- 检测环状引用
- 设置超时机制
8. 开发实践建议
-
节点设计原则:
- 单一职责:每个节点只做一件事
- 明确接口:定义清晰的输入输出
- 无状态:节点自身不保存业务数据
-
测试策略:
java复制@Test void testConditionNode() { ConditionNode node = new ConditionNode(); node.setConditionExpression("${global.value} > 10"); WorkflowContext context = new WorkflowContext(); context.setGlobalParam("value", 15); String nextNode = handler.handle(node, context); assertEquals("true_branch", nextNode); } -
调试技巧:
- 开启DEBUG日志
- 使用Mock AI响应
- 可视化上下文变化
这个实现完整展示了如何基于Spring AI构建企业级AI工作流系统,开发者可以在此基础上根据实际业务需求进行深度定制。核心价值在于将AI能力转化为可编排的业务组件,大幅提升复杂AI应用的开发效率。