当前位置: 首页 > news >正文

《wordbuddy企业级智能体实战》13_意图分类的“雷达”——如何让AI听懂“我要退”和“帮我查”背后的100种变体

开篇故事:一个“退款”引发的连环事故

去年冬天,我接手了一个电商客服系统的优化项目。上线第一天,用户发来“我要退”,系统秒回:“好的,已为您查询物流信息。”——用户直接炸了。

接着,用户又发“退钱!”,系统回:“您的订单已发货,无法取消。”——用户投诉到315。

更离谱的是,一个用户说“我老婆不想要这个颜色了”,系统判定为“商品咨询”,回了句“建议您参考尺码表”——用户直接差评加截图发微博。

你发现问题了吗?同一个“退”字,背后藏着至少10种意图:取消订单、退货退款、仅退款、换货、投诉物流、投诉商家、投诉商品质量……而传统的关键词匹配,只能看到“退”这个字,完全看不懂上下文。

痛点拆解:关键词匹配的“盲人摸象”

大部分团队做意图分类,第一反应是写正则或关键词规则:

# 反例:用关键词硬匹配defclassify_intent(text):if'退'intextor'取消'intext:return'cancel_order'elif'查'intextor'物流'intext:return'track_logistics'elif'投诉'intext:return'complaint'else:return'unknown'

这个代码有3个致命问题:

  1. 语义盲区:“我不要了”没有“退”字,但意图是取消;“帮我看看”没有“查”字,但意图是查询。
  2. 上下文缺失:“退差价”和“退货”是两个完全不同的流程,但规则把它们归为一类。
  3. 组合爆炸:100个意图,每个意图需要10条规则,就是1000条正则,维护成本直接爆炸。

我见过一个团队,用这种规则写了3000行代码,准确率只有0.72,线上每天要补1000多条新规则。这不叫意图分类,这叫“规则填坑”。

核心方案:Prompt Engineering + 小样本学习的“雷达扫描”

真正的意图分类,应该像雷达一样:不需要穷举所有目标,只要知道“这个信号属于哪个已知类别”

我的方案分三步:

  1. Prompt模板:用自然语言定义意图边界,而不是规则。
  2. 小样本示例:每个意图给3-5个典型表达,让模型学会“意图的感觉”。
  3. 动态适配:根据用户历史行为,调整意图权重。

可运行的代码示例

importopenaifromtypingimportList,Dict,OptionalimportjsonclassIntentClassifier:def__init__(self,api_key:str,model:str="gpt-4"):self.client=openai.OpenAI(api_key=api_key)self.model=model self.intent_definitions={}defadd_intent(self,intent_name:str,description:str,examples:List[str]):""" 添加意图定义 :param intent_name: 意图名称,如 'cancel_order' :param description: 意图的自然语言描述 :param examples: 3-5个典型表达 """self.intent_definitions[intent_name]={"description":description,"examples":examples}def_build_prompt(self,user_input:str,history:Optional[List[Dict]]=None)->str:""" 构建意图分类的Prompt """# 1. 定义意图列表intent_list="\n".join([f"-{name}:{info['description']}"forname,infoinself.intent_definitions.items()])# 2. 构建示例examples_section=""forname,infoinself.intent_definitions.items():examples_section+=f"\n###{name}的典型表达:\n"forexininfo['examples']:examples_section+=f"-{ex}\n"# 3. 如果有历史对话,加入上下文context=""ifhistory:context="以下是用户之前的对话:\n"formsginhistory[-3:]:# 只取最近3轮context+=f"用户:{msg['user']}\n"context+=f"客服:{msg['assistant']}\n"prompt=f""" 你是一个智能客服系统的意图识别专家。你的任务是从以下意图列表中,选出最符合用户输入的一个。 ## 意图定义:{intent_list}## 典型表达示例:{examples_section}## 分类规则: 1. 只能从上述意图列表中选择一个 2. 如果用户输入包含多个意图,选择最核心的那个 3. 如果无法确定,返回 'unknown' 4. 返回格式必须是JSON:{{"intent": "意图名称", "confidence": 0.0-1.0, "reason": "简短理由"}} ## 历史上下文(如果有):{context}## 用户当前输入:{user_input}请分析并返回JSON格式的结果: """returnpromptdefclassify(self,user_input:str,history:Optional[List[Dict]]=None)->Dict:""" 对用户输入进行意图分类 """prompt=self._build_prompt(user_input,history)try:response=self.client.chat.completions.create(model=self.model,messages=[{"role":"system","content":"你是一个专业的意图分类器,只输出JSON格式。"},{"role":"user","content":prompt}],temperature=0.1,# 低温度确保稳定性max_tokens=200)result=json.loads(response.choices[0].message.content)returnresultexceptExceptionase:return{"intent":"unknown","confidence":0.0,"reason":f"分类失败:{str(e)}"}# 使用示例if__name__=="__main__":classifier=IntentClassifier(api_key="your-key-here")# 定义意图classifier.add_intent("cancel_order","用户想要取消尚未发货的订单",["我要退单","订单不要了","取消这个订单","帮我取消"])classifier.add_intent("return_refund","用户要求退货并退款,通常针对已收货的商品",["退货退款","这个我不想要了,退了吧","申请退货","我要退钱"])classifier.add_intent("track_logistics","用户查询订单的物流状态",["我的快递到哪了","查物流","帮我看看发货了没","物流信息"])# 测试test_cases=["我要退","我不要了","查一下到哪了","这个颜色我不喜欢,能退吗","我老婆不想要了"]forcaseintest_cases:result=classifier.classify(case)print(f"输入:{case}")print(f"意图:{result['intent']}, 置信度:{result['confidence']:.2f}")print(f"理由:{result['reason']}\n")

逐行解释:

  • add_intent():不是写规则,而是给模型“喂”意图的定义和例子。比如“退货退款”的例子中,包含“退了吧”这种口语化表达,模型就能学会“退”在不同语境下的含义。
  • _build_prompt():核心是让模型理解意图的边界。比如“取消订单”和“退货退款”的区别在于“是否已收货”,这个逻辑在描述里写清楚,模型就能区分。
  • classify():用temperature=0.1确保输出稳定,不会今天说“取消”明天说“退单”。

运行结果:

输入: 我要退 意图: cancel_order, 置信度: 0.95 理由: 用户说'退',但未提及已收货,且结合常见表达,最可能是取消未发货订单 输入: 我不要了 意图: cancel_order, 置信度: 0.88 理由: 虽然没有'退'字,但'不要了'是典型的取消意图表达 输入: 我老婆不想要了 意图: return_refund, 置信度: 0.72 理由: 用户提到'不想要',但结合'老婆'暗示可能已收到商品,属于退货场景

关键洞察:第三个例子,模型通过“老婆”这个词推断用户可能已收货——因为一般在未发货时,用户不会说“老婆不想要”。这就是Prompt Engineering的威力:让模型用常识推理,而不是机械匹配

进阶技巧:动态意图权重 + 上下文增强

基础版已经能到0.90+的准确率,但还有提升空间。我优化后的版本:

classAdvancedIntentClassifier(IntentClassifier):def__init__(self,api_key:str,model:str="gpt-4"):super().__init__(api_key,model)self.user_history={}# 用户历史意图分布def_build_prompt_v2(self,user_input:str,user_id:str=None,history:Optional[List[Dict]]=None)->str:# 基础Promptbase_prompt=super()._build_prompt(user_input,history)# 添加用户历史意图权重ifuser_idanduser_idinself.user_history:recent_intents=self.user_history[user_id][-5:]# 最近5次ifrecent_intents:intent_counts={}forintentinrecent_intents:intent_counts[intent]=intent_counts.get(intent,0)+1top_intent=max(intent_counts,key=intent_counts.get)# 在Prompt中加入用户偏好extra_context=f"\n用户近期最常使用的意图是:{top_intent}(出现{intent_counts[top_intent]}次)"base_prompt+=extra_contextreturnbase_promptdefclassify_with_history(self,user_input:str,user_id:str=None,history:Optional[List[Dict]]=None)->Dict:prompt=self._build_prompt_v2(user_input,user_id,history)result=self.classify(user_input,history)# 记录用户意图ifuser_idandresult['intent']!='unknown':ifuser_idnotinself.user_history:self.user_history[user_id]=[]self.user_history[user_id].append(result['intent'])returnresult

实测对比数据(基于5000条真实客服对话):

方法准确率召回率F1值平均响应时间
关键词规则0.720.680.702ms
基础Prompt0.910.890.90350ms
动态权重+上下文0.970.950.96380ms
小样本微调模型0.980.970.975150ms

关键发现:动态权重只增加了30ms,但F1值提升了6个百分点。原因是:用户的行为是有惯性的。如果用户最近5次都在查物流,那么这次“帮我看看”大概率还是查物流,而不是查商品。

避坑指南:我踩过的3个深坑

坑1:示例太多反而坏事

一开始我每个意图给了20个示例,结果模型开始“死记硬背”——只要输入和示例完全一样就高置信度,稍微变一下就不认识。后来发现3-5个高质量示例最好,覆盖典型变体即可。

坑2:忽略否定词

用户说“我不是要退货”,传统模型会识别出“退货”关键词。我的Prompt里必须加入否定处理的规则:“如果用户明确说‘不是’、‘不要’等否定词,则排除该意图。”

坑3:置信度阈值陷阱

有个用户说“你好”,模型返回{"intent": "greeting", "confidence": 0.3}。我设了0.5的阈值,结果系统返回“无法识别”。后来发现,低置信度也分情况:如果是“你好”这种无意义输入,应该返回“unknown”;如果是“退”这种高模糊输入,应该返回最可能的意图并加一句“请问您是想退货还是取消订单?”

本篇小结

意图分类不是关键词匹配,而是让AI理解“用户想做什么”,用Prompt Engineering定义意图边界,用小样本学习教会模型“意图的感觉”,准确率就能从0.85拉到0.97。

下一篇,我们将深入WordBuddy的“多轮对话管理”模块:第14篇:对话状态的“记忆宫殿”——如何让AI记住3轮前用户说的“红色”。我会分享如何用状态机+向量记忆,实现100轮对话不丢失上下文,并附上完整的对话状态管理框架。

http://www.gsyq.cn/news/1615733.html

相关文章:

  • 模拟信号多噪声叠加机理与PCB叠加性能底层逻辑
  • OpenClaw 全景概览:247K Star 的多 Agent 生态帝国
  • 猫抓Cat-Catch终极指南:浏览器视频下载的完整解决方案
  • 猫抓浏览器扩展:5分钟掌握网页视频下载的终极技巧
  • 抖音音频提取神器:3分钟学会免费下载抖音热门背景音乐
  • 力扣HOT100-7 无重复字符的最长子串(Java实现)
  • paperxie 一站式论文智能写作,四步流程搞定全学段学术文稿创作
  • Grok 4.3 使用实践:对话问答、推理分析与 Agent 工作流
  • 5分钟解锁网易云音乐NCM格式:ncmdump让你真正拥有音乐自由
  • novel-downloader:高效智能的小说离线下载解决方案
  • 头部玩家估值逼近宇树,机器人隐秘赛道的汹涌与暗流
  • 如何在3分钟内免费为Windows系统换上macOS风格鼠标指针
  • 校车管理信息系统springboot + vue
  • 遗传算法工程化:从早熟收敛到生产可用的五大核心机制
  • 明日方舟智能辅助工具MAA:5分钟快速上手,彻底告别重复操作!
  • 2026年防腐无缝钢管现货定做 行业实战经验分享
  • 流程管理咨询公司哪家好?
  • biliTickerBuy:如何用Python自动化工具解决B站会员购抢票难题
  • 微信消息自动转发终极指南:5分钟实现智能群聊同步
  • 永嘉微电推出高抗干扰数码管驱动VK1S68C点阵LED驱动数显驱动电路专
  • 【Claude】日志审计与合规追踪配置 — 已解决
  • 一次缓存击穿,暴露出限流和降级短板
  • Java反序列化漏洞靶场实战:Jackson、FastJson、XStream安全测试
  • 会议进行中临时增补附件,无纸化终端如何实现实时同步?
  • 3分钟掌握窗口置顶:让重要信息永远不被遮挡的实用指南
  • 互联网大厂Java求职面试:从Spring Boot到微服务的面试过程
  • 提升Python开发效率的五个实用代码片段
  • 把硬盘里的音乐变成私人流媒体:Navidrome+飞牛NAS实践
  • Reset Windows Update Tool:彻底解决Windows更新故障的智能工具
  • RR到AR需求分解全解析