ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Dify 高级实验(05):多步推理——如何把复杂问题拆解成子问题逐个击破

Dify 高级实验(05):多步推理——如何把复杂问题拆解成子问题逐个击破

Dify 高级实验(05):多步推理——如何把复杂问题拆解成子问题逐个击破?

Dify 实验系列 · 高级 05/10 | 实验编号:DIFY-103-05
基于 Dify 1.16.1 实测(2026-08)

1. 业务场景

先讲一个我们实际遇到的场景。

一位用户在产品社区里问:「我该买 iPhone 还是华为?」这个看似简单的问题,直接问 LLM 往往得到一段含糊其辞的「各有优劣,看你的需求」——因为「买哪个」背后其实是一串子问题:价格差多少?性能谁强?续航谁久?生态谁全?最后还要结合用户自己的偏好做判断。一步回答,模型要么信息不全,要么夹带私货,用户看完还是不知道买哪个。

我们第一次遇到这类问题时,第一反应也是「让模型多思考一会儿不就行了」。后来才发现——问题不在思考时长,而在上下文:模型在同一个上下文里既要回忆事实、又要对比、还要下判断,互相干扰,一步回答天生不可靠。把问题拆开、逐个击破、最后综合,才是人类分析问题的方式,也是 LLM 该有的方式。

这不是个例。任何「一步答不准」的问题都是这个模式:产品对比(A 和 B 选哪个)、行业分析(新能源车会不会取代燃油车)、研究型问答(这个技术方案值不值得采用)——问题越复杂,一步到位的回答越不可靠,因为模型在同一个上下文里既要回忆事实、又要对比、还要下判断,互相干扰。

2. 场景痛点

这个流程的痛点,在问问题的人身上体现得最直接:

  • 一步回答信息不全:模型一次回答只能覆盖部分维度——对比手机只聊了价格没聊续航,用户拿到的结论是残缺的,决策依据不足。
  • 不可解释:模型直接给结论「推荐华为」,但没说清楚基于哪些维度、每个维度怎么比较的——用户无法判断这个推荐是否适合自己,也不敢信。
  • 复杂问题答不准:事实、对比、判断混在一个回答里,模型容易顾此失彼——说了价格忘了性能,说了优点不提缺点,回答质量随问题复杂度直线下降。
  • 无法定位错误:回答错了,用户不知道错在哪一步——是事实记错了,还是对比维度漏了,还是判断逻辑有问题,完全无法追溯。

本质上,一步回答的瓶颈不是模型不够聪明,而是「把复杂问题当简单问题处理」——复杂问题需要先拆解、再逐个击破、最后综合,这是人类分析问题的方式,也是 LLM 该有的方式。

3. 方案:为什么是这套多步推理范式

Dify 工作流里的「拆解 → 迭代推理 → 综合」链路,把「一步回答」变成「先拆后答」,正好复刻人类分析问题的过程。

选它的理由,我们实际对比过:

  • 拆解让每个子问题都可独立求解:LLM 把复杂问题拆成 3-5 个可独立检索或推理的子问题,每个子问题单独回答——单点准确率远高于一次答全;
  • 迭代节点逐个击破:Dify 原生迭代节点对每个子问题单独跑一轮 LLM,子问题之间不互相污染,答案可逐条核对;
  • 综合阶段显式标注缺口:子回答出现「不确定」时,汇总代码检测出来,最终答案显式标注信息缺口——不掩盖不确定性,用户知道哪些结论可靠、哪些要核实。

这篇文章我们就用它搭一个「多步推理问答助手」:复杂问题先拆成子问题逐个推理,再综合成结构化回答。

4. 整体架构

开始:user_query / query_subject

LLM 问题拆解:输出子问题 JSON

Code 准备迭代:解析 JSON → items 数组

迭代「逐子问题分析」:itstart0 → LLM 子问题回答

Code 汇总子回答:拼接 + 检测「不确定」缺口

LLM 综合回答:引用分步结果,标注缺口

结束

链路很清晰:入口收复杂问题 → 拆解成子问题 → 逐个子问题推理 → 汇总检测缺口 → 综合回答。拆解和迭代是这个架构的核心——拆得好,每个子问题都能独立求解;迭代让子问题逐个处理,互不干扰。

5. 模块设计

5.1 拆解 LLM(lm_decompose)

系统提示词限定输出纯 JSON,并要求 3-5 个子问题、先事实后判断:

你是一个问题拆解专家。将用户的问题拆解成 3-5 个独立的子问题。 用户问题:{{#start.user_query#}} 拆解规则: 1. 每个子问题必须是可独立检索或推理的 2. 子问题之间不要重叠 3. 按逻辑顺序排列(先事实后判断) 输出格式(纯 JSON,不要 Markdown): {"sub_questions": [{"id": 1, "question": "xxx", "type": "fact/compare/judge"}], "reasoning_path": "从基本信息到综合判断的逻辑链描述"}

5.2 准备迭代代码(cd_prepare)

解析拆解结果并兜底<think>剥离 →json.loads→ 失败则按行正则提取子问题,最后items[:5]截断(防迭代 30 元素上限):

defmain(sub_questions_json,query_subject):importjson,re text=sub_questions_jsonifisinstance(sub_questions_json,str)elsestr(sub_questions_json)text=re.sub(r'<think>.*?</think>','',text,flags=re.DOTALL).strip()m=re.search(r'\{[^}]',text)ifm:text=text[m.start():]try:questions=json.loads(text)items=questions.get("sub_questions",[])ifisinstance(items,list)andlen(items)>0:return{"items":items,"total":len(items),"reasoning_path":questions.get("reasoning_path","")}except:passlines=text.split('\n')items=[]forlninlines:m2=re.match(r'^\d+[\.\、\,]\s*(.+)$',ln.strip())ifm2andlen(m2.group(1).strip())>5:items.append({"id":len(items)+1,"question":m2.group(1).strip(),"type":"fact"})return{"items":items[:5],"total":min(len(items),5),"reasoning_path":""}

5.3 迭代节点(it)

三件套缺一不可:iterator_selector指向 items 数组、output_selector只选可见类型(string)、start_node_id与迭代起始标记 id 一致:

-data:desc:''iterator_selector:[cd_prepare,items]output_selector:[lm_sub,text]start_node_id:itstart0title:逐子问题分析type:iteration

5.4 子问题 LLM(lm_sub)

迭代内部 LLM 引用{{#it.index#}}(序号)与{{#it.item.子字段#}},这是迭代标准机制;必须配reasoning_format: separated

你是一个专题分析师。请回答以下子问题。 主题:{{#start.query_subject#}} 子问题(第{{#it.index#}}个):{{#it.item.question#}} 问题类型:{{#it.item.type#}} 回答要求: 1. fact 类型基于知识回答;compare 列出对比维度;judge 给出判断理由 2. 控制在 100 字以内 3. 如不确定,明确说"不确定" 4. 直接给出回答,不要重复问题

5.5 汇总代码(cd_summary)

逐条拼接 Q/A,并检测信息缺口——has_information_gaps展平为字符串(boolean 在变量选择器不可见):

defmain(sub_results,original_query,reasoning_path):lines=[]has_gaps=Falseforitemin(sub_resultsifisinstance(sub_results,list)else[]):q=item.get("question","?")ifisinstance(item,dict)else"?"a=item.get("answer",item.get("text","未回答"))ifisinstance(item,dict)elsestr(item)lines.append(f"Q:{q}\nA:{a}\n")if"不确定"ina:has_gaps=Truereturn{"intermediate_results":"\n".join(lines),"has_information_gaps":"true"ifhas_gapselse"false","gap_note":"部分信息未确认,建议核实"ifhas_gapselse"信息完整"}

5.6 综合 LLM(lm_final)

引用{{#cd_summary.intermediate_results#}}{{#cd_prepare.reasoning_path#}},要求先给直接答案、再引关键论据、最后按缺口标注。

6. 运行验证

输入期望行为实测
user_query:我该买 iPhone 还是华为?query_subject:手机对比拆解出 5 个子问题(价格/性能/续航/生态/推荐),逐个回答后给出综合对比与推荐与预期一致,回答结构化、含缺口标注逻辑
user_query:AI 会不会取代程序员?query_subject:职业分析拆解出「AI 能力现状/程序员工作内容/替代分析/趋势」等子问题,输出行业分析与预期一致
构造一个让子回答出现「不确定」的输入gap_note= 部分信息未确认,建议核实,最终回答末尾标注与预期一致(信息缺口检测生效)

7. 实战坑

现象修复
迭代内部 LLM 缺reasoning_format: separated思考过程混入text污染整个迭代输出数组cd_summary"不确定" in a把思考中的「不确定」误判为信息缺口迭代内 LLM 显式加reasoning_format: separated(dify103_05 实测)
迭代五件套缺失或yaml.dump()重写内部节点 UI 叠在一起 /iteration_idparentIdisInIterationsourcePositiontargetPosition被静默销毁,校验报错内部节点补全五件套;修改用patch精确定位,不用 yaml round-trip
prompt 变量用{{变量名}}双花括号1.16 不替换双花括号,字面传给模型,回答「参考资料为空」LLM prompt 一律{{#节点id.字段#}}三花括号(dify102_12 实测)
拆解出过多子问题迭代运行报then length of var "item" must be less than 30 elements拆解 prompt 限 3-5 个 +cd_prepareitems[:5]截断留余量(dify103_09 实测)

8. 实验文档及源码获取

  • 实验文档(完整操作步骤):DIFY-103-05:多步推理工作流.md
  • 源码(可直接导入):dify103_05_多步推理工作流.yml

文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。


下一篇:Dify 高级实验(06):自动化报告——如何让系统定时自动生成周报?

💬 你在这个实验的场景里踩过什么坑?欢迎评论区分享你的实战经验。

返回列表