fix(prompts): accept conversational time confirmations and improve clarification

This commit is contained in:
Eric Wang
2026-10-28 17:46:22 +08:00
parent ddcfe5bd05
commit 89acf4f617
10 changed files with 216 additions and 53 deletions
+7 -7
View File
@@ -1,6 +1,6 @@
# 评估案例与分组运行
`evals/cases/` 当前保留52个完整对话案例、8个目录分组。每个文件独立创建会话,检查阶段码,以及可选的表单、回复断言。
`evals/cases/` 当前保留54个完整对话案例、8个目录分组。每个文件独立创建会话,检查阶段码,以及可选的表单、回复断言。
- `python -m evals.run`:通过HTTP调用已经启动的API服务,可测试FastGPT或LangGraph后端。
- `python -m evals.prompt_compare`:在进程内启动生产文本路由和后端生命周期,使用配置中的真实LLM测试本地LangGraph,不初始化无关语音模型。
@@ -31,7 +31,7 @@ conda run -n py3.11 python -m evals.prompt_compare --group complex_vehicle --lis
| `customer_feedback` | 4 | 客户原始反馈、连续呼叫、已知信息不重复问 |
| `non_motor_vehicle` | 14 | 非机动车参与、零机动车提取、保留已知事实、类型更正 |
| `no_false` | 2 | ASR噪声不误转人工 |
| `time_confirmation` | 2 | 独立确认完整时间、补充信息保留、更正后重新确认 |
| `time_confirmation` | 4 | 独立确认完整时间、补充信息保留、更正后重新确认,以及“可以”认可后不重复问 |
| `photo_button_guidance` | 12 | 单双车按钮指引、失败处理、不重复说明 |
| `photo_skip_plate_confirm` | 4 | 当前拍照步骤转换,不额外确认车牌 |
@@ -39,13 +39,13 @@ conda run -n py3.11 python -m evals.prompt_compare --group complex_vehicle --lis
| 测试集 | 两种模式合计 | 适用场景 |
| --- | ---: | --- |
| `smoke` | 12 | 修改后的快速回归;配合`--mode direct`仅6个案例 |
| `collection` | 36 | 采集提示词、表单保持、时间与事故分流 |
| `smoke` | 14 | 修改后的快速回归;配合`--mode direct`仅7个案例 |
| `collection` | 38 | 采集提示词、表单保持、时间与事故分流 |
| `vehicle_count` | 22 | 数量推断、零机动车、类型更正、三车和客户反馈 |
| `safety` | 20 | 复杂事故、非机动车、ASR噪声、不确定人伤 |
| `photo` | 16 | 拍照节点和按钮交互 |
| `feedback` | 6 | 客户反馈,包括三车立即转人工 |
| `full` | 52 | 全量回归;不提供筛选条件也执行全量 |
| `feedback` | 8 | 客户反馈,包括三车立即转人工、时间认可不重复问 |
| `full` | 54 | 全量回归;不提供筛选条件也执行全量 |
详细场景索引及保留历史文件名的原因见[cases/README.md](cases/README.md)。
@@ -92,7 +92,7 @@ conda run -n py3.11 python -m evals.prompt_compare --suite full --workers 4 --ou
- 未知分组、未知测试集或空选择返回配置错误,避免把“没有跑任何案例”当作成功。
- 案例名必须唯一,避免输出文件相互覆盖。
此次整理未删除、移动或改写任何原案例。核对结果:同一客户端模式内,没有输入和断言完全相同的重复文件,也没有完整输入序列相同的文件。direct和browser_addon的相似案例验证不同请求模式与阶段码,保留为独立覆盖;只想测一种模式时使用`--mode`减少执行量。共同的欢迎和时间确认轮次是对话上下文的一部分,不单独抽掉。
案例整理时未删除、移动或改写当时52个原案例;之后新增两份时间认可回归案例,当前共54份。核对结果:同一客户端模式内,没有输入和断言完全相同的重复文件,也没有完整输入序列相同的文件。direct和browser_addon的相似案例验证不同请求模式与阶段码,保留为独立覆盖;只想测一种模式时使用`--mode`减少执行量。共同的欢迎和时间确认轮次是对话上下文的一部分,不单独抽掉。
## 新增案例与断言
+3 -2
View File
@@ -1,6 +1,6 @@
# 对话案例索引
本目录保留52个YAML案例,输入、断言和文件路径均保持原样。执行入口、分组参数及断言格式见[评估说明](../README.md)。跨目录测试集定义在[../suites.yaml](../suites.yaml),不会复制这里的案例。
本目录保留54个YAML案例,输入、断言和文件路径均保持原样。执行入口、分组参数及断言格式见[评估说明](../README.md)。跨目录测试集定义在[../suites.yaml](../suites.yaml),不会复制这里的案例。
## 信息采集
@@ -11,6 +11,7 @@
| 同上 | `dual_unclear_injury` | 人伤不确定先澄清,再使用已有信息 |
| 同上 | `single_no_inferred_count` | 历史文件名;最新边界已改为倒车撞固定物提取1,不再问数量 |
| `time_confirmation` | `correction` | 首次确认、补充信息、时间更正和再次确认 |
| 同上 | `acceptance` | 时间确认回答“可以”后直接补问现场,不重复时间确认 |
| `customer_feedback` | `repeated_question` | 客户原始多轮反馈,单车描述提取1,后续不重复问数量 |
| 同上 | `known_fields` | 时间确认与抱怨一起表达时,仍保留已知信息 |
@@ -40,7 +41,7 @@
## 常用选择
```bash
# 快速回归,6个direct案例
# 快速回归,7个direct案例
conda run -n py3.11 python -m evals.run --suite smoke --mode direct --list
# 只看三车事故
@@ -0,0 +1,27 @@
name: browser_addon_time_confirmation_acceptance
clientMode: browser_addon
# 客户反馈:“可以”认可时间后不得继续重复确认;只补问现场。
steps:
- input: 新对话
expect: {stageCode: "browser_addon.1001"}
- input: 【继续办理】
expect: {stageCode: "browser_addon.1002"}
- input: 我的车子是自己的小轿车,撞到了路边围栏。没有人员受伤,没有涉及非机动车,也没有行人。时间是2026年10月27日十四点左右。
expect:
stageCode: "browser_addon.1002"
form: {vehicle_count: 1, has_injury: false, has_non_motor_vehicle_hit: false, accident_time: "2026年10月27日14点0分"}
formAbsent: [is_on_scene]
replyMatches: "我记录的时间是2026年10月27日14点00分.*这个时间对吗"
replyNotMatches: "现场|拍照"
- input: 可以。
expect:
stageCode: "browser_addon.1002"
replyMatches: "(在.*现场|现场.*吗)"
replyNotMatches: "(这个时间对吗|几点|什么时候|再次确认|再确认.*时间)"
formAbsent: [is_on_scene]
- input: 我现在还在事故现场。
expect:
stageCode: "browser_addon.2000"
form: {vehicle_count: 1, is_on_scene: true, accident_time: "2026年10月27日14点0分"}
replyNotMatches: "(这个时间对吗|几点|什么时候|几辆|多少辆)"
@@ -0,0 +1,27 @@
name: direct_time_confirmation_acceptance
clientMode: direct
# 客户反馈:“可以”认可时间后不得继续重复确认;只补问现场。
steps:
- input: 新对话
expect: {stageCode: "1001"}
- input: 【继续办理】
expect: {stageCode: "1002"}
- input: 我的车子是自己的小轿车,撞到了路边围栏。没有人员受伤,没有涉及非机动车,也没有行人。时间是2026年10月27日十四点左右。
expect:
stageCode: "1002"
form: {vehicle_count: 1, has_injury: false, has_non_motor_vehicle_hit: false, accident_time: "2026年10月27日14点0分"}
formAbsent: [is_on_scene]
replyMatches: "我记录的时间是2026年10月27日14点00分.*这个时间对吗"
replyNotMatches: "现场|拍照"
- input: 可以。
expect:
stageCode: "1002"
replyMatches: "(在.*现场|现场.*吗)"
replyNotMatches: "(这个时间对吗|几点|什么时候|再次确认|再确认.*时间)"
formAbsent: [is_on_scene]
- input: 我现在还在事故现场。
expect:
stageCode: "2000"
form: {vehicle_count: 1, is_on_scene: true, accident_time: "2026年10月27日14点0分"}
replyNotMatches: "(这个时间对吗|几点|什么时候|几辆|多少辆)"
+44 -2
View File
@@ -26,7 +26,49 @@ def probes():
confirmed = [{'role': 'assistant', 'content': CONFIRM}, {'role': 'user', 'content': '可以。'}]
known_form = dict(accident_cause='其他:车辆碰撞', has_injury=False,
has_non_motor_vehicle_hit=False, accident_time=TIME, is_on_scene=True)
time_ack_history = [
{'role': 'user', 'content': '我的小轿车撞到了围栏,无人受伤,也没有非机动车或行人。'},
{'role': 'assistant', 'content': CONFIRM},
]
return [
*[dict(name='time_ack_' + name, user=answer, history=time_ack_history,
code='1002', matches='(在.*现场|现场.*吗)',
not_matches='(这个时间对吗|请.*确认.*时间|几点|什么时候|拍照)')
for name, answer in [('keyi', '可以。'), ('xing', '行。'), ('haode', '好的。'),
('meiwenti', '没问题。'), ('en_dui', '嗯,对。')]],
dict(name='time_ack_ready_to_photo', user='可以。', history=[
{'role': 'user', 'content': '我的小轿车撞到了围栏,无人受伤,也没有非机动车或行人,我在现场。'},
{'role': 'assistant', 'content': CONFIRM}], code='2000', count=1,
not_matches='(这个时间对吗|请.*确认.*时间|几点|什么时候)'),
dict(name='time_ack_recover_duplicate', user='可以。', history=[
*time_ack_history, {'role': 'user', 'content': '可以。'},
{'role': 'assistant', 'content': CONFIRM}], code='1002',
matches='(在.*现场|现场.*吗)', not_matches='(这个时间对吗|几点|什么时候|拍照)'),
dict(name='time_ack_uncertain', user='可以吧,但时间我还不能确定。', history=time_ack_history,
code='1002', matches='(时间|几点|什么时候)', not_matches='(现场|拍照)'),
dict(name='time_ack_correction', user='可以,不过时间不是这个,是2026年10月22日11点40分。',
history=time_ack_history, code='1002',
matches='我记录的时间是2026年10月22日11点40分.*这个时间对吗', not_matches='(现场|拍照)'),
dict(name='time_ack_other_subject', user='可以拍照,我在现场。', history=time_ack_history,
code='1002', matches='我记录的时间是.*这个时间对吗', not_matches='接下来.*拍照'),
dict(name='clarify_non_motor_changes_question', user='没看清,我还是不知道有没有碰到。', history=[
{'role': 'user', 'content': '两辆汽车追尾,大家都没受伤。'},
{'role': 'assistant', 'content': '<state>1002</state>请问事故中是否涉及非机动车或行人呢?'},
{'role': 'user', 'content': '好像有,我没看清。'},
{'role': 'assistant', 'content': '<state>1002</state>请问事故中是否涉及非机动车或行人呢?'}],
code='1002', non_motor=None, matches='(自行车|电动|行人|确定|回忆|人工|看清)',
not_matches='请问事故中是否涉及非机动车或行人呢'),
dict(name='clarify_time_changes_question', user='我还是想不起来。', history=[
*time_ack_history, {'role': 'user', 'content': '这个时间我记不清了。'},
{'role': 'assistant', 'content': CONFIRM}], code='1002',
matches='(时间|几点|回忆|确定|人工|大概)', not_matches='我记录的时间是.*这个时间对吗'),
dict(name='clarify_injury_preserves_fields', user='我看不清,还是不能确定。', history=[
{'role': 'assistant', 'content': '<state>1002</state>请问这次事故中,有没有人员受伤呢?'},
{'role': 'user', 'content': '不知道。'},
{'role': 'assistant', 'content': '<state>1002</state>请问这次事故中,有没有人员受伤呢?'}],
form={'vehicle_count': 1, 'accident_cause': '倒车'}, code='1002', count=1, injury=None,
matches='(受伤|不舒服|身体|伤情|人工|安全|救护车|送医)',
not_matches='(请问这次事故中,有没有人员受伤呢|几辆|多少辆)'),
dict(name='complex_three_original', user='在高速上,三辆车子追尾了。', history=[],
code='0003', count=3, not_matches='(请问|是否.*[??]|拍照|这个时间对吗)',
matches='三辆机动车追尾.*复杂事故.*立即.*转接人工警员'),
@@ -65,7 +107,7 @@ def probes():
dict(name='feedback_unknown_count_acknowledge_history', user=complaint,
history=[{'role': 'user', 'content': fixed_object}, *confirmed, count_question],
code='1002', count_absent=True,
matches='(其他|还有|几辆|多少辆)',
matches='(其他|还有|几辆|多少辆|旁边|那辆|除了)',
not_matches='(没.*(看到|收到)|未.*(提供|回答)|具体的数字)'),
dict(name='feedback_known_count_in_history', user=complaint,
history=[{'role': 'user', 'content': '只有我这一辆小轿车撞到围栏。没有人受伤,没有非机动车或行人,我还在现场。'}, *confirmed, count_question],
@@ -119,7 +161,7 @@ def probes():
dict(name='uncertain_non_motor', user='好像有电瓶车,我没看清。', history=[
{'role':'user','content':'两辆汽车追尾,人员都没事。'},
{'role':'assistant','content':'<state>1002</state>请问事故中是否涉及非机动车或行人呢?'}],
code='1002', non_motor=None, contains='非机动车'),
code='1002', non_motor=None, matches='(非机动车|电瓶车|电动自行车|自行车|行人)'),
dict(name='single_reverse_count_after_time', user='对', history=[
{'role':'user','content':'十分钟前我倒车撞到护栏,没有人受伤,没有非机动车或行人,我还在现场。'},
{'role':'assistant','content':CONFIRM}], code='2000', count=1,
+2 -1
View File
@@ -38,10 +38,11 @@ photo:
- photo_button_guidance/*.yaml
- photo_skip_plate_confirm/*.yaml
feedback:
description: 客户反馈回归,包括已知信息不重复问和三车立即转人工
description: 客户反馈回归,包括已知信息不重复问、三车立即转人工和时间认可不重复问
include:
- customer_feedback/*.yaml
- complex_vehicle/*.yaml
- time_confirmation/*_acceptance.yaml
full:
description: 全部案例,等价于不指定测试集
include: