首页 核心备考 STAR 案例 评估与护栏 评估深入 护栏深入 核心概念 意图识别 EN

意图识别 · 第 1 / 2 部分

弄清楚用户到底想要什么

意图识别(intent recognition)是智能体判断「用户要的,是自己能做的哪一件事」的那一步。这一步错了,智能体给你的不是一个稍差的答案——它会直接做错事。本页从零开始建立这个概念,一直讲到你该如何衡量它。

它到底是什么

它也叫意图分类(intent classification)。最朴素的定义是:判断用户真正想让系统做什么。

先从它要解决的问题说起。一个人打进来一句话。句子只是文本——一串字符而已。软件没办法执行一个句子,软件执行的是函数: get_order_status(order_id), cancel_order(order_id)。所以必须有个东西站在句子和函数之间,判断这句话要的是哪一个函数。这个东西就是意图识别。

关键的一点在于,它 不是 关键词匹配。来看三个不同的句子:

"帮我看看订单 123 到哪儿了?"
"我的包裹怎么还没到?"
"这个订单现在是什么状态?"

它们几乎没有共同的词。其中一句里根本没有像另外两句那样出现「订单」;还有一句是抱怨的语气,而不是请求。但这三句想让系统做的是同一件事,而这件事有一个名字:

check_order_status

这个名字就是意图(intent)。智能体一旦拿到它,就知道下一步该做什么:

check_order_status
   ↓
订单 API → get_order_status(order_id)
一句话版本 意图识别把自然语言映射到一个 可执行的任务类别(executable task category) ——这个标签告诉智能体:该调用哪个工具、该走哪条工作流,以及还差哪些信息需要补齐。

注意「可执行的任务类别」这个说法。意图不是话题。「订单」是一个话题,而 cancel_order 则是一个任务类别,因为系统看到这个标签时,有一件具体的事情要做。这个区别最终决定了整套设计,我们后面会在这一节回到它: 从业务动作出发.

它在智能体中的位置

意图只是一条链路里的一步。把这条链路讲清楚,就是你回答「讲讲你的智能体是怎么跑的」时不绕圈子的办法。

下面是一次典型的智能体对话轮次的形状。从上往下读——每一步都依赖上一步:

用户输入
   ↓
理解请求
   ↓
意图识别          ← 用户想要什么?
   ↓
抽取参数 / 实体     ← 这件事需要哪些取值?
   ↓
有没有缺失的?      ← 如果缺,就追问
   ↓
选择工具 / 工作流
   ↓
执行
   ↓
生成答案

用一句真实的话完整走一遍:

用户说

"帮我查一下订单 A123 的配送状态?"

智能体推导出

intent = check_delivery_status
order_id = "A123"
→ get_delivery_status(order_id="A123")

两个不同的东西:意图和槽位

这两者经常被混为一谈,所以分开钉死。

1
意图 —— 用户想让系统做什么

一个标签,用来命名那个动作。它回答的是「这属于我的哪一项能力?」

check_ordercancel_orderchange_addressrequest_refundcreate_ticket

2
实体,或者说槽位 —— 这个动作跑起来需要什么

也就是参数。 实体(entity) 是文本里提到的一个值;槽位(slot)是工作流需要被填上的一个空。同一件事的两种说法。

order_idcustomer_idproduct_namedatelocation

把两者放在一起,从同一句话里取出来——「把我昨天订的那个 iPhone 取消掉」:

意图
  cancel_order

槽位
  date    = 昨天
  product = iPhone

注意这里的槽位还不能直接用。「昨天」不是 API 能接受的日期,「iPhone」也不是一个订单号。把这些松散的人类说法解析成系统能用的值,本身是独立的一步,也是常见的失败来源——这类失败经常被算到意图识别头上,其实是槽位填充(slot filling)的问题。

智能体到底为什么需要它

因为智能体有很多种能力,而其中只有一种是对的。

设想一个客服智能体,手上握着这些工具:

get_order()
cancel_order()
create_return()
check_inventory()
update_address()
create_support_ticket()

用户写下五个字: "我的东西还没到。" 在任何事情发生之前,智能体必须先定下一种解读:

这到底是……
  A. 查订单?
  B. 查配送?
  C. 要求退款?
  D. 投诉?

这里没有「中立」这个选项。它选了什么,就决定了哪个工具会被执行。而这件事之所以比乍看之下更重要,原因在于:

意图错了,不是答案错了——是动作错了 如果一个问答系统理解错了你,你拿到的是一段不好的文字,你换个说法再问一次就行。但如果一个 智能体 理解错了你,它可能已经把你的订单取消了。影响半径完全不同,而且会随着你给工具的权限变大而变大。

具体来说:

误判

"我的包裹到现在还没到"
→ refund_order

智能体发起了一笔没有人要求过的退款。

正确

"我的包裹到现在还没到"
→ check_delivery_status

智能体去查物流单号,然后解释延误的原因。

所以任何智能体架构里,很大一部分其实就是这条链路:

意图
   ↓
策略 / 工作流
   ↓
工具

意图挑出策略,策略挑出工具。把这三者分开,才能让你在不重新训练任何东西的前提下改一条业务规则。

从业务动作出发,而不是从语言出发

最常见的错误,是第一天就设计出几十个意图。第二常见的,是拿话题给它们命名。

人的本能是去看用户的消息,然后按「听起来像什么」把它们分组。这样得到的是话题标签。要反过来做:先写下系统 能做哪些事,再让意图从这里自然长出来。对一个电商助理来说,能力地图可能是这样:

订单
 ├─ 查询订单
 ├─ 修改订单
 └─ 取消订单

配送
 ├─ 查询配送状态
 └─ 修改配送地址

退款
 ├─ 发起退款
 └─ 查询退款状态

账户
 ├─ 更新资料
 └─ 重置密码

到这一步,意图不过是这些叶子节点的名字:

order_status
cancel_order
modify_order
delivery_status
change_delivery_address
request_refund
refund_status
reset_password

为什么用话题命名会失败

对比两套候选标签:

差 —— 话题

order
product
payment
delivery

好 —— 动作

check_order_status
cancel_order
modify_order
reorder

标签 order 对智能体毫无用处,因为它没有回答唯一重要的那个问题——我现在该做什么?这个标签下面住着四件完全不同的事:查询、取消、修改、重新下单。其中一件是只读的、无害的;另一件是不可逆的。把它们塌缩成一个标签,恰好扔掉了下一步最需要的那部分信息。

这条规则 一个好的意图对应 一个明确的业务动作或工作流 ——而不是一个主题领域。

选择粒度

这是意图设计里最有用的一条检验,而且只要问一个问题就能用。

假设用户既会问「查一下我的订单」,也会问「查一下我的配送」。你需要一个意图,还是两个?

check_order

        ...或者...

check_order_status
check_delivery_status

很诱人的做法是按两句话听起来差别有多大来决定。这完全是错的维度。正确的问题是:

这条检验 接下来发生的事有没有区别?如果两种说法会导向不同的决策、不同的工作流,或者不同的工具,那它们就是不同的意图。如果它们最终汇聚到同一个调用,那它们就是一个意图。

套用一下:

拆开它们

check_order_status → 订单 API
check_delivery_status → 物流 API

不同的系统、不同的失败模式、不同的答案。两个意图。

把它们合在一起

两者最终都落到 get_order(order_id)

拆开没有带来任何收益,却多了一个分类器可能分错的类别。一个意图。

第二种情况值得多琢磨一会儿,因为它解释了为什么「意图越多越保险」并不成立。每多一个标签,就多一次分错的机会、多一批要写的示例、多一行一列的混淆矩阵。一个对下游毫无影响的区分,是纯粹的成本。

一份像样的意图定义包含什么

光有名字不算定义。边界才是定义。

在清单里写下 refund 然后就翻篇,最后得到的就是一个行为不可预测的分类器——因为没有人(无论是人还是模型)确切知道什么该归进去。一份完整的定义长这样:

意图名
  request_refund

描述
  用户明确要求把一笔已经付过款的订单的钱
  退回来。

包含
  "我想退款"
  "这个订单能退货吗?"
  "取消掉,然后把钱退给我"

排除
  "我的退款什么时候到?"
  "为什么我的退款还没收到?"

其中 包含 和 排除 这两块才是真正干活的地方。定义是在边缘处失效的,不是在中间——没有人会把「我要退款」分错。失败都发生在相邻的那一对上:

request_refund

用户希望一笔退款被 发起。这会写数据——钱真的会动。

refund_status

退款已经存在了,用户只是想知道它到哪儿了。只读。

「为什么我的退款还没到?」里带着「退款」两个字,语气还不太高兴,于是一个粗心的系统就把它归到 request_refund ,然后试图再发一笔退款。它其实是 refund_status。一个是查询,另一个是交易。把这条边界明确写下来、两边都给上例子,是意图设计里性价比最高的准确率提升手段——而且它顺带就是你的测试集。

真正实现它的三种方式

经典分类器、LLM 分类器,或者干脆不做显式的分类器。三种都还在用——大多数新系统落在第三种。

1
训练一个分类器

传统做法。你收集带标注的样本,微调一个小语言模型,比如 BERT,它会为每一个意图输出一个概率。

用户文本
   ↓
分类器
   ↓
refund_status:   0.85
request_refund:  0.10
other:           0.05

好处: 快、单次调用便宜、稳定——同样的输入永远给出同样的输出,因此很容易测试。
坏处: 需要你可能根本没有的训练数据;而且新增一个意图,就意味着整个模型要重新训练、重新验证。

2
让一个 LLM 去做分类

你把意图清单写进提示词,交给一个通用模型,让它返回一个标签。完全不需要训练。

你是一个意图分类器。

可用意图:
  1. check_order_status
  2. cancel_order
  3. request_refund
  4. refund_status
  5. change_address
  6. unknown

只返回 JSON。

输入 "我的退款还没到。" 然后模型返回:

{
  "intent": "refund_status",
  "confidence": 0.94
}

好处: 不需要训练数据,在提示词里放几个例子就能跑起来;而新增一个意图,只是改一个文本文件。
坏处: 单次调用更慢也更贵,而且不确定(non-deterministic)——这恰恰就是下面那节评估存在的原因。

值得说出来的一点现代智能体技术栈里,大多数所谓的「意图路由器」,字面上就是这个:一次 LLM 调用,带上一份清单和一个 JSON schema。

3
不做分类器 —— 意图隐含在工具选择里

你干脆跳过标签这一层。把工具定义交给模型,让它直接挑一个。

工具:
  get_order_status
  cancel_order
  request_refund
  get_refund_status
  change_address

用户: "我的退款一直没到账。" 模型调用了 get_refund_status()。整个过程里从来没有出现过意图标签——但这个决策照样被做出来了,因为选择一个工具 就是 在选择一个意图。

用户
 ↓
LLM
 ↓
工具选择
 ↓
工具执行

好处: 一次调用而不是两次,没有需要保持同步的东西,天然能处理你从没穷举过的情况。
坏处: 这个决策是看不见的。你没办法轻松地记录「它当时认为这是什么」,没办法在意图和工具之间插一层策略,也没办法把意图准确率报给风险委员会。

面试时值得点出来的那句话 一个现代智能体,代码库里可能根本没有任何一个叫「意图分类器」的模块,但它每一轮对话都在做意图识别。问题从来不是「我们有没有这个模块」——而是「这个决策是否显式到我们能记录它、能给它加闸、能衡量它」。在受监管的环境里,这一点支持你把它做成显式的,哪怕模型本来可以隐式地完成。

分层路由:不要一次性分四十个类

只要规模到了企业级,两次粗决策就好过一次细决策。

让一个分类器一次性在四十个标签里做选择,是很脆弱的:标签之间开始重叠,示例变得模糊,准确率下降却很难归因。改成把这个决策拆成几层。最上面一层只回答「这是哪一类东西」:

                 ┌─ knowledge_query  → 检索 / RAG
                 │
User → Router ───┼─ business_action   → 工作流 / 工具
                 │
                 ├─ conversation      → 普通 LLM 回复
                 │
                 └─ unknown           → 请求澄清

只有在 business_action 内部,你才去做细粒度的选择:

business_action
       ↓
order      refund       delivery
 ├ check    ├ request     ├ status
 ├ cancel   └ status      └ change_address
 └ modify

这叫做 分层意图识别(hierarchical intent classification)。它更经得起考验,有三个原因:

  • 每一次决策的选项更少。 先四选一、再五选一,比一次二十选一可靠得多。
  • 危险的那条边界被提前守住了。 「这是一个问题,还是一个动作?」这一刀决定了有没有任何东西可能被改动——而且它恰好是最容易做对的一刀。
  • 它把失败定位到局部。 准确率掉下来的时候,你能看出是哪一层动了,而不是盯着一个总数发呆。

所有人都会忘掉的那两个意图

未知,和歧义。把它们漏掉,就是把分类器变成一个隐患。

未知 / 超出范围

一个被要求在五个标签里做选择的分类器,永远会选中这五个里的一个。分类的定义本来就是这样。所以,如果你的智能体只认识 cancel_order, check_order 和 refund,而用户问「你们 CEO 是谁?」,模型根本没有「都不是」这个选项——除非你给了它一个。

unknown
out_of_scope
ambiguous

如果没有这些标签,一个跑题的问题就会被硬塞进某个业务意图,智能体于是为一个与之毫无关系的请求去调用工具。这是一整类尴尬的线上事故背后的机制,而防住它只需要加一个标签和一句指令。

含糊不清 —— 为什么不该去猜

用户说: 「我要改一下我的订单。」 改什么?下面每一个都说得通:

change_address
change_quantity
change_product
cancel_order

错误的做法是挑一个最可能的然后直接执行。正确的做法是意识到没有哪个选项明显胜出,并且把这一点说出来:

intent     = modify_order
confidence = low
   ↓
「好的 —— 你是想改配送地址、
 改数量,还是订单里
 其他方面的内容?」
一个成熟的意图层,产出的到底是什么 不是一个标签,而是一个标签 加上一个置信度(confidence) 再加上一套澄清策略(clarification policy) —— 一条写下来的规则,规定「不确定到什么程度就不该动手」,而且这个门槛可以按意图分别设定。只读的查询类操作,信号弱一点也可以照常执行;而任何不可逆的操作,都应该要求一个很强的信号。

这种不对称正是整个思路的核心:多问一个问题的代价很小,而且是可以挽回的;而取消错一笔订单的代价,既不小,也无法挽回。

当一条消息里包含两个意图

真实的用户不会一次只发一条指令。正是在这里,意图识别开始变成任务规划。

「先查一下我的订单,如果还没发货就帮我取消。」

这句话里有两个动作,而第二个动作取决于第一个动作的结果。一个建立在「一条消息 = 一个意图」这个假设之上的系统,根本没有办法表示这种情况;它只会挑一个自己认为占主导的意图,然后悄无声息地把另一半丢掉 —— 这比大声报错还要糟糕。

{
  "intents": [
    { "intent": "check_order_status" },
    { "intent": "cancel_order",
      "condition": "if not shipped" }
  ]
}

智能体据此构建出一个小型执行计划:

get_order_status
       ↓
if status != shipped
       ↓
cancel_order
值得点名的那个转变 一旦意图开始带上顺序和条件,你做的就不再是 意图分类 ,而是变成了 任务规划。输入还是同一句话,但输出变成了一张步骤图,而不是一个标签。

你不必从第一天就支持这种情况。但你必须清楚自己这套设计的天花板在哪里,并且能说清楚系统目前遇到这样一条消息会怎么处理 —— 面试官很喜欢问这个问题,因为一个诚实的回答,能看出你是不是真的把这类系统跑到过生产环境。

怎么评估它:先从准确率说起

准确率是你的起点,但离终点还差得很远。

起点是一个测试集(test set):把输入和你认为正确的标签配成一对,这个标签叫作 标注真值(ground truth).

用户输入标注真值
我的订单到哪了?order_status
取消我的订单cancel_order
我的退款还没到账refund_status
我要把钱退回来request_refund

跑一遍模型,然后逐条对比:

输入期望预测
我的订单到哪了order_statusorder_status
取消订单cancel_ordercancel_order
退款还没到账refund_statusrequest_refund ✗

准确率就是最简单不过的一种汇总方式 —— 做对了多少条,除以一共有多少条:

Accuracy = correct / total

950 / 1000  =  95%

而这个数字单独拿出来看,对一个智能体来说几乎没有意义。接下来的三节讲的就是为什么。

精确率、召回率、F1

准确率是把所有情况平均起来看。而下面这几个指标一次只看一个意图 —— 真正的伤害恰恰就藏在那里。

单一准确率数字的问题在于,它掩盖了分布。如果你 90% 的流量都是 order_status ,而模型把它做得很好,那你整体就能拿到 95% 的分数,但在 request_refund 上却可能一塌糊涂 —— 而那正是会动钱的那个意图。所以要按意图逐个衡量。

P
精确率 —— 当它说是 X 时,有多大比例真的是 X?

模型一共预测了 request_refund 100 次,其中 90 次确实是退款请求。

Precision = 90 / 100 = 90%

精确率低意味着 误报(false alarms):智能体对一些其实并不是它所以为的那类请求动了手。对任何会修改数据的意图来说,这就是最要命的那个数字。

R
召回率 —— 在所有真正的 X 里,它抓到了多少?

测试集里一共有 120 条真正的退款请求,模型找出了其中 90 条。

Recall = 90 / 120 = 75%

召回率低意味着 漏检(misses):真实的请求被路由到了别的地方,用户的问题始终没被解决,最后只能转人工。

F
F1 —— 把两者合成一个数字
      2 × Precision × Recall
F1 = ────────────────────────
        Precision + Recall

它是刻意对失衡下重手的:一个精确率 100%、召回率 10% 的模型,F1 大约只有 18%,而不是 55%。用它在同一列里比较不同版本的模型;而要判断某一类具体的失败能不能接受,还是要把精确率和召回率分开看。

这个取舍是业务决策,不是技术决策 你几乎总能通过挪动一个阈值,用精确率换召回率,或者反过来。往哪边偏,取决于这个意图会做什么事。对于 cancel_order,要偏向精确率 —— 不确定就绝不动手。而对于安全类或转人工升级类的意图,要偏向召回率 —— 宁可多报几次,也不要漏掉一个。

混淆矩阵告诉你该修哪里

汇总指标只告诉你「有地方不对」。混淆矩阵告诉你的是:哪一对意图正在往哪个方向串。

混淆矩阵(confusion matrix)是一张网格:行是输入真正属于的类别,列是模型给出的判断。对角线上的是判对的;对角线之外的每一格,都是一个具体的、叫得出名字的错误。

真实 ↓ / 预测 →request_refundrefund_statusorder_status
request_refund9082
refund_status20755
order_status1396

看中间那一行。在 100 条真正的 refund_status 问题里 —— 也就是用户在问自己的钱到哪去了 —— 模型把其中 20 条判成了 request_refund。这不是一个含糊的「质量问题」。它是一处具体的、单向的泄漏,而且正是危险的那个方向:一个查询状态的问题被当成了一笔交易请求。

它同时也告诉了你修法,而这个修法不是「多训练」,而是「定义得更清楚」:

request_refund
  「我要退款」
  「请把这笔订单退掉」

refund_status
  「我的退款到哪了?」
  「我的退款还没到账」
  「退款一般要多久?」
为什么这比一个单一分数更有用 准确率告诉你「87%」。混淆矩阵告诉你「你那两个退款意图,按现在的写法根本区分不开」。这两条信息里只有一条是可以立刻动手的 —— 而且这个动作的成本是花十分钟补几条例句,而不是跑一次重新训练。

评估的四个层次

意图准确率只是四层里的第一层。业务方真正关心的是第四层。

把标签判对本身并不是目标。目标是用户的问题被解决了。而这两件事分开的频率,比你以为的要高得多,所以要把每一个阶段分别评估 —— 否则任何一层出了问题,从外面看起来都长得一模一样。

第 1 层  意图识别
第 2 层  实体 / 槽位抽取
第 3 层  工具 / 工作流选择
第 4 层  端到端的任务成功

拿一条请求来看 —— 「把订单 A123 的配送地址改成墨尔本办公室」 —— 把这四层都检查一遍:

层次检查什么通过条件
1 · 意图那个标签change_delivery_address
2 · 槽位那些参数order_id = A123, new_address = Melbourne office
3 · 工具实际发出的调用update_delivery_address()
4 · 结果现实系统里的地址确实被改掉了

第 4 层 —— 任务成功率(task success rate),也就是最终让用户问题得到解决的请求所占的比例 —— 当资深的人问你「这个智能体到底好不好」时,这就是你应该首先端出来的那个数字:

Task success rate = requests completed successfully
                    ─────────────────────────────
                          total requests

而且第 1 层失败、第 4 层却通过,是真实可能发生的。如果模型把一个查询标成了 check_order ,而你的标注真值写的是 check_delivery,但两者都会路由到同一个查询,用户也拿到了正确的答案,那么意图指标就记下了一次其实并不存在的失败。这一点很值得能说出来:它说明你清楚哪些数字只是代理指标,而哪一个数字才是真正的结果。

最要紧的那个指标:错误动作率

对任何能修改数据的智能体来说,这个指标的分量高于准确率。

一位用户问: 「如果我取消订单,能退款吗?」

这是一个关于政策的问题,并不是要取消任何东西的请求。但它里面有 cancel(取消)这个词,讲的又是他自己的订单,而一个被迫必须挑出某个动作的模型,就会挑 cancel_order。如果智能体接着真的执行了它,那这位用户就因为问了一句「会不会失去订单」,而真的失去了订单。

误触发动作率(false positive action rate) 指的是:在所有对话轮次里,智能体执行了一个本来根本不该执行的、会改变系统状态的动作,这类轮次所占的比例。对于那些能动钱、能下单、能删记录、能替别人发消息的智能体来说,这就是最该优化的数字 —— 排在意图准确率之前,也排在 F1 之前。

一个正经的企业级智能体,按层次划分的完整指标集:

层次指标它告诉你什么
意图准确率整体健康度,比较粗
意图精确率 / 召回率 / F1单个意图的健康度
意图混淆矩阵该修的是哪一对
路由路由准确率有没有走到正确的分支
工具工具选择准确率有没有调用正确的函数
参数槽位准确率参数填得对不对
安全错误动作率有没有做了不该做的事
智能体任务成功率用户的问题有没有被解决
UX澄清率它有多经常需要反问
UX无谓澄清率它明明知道却还要问的频率

最后这一对指标,很值得你不等人问就主动提出来。澄清是应对含糊输入的安全阀 —— 但一个每一轮都要反问一句的智能体会让人筋疲力尽,用户会直接弃用它。把 无谓的 澄清单独统计出来,才能防止这个本来安全的行为,悄悄变成一个没用的行为。

做一个不讨好自己的测试集

如果你的测试用例全是教科书式的句子,那你的指标什么也没量到。

很容易掉进去的诱惑是:给每个意图写一句最标准的说法,就当成测试集:

取消我的订单
查一下我的订单
申请退款

这些当然会全部通过,但它们什么都说明不了,因为没有哪个真实用户是这么说话的。一个意图需要很多种不同的形态。单就 cancel_order 这一个而言:

取消我的订单。
我不想要这个了。
能不能把我的订单停掉?
我下错东西了。
请取消订单 #1234。
我改主意了。
能把我这笔购买撤销吗?

然后,有意识地把那些会把系统搞坏的类别都放进去:

  • 拼写错误和随手打的输入 —— 「cancle my ordr」
  • 口语化的说法 —— 半截句子、没有标点、各种口头语
  • 又长又啰嗦的消息 ,真正的请求只是夹在中间的一个小句
  • 含糊的表述 ,这类确实应该触发澄清
  • 多个意图 出现在同一条消息里
  • 依赖上下文的 消息 —— 「对,就这么做」只有结合上一轮对话才有意义
  • 否定 —— 价值最高的一类,见下方

否定这一类值得单独举一个例子,因为它能击穿所有基于关键词的系统,以及多到令人意外的一批基于模型的系统:

「我 不 想取消订单,
 我只是想改一下地址。」

句子里明明就有 cancel 这个词。一条基于关键词的规则会直接触发 cancel_order ,把订单毁掉。而正确的标签是 change_delivery_address。第一天就往测试集里放上几条这样的例子 —— 正是这些用例,把「看起来能用的系统」和「真的能用的系统」区分开来。

测试用例应该从哪里来 三个来源,按价值从高到低:真实的生产环境会话记录;你经历过的每一次事件或投诉(每一条都变成一个永久用例);最后才轮到手写的各种变体。凭空编出来的例子,只会继承写它的那个人的想象力,而那恰恰就是你想要覆盖的盲区。

把整件事浓缩成一条线

如果这一页你什么都记不住,那就记住这条链路。

用户请求
   ↓
用户想要什么?
   ↓
意图
   ↓
那需要哪些信息?
   ↓
槽位
   ↓
系统应该做什么?
   ↓
工作流 / 工具
   ↓
它真的把问题解决了吗?
   ↓
任务成功

这也正是为什么「意图识别」对这门手艺来说是个稍有误导的名字。它并不是一个孤立的分类问题,而是下面这条链条上的第一环:

意图 → 参数 → 路由 → 工具 → 任务完成 还有一句值得直接背下来的工程定义:意图识别,就是把用户的自然语言,映射到系统真正能够执行的下一个决策上。

这一页里的所有内容都由此推导而来:围绕可执行的动作来设计意图;只有当执行方式不同时才拆分意图;用相邻的意图来划定彼此的边界;为「未知」和「含糊」留一个逃生口;并且端到端地衡量整条链路,而不是只衡量第一环。

术语表

上文用到的每一个术语,都用大白话解释一遍。

意图
一个标签,用来指明用户希望系统做什么;它取自一份固定的清单,清单里的每一项系统都知道该怎么执行。
意图分类
从用户的文字里挑出那个标签这件事。和意图识别是同一回事。
实体
文本里提到的一个具体的值 —— 一个订单号、一个日期、一个公司名称。
槽位
工作流里的一处空白,必须先填上才能跑起来。实体就是用来填槽位的东西。
槽位填充
把缺失的值收集齐全,包括直接开口向用户要。
路由器
读取意图、并决定把请求送进系统哪一条分支而不是另一条的那个组件。
分层意图识别
先做粗粒度的判断(是提问还是执行动作),再在这个大类里做细粒度的判断。比一次性做一个大而扁平的选择要可靠得多。
置信度
分类器给自己的答案附上的一个数字,表示它有多确定。用来决定是直接行动还是先反问。
澄清策略
写下来的规则,规定「不确定到什么程度就不该动手」;通常每个意图各不相同,取决于这个动作是否可逆。
超出范围
一个刻意设置的标签,专门用于那些系统本来就不该处理的请求,免得它们被硬塞进某个业务意图里。
标注真值
由人判定为正确的那个标签,在给预测打分时作为参照。
准确率
测试用例中被标对的比例。比较粗糙,而且当流量分布不均匀时容易产生误导。
精确率
在模型判为意图 X 的所有样本里,真正是 X 的比例。精确率低意味着误报多。
召回率
在真正属于意图 X 的所有样本里,被模型找出来的比例。召回率低意味着漏检多。
F1 分数
把精确率和召回率合成一个数字,加权方式使得任何一项差都会把它拉低。
混淆矩阵
一张以真实标签为行、预测标签为列的网格,能精确显示出哪些意图正被误判成哪些意图。
工具选择准确率
智能体调用到正确函数的频率 —— 比意图准确率更接近最终结果一步。
任务成功率
最终真正解决了用户问题的请求所占的比例。这是结果指标。
错误动作率
智能体执行了一个不该执行的、会改变状态的动作的频率。这是安全指标。
任务规划
把一个请求变成一串有先后顺序、可能还带条件的步骤,而不只是一个标签。