传统 NLU 模型靠语料喂养意图,写几条样本句就指望模型猜出用户心思——结果往往是意图混淆、槽位漏抓、上线后疯狂补样本。Amazon Lex 推出的 Assisted NLU 改变了这条老路:通过意图和槽位的结构化描述,让模型在训练阶段就"读懂"每个意图的业务语义,而不是仅靠样本句做模式匹配。下面拆解具体做法。
意图描述:把业务语义写进模型
Assisted NLU 的核心变化是——意图不再只是一组样本句,它有了一段自然语言描述。这段描述直接参与模型训练,相当于给模型一份"岗位说明书"。
写好意图描述的关键原则:
- 说清楚意图触发场景:用户在什么情境下会表达这个意图?
- 区分边界:明确这个意图不覆盖什么,减少与相邻意图的混淆。
- 用业务术语:避免泛泛而谈,用你的领域词汇让模型理解上下文。
举个例子,一个订餐机器人里 OrderFood 和 ReserveTable 容易打架。传统做法只能多写样本句硬拉距离;用 Assisted NLU,给每个意图加一段描述就能大幅降低误判率。
槽位描述:让模型知道该抓什么
槽位描述同样参与训练。一个 DeliveryTime 槽位,如果只挂一个时间类型,模型不知道用户说"明天下午三点"时到底要抓什么。加上描述:"用户期望的送餐时间,包含日期和时间信息",模型就能更精准地从模糊表述中提取目标字段。
槽位描述的写法要点:
- 指明槽位在业务流程中的角色("这是用户选择的配送地址,不是收据地址")。
- 对枚举型槽位,在描述中列举关键取值及其含义。
- 对复合信息(地址、时间),说明期望的粒度。
用 Test Workbench 验证效果
写完描述不代表万事大吉,必须验证。Lex 的 Test Workbench 是专门为此设计的:批量输入测试 utterance,看意图识别和槽位提取是否命中预期。
验证流程建议:
- 先用传统 NLU(不加描述)跑一批 baseline 测试,记录准确率。
- 加入意图和槽位描述,重新训练,再跑同一批测试。
- 对比混淆矩阵,重点看之前容易交叉的意图是否分离了。
- 对仍误判的 utterance,针对性补充描述细节,而非堆样本句。
实操示例:用 boto3 创建带描述的意图
下面是一个完整的 Python 脚本,用 AWS SDK 为 Lex V2 bot 创建一个带 Assisted NLU 描述的意图。替换 BOT_ID、BOT_VERSION 和 INTENT_ID 后可直接运行。
import boto3
client = boto3.client('lexv2-models')
# ---- 配置参数(运行前替换为你的实际值)----
BOT_ID = 'YOUR_BOT_ID'
BOT_VERSION = 'DRAFT'
INTENT_ID = 'YOUR_INTENT_ID'
# ---- 创建 / 更新意图,附带 Assisted NLU 描述 ----
response = client.update_intent(
botId=BOT_ID,
botVersion=BOT_VERSION,
intentId=INTENT_ID,
intentName='OrderFood',
description='用户想要下单购买餐食,包含选择菜品、指定配送地址和时间。不包含预约座位或查询菜单。', # 意图描述
sampleUtterances=[
{'utterance': '我想点一份炸鸡套餐'},
{'utterance': '帮我下单两份披萨送到公司'},
{'utterance': '订餐,红烧肉盖饭'},
],
intentConfirmationSetting={
'promptSpecification': {
'messageGroups': [{
'message': {
'plainTextMessage': {'value': '确认下单:{MenuItem} 送至 {DeliveryAddress},预计 {DeliveryTime} 送达,对吗?'}
}
}],
'allowInterrupt': True
}
},
slotPriorities=[
{'priority': 1, 'slotId': 'SLOT_MENU_ITEM'},
{'priority': 2, 'slotId': 'SLOT_DELIVERY_ADDRESS'},
{'priority': 3, 'slotId': 'SLOT_DELIVERY_TIME'},
]
)
print(f'意图更新完成: {response["intentName"]}')
# ---- 更新槽位,附带槽位描述 ----
slots = [
{
'slot_id': 'SLOT_MENU_ITEM',
'name': 'MenuItem',
'description': '用户选择的具体菜品名称,如"炸鸡套餐"、"红烧肉盖饭"。不含数量信息。',
'type': 'AMAZON.AlphaNumeric',
},
{
'slot_id': 'SLOT_DELIVERY_ADDRESS',
'name': 'DeliveryAddress',
'description': '用户期望的送餐地址,包含街道、门牌号和楼层。这是配送目的地,不是账单地址。',
'type': 'AMAZON.AlphaNumeric',
},
{
'slot_id': 'SLOT_DELIVERY_TIME',
'name': 'DeliveryTime',
'description': '用户期望的送餐时间,包含日期和具体时刻,如"明天下午三点"、"今晚七点"。',
'type': 'AMAZON.DateTime',
},
]
for slot in slots:
client.update_slot(
botId=BOT_ID,
botVersion=BOT_VERSION,
intentId=INTENT_ID,
slotId=slot['slot_id'],
slotName=slot['name'],
description=slot['description'], # 槽位描述——Assisted NLU 的关键输入
slotTypeId=slot['type'],
)
print(f'槽位更新完成: {slot["name"]}')
运行前确保:
- boto3 已安装(
pip install boto3)。 - AWS 凭证已配置,且有 Lex V2 的写入权限。
BOT_ID、INTENT_ID、各SLOT_ID替换为你环境中已有的实际 ID(可先通过list_intents获取)。
更新完成后,在 Lex 控制台触发构建(Build),再进入 Test Workbench 执行批量验证。
迁移策略:从传统 NLU 切换到 Assisted NLU
新 Bot
直接启用 Assisted NLU,从第一个意图就写描述。好处是模型从一开始就有语义锚点,后续加意图时不容易与已有意图混淆。
已有 Bot
不建议一步到位全量切换。推荐路径:
- 挑最痛的意图先改——找到线上混淆率最高的意图对,先为它们加描述。
- A/B 验证——用 Test Workbench 对改前改后跑同一批 utterance,确认准确率提升。
- 逐步扩展——验证有效后,按意图分组逐步补描述,每次构建后都跑测试。
- 保留样本句——Assisted NLU 不是替代样本句,而是补充。描述+样本句的组合效果优于单独使用任一项。
需要注意的边界
- 描述长度有上限(意图描述建议控制在 200 词以内,过长反而稀释关键信息)。
- 描述的语言应与 Bot 的主要交互语言一致——中文 Bot 写中文描述,别写英文。
- 槽位描述对枚举型槽位效果最显著,对自由文本型槽位提升有限,别过度投入。
上线前的检查清单
| 检查项 | 说明 |
|---|---|
| 每个意图都有描述 | 描述中包含触发场景和边界排除 |
| 易混淆意图的描述有明确区分 | 对比相邻意图描述,确保语义不重叠 |
| 每个槽位都有业务角色描述 | 说明"这是什么"而非"这是什么类型" |
| Test Workbench baseline 已建立 | 传统 NLU 下的准确率数据留档 |
| 改动后重新构建并批量测试 | 不构建等于没训练 |
| 描述语言与 Bot 交互语言一致 | 中文 Bot 用中文描述 |
Assisted NLU 把"写样本句"的体力活变成了"写业务描述"的设计活——投入相同的时间,回报更稳定。下次面对意图混淆的烂摊子,先别急着堆样本句,试试把意图到底干什么用一句话写清楚。