前面的博客有讲到Dify工作流调接口查询数据以及调用Dify的聊天助手API,此博客需具备以上基础。
背景:本人就职于某IT企业,当前公司正推进企业级AI应用的建设。基于微服务架构的业务复杂度较高,在技术选型中我们采用Dify工作流来实现核心需求。该方案的实现流程与此博客大体一致,可满足企业级应用的技术要求。
步骤一:启动Dify
目前版本:1.2.0
步骤二:搭建工作流
搭建步骤已省略,核心内容在下方。Dify的工作流具备很多组件,大家可以根据需求灵活使用,此次演示为Demo,真实场景比这个要复杂哈。
步骤三:接口测试
由于在工作流中使用http请求保存数据,需要使用postman验证该接口是否可用。
还需要去数据库查看一下
数据保存成功,进入下一环节
步骤四:发布工作流
在发布前,需要运行或调试工作流是否正常。遇到问题不要慌,解决问题就是,大家可以加入技术群或个人微信交流~
步骤五:对接工作流
有些人就会问,Dify不是都实现了吗,为啥还要编写java代码去对接了,前端不是能对接python接口吗?
回答:基于业务需求,没错,就这么简单!
自己也不确定,特意借助DeepSeek查了下,支持国产!
真是良心,回答的非常详细。
WorkFlowController
@RestController
@RequestMapping("/workflow")
public class WorkFlowController {
@Autowired
private WorkFlowService workFlowService;
/**
* 上传文件
*
* @param file
* @return
* @throws IOException
*/
@PostMapping("/upload")
public WorkFlowFileVo upload(@RequestParam("file") MultipartFile file) throws IOException {
return workFlowService.upload(file);
}
/**
* 执行工作流
*
* @param workFlowRunDto
* @return
*/
@PostMapping("/runWorkFlow")
public SseEmitter runWorkFlow(@RequestBody WorkFlowRunDto workFlowRunDto) {
return workFlowService.runWorkFlow(workFlowRunDto);
}
/**
* 获取工作流执行情况
*
* @param workflowRunId
* @return
*/
@GetMapping("/workFlowInfo")
public WorkFlowExeVo workFlowRunInfo(String workflowRunId) {
return workFlowService.workFlowRunInfo(workflowRunId);
}
}
WorkFlowService
public interface WorkFlowService {
/**
* 上传文件
*
* @param file
* @return
*/
public WorkFlowFileVo upload(@RequestParam("file") MultipartFile file) throws IOException;
/**
* 执行工作流
*
* @param workFlowRunDto
* @return
*/
public SseEmitter runWorkFlow(@RequestBody WorkFlowRunDto workFlowRunDto);
/**
* 获取工作流执行情况
*
* @param workflowRunId
* @return
*/
public WorkFlowExeVo workFlowRunInfo(String workflowRunId);
}
WorkFlowServiceImpl
/**
* 上传文件
*
* @param file
* @return
* @throws IOException
*/
@Override
public WorkFlowFileVo upload(MultipartFile file) throws IOException {
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set("Authorization", difyConfig.getSaveDataAuthorization());
// 创建请求实体
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
// 处理文件内容
body.add("file", new ByteArrayResource(file.getBytes()) {
@Override
public String getFilename() {
// 返回原始文件名
return file.getOriginalFilename();
}
});
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String uploadUrl = difyConfig.getSaveDataUrl() + "/files/upload";
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(uploadUrl, HttpMethod.POST, requestEntity, String.class);
log.info("上传文件的response: {}", response);
WorkFlowFileVo workFlowFileVo = JSON.parseObject(response.getBody(), WorkFlowFileVo.class);
// 构建工作流请求体
WorkFlowRunDto workFlowRunDto = buildWorkFlowRunDto(workFlowFileVo.getId());
// 执行工作流
this.runWorkFlow(workFlowRunDto);
}
/**
* 执行工作流
*
* @param workFlowRunDto
* @return
*/
@Override
public SseEmitter runWorkFlow(WorkFlowRunDto workFlowRunDto) {
// 设置超时时间 300s
SseEmitter emitter = new SseEmitter(300_000L);
// 使用异步线程处理流式响应
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
try {
String runUrl = difyConfig.getSaveDataUrl() + "/workflows/run";
log.info("runUrl: {}", runUrl);
// 创建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", difyConfig.getSaveDataAuthorization());
headers.setContentType(MediaType.APPLICATION_JSON);
// 设置接收流
headers.set(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE);
HttpEntity<WorkFlowRunDto> requestEntity = new HttpEntity<>(workFlowRunDto, headers);
// 使用restTemplate的execute方法处理流式响应
restTemplate.execute(
runUrl,
HttpMethod.POST,
request -> {
// 关键修复:手动设置Content-Type和请求头
request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
request.getHeaders().addAll(requestEntity.getHeaders());
// 写入请求体
if (requestEntity.getBody() != null) {
new ObjectMapper().writeValue(request.getBody(), requestEntity.getBody());
}
},
response -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.getBody()))) {
// 标记是否已处理workflow_run_id
boolean workflowRunIdProcessed = false;
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("event: ping")) {
// 跳过心跳事件
continue;
}
emitter.send(line);
log.info("line: {}", line);
if (!workflowRunIdProcessed) {
try {
workflowRunIdProcessed = processLine(line);
} catch (Exception e) {
log.error("记录id出现异常: {}", e.getMessage());
emitter.completeWithError(e);
}
}
}
}
emitter.complete();
return null;
}
);
} catch (Exception e) {
log.error("处理过程中发生错误: {}", e.getMessage());
emitter.completeWithError(e);
} finally {
log.info("流式输出结束...");
}
});
// 关闭线程池
executor.shutdown();
log.info("流式输出完成...");
return emitter;
}
@Override
public WorkFlowExeVo workFlowRunInfo(String workflowRunId) {
log.info("获取到的工作流id: {}", workflowRunId);
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", difyConfig.getWorkFlowAuthorization());
String workFlowInfoUrl = difyConfig.getWorkFlowUrl() + "/workflows/run/" + workflowRunId;
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(workFlowInfoUrl, HttpMethod.GET, requestEntity, String.class);
log.info("response: {}", response);
return JSON.parseObject(response.getBody(), WorkFlowExeVo.class);
}
步骤六:测试
有小伙伴就纳闷了,公司不是有测试吗,怎么还要自己测啊?
回答:招你进来不是闲着玩的
个人:都是牛马
测试文件上传,由于Demo版代码执行文件与工作流执行在一块,如果基于业务需求,中间会进行其他操作。
控制台信息:
建议大家在开发过程中规范记录日志,以便在系统出现异常时能快速定位问题根源。日志的详细程度可根据实际需求和开发经验灵活调整。
基于上述流程方案,我们已顺利完成了企业级应用需求的开发。
本人正在打造技术交流群,欢迎志同道合的朋友一起探讨,一起努力,通过自己的努力,在技术岗位这条道路上走得更远。QQ群号:914683950 备注:技术交流 即可通过!