1. 为什么需要"用户澄清"
当用户使用自然语言输入业务信息时,LLM 抽取出来的往往只是原始表达,而不是系统可直接执行的标准主数据。
例如:
- "华通" → 可能对应多个客户
- "旗舰版" → 可能对应多个产品 SKU
- "张总" → 可能对应多个联系人
- "上次那个客户" → 强依赖上下文
- "不是信息那个" → 需要基于候选上下文理解
如果系统在不确定时仍然直接采用某个结果,就会产生:
- 错误客户
- 错误产品
- 错误联系人
- 后续报价、审批、查询全链路错误
所以必须在"自动匹配不足够确定"时,引入一个受控的澄清流程:
用户澄清 = 在实体匹配不确定时,系统主动让用户确认最小必要信息,以保证后续业务执行的准确性。
2. 什么时候触发澄清
推荐把匹配结果统一分成三类:
2.1 confirmed
高置信唯一命中,可自动采用。
例如:
- 用户说"华通科技有限公司"
- 系统唯一命中
cust_001
策略:
- 直接写入草稿
- 不打断用户
2.2 ambiguous
存在多个合理候选,需要用户确认。
例如:
- "华通" →
- 华通科技有限公司
- 华通信息技术有限公司
策略:
- 进入用户澄清流程
- 提供候选列表
- 等待用户选择
2.3 failed
未找到候选,或候选置信度太低。
例如:
- "至尊版"在产品库里不存在
- "老王公司"无法匹配客户
策略:
- 不进入"候选选择型澄清"
- 而是提示用户补充更完整信息
3. 澄清流程整体设计
标准流程建议如下:
text
用户输入
↓
字段抽取
↓
主数据匹配
↓
结果判断
├── confirmed → 自动采用并继续
├── ambiguous → 发起澄清
└── failed → 请求补充
如果触发 ambiguous:
text
系统生成候选问题
↓
保存 pending_resolution
↓
用户回复澄清信息
↓
解析用户选择
├── 解析成功 → 写回草稿并继续
└── 解析失败 → 再提示用户
4. 澄清状态存储设计
用户澄清必须有显式状态,不能仅依赖聊天上下文。
推荐在 session / conversation context 里存:
json
{
"active_draft_id": "qd_123",
"pending_resolution": {
"entity_type": "customer",
"raw_value": "华通",
"candidates": [
{"id": "cust_001", "name": "华通科技有限公司", "score": 0.91},
{"id": "cust_002", "name": "华通信息技术有限公司", "score": 0.88}
]
}
}
4.1 pending_resolution 的作用
它的作用是:
- 告诉系统:当前这一轮用户输入优先被解释为"候选澄清"
- 提供候选上下文,让
"1"/"第一个"/"科技那个"有可解释对象 - 在澄清成功后,把正确实体写回草稿
- 在澄清失败时,引导用户继续澄清
5. 澄清优先级设计策略
这是整个澄清模块里非常关键的一部分。
如果一轮输入中同时出现多个歧义项,系统不能一次性全部抛给用户,而应采用:
一次只澄清一个最关键的歧义项
这就是"澄清优先级设计策略"。
5.1 为什么必须有优先级策略
假设用户输入:
给华通做报价,产品是标准服务包,联系人是张总
系统匹配后可能得到:
- 客户
华通→ ambiguous - 产品
标准服务包→ ambiguous - 联系人
张总→ failed / ambiguous
如果没有优先级策略,系统可能同时问:
- 客户选哪个?
- 产品选哪个?
- 联系人是谁?
这会带来几个问题:
- 用户认知负担太高
- 用户回复可能只回答其中一个,系统难以判断
- 多个问题混在一轮里,回复解析难度剧增
- 很多歧义其实存在依赖关系,应该先解上游
5.2 优先级策略的目标
澄清优先级策略的目标不是"把所有歧义都列出来",而是:
- 在多个歧义项中找到下一步最值得问的一个
- 让系统每一轮只推进一个最关键的不确定点
- 保持对话简单、可控、可恢复
5.3 推荐优先级原则
推荐按以下思路确定优先级:
原则 A:先上游,后下游
如果某个实体的确认结果会影响其他实体匹配,就优先澄清它。
例如:
- 客户会影响联系人匹配
- 客户会影响产品可售范围
- 客户会影响价格表
所以通常:
text
customer > contact > product
或者:
text
customer > product > contact
要根据你的业务来定。
原则 B:先主实体,后附属实体
报价场景里,通常主实体优先级更高:
- customer
- product
- contact
- delivery_location
- remarks
原则 C:先阻断型歧义,后非阻断型歧义
如果某个歧义会阻止整个流程继续,那它优先级更高。
例如:
- 客户没确认,不能继续建报价
- 联系人没确认,也许还能先继续
- remarks 不明确,可能完全不影响
原则 D:优先问"用户最容易回答"的问题
如果两个歧义都很关键,优先问更容易让用户直接选出的那一个。
例如:
- 两个客户名差异明显,用户容易选
- 两个产品版本差异复杂,用户不一定清楚
这种情况下可以优先问客户。
5.4 一个实用的默认优先级顺序
在报价类系统里,推荐默认优先级:
text
1. customer
2. product
3. contact
4. delivery / address
5. other optional fields
原因:
- 客户通常是报价上下文的根节点
- 产品决定报价项主体
- 联系人通常依赖客户
- 其他字段很多时候不是阻断项
5.5 建议把"first_ambiguous"升级成策略函数
不要只把它理解成"返回第一个歧义项"。
更准确的设计应该是:
python
get_next_resolution_target(bundle, current_draft, business_context)
它的职责是:
- 从多个 ambiguous / failed 中选出当前应优先处理的目标
- 依据业务优先级和依赖关系做决策
5.6 设计建议
建议 1:优先级策略由代码实现,不交给 LLM
因为这是业务流程控制,不是开放式推理。
建议 2:优先级策略要可配置
可以做成:
python
PRIORITY_ORDER = ["customer", "product", "contact"]
建议 3:后续可以升级成规则引擎
比如:
- 如果 contact 依赖 customer 未确认,则 contact 自动降级
- 如果 product 依赖 pricebook,而 pricebook 依赖 customer,则 customer 必须先确认
5.7 一句话总结
澄清优先级设计策略的本质,是在多个待澄清项中,决定"下一轮只问哪一个最关键的问题"。
6. 用户澄清输入的典型形式
用户在澄清时通常不会总是说标准表达。常见输入可以分成四类:
6.1 序号类
12第一个第1个选第二个我选第一个
适合规则解析。
6.2 完整名称类
华通科技有限公司标准服务包-高级版
适合规则解析。
6.3 部分名称类
华通科技高级版
适合规则 + 子串 / 弱匹配。
6.4 模糊表达类
科技那个不是信息那个我要第一个公司不是第二个上面做软件的那个
这类适合 LLM 兜底解析。
7. 用户澄清解析策略:规则优先 + LLM 兜底
这是推荐的总体方案:
7.1 第一层:规则解析
优先解析明确表达:
序号
1第一个第1个
精确名称
- 完整命中候选名称
唯一子串
- 候选中只有一个包含该子串
规则优点:
- 快
- 稳定
- 可审计
- 成本低
7.2 第二层:LLM 兜底解析
当规则无法判断时,再走 LLM。
适合:
科技那个不是信息那个我要做软件的那家不是第二个,是前面那个
LLM 的职责不是自由猜测,而是:
在给定候选列表中,做受限选择。
7.3 第三层:仍然无法确定就 unresolved
如果规则和 LLM 都无法可靠判断,就返回 unresolved,并再次提示用户。
原则:
宁可 unresolved,也不要误选。
8. LLM 兜底提示词设计
8.1 LLM 兜底提示词的设计目标
这个提示词的任务不是开放生成,而是一个受限选择任务:
- 输入:用户澄清语句 + 候选列表
- 输出:在候选列表中选一个,或者 unresolved
所以 prompt 必须强调:
- 只能从候选列表中选
- 不能编造新的候选
- 不确定时返回 unresolved
- 输出必须结构化
8.2 中文提示词模板(推荐版)
text
你是一个"候选项澄清解析模块"。
任务:
根据用户的澄清语句,从给定候选列表中判断用户指的是哪一个候选项。
请严格遵守以下规则:
1. 只能从候选列表中选择;
2. 不能编造新的候选项;
3. 如果无法可靠判断,必须返回 unresolved;
4. 不要凭空推断用户意图;
5. 输出必须是合法 JSON,且不能输出额外解释。
输出格式:
{
"status": "confirmed | unresolved",
"selected_id": "候选ID或null",
"reason": "简短原因"
}
候选列表:
{{candidates}}
用户澄清语句:
{{user_input}}
8.3 更保守版提示词(推荐生产默认)
如果你想让系统更稳,可以用更保守的版本:
text
你是一个"候选项澄清解析模块"。
你只能做以下两件事之一:
1. 从候选列表中明确选择一个候选项;
2. 返回 unresolved。
请严格遵守:
1. 只能使用候选列表和用户澄清语句中的信息;
2. 禁止根据常识扩展、脑补或猜测;
3. 如果用户表达不够明确,必须返回 unresolved;
4. 如果多个候选都可能符合,必须返回 unresolved;
5. 只返回 JSON,不要输出任何额外说明。
输出格式:
{
"status": "confirmed | unresolved",
"selected_id": "候选ID或null",
"reason": "简短原因"
}
候选列表:
{{candidates}}
用户澄清语句:
{{user_input}}
8.4 示例
候选列表:
json
[
{"id": "cust_001", "name": "华通科技有限公司"},
{"id": "cust_002", "name": "华通信息技术有限公司"}
]
用户说:
不是信息那个
期望输出:
json
{
"status": "confirmed",
"selected_id": "cust_001",
"reason": "用户排除了信息公司,因此选择科技公司"
}
如果用户说:
就那个吧
期望输出:
json
{
"status": "unresolved",
"selected_id": null,
"reason": "用户表达过于模糊,无法确定具体候选"
}
8.5 实现建议
LLM 兜底模块建议单独封装成:
python
resolve_pending_choice_by_llm(user_input, candidates)
这样好处是:
- 易于替换模型
- 易于监控命中率
- 易于统计哪些场景规则没覆盖
- 后续容易持续优化
9. 用户澄清实现要点
9.1 保留 raw value 和 resolved value
例如:
json
{
"customer": {
"raw_name": "华通",
"resolved_id": "cust_001",
"resolved_name": "华通科技有限公司",
"resolution_status": "confirmed"
}
}
这样便于:
- 审计
- 调试
- 回显
- 复盘错误
9.2 一次只澄清一个问题
这是最重要的交互原则之一。
9.3 高置信命中自动通过
不要让系统显得太"笨"。
9.4 候选数量要控制
一般建议:
- 2~5 个
- 超过这个数量,应该优化召回/重排,而不是让用户承担负担
9.5 记录解析来源
建议在结果中保留:
json
"source": "rule:index"
或:
json
"source": "llm"
这样便于:
- 监控
- 调优
- 审计
- 回放问题
9.6 澄清成功后必须清理 pending_resolution
否则下一轮会误判。
9.7 澄清完成后要回到主业务流程
也就是:
- 写回 draft
- 重新 validate
- 继续输出摘要 / 等待确认
10. 常见坑
坑 1:把所有澄清都交给 LLM
问题:
- 成本高
- 延迟高
- 不可控
- 难审计
坑 2:没有 pending_resolution
问题:
- 下一轮
1不知道在选什么 - 系统状态混乱
坑 3:一次问多个歧义问题
问题:
- 用户负担大
- 回复难解析
- 流程变脆
坑 4:规则和 LLM 都无法判断时仍然强选
问题:
- 这是业务系统里的高风险错误
坑 5:不区分 ambiguous 和 failed
问题:
- 有候选和没候选,本质上是两种不同交互
坑 6:不保留 raw value
问题:
- 后续很难解释"为什么系统匹配成这个实体"
11. 推荐模块划分
建议分成四层:
11.1 Resolver Tools
负责主数据匹配。
11.2 ResolutionService
负责:
- 汇总匹配结果
- 判断 confirmed / ambiguous / failed
- 应用澄清优先级策略
- 处理
pending_resolution - 调用规则或 LLM 完成候选确认
11.3 SessionStore
负责:
active_draft_idpending_resolution
11.4 ChatOrchestrator
负责:
- 判断当前是在正常业务流程还是澄清流程
- 调用 ResolutionService
- 把确认结果写回草稿
- 恢复主流程
12. 一句话总结
用户澄清的核心设计,是在主数据匹配不够确定时,通过"澄清优先级设计策略"选出当前最关键的待确认实体,用
pending_resolution保存上下文,并通过"规则优先 + LLM 兜底"的方式解析用户回复,在确认后把标准化结果写回草稿,再继续主业务流程。
13. 极简落地 checklist
如果你要快速落地,最少做到这 8 条:
- 匹配结果区分
confirmed / ambiguous / failed - session 中保存
pending_resolution - 一次只澄清一个实体
- 明确实现 澄清优先级设计策略
- 用户澄清解析采用 规则优先
- 规则失败后采用 LLM 兜底提示词
- 无法确定时返回
unresolved - 成功确认后写回 draft,并清理 pending 状态