Dify 中级实验(01):参数提取器实战——如何从自然语言中提取结构化数据?
1. 业务场景
先讲一个我们实际遇到的场景。
一家做招聘服务的公司,每天要处理几十份简历。HR 收到的简历格式五花八门——有人写「5 年经验,精通 Python」,有人写「Python 五年,Java 熟练」,还有人的简历是纯叙述段落,技能、年限、薪资期望全混在一段话里。他们的流程是:人工把简历里的关键信息(姓名、年龄、技能、经验年限、期望薪资、学历)逐项录入到筛选系统,再由系统按岗位要求做初筛。
我们第一次接这类需求时,第一反应也是「是不是得上个 NLP 解析库」。后来翻 Dify 的节点列表才发现:平台原生的 Parameter Extractor(参数提取器) 就是干这个的——LLM 从自然语言里提取结构化字段,直接输出 JSON,连解析代码都不用写。
这不是个例。任何「用户说人话、系统要结构化数据」的业务场景都是这个模式:工单系统里用户用一句话描述故障,客服要手动拆出设备型号和故障现象;CRM 里销售把客户需求复制进来,运营要手动提取预算和期限;搜索系统里用户随口问「有没有便宜点的 4K 显示器」,后端需要的是结构化的「屏幕尺寸、分辨率、预算上限」。
2. 场景痛点
这个流程的痛点,在招聘公司的 HR 身上体现得最直接:
- 手工录入慢:一份简历从读完到录入完,平均 5-10 分钟,几十份简历就是半天——录入动作本身不产生任何价值,却是每天必须做的。
- 格式不统一导致解析错:「5 年经验」「五年经验」「5 年+」说的是同一件事,人工录入时口径全凭手感,系统里同一字段出现三种写法,后续筛选和统计全部失真。
- 薪资等数值字段没法直接用:简历写「期望薪资 25K-30K」,系统要的是数字(比如 27500),人工转换既慢又容易错。
- 漏字段靠人工补:一份简历漏了学历,HR 要么放下手头的事去翻原文,要么直接跳过——筛选用「学历」过滤时,这份简历就无声无息地消失了。
本质上,最没有技术含量、却最费人力的环节,恰恰是「把自然语言变成结构化数据」这一步。模型负责理解,规则负责确定性——而这一步的确定性,不该靠人工。
3. 方案:为什么是参数提取器
选参数提取器的理由,我们实际对比过:
- 平台原生能力:不用自己写 NLP
解析,节点配置好字段定义即可,LLM 理解语义(「5
年经验」和「五年」都能提取成
5); - 数值归一化:期望薪资这种字段,声明成 number 类型,LLM 会自动把「25K-30K」转成统一数值(取 27500)——人工最容易错的一步被自动化;
- 可校验可兜底:提取结果可以接代码节点做完整性校验,漏字段时显式提示补充,而不是带着残缺数据往下走。
这篇文章我们就用它搭一个「简历筛选助手」:用户用自然语言描述候选人,系统提取出 6 个结构化字段,再根据字段做筛选判断。
4. 整体架构
链路很清晰:入口收自然语言 → 提取结构化 → 校验完整性 → 分支处理。校验和分支是这个架构的关键——参数提取偶尔漏字段是 LLM 节点的常态,不校验直接往下走,筛选意见就会基于残缺数据生成。
5. 模块设计
5.1 开始节点
resume_text
变量类型必须选「段落」(paragraph)而不是「文本框」(text-input)——简历描述是长文本,text-input
有 48 字符输入限制,粘贴一段简历直接报错:
- label: 简历描述
max_length: 10000
required: true
type: paragraph
variable: resume_text💡 这是同批实验里踩过的真实坑:长文本/JSON 粘贴类变量用 paragraph + 显式
max_length: 10000,否则 Service API 报must be less than 48 characters。
5.2 参数提取节点(核心)
配置 6 个提取参数:
query: ['start', 'resume_text']
parameters:
- name: name # 候选人姓名
type: string
required: true
- name: age # 候选人年龄
type: number
required: false
- name: skills # 技能列表
type: array[string]
required: true
- name: experience_years # 工作经验(年)
type: number
required: true
- name: expected_salary # 期望薪资(元/月)
type: number
required: false
- name: education # 最高学历
type: string
required: false要点:
- 必填字段给足描述——参数描述直接影响 LLM 提取准确率(描述含糊 → 提取错)
- 金额类(期望薪资)用 number,让 LLM 把「25K-30K」归一为数值(取 27500)
5.3 校验节点(Code)
参数提取偶尔会漏字段,用代码节点做完整性校验,返回 valid
标志 + 缺失项提示:
def main(name: str, age: str, skills: list, experience_years: str, expected_salary: str, education: str) -> dict:
missing = []
if not name: missing.append("姓名")
if not skills: missing.append("技能")
if not experience_years: missing.append("工作经验")
return {
"valid": "false" if missing else "true",
"missing_fields": "、".join(missing) if missing else "",
"summary": f"{name},{experience_years}年经验,技能:{', '.join(skills or [])}"
}💡 boolean 必须展平为 string(
"true"/"false")——Dify 的 object/boolean 类型在节点间不可见,IF-ELSE 判断的是字符串。
6. 运行验证
输入上面的简历描述,预期:
- 参数提取返回 6 个结构化字段(skills 是数组)
- 校验节点
valid=true - LLM 生成筛选意见(结合经验年限/技能与岗位匹配度)
实测结果:完整简历 → 提取 6 字段全中 →
筛选意见输出正常;缺技能描述的简历 → valid=false →
走提示分支。
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| start 变量用 text-input | 长简历粘贴报「must be less than 48 characters」 | 改 paragraph +
max_length: 10000 |
| 校验结果 boolean 直出 | IF-ELSE 判断不到(类型不可见) | 展平为 string "true"/"false" |
| PE 提取字段描述含糊 | LLM 提取错/漏字段 | 每个字段写清楚业务描述 |
8. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-02:参数提取——从非结构化到结构化.md
- 源码(可直接导入):dify102_02_简历参数提取器.yml
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
- Dify 中级实验(01):参数提取器实战——如何从自然语言中提取结构化数据?
- Dify 中级实验(02):问题分类器——智能路由引擎如何四路分发?
- Dify 中级实验(03):模板转换实战——如何用零 Token 完成文本加工?
- Dify 中级实验(04):迭代进阶——如何批量处理数据并守住性能边界?
- Dify 中级实验(05):并行执行——如何让多路任务同时跑?
- Dify 中级实验(06):变量聚合——如何确定性合并多路分支结果?
- Dify 中级实验(07):子工作流——如何把公共逻辑做成可复用积木?
- Dify 中级实验(08):代码节点进阶——如何用标准库处理文件与数据?
- Dify 中级实验(09):HTTP 节点进阶——如何搞定认证、分页与错误重试?
- Dify 中级实验(10):知识库深度调优——如何科学评估检索质量?
- Dify 中级实验(11):高级 RAG 流水线——如何搭建多路检索与精排?
- Dify 中级实验(12):Agent 深度配置——如何让智能体自主调用工具?
- Dify 中级实验(13):多 Agent 协作——如何编排多个智能体分工干活?
- Dify 中级实验(14):对话变量与状态管理——如何让工作流记住多轮对话的状态?
- Dify 中级实验(15):条件分支高阶策略——多条件路由如何避免分支爆炸?
- Dify 中级实验(16):错误处理与降级——工作流如何有尊严地失败?
- Dify 中级实验(17):调试监控与性能优化——响应慢和 Token 超支如何定位?
- Dify 中级实验(18):插件开发入门——如何把工作流变成 Agent 可调用的工具?
- Dify 中级实验(19):综合实战——如何把 19 个实验串成一条生产级流水线?
- Dify 中级实验(20):综合实战——自动化报告生成流水线如何从数据到周报一步到位?