>_ AI 应用训练营
lang ~ ai-course/08-intent-classification-node 4 min read

08 · 搭建意图判断节点

为什么要做意图识别、三条路线、用「问题分类器」节点搭建三分类

8.1 为什么要做意图识别

核心原因:不同类型的问题,需要走不同的知识库、需要不同的回答语气,混在一起处理效果会打折扣。

想一下如果不做意图识别会怎样:每个问题都同时搜商品库+生活库,再用一个通用Prompt去回答所有类型的问题。这样会带来两个问题:

检索精度打折——检索范围没有先收窄,无关类别的内容更容易
    混进候选,稀释掉真正相关的结果

Prompt没法针对性优化——导购类问题需要"报价格、报规格、
    列全所有相关商品",知识类问题需要"讲挑选/保存方法、
    语气偏科普",两种诉求写进同一个Prompt里,
    容易顾此失彼,谁都做不精

这正是这个项目最后长成"三条独立分支、三个独立生成回答节点、各自配独立Prompt"这个样子的根本原因——本章和第9章先分类分流,第10章才能各自去查更精准的库,第11章才能各自用更贴合场景的Prompt。意图识别是这一整套"分流架构"最开始的那个决策点,没有它,后面这些"分开配置"都无从谈起。

8.2 判断意图的三条路线

规则/关键词:列好每类意图的关键词,命中就归类。
    简单可控,但用户换种说法就漏检,需要人工持续维护

机器学习(BERT分类):用标注数据微调一个分类器,
    对同义表达更鲁棒,但要标注数据、要训练

LLM Prompt:把意图选项写进prompt让大模型判断,
    零样本就能用,但有调用成本、有延迟——本章走的就是这条路

业内行话: 实务里常组合用——冷启动期先用LLM粗分类(快、不用标数据),等线上攒够真实问题和标注,再训一个BERT分类器接管(更稳、更便宜、延迟更低)。教学项目只需要知道这个思路,用不上真的去训练模型。

8.3 搭建:用Dify的"问题分类器"节点

Dify里做意图分类,不是拿一个普通LLM节点自己手写判断逻辑,而是用一个专门的节点类型——「问题分类器」

  1. 进入Chatflow应用画布,在第7章搭好的"Query改写"节点右侧点「+」

  2. 添加「问题分类器」节点(不是「LLM」节点),改名为"意图判断"

  3. 模型选择 deepseek-flash(跟其他节点保持一致)

  4. **输入变量:选{{#Query改写.text#}},不要用原始的{{#开始.query#}}**——这个节点判断用的内容必须是改写后的问题。如果这里漏改、还接的是原始输入,那第7章新加的Query改写节点等于白搭,"有货吗"这种省略句,意图判断依然只能瞎猜

  5. 配置「分类」——这里是三张卡片,每张卡片一个类别名+一段描述:

    导购:导购类:用户想了解购买咨询具体商品
    知识:知识类:用户想了解知识、挑选方法、保存方法等
    模糊:同时涉及商品和知识,无法明确分类的
    
  6. 展开「高级设置」,配置「指令」——这里写具体的例句,不是抽象定义:

    导购类例子:有什么鱼推荐?鲈鱼多少钱?
    知识类例子:榴莲怎么看好坏?西瓜如何保存?
    模糊类例子:榴莲怎么看,有什么推荐吗?家里有一条鱼,
               怎么做合适,需要搭配些什么菜?
    
  7. 「记忆」开关:开不开都可以——因为这个节点吃进去的已经是Query改写处理过的完整问题(省略、指代都已经被补全了),意图判断本身不强依赖历史。开着也没坏处,不开也不影响分类效果。

8.4 这几个字段,分别对应"写好意图识别Prompt"的四条原则

写一个能稳定输出的意图识别Prompt,业内公认要做到四件事——刚好都能在上面这几个字段里找到对应:

枚举候选:把所有意图类别明明白白列出来
    → 对应"分类"这三张卡片。模型只能在这三个选项里选,
      不存在第四个选项

强制输出格式:只要类别名,不要让模型自由发挥、附加解释
    → 这一条是"问题分类器"这个专用节点类型自带的约束,
      不用你手写"只输出类别名"这种话去强调——
      这也是为什么用专用节点比用普通LLM节点自己写Prompt更稳:
      有些约束平台已经帮你保证了

few-shot示例:给几个"问题→类别"的真实范例
    → 对应「高级设置→指令」这个框。"分类"卡片里写的是
      抽象定义("用户想了解购买咨询具体商品"),
      "指令"里写的是具体范例(问题实际读起来是什么样)——
      两者分工不同:定义告诉模型"这个类别在概念上是什么",
      范例告诉模型"这个类别的问题长什么样"

留一个兜底类:避免模型硬把不相关问题塞进某一类
    → 对应"模糊"这张卡片。它存在的意义,就是给
      "两边都不像、两边都有点像"的问题一个去处,
      不让模型被迫在"导购"和"知识"里硬选一个不完全对的答案

8.5 进阶:意图太多怎么办(300-500个意图,企业应用需要了解的知识)

先纠正一个容易理解偏的地方:这里说的"意图太多",不是"一句话里混了好几个意图"(那是"模糊"这一类兜底类在处理的事),而是"系统总共定义的意图分类,多到没法一次性塞进一个Prompt里全部列出来"。

现在意图就3个——导购、知识、模糊,少,可以直接把这3个类别的名字和例子全写进「分类」+「指令」,一次性让LLM选,就是本章做的事。

但假设业务做大了: 不再只服务一个生鲜群,而是扩展成覆盖更多品类、更多服务的客服系统,意图不再是粗粗的"导购/知识/模糊"三档,而是被拆得很细——

商品类(细分几十个):
    水产类导购、水果类导购、蔬菜类导购、
    肉禽类导购、促销活动咨询、团购规则……

知识类(细分几十个):
    挑选方法、保存方法、烹饪做法、
    营养搭配建议、过敏原查询……

新增的售后/服务类:
    退换货政策、配送时效、会员积分、投诉建议……

加起来可能从3个变成几百个。这时候如果还照老办法,把几百条意图描述全塞进「分类」卡片让LLM去挑"这句话属于哪一个",会出两个问题:每次调用都要把几百条描述发一遍,又长又贵;LLM在几百个选项里挑一个,比在3个里挑一个更容易挑错——选项越多,判断力反而越差。

两种成熟解法,映射到这个放大后的场景:

解法1:检索式意图识别(推荐)
    把每条"意图描述"(比如"水产类导购:用户想了解鱼虾蟹贝类的
    价格、推荐、规格")当成[第5章](05-prepare-data-and-kb.html)/
    [第6章](06-retrieval-and-hybrid-search.html)里讲的"知识库文档",
    单独建一个向量库——只是这次存的不是商品信息或生活知识,
    存的是"意图的描述"

    用户问"鲈鱼多少钱"来了,先拿这句话去这个"意图向量库"里
    检索,召回最相关的top-10候选意图(比如"水产类导购"
    "商品导购通用""水产知识查询"这10个),再把这10个候选
    交给LLM做最终判断——不是从几百个里选,是从检索预先筛出来
    的10个里选

    本质就是把检索能力复用到"意图"这件事上,原理跟检索商品/
    知识完全一样,只是检索的对象换了

解法2:两级分类
    先做一次粗判断:这句话是"商品类""知识类"还是
    "售后服务类"(跟现在"导购/知识"类似,只是换成了更大的
    顶层分类,控制在10个以内)

    判断出是"商品类"之后,再在"商品类"这几十个细分意图里
    做第二次、更精细的分类(水产类导购、水果类导购……)

    每一层要选的候选都不多,比一次性从几百个里选更快更准,
    代价是要多维护一层分类逻辑(一棵两层的意图树)

教学项目只有3个类别用不上,但以后类目变多(比如商品品类、知识类型都扩展),这是企业级系统的标准解法——业内很多面试也会问到这个问题,能答出"把意图当文档去检索、先召回再判断",比只会堆Prompt更能体现工程思维。