大家好,我是小富~
最近在弄个智能售后客服的 AI 改造项目,业务逻辑的第一步非常明确,就是要把用户口语化的发问,转成后端系统能看懂的结构化参数。
用专业的 AI 术语来说,这叫 意图识别(Intent Recognition) 和 命名实体识别(NER, Named Entity Recognition)。
1. 意图识别与 NER 到底是个啥?
很多没做过 AI 的同学听到这两个词觉得很高大上,其实用大白话解释,就是找动词和找名词。
举个例子,用户在客服框里发了一句:
“我昨天买的 iPhone 15 发货没?订单号 8848,赶紧的。”
如果我们直接把这句话扔给后端的 Java 代码,Java 根本不知道该调哪个接口。这时候就需要做两件事:
意图识别(找动词): 判断这个用户到底想干嘛?是想退款、催发货还是开发票?在这句话里,意图显然是 Query_Logistics 查物流。
NER(找名词/实体): 既然要查物流,后端接口肯定需要参数。从这句话里,我们要提取出关键实体:{"product": "iPhone 15", "order_id": "8848", "time": "昨天"}。
以前没有大模型的时候,算法工程师为了干这事儿,得用 BERT 等传统 NLP 模型,标几万条语料,辛辛苦苦训练几个月,而且如果遇到生僻商品名还经常识别不出来。
现在有了 LLM 大语言模型,很多同学觉得这简直是白给的送分题,连模型都不用训,直接写了一段 Prompt:
“你是一个数据提取专家。请提取用户输入中的意图(intent)、商品名(product)和订单号(order_id)。必须且只能以 JSON 格式输出,绝对不允许包含任何多余的汉字!”
在测试环境跑了几十条数据,完美输出 JSON,开开心心上线了。结果一上生产环境就报错,下游的后端节点疯狂抛出 JSONParseException。
2. 奇怪的问题
拉出线上日志,看到大模型的返回结果可以说是防不胜防,明明千叮咛万嘱咐“只准输出 JSON”,但大模型就是给你自由发挥一下
现场一(自带排版的废话)
它确实输出了 JSON,但非要在前后加上礼貌的废话和 Markdown 标记
好的,根据您提供的信息,提取结果如下:
{ "intent": "Query_Logistics", "order_id": "8848" }希望对您有帮助!
后端的 JSON.parse() 碰到这些汉字和反引号,解析失败报错。
现场二(随意重命名)
昨天说好的字段名叫 "order_id",今天它觉得 "order_number" 更地道,自作主张把 JSON 的 Key 给换了。后端的反序列化对象拿到的值全是 Null。
现场三(无中生有,最致命!)
用户明明只发了一句“查一下我的快递”,压根没提订单号。但大模型为了保持 JSON 结构的完整,自己瞎编了一个 "order_id": "123456",这导致系统直接去查了别人的订单,引发了严重的越权 Bug!
为什么会这样?
因为我们在潜意识里,把大模型当成了一个听指令的函数,但大模型的物理底层,本质还是一个概率输出的机器。
在经历了强化学习(RLHF)后,大模型被刻意训练得“礼貌、热情”。你让它提取数据,它顺手在开头加一句“好的”,在它的概率模型里,是一件极其“合理且正确”的事。
指望用 Prompt 去约束它生成严格的机器语言,就像是用道德规范去约束交通事故,必然会有防不住的时候。
3. 意图提取方案
真正的 AI 工程化落地中,想要让大模型 100% 稳定地做意图识别和 NER,我接触过的几种做法。
解法一:给占位符
首先解决瞎编数据的问题,提供提取规则,绝对不要要求大模型强制填满每个字段。
必须在字段的描述里提供显式的找不到处理策略。例如:order_id: 用户的订单编号。如果用户输入中未提供,必须填充固定字符串 NOT_FOUND,绝对禁止自己推测或编造。
有了这个规则,后端拿到 NOT_FOUND 就可以直接过滤,或者触发客服的反问逻辑(“请问您的订单号是多少呢?”)。
解法二:Function Calling
不想写正则去扣字符串,可以用 Function Calling 函数调用
不要强迫大模型输出纯文本 JSON,而是告诉大模型,后端有一个现成的函数叫 query_order(order_id, product_name),请你根据用户的输入,去调用这个函数。”
主流大模型在训练时,都进行过专门的工具调用微调。当它发现需要调函数,它会意识到自己现在是在填写参数表,而不是在跟人聊天。
这样一来,后端收到的就不再是一段带有 Markdown 的文本,是个标准的函数调用 Payload 对象,直接用 Java 或 Go 就能序列化。
约束解码技术
在金融、医疗这种对容错率为零的严苛场景,哪怕 Function Calling 偶尔改变字段类型,也是不可接受的。这时候就得用约束解码(Constrained Decoding)。
如果用的是 OpenAI 的新接口,或者用 vLLM 等框架在本地部署大模型,就可以开启这个功能。
它的工作原理是大模型在生成每一个词前,都会在几万个词库里算一遍概率。约束解码机制会拿着你定义好的 JSON 格式规则,直接拦截在推理引擎的最底层。
比如按照规则,这个位置只能填双引号 ",那底层状态机就会把词库里其他 99990 个词汇(比如“好的”、“抱歉”)的生成概率强行修改为 0!
模型想胡说,直接在物理层面上拦截,只能吐出绝对符合格式的 JSON 字符串。
说在最后
把大模型当做纯粹的黑盒,让它直接吐出文本去怼后端的解析层,是系统设计上最脆弱的一环。
日常的业务代码是确定性的,但 AI 业务很多开发最大的痛苦,就是总是试图用传统的“输入-输出”思维,去驾驭一个充满概率性的大模型。
