内容纲要

dify工作流实战(一)

材料信息识别及持久化的可行性验证

引言

在数字化转型升级的浪潮中,如何高效、准确地处理大量纸质证件信息成为企业面临的重要挑战。本文将通过Dify工作流与Spring Boot的深度集成,展示如何构建一个智能化的学位证信息提取系统,为后续扩展至全流程入职材料自动化处理奠定坚实基础。

系统组成

  • Dify工作流: 负责学位证图像的OCR识别和信息提取
  • Spring Boot应用: 提供RESTful API接口和业务逻辑处理,通过调用Dify工作流完成传入材料的识别和数据持久化工作。未来会扩展为支持多文件类型的识别,以及审批流相关业务的处理。
  • MySQL数据库: 存储提取后的结构化学位证信息

业务价值

效率提升
  • 处理时间从分钟级降低到秒级
  • 减少人工操作错误率
成本优化
  • 大幅降低人力成本
  • 减少纸质材料存储成本
风险控制
  • 标准化审核流程
  • 完整的操作日志记录
  • 可追溯的数据审计轨迹

为什么选择Dify?

  • 低代码开发: 可视化工作流设计,快速迭代AI能力
  • 多模型支持: 无缝集成多种OCR和NLP模型
  • 灵活部署: 支持云端和本地化部署
  • 生态丰富: 丰富的预构建节点和模板

Dify工作流配置与发布

虽然Dify支持本地部署,但云端同样提供功能。对于该实战场景下,云端Dify提供的功能已经足够,所以我们的工作流部署与调用皆是基于云端Dify。

云端Dify地址:https://cloud.dify.ai/

大模型API设置

该实践使用了阿里的通义千问模型作为LLM,需要在【设置 -> 模型供应商】中按照引导进行API key配置:

工作流配置

  1. 创建工作流

  1. 配置工作流节点

对于识别图片中的证书信息,需要配置如下3个节点:

  • 开始:提供输入的文件
  • LLM:包含视觉功能,用于识别图片中的信息,并按照prompt提取指定的数据
  • 结束:输出结果内容
  1. 各节点配置
  • 开始

    • 插入变量:定义文件作为输入变量

  • 下一个节点:将该节点与LLM节点相连,则该节点的输出为LLM节点的输入

  • LLM

    • 选择模型:选择模型时需要选择有视觉功能的大模型

  • 上下文选择:选择开始节点的输出字段【files】作为模型的上下文

  • 添加prompt:消息分为SYSTEM和USER。在user的prompt中使用了上下文的字段files,指示大模型将files中的文件提取出关键信息。

  • 开启视觉:开启视觉并传入文件,LLM才会识别图片中的信息

  • 下一个节点:该节点的下一个节点为结束

  • 结束

    • 输出变量:将LLM的输出text作为结束节点的输出变量

工作流发布

若需要用API调用工作流,则需要将该工作流发布,包括:发布为工具、发布更新操作。

spring集成Dify工作流

详细代码参考git:https://github.com/lemon-piepie/entry-material-ocr

项目依赖

implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'mysql:mysql-connector-java'

Dify外部服务

对于使用了文件的工作流来说,需要调用Dify的2个接口:

  1. 上传文件 /files/upload
  2. 执行workflow /workflows/run
@Component
public class DifyClient {

    @Value("${dify.api.key}")
    private String apiKey;

    @Value("${dify.api.base-url}")
    private String baseUrl;

    private final WebClient webClient = WebClient.create();

    public Mono<String> uploadFile(MultipartFile file) {
        MultipartBodyBuilder builder = new MultipartBodyBuilder();
        builder.part("file", file.getResource());

        return webClient
                .post()
                .uri(baseUrl + "/files/upload")
                .header("Authorization", "Bearer " + apiKey)
                .contentType(MediaType.MULTIPART_FORM_DATA)
                .body(BodyInserters.fromMultipartData(builder.build()))
                .retrieve()
                .bodyToMono(String.class);
    }

    public Mono<String> runWorkflow(Map<String, Object> requestBody) {
        return webClient
                .post()
                .uri(baseUrl + "/workflows/run")
                .header("Authorization", "Bearer " + apiKey)
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(requestBody)
                .retrieve()
                .bodyToMono(String.class);
    }
}

业务服务层

@Service
@Slf4j
@RequiredArgsConstructor
public class WorkflowService {

    private final DifyClient difyClient;

    public Mono<DegreeDTO> executeOcrWorkflow(MultipartFile file) {
        return executeWorkflow(file)
                .flatMap(this::parseAndMapToDegreeDTO);
    }

    private Mono<String> executeWorkflow(MultipartFile file) {
        return difyClient.uploadFile(file)
                .flatMap(fileResponse -> {
                    try {
                        ObjectMapper mapper = new ObjectMapper();
                        JsonNode fileJson = mapper.readTree(fileResponse);
                        String fileId = fileJson.get("id").asText();

                        Map<String, Object> requestBody = new HashMap<>();
                        Map<String, Object> inputs = new HashMap<>();
                        Map<String, Object> fileInput = new HashMap<>();
                        fileInput.put("type", "image");
                        fileInput.put("transfer_method", "local_file");
                        fileInput.put("upload_file_id", fileId);
                        inputs.put("files", fileInput);
                        requestBody.put("inputs", inputs);
                        requestBody.put("response_mode", "blocking");
                        requestBody.put("user", "abc-123");

                        return difyClient.runWorkflow(requestBody);
                    } catch (Exception e) {
                        log.error("Error executing workflow: " + e.getMessage());
                        return Mono.error(e);
                    }
                });
    }
}

控制层Controller

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/dify")
public class OcrController {
    private final WorkflowService workflowService;

    @PostMapping("/workflow/ocr")
    public Mono<DegreeDTO> runWorkflow(@RequestPart("file") MultipartFile file) {
        return workflowService.executeOcrWorkflow(file);
    }
}

API调用测试

未来技术演进路径

第一阶段:学位证信息提取(当前)

  • 实现单一证件类型的识别流水线
  • 建立基础的数据模型和API接口
  • 验证技术方案的可行性和准确性

第二阶段:多证件类型扩展

  • 扩展dify工作流,支持更多材料的信息提取
  • 扩展springboot业务代码,支持多种类型的材料识别和持久化
// 证件类型枚举,支持扩展
public enum CertificateType {
    ID_CARD("身份证"),
    DEGREE_CERTIFICATE("学位证"), 
    GRADUATION_CERTIFICATE("毕业证"),
    // 更多可继续扩展类型
    ;

    private final String description;
}

第三阶段:智能审核工作流

  • 自动化合规性检查
  • 风险识别与预警机制

第四阶段:全流程自动化

  • 与现有HR系统深度集成
  • 自动化入职流程触发
  • 智能化数据分析和报表
最后修改日期: 2025年9月21日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。