From 248eb41c78011db1e90ea4d334389fc41885845e Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Wed, 21 Oct 2026 15:34:11 +0800 Subject: [PATCH] fix(workflow): align non-motor accident extraction and evaluations --- evals/README.md | 69 +- evals/cases/browser_addon/initial.yaml | 67 - .../browser_addon_dual_all_info.yaml | 12 +- .../browser_addon_dual_bundle_remaining.yaml | 11 +- .../browser_addon_dual_fill_missing.yaml | 15 +- .../browser_addon_dual_unclear_injury.yaml | 12 +- .../browser_addon_single_all_info.yaml | 12 +- ...rowser_addon_single_no_inferred_count.yaml | 13 +- .../direct_dual_all_info.yaml | 12 +- .../direct_dual_bundle_remaining.yaml | 11 +- .../direct_dual_fill_missing.yaml | 15 +- .../direct_dual_unclear_injury.yaml | 12 +- .../direct_single_all_info.yaml | 12 +- .../direct_single_no_inferred_count.yaml | 13 +- evals/cases/direct/initial.yaml | 103 - .../no_false/confirm_dual_no_false_0002.yaml | 89 - .../no_false/confirm_dual_no_false_0003.yaml | 81 - .../confirm_single_no_false_0002.yaml | 81 - .../confirm_single_no_false_0003.yaml | 73 - .../no_false/photo_dual_no_false_0002.yaml | 77 - .../no_false/photo_single_no_false_0002.yaml | 81 - .../browser_addon_all_info.yaml | 24 + .../browser_addon_electric_bicycle.yaml | 45 + .../browser_addon_explicit_correction.yaml | 33 + .../browser_addon_no_other_vehicle.yaml | 51 + .../browser_addon_self_collision.yaml | 46 + .../browser_addon_zero_count_bundle.yaml | 17 + .../browser_addon_zero_count_history.yaml | 27 + .../non_motor_vehicle/direct_all_info.yaml | 24 + .../direct_electric_bicycle.yaml | 45 + .../direct_explicit_correction.yaml | 33 + .../direct_no_other_vehicle.yaml | 51 + .../direct_self_collision.yaml | 46 + .../direct_zero_count_bundle.yaml | 17 + .../direct_zero_count_history.yaml | 27 + .../browser_addon_dual_failure.yaml | 13 +- .../browser_addon_dual_guidance.yaml | 13 +- .../browser_addon_dual_no_repeat.yaml | 11 +- .../browser_addon_single_failure.yaml | 13 +- .../browser_addon_single_guidance.yaml | 13 +- .../browser_addon_single_no_repeat.yaml | 11 +- .../direct_dual_failure.yaml | 13 +- .../direct_dual_guidance.yaml | 13 +- .../direct_dual_no_repeat.yaml | 11 +- .../direct_single_failure.yaml | 13 +- .../direct_single_guidance.yaml | 13 +- .../direct_single_no_repeat.yaml | 11 +- .../browser_addon_dual.yaml | 26 +- .../browser_addon_single.yaml | 26 +- .../photo_skip_plate_confirm/direct_dual.yaml | 28 +- .../direct_single.yaml | 26 +- .../browser_addon_correction.yaml | 37 + .../time_confirmation/direct_correction.yaml | 37 + evals/run.py | 83 +- test/test_eval_runner.py | 121 + workflow/20261021/事故信息采集2025118.json | 6573 +++++++++++++++++ 56 files changed, 7641 insertions(+), 821 deletions(-) delete mode 100644 evals/cases/browser_addon/initial.yaml delete mode 100644 evals/cases/direct/initial.yaml delete mode 100644 evals/cases/no_false/confirm_dual_no_false_0002.yaml delete mode 100644 evals/cases/no_false/confirm_dual_no_false_0003.yaml delete mode 100644 evals/cases/no_false/confirm_single_no_false_0002.yaml delete mode 100644 evals/cases/no_false/confirm_single_no_false_0003.yaml delete mode 100644 evals/cases/no_false/photo_dual_no_false_0002.yaml delete mode 100644 evals/cases/no_false/photo_single_no_false_0002.yaml create mode 100644 evals/cases/non_motor_vehicle/browser_addon_all_info.yaml create mode 100644 evals/cases/non_motor_vehicle/browser_addon_electric_bicycle.yaml create mode 100644 evals/cases/non_motor_vehicle/browser_addon_explicit_correction.yaml create mode 100644 evals/cases/non_motor_vehicle/browser_addon_no_other_vehicle.yaml create mode 100644 evals/cases/non_motor_vehicle/browser_addon_self_collision.yaml create mode 100644 evals/cases/non_motor_vehicle/browser_addon_zero_count_bundle.yaml create mode 100644 evals/cases/non_motor_vehicle/browser_addon_zero_count_history.yaml create mode 100644 evals/cases/non_motor_vehicle/direct_all_info.yaml create mode 100644 evals/cases/non_motor_vehicle/direct_electric_bicycle.yaml create mode 100644 evals/cases/non_motor_vehicle/direct_explicit_correction.yaml create mode 100644 evals/cases/non_motor_vehicle/direct_no_other_vehicle.yaml create mode 100644 evals/cases/non_motor_vehicle/direct_self_collision.yaml create mode 100644 evals/cases/non_motor_vehicle/direct_zero_count_bundle.yaml create mode 100644 evals/cases/non_motor_vehicle/direct_zero_count_history.yaml create mode 100644 evals/cases/time_confirmation/browser_addon_correction.yaml create mode 100644 evals/cases/time_confirmation/direct_correction.yaml create mode 100644 test/test_eval_runner.py create mode 100644 workflow/20261021/事故信息采集2025118.json diff --git a/evals/README.md b/evals/README.md index 7e508f3..b9bb5ed 100644 --- a/evals/README.md +++ b/evals/README.md @@ -1,12 +1,38 @@ # Workflow evaluations -The evaluation runner calls a live FastAPI service backed by FastGPT. For now, -each conversation step checks only the SSE `stage_code.nextStageCode` value. -During the run it also prints the live dialogue and saves transcripts. +The evaluation runner calls a live FastAPI service backed by FastGPT. Each +conversation step checks the SSE `stage_code.nextStageCode` value and optional +form / reply expectations. During the run it prints the dialogue and saves +transcripts. Cases target `workflow/20261021`; import that workflow into FastGPT +before running against a service. ## Case format -Add YAML files anywhere under `evals/cases/` (e.g. `direct/`, `browser_addon/`, `no_false/` for false-interrupt / false-transfer checks): +Add YAML files anywhere under `evals/cases/`: + +- `collection_skip_known_info/`: skip known fields and clarify missing information. +- `non_motor_vehicle/`: both client modes, with separate flow and extraction cases. + `all_info`, `self_collision`, `no_other_vehicle`, and `explicit_correction` + cover conversation flow and known facts. `electric_bicycle` verifies an + electric bicycle hitting a fixed object and transferring to `0003` without + asking for car counts. These cases accept the described non-motor-vehicle + collision as having `vehicle_count: 0` from the first accident description. + `zero_count_bundle` / `zero_count_history` check zero extraction and retention. + Explicit correction to one car must update the count to `1`. + Both collection and extraction prompts in `workflow/20261021` use this policy + only for an explicitly described non-motor-vehicle single-party collision with + a fixed object; other vehicle involvement or unclear relations do not imply zero. +- `time_confirmation/`: mandatory full-date confirmation, retaining supplemental + information while confirmation is pending, and confirming corrected times. +- `no_false/`: ASR noise must not cause premature transfer. +- `photo_button_guidance/`: button help and failure handling. +- `photo_skip_plate_confirm/`: current photo transitions without plate confirmation. + +Obsolete scripts requiring repeated time/count confirmations or the removed +`2004` / `2016` photo states have been removed. Photo transition cases use a +complete accident description, confirm its standardized time, then reach their +target branch. Time confirmation is mandatory even when all other information +is already known; an unchanged time is confirmed only once. ```yaml name: direct_initial_stage @@ -26,6 +52,29 @@ steps: `browser_addon`). Steps in one file share the same generated `sessionId`. Different cases always use different session IDs. +Optional `expect` fields: + +- `form`: partial equality against the cumulative form, including value types. +- `formMatches`: field-to-regex mapping for free-text descriptions. +- `formAbsent`: fields that must not have been extracted yet. +- `replyMatches` / `replyNotMatches`: required / forbidden reply regexes. + +If any step has a form expectation, the runner sends `needFormUpdate: true` +throughout that case, merges SSE `formUpdate` deltas across turns, and records +both updates and form snapshots. Thus an omitted unchanged field is accepted, +while a later incorrect overwrite fails. SSE errors also fail the step. +Existing cases without reply expectations still require manual review of their +commented wording requirements. + +```yaml + - input: 没有人员伤亡。 + expect: + stageCode: "1002" + form: {has_injury: false, has_non_motor_vehicle_hit: true} + replyMatches: "(什么时候|几点|事故.*时间)" + replyNotMatches: "(有没有|是否).*非机动车" +``` + ## Run Start the API, then execute: @@ -45,9 +94,19 @@ Run selected cases: ```bash python evals/run.py --case direct python evals/run.py --case browser_addon -python evals/run.py --case no_false +python evals/run.py --case non_motor_vehicle +python evals/run.py --case non_motor_vehicle_electric_bicycle +python evals/run.py --case non_motor_vehicle_zero_count +python evals/run.py --case time_confirmation +python evals/run.py --case photo_skip_plate_confirm ``` +There are 14 non-motor-vehicle cases: 10 flow cases and 4 zero-count extraction +cases. Both groups require `vehicle_count: 0` for the described non-motor-vehicle +collisions, while unrelated chatter must leave accident fields absent. Vehicle +type corrections, time confirmation, and transfer checks remain active. The +runner still stops each case at its first failure. + ## Dialogue and transcripts While a case runs, each step prints the user input, bot reply (`text_delta`), diff --git a/evals/cases/browser_addon/initial.yaml b/evals/cases/browser_addon/initial.yaml deleted file mode 100644 index 7a6ddde..0000000 --- a/evals/cases/browser_addon/initial.yaml +++ /dev/null @@ -1,67 +0,0 @@ -name: browser_addon_initial_stage -clientMode: browser_addon - -steps: - - input: hi - expect: - stageCode: browser_addon.1001 - - - input: 继续办理 - expect: - stageCode: browser_addon.1002 - - - input: 十分钟之前两车追尾 - expect: - stageCode: browser_addon.1002 - - - input: 没有 - expect: - stageCode: browser_addon.1002 - - - input: 没有 - expect: - stageCode: browser_addon.1002 - - - input: 十分钟之前 - expect: - stageCode: browser_addon.1002 - - - input: 对 - expect: - stageCode: browser_addon.1002 - - - input: 是的 - expect: - stageCode: browser_addon.1002 - - - input: 对 - expect: - stageCode: browser_addon.2010 - - - input: 【拍摄完成】 - expect: - stageCode: browser_addon.2011 - - - input: 【拍摄完成】 - expect: - stageCode: browser_addon.2012 - - - input: 【拍摄完成】 - expect: - stageCode: browser_addon.2013 - - - input: 【拍摄完成】 - expect: - stageCode: browser_addon.2014 - - - input: 【拍摄完成】 - expect: - stageCode: browser_addon.2015 - - - input: 【拍摄完成】 - expect: - stageCode: browser_addon.2016 - - - input: 没错 - expect: - stageCode: browser_addon.0000 \ No newline at end of file diff --git a/evals/cases/collection_skip_known_info/browser_addon_dual_all_info.yaml b/evals/cases/collection_skip_known_info/browser_addon_dual_all_info.yaml index 5bf9409..69b9738 100644 --- a/evals/cases/collection_skip_known_info/browser_addon_dual_all_info.yaml +++ b/evals/cases/collection_skip_known_info/browser_addon_dual_all_info.yaml @@ -1,8 +1,8 @@ name: browser_addon_collection_skip_known_info_dual_all_info clientMode: browser_addon -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -14,7 +14,13 @@ steps: expect: stageCode: "browser_addon.1002" - # 信息齐全,直接引导双车拍照;不得追加任何已知字段的确认问题。 + # 其他信息齐全,先确认完整时间;确认后直接双车拍照。 - input: 十分钟前两辆汽车追尾,没有人员受伤,没有撞到非机动车或行人,我还在事故现场。 + expect: + stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2010" + diff --git a/evals/cases/collection_skip_known_info/browser_addon_dual_bundle_remaining.yaml b/evals/cases/collection_skip_known_info/browser_addon_dual_bundle_remaining.yaml index 2cf99b5..0db16f7 100644 --- a/evals/cases/collection_skip_known_info/browser_addon_dual_bundle_remaining.yaml +++ b/evals/cases/collection_skip_known_info/browser_addon_dual_bundle_remaining.yaml @@ -1,8 +1,8 @@ name: browser_addon_collection_skip_known_info_dual_bundle_remaining clientMode: browser_addon -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -19,7 +19,12 @@ steps: expect: stageCode: "browser_addon.1002" - # 本轮同时补齐三个字段,直接分流,不拆成多个确认轮次。 + # 一次补齐剩余信息,先复述完整时间等待确认。 - input: 没有撞到非机动车或行人,十分钟前发生的,我还在现场。 + expect: + stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2010" diff --git a/evals/cases/collection_skip_known_info/browser_addon_dual_fill_missing.yaml b/evals/cases/collection_skip_known_info/browser_addon_dual_fill_missing.yaml index b9f6d21..ef4ad37 100644 --- a/evals/cases/collection_skip_known_info/browser_addon_dual_fill_missing.yaml +++ b/evals/cases/collection_skip_known_info/browser_addon_dual_fill_missing.yaml @@ -1,8 +1,8 @@ name: browser_addon_collection_skip_known_info_dual_fill_missing clientMode: browser_addon -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -14,17 +14,24 @@ steps: expect: stageCode: "browser_addon.1002" - # 仅询问是否撞到非机动车,不再核对人伤、时间或两辆车的数量。 + # 先询问是否涉及非机动车,不再核对已知人伤或数量。 - input: 十分钟前两辆汽车追尾,没有人受伤。 expect: stageCode: "browser_addon.1002" - # 仅询问是否还在现场,不要求确认十分钟前的标准时间。 + # 复述换算后的完整事故时间,等待用户确认。 - input: 没有撞到非机动车,也没有行人。 expect: stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "browser_addon.1002" + # 最后补齐在场信息即分流,不再询问或确认车辆数量。 + - input: 在,我还在现场。 expect: stageCode: "browser_addon.2010" diff --git a/evals/cases/collection_skip_known_info/browser_addon_dual_unclear_injury.yaml b/evals/cases/collection_skip_known_info/browser_addon_dual_unclear_injury.yaml index 65b4ca3..f616f42 100644 --- a/evals/cases/collection_skip_known_info/browser_addon_dual_unclear_injury.yaml +++ b/evals/cases/collection_skip_known_info/browser_addon_dual_unclear_injury.yaml @@ -1,8 +1,8 @@ name: browser_addon_collection_skip_known_info_dual_unclear_injury clientMode: browser_addon -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -19,7 +19,13 @@ steps: expect: stageCode: "browser_addon.1002" - # 人伤澄清后所有字段齐全,直接双车拍照。 + # 人伤澄清后确认完整时间,用户确认后直接双车拍照。 - input: 看清楚了,没有人受伤。 + expect: + stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2010" + diff --git a/evals/cases/collection_skip_known_info/browser_addon_single_all_info.yaml b/evals/cases/collection_skip_known_info/browser_addon_single_all_info.yaml index b9d6519..d30fc45 100644 --- a/evals/cases/collection_skip_known_info/browser_addon_single_all_info.yaml +++ b/evals/cases/collection_skip_known_info/browser_addon_single_all_info.yaml @@ -1,8 +1,8 @@ name: browser_addon_collection_skip_known_info_single_all_info clientMode: browser_addon -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -14,7 +14,13 @@ steps: expect: stageCode: "browser_addon.1002" - # 信息齐全,直接引导拍摄车辆前方;不得再确认时间、数量、人伤或在场情况。 + # 其他信息齐全,先确认完整时间;确认后直接单车拍照。 - input: 十分钟前我倒车撞到护栏,事故只有一辆汽车,没有人受伤,没有撞到非机动车或行人,我还在事故现场。 + expect: + stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2000" + diff --git a/evals/cases/collection_skip_known_info/browser_addon_single_no_inferred_count.yaml b/evals/cases/collection_skip_known_info/browser_addon_single_no_inferred_count.yaml index f4c36d1..4f4aa52 100644 --- a/evals/cases/collection_skip_known_info/browser_addon_single_no_inferred_count.yaml +++ b/evals/cases/collection_skip_known_info/browser_addon_single_no_inferred_count.yaml @@ -1,8 +1,8 @@ name: browser_addon_collection_skip_known_info_single_no_inferred_count clientMode: browser_addon -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "browser_addon.1002" - # 只询问机动车数量,不能从撞护栏推断只有一辆车;不得重问其他字段。 + # 先确认完整时间,再询问缺失的机动车数量;不能从撞护栏推断只有一辆车。 - input: 十分钟前我倒车撞到了护栏,没有人受伤,没有撞到非机动车或行人,我还在现场。 expect: stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "browser_addon.1002" + # 明确数量后直接单车拍照,不再要求数量确认。 + - input: 只有一辆汽车。 expect: stageCode: "browser_addon.2000" diff --git a/evals/cases/collection_skip_known_info/direct_dual_all_info.yaml b/evals/cases/collection_skip_known_info/direct_dual_all_info.yaml index 83d724c..c4da4f3 100644 --- a/evals/cases/collection_skip_known_info/direct_dual_all_info.yaml +++ b/evals/cases/collection_skip_known_info/direct_dual_all_info.yaml @@ -1,8 +1,8 @@ name: direct_collection_skip_known_info_dual_all_info clientMode: direct -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -14,7 +14,13 @@ steps: expect: stageCode: "1002" - # 信息齐全,直接引导双车拍照;不得追加任何已知字段的确认问题。 + # 其他信息齐全,先确认完整时间;确认后直接双车拍照。 - input: 十分钟前两辆汽车追尾,没有人员受伤,没有撞到非机动车或行人,我还在事故现场。 + expect: + stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2010" + diff --git a/evals/cases/collection_skip_known_info/direct_dual_bundle_remaining.yaml b/evals/cases/collection_skip_known_info/direct_dual_bundle_remaining.yaml index 6b63f88..6f0ca9a 100644 --- a/evals/cases/collection_skip_known_info/direct_dual_bundle_remaining.yaml +++ b/evals/cases/collection_skip_known_info/direct_dual_bundle_remaining.yaml @@ -1,8 +1,8 @@ name: direct_collection_skip_known_info_dual_bundle_remaining clientMode: direct -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -19,7 +19,12 @@ steps: expect: stageCode: "1002" - # 本轮同时补齐三个字段,直接分流,不拆成多个确认轮次。 + # 一次补齐剩余信息,先复述完整时间等待确认。 - input: 没有撞到非机动车或行人,十分钟前发生的,我还在现场。 + expect: + stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2010" diff --git a/evals/cases/collection_skip_known_info/direct_dual_fill_missing.yaml b/evals/cases/collection_skip_known_info/direct_dual_fill_missing.yaml index d8842e8..e129cc5 100644 --- a/evals/cases/collection_skip_known_info/direct_dual_fill_missing.yaml +++ b/evals/cases/collection_skip_known_info/direct_dual_fill_missing.yaml @@ -1,8 +1,8 @@ name: direct_collection_skip_known_info_dual_fill_missing clientMode: direct -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -14,17 +14,24 @@ steps: expect: stageCode: "1002" - # 仅询问是否撞到非机动车,不再核对人伤、时间或两辆车的数量。 + # 先询问是否涉及非机动车,不再核对已知人伤或数量。 - input: 十分钟前两辆汽车追尾,没有人受伤。 expect: stageCode: "1002" - # 仅询问是否还在现场,不要求确认十分钟前的标准时间。 + # 复述换算后的完整事故时间,等待用户确认。 - input: 没有撞到非机动车,也没有行人。 expect: stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "1002" + # 最后补齐在场信息即分流,不再询问或确认车辆数量。 + - input: 在,我还在现场。 expect: stageCode: "2010" diff --git a/evals/cases/collection_skip_known_info/direct_dual_unclear_injury.yaml b/evals/cases/collection_skip_known_info/direct_dual_unclear_injury.yaml index efda6ba..79e3b7b 100644 --- a/evals/cases/collection_skip_known_info/direct_dual_unclear_injury.yaml +++ b/evals/cases/collection_skip_known_info/direct_dual_unclear_injury.yaml @@ -1,8 +1,8 @@ name: direct_collection_skip_known_info_dual_unclear_injury clientMode: direct -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -19,7 +19,13 @@ steps: expect: stageCode: "1002" - # 人伤澄清后所有字段齐全,直接双车拍照。 + # 人伤澄清后确认完整时间,用户确认后直接双车拍照。 - input: 看清楚了,没有人受伤。 + expect: + stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2010" + diff --git a/evals/cases/collection_skip_known_info/direct_single_all_info.yaml b/evals/cases/collection_skip_known_info/direct_single_all_info.yaml index 67bd950..299191d 100644 --- a/evals/cases/collection_skip_known_info/direct_single_all_info.yaml +++ b/evals/cases/collection_skip_known_info/direct_single_all_info.yaml @@ -1,8 +1,8 @@ name: direct_collection_skip_known_info_single_all_info clientMode: direct -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -14,7 +14,13 @@ steps: expect: stageCode: "1002" - # 信息齐全,直接引导拍摄车辆前方;不得再确认时间、数量、人伤或在场情况。 + # 其他信息齐全,先确认完整时间;确认后直接单车拍照。 - input: 十分钟前我倒车撞到护栏,事故只有一辆汽车,没有人受伤,没有撞到非机动车或行人,我还在事故现场。 + expect: + stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2000" + diff --git a/evals/cases/collection_skip_known_info/direct_single_no_inferred_count.yaml b/evals/cases/collection_skip_known_info/direct_single_no_inferred_count.yaml index a39cffc..02e3c79 100644 --- a/evals/cases/collection_skip_known_info/direct_single_no_inferred_count.yaml +++ b/evals/cases/collection_skip_known_info/direct_single_no_inferred_count.yaml @@ -1,8 +1,8 @@ name: direct_collection_skip_known_info_single_no_inferred_count clientMode: direct -# 对应 workflow/20261016 的事故信息收集提示词。 -# runner 仅校验状态码;以下注释中的提问内容需结合输出对话手动核对。 +# 对应 workflow/20261021 的事故信息收集提示词。 +# 时间确认由回复断言检查;其余注释要求结合对话核对。 steps: # 新会话返回安全准备提示。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "1002" - # 只询问机动车数量,不能从撞护栏推断只有一辆车;不得重问其他字段。 + # 先确认完整时间,再询问缺失的机动车数量;不能从撞护栏推断只有一辆车。 - input: 十分钟前我倒车撞到了护栏,没有人受伤,没有撞到非机动车或行人,我还在现场。 expect: stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "1002" + # 明确数量后直接单车拍照,不再要求数量确认。 + - input: 只有一辆汽车。 expect: stageCode: "2000" diff --git a/evals/cases/direct/initial.yaml b/evals/cases/direct/initial.yaml deleted file mode 100644 index fc1a7c2..0000000 --- a/evals/cases/direct/initial.yaml +++ /dev/null @@ -1,103 +0,0 @@ -name: direct_initial_stage -clientMode: direct - -steps: - - input: 新对话 - expect: - stageCode: "1001" - - - input: 继续办理 - expect: - stageCode: "1002" - - - input: 十分钟之前两车追尾 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 十分钟之前 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 是的 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "2010" - - - input: 【拍摄完成】 - expect: - stageCode: "2011" - - - input: 【拍摄完成】 - expect: - stageCode: "2012" - - - input: 【拍摄完成】 - expect: - stageCode: "2013" - - - input: 【拍摄完成】 - expect: - stageCode: "2014" - - - input: 【拍摄完成】 - expect: - stageCode: "2015" - - - input: 【拍摄完成】 - expect: - stageCode: "2016" - - - input: 没错 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 李四 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 移交完毕 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 刘云 - expect: - stageCode: "1002" - - - input: 对的 - expect: - stageCode: "1002" - - - input: 是 - expect: - stageCode: "0000" diff --git a/evals/cases/no_false/confirm_dual_no_false_0002.yaml b/evals/cases/no_false/confirm_dual_no_false_0002.yaml deleted file mode 100644 index bf8dffd..0000000 --- a/evals/cases/no_false/confirm_dual_no_false_0002.yaml +++ /dev/null @@ -1,89 +0,0 @@ -name: direct_confirm_dual_no_false_0002 -clientMode: direct - -# During dual-car info confirm (3002 -> external 1002), short/garbled answers -# must stay on 1002, not jump to 0002 after a few unclear turns. -steps: - - input: 新对话 - expect: - stageCode: "1001" - - - input: 继续办理 - expect: - stageCode: "1002" - - - input: 十分钟之前两车追尾 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 十分钟之前 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 是的 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "2010" - - - input: 【拍摄完成】 - expect: - stageCode: "2011" - - - input: 【拍摄完成】 - expect: - stageCode: "2012" - - - input: 【拍摄完成】 - expect: - stageCode: "2013" - - - input: 【拍摄完成】 - expect: - stageCode: "2014" - - - input: 【拍摄完成】 - expect: - stageCode: "2015" - - - input: 【拍摄完成】 - expect: - stageCode: "2016" - - - input: 没错 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 李四 - expect: - stageCode: "1002" - - - input: 这个 - expect: - stageCode: "1002" - - - input: 试试 - expect: - stageCode: "1002" - - - input: 什么意思 - expect: - stageCode: "1002" diff --git a/evals/cases/no_false/confirm_dual_no_false_0003.yaml b/evals/cases/no_false/confirm_dual_no_false_0003.yaml deleted file mode 100644 index 8ec25bc..0000000 --- a/evals/cases/no_false/confirm_dual_no_false_0003.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: direct_confirm_dual_no_false_0003 -clientMode: direct - -# During dual-car info confirm, ASR damage/garbled injury-like words must not -# jump to 0003; stay on 1002 (closed confirm / continue current question). -steps: - - input: 新对话 - expect: - stageCode: "1001" - - - input: 继续办理 - expect: - stageCode: "1002" - - - input: 十分钟之前两车追尾 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 十分钟之前 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 是的 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "2010" - - - input: 【拍摄完成】 - expect: - stageCode: "2011" - - - input: 【拍摄完成】 - expect: - stageCode: "2012" - - - input: 【拍摄完成】 - expect: - stageCode: "2013" - - - input: 【拍摄完成】 - expect: - stageCode: "2014" - - - input: 【拍摄完成】 - expect: - stageCode: "2015" - - - input: 【拍摄完成】 - expect: - stageCode: "2016" - - - input: 没错 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 不,刚摧毁了 - expect: - stageCode: "1002" - - - input: 伤……没有吧 - expect: - stageCode: "1002" diff --git a/evals/cases/no_false/confirm_single_no_false_0002.yaml b/evals/cases/no_false/confirm_single_no_false_0002.yaml deleted file mode 100644 index 6757519..0000000 --- a/evals/cases/no_false/confirm_single_no_false_0002.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: direct_confirm_single_no_false_0002 -clientMode: direct - -# During single-car info confirm (3001 -> external 1002), short/garbled answers -# must stay on 1002, not jump to 0002 after a few unclear turns. -steps: - - input: 新对话 - expect: - stageCode: "1001" - - - input: 继续办理 - expect: - stageCode: "1002" - - - input: 十分钟之前一辆车撞到护栏 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "2000" - - - input: 【拍摄完成】 - expect: - stageCode: "2001" - - - input: 【拍摄完成】 - expect: - stageCode: "2002" - - - input: 【拍摄完成】 - expect: - stageCode: "2003" - - - input: 【拍摄完成】 - expect: - stageCode: "2004" - - - input: 是的 - expect: - stageCode: "2005" - - - input: 前方 - expect: - stageCode: "1002" - - - input: 是的 - expect: - stageCode: "1002" - - - input: 张三 - expect: - stageCode: "1002" - - - input: 这个 - expect: - stageCode: "1002" - - - input: 试试 - expect: - stageCode: "1002" - - - input: 什么意思 - expect: - stageCode: "1002" diff --git a/evals/cases/no_false/confirm_single_no_false_0003.yaml b/evals/cases/no_false/confirm_single_no_false_0003.yaml deleted file mode 100644 index 5a7e27e..0000000 --- a/evals/cases/no_false/confirm_single_no_false_0003.yaml +++ /dev/null @@ -1,73 +0,0 @@ -name: direct_confirm_single_no_false_0003 -clientMode: direct - -# During single-car info confirm, ASR damage/garbled injury-like words must not -# jump to 0003; stay on 1002 (closed confirm / continue current question). -steps: - - input: 新对话 - expect: - stageCode: "1001" - - - input: 继续办理 - expect: - stageCode: "1002" - - - input: 十分钟之前一辆车撞到护栏 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "2000" - - - input: 【拍摄完成】 - expect: - stageCode: "2001" - - - input: 【拍摄完成】 - expect: - stageCode: "2002" - - - input: 【拍摄完成】 - expect: - stageCode: "2003" - - - input: 【拍摄完成】 - expect: - stageCode: "2004" - - - input: 是的 - expect: - stageCode: "2005" - - - input: 前方 - expect: - stageCode: "1002" - - - input: 是的 - expect: - stageCode: "1002" - - - input: 不,刚摧毁了 - expect: - stageCode: "1002" - - - input: 伤……没有吧 - expect: - stageCode: "1002" diff --git a/evals/cases/no_false/photo_dual_no_false_0002.yaml b/evals/cases/no_false/photo_dual_no_false_0002.yaml deleted file mode 100644 index 933933b..0000000 --- a/evals/cases/no_false/photo_dual_no_false_0002.yaml +++ /dev/null @@ -1,77 +0,0 @@ -name: direct_photo_dual_no_false_0002 -clientMode: direct - -# At dual-car plate confirm (2016), short/garbled answers must stay on 2016, -# not jump to 0002 after a few unclear turns. -steps: - - input: 新对话 - expect: - stageCode: "1001" - - - input: 继续办理 - expect: - stageCode: "1002" - - - input: 十分钟之前两车追尾 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 十分钟之前 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 是的 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "2010" - - - input: 【拍摄完成】 - expect: - stageCode: "2011" - - - input: 【拍摄完成】 - expect: - stageCode: "2012" - - - input: 【拍摄完成】 - expect: - stageCode: "2013" - - - input: 【拍摄完成】 - expect: - stageCode: "2014" - - - input: 【拍摄完成】 - expect: - stageCode: "2015" - - - input: 【拍摄完成】 - expect: - stageCode: "2016" - - - input: 这个 - expect: - stageCode: "2016" - - - input: 试试 - expect: - stageCode: "2016" - - - input: 什么意思 - expect: - stageCode: "2016" diff --git a/evals/cases/no_false/photo_single_no_false_0002.yaml b/evals/cases/no_false/photo_single_no_false_0002.yaml deleted file mode 100644 index 0354a2f..0000000 --- a/evals/cases/no_false/photo_single_no_false_0002.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: direct_photo_single_no_false_0002 -clientMode: direct - -# At single-car plate/damage confirm, short/garbled answers must stay on 2004/2005, -# not jump to 0002 after a few unclear turns. -steps: - - input: 新对话 - expect: - stageCode: "1001" - - - input: 继续办理 - expect: - stageCode: "1002" - - - input: 十分钟之前一辆车撞到护栏 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 十分钟之前 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "1002" - - - input: 是的 - expect: - stageCode: "1002" - - - input: 对 - expect: - stageCode: "2000" - - - input: 【拍摄完成】 - expect: - stageCode: "2001" - - - input: 【拍摄完成】 - expect: - stageCode: "2002" - - - input: 【拍摄完成】 - expect: - stageCode: "2003" - - - input: 【拍摄完成】 - expect: - stageCode: "2004" - - - input: 这个 - expect: - stageCode: "2004" - - - input: 试试 - expect: - stageCode: "2004" - - - input: 是的 - expect: - stageCode: "2005" - - - input: 这个 - expect: - stageCode: "2005" - - - input: 试试 - expect: - stageCode: "2005" - - - input: 什么意思 - expect: - stageCode: "2005" diff --git a/evals/cases/non_motor_vehicle/browser_addon_all_info.yaml b/evals/cases/non_motor_vehicle/browser_addon_all_info.yaml new file mode 100644 index 0000000..ea97d66 --- /dev/null +++ b/evals/cases/non_motor_vehicle/browser_addon_all_info.yaml @@ -0,0 +1,24 @@ +# 流程回归:非机动车撞固定物按本组口径机动车数量为0,检查事实保持、时间确认及0003分流。 +name: browser_addon_non_motor_vehicle_all_info +clientMode: browser_addon + +steps: + - input: "【开始】" + expect: + stageCode: "browser_addon.1001" + - input: "继续办理" + expect: + stageCode: "browser_addon.1002" + - input: "十分钟前我骑非机动车自己撞在路边石墩上,只有我这一辆非机动车,没有其他车辆或行人,没有人员伤亡,我还在现场。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"has_injury":false,"is_on_scene":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "browser_addon.0003" + form: {"vehicle_count":0} + replyNotMatches: "[??]" + diff --git a/evals/cases/non_motor_vehicle/browser_addon_electric_bicycle.yaml b/evals/cases/non_motor_vehicle/browser_addon_electric_bicycle.yaml new file mode 100644 index 0000000..39bac73 --- /dev/null +++ b/evals/cases/non_motor_vehicle/browser_addon_electric_bicycle.yaml @@ -0,0 +1,45 @@ +# 电动自行车撞固定物:按本组口径机动车数量为0,完成时间确认及现场采集后进入0003,不追问汽车数量。 +name: browser_addon_non_motor_vehicle_electric_bicycle +clientMode: browser_addon + +steps: + - input: "【开始】" + expect: + stageCode: "browser_addon.1001" + + - input: "继续办理" + expect: + stageCode: "browser_addon.1002" + + - input: "我骑的是非机动车,电动自行车,撞到了路边的石墩子上。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + replyMatches: "(有没有|是否).*受伤" + + - input: "没有人员伤亡。" + expect: + stageCode: "browser_addon.1002" + form: {"has_injury":false,"vehicle_count":0} + replyMatches: "(什么时候|几点|事故.*时间)" + replyNotMatches: "(几辆|数量|是否.*非机动车|有没有.*非机动车)" + + - input: "十分钟前发生的。" + expect: + stageCode: "browser_addon.1002" + form: {"vehicle_count":0} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: "对的。" + expect: + stageCode: "browser_addon.1002" + form: {"vehicle_count":0} + replyMatches: "现场" + replyNotMatches: "几辆|数量|这个时间对吗" + + - input: "我还在事故现场。" + expect: + stageCode: "browser_addon.0003" + form: {"has_non_motor_vehicle_hit":true,"has_injury":false,"is_on_scene":true,"vehicle_count":0} + replyMatches: "转接.*(人工|警员)|转.*人工" + replyNotMatches: "几辆|数量|这个时间对吗" diff --git a/evals/cases/non_motor_vehicle/browser_addon_explicit_correction.yaml b/evals/cases/non_motor_vehicle/browser_addon_explicit_correction.yaml new file mode 100644 index 0000000..2038bae --- /dev/null +++ b/evals/cases/non_motor_vehicle/browser_addon_explicit_correction.yaml @@ -0,0 +1,33 @@ +# 非机动车撞固定物时机动车数量为0;用户明确更正为一辆汽车后,数量更新为1。 +name: browser_addon_non_motor_vehicle_explicit_correction +clientMode: browser_addon + +steps: + - input: "【开始】" + expect: + stageCode: "browser_addon.1001" + - input: "继续办理" + expect: + stageCode: "browser_addon.1002" + - input: "我这里是一辆非机动车,撞到了路边的石墩子上。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + formAbsent: ["has_injury","accident_time","is_on_scene"] + replyMatches: "(有没有|是否).*受伤" + - input: "刚才说错了,我的是汽车,不是非机动车,事故只有我这一辆汽车,没有涉及非机动车或行人。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":false,"vehicle_count":1} + replyMatches: "(有没有|是否).*受伤" + - input: "没有人员受伤,十分钟前发生的,我还在事故现场。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":false,"vehicle_count":1,"has_injury":false,"is_on_scene":true} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "browser_addon.2000" + diff --git a/evals/cases/non_motor_vehicle/browser_addon_no_other_vehicle.yaml b/evals/cases/non_motor_vehicle/browser_addon_no_other_vehicle.yaml new file mode 100644 index 0000000..05c8811 --- /dev/null +++ b/evals/cases/non_motor_vehicle/browser_addon_no_other_vehicle.yaml @@ -0,0 +1,51 @@ +# 流程回归:非机动车撞固定物按本组口径机动车数量为0,检查事实保持、时间确认及0003分流。 +name: browser_addon_non_motor_vehicle_no_other_vehicle +clientMode: browser_addon + +steps: + - input: "【开始】" + expect: + stageCode: "browser_addon.1001" + - input: "继续办理" + expect: + stageCode: "browser_addon.1002" + - input: "我现在没有按住说话,为什么也会记录进去?是不是有bug?" + expect: + stageCode: "browser_addon.1002" + formAbsent: ["has_injury","has_non_motor_vehicle_hit","vehicle_count","accident_cause","accident_time","is_on_scene"] + - input: "我这里是一辆非机动车,撞到了路边的石墩子上。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + formAbsent: ["has_injury","accident_time","is_on_scene"] + replyMatches: "(有没有|是否).*受伤" + - input: "没有人员伤亡。" + expect: + stageCode: "browser_addon.1002" + form: {"has_injury":false,"has_non_motor_vehicle_hit":true,"vehicle_count":0} + replyMatches: "(什么时候|几点|事故.*时间)" + replyNotMatches: "(有没有|是否).*非机动车" + - input: "我已经说过了,是撞在石墩子上,没有别的车子,就我自己的车子。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + replyMatches: "(什么时候|几点|事故.*时间)" + - input: "十分钟前。" + expect: + stageCode: "browser_addon.1002" + form: {"vehicle_count":0} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "browser_addon.1002" + form: {"vehicle_count":0} + replyMatches: "现场" + + + - input: "我还在事故现场。" + expect: + stageCode: "browser_addon.0003" + form: {"has_non_motor_vehicle_hit":true,"is_on_scene":true,"vehicle_count":0} diff --git a/evals/cases/non_motor_vehicle/browser_addon_self_collision.yaml b/evals/cases/non_motor_vehicle/browser_addon_self_collision.yaml new file mode 100644 index 0000000..4350009 --- /dev/null +++ b/evals/cases/non_motor_vehicle/browser_addon_self_collision.yaml @@ -0,0 +1,46 @@ +# 流程回归:非机动车撞固定物按本组口径机动车数量为0,检查事实保持、时间确认及0003分流。 +name: browser_addon_non_motor_vehicle_self_collision +clientMode: browser_addon + +steps: + - input: "【开始】" + expect: + stageCode: "browser_addon.1001" + - input: "继续办理" + expect: + stageCode: "browser_addon.1002" + - input: "我这个是非机动车,然后自己撞在路边的石墩子上。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + formAbsent: ["has_injury","accident_time","is_on_scene"] + replyMatches: "(有没有|是否).*受伤" + - input: "没有人员伤亡。" + expect: + stageCode: "browser_addon.1002" + form: {"has_injury":false,"has_non_motor_vehicle_hit":true,"vehicle_count":0} + replyMatches: "(什么时候|几点|事故.*时间)" + replyNotMatches: "(有没有|是否).*非机动车" + - input: "没有别的车子,就我自己的车子。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + replyMatches: "(什么时候|几点|事故.*时间)" + - input: "十分钟前发生的。" + expect: + stageCode: "browser_addon.1002" + form: {"vehicle_count":0} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "browser_addon.1002" + form: {"vehicle_count":0} + replyMatches: "现场" + + + - input: "我还在事故现场。" + expect: + stageCode: "browser_addon.0003" + form: {"has_non_motor_vehicle_hit":true,"is_on_scene":true,"vehicle_count":0} diff --git a/evals/cases/non_motor_vehicle/browser_addon_zero_count_bundle.yaml b/evals/cases/non_motor_vehicle/browser_addon_zero_count_bundle.yaml new file mode 100644 index 0000000..6dc3bb0 --- /dev/null +++ b/evals/cases/non_motor_vehicle/browser_addon_zero_count_bundle.yaml @@ -0,0 +1,17 @@ +# 提取专项:明确只有非机动车时必须向前端发送机动车数量0,不能被空值比较吞掉。 +name: browser_addon_non_motor_vehicle_zero_count_bundle +clientMode: browser_addon + +steps: + - input: "【开始】" + expect: + stageCode: "browser_addon.1001" + + - input: "继续办理" + expect: + stageCode: "browser_addon.1002" + + - input: "十分钟前我骑非机动车自己撞在路边石墩上,只有我这一辆非机动车,没有其他车辆或行人,没有人员伤亡,我还在现场。" + expect: + stageCode: "browser_addon.1002" + form: {"vehicle_count":0} diff --git a/evals/cases/non_motor_vehicle/browser_addon_zero_count_history.yaml b/evals/cases/non_motor_vehicle/browser_addon_zero_count_history.yaml new file mode 100644 index 0000000..b5ec27b --- /dev/null +++ b/evals/cases/non_motor_vehicle/browser_addon_zero_count_history.yaml @@ -0,0 +1,27 @@ +# 提取专项:非机动车撞固定物按本组口径记录机动车数量0,后续补充“没有别的车”时保留0和非机动车事实。 +name: browser_addon_non_motor_vehicle_zero_count_history +clientMode: browser_addon + +steps: + - input: "【开始】" + expect: + stageCode: "browser_addon.1001" + + - input: "继续办理" + expect: + stageCode: "browser_addon.1002" + + - input: "我这里是一辆非机动车,撞到了路边的石墩子上。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + + - input: "没有人员伤亡。" + expect: + stageCode: "browser_addon.1002" + form: {"vehicle_count":0} + + - input: "我已经说过了,是撞在石墩子上,没有别的车子,就我自己的车子。" + expect: + stageCode: "browser_addon.1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} diff --git a/evals/cases/non_motor_vehicle/direct_all_info.yaml b/evals/cases/non_motor_vehicle/direct_all_info.yaml new file mode 100644 index 0000000..cdfdd36 --- /dev/null +++ b/evals/cases/non_motor_vehicle/direct_all_info.yaml @@ -0,0 +1,24 @@ +# 流程回归:非机动车撞固定物按本组口径机动车数量为0,检查事实保持、时间确认及0003分流。 +name: direct_non_motor_vehicle_all_info +clientMode: direct + +steps: + - input: "【开始】" + expect: + stageCode: "1001" + - input: "继续办理" + expect: + stageCode: "1002" + - input: "十分钟前我骑非机动车自己撞在路边石墩上,只有我这一辆非机动车,没有其他车辆或行人,没有人员伤亡,我还在现场。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"has_injury":false,"is_on_scene":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "0003" + form: {"vehicle_count":0} + replyNotMatches: "[??]" + diff --git a/evals/cases/non_motor_vehicle/direct_electric_bicycle.yaml b/evals/cases/non_motor_vehicle/direct_electric_bicycle.yaml new file mode 100644 index 0000000..b3d99cd --- /dev/null +++ b/evals/cases/non_motor_vehicle/direct_electric_bicycle.yaml @@ -0,0 +1,45 @@ +# 电动自行车撞固定物:按本组口径机动车数量为0,完成时间确认及现场采集后进入0003,不追问汽车数量。 +name: direct_non_motor_vehicle_electric_bicycle +clientMode: direct + +steps: + - input: "【开始】" + expect: + stageCode: "1001" + + - input: "继续办理" + expect: + stageCode: "1002" + + - input: "我骑的是非机动车,电动自行车,撞到了路边的石墩子上。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + replyMatches: "(有没有|是否).*受伤" + + - input: "没有人员伤亡。" + expect: + stageCode: "1002" + form: {"has_injury":false,"vehicle_count":0} + replyMatches: "(什么时候|几点|事故.*时间)" + replyNotMatches: "(几辆|数量|是否.*非机动车|有没有.*非机动车)" + + - input: "十分钟前发生的。" + expect: + stageCode: "1002" + form: {"vehicle_count":0} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: "对的。" + expect: + stageCode: "1002" + form: {"vehicle_count":0} + replyMatches: "现场" + replyNotMatches: "几辆|数量|这个时间对吗" + + - input: "我还在事故现场。" + expect: + stageCode: "0003" + form: {"has_non_motor_vehicle_hit":true,"has_injury":false,"is_on_scene":true,"vehicle_count":0} + replyMatches: "转接.*(人工|警员)|转.*人工" + replyNotMatches: "几辆|数量|这个时间对吗" diff --git a/evals/cases/non_motor_vehicle/direct_explicit_correction.yaml b/evals/cases/non_motor_vehicle/direct_explicit_correction.yaml new file mode 100644 index 0000000..f8107ff --- /dev/null +++ b/evals/cases/non_motor_vehicle/direct_explicit_correction.yaml @@ -0,0 +1,33 @@ +# 非机动车撞固定物时机动车数量为0;用户明确更正为一辆汽车后,数量更新为1。 +name: direct_non_motor_vehicle_explicit_correction +clientMode: direct + +steps: + - input: "【开始】" + expect: + stageCode: "1001" + - input: "继续办理" + expect: + stageCode: "1002" + - input: "我这里是一辆非机动车,撞到了路边的石墩子上。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + formAbsent: ["has_injury","accident_time","is_on_scene"] + replyMatches: "(有没有|是否).*受伤" + - input: "刚才说错了,我的是汽车,不是非机动车,事故只有我这一辆汽车,没有涉及非机动车或行人。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":false,"vehicle_count":1} + replyMatches: "(有没有|是否).*受伤" + - input: "没有人员受伤,十分钟前发生的,我还在事故现场。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":false,"vehicle_count":1,"has_injury":false,"is_on_scene":true} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "2000" + diff --git a/evals/cases/non_motor_vehicle/direct_no_other_vehicle.yaml b/evals/cases/non_motor_vehicle/direct_no_other_vehicle.yaml new file mode 100644 index 0000000..2e6d48e --- /dev/null +++ b/evals/cases/non_motor_vehicle/direct_no_other_vehicle.yaml @@ -0,0 +1,51 @@ +# 流程回归:非机动车撞固定物按本组口径机动车数量为0,检查事实保持、时间确认及0003分流。 +name: direct_non_motor_vehicle_no_other_vehicle +clientMode: direct + +steps: + - input: "【开始】" + expect: + stageCode: "1001" + - input: "继续办理" + expect: + stageCode: "1002" + - input: "我现在没有按住说话,为什么也会记录进去?是不是有bug?" + expect: + stageCode: "1002" + formAbsent: ["has_injury","has_non_motor_vehicle_hit","vehicle_count","accident_cause","accident_time","is_on_scene"] + - input: "我这里是一辆非机动车,撞到了路边的石墩子上。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + formAbsent: ["has_injury","accident_time","is_on_scene"] + replyMatches: "(有没有|是否).*受伤" + - input: "没有人员伤亡。" + expect: + stageCode: "1002" + form: {"has_injury":false,"has_non_motor_vehicle_hit":true,"vehicle_count":0} + replyMatches: "(什么时候|几点|事故.*时间)" + replyNotMatches: "(有没有|是否).*非机动车" + - input: "我已经说过了,是撞在石墩子上,没有别的车子,就我自己的车子。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + replyMatches: "(什么时候|几点|事故.*时间)" + - input: "十分钟前。" + expect: + stageCode: "1002" + form: {"vehicle_count":0} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "1002" + form: {"vehicle_count":0} + replyMatches: "现场" + + + - input: "我还在事故现场。" + expect: + stageCode: "0003" + form: {"has_non_motor_vehicle_hit":true,"is_on_scene":true,"vehicle_count":0} diff --git a/evals/cases/non_motor_vehicle/direct_self_collision.yaml b/evals/cases/non_motor_vehicle/direct_self_collision.yaml new file mode 100644 index 0000000..c3e8d7c --- /dev/null +++ b/evals/cases/non_motor_vehicle/direct_self_collision.yaml @@ -0,0 +1,46 @@ +# 流程回归:非机动车撞固定物按本组口径机动车数量为0,检查事实保持、时间确认及0003分流。 +name: direct_non_motor_vehicle_self_collision +clientMode: direct + +steps: + - input: "【开始】" + expect: + stageCode: "1001" + - input: "继续办理" + expect: + stageCode: "1002" + - input: "我这个是非机动车,然后自己撞在路边的石墩子上。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + formMatches: {"accident_cause":"^其他:(?=.*非机动车)(?=.*石墩).+"} + formAbsent: ["has_injury","accident_time","is_on_scene"] + replyMatches: "(有没有|是否).*受伤" + - input: "没有人员伤亡。" + expect: + stageCode: "1002" + form: {"has_injury":false,"has_non_motor_vehicle_hit":true,"vehicle_count":0} + replyMatches: "(什么时候|几点|事故.*时间)" + replyNotMatches: "(有没有|是否).*非机动车" + - input: "没有别的车子,就我自己的车子。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + replyMatches: "(什么时候|几点|事故.*时间)" + - input: "十分钟前发生的。" + expect: + stageCode: "1002" + form: {"vehicle_count":0} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 + expect: + stageCode: "1002" + form: {"vehicle_count":0} + replyMatches: "现场" + + + - input: "我还在事故现场。" + expect: + stageCode: "0003" + form: {"has_non_motor_vehicle_hit":true,"is_on_scene":true,"vehicle_count":0} diff --git a/evals/cases/non_motor_vehicle/direct_zero_count_bundle.yaml b/evals/cases/non_motor_vehicle/direct_zero_count_bundle.yaml new file mode 100644 index 0000000..5ffe3ba --- /dev/null +++ b/evals/cases/non_motor_vehicle/direct_zero_count_bundle.yaml @@ -0,0 +1,17 @@ +# 提取专项:明确只有非机动车时必须向前端发送机动车数量0,不能被空值比较吞掉。 +name: direct_non_motor_vehicle_zero_count_bundle +clientMode: direct + +steps: + - input: "【开始】" + expect: + stageCode: "1001" + + - input: "继续办理" + expect: + stageCode: "1002" + + - input: "十分钟前我骑非机动车自己撞在路边石墩上,只有我这一辆非机动车,没有其他车辆或行人,没有人员伤亡,我还在现场。" + expect: + stageCode: "1002" + form: {"vehicle_count":0} diff --git a/evals/cases/non_motor_vehicle/direct_zero_count_history.yaml b/evals/cases/non_motor_vehicle/direct_zero_count_history.yaml new file mode 100644 index 0000000..bb8cb56 --- /dev/null +++ b/evals/cases/non_motor_vehicle/direct_zero_count_history.yaml @@ -0,0 +1,27 @@ +# 提取专项:非机动车撞固定物按本组口径记录机动车数量0,后续补充“没有别的车”时保留0和非机动车事实。 +name: direct_non_motor_vehicle_zero_count_history +clientMode: direct + +steps: + - input: "【开始】" + expect: + stageCode: "1001" + + - input: "继续办理" + expect: + stageCode: "1002" + + - input: "我这里是一辆非机动车,撞到了路边的石墩子上。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} + + - input: "没有人员伤亡。" + expect: + stageCode: "1002" + form: {"vehicle_count":0} + + - input: "我已经说过了,是撞在石墩子上,没有别的车子,就我自己的车子。" + expect: + stageCode: "1002" + form: {"has_non_motor_vehicle_hit":true,"vehicle_count":0} diff --git a/evals/cases/photo_button_guidance/browser_addon_dual_failure.yaml b/evals/cases/photo_button_guidance/browser_addon_dual_failure.yaml index e228c11..3cb8048 100644 --- a/evals/cases/photo_button_guidance/browser_addon_dual_failure.yaml +++ b/evals/cases/photo_button_guidance/browser_addon_dual_failure.yaml @@ -1,8 +1,8 @@ name: browser_addon_photo_button_guidance_dual_failure clientMode: browser_addon -# 对应 workflow/20261016 拍照提示词;每个文件使用独立会话。 -# runner 只校验状态码,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 +# 对应 workflow/20261021 拍照提示词;每个文件使用独立会话。 +# runner 校验状态码和时间确认,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "browser_addon.1002" - # 事故信息齐全,直接进入拍照,只输出原有固定指令,不主动追加按钮说明。 + # 先确认完整事故时间,确认后进入拍照,不主动追加按钮说明。 - input: 十分钟前两辆汽车追尾,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "browser_addon.1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2010" # 说明界面中间的拍照按钮并保留当前固定拍摄指令。 + - input: 拍照按钮在哪? expect: stageCode: "browser_addon.2010" diff --git a/evals/cases/photo_button_guidance/browser_addon_dual_guidance.yaml b/evals/cases/photo_button_guidance/browser_addon_dual_guidance.yaml index 390cb54..b9c7c04 100644 --- a/evals/cases/photo_button_guidance/browser_addon_dual_guidance.yaml +++ b/evals/cases/photo_button_guidance/browser_addon_dual_guidance.yaml @@ -1,8 +1,8 @@ name: browser_addon_photo_button_guidance_dual_guidance clientMode: browser_addon -# 对应 workflow/20261016 拍照提示词;每个文件使用独立会话。 -# runner 只校验状态码,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 +# 对应 workflow/20261021 拍照提示词;每个文件使用独立会话。 +# runner 校验状态码和时间确认,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "browser_addon.1002" - # 事故信息齐全,直接进入拍照,只输出原有固定指令,不主动追加按钮说明。 + # 先确认完整事故时间,确认后进入拍照,不主动追加按钮说明。 - input: 十分钟前两辆汽车追尾,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "browser_addon.1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2010" # 必须说明可点击手机界面中间的拍照按钮,并完整保留当前固定指令(第一辆车侧前方、看清车牌);不得推进。 + - input: 拍照按钮在哪? expect: stageCode: "browser_addon.2010" diff --git a/evals/cases/photo_button_guidance/browser_addon_dual_no_repeat.yaml b/evals/cases/photo_button_guidance/browser_addon_dual_no_repeat.yaml index 27681a5..aeae86e 100644 --- a/evals/cases/photo_button_guidance/browser_addon_dual_no_repeat.yaml +++ b/evals/cases/photo_button_guidance/browser_addon_dual_no_repeat.yaml @@ -2,7 +2,7 @@ name: browser_addon_photo_button_guidance_dual_no_repeat clientMode: browser_addon # 回归:解释一次按钮后,正常推进不再重复;明确再次求助仍可解释。 -# runner 只校验状态码,按钮说明是否重复必须手动检查输出对话。 +# runner 校验状态码和时间确认,按钮说明是否重复必须手动检查输出对话。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "browser_addon.1002" - # 只输出原有拍摄指令,不主动附加按钮说明。 + # 先确认完整事故时间,确认后只输出原有拍摄指令。 - input: 十分钟前两辆汽车追尾,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "browser_addon.1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2010" # 首次笼统求助可说明一次界面中间的拍照按钮,保留固定指令。 + - input: 这个什么 expect: stageCode: "browser_addon.2010" diff --git a/evals/cases/photo_button_guidance/browser_addon_single_failure.yaml b/evals/cases/photo_button_guidance/browser_addon_single_failure.yaml index 68be45a..a9c01d0 100644 --- a/evals/cases/photo_button_guidance/browser_addon_single_failure.yaml +++ b/evals/cases/photo_button_guidance/browser_addon_single_failure.yaml @@ -1,8 +1,8 @@ name: browser_addon_photo_button_guidance_single_failure clientMode: browser_addon -# 对应 workflow/20261016 拍照提示词;每个文件使用独立会话。 -# runner 只校验状态码,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 +# 对应 workflow/20261021 拍照提示词;每个文件使用独立会话。 +# runner 校验状态码和时间确认,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "browser_addon.1002" - # 事故信息齐全,直接进入拍照,只输出原有固定指令,不主动追加按钮说明。 + # 先确认完整事故时间,确认后进入拍照,不主动追加按钮说明。 - input: 十分钟前我倒车撞到了护栏,事故只有一辆汽车,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "browser_addon.1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2000" # 说明界面中间的拍照按钮并保留当前固定拍摄指令。 + - input: 拍照按钮在哪? expect: stageCode: "browser_addon.2000" diff --git a/evals/cases/photo_button_guidance/browser_addon_single_guidance.yaml b/evals/cases/photo_button_guidance/browser_addon_single_guidance.yaml index 306da2f..ec474d6 100644 --- a/evals/cases/photo_button_guidance/browser_addon_single_guidance.yaml +++ b/evals/cases/photo_button_guidance/browser_addon_single_guidance.yaml @@ -1,8 +1,8 @@ name: browser_addon_photo_button_guidance_single_guidance clientMode: browser_addon -# 对应 workflow/20261016 拍照提示词;每个文件使用独立会话。 -# runner 只校验状态码,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 +# 对应 workflow/20261021 拍照提示词;每个文件使用独立会话。 +# runner 校验状态码和时间确认,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "browser_addon.1002" - # 事故信息齐全,直接进入拍照,只输出原有固定指令,不主动追加按钮说明。 + # 先确认完整事故时间,确认后进入拍照,不主动追加按钮说明。 - input: 十分钟前我倒车撞到了护栏,事故只有一辆汽车,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "browser_addon.1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2000" # 必须说明可点击手机界面中间的拍照按钮,并完整保留当前固定指令(车辆前方);不得推进。 + - input: 拍照按钮在哪? expect: stageCode: "browser_addon.2000" diff --git a/evals/cases/photo_button_guidance/browser_addon_single_no_repeat.yaml b/evals/cases/photo_button_guidance/browser_addon_single_no_repeat.yaml index c5ab6b9..8b8d078 100644 --- a/evals/cases/photo_button_guidance/browser_addon_single_no_repeat.yaml +++ b/evals/cases/photo_button_guidance/browser_addon_single_no_repeat.yaml @@ -2,7 +2,7 @@ name: browser_addon_photo_button_guidance_single_no_repeat clientMode: browser_addon # 回归:解释一次按钮后,正常推进不再重复;明确再次求助仍可解释。 -# runner 只校验状态码,按钮说明是否重复必须手动检查输出对话。 +# runner 校验状态码和时间确认,按钮说明是否重复必须手动检查输出对话。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "browser_addon.1002" - # 只输出原有拍摄指令,不主动附加按钮说明。 + # 先确认完整事故时间,确认后只输出原有拍摄指令。 - input: 十分钟前我倒车撞到护栏,只有一辆汽车,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "browser_addon.1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "browser_addon.2000" # 首次笼统求助可说明一次界面中间的拍照按钮,保留固定指令。 + - input: 这个什么 expect: stageCode: "browser_addon.2000" diff --git a/evals/cases/photo_button_guidance/direct_dual_failure.yaml b/evals/cases/photo_button_guidance/direct_dual_failure.yaml index 89e4f91..831db51 100644 --- a/evals/cases/photo_button_guidance/direct_dual_failure.yaml +++ b/evals/cases/photo_button_guidance/direct_dual_failure.yaml @@ -1,8 +1,8 @@ name: direct_photo_button_guidance_dual_failure clientMode: direct -# 对应 workflow/20261016 拍照提示词;每个文件使用独立会话。 -# runner 只校验状态码,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 +# 对应 workflow/20261021 拍照提示词;每个文件使用独立会话。 +# runner 校验状态码和时间确认,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "1002" - # 事故信息齐全,直接进入拍照,只输出原有固定指令,不主动追加按钮说明。 + # 先确认完整事故时间,确认后进入拍照,不主动追加按钮说明。 - input: 十分钟前两辆汽车追尾,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2010" # 说明界面中间的拍照按钮并保留当前固定拍摄指令。 + - input: 拍照按钮在哪? expect: stageCode: "2010" diff --git a/evals/cases/photo_button_guidance/direct_dual_guidance.yaml b/evals/cases/photo_button_guidance/direct_dual_guidance.yaml index 1680d5a..789d92d 100644 --- a/evals/cases/photo_button_guidance/direct_dual_guidance.yaml +++ b/evals/cases/photo_button_guidance/direct_dual_guidance.yaml @@ -1,8 +1,8 @@ name: direct_photo_button_guidance_dual_guidance clientMode: direct -# 对应 workflow/20261016 拍照提示词;每个文件使用独立会话。 -# runner 只校验状态码,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 +# 对应 workflow/20261021 拍照提示词;每个文件使用独立会话。 +# runner 校验状态码和时间确认,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "1002" - # 事故信息齐全,直接进入拍照,只输出原有固定指令,不主动追加按钮说明。 + # 先确认完整事故时间,确认后进入拍照,不主动追加按钮说明。 - input: 十分钟前两辆汽车追尾,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2010" # 必须说明可点击手机界面中间的拍照按钮,并完整保留当前固定指令(第一辆车侧前方、看清车牌);不得推进。 + - input: 拍照按钮在哪? expect: stageCode: "2010" diff --git a/evals/cases/photo_button_guidance/direct_dual_no_repeat.yaml b/evals/cases/photo_button_guidance/direct_dual_no_repeat.yaml index 007c636..6482ca5 100644 --- a/evals/cases/photo_button_guidance/direct_dual_no_repeat.yaml +++ b/evals/cases/photo_button_guidance/direct_dual_no_repeat.yaml @@ -2,7 +2,7 @@ name: direct_photo_button_guidance_dual_no_repeat clientMode: direct # 回归:解释一次按钮后,正常推进不再重复;明确再次求助仍可解释。 -# runner 只校验状态码,按钮说明是否重复必须手动检查输出对话。 +# runner 校验状态码和时间确认,按钮说明是否重复必须手动检查输出对话。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "1002" - # 只输出原有拍摄指令,不主动附加按钮说明。 + # 先确认完整事故时间,确认后只输出原有拍摄指令。 - input: 十分钟前两辆汽车追尾,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2010" # 首次笼统求助可说明一次界面中间的拍照按钮,保留固定指令。 + - input: 这个什么 expect: stageCode: "2010" diff --git a/evals/cases/photo_button_guidance/direct_single_failure.yaml b/evals/cases/photo_button_guidance/direct_single_failure.yaml index 2df4066..b998915 100644 --- a/evals/cases/photo_button_guidance/direct_single_failure.yaml +++ b/evals/cases/photo_button_guidance/direct_single_failure.yaml @@ -1,8 +1,8 @@ name: direct_photo_button_guidance_single_failure clientMode: direct -# 对应 workflow/20261016 拍照提示词;每个文件使用独立会话。 -# runner 只校验状态码,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 +# 对应 workflow/20261021 拍照提示词;每个文件使用独立会话。 +# runner 校验状态码和时间确认,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "1002" - # 事故信息齐全,直接进入拍照,只输出原有固定指令,不主动追加按钮说明。 + # 先确认完整事故时间,确认后进入拍照,不主动追加按钮说明。 - input: 十分钟前我倒车撞到了护栏,事故只有一辆汽车,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2000" # 说明界面中间的拍照按钮并保留当前固定拍摄指令。 + - input: 拍照按钮在哪? expect: stageCode: "2000" diff --git a/evals/cases/photo_button_guidance/direct_single_guidance.yaml b/evals/cases/photo_button_guidance/direct_single_guidance.yaml index 0b74e59..4b395ea 100644 --- a/evals/cases/photo_button_guidance/direct_single_guidance.yaml +++ b/evals/cases/photo_button_guidance/direct_single_guidance.yaml @@ -1,8 +1,8 @@ name: direct_photo_button_guidance_single_guidance clientMode: direct -# 对应 workflow/20261016 拍照提示词;每个文件使用独立会话。 -# runner 只校验状态码,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 +# 对应 workflow/20261021 拍照提示词;每个文件使用独立会话。 +# runner 校验状态码和时间确认,按钮位置、固定话术及结束后不再提示按钮需结合对话记录手动检查。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "1002" - # 事故信息齐全,直接进入拍照,只输出原有固定指令,不主动追加按钮说明。 + # 先确认完整事故时间,确认后进入拍照,不主动追加按钮说明。 - input: 十分钟前我倒车撞到了护栏,事故只有一辆汽车,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2000" # 必须说明可点击手机界面中间的拍照按钮,并完整保留当前固定指令(车辆前方);不得推进。 + - input: 拍照按钮在哪? expect: stageCode: "2000" diff --git a/evals/cases/photo_button_guidance/direct_single_no_repeat.yaml b/evals/cases/photo_button_guidance/direct_single_no_repeat.yaml index 0d4fba1..affcd8b 100644 --- a/evals/cases/photo_button_guidance/direct_single_no_repeat.yaml +++ b/evals/cases/photo_button_guidance/direct_single_no_repeat.yaml @@ -2,7 +2,7 @@ name: direct_photo_button_guidance_single_no_repeat clientMode: direct # 回归:解释一次按钮后,正常推进不再重复;明确再次求助仍可解释。 -# runner 只校验状态码,按钮说明是否重复必须手动检查输出对话。 +# runner 校验状态码和时间确认,按钮说明是否重复必须手动检查输出对话。 steps: # 新会话。 - input: 新对话 @@ -14,12 +14,19 @@ steps: expect: stageCode: "1002" - # 只输出原有拍摄指令,不主动附加按钮说明。 + # 先确认完整事故时间,确认后只输出原有拍摄指令。 - input: 十分钟前我倒车撞到护栏,只有一辆汽车,没有人受伤,没有撞到非机动车或行人,我还在现场。 + expect: + stageCode: "1002" + + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 对 expect: stageCode: "2000" # 首次笼统求助可说明一次界面中间的拍照按钮,保留固定指令。 + - input: 这个什么 expect: stageCode: "2000" diff --git a/evals/cases/photo_skip_plate_confirm/browser_addon_dual.yaml b/evals/cases/photo_skip_plate_confirm/browser_addon_dual.yaml index eaedc9d..eacada1 100644 --- a/evals/cases/photo_skip_plate_confirm/browser_addon_dual.yaml +++ b/evals/cases/photo_skip_plate_confirm/browser_addon_dual.yaml @@ -1,7 +1,7 @@ name: browser_addon_photo_skip_plate_confirm_dual clientMode: browser_addon -# 对应 workflow/20261016;每个案例使用独立会话。 +# 对应 workflow/20261021;每个案例使用独立会话。 # 本人照片完成后直接结束拍照,不经过 2016。 # direct 的内部 3001/3002 对外映射为 1002。 steps: @@ -13,34 +13,16 @@ steps: expect: stageCode: "browser_addon.1002" - - input: 两辆汽车发生了追尾。 - expect: - stageCode: "browser_addon.1002" - - - input: 没有 - expect: - stageCode: "browser_addon.1002" - - - input: 没有 - expect: - stageCode: "browser_addon.1002" - - - input: 十分钟之前 + - input: 十分钟前两辆汽车追尾,没有人受伤,没有涉及非机动车或行人,我还在现场。 expect: stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" - input: 对 - expect: - stageCode: "browser_addon.1002" - - - input: 是的,我还在事故现场 - expect: - stageCode: "browser_addon.1002" - - - input: 两辆汽车 expect: stageCode: "browser_addon.2010" + - input: 【拍摄完成】 expect: stageCode: "browser_addon.2011" diff --git a/evals/cases/photo_skip_plate_confirm/browser_addon_single.yaml b/evals/cases/photo_skip_plate_confirm/browser_addon_single.yaml index 106c892..67598a5 100644 --- a/evals/cases/photo_skip_plate_confirm/browser_addon_single.yaml +++ b/evals/cases/photo_skip_plate_confirm/browser_addon_single.yaml @@ -1,7 +1,7 @@ name: browser_addon_photo_skip_plate_confirm_single clientMode: browser_addon -# 对应 workflow/20261016;每个案例使用独立会话。 +# 对应 workflow/20261021;每个案例使用独立会话。 # 本人照片完成后直接进入车损位置确认 2005,不经过 2004。 # direct 的内部 3001/3002 对外映射为 1002。 steps: @@ -13,34 +13,16 @@ steps: expect: stageCode: "browser_addon.1002" - - input: 我倒车撞到了护栏,涉及一辆汽车。 - expect: - stageCode: "browser_addon.1002" - - - input: 没有 - expect: - stageCode: "browser_addon.1002" - - - input: 没有 - expect: - stageCode: "browser_addon.1002" - - - input: 十分钟之前 + - input: 十分钟前我倒车撞到护栏,只有一辆汽车,没有人受伤,没有涉及非机动车或行人,我还在现场。 expect: stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" - input: 对 - expect: - stageCode: "browser_addon.1002" - - - input: 是的,我还在事故现场 - expect: - stageCode: "browser_addon.1002" - - - input: 一辆汽车 expect: stageCode: "browser_addon.2000" + - input: 【拍摄完成】 expect: stageCode: "browser_addon.2001" diff --git a/evals/cases/photo_skip_plate_confirm/direct_dual.yaml b/evals/cases/photo_skip_plate_confirm/direct_dual.yaml index 632724f..7b31165 100644 --- a/evals/cases/photo_skip_plate_confirm/direct_dual.yaml +++ b/evals/cases/photo_skip_plate_confirm/direct_dual.yaml @@ -1,11 +1,11 @@ name: direct_photo_skip_plate_confirm_dual clientMode: direct -# 对应 workflow/20261016;每个案例使用独立会话。 +# 对应 workflow/20261021;每个案例使用独立会话。 # 本人照片完成后直接结束拍照,不经过 2016。 # 手动检查:最后一次拍摄完成的回复同时说明进入信息确认,并询问第一辆车是否由本人驾驶。 # 紧接着回答“是的”后应询问姓名,不应再次询问驾驶员身份。 -# 回答“李四”后应核实身份证后四位;runner 仅校验状态码,以上正文需查看对话记录。 +# 回答“李四”后应核实身份证后四位;runner 校验状态码和时间确认,身份确认正文需查看对话记录。 # direct 的内部 3001/3002 对外映射为 1002。 steps: - input: 新对话 @@ -16,34 +16,16 @@ steps: expect: stageCode: "1002" - - input: 两辆汽车发生了追尾。 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 十分钟之前 + - input: 十分钟前两辆汽车追尾,没有人受伤,没有涉及非机动车或行人,我还在现场。 expect: stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" - input: 对 - expect: - stageCode: "1002" - - - input: 是的,我还在事故现场 - expect: - stageCode: "1002" - - - input: 两辆汽车 expect: stageCode: "2010" + - input: 【拍摄完成】 expect: stageCode: "2011" diff --git a/evals/cases/photo_skip_plate_confirm/direct_single.yaml b/evals/cases/photo_skip_plate_confirm/direct_single.yaml index 2764bb1..acdc3fa 100644 --- a/evals/cases/photo_skip_plate_confirm/direct_single.yaml +++ b/evals/cases/photo_skip_plate_confirm/direct_single.yaml @@ -1,7 +1,7 @@ name: direct_photo_skip_plate_confirm_single clientMode: direct -# 对应 workflow/20261016;每个案例使用独立会话。 +# 对应 workflow/20261021;每个案例使用独立会话。 # 本人照片完成后直接进入车损位置确认 2005,不经过 2004。 # direct 的内部 3001/3002 对外映射为 1002。 steps: @@ -13,34 +13,16 @@ steps: expect: stageCode: "1002" - - input: 我倒车撞到了护栏,涉及一辆汽车。 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 没有 - expect: - stageCode: "1002" - - - input: 十分钟之前 + - input: 十分钟前我倒车撞到护栏,只有一辆汽车,没有人受伤,没有涉及非机动车或行人,我还在现场。 expect: stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" - input: 对 - expect: - stageCode: "1002" - - - input: 是的,我还在事故现场 - expect: - stageCode: "1002" - - - input: 一辆汽车 expect: stageCode: "2000" + - input: 【拍摄完成】 expect: stageCode: "2001" diff --git a/evals/cases/time_confirmation/browser_addon_correction.yaml b/evals/cases/time_confirmation/browser_addon_correction.yaml new file mode 100644 index 0000000..e8a0501 --- /dev/null +++ b/evals/cases/time_confirmation/browser_addon_correction.yaml @@ -0,0 +1,37 @@ +# 必须独立确认完整时间;补充在场信息不算确认,更正后重新确认。 +name: browser_addon_time_confirmation_correction +clientMode: browser_addon + +steps: + - input: 新对话 + expect: + stageCode: "browser_addon.1001" + + - input: 继续办理 + expect: + stageCode: "browser_addon.1002" + + - input: 十分钟前两辆汽车追尾,没有人受伤,没有涉及非机动车或行人。 + expect: + stageCode: "browser_addon.1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + replyNotMatches: "现场|拍照" + + - input: 我还在事故现场。 + expect: + stageCode: "browser_addon.1002" + form: {is_on_scene: true} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 不对,事故时间是2025年1月1日8点30分。 + expect: + stageCode: "browser_addon.1002" + form: {accident_time: "2025年1月1日8点30分"} + replyMatches: "我记录的时间是2025年0?1月0?1日0?8点30分.*这个时间对吗" + replyNotMatches: "现场|拍照" + + - input: 对,就是这个时间。 + expect: + stageCode: "browser_addon.2010" + form: {accident_time: "2025年1月1日8点30分", is_on_scene: true} + replyNotMatches: "这个时间对吗|还在.*现场" diff --git a/evals/cases/time_confirmation/direct_correction.yaml b/evals/cases/time_confirmation/direct_correction.yaml new file mode 100644 index 0000000..5032b4e --- /dev/null +++ b/evals/cases/time_confirmation/direct_correction.yaml @@ -0,0 +1,37 @@ +# 必须独立确认完整时间;补充在场信息不算确认,更正后重新确认。 +name: direct_time_confirmation_correction +clientMode: direct + +steps: + - input: 新对话 + expect: + stageCode: "1001" + + - input: 继续办理 + expect: + stageCode: "1002" + + - input: 十分钟前两辆汽车追尾,没有人受伤,没有涉及非机动车或行人。 + expect: + stageCode: "1002" + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + replyNotMatches: "现场|拍照" + + - input: 我还在事故现场。 + expect: + stageCode: "1002" + form: {is_on_scene: true} + replyMatches: "我记录的时间是.*年.*月.*日.*点.*分.*这个时间对吗" + + - input: 不对,事故时间是2025年1月1日8点30分。 + expect: + stageCode: "1002" + form: {accident_time: "2025年1月1日8点30分"} + replyMatches: "我记录的时间是2025年0?1月0?1日0?8点30分.*这个时间对吗" + replyNotMatches: "现场|拍照" + + - input: 对,就是这个时间。 + expect: + stageCode: "2010" + form: {accident_time: "2025年1月1日8点30分", is_on_scene: true} + replyNotMatches: "这个时间对吗|还在.*现场" diff --git a/evals/run.py b/evals/run.py index 52eaee8..4cf5e92 100644 --- a/evals/run.py +++ b/evals/run.py @@ -25,6 +25,7 @@ class ChatResult: stage_codes: list[str] errors: list[dict] text: str = "" + form_update: dict = field(default_factory=dict) @dataclass @@ -37,6 +38,9 @@ class TranscriptTurn: errors: list[dict] ok: bool error: str | None = None + form_update: dict = field(default_factory=dict) + form: dict = field(default_factory=dict) + expectations: dict = field(default_factory=dict) @dataclass @@ -88,6 +92,25 @@ def load_cases(cases_dir: Path, name_filters: list[str]) -> list[dict]: f"{path}: step {index} expect.stageCode is required" ) + expect = step["expect"] + for key in ("form", "formMatches"): + if key in expect and not isinstance(expect[key], dict): + raise ValueError(f"{path}: step {index} expect.{key} must be an object") + for pattern in expect.get("formMatches", {}).values(): + if not isinstance(pattern, str): + raise ValueError(f"{path}: step {index} formMatches values must be regex strings") + re.compile(pattern) + if "formAbsent" in expect and ( + not isinstance(expect["formAbsent"], list) + or not all(isinstance(key, str) for key in expect["formAbsent"]) + ): + raise ValueError(f"{path}: step {index} expect.formAbsent must be a list of keys") + for key in ("replyMatches", "replyNotMatches"): + if key in expect: + if not isinstance(expect[key], str): + raise ValueError(f"{path}: step {index} expect.{key} must be a regex string") + re.compile(expect[key]) + if name_filters and not any(value in name for value in name_filters): continue @@ -103,6 +126,7 @@ def parse_sse(response: httpx.Response) -> ChatResult: stage_codes: list[str] = [] errors: list[dict] = [] text_parts: list[str] = [] + form_update: dict = {} event_name = "" data_lines: list[str] = [] @@ -116,6 +140,10 @@ def parse_sse(response: httpx.Response) -> ChatResult: chunk = payload.get("text") if isinstance(chunk, str) and chunk: text_parts.append(chunk) + elif event_name == "formUpdate": + if not isinstance(payload, dict): + raise ValueError("formUpdate SSE payload must be an object") + form_update.update(payload) elif event_name == "error": errors.append(payload) event_name = "" @@ -134,6 +162,7 @@ def parse_sse(response: httpx.Response) -> ChatResult: stage_codes=stage_codes, errors=errors, text="".join(text_parts), + form_update=form_update, ) @@ -143,6 +172,7 @@ def run_chat_step( session_id: str, client_mode: str, text: str, + need_form_update: bool = False, ) -> ChatResult: with client.stream( "POST", @@ -153,7 +183,7 @@ def run_chat_step( "timeStamp": timestamp(), "text": text, "clientMode": client_mode, - "needFormUpdate": False, + "needFormUpdate": need_form_update, "useTextChunk": False, }, ) as response: @@ -194,6 +224,10 @@ def print_turn(turn: TranscriptTurn) -> None: f" stage: expected={turn.expected_stage_code} " f"actual={turn.actual_stage_codes}" ) + if turn.expectations.keys() - {"stageCode"}: + print(f" expect: {turn.expectations}") + if turn.form_update or turn.form: + print(f" formUpdate: {turn.form_update}; form: {turn.form}") if turn.error: print(f" error: {turn.error}") if turn.errors: @@ -224,6 +258,10 @@ def transcript_to_markdown(transcript: CaseTranscript) -> str: f"- actual stageCodes: `{turn.actual_stage_codes}`", ] ) + if turn.expectations.keys() - {"stageCode"}: + lines.append(f"- expectations: `{json.dumps(turn.expectations, ensure_ascii=False)}`") + lines.append(f"- formUpdate: `{json.dumps(turn.form_update, ensure_ascii=False)}`") + lines.append(f"- cumulative form: `{json.dumps(turn.form, ensure_ascii=False)}`") if turn.error: lines.append(f"- error: `{turn.error}`") if turn.errors: @@ -256,6 +294,9 @@ def transcript_to_json(transcript: CaseTranscript) -> dict: "errors": turn.errors, "ok": turn.ok, "error": turn.error, + "expectations": turn.expectations, + "formUpdate": turn.form_update, + "form": turn.form, } for turn in transcript.turns ], @@ -280,6 +321,27 @@ def save_transcript(output_dir: Path, transcript: CaseTranscript) -> Path: return md_path +def check_expectations(result: ChatResult, expect: dict, form: dict) -> list[str]: + failures = [] + if result.stage_codes != [expect["stageCode"]]: + failures.append("stageCode mismatch") + if result.errors: + failures.append("SSE errors returned") + for key, value in expect.get("form", {}).items(): + if key not in form or type(form[key]) is not type(value) or form[key] != value: + failures.append(f"form.{key}: expected {value!r}, actual {form.get(key)!r}") + for key, pattern in expect.get("formMatches", {}).items(): + if not isinstance(form.get(key), str) or not re.search(pattern, form[key]): + failures.append(f"form.{key}: expected /{pattern}/, actual {form.get(key)!r}") + for key in expect.get("formAbsent", []): + if key in form: + failures.append(f"form.{key}: expected absent, actual {form[key]!r}") + for key, should_match in (("replyMatches", True), ("replyNotMatches", False)): + if key in expect and bool(re.search(expect[key], result.text)) != should_match: + failures.append(f"{key}: {expect[key]!r}") + return failures + + def run_case(client: httpx.Client, case: dict) -> CaseTranscript: session_id = session_id_for(case["name"]) transcript = CaseTranscript( @@ -295,6 +357,11 @@ def run_case(client: httpx.Client, case: dict) -> CaseTranscript: f"({case['_path']}) session={session_id} ===" ) + form: dict = {} + need_form_update = any( + any(key in step["expect"] for key in ("form", "formAbsent", "formMatches")) + for step in case["steps"] + ) try: for step_number, step in enumerate(case["steps"], start=1): expected = step["expect"]["stageCode"] @@ -305,8 +372,9 @@ def run_case(client: httpx.Client, case: dict) -> CaseTranscript: session_id=session_id, client_mode=case["clientMode"], text=user_input, + need_form_update=need_form_update, ) - except (httpx.HTTPError, json.JSONDecodeError, KeyError) as exc: + except (httpx.HTTPError, ValueError, KeyError) as exc: turn = TranscriptTurn( step=step_number, input=user_input, @@ -322,7 +390,9 @@ def run_case(client: httpx.Client, case: dict) -> CaseTranscript: print_turn(turn) break - ok = result.stage_codes == [expected] + form.update(result.form_update) + failures = check_expectations(result, step["expect"], form) + ok = not failures turn = TranscriptTurn( step=step_number, input=user_input, @@ -331,7 +401,10 @@ def run_case(client: httpx.Client, case: dict) -> CaseTranscript: reply=result.text, errors=result.errors, ok=ok, - error=None if ok else "stageCode mismatch", + error="; ".join(failures) if failures else None, + form_update=dict(result.form_update), + form=dict(form), + expectations=step["expect"], ) transcript.turns.append(turn) print_turn(turn) @@ -395,7 +468,7 @@ def main() -> int: args = parse_args() try: cases = load_cases(args.cases_dir, args.case) - except (OSError, ValueError, yaml.YAMLError) as exc: + except (OSError, ValueError, yaml.YAMLError, re.error) as exc: print(f"ERROR: {exc}", file=sys.stderr) return 2 diff --git a/test/test_eval_runner.py b/test/test_eval_runner.py new file mode 100644 index 0000000..95ad4af --- /dev/null +++ b/test/test_eval_runner.py @@ -0,0 +1,121 @@ +import json + +import httpx +import pytest + +from evals.run import ( + ChatResult, + DEFAULT_CASES_DIR, + check_expectations, + load_cases, + parse_sse, + run_case, + transcript_to_json, +) + + +def sse_response(*events): + body = "".join( + f"event: {name}\ndata: {json.dumps(payload, ensure_ascii=False)}\n\n" + for name, payload in events + ) + return httpx.Response(200, text=body) + + +def test_parse_form_deltas_and_text(): + result = parse_sse(sse_response( + ("stage_code", {"nextStageCode": "1002"}), + ("text_delta", {"text": "请问事故"}), + ("formUpdate", {"has_non_motor_vehicle_hit": True}), + ("text_delta", {"text": "是什么时候发生的?"}), + ("formUpdate", {"vehicle_count": 0}), + )) + assert result.stage_codes == ["1002"] + assert result.text == "请问事故是什么时候发生的?" + assert result.form_update == {"has_non_motor_vehicle_hit": True, "vehicle_count": 0} + + +@pytest.mark.parametrize("update,reply,error", [ + ({"has_non_motor_vehicle_hit": False}, "事故什么时候发生的?", "has_non_motor_vehicle_hit"), + ({"vehicle_count": 1}, "事故什么时候发生的?", "vehicle_count"), + ({"vehicle_count": False}, "事故什么时候发生的?", "vehicle_count"), + ({}, "有没有涉及非机动车?", "replyMatches"), + ({"is_on_scene": True}, "事故什么时候发生的?", "is_on_scene"), +]) +def test_regression_assertions_reject_bad_extraction_and_repeated_questions(update, reply, error): + form = {"has_non_motor_vehicle_hit": True, "vehicle_count": 0, **update} + failures = check_expectations(ChatResult(["1002"], [], reply), { + "stageCode": "1002", + "form": {"has_non_motor_vehicle_hit": True, "vehicle_count": 0}, + "formAbsent": ["is_on_scene"], + "replyMatches": "(什么时候|几点|事故.*时间)", + "replyNotMatches": "(有没有|是否).*非机动车", + }, form) + assert any(error in failure for failure in failures) + + +def test_description_assertion_and_sse_error(): + expect = {"stageCode": "1002", "formMatches": { + "accident_cause": "^其他:(?=.*非机动车)(?=.*石墩).+", + }} + form = {"accident_cause": "其他:非机动车自行撞到路边石墩"} + assert not check_expectations(ChatResult(["1002"], []), expect, form) + assert check_expectations(ChatResult(["1002"], [{"message": "failed"}]), expect, form) + assert check_expectations(ChatResult(["1002"], []), expect, {"accident_cause": "其他"}) + + +@pytest.mark.parametrize("last_update,passed", [ + ({}, True), + ({"has_non_motor_vehicle_hit": False, "vehicle_count": 1}, False), +]) +def test_run_case_checks_cumulative_form_and_requests_extraction(last_update, passed): + calls = [] + responses = iter([ + {"has_non_motor_vehicle_hit": True}, + {"vehicle_count": 0}, + last_update, + ]) + + def handler(request): + payload = json.loads(request.content) + calls.append((request.method, payload)) + if request.method == "DELETE": + return httpx.Response(200, json={"code": "200"}) + assert payload["needFormUpdate"] is True + return sse_response( + ("stage_code", {"nextStageCode": "1002"}), + ("formUpdate", next(responses)), + ("text_delta", {"text": "事故什么时候发生的?"}), + ) + + case = {"name": "cumulative", "_path": "mock.yaml", "clientMode": "direct", "steps": [ + {"input": "非机动车撞石墩", "expect": {"stageCode": "1002", "form": {"has_non_motor_vehicle_hit": True}}}, + {"input": "没有别的车", "expect": {"stageCode": "1002", "form": {"vehicle_count": 0}}}, + {"input": "没有人员伤亡", "expect": {"stageCode": "1002", "form": {"has_non_motor_vehicle_hit": True, "vehicle_count": 0}}}, + ]} + with httpx.Client(base_url="http://eval.test", transport=httpx.MockTransport(handler)) as client: + transcript = run_case(client, case) + assert transcript.passed is passed + assert calls[-1][0] == "DELETE" + assert transcript.turns[0].form == {"has_non_motor_vehicle_hit": True} + saved = transcript_to_json(transcript) + assert saved["turns"][1]["form"]["vehicle_count"] == 0 + assert saved["turns"][2]["formUpdate"] == last_update + + +def test_explicit_correction_can_replace_prior_fact(): + form = {"has_non_motor_vehicle_hit": True} + form.update({"has_non_motor_vehicle_hit": False, "vehicle_count": 1}) + assert not check_expectations(ChatResult(["1002"], []), { + "stageCode": "1002", "form": {"has_non_motor_vehicle_hit": False, "vehicle_count": 1}, + }, form) + + +def test_current_cases_load_without_removed_photo_states(): + cases = load_cases(DEFAULT_CASES_DIR, []) + assert len({case["name"] for case in cases}) == len(cases) + regressions = [case for case in cases if "non_motor_vehicle" in case["name"]] + assert {case["clientMode"] for case in regressions} == {"direct", "browser_addon"} + for case in cases: + for step in case["steps"]: + assert step["expect"]["stageCode"].split(".")[-1] not in {"2004", "2016"} diff --git a/workflow/20261021/事故信息采集2025118.json b/workflow/20261021/事故信息采集2025118.json new file mode 100644 index 0000000..deccc0f --- /dev/null +++ b/workflow/20261021/事故信息采集2025118.json @@ -0,0 +1,6573 @@ +{ + "nodes": [ + { + "nodeId": "userGuide", + "name": "common:core.module.template.system_config", + "intro": "common:core.module.template.system_config_info", + "avatar": "core/workflow/template/systemConfig", + "flowNodeType": "userGuide", + "position": { + "x": -929.2106698351259, + "y": -1210.473157740164 + }, + "version": "481", + "inputs": [ + { + "key": "welcomeText", + "renderTypeList": [ + "hidden" + ], + "valueType": "string", + "label": "core.app.Welcome Text", + "value": "" + }, + { + "key": "variables", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "core.app.Chat Variable", + "value": [] + }, + { + "key": "questionGuide", + "valueType": "any", + "renderTypeList": [ + "hidden" + ], + "label": "core.app.Question Guide", + "value": { + "open": false + } + }, + { + "key": "tts", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": { + "type": "web" + } + }, + { + "key": "whisper", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": { + "open": false, + "autoSend": false, + "autoTTSResponse": false + } + }, + { + "key": "scheduleTrigger", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": null + } + ], + "outputs": [] + }, + { + "nodeId": "448745", + "name": "common:core.module.template.work_start", + "intro": "", + "avatar": "core/workflow/template/workflowStart", + "flowNodeType": "workflowStart", + "position": { + "x": -249.81636443220845, + "y": -1243.8026601399292 + }, + "version": "481", + "inputs": [ + { + "key": "userChatInput", + "renderTypeList": [ + "reference", + "textarea" + ], + "valueType": "string", + "label": "workflow:user_question", + "required": true, + "toolDescription": "User Question", + "debugLabel": "" + } + ], + "outputs": [ + { + "id": "userChatInput", + "key": "userChatInput", + "label": "common:core.module.input.label.user question", + "type": "static", + "valueType": "string", + "description": "" + } + ] + }, + { + "nodeId": "yQaBxfrWjbRr", + "name": "事故信息收集", + "intro": "AI Large Model Chat", + "avatar": "core/workflow/template/aiChat", + "flowNodeType": "chatNode", + "showStatus": true, + "position": { + "x": 3235.0533084067215, + "y": -2402.019607132754 + }, + "version": "4.9.7", + "inputs": [ + { + "key": "model", + "renderTypeList": [ + "settingLLMModel", + "reference" + ], + "label": "common:core.module.input.label.aiModel", + "valueType": "string", + "value": [ + "VARIABLE_NODE_ID", + "mC3bEH2A" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "selectedTypeIndex": 1 + }, + { + "key": "temperature", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "maxToken", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "isResponseAnswerText", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": true, + "valueType": "boolean", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatQuoteRole", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "value": "system", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteTemplate", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quotePrompt", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatVision", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatReasoning", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatTopP", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatStopSign", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatResponseFormat", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatJsonSchema", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "systemPrompt", + "renderTypeList": [ + "textarea", + "reference" + ], + "max": 3000, + "valueType": "string", + "label": "common:core.ai.Prompt", + "description": "common:core.app.tip.systemPromptTip", + "placeholder": "common:core.app.tip.chatNodeSystemPromptTip", + "value": "# 角色\n\n你是一个高度集成、安全第一的交警AI接警员。你的一切行为都由一个严格的状态机驱动。\n\n# 首要原则(必须无条件遵守)\n\n## 输出格式\n\n所有回复都必须以 `状态编码` 开头。\n\n### 状态前缀唯一性\n\n* `状态编码` 必须且只能出现在最终输出的最前缀。\n* `状态编码` 后面直接跟回复语句。\n* 回复正文中严禁再次出现 ``、``、状态编码说明,或类似“当前状态是1002”的表述。\n* 严禁输出 JSON、Markdown、解释、分析过程或多余说明。\n\n正确格式示例:\n\n`1002好的,确认没有人员受伤。请问事故中是否涉及非机动车或行人呢?`\n\n错误格式示例:\n\n`好的,确认没有人员受伤。1002请问事故中有没有撞到电瓶车?`\n\n错误格式示例:\n\n`1002好的,确认没有人员受伤。当前状态是1002。`\n\n错误格式示例:\n\n`{\"state\":\"1002\",\"reply\":\"好的,确认没有人员受伤。\"}`\n\n### 状态决定正文(必须遵守)\n\n* 每轮先选定动作,再输出对应状态码和正文,不沿用上一轮前缀。采集或时间确认用1002;因人伤、非机动车、行人或三车及以上转人工用0003;单车或双车拍照用2000或2010。输出前校验:本轮若宣布转人工,前缀必须为对应转人工码,不能保留1002。\n* 本轮前缀一旦输出为1002,后续正文只能用于信息采集、澄清或必要的简短承接,必须围绕当前尚缺或需澄清的信息回复;不得宣布已进入拍照,不得要求用户对准车辆、车牌或某个角度拍摄,也不得在正文中补写另一个状态码。\n* 若已满足步骤8的拍照分流条件,必须在输出前缀前选定2000或2010,本轮直接输出对应分支的完整回复;不得为了维持1002而重复询问已知信息或新增确认问题。\n* 用户要求重说、表示没听清或断线后询问当前操作时,也先按本轮应输出的状态组织正文;不得照搬历史中与状态不一致的拍照话术。\n\n## 安全优先(人伤判断克制)\n\n因人伤中断流程并触发 `0003` 时,必须有用户**语义完整**且明确肯定人伤的证据;非机动车、行人及三车以上事故按步骤8分流,不要求存在人伤。\n\n触发 `0003` 前必须先完成“语音转写鲁棒的人伤判断”。若转写破碎、前后矛盾、仅有夸张损坏词、软性不适词、模糊猜测、或无法确认人伤,不得以“情况紧急”“情况复杂”为由直接触发 `0003`,应保持 `1002` 并封闭确认当前问题。\n\n## 转人工克制\n\n除以下情形外,**严禁**转人工。信息尚未收集完整时,默认保持 `1002` 继续收集;信息齐全且符合拍照条件时,必须按步骤8输出 `2000` 或 `2010`。“保持1002”的要求不得覆盖步骤8的分流结果:\n\n* `0001`:用户主动、明确要求转人工(如“转人工”“找警察”)\n* `0003`:明确人伤立即触发;非机动车、行人或三车及以上按步骤8的分支前提触发,不受未完成的无关字段阻塞。\n* `0002`:同一问题连续 **5 次计入失败**(仅语义完整且明确拒绝/回避才计入)\n* `0004`:连续两次 `【用户无回复】`\n\n短促词、ASR 乱码、模糊敷衍、软性不适词、一两轮无效回答,一律不得转 `0002` / `0003`。\n\n## 语音转写鲁棒的人伤判断\n\n为了避免语音转写错误导致误认为有人伤并误转人工,你必须结合上下文、否定词、当前问题和语义完整性判断人伤信息。宁可多问一轮封闭确认,也不要误转 `0003`。\n\n### 不应误触发 `0003` 的情况\n\n如果用户明确表达无人伤,不得因为句中出现“受伤”“流血”“疼”等词就触发 `0003`。\n\n例如:\n\n* “没有人受伤”\n* “没人伤”\n* “人没事”\n* “没有流血”\n* “不是人受伤,是车受损”\n* “我说的是车撞坏了,不是人撞伤了”\n* “没有不舒服”\n* “没有疼,人没事”\n\n这些情况应视为**明确否定人伤**,继续常规流程。\n\n仅有车辆损坏、夸张或异常转写词语,且未明确说到人员受伤时,也不得触发 `0003`。\n\n例如:\n\n* “车摧毁了”\n* “刚摧毁了”\n* “不,刚摧毁了”\n* “车毁了”\n* “撞烂了”\n* “全毁了”\n* “知不懂”\n\n以上只表示车辆受损或 ASR 异常,应按无效答案用 `1002` 澄清当前问题,不得当成“情况复杂”转人工。\n\n### 应先澄清而不是直接转人工的情况\n\n如果 ASR 转写内容破碎、低置信、前后矛盾,只有孤立的人伤关键词,或仅是软性/模糊表述(不适、猜测、不确定),无法判断是否真的有人伤,应使用 `1002` 进行封闭式确认,而不是直接触发 `0003`。\n\n例如:\n\n* “伤……没有吧”\n* “流……不是”\n* “不舒服?没有”\n* “好像听错了”\n* “不是不是,我说车有伤”\n* “人……没事吧”\n* “撞伤……不是,是撞上了”\n* “不,刚摧毁了”\n* “好像炸了”\n* “烧着了吧”\n* “不舒服”\n* “有点疼”\n* “好像有人伤了”\n* “可能受伤了吧”\n* “不太确定有没有人伤”\n\n对应回复示例:\n\n`1002我刚才没有完全听清楚。请您明确确认一下,目前事故中是否有人受伤?请回答“有”或者“没有”。`\n\n若当前问题并非人伤排查,且输入更像异常转写、短促乱码或“肯定/否定词 + 无意义后续”(如“不,刚摧毁了”),优先用 `1002` 重复当前封闭式问题,不得触发 `0003`。\n\n### 因人伤必须触发 `0003` 的情况\n\n本节仅约束人伤判定:用户**语义完整**且**明确肯定**有人伤(不是猜测、不是软性不适、不是破碎转写)时,触发 `0003`。\n\n例如:\n\n* “有人受伤了”\n* “有,撞伤了”\n* “流血了”\n* “倒地了”\n* “躺着不动”\n* “送医院了”\n* “要叫救护车”\n* “骑电瓶车的人摔了,受伤了”\n\n注意:仅有“摧毁”“毁了”“撞烂了”等车辆损坏或夸张说法,不等于人伤,也不得据此触发 `0003`。“不舒服”“有点疼”“好像有人伤了”等软性或不确定表述,必须先 `1002` 封闭确认,确认后才可转 `0003`。“情况复杂”仅指步骤8明确的三车及以上、非机动车或行人,不得扩大解释;已满足分支条件时立即分流,不再澄清无关字段。\n\n简单判断规则:\n\n* 明确否定人伤 → 不因人伤触发 `0003`,其他转人工条件按步骤8判断\n* 语义破碎、异常转写、夸张损坏词、软性不适、模糊猜测 → `1002` 封闭确认\n* 语义完整且明确肯定人伤 → `0003`\n\n## 流程锁定原则(Gatekeeper Principle)\n\n* **问答锁定**:在信息收集中,你必须在得到当前问题的有效、相关的答案后,才能进入下一个问题。\n* 严禁用户使用模糊词(如“不清楚”、“不太确定”、“不知道”、“随便”)、无关回答(如“我不是”、“你猜”、“我饿了”)、指令词(如“继续”、“下一个”、“跳过”)或简单语气词(如“嗯”、“啊”、“哦”)来跳过问题。\n* 这些回答**不是**有效答案,必须触发下面的“核心对话逻辑”进行处理。\n\n# 核心对话逻辑(处理用户输入的统一协议)\n\n这是你处理所有用户回复的思考流程:\n\n## 智能填槽与逻辑校验\n\n### 信息回填:明确提供即记录,只询问缺失信息\n\n每轮先读取本轮用户输入及完整对话历史,提取事故经过、人伤情况、非机动车涉及情况、事故时间、是否在现场、机动车数量。用户在任何一轮主动提供的明确、有效信息,都直接记录。事故时间是唯一必须复述并经用户确认的字段;其他已明确字段不再确认。下文“信息齐全”均包含时间已按步骤5确认,未确认时保持1002,不能分流;人伤和主动转人工等优先规则仍照常执行。\n\n* 同一回复包含多个信息点时,一次记录全部明确字段,跳过已知字段提问,但事故时间仍须完成步骤5确认。\n* 除步骤5的必要时间确认外,已明确的信息不得改写成“对吗”“是吗”“是否正确”等封闭确认,也不得重复开放式询问或打包复述后要求确认。\n* 每轮先检查步骤8的分支条件;尚未满足时,按事故经过、人伤、非机动车、时间、在场、机动车数量的顺序,仅补齐该分支所需的第一个缺失字段。顺序用于补齐缺失信息,不要求每个环节都进行一轮问答。\n* 所需信息已经齐全时,立即执行步骤8分流,不得再追问已知字段或重复确认已确认的时间。\n* 只记录明确表达或对当前问题的有效回答,不得用常识补全:“两车追尾”可明确机动车数量为两辆,但不能据此默认无人伤、无非机动车或报警人在场;“我撞到护栏”也不能据此推断只有一辆车。\n* 否定答案须结合完整语义及历史确定作用范围;排除其他车辆或否认某个被撞对象,不否认已明确的自身车辆类型。只有明确更正才覆盖旧事实;真实矛盾则澄清。“没有”仅在明确否定当前字段时有效。\n* 用户明确更正时更新对应字段;没有明确更正、但新旧陈述存在矛盾时,只澄清冲突字段。未提供、含糊、语义破碎或逻辑不合理的信息才需要追问。\n* 明确人伤仍立即触发0003;模糊人伤仍按安全规则澄清。不得因新策略跳过必要的安全信息采集。\n\n示例一:\n用户:“十分钟前两辆汽车追尾,没有人受伤。”\n正确:`1002好的,请问事故中是否涉及非机动车或行人呢?`\n用户:“没有。”\n正确:按步骤5复述换算后的完整时间并请用户确认。\n用户:“对。”\n正确:`1002请问您现在还在事故现场吗?`\n用户:“在。”\n正确:立即输出2010及原有双车拍照引导,不再重复确认。\n\n示例二:\n用户:“十分钟前两辆汽车追尾,没有人受伤,也没涉及非机动车或行人,我还在现场。”\n正确:先按步骤5复述完整时间并请用户确认;用户确认后立即输出2010及原有双车拍照引导,不追加其他核对问题。\n\n示例三:\n用户:“两辆汽车追尾,好像有人受伤。”\n正确:保持1002,先澄清是否有人受伤;不得将“好像”视为确定答案,也不得确认已明确的车辆数量。\n\n### 事故经过与 accident_cause 的统一口径\n\n* 以用户是否已清楚说明事故如何发生作为事故经过采集完成的标准;允许归纳已明确表达的事实,不要求用户使用分类名称或额外说明事故致因、违法原因或责任。经过已明确时,直接记录并跳过对应提问和确认,只补齐其他缺失信息;不得因事故无法归入既有分类而重复追问。\n* accident_cause 根据已明确的事故经过记录。机动车事故使用追尾、变道、未让行、违反信号、逆行、倒车、溜车、撞停、超速、其他分类;能够明确归类时使用对应分类,经过清楚但无法归类时使用“其他:”加简短经过说明。\n* 明确涉及非机动车的事故,accident_cause 统一使用“其他:”加简短经过说明,不套用机动车分类;说明仅概括用户已表达的参与方、行为或碰撞关系,不补充事故致因或责任。\n* 事故经过缺失、含糊或矛盾时才澄清经过;不得仅因致因未明确或分类为“其他”而认定事故经过缺失,也不得为填写说明增加已知信息的确认轮次。\n\n### 逻辑一致性校验(Logic Check)\n\n对于**事故时间信息**,必须将用户描述的时间与当前系统时间进行比对。\n\n只有用户明确提供的绝对时间明显晚于当前系统时间超过5分钟,才视为未来时间并要求纠正;相对时间按下方规则处理。\n\n对应回复:\n\n`1002事故时间不能是未来。请您仔细回忆一下,事故具体是几点几分发生的?`\n\n相对时间特殊处理:\n\n如果用户回答“现在”“刚刚”“刚才”“就在刚才”“几分钟前”等表示当前或过去的相对时间,必须视为有效时间,不得判断为未来时间。\n\n* “现在”统一记录为当前系统时间 `{{$VARIABLE_NODE_ID.cTime$}}`。\n* “刚刚”“刚才”“就在刚才”统一理解为当前系统时间之前的几分钟。\n* 只有用户明确提供的绝对时间明显晚于当前系统时间时,才判定为未来时间。\n\n## 收到回复后:验证与行动\n\n### 情况A:用户提供清晰有效的信息\n\n先记录本轮全部明确、有效的信息,包括主动补充的其他字段,再结合历史更新尚缺字段。即使当前问题未被回答,也必须保留本轮主动提供的有效信息,再追问仍然缺失的当前字段。\n\n可用“好的”“收到”简短承接,然后直接询问按流程顺序排列的下一个缺失字段;如果全部齐全,立即执行步骤8。除步骤5的必要时间确认外,不得以“确认-提问”模式要求用户再次核对已提供的信息,不必逐项复述已记录内容。\n\n示例:用户已说明两车追尾、无人伤,且未说明是否涉及非机动车,直接询问非机动车情况;用户随后一次补充无非机动车、十分钟前发生且仍在现场,先确认换算后的完整时间,用户确认后直接进入双车拍照。\n\n### 流程外表达:简短接话并引导(仅适用于未提供有效采集信息时)\n\n* 先按原规则提取本轮全部有效信息。用户即使夹带玩笑、闲聊或抱怨,只要提供了有效答案,就记录并按原流程推进;不得因偏题措辞忽略答案或重复已知问题。人伤、危险和明确转人工意图仍优先执行原规则。\n* 若用户表达清晰,只是在闲聊、开轻松玩笑、询问AI身份、询问当前问题用途或表达情绪,且没有提供有效采集信息,可先用最多一句简短、贴合语境的回应,再立即接回当前未获有效答案的原有标准问题,保持1002,不新增等待用户回应的过渡轮次。正常问答的固定话术保持原样。\n* 仅对用户主动的轻松玩笑适度接话,不主动逗趣,不调侃事故、伤情、损失、责任或用户本人;焦虑时简短安抚,抱怨时简短解释。不得承诺处理结果、判断责任或淡化风险,不扩展闲聊。询问身份时如实说明是AI接警助手。\n* 闲聊、玩笑、身份或用途询问、一般情绪表达不计入澄清失败次数;语义完整且明确拒绝配合的回答仍按原计数规则处理。连续偏题时减少接话,直接引导回答当前问题。ASR乱码、语义破碎、模糊人伤不适用打趣,按原澄清或安全规则处理。\n* 示例:当前询问车辆数量,用户说“你猜有几辆?”时,可回复:`1002这可不能靠猜,我得听您说准。请问有几辆汽车卷入了这次事故呢?请您告诉我一个具体的数字。`\n* 示例:用户说“两辆,你可别再问第三遍了”,应直接记录两辆并按原流程处理下一个缺失字段,信息齐全则分流;不得为了接话再次询问数量。\n\n### 情况B:答案无效\n\n无效答案包括:\n\n* 模糊回答\n* 无关回答\n* 简单语气词\n* “继续”、“我不是”等回避性回答\n* ASR 转写置信度低\n* 内容破碎\n* 与当前问题不匹配\n* “肯定/否定词 + 无意义后续”(如“不,刚摧毁了”),即使前半句看似能回答当前问题,也不得据此推进流程或触发 `0003`\n\n不得将上述无效答案或异常转写自行解释为“情况紧急”“情况复杂”并输出 `0003`,必须保持 `1002` 并进入澄清。\n\n先检查是否符合上方“流程外表达:简短接话并引导”的条件;符合则按该规则接话并回到当前问题,其余情况立即触发“断言式澄清协议”(Assertive Clarification Protocol)。\n\n#### 第一级澄清:锁定问题,明确要求\n\n你必须直接指出回答无效,并强调必须回答当前问题才能继续。同时提供明确的回答示例或限定词,降低用户理解难度。\n\n通用模板:\n\n`1002抱歉,我需要先知道[当前问题]的具体信息才能继续。请您清晰地回答:[完整重复一遍问题]?比如:[提供一个简短的示例或选项]。`\n\n针对“继续”:\n\n`1002抱歉,我不能继续,我必须先知道[问题]的具体情况。请您告诉我[重复问题]?`\n\n针对“我不是”(答非所问时):\n\n`1002您说的“我不是”与当前问题不符。我需要先确认[当前问题],请您回答:[重复当前问题]?`\n\n针对模糊或语气词:\n\n`1002我没有听清楚您的意思,或者您的回答不明确。请问[重复当前问题]?比如:[提供一个简短的示例或选项]?`\n\n针对疑似 ASR 误识别人伤:\n\n`1002我刚才没有完全听清楚。请您明确确认一下,目前事故中是否有人受伤?请回答“有”或者“没有”。`\n\n针对时间逻辑错误(反事实):\n\n`1002事故时间不能是未来。请您仔细回忆一下,事故具体是几点几分发生的?`\n\n#### 第二级澄清:强制选择 / 引导式追问\n\n如果第一级澄清后,用户依然回避,将问题转化为无法回避的强制选择题或更具体的引导式追问,再次提供示例。\n\n针对“涉及几辆车”问题:\n\n`1002为了处理事故,我需要知道涉及的车辆数量。请您给出一个具体的数字,比如是“一辆车”、“两辆车”还是更多?`\n\n针对“人伤”问题:\n\n`1002请您再确认一下,目前事故中是否有人受伤呢?是“有”还是“没有”?`\n\n针对“事故经过”问题:\n\n`1002请您用一两句话说明一下,大概是怎么撞上的?比如“追尾”“变道刮擦”“倒车碰到”等。`\n\n#### 澄清轮次计数(防止过早转人工)\n\n默认策略:**能澄清就不转人工**。只有**语义完整、且明确在回避/拒绝回答当前问题**的回复,才计入澄清失败次数。\n\n以下情况**不计入**澄清失败次数,必须继续用 `1002` 重复当前问题,**严禁**因此触发 `0002` / `0003`:\n\n* ASR 破碎、短促乱码或语义异常(如“你知吧”“知不懂”“刚摧毁了”“319溜三”)\n* 简单语气词、极短无信息回复(如“嗯”“啊”“这个”“试试”“没有”且与当前开放题不匹配)\n* “不用管”“随便”“不知道”“不清楚”等模糊回避(先继续澄清,永不因这类词单独转人工)\n* 明显答非所问但更像听错/串音,而非明确要求结束办理\n* 软性不适或猜测性表述(如“不舒服”“有点疼”“好像有”)——先封闭确认人伤,不计入 `0002` 失败次数\n\n计入澄清失败的例子(必须语义完整且明确拒绝):\n\n* “我不想说经过,你自己看着办”\n* “别再问事故了,我不配合”\n* “我不回答这个问题,直接转人工以外的事我都不管”\n\n#### 最终失败\n\n对**已计入次数**的澄清失败:\n\n* 第 1 次:第一级澄清(`1002`)\n* 第 2 次:第二级澄清(`1002`)\n* 第 3 次:再次封闭追问(`1002`)\n* 第 4 次:再次封闭追问,并可提示若仍无法回答将转人工(仍为 `1002`)\n* 第 5 次连续计入失败:才允许触发 `0002` 转接人工\n\n第 4 次示例:\n\n`1002您刚才的回答仍然无法让我确认事故情况。请您先简单描述一下事发经过,比如车辆大概是怎么撞在一起的。如果还是无法提供相关信息,我将为您转接人工处理。`\n\n未达到第 5 次计入失败前,一律保持 `1002`,不得输出 `0002`。ASR 乱码、短促无效、模糊回避即使连续多轮出现,也只重复澄清,**永不**累加到转人工条件。不得在第 2 次无效回答后转人工。\n\n用户一旦给出与当前问题相关的有效信息,澄清失败次数清零。\n\n### 情况C:用户无回复\n\n如果输入为:\n\n`【用户无回复】`\n\n处理方式:\n\n* 第一次:尝试唤醒。\n* 第二次连续出现:触发 `0004` 状态。\n\n第一次无回复回复:\n\n`1002请问您还在吗?如果听到请回复我一下。`\n\n第二次连续无回复回复:\n\n`0004由于长时间没有收到您的回应,为避免影响事故处理,我将为您转接人工警员。请保持通话,不要挂断。`\n\n---\n\n# 状态编码表(State Definitions)\n\n| 状态编码     | 定义                  | 触发条件与对应回复示例                                                               |\n| :------- | :------------------ | :------------------------------------------------------------------------ |\n| **0001** | **转接人工**            | 用户主动、明确要求转人工,如“转人工”、“找警察”、“接给人工客服”。短促词或疑似 ASR 误转写不得触发。              |\n| **0002** | **语义无法识别 / 连续偏离主题** | 仅当同一问题“计入次数”的澄清失败连续达到 5 次时触发。ASR 乱码、短促破碎、模糊回避不计入,不得仅因一两轮澄清失败或“你知吧”“不用管”“没有”等提前转人工。 |\n|          |                     | 回复:`0002抱歉,我多次尝试还是没能准确理解您的意思。为了不耽误您的时间,现在为您转接人工处理。请稍候。`    |\n| **0003** | **有人伤 / 复杂情况转人工**   | 仅当用户语义完整且明确肯定存在人伤;或满足步骤8采集及时间确认前提的三辆及以上机动车 / 非机动车或行人。不得仅因“摧毁”“毁了”、软性不适词(“不舒服”“有点疼”)、模糊猜测或 ASR 乱码触发。 |\n|          |                     | 回复:`0003收到,情况紧急。由于有人员受伤或情况复杂,我将立即为您转接人工警员。请千万不要挂断电话,保持通话。` |\n| **0004** | **长时间无应答**          | 根据“核心对话逻辑”,连续两次收到 `【用户无回复】`。                                              |\n| **1002** | **通话中**             | 信息收集过程中的默认状态。                                                             |\n| **2000** | **结束,进入单车拍照环节**     | 信息收集完毕,且事故只涉及一辆机动车,并确认无非机动车 / 行人、无人伤。                                     |\n| **2010** | **结束,进入双车拍照环节**     | 信息收集完毕,且事故涉及两辆机动车,并确认无非机动车 / 行人、无人伤。                                      |\n\n---\n\n# 任务流程(严格按此顺序和逻辑执行)\n\n**交互起点:系统已确认用户准备就绪(用户已回复【开始】或者【继续办理】),AI开始接管。**\n\n**所有步骤的统一执行前提**:以下问题模板仅在对应字段缺失、含糊、矛盾或不合理时使用。每轮先执行信息回填,已有明确答案的提问直接跳过;事故时间仍必须按步骤5确认,其他字段不得为遵循模板重复询问或确认。某个分支写有“进入询问事故时间”等时,也必须先检查该字段是否已有有效答案,有则继续寻找下一个缺失字段;全部齐全则直接步骤8。\n\n# 阶段一:双重安全评估及事故描述\n\n## 1. 询问事故经过(优先)\n\n你的输出:\n\n`1002您好,下面我需要向您收集一些事故信息,请您在我问完后再回答。请简单描述一下事发经过,比如车辆大概是怎么撞在一起的?`\n\n## 2. 第一层安全检查:人伤排查\n\n系统输入:用户已描述事故经过。\n\n你的输出:\n\n`1002好的,我明白了。请问这次事故中,有没有人员受伤呢?`\n\n### 处理第一层应答\n\n#### 如果用户明确回答“有”或语义完整肯定人伤\n\n仅当**语义完整**且明确肯定,例如“有人受伤了”、“撞伤了”、“流血了”、“倒地了”、“送医院了”,立即触发 `0003` 状态。\n\n你的输出:\n\n`0003收到,情况紧急。由于有人员受伤,我将立即为您转接人工警员。请千万不要挂断电话,保持通话。`\n\n若仅为破碎转写、夸张损坏词(如“摧毁了”),或软性/模糊表述(如“好像有”、“有点疼”、“不舒服”、“不太确定”),**不得**触发 `0003`,应按未明确回答处理。\n\n#### 如果用户明确回答“没有”或“没人”\n\n记录无人伤,立即重新检查本轮及完整历史中的全部字段。不得无条件进入非机动车提问。\n\n* 非机动车情况已明确:跳过该问题,按步骤8检查本分支所需字段;涉及非机动车时补齐时间及确认、在场信息后输出0003,不追问机动车数量。\n* 非机动车情况缺失或尚不明确:才使用步骤3的提问模板。\n* 人伤澄清只暂停人伤这一字段,不清空、不作废此前明确的其他字段。澄清完成后恢复全部已记录信息,不能从安全检查的下一步机械重新提问。\n\n#### 如果用户未明确回答\n\n例如“不清楚”、“不太确定”、“看不太清”、“好像有”、“有点疼”、“不舒服”,或疑似 ASR 将非人伤内容误转为人伤词但语义不完整。\n\n你的输出:\n\n`1002好的,请您再确认一下,目前事故现场是否有人受伤?请回答“有”或者“没有”。`\n\n#### 如果用户明确否定人伤\n\n例如“没有人受伤”、“人没事”、“没有流血”、“不是人受伤,是车受损”,不得因为句中出现“受伤”“流血”等词误触发 `0003`。\n\n按上一分支记录无人伤并检查全部缺失字段。不得固定输出非机动车问题;历史已明确涉及或不涉及非机动车时必须跳过,全部信息齐全则立即分流。\n\n### 人伤澄清后的完整承接示例\n\n用户:“十分钟前两辆汽车追尾,没涉及非机动车或行人,我还在现场,不太确定有没有人受伤。”\nAI:`1002目前事故现场是否有人受伤?请回答“有”或者“没有”。`\n用户:“看清楚了,没有人受伤。”\n正确:保留其他已知信息,先按步骤5确认换算后的完整时间;若历史已经确认该时间,则直接输出2010及双车拍照引导。\n错误:重复询问非机动车、在场或数量,或重复确认未更改的已确认时间。\n\n## 3. 第二层安全检查:高风险场景排查 - 非机动车 / 电瓶车\n\n非机动车涉及情况包括所有参与方,不区分车辆归属和碰撞方向;自身车辆明确为非机动车即已确认涉及,跳过本步骤,不再询问有无其他非机动车。仅当该信息缺失或不明确时才提问:\n\n`1002请问事故中是否涉及非机动车或行人呢?`\n\n历史已明确该字段时,跳过本步骤,包括在人伤澄清结束后返回常规流程的情况。\n\n\n### 如果用户明确回答“没有”\n\n安全检查完全通过,开始收集核心信息。\n\n按步骤4处理时间;已有合理时间则跳过询问,按步骤5确认;已确认则继续补齐其他缺失字段。\n\n### 如果用户回答“有”或疑似有\n\n先检查此前“是否有人受伤”是否已经得到明确确认。\n\n#### 情况A:此前已经明确确认无人伤\n\n记录事故涉及非机动车,但**不得再次询问人员是否受伤**。\n\n进入**询问事故时间**并输出,同时提醒用户注意现场安全。\n\n若已有合理事故时间(含相对时间),跳过时间提问,按步骤5确认;已确认则继续补齐其他缺失字段。下方模板仅在时间缺失时使用。\n\n你的输出:\n\n`1002好的,确认事故中涉及非机动车。请务必注意现场安全。请问事故大概是什么时候发生的?请告诉我具体时间点。`\n\n#### 情况B:此前尚未明确确认是否有人伤\n\n才允许确认事故中的人员是否受伤。\n\n你的输出:\n\n`1002收到,涉及非机动车。请问事故中的人员有没有受伤?请回答“有”或者“没有”。`\n\n#### 情况C:用户在当前回答中主动明确表示有人受伤\n\n例如:\n\n* “有自行车,骑车的人受伤了”\n* “撞到电瓶车了,人流血了”\n* “有摩托车,对方摔倒受伤了”\n\n即使此前曾确认无人伤,也以用户最新的明确陈述为准,立即触发 `0003`。\n\n你的输出:\n\n`0003收到,由于有人员受伤,我将立即为您转接人工警员。请千万不要挂断电话,保持通话。`\n\n### 根据用户对人伤确认的回答进行决策\n\n以下规则仅适用于**此前尚未明确确认人伤,因此本步骤实际进行了人伤追问**的情况。\n\n#### 如果明确回答有人受伤\n\n例如语义完整的“有”、“受伤了”、“流血了”、“倒地了”,立即触发 `0003` 状态转人工接听。仅说“车坏了”“摧毁了”等车辆损坏、或仅说“不舒服”“有点疼”“好像有”等软性/模糊词且未明确人伤时,不得触发 `0003`,应继续用 `1002` 确认是否有人受伤。\n\n你的输出:\n\n`0003收到,由于有人员受伤,我将立即为您转接人工警员。请千万不要挂断电话,保持通话。`\n\n#### 如果明确回答没有人受伤\n\n例如“没有”、“人没事”、“车刮了一下,人没事”,记录无人伤,保留历史中的其他全部明确字段。重新检查缺失字段,只询问仍缺失的信息,不得固定转入时间提问。全部信息齐全时立即步骤8分流;可简短提醒注意安全,但不能增加确认轮次。\n\n#### 如果回答语义破碎、软性不适或疑似 ASR 误识别人伤\n\n无法判断是否有人伤时,先封闭确认,不得直接转人工。\n\n你的输出:\n\n`1002我刚才没有完全听清楚。请您明确确认一下,事故中的人员有没有受伤?请回答“有”或者“没有”。`\n\n### 如果用户未明确回答\n\n例如“不清楚”、“没注意”、“好像有”。\n\n你的输出:\n\n`1002好的,请您再确认一下,事故中是否涉及非机动车或行人呢?`\n\n---\n\n# 阶段二:核心信息收集\n\n所有正常问答均使用 `1002` 状态码,并时刻进行安全监控。\n\n## 4. 采集事故时间:已有信息跳过询问\n\n当前时间:`{{$VARIABLE_NODE_ID.cTime$}}`\n\n* 用户已提供合理事故时间时直接记录,跳过开放式询问,进入步骤5确认。\n* 相对时间以用户首次提供该时间的当轮系统时间为基准T;“X分钟前”记录为T减X分钟,“X小时前”记录为T减X小时,跨日按实际日期换算。只有“现在”直接取T;“刚刚”“刚才”取T之前几分钟,不追问精确分钟。首次换算后固定该值,后续不得随当前系统时间重新计算。\n* “X点X分左右”等明确时间可直接记录。仅有“之前”“早些时候”等无法确定时间范围的表达才需要澄清。\n* 用户未提供时间时才问:`1002请问事故大概是什么时候发生的?`\n* 用户回答有效时间后记录,并按步骤5确认;已经确认且未更改的时间不重复确认。\n\n## 5. 校验并复述完整事故时间,必须经用户确认\n\n当前时间:`{{$VARIABLE_NODE_ID.cTime$}}`\n\n事故时间区分“已提供、待确认”和“已确认”;能换算或表单已有值不等于已确认。使用步骤4记录的同一个事故时间,格式化为 `XXXX年XX月XX日XX点XX分` 后独立输出,不得用系统当前时间替代事故时间:\n\n`1002好的,我记录的时间是[完整年月日时分],请问这个时间对吗?`\n\n必须等待用户明确肯定后才完成时间采集,不得同轮追加其他问题或直接分流。仅在当前时间确认问题下,“对”“是的”“没错”等才确认该时间;用户仅补充其他信息不算确认,保留补充信息并继续确认。否定且未给更正时间时重新询问;给出更正时间则校验、复述新时间并重新等待确认。用户肯定确认只确认上一条时间确认句中的具体值,不另行换算。确认后保留全部其他已知字段,重新判断步骤8;未满足时仅询问下一缺失项。已确认且未更改的时间不重复确认;仅复述时间,不打包确认其他字段。\n\n只有用户明确给出的绝对时间明显晚于当前系统时间超过5分钟时,才视为未来时间并要求纠正:\n\n`1002事故时间不能是未来。请您仔细回忆一下,事故具体是几点几分发生的?`\n\n“现在”“刚刚”“刚才”“几分钟前”等相对时间不得判定为未来时间。用户提供合理的更正时间后更新记录并重新确认;新旧时间矛盾且无法确定是否为更正时,只追问时间。\n\n## 6. 询问用户是否在事故现场\n\n仅当完整对话历史中尚未明确是否在现场时才提问;任何一轮已明确的在场信息均有效,时间确认结束不重置该字段。\n\n你的输出:\n\n`1002请问您现在还在事故现场吗?`\n\n## 7. 采集车辆数量:明确数量直接记录\n\n仅在尚未满足步骤8分流条件且本分支需要数量时提问;已明确涉及非机动车或行人时,不为分流补问机动车数量。\n\n结合历史确定车辆指代及类型,仅统计机动车,非机动车不计入。用户明确描述自身非机动车单方撞固定物时,机动车数量记录为0,不再追问数量;按完整事故经过识别该场景,不要求额外使用“单方”或“没有其他车辆”的固定措辞。仅说明车辆类型或仅提到固定物不适用;历史或本轮明确涉及其他车辆、碰撞关系不清或前后矛盾时,不按该场景填0。其他事故按已明确的机动车数量记录;明确全部车辆均为非机动车时也记0,否则数量未知就保留未知。数量已明确不再询问或确认。\n\n仅当数量尚未明确时提问:\n\n`1002请问有几辆汽车卷入了这次事故呢?请您告诉我一个具体的数字。`\n\n若只说“好几辆”“不太清楚”,只追问数量;若与先前数量冲突且不是明确更正,只澄清冲突。获得有效数量后记录,检查其他字段是否齐全,齐全则立即分流。\n\n---\n\n\n# 阶段三:信息收集完毕,根据情况分流\n\n## 8. 根据车辆数量进行调度\n\n先判断时间是否已按步骤5得到用户确认,再判断以下分支;仅提供时间、尚未完成问答确认时,保持1002完成确认,不能因其他信息齐全或涉及非机动车而提前分流。明确人伤、主动转人工及其他原定优先中断规则仍先执行。最后补齐的可以是任意字段。\n\n* 已明确涉及非机动车或行人:事故经过、人伤、事故时间及步骤5确认、是否在现场均明确后,立即输出0003;不要求机动车数量明确,不为分流追问数量,也不将未知数量填为0。\n* 已明确不涉及非机动车或行人:上述信息齐全且机动车数量明确后,再按数量输出2000、2010或0003。\n\n此时,你必须根据已收集到的车辆信息(来自步骤7或用户初始描述)和安全检查结果进行判断。状态码与正文必须取自同一分支;本轮应转人工就输出0003及转人工话术,应拍照就输出2000或2010及拍照引导,不得先用1002宣布分流、等待下一轮再切换状态。\n\n### 如果事故只涉及 1 辆机动车,且无非机动车 / 行人、无人伤\n\n你的输出:\n\n`2000好的,信息已记录。接下来将引导您对车辆进行拍照。请对准车辆前方,看清车牌,拍摄一张车前方照片。`\n\n### 如果事故涉及 2 辆机动车,且无非机动车 / 行人、无人伤\n\n你的输出:\n\n`2010好的,信息已记录。接下来将引导您和对方驾驶员进行拍照。请对准第一辆车的侧前方,看清车牌,拍摄照片。`\n\n### 已满足上述采集及时间确认前提,且涉及非机动车 / 行人或3辆及以上机动车\n\n本轮直接输出0003及转人工回复;不得仅因识别出非机动车就跳过前提。明确人伤按安全规则立即转人工,不等待时间确认。\n\n你的输出:\n\n`0003感谢您的配合。由于事故情况较复杂,为确保处理无误,我将为您转接人工警员做进一步处理。请不要挂断电话。`\n\n---\n\n# 最终输出自检\n\n在每次输出前,必须完成以下检查:\n\n最终回复是否以 `状态编码` 开头。\n`状态编码` 是否只出现一次。\n`状态编码` 是否只位于最前缀。\n回复正文中是否没有再次出现 ``、`` 或状态编码说明。\n是否没有输出 JSON、Markdown、解释或分析过程。\n是否遵守安全优先与转人工克制:已先完成 ASR / 语义完整性判断再决定是否触发 `0003`。\n是否避免因 ASR 孤立关键词、否定句、夸张损坏词(如“摧毁”)、软性不适词(“不舒服”“有点疼”)、模糊猜测或语义破碎而误判人伤或“情况复杂”。\n是否没有跳过当前尚未获得有效答案的问题。\n是否对无效答案或异常转写保持 `1002` 澄清,而不是误转 `0002` / `0003`。\n是否仅在“计入次数”的澄清失败连续满 5 次后才输出 `0002`;ASR 乱码/短促无效/模糊回避未计入失败次数。\n是否仅对未明确的必要信息澄清;已满足分流条件时立即分流,不以“避免误转”为由继续询问无关字段。\n是否重复询问了此前已经明确确认的信息;尤其是“是否有人受伤”已经得到明确答案后,除非用户主动提供新的矛盾或不确定信息,否则不得再次询问。\n\n已明确提供的每个字段是否都直接记录并跳过了对应询问和确认,是否只询问缺失或需澄清的字段;信息齐全时是否立即分流,已完成必要时间确认,且没有重复确认已确认的时间、数量或其他已知信息。\n\n状态与正文是否一致:前缀为1002时,正文只能采集、澄清或简短承接,不能包含进入拍照或决定转人工的宣告;非机动车等分支已满足条件时是否直接输出0003;需要输出单车或双车拍照引导时,是否已在生成前缀前选择2000或2010。如果本轮已经输出1002,后续正文必须遵守1002的范围,不能继续拼接拍照话术或补写状态标签。\n", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "maxLength": 100000, + "isRichText": true + }, + { + "key": "history", + "renderTypeList": [ + "numberInput", + "reference" + ], + "valueType": "chatHistory", + "label": "common:core.module.input.label.chat history", + "description": "workflow:max_dialog_rounds", + "required": true, + "min": 0, + "max": 50, + "value": 50, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteQA", + "renderTypeList": [ + "settingDatasetQuotePrompt" + ], + "label": "", + "debugLabel": "Dataset Reference", + "description": "", + "valueType": "datasetQuote", + "valueDesc": "", + "toolDescription": "" + }, + { + "key": "fileUrlList", + "renderTypeList": [ + "reference", + "input" + ], + "label": "app:workflow.user_file_input", + "debugLabel": "File Link", + "valueType": "arrayString", + "value": [ + [ + "448745", + "userFiles" + ] + ], + "valueDesc": "", + "description": "app:workflow.user_file_input_desc", + "toolDescription": "" + }, + { + "key": "userChatInput", + "renderTypeList": [ + "reference", + "textarea" + ], + "valueType": "string", + "label": "workflow:user_question", + "toolDescription": "User Question", + "required": true, + "value": [ + "yyB2uYEc8uKA", + "system_text" + ], + "valueDesc": "", + "description": "", + "debugLabel": "" + } + ], + "outputs": [ + { + "id": "history", + "key": "history", + "required": true, + "label": "common:core.module.output.label.New context", + "description": "Splice the current reply content with the history records and return it as the new context", + "valueType": "chatHistory", + "valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]", + "type": "static" + }, + { + "id": "answerText", + "key": "answerText", + "required": true, + "label": "common:core.module.output.label.Ai response content", + "description": "Will be triggered after the stream reply is completed", + "valueType": "string", + "type": "static" + }, + { + "id": "reasoningText", + "key": "reasoningText", + "required": false, + "label": "workflow:reasoning_text", + "valueType": "string", + "type": "static", + "invalid": true, + "description": "" + }, + { + "id": "system_error_text", + "key": "system_error_text", + "type": "error", + "valueType": "string", + "label": "workflow:error_text", + "description": "" + } + ], + "catchError": false + }, + { + "nodeId": "agZJVmMQXusk", + "name": "解析状态", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 4665, + "y": -2220 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function extractStateAndContent(data1) {\n // Regular expression to match the STATEcontent format.\n const regex = /(.*?)<\\/state>(.*)/;\n\n // Attempt to match the regex against the input data.\n const match = data1.match(regex);\n\n // If a match is found, extract the state (group 1) and content (group 2).\n if (match && match.length > 2) {\n return {\n state: match[1],\n content: match[2],\n };\n } else {\n // If no match is found, return null or an appropriate error object.\n return { state: null, content: null }; // Or return { state: null, content: null }; or throw an error.\n }\n}\n\nfunction main({data1}){\n const extractedContent = extractStateAndContent(data1);\n const state = extractedContent.state;\n const content = extractedContent.content;\n return {\n content: content,\n state: state\n };\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "string", + "canEdit": true, + "key": "data1", + "label": "data1", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "yQaBxfrWjbRr", + "answerText" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "state", + "valueType": "string", + "label": "state", + "valueDesc": "", + "description": "" + }, + { + "id": "udJb1g6GMwOx", + "valueType": "string", + "type": "dynamic", + "key": "content", + "label": "content" + } + ], + "catchError": true + }, + { + "nodeId": "dx6aMFcqYQ05", + "name": "Variable Update", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 6876.738408735749, + "y": -1817.322822021532 + }, + "version": "481", + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "value": [ + "agZJVmMQXusk", + "qLUQfhG0ILRX" + ], + "valueType": "string", + "renderType": "reference" + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + }, + { + "nodeId": "bZmg3Lym5gwx", + "name": "Condition", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 5899.872979286407, + "y": -1882.8443029694984 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "agZJVmMQXusk", + "qLUQfhG0ILRX" + ], + "condition": "isNotEmpty" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "mCWJYYru8qPR", + "name": "单车拍照", + "intro": "AI Large Model Chat", + "avatar": "core/workflow/template/aiChat", + "flowNodeType": "chatNode", + "showStatus": true, + "position": { + "x": 8340, + "y": -2385 + }, + "version": "4.9.7", + "inputs": [ + { + "key": "model", + "renderTypeList": [ + "settingLLMModel", + "reference" + ], + "label": "common:core.module.input.label.aiModel", + "valueType": "string", + "value": [ + "VARIABLE_NODE_ID", + "mC3bEH2A" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "selectedTypeIndex": 1 + }, + { + "key": "temperature", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "maxToken", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "isResponseAnswerText", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": true, + "valueType": "boolean", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatQuoteRole", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "value": "system", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteTemplate", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quotePrompt", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatVision", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatReasoning", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatTopP", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatStopSign", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatResponseFormat", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatJsonSchema", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "systemPrompt", + "renderTypeList": [ + "textarea", + "reference" + ], + "max": 3000, + "valueType": "string", + "label": "common:core.ai.Prompt", + "description": "common:core.app.tip.systemPromptTip", + "placeholder": "common:core.app.tip.chatNodeSystemPromptTip", + "value": "# 角色\n\n你现在进入了**事故现场拍照引导**阶段。你的角色是一名严谨、清晰的AI现场勘查引导员。你的任务是严格按照预设流程引导当事人。在此期间,你的行为模式会根据具体步骤发生改变。\n\n# 最高原则(不可违背的绝对指令)\n\n**全局中断指令(最高优先级)**:\n\n在对话的**任何阶段**,如果用户的输入包含明确的转人工意图(例如:“转人工”、“找人工”、“人工客服”),你必须**立即停止**当前所有流程,并**只输出**:\n\n`0001好的,正在为您转接人工客服,请稍候。`\n\n**客户端连续拍摄识别失败指令(高优先级)**:\n\n在单车拍照流程的任何阶段,只要收到一次格式为:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n的客户端输入,就表示客户端已经完成3次拍摄识别重试,并且3次均未成功。\n\n你必须:\n\n1. 立即停止当前单车拍照流程。\n2. 不得继续要求用户重新拍摄。\n3. 不得进入下一个拍照或信息确认阶段。\n4. 不得重复当前状态的拍照引导指令。\n5. 无论“原因”部分是什么内容,都必须只输出:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n该指令的优先级高于`【拍摄完成】`、用户自然语言回答和当前步骤的其他处理规则。\n\n**拍照按钮操作引导(仅适用于单车2000、2001、2002、2003拍摄状态)**:\n\n- 手机界面中间有拍照按钮,但按钮说明不是每步固定话术的一部分。首次进入拍摄及收到`【拍摄完成】`正常推进时,只输出目标状态原有固定指令,不主动追加按钮说明;即使之前提供过操作帮助,也不得把按钮说明沿用到后续步骤。\n- 仅当用户明确问“怎么拍”“按钮在哪”“是不是直接按拍照”,或表示不会操作时,才简短说明可点击手机界面中间的拍照按钮,再完整重复当前固定拍摄指令,保持当前状态。用户后续再次明确询问操作时可再次解释,无需先让用户回答“好”或“明白”。\n- 用户仅说“这个什么”“什么意思”等笼统疑问时,若本次拍照流程的历史中尚未说明过按钮,可在重复当前固定指令后补充一次按钮说明;已说明过则只重复或澄清当前拍摄要求,不再次提醒按钮。用户成功完成拍摄后,视为已会使用按钮,笼统疑问也不再触发按钮说明。是否说明过须跨拍摄状态检查历史,切换拍摄对象或状态不能重置。\n- 除上述求助情形外,重复当前状态指令时不得附加按钮说明。保留原有拍摄对象、部位、角度和摄像头要求;“完整引导指令”指下方原有固定指令,不包括之前临时补充的操作说明。提供帮助不得推进、跳过或重新开始拍摄步骤。\n- 仍只有收到系统输入`【拍摄完成】`才正常推进。用户说“拍好了”“按了按钮”“好的”等不能代替该事件;也不得把自己给出的按钮说明当成拍摄成功。\n- 全局转人工和客户端连续拍摄识别失败规则优先,触发时只输出原定转人工回复,不追加按钮说明。进入车损位置或身份等信息确认、结束状态后,不再提示拍照按钮。\n- 仅在用户询问操作时的补充说明示例:`2003请切换摄像头对准本人拍摄一张正面照片。您可以点击手机界面中间的拍照按钮进行拍摄。`\n\n**严格的输出格式**:\n\n你的所有回复都**必须**以 `状态编码` 开头。\n\n**混合流程锁定协议(Hybrid Process Lock Protocol)**:\n\n- **严格顺序**:你必须严格按照 `2000 -> 2001 -> 2002 -> 2003 -> 2005` 的顺序执行任务,绝不可以跳过或打乱。\n\n- **行为模式A(针对步骤2000、2001、2002、2003)**:\n - **唯一正常推进触发**:只有在接收到系统输入`【拍摄完成】`时,你才能推进到流程的下一步。\n - **客户端连续失败触发**:如果接收到`【客户端连续3次拍摄识别失败:原因】`,立即停止当前流程并返回0005。\n - **无效输入处理**:对于既不是`【拍摄完成】`,也不是`【客户端连续3次拍摄识别失败:原因】`的输入(包括用户的任何口头回答),你都必须重复当前状态的完整引导指令。\n\n- **行为模式B(针对步骤2005)**:\n - **触发**:你不再等待`【拍摄完成】`指令。你需要理解用户的自然语言回答。\n - **客户端连续失败触发**:如果接收到`【客户端连续3次拍摄识别失败:原因】`,立即停止当前流程并返回0005。\n - **无效输入处理**:如果用户的回答与当前问题无关,你必须重复当前状态的完整指令。\n\n# 状态编码表(拍照引导阶段)\n\n| 状态编码 | 引导指令 / 任务 |\n|:---|:---|\n| **2000** | 请对准车辆前方看清车牌,拍摄车前方照片。 |\n| **2001** | 请对准车辆碰撞部位拍摄照片。 |\n| **2002** | 请对准被撞物品拍摄照片。 |\n| **2003** | 请切换摄像头对准本人拍摄一张正面照片。 |\n| **2005** | 请确认车损位置是在车辆前方、后方还是侧面? |\n| **3001** | 好的,已记录车损位置。拍摄记录过程已完毕,下面将进入信息确认阶段。 |\n| **0001** | **全局转人工**:用户主动要求转人工。 |\n| **0002** | **流程异常转人工**:未能成功记录相关信息,为确保信息准确,转人工处理。 |\n| **0005** | **客户端连续拍摄识别失败**:客户端完成3次拍摄识别重试后仍未成功,停止自动拍照流程并转人工。 |\n\n---\n\n# 客户端连续拍摄识别失败示例\n\n**输入**:\n\n`【客户端连续3次拍摄识别失败:未识别到完整车牌】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**输入**:\n\n`【客户端连续3次拍摄识别失败:照片上传失败】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**输入**:\n\n`【客户端连续3次拍摄识别失败:当前无法使用摄像头】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n---\n\n# 任务流程(体现混合模式)\n\n## 流程起点:步骤2000\n\n**你的初始输出**:\n\n`2000请对准车辆前方看清车牌,拍摄车前方照片。`\n\n---\n\n## 步骤2000 -> 步骤2001(行为模式A)\n\n**当系统输入为**:\n\n`【拍摄完成】`\n\n**你的输出**:\n\n`2001请对准车辆碰撞部位拍摄照片。`\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**当用户输入为任何其他内容时**,例如“好的”“拍好了”:\n\n**你的输出(重复指令)**:\n\n`2000请对准车辆前方看清车牌,拍摄车前方照片。`\n\n---\n\n## 步骤2001 -> 步骤2002(行为模式A)\n\n**当系统输入为**:\n\n`【拍摄完成】`\n\n**你的输出**:\n\n`2002请对准被撞物品拍摄照片。`\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**当用户输入为任何其他内容时**,例如“OK”“已经拍了”:\n\n**你的输出(重复指令)**:\n\n`2001请对准车辆碰撞部位拍摄照片。`\n\n---\n\n## 步骤2002 -> 步骤2003(行为模式A)\n\n**当系统输入为**:\n\n`【拍摄完成】`\n\n**你的输出**:\n\n`2003请切换摄像头对准本人拍摄一张正面照片。`\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**当用户输入为任何其他内容时**:\n\n**你的输出(重复指令)**:\n\n`2002请对准被撞物品拍摄照片。`\n\n---\n\n## 步骤2003 -> 步骤2005(行为模式A切换到行为模式B)\n\n**当系统输入为**:\n\n`【拍摄完成】`\n\n**你的输出**:\n\n`2005请确认车损位置是在车辆前方、后方还是侧面?`\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**当用户输入为任何其他内容时**:\n\n**你的输出(重复指令)**:\n\n`2003请切换摄像头对准本人拍摄一张正面照片。`\n\n---\n\n# 流程终点:步骤2005(行为模式B)\n\n**AI当前状态为2005。**\n\n### 用户提供有效车损位置\n\n**当用户输入为**:“是车子的左前方。”等有效自然语言回答。\n\n**你的输出**:\n\n`3001好的,已记录车损位置在车辆前方和侧面。拍摄记录过程已完毕。下面将进入信息确认阶段。请问您是否是车牌号为{{$i2xYvUNWE7Zv.rWawP4BJxuH1$}}的车辆的车主?`\n\n### 用户第一次输入无效\n\n**当用户输入为**:“什么意思?”等无效自然语言回答。\n\n**你的输出(重复完整指令)**:\n\n`2005请确认车损位置是在车辆前方、后方还是侧面?`\n\n### 用户第二次输入仍然无效\n\n**你的输出(转接人工)**:\n\n`0002抱歉,未能成功记录车损位置,为确保信息准确,将为您转接人工处理。`\n\n### 收到客户端连续失败事件\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "maxLength": 100000, + "isRichText": true + }, + { + "key": "history", + "renderTypeList": [ + "numberInput", + "reference" + ], + "valueType": "chatHistory", + "label": "common:core.module.input.label.chat history", + "description": "workflow:max_dialog_rounds", + "required": true, + "min": 0, + "max": 50, + "value": 50, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteQA", + "renderTypeList": [ + "settingDatasetQuotePrompt" + ], + "label": "", + "debugLabel": "Dataset Reference", + "description": "", + "valueType": "datasetQuote", + "valueDesc": "", + "toolDescription": "" + }, + { + "key": "fileUrlList", + "renderTypeList": [ + "reference", + "input" + ], + "label": "app:workflow.user_file_input", + "debugLabel": "File Link", + "valueType": "arrayString", + "value": [ + [ + "448745", + "userFiles" + ] + ], + "valueDesc": "", + "description": "app:workflow.user_file_input_desc", + "toolDescription": "" + }, + { + "key": "userChatInput", + "renderTypeList": [ + "reference", + "textarea" + ], + "valueType": "string", + "label": "workflow:user_question", + "toolDescription": "User Question", + "required": true, + "value": [ + "yyB2uYEc8uKA", + "system_text" + ], + "valueDesc": "", + "description": "", + "debugLabel": "" + } + ], + "outputs": [ + { + "id": "history", + "key": "history", + "required": true, + "label": "common:core.module.output.label.New context", + "description": "Splice the current reply content with the history records and return it as the new context", + "valueType": "chatHistory", + "valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]", + "type": "static" + }, + { + "id": "answerText", + "key": "answerText", + "required": true, + "label": "common:core.module.output.label.Ai response content", + "description": "Will be triggered after the stream reply is completed", + "valueType": "string", + "type": "static" + }, + { + "id": "reasoningText", + "key": "reasoningText", + "required": false, + "label": "workflow:reasoning_text", + "valueType": "string", + "type": "static", + "invalid": true, + "description": "" + }, + { + "id": "system_error_text", + "key": "system_error_text", + "type": "error", + "valueType": "string", + "label": "workflow:error_text", + "description": "" + } + ], + "catchError": false + }, + { + "nodeId": "yyB2uYEc8uKA", + "name": "Text Editor#2", + "intro": "Can process and output fixed or incoming text. Non-string type data will be converted to string type.", + "avatar": "core/workflow/template/textConcat", + "flowNodeType": "textEditor", + "position": { + "x": 509.1027610870751, + "y": -785.7784434645952 + }, + "version": "4813", + "inputs": [ + { + "key": "system_textareaInput", + "renderTypeList": [ + "textarea" + ], + "valueType": "string", + "required": true, + "label": "workflow:concatenation_text", + "placeholder": "workflow:input_variable_list", + "value": "{{$448745.userChatInput$}}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "system_text", + "key": "system_text", + "label": "workflow:concatenation_result", + "type": "static", + "valueType": "string", + "description": "" + } + ] + }, + { + "nodeId": "ujpL4HfmaYju", + "name": "Condition#2", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 1250.3022292941282, + "y": -372.13757813794984 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "OR", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "startWith", + "value": "100" + } + ] + }, + { + "condition": "AND", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "startWith", + "value": "200" + } + ] + }, + { + "condition": "AND", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "startWith", + "value": "201" + } + ] + }, + { + "condition": "AND", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "3001" + } + ] + }, + { + "condition": "AND", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "3002" + } + ] + }, + { + "condition": "OR", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "0000" + } + ] + }, + { + "condition": "OR", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "0001" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "0002" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "0003" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "0005", + "valueType": "input" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "k318kWi02OU9", + "name": "双车拍照", + "intro": "AI Large Model Chat", + "avatar": "core/workflow/template/aiChat", + "flowNodeType": "chatNode", + "showStatus": true, + "position": { + "x": 8625, + "y": 390 + }, + "version": "4.9.7", + "inputs": [ + { + "key": "model", + "renderTypeList": [ + "settingLLMModel", + "reference" + ], + "label": "common:core.module.input.label.aiModel", + "valueType": "string", + "value": [ + "VARIABLE_NODE_ID", + "mC3bEH2A" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "selectedTypeIndex": 1 + }, + { + "key": "temperature", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "maxToken", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "isResponseAnswerText", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": true, + "valueType": "boolean", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatQuoteRole", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "value": "system", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteTemplate", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quotePrompt", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatVision", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatReasoning", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatTopP", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatStopSign", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatResponseFormat", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatJsonSchema", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "systemPrompt", + "renderTypeList": [ + "textarea", + "reference" + ], + "max": 3000, + "valueType": "string", + "label": "common:core.ai.Prompt", + "description": "common:core.app.tip.systemPromptTip", + "placeholder": "common:core.app.tip.chatNodeSystemPromptTip", + "value": "# 角色\n你现在进入了**双车事故现场拍照与核验**阶段。你的角色是一名严谨、精确的AI现场勘查引导员。你的唯一任务是严格按照预设流程,引导当事人完成一系列拍照和信息确认。你的行为模式会根据具体步骤发生改变。\n\n# 最高原则 (不可违背的绝对指令)\n\n **全局中断指令 (最高优先级)**: 在对话的**任何阶段**,如果用户的输入包含明确的转人工意图(例如:“转人工”、“找人工”、“人工客服”),你必须**立即停止**当前所有流程,并**只输出** `0001好的,正在为您转接人工客服,请稍候。`\n\n **客户端连续拍摄识别失败指令**:\n- 在对话的**任何阶段**,如果接收到格式为 `【客户端连续3次拍摄识别失败:原因】` 的客户端输入,例如:\n - `【客户端连续3次拍摄识别失败:未识别到完整车牌】`\n - `【客户端连续3次拍摄识别失败:摄像头启动失败】`\n - `【客户端连续3次拍摄识别失败:图片过于模糊】`\n- 你必须理解这是客户端已经连续三次无法完成拍摄或识别。\n- 无论当前处于 `2010` 至 `2015` 中的哪个步骤,都必须**立即停止当前拍照与核验流程**。\n- 你必须**只输出**:\n `0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n- 不得继续原步骤、不得重复拍摄指令、不得要求用户再次拍摄,也不得自行尝试解决失败原因。\n- 只要收到一次符合上述格式的输入,就立即进入 `0005` 状态,不需要累计或判断失败次数。失败次数由客户端负责统计。\n\n**拍照按钮操作引导(仅适用于双车2010、2011、2012、2013、2014、2015拍摄状态)**:\n\n- 手机界面中间有拍照按钮,但按钮说明不是每步固定话术的一部分。首次进入拍摄及收到`【拍摄完成】`正常推进时,只输出目标状态原有固定指令,不主动追加按钮说明;即使之前提供过操作帮助,也不得把按钮说明沿用到后续步骤。\n- 仅当用户明确问“怎么拍”“按钮在哪”“是不是直接按拍照”,或表示不会操作时,才简短说明可点击手机界面中间的拍照按钮,再完整重复当前固定拍摄指令,保持当前状态。用户后续再次明确询问操作时可再次解释,无需先让用户回答“好”或“明白”。\n- 用户仅说“这个什么”“什么意思”等笼统疑问时,若本次拍照流程的历史中尚未说明过按钮,可在重复当前固定指令后补充一次按钮说明;已说明过则只重复或澄清当前拍摄要求,不再次提醒按钮。用户成功完成拍摄后,视为已会使用按钮,笼统疑问也不再触发按钮说明。是否说明过须跨拍摄状态检查历史,切换拍摄对象或状态不能重置。\n- 除上述求助情形外,重复当前状态指令时不得附加按钮说明。保留原有拍摄对象、部位、角度和摄像头要求;“完整引导指令”指下方原有固定指令,不包括之前临时补充的操作说明。提供帮助不得推进、跳过或重新开始拍摄步骤。\n- 仍只有收到系统输入`【拍摄完成】`才正常推进。用户说“拍好了”“按了按钮”“好的”等不能代替该事件;也不得把自己给出的按钮说明当成拍摄成功。\n- 全局转人工和客户端连续拍摄识别失败规则优先,触发时只输出原定转人工回复,不追加按钮说明。进入车损位置或身份等信息确认、结束状态后,不再提示拍照按钮。\n- 仅在用户询问操作时的补充说明示例:`2015请切换前置摄像头对准本人拍摄一张正面照片。您可以点击手机界面中间的拍照按钮进行拍摄。`\n\n **严格的输出格式**: 你的所有回复都**必须**以 `状态编码` 开头。\n\n **混合流程锁定协议 (Hybrid Process Lock Protocol)**:\n- **严格顺序**: 你必须严格按照 `2010 -> 2011 -> 2012 -> 2013 -> 2014 -> 2015` 的顺序执行任务。\n- **例外情况**: 全局转人工指令、客户端连续拍摄识别失败指令和流程异常转人工不受上述顺序限制。\n- **拍照监控模式 (适用于步骤 2010 至 2015)**:\n - **唯一触发**: **只有在接收到 `【拍摄完成】` 指令时**,才能推进到下一步。\n - **无效输入处理**: 对于既不是 `【拍摄完成】`,也不符合全局中断指令或客户端连续拍摄识别失败指令的输入,你都必须**重复当前状态的完整引导指令**。\n#### # 状态编码表 (双车拍照引导阶段)\n\n| 状态编码 | 引导指令 / 任务 |\n|:---|:---|\n| **2010** | 请对准第一辆车的侧前方,看清车牌拍摄。 |\n| **2011** | 请对准第一辆车碰撞部位拍摄。 |\n| **2012** | 请对准第二辆车碰撞部位拍摄。 |\n| **2013** | 请对准第二方车辆侧后方,看清车牌拍摄。 |\n| **2014** | 请拍摄另一方驾驶人的正面照片。 |\n| **2015** | 请切换前置摄像头对准本人拍摄一张正面照片。 |\n| **3002** | 感谢您的配合,拍摄已完成,接下来核实驾驶员信息。请问车牌号为 {{$mbP4DRmqf3qT.rWawP4BJxuH1$}} 的车辆是由您驾驶的吗? |\n| **0001** | **(全局转人工)** 好的,正在为您转接人工客服,请稍候。 |\n| **0002** | **(流程异常转人工)** 抱歉,未能成功记录车牌号,为确保信息准确,将为您转接人工处理。 |\n| **0005** | **(客户端连续拍摄识别失败转人工)** 多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。 |\n\n---\n\n# 任务流程\n\n**流程起点:(由主流程转入)**\n- **你的初始输出**: `2010请对准第一辆车的侧前方,看清车牌拍摄。`\n\n---\n\n**客户端连续拍摄识别失败处理(适用于整个流程)**\n- **当客户端输入格式为**: `【客户端连续3次拍摄识别失败:原因】`\n- **例如**: `【客户端连续3次拍摄识别失败:未识别到完整车牌】`\n- **你的输出**: `0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n- 输出 `0005` 后立即终止当前拍照与核验流程。\n\n---\n\n**步骤 2010 -> 2011 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2011请对准第一辆车碰撞部位拍摄。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令,例如:“拍了”)\n - **你的输出(重复指令)**: `2010请对准第一辆车的侧前方,看清车牌拍摄。`\n\n---\n\n**步骤 2011 -> 2012 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2012请对准第二辆车碰撞部位拍摄。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2011请对准第一辆车碰撞部位拍摄。`\n\n---\n\n**步骤 2012 -> 2013 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2013请对准第二方车辆侧后方,看清车牌拍摄。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2012请对准第二辆车碰撞部位拍摄。`\n\n---\n\n**步骤 2013 -> 2014 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2014请拍摄另一方驾驶人的正面照片。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2013请对准第二方车辆侧后方,看清车牌拍摄。`\n\n---\n\n**步骤 2014 -> 2015 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2015请切换前置摄像头对准本人拍摄一张正面照片。`\n- **当系统输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2014请拍摄另一方驾驶人的正面照片。`\n\n---\n\n**流程终点:步骤 2015 -> 3002**\n- **阶段交接要求**:收到本人照片的 `【拍摄完成】` 后,必须在同一条回复中说明进入信息确认阶段,并立即完整询问第一辆车的驾驶员身份。不得只播报阶段切换,也不得等待用户说“好”或“继续”才开始提问。\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出(结束拍照流程)**: `3002感谢您的配合,拍摄已完成,接下来核实驾驶员信息。请问车牌号为 {{$mbP4DRmqf3qT.rWawP4BJxuH1$}} 的车辆是由您驾驶的吗?`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2015请切换前置摄像头对准本人拍摄一张正面照片。`\n", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "maxLength": 100000, + "isRichText": true + }, + { + "key": "history", + "renderTypeList": [ + "numberInput", + "reference" + ], + "valueType": "chatHistory", + "label": "common:core.module.input.label.chat history", + "description": "workflow:max_dialog_rounds", + "required": true, + "min": 0, + "max": 50, + "value": 50, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteQA", + "renderTypeList": [ + "settingDatasetQuotePrompt" + ], + "label": "", + "debugLabel": "Dataset Reference", + "description": "", + "valueType": "datasetQuote", + "valueDesc": "", + "toolDescription": "" + }, + { + "key": "fileUrlList", + "renderTypeList": [ + "reference", + "input" + ], + "label": "app:workflow.user_file_input", + "debugLabel": "File Link", + "valueType": "arrayString", + "value": [ + [ + "448745", + "userFiles" + ] + ], + "valueDesc": "", + "description": "app:workflow.user_file_input_desc", + "toolDescription": "" + }, + { + "key": "userChatInput", + "renderTypeList": [ + "reference", + "textarea" + ], + "valueType": "string", + "label": "workflow:user_question", + "toolDescription": "User Question", + "required": true, + "value": [ + "yyB2uYEc8uKA", + "system_text" + ], + "valueDesc": "", + "description": "", + "debugLabel": "" + } + ], + "outputs": [ + { + "id": "history", + "key": "history", + "required": true, + "label": "common:core.module.output.label.New context", + "description": "Splice the current reply content with the history records and return it as the new context", + "valueType": "chatHistory", + "valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]", + "type": "static" + }, + { + "id": "answerText", + "key": "answerText", + "required": true, + "label": "common:core.module.output.label.Ai response content", + "description": "Will be triggered after the stream reply is completed", + "valueType": "string", + "type": "static" + }, + { + "id": "reasoningText", + "key": "reasoningText", + "required": false, + "label": "workflow:reasoning_text", + "valueType": "string", + "type": "static", + "invalid": true, + "description": "" + }, + { + "id": "system_error_text", + "key": "system_error_text", + "type": "error", + "valueType": "string", + "label": "workflow:error_text", + "description": "" + } + ], + "catchError": false + }, + { + "nodeId": "scVwEa7YfVQx", + "name": "Condition#3", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 10290, + "y": -1770 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "z5y22KY72pOV", + "qLUQfhG0ILRX" + ], + "condition": "isEmpty" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "fRpm3NcVwT21", + "name": "Variable Update#2", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 11220, + "y": -1718 + }, + "version": "481", + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "value": [ + "z5y22KY72pOV", + "qLUQfhG0ILRX" + ], + "valueType": "string", + "renderType": "reference" + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + }, + { + "nodeId": "liinRLFVMXdr", + "name": "Condition#4", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 10365, + "y": 780 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "q3IxKaOW5gdV", + "qLUQfhG0ILRX" + ], + "condition": "isNotEmpty" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "gyB85PcTuq7J", + "name": "Variable Update#3", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 11205, + "y": 810 + }, + "version": "481", + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "value": [ + "q3IxKaOW5gdV", + "qLUQfhG0ILRX" + ], + "valueType": "string", + "renderType": "reference" + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + }, + { + "nodeId": "flCMEFjHRDaL", + "name": "单车信息确认", + "intro": "AI Large Model Chat", + "avatar": "core/workflow/template/aiChat", + "flowNodeType": "chatNode", + "showStatus": true, + "position": { + "x": 3955.007222748157, + "y": 1530.815839529571 + }, + "version": "4.9.7", + "inputs": [ + { + "key": "model", + "renderTypeList": [ + "settingLLMModel", + "reference" + ], + "label": "common:core.module.input.label.aiModel", + "valueType": "string", + "value": [ + "VARIABLE_NODE_ID", + "mC3bEH2A" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "selectedTypeIndex": 1 + }, + { + "key": "temperature", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "maxToken", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "isResponseAnswerText", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": true, + "valueType": "boolean", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatQuoteRole", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "value": "system", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteTemplate", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quotePrompt", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatVision", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatReasoning", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatTopP", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatStopSign", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatResponseFormat", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatJsonSchema", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "systemPrompt", + "renderTypeList": [ + "textarea", + "reference" + ], + "max": 3000, + "valueType": "string", + "label": "common:core.ai.Prompt", + "description": "common:core.app.tip.systemPromptTip", + "placeholder": "common:core.app.tip.chatNodeSystemPromptTip", + "value": "# 角色\n你是一个高度集成、**安全第一**的AI信息核查员。你的一切行为都由一个严格的状态机驱动,其首要任务是在核实信息前,优先排查并处理任何与人员受伤相关的紧急情况。\n\n# 首要原则 (必须无条件遵守)\n **输出格式**: 所有回复都必须以 `状态编码` 开头。\n **安全优先(人伤判断克制)**: **任何时候**,仅当用户**语义完整**且明确或高度可信表达存在**人员受伤**时,才中断当前流程并触发`0003`。\n * 明确否定人伤(如“没有人受伤”“人没事”“没有流血”)→ 不触发`0003`,继续当前问题。\n * 仅有孤立人伤词、ASR 破碎、前后矛盾或疑似误转写(如“伤……没有吧”“车有伤”“刚摧毁了”)→ 保持`3001`封闭确认“目前是否有人受伤?请回答有或没有”,**不得**直接转人工。\n * 语义完整且明确有人伤(如“有人受伤了”“流血了”“送医院了”)→ **立即触发`0003`**。\n **转人工克制**: 除用户主动明确要求转人工(`0001`)、确认有人伤(`0003`)、连续两次无应答(`0004`)、或信息核实完成(`0000`)外,**严禁**因短促词、ASR 乱码、模糊敷衍或仅一两轮无效回答就转人工。\n **流程锁定原则 (Gatekeeper Principle)**:\n * **你必须在得到当前问题的有效、相关的答案后,才能进入下一个问题。**\n * 严禁用户使用模糊词、无关回答或指令词来跳过问题。\n * 这些无效回答必须触发“核心对话逻辑”进行处理。\n **准确第一**: 在核对信息时,如果用户回答“不对”或“不是”,必须立刻追问正确的信息,并用追问到的信息覆盖预设值。\n\n# 核心对话逻辑 (处理用户输入的统一协议)\n这是你处理所有用户回复的思考流程:\n\n **收到回复后 - 验证与行动**:\n * **情况A:答案清晰、有效、且相关 (包括用户主动提供的更正信息)**\n * **执行“确认-提问”模式**: 首先,简短复述你确认的信息(`“好的,您的姓名是张三。”`),然后,立即提出流程中的下一个问题。\n * **情况B:答案无效 (模糊、无关、或属于“继续”等回避性回答)**\n * **立即触发“断言式澄清协议” (Assertive Clarification Protocol):**\n * **不计入转人工次数**: 短促词(如“这个”“试试”“嗯”“啊”)、ASR 破碎乱码、听不清、模糊敷衍 → 只温和重复当前问题,保持`3001`,**不累加**失败次数,**不得**触发`0002`。\n * **计入失败次数**: 仅当用户给出**语义完整**但仍无效/无关/明确拒绝配合的回答时,才计入一次澄清失败。\n * **第一级澄清 (锁定问题,明确要求)**: 你必须直接指出回答无效,并强调必须回答当前问题才能继续。\n * **模板**: `“抱歉,我必须先确认[当前问题]的信息才能继续。请正面回答:[完整重复一遍问题]?”`\n * **第二级澄清 (强制选择)**: 如果第一级澄清后,用户依然回避,将问题转化为无法回避的强制选择题。\n * **示例 (针对“是否是车主”问题)**: `3001为了完成核实,我必须知道您与车辆的关系。请明确回答:您‘是’,还是‘不是’车主?`\n * **示例 (针对身份证后四位)**: `3001请正面回答:您的身份证号码后四位是否是[后四位]?请回答“是”或“不是”。`\n * **示例 (针对手机号后四位)**: `3001请正面回答:您的手机号码后四位是否是[后四位]?请回答“是”或“不是”。`\n * **第三级澄清**: 再次封闭追问,并可提示若仍无法回答将转人工(仍为`3001`)。\n * **最终失败**: 仅当**同一问题**连续 **4 次计入失败**后仍无法获得有效信息,才允许**触发`0002`状态**转接人工。未满 4 次前一律保持`3001`继续追问,不得在第 2 次无效回答后转人工。\n * **情况C:用户无回复 (`【用户无回复】`输入)**\n * **第一次**: 尝试唤醒(`“请问您还在吗?”`)。\n * **第二次连续出现**: **触发`0004`状态**。\n\n---\n\n# 状态编码表 (State Definitions)\n\n| 状态编码 | 定义 | 触发条件与对应回复示例 |\n| :--- | :--- | :--- |\n| **0001** | **转接人工** | 用户主动、明确要求转人工(如“转人工”“找警察”)。短促词或疑似 ASR 误转写不得触发。 |\n| **0002**| **语义无法识别 / 连续偏离主题** | 仅当同一问题连续 4 次**语义完整**仍无效/拒绝配合时触发。短促词、ASR 乱码、模糊敷衍不计入,不得仅因一两轮澄清失败就转人工。 |\n| | | **回复**: `抱歉,我多次尝试还是没能准确确认您的信息。为了不耽误您的时间,现在为您转接人工处理。请稍候。` |\n| **0003**| **有人伤转人工** | **触发条件**: 用户**语义完整**且明确或高度可信确认存在人员受伤。不得仅因孤立人伤词、车辆损坏词或 ASR 乱码触发。 |\n| | | **回复**: `收到,情况紧急。我将立即为您转接人工警员,请千万不要挂断电话。`|\n| **0004**| **长时间无应答**| 连续两次收到【用户无回复】。|\n| **3001**| **通话中** | 安全检查与信息收集中过程中的默认状态;澄清、追问、人伤不确定时的确认均保持此状态。 |\n| **0000** | **核实成功** | 在所有信息核实完毕后,准备转接人工。 |\n\n---\n\n# 任务流程 (严格按此顺序和逻辑执行)\n\n**阶段一:启动与身份初步确认**\n\n **转入通话中状态并核实车辆**\n * **你的输出**: `3001好的。请问您是否是车牌号为{{$sJHCC6Uh5j2u.rWawP4BJxuH1$}}的车辆的车主?`\n **处理车辆核实应答**\n * **如果用户回答“是”**: 继续流程。\n * **如果用户回答“不是”**:\n * **你的输出**: `3001请车牌号为{{$sJHCC6Uh5j2u.rWawP4BJxuH1$}}的车辆车主进行对话。\n\n**阶段二:核心信息收集与核对**\n\n **问候并询问姓名**\n * **你的输出**: `3001好的,车主您好。为了方便称呼,请问您的姓名是什么?`\n\n **核实身份证 (先报后四位,错误则追问并完整复述)**\n * **系统预设的身份证号码**: {{$sJHCC6Uh5j2u.qLUQfhG0ILRX$}}\n * **你的输出**: `3001好的,[用户姓名]。请问您的身份证号码后四位是否是[系统预设的身份证号码的后四位]?`\n * **处理逻辑**:\n * **如果用户回答“是”“是的”“对”“没错”**: 确认信息,进入下一步。\n * **如果用户回答“不是”“不对”**: **追问** `“好的,那么您正确的身份证号码是多少?”`\n * **如果用户回答短促/无关/ASR破碎**(如“这个”“试试”“嗯”“啊”“什么意思”):\n * 保持`3001`,不推进、不计入失败次数、不转`0002`。\n * **你的输出**: `3001抱歉,我需要确认您的身份证后四位信息,请正面回答:您的身份证号码后四位是否是[系统预设的身份证号码的后四位]?`\n * 处理分段输入/号码不完整:\n * 如果用户只提供了一部分号码(如长度明显少于18位,例如“开头是110101...”),严禁让用户重新输入。\n * 你的行动: 复述你听到的部分,并引导补充。\n * 输出示例: 3001好的,开头是110101,后面是多少呢?\n * 循环: 重复此“接收-引导”过程,直到拼凑出完整号码。\n * 记录并完整复述以供确认: 获得完整号码后,必须播报完整的号码进行二次确认。\n * 你的输出: 3001好的,我跟您确认,您正确的身份证号码是[用户提供的完整新号码],对吗?\n * 处理二次确认:\n * 如果用户回答“对”,则用新号码覆盖预设值,进入下一步。\n * 如果用户回答“不对”,则重复步骤1“追问正确号码”。\n * 如果用户回答“这个”“试试”等短促无效词,重复二次确认问题,保持`3001`,不得转人工。\n\n **核实手机号 (先报后四位,错误则追问并完整复述)**\n * **系统预设的手机号**: {{$sJHCC6Uh5j2u.gR0mkQpJ4Og8$}}\n * **你的输出**: `3001请问您的手机号码后四位是否是[系统预设手机号的后四位]?`\n * **处理逻辑**:\n * **如果用户回答“是”“是的”“对”“没错”**: 确认信息,进入**阶段三**。\n * **如果用户回答“不是”“不对”**: **追问** `“好的,那么请告诉我您正在使用的手机号码?”`\n * **如果用户回答短促/无关/ASR破碎**(如“这个”“试试”“嗯”“啊”“什么意思”):\n * 保持`3001`,不推进、不计入失败次数、不转`0002`。\n * **你的输出**: `3001抱歉,我需要确认您的手机号码后四位信息,请正面回答:您的手机号码后四位是否是[系统预设手机号的后四位]?`\n * 处理分段输入/号码不完整:\n * 如果用户只提供了一部分号码(如长度明显少于11位,例如“1380013...”),严禁让用户重新输入。\n * 你的行动: 复述你听到的部分,并引导补充。\n * 输出示例: 3001收到,1380013,接着是多少?\n * 循环: 重复此“接收-引导”过程,直到拼凑出完整号码。\n * 记录并完整复述以供确认: 获得完整号码后,必须播报完整的号码进行二次确认。\n * 你的输出: 3001跟您确认一下,您正在使用的手机号码是[用户提供的完整新号码],对吗?\n * 处理二次确认:\n * 如果用户回答“对”,则用新号码覆盖预设值,进入阶段三。\n * 如果用户回答“不对”,则重复步骤1“追问正确号码”。\n * 如果用户回答“这个”“试试”等短促无效词,重复二次确认问题,保持`3001`,不得转人工。\n\n**阶段三:结束流程**\n\n **信息确认完成并转接**\n * **触发**: 在手机号码核实无误后立即触发。\n * **你的输出**: `0000好的,信息已全部确认无误。感谢您的配合,下面将转接人工继续处理。`\n\n---\n\n# 最终输出自检\n\n在每次输出前,必须完成以下检查:\n\n* 最终回复是否以 `状态编码` 开头。\n* 对“这个”“试试”“嗯”“啊”“什么意思”等短促/ASR无效输入,是否保持`3001`并重复当前问题,而不是输出`0002`/`0001`/`0003`。\n* 是否仅在同一问题连续 4 次**计入失败**后才允许`0002`。\n* 是否没有把短促词计入转人工失败次数。\n", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "maxLength": 100000, + "isRichText": true + }, + { + "key": "history", + "renderTypeList": [ + "numberInput", + "reference" + ], + "valueType": "chatHistory", + "label": "common:core.module.input.label.chat history", + "description": "workflow:max_dialog_rounds", + "required": true, + "min": 0, + "max": 50, + "value": 50, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteQA", + "renderTypeList": [ + "settingDatasetQuotePrompt" + ], + "label": "", + "debugLabel": "Dataset Reference", + "description": "", + "valueType": "datasetQuote", + "valueDesc": "", + "toolDescription": "" + }, + { + "key": "fileUrlList", + "renderTypeList": [ + "reference", + "input" + ], + "label": "app:workflow.user_file_input", + "debugLabel": "File Link", + "valueType": "arrayString", + "value": [ + [ + "448745", + "userFiles" + ] + ], + "valueDesc": "", + "description": "app:workflow.user_file_input_desc", + "toolDescription": "" + }, + { + "key": "userChatInput", + "renderTypeList": [ + "reference", + "textarea" + ], + "valueType": "string", + "label": "workflow:user_question", + "toolDescription": "User Question", + "required": true, + "value": [ + "yyB2uYEc8uKA", + "system_text" + ], + "valueDesc": "", + "description": "", + "debugLabel": "" + } + ], + "outputs": [ + { + "id": "history", + "key": "history", + "required": true, + "label": "common:core.module.output.label.New context", + "description": "Splice the current reply content with the history records and return it as the new context", + "valueType": "chatHistory", + "valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]", + "type": "static" + }, + { + "id": "answerText", + "key": "answerText", + "required": true, + "label": "common:core.module.output.label.Ai response content", + "description": "Will be triggered after the stream reply is completed", + "valueType": "string", + "type": "static" + }, + { + "id": "reasoningText", + "key": "reasoningText", + "required": false, + "label": "workflow:reasoning_text", + "valueType": "string", + "type": "static", + "invalid": true, + "description": "" + }, + { + "id": "system_error_text", + "key": "system_error_text", + "type": "error", + "valueType": "string", + "label": "workflow:error_text", + "description": "" + } + ], + "catchError": false + }, + { + "nodeId": "cOeMtHzfTu96", + "name": "解析状态#4", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 5037.393022505725, + "y": 1728.3425878492474 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function extractStateAndContent(data1) {\n // Regular expression to match the STATEcontent format.\n const regex = /(.*?)<\\/state>(.*)/;\n\n // Attempt to match the regex against the input data.\n const match = data1.match(regex);\n\n // If a match is found, extract the state (group 1) and content (group 2).\n if (match && match.length > 2) {\n return {\n state: match[1],\n content: match[2],\n };\n } else {\n // If no match is found, return null or an appropriate error object.\n return { state: null, content: null }; // Or return { state: null, content: null }; or throw an error.\n }\n}\n\nfunction main({data1}){\n const extractedContent = extractStateAndContent(data1);\n const state = extractedContent.state;\n const content = extractedContent.content;\n return {\n content: content,\n state: state\n };\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "string", + "canEdit": true, + "key": "data1", + "label": "data1", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "flCMEFjHRDaL", + "answerText" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "state", + "valueType": "string", + "label": "state", + "valueDesc": "", + "description": "" + }, + { + "id": "udJb1g6GMwOx", + "valueType": "string", + "type": "dynamic", + "key": "content", + "label": "content" + } + ], + "catchError": true + }, + { + "nodeId": "i5yBQcW0h923", + "name": "Condition#5", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 6094.872979286407, + "y": 1957.8425878492471 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "cOeMtHzfTu96", + "qLUQfhG0ILRX" + ], + "condition": "isNotEmpty" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "aln2gMrtCYOo", + "name": "Variable Update#4", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 7080.003590875616, + "y": 2009.8425878492471 + }, + "version": "481", + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "value": [ + "cOeMtHzfTu96", + "qLUQfhG0ILRX" + ], + "valueType": "string", + "renderType": "reference" + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + }, + { + "nodeId": "tBpOv2BTrsvA", + "name": "双车信息确认", + "intro": "AI Large Model Chat", + "avatar": "core/workflow/template/aiChat", + "flowNodeType": "chatNode", + "showStatus": true, + "position": { + "x": 3414.232981050866, + "y": 2994.3781381449016 + }, + "version": "4.9.7", + "inputs": [ + { + "key": "model", + "renderTypeList": [ + "settingLLMModel", + "reference" + ], + "label": "common:core.module.input.label.aiModel", + "valueType": "string", + "value": [ + "VARIABLE_NODE_ID", + "mC3bEH2A" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "selectedTypeIndex": 1 + }, + { + "key": "temperature", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "maxToken", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "isResponseAnswerText", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": true, + "valueType": "boolean", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatQuoteRole", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "value": "system", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteTemplate", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quotePrompt", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatVision", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatReasoning", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatTopP", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatStopSign", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatResponseFormat", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatJsonSchema", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "systemPrompt", + "renderTypeList": [ + "textarea", + "reference" + ], + "max": 3000, + "valueType": "string", + "label": "common:core.ai.Prompt", + "description": "common:core.app.tip.systemPromptTip", + "placeholder": "common:core.app.tip.chatNodeSystemPromptTip", + "value": "# 角色\n你是一个高度集成、**安全第一**的AI信息核查员。你的一切行为都由一个严格的状态机驱动,其首要任务是在核实信息前,优先排查并处理任何与人员受伤相关的紧急情况。\n\n# 首要原则 (必须无条件遵守)\n **输出格式**: 所有回复都必须以 `状态编码` 开头。\n **安全优先(人伤判断克制)**: **任何时候**,仅当用户**语义完整**且明确或高度可信表达存在**人员受伤**时,才中断当前流程并触发`0003`。\n * 明确否定人伤(如“没有人受伤”“人没事”“没有流血”)→ 不触发`0003`,继续当前问题。\n * 仅有孤立人伤词、ASR 破碎、前后矛盾或疑似误转写(如“伤……没有吧”“车有伤”“刚摧毁了”)→ 保持`3002`封闭确认“目前是否有人受伤?请回答有或没有”,**不得**直接转人工。\n * 语义完整且明确有人伤(如“有人受伤了”“流血了”“送医院了”)→ **立即触发`0003`**。\n **转人工克制**: 除用户主动明确要求转人工(`0001`)、确认有人伤(`0003`)、连续两次无应答(`0004`)、或信息核实完成(`0000`)外,**严禁**因短促词、ASR 乱码、模糊敷衍或仅一两轮无效回答就转人工。\n **流程锁定原则 (Gatekeeper Principle)**:\n * **你必须在得到当前问题的有效、相关的答案后,才能进入下一个问题。**\n * 严禁用户使用模糊词、无关回答或指令词来跳过问题。\n * 这些无效回答必须触发“核心对话逻辑”进行处理。\n **准确第一**: 在核对信息时,如果用户回答“不对”或“不是”,必须立刻追问正确的信息,并用追问到的信息覆盖预设值。\n\n# 核心对话逻辑 (处理用户输入的统一协议)\n这是你处理所有用户回复的思考流程:\n\n **收到回复后 - 验证与行动**:\n * **情况A:答案清晰、有效、且相关 (包括用户主动提供的更正信息)**\n * **执行“确认-提问”模式**: 首先,简短复述你确认的信息(`“好的,您的姓名是张三。”`),然后,立即提出流程中的下一个问题。\n * **情况B:答案无效 (模糊、无关、或属于“继续”等回避性回答)**\n * **立即触发“断言式澄清协议” (Assertive Clarification Protocol):**\n * **不计入转人工次数**: 短促词(如“这个”“试试”“嗯”“啊”)、ASR 破碎乱码、听不清、模糊敷衍 → 只温和重复当前问题,保持`3002`,**不累加**失败次数,**不得**触发`0002`。\n * **计入失败次数**: 仅当用户给出**语义完整**但仍无效/无关/明确拒绝配合的回答时,才计入一次澄清失败。\n * **第一级澄清 (锁定问题,明确要求)**: 你必须直接指出回答无效,并强调必须回答当前问题才能继续。\n * **模板**: `“抱歉,我必须先确认[当前问题]的信息才能继续。请正面回答:[完整重复一遍问题]?”`\n * **第二级澄清 (强制选择)**: 如果第一级澄清后,用户依然回避,将问题转化为无法回避的强制选择题。\n * **示例 (针对“是否是驾驶员”问题)**: `3002为了完成核实,我必须知道您是否是该车辆的驾驶员。请明确回答:您‘是’,还是‘不是’?`\n * **示例 (针对身份证后四位)**: `3002请正面回答:您的身份证号码后四位是否是[后四位]?请回答“是”或“不是”。`\n * **示例 (针对手机号后四位)**: `3002请正面回答:您的手机号码后四位是否是[后四位]?请回答“是”或“不是”。`\n * **第三级澄清**: 再次封闭追问,并可提示若仍无法回答将转人工(仍为`3002`)。\n * **最终失败**: 仅当**同一问题**连续 **4 次计入失败**后仍无法获得有效信息,才允许**触发`0002`状态**转接人工。未满 4 次前一律保持`3002`继续追问,不得在第 2 次无效回答后转人工。\n * **情况C:用户无回复 (`【用户无回复】`输入)**\n * **第一次**: 尝试唤醒(`“请问您还在吗?”`)。\n * **第二次连续出现**: **触发`0004`状态**。\n\n---\n\n# 状态编码表 (State Definitions)\n\n| 状态编码 | 定义 | 触发条件与对应回复示例 |\n| :--- | :--- | :--- |\n| **0001** | **转接人工** | 用户主动、明确要求转人工(如“转人工”“找警察”)。短促词或疑似 ASR 误转写不得触发。 |\n| **0002**| **语义无法识别 / 连续偏离主题** | 仅当同一问题连续 4 次**语义完整**仍无效/拒绝配合时触发。短促词、ASR 乱码、模糊敷衍不计入,不得仅因一两轮澄清失败就转人工。 |\n| | | **回复**: `抱歉,我多次尝试还是没能准确确认您的信息。为了不耽误您的时间,现在为您转接人工处理。请稍候。` |\n| **0003**| **有人伤转人工** | **触发条件**: 用户**语义完整**且明确或高度可信确认存在人员受伤。不得仅因孤立人伤词、车辆损坏词或 ASR 乱码触发。 |\n| | | **回复**: `收到,情况紧急。我将立即为您转接人工警员,请千万不要挂断电话。`|\n| **0004**| **长时间无应答**| 连续两次收到【用户无回复】。|\n| **3002**| **通话中** | 安全检查与信息收集中过程中的默认状态;澄清、追问、人伤不确定时的确认均保持此状态。 |\n| **0000** | **核实成功** | 所有驾驶员信息均核实完毕,准备转接人工。 |\n\n---\n\n# 任务流程 (严格按此顺序和逻辑执行)\n\n## **第一部分:采集第一位驾驶员信息**\n\n **承接拍照阶段的驾驶员身份提问(优先于重新启动)**\n * 先检查上一条AI回复。若拍照节点已在 `3002` 中询问第一辆车是否由当前接听人驾驶,本轮用户输入就是对该身份问题的回答;不得因为切换节点而重新启动或再问一遍。\n * 用户回答“是”“是的”“对”“没错”或明确表示本人驾驶:确认身份,立即执行下方“问候并询问姓名”,不得再次询问车牌或驾驶员身份。\n * 用户回答“不是”或明确表示非本人驾驶:执行原有更换通话人逻辑,停留在身份核实步骤。\n * 用户仅说“好”“继续”或回答不清晰:不得默认本人驾驶,按原有澄清规则重复驾驶员身份问题。\n * 只有历史中尚未提出驾驶员身份问题时,才执行下方的启动提问。身份已确认或已进入姓名、身份证、手机号核实时,按历史进度继续,不得退回身份问题。\n\n\n **启动并核实驾驶员身份**\n * **你的输出**: `3002您好。请问车牌号为 {{$fsWVpSkboqW3.rWawP4BJxuH1$}} 的车辆是由您驾驶的吗?`\n * **处理逻辑**:\n * 如用户答“是”,则记录信息并进入下一步。\n * 如用户答“不是”,则要求更换通话人:`3002好的,请车牌号为 {{$fsWVpSkboqW3.rWawP4BJxuH1$}} 的车辆驾驶员来接听电话。`\n * 如用户答短促/ASR破碎(如“这个”“试试”“嗯”):保持`3002`,重复当前问题,不转人工。\n\n **问候并询问姓名**\n * **你的输出**: `3002好的,驾驶员您好。为了方便称呼,请问您的姓名是什么?`\n\n **核实身份证**\n * **系统预设的身份证号码**: {{$fsWVpSkboqW3.qLUQfhG0ILRX$}}\n * **你的输出**: `3002好的,[用户姓名]。请问您的身份证号码后四位是否是[系统预设的身份证号码的后四位]?`\n * **处理逻辑**:\n * **如果用户回答“是”“是的”“对”“没错”**: 确认信息,进入下一步。\n * **如果用户回答“不是”“不对”**: **追问** `“好的,那么您正确的身份证号码是多少?”`\n * **如果用户回答短促/无关/ASR破碎**(如“这个”“试试”“嗯”“啊”“什么意思”):\n * 保持`3002`,不推进、不计入失败次数、不转`0002`。\n * **你的输出**: `3002抱歉,我需要确认您的身份证后四位信息,请正面回答:您的身份证号码后四位是否是[系统预设的身份证号码的后四位]?`\n * 处理分段输入/号码不完整:\n * 如果用户只提供了一部分号码(如长度明显少于18位,例如“开头是110101...”),严禁让用户重新输入。\n * 你的行动: 复述你听到的部分,并引导补充。\n * 输出示例: 3002好的,开头是110101,后面是多少呢?\n * 循环: 重复此“接收-引导”过程,直到拼凑出完整号码。\n * 记录并完整复述以供确认: 获得完整号码后,必须播报完整的号码进行二次确认。\n * 你的输出: 3002好的,我跟您确认,您正确的身份证号码是[用户提供的完整新号码],对吗?\n * 处理二次确认:\n * 如果用户回答“对”,则用新号码覆盖预设值,进入下一步。\n * 如果用户回答“不对”,则重复步骤1“追问正确号码”。\n * 如果用户回答“这个”“试试”等短促无效词,重复二次确认问题,保持`3002`,不得转人工。\n\n **核实手机号**\n * **系统预设的手机号**: {{$fsWVpSkboqW3.gR0mkQpJ4Og8$}}\n * **你的输出**: `3002请问您的手机号码后四位是否是[系统预设手机号的后四位]?`\n * **处理逻辑**:\n * **如果用户回答“是”“是的”“对”“没错”**: 确认信息,进入**第二部分**。\n * **如果用户回答“不是”“不对”**: **追问** `“好的,那么请告诉我您正在使用的手机号码?”`\n * **如果用户回答短促/无关/ASR破碎**(如“这个”“试试”“嗯”“啊”“什么意思”):\n * 保持`3002`,不推进、不计入失败次数、不转`0002`。\n * **你的输出**: `3002抱歉,我需要确认您的手机号码后四位信息,请正面回答:您的手机号码后四位是否是[系统预设手机号的后四位]?`\n * 处理分段输入/号码不完整:\n * 如果用户只提供了一部分号码(如长度明显少于11位,例如“1380013...”),严禁让用户重新输入。\n * 你的行动: 复述你听到的部分,并引导补充。\n * 输出示例: 3002收到,1380013,接着是多少?\n * 循环: 重复此“接收-引导”过程,直到拼凑出完整号码。\n * 记录并完整复述以供确认: 获得完整号码后,必须播报完整的号码进行二次确认。\n * 你的输出: 3002跟您确认一下,您正在使用的手机号码是[用户提供的完整新号码],对吗?\n * 处理二次确认:\n * 如果用户回答“对”,则用新号码覆盖预设值,进入第二部分。\n * 如果用户回答“不对”,则重复步骤1“追问正确号码”。\n * 如果用户回答“这个”“试试”等短促无效词,重复二次确认问题,保持`3002`,不得转人工。\n\n## **第二部分:采集第二位驾驶员信息**\n\n **请求转接电话**\n * **触发**: 第一位驾驶员手机号核实完毕后。\n * **你的输出**: `3002好的,第一位驾驶员的信息已核实完毕。现在请您将电话交给另一位当事驾驶员,然后告诉我移交完毕。`\n\n **核实第二位驾驶员身份**\n * **你的输出**: `3002您好。请问车牌号为 {{$fsWVpSkboqW3.wHUmHTU2Rhlb$}} 的车辆是由您驾驶的吗?`\n * **处理逻辑**:\n * 如用户答“是”,则记录信息并进入下一步。\n * 如用户答“不是”,则必须要求更换通话人:`3002好的,为了完成核实,必须由该车驾驶员本人接听。请将电话交给他。` 然后**停留在此步骤**,直到确认为本人。\n * 如用户答短促/ASR破碎:保持`3002`,重复当前问题,不转人工。\n\n **询问姓名**\n * **你的输出**: `3002好的,驾驶员您好。请问您的姓名是?`\n\n **核实身份证**\n * **系统预设的身份证号码**: {{$fsWVpSkboqW3.cUeHVjpSQUr1$}}\n * **你的输出**: `3002好的,[用户姓名]。请问您的身份证号码后四位是否是[系统预设的身份证号码的后四位]?`\n * **处理逻辑**: 与第一位驾驶员相同,确认后进入下一步。短促/ASR无效输入保持`3002`重复当前问题,不得转人工。\n\n **核实手机号**\n * **系统预设的手机号**: {{$fsWVpSkboqW3.bXzM05j1bbwn$}}\n * **你的输出**: `3002请问您的手机号码后四位是否是[系统预设手机号的后四位]?`\n * **处理逻辑**: 与第一位驾驶员相同。确认后进入**第三部分**。短促/ASR无效输入保持`3002`重复当前问题,不得转人工。\n\n## **第三部分:结束流程**\n\n**完成核实并转接**\n * **触发条件**: 第二位驾驶员手机号核实完毕后。\n * **你的输出**: `0000好的,两位驾驶员的信息均已核实完毕。感谢您的配合,下面将转接人工继续处理。`\n\n---\n\n# 最终输出自检\n\n在每次输出前,必须完成以下检查:\n\n* 最终回复是否以 `状态编码` 开头。\n* 对“这个”“试试”“嗯”“啊”“什么意思”等短促/ASR无效输入,是否保持`3002`并重复当前问题,而不是输出`0002`/`0001`/`0003`。\n* 是否仅在同一问题连续 4 次**计入失败**后才允许`0002`。\n* 是否没有把短促词计入转人工失败次数。\n* 是否没有因孤立人伤词、车辆损坏词或 ASR 乱码误触发`0003`。\n", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "maxLength": 100000, + "isRichText": true + }, + { + "key": "history", + "renderTypeList": [ + "numberInput", + "reference" + ], + "valueType": "chatHistory", + "label": "common:core.module.input.label.chat history", + "description": "workflow:max_dialog_rounds", + "required": true, + "min": 0, + "max": 50, + "value": 50, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteQA", + "renderTypeList": [ + "settingDatasetQuotePrompt" + ], + "label": "", + "debugLabel": "Dataset Reference", + "description": "", + "valueType": "datasetQuote", + "valueDesc": "", + "toolDescription": "" + }, + { + "key": "fileUrlList", + "renderTypeList": [ + "reference", + "input" + ], + "label": "app:workflow.user_file_input", + "debugLabel": "File Link", + "valueType": "arrayString", + "value": [ + [ + "448745", + "userFiles" + ] + ], + "valueDesc": "", + "description": "app:workflow.user_file_input_desc", + "toolDescription": "" + }, + { + "key": "userChatInput", + "renderTypeList": [ + "reference", + "textarea" + ], + "valueType": "string", + "label": "workflow:user_question", + "toolDescription": "User Question", + "required": true, + "value": [ + "yyB2uYEc8uKA", + "system_text" + ], + "valueDesc": "", + "description": "", + "debugLabel": "" + } + ], + "outputs": [ + { + "id": "history", + "key": "history", + "required": true, + "label": "common:core.module.output.label.New context", + "description": "Splice the current reply content with the history records and return it as the new context", + "valueType": "chatHistory", + "valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]", + "type": "static" + }, + { + "id": "answerText", + "key": "answerText", + "required": true, + "label": "common:core.module.output.label.Ai response content", + "description": "Will be triggered after the stream reply is completed", + "valueType": "string", + "type": "static" + }, + { + "id": "reasoningText", + "key": "reasoningText", + "required": false, + "label": "workflow:reasoning_text", + "valueType": "string", + "type": "static", + "invalid": true, + "description": "" + }, + { + "id": "system_error_text", + "key": "system_error_text", + "type": "error", + "valueType": "string", + "label": "workflow:error_text", + "description": "" + } + ], + "catchError": false + }, + { + "nodeId": "k4pGjQ0cd04e", + "name": "解析状态#5", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 4885.7185029331395, + "y": 3121.0631098659824 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function extractStateAndContent(data1) {\n // Regular expression to match the STATEcontent format.\n const regex = /(.*?)<\\/state>(.*)/;\n\n // Attempt to match the regex against the input data.\n const match = data1.match(regex);\n\n // If a match is found, extract the state (group 1) and content (group 2).\n if (match && match.length > 2) {\n return {\n state: match[1],\n content: match[2],\n };\n } else {\n // If no match is found, return null or an appropriate error object.\n return { state: null, content: null }; // Or return { state: null, content: null }; or throw an error.\n }\n}\n\nfunction main({data1}){\n const extractedContent = extractStateAndContent(data1);\n const state = extractedContent.state;\n const content = extractedContent.content;\n return {\n content: content,\n state: state\n };\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "string", + "canEdit": true, + "key": "data1", + "label": "data1", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "tBpOv2BTrsvA", + "answerText" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "state", + "valueType": "string", + "label": "state", + "valueDesc": "", + "description": "" + }, + { + "id": "udJb1g6GMwOx", + "valueType": "string", + "type": "dynamic", + "key": "content", + "label": "content" + } + ], + "catchError": true + }, + { + "nodeId": "euRwFTajqcRO", + "name": "Condition#6", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 5853.466296984162, + "y": 3338.8749119355352 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "k4pGjQ0cd04e", + "qLUQfhG0ILRX" + ], + "condition": "isEmpty" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "io07e4nO84k2", + "name": "Variable Update#5", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 7154.397349538959, + "y": 3465.415610920879 + }, + "version": "481", + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "value": [ + "k4pGjQ0cd04e", + "qLUQfhG0ILRX" + ], + "valueType": "string", + "renderType": "reference" + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + }, + { + "nodeId": "krClalszmq68", + "name": "Condition#7", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 3235.0533084067215, + "y": -785.7784434645952 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "OR", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2000" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2001" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2002" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2003" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "sUStm4ekiYNS", + "name": "Condition#9", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 3534.6261499054604, + "y": 226.47271316136192 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "OR", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2010" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2011" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2012" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2013" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2014" + }, + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "condition": "equalTo", + "value": "2015" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "fsWVpSkboqW3", + "name": "Code Sandbox#7", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 2587.8879951603426, + "y": 2099.7067792182334 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function main({state}){\n return {driver1License: state.hphm1,\n driver1ID: state.driver1ID,\n driver1Phonenumber: state.driver1Phonenumber,\n driver2License: state.hphm2,\n driver2ID: state.driver2ID,\n driver2Phonenumber: state.driver2Phonenumber\n }\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "object", + "canEdit": true, + "key": "state", + "label": "state", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "value": [ + "VARIABLE_NODE_ID", + "state" + ] + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "driver1ID", + "valueType": "string", + "label": "driver1ID", + "valueDesc": "", + "description": "" + }, + { + "id": "gR0mkQpJ4Og8", + "type": "dynamic", + "key": "driver1Phonenumber", + "valueType": "string", + "label": "driver1Phonenumber", + "valueDesc": "", + "description": "" + }, + { + "id": "wHUmHTU2Rhlb", + "valueType": "string", + "type": "dynamic", + "key": "driver2License", + "label": "driver2License" + }, + { + "id": "cUeHVjpSQUr1", + "valueType": "string", + "type": "dynamic", + "key": "driver2ID", + "label": "driver2ID" + }, + { + "id": "rWawP4BJxuH1", + "valueType": "string", + "type": "dynamic", + "key": "driver1License", + "label": "driver1License" + }, + { + "id": "bXzM05j1bbwn", + "valueType": "string", + "type": "dynamic", + "key": "driver2Phonenumber", + "label": "driver2Phonenumber" + } + ], + "catchError": true + }, + { + "nodeId": "sJHCC6Uh5j2u", + "name": "Code Sandbox#10", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 3135, + "y": 945 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function main({state}){\n return {driver1License: state.hphm1,\n driver1ID: state.driver1ID,\n driver1Phonenumber: state.driver1Phonenumber,\n driver2License: state.hphm2,\n driver2ID: state.driver2ID,\n driver2Phonenumber: state.driver2Phonenumber\n }\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "object", + "canEdit": true, + "key": "state", + "label": "state", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "value": [ + "VARIABLE_NODE_ID", + "state" + ] + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "driver1ID", + "valueType": "string", + "label": "driver1ID", + "valueDesc": "", + "description": "" + }, + { + "id": "gR0mkQpJ4Og8", + "type": "dynamic", + "key": "driver1Phonenumber", + "valueType": "string", + "label": "driver1Phonenumber", + "valueDesc": "", + "description": "" + }, + { + "id": "wHUmHTU2Rhlb", + "valueType": "string", + "type": "dynamic", + "key": "driver2License", + "label": "driver2License" + }, + { + "id": "cUeHVjpSQUr1", + "valueType": "string", + "type": "dynamic", + "key": "driver2ID", + "label": "driver2ID" + }, + { + "id": "rWawP4BJxuH1", + "valueType": "string", + "type": "dynamic", + "key": "driver1License", + "label": "driver1License" + }, + { + "id": "bXzM05j1bbwn", + "valueType": "string", + "type": "dynamic", + "key": "driver2Phonenumber", + "label": "driver2Phonenumber" + } + ], + "catchError": true + }, + { + "nodeId": "mbP4DRmqf3qT", + "name": "Code Sandbox#9", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 4885.7185029331395, + "y": 168.96756250333965 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function main({state}){\n return {driver1License: state.hphm1,\n driver1ID: state.driver1ID,\n driver1Phonenumber: state.driver1Phonenumber,\n driver2License: state.hphm2,\n driver2ID: state.driver2ID,\n driver2Phonenumber: state.driver2Phonenumber\n }\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "object", + "canEdit": true, + "key": "state", + "label": "state", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "value": [ + "VARIABLE_NODE_ID", + "state" + ] + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "driver1ID", + "valueType": "string", + "label": "driver1ID", + "valueDesc": "", + "description": "" + }, + { + "id": "gR0mkQpJ4Og8", + "type": "dynamic", + "key": "driver1Phonenumber", + "valueType": "string", + "label": "driver1Phonenumber", + "valueDesc": "", + "description": "" + }, + { + "id": "wHUmHTU2Rhlb", + "valueType": "string", + "type": "dynamic", + "key": "driver2License", + "label": "driver2License" + }, + { + "id": "cUeHVjpSQUr1", + "valueType": "string", + "type": "dynamic", + "key": "driver2ID", + "label": "driver2ID" + }, + { + "id": "rWawP4BJxuH1", + "valueType": "string", + "type": "dynamic", + "key": "driver1License", + "label": "driver1License" + }, + { + "id": "bXzM05j1bbwn", + "valueType": "string", + "type": "dynamic", + "key": "driver2Phonenumber", + "label": "driver2Phonenumber" + } + ], + "catchError": true + }, + { + "nodeId": "i2xYvUNWE7Zv", + "name": "Code Sandbox#9", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 5138.581271863097, + "y": -999.6250337216792 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function main({state}){\n return {driver1License: state.hphm1,\n driver1ID: state.driver1ID,\n driver1Phonenumber: state.driver1Phonenumber,\n driver2License: state.hphm2,\n driver2ID: state.driver2ID,\n driver2Phonenumber: state.driver2Phonenumber\n }\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "object", + "canEdit": true, + "key": "state", + "label": "state", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "value": [ + "VARIABLE_NODE_ID", + "state" + ] + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "driver1ID", + "valueType": "string", + "label": "driver1ID", + "valueDesc": "", + "description": "" + }, + { + "id": "gR0mkQpJ4Og8", + "type": "dynamic", + "key": "driver1Phonenumber", + "valueType": "string", + "label": "driver1Phonenumber", + "valueDesc": "", + "description": "" + }, + { + "id": "wHUmHTU2Rhlb", + "valueType": "string", + "type": "dynamic", + "key": "driver2License", + "label": "driver2License" + }, + { + "id": "cUeHVjpSQUr1", + "valueType": "string", + "type": "dynamic", + "key": "driver2ID", + "label": "driver2ID" + }, + { + "id": "rWawP4BJxuH1", + "valueType": "string", + "type": "dynamic", + "key": "driver1License", + "label": "driver1License" + }, + { + "id": "bXzM05j1bbwn", + "valueType": "string", + "type": "dynamic", + "key": "driver2Phonenumber", + "label": "driver2Phonenumber" + } + ], + "catchError": true + }, + { + "nodeId": "z5y22KY72pOV", + "name": "解析状态#10", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 9390, + "y": -2109 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function extractStateAndContent(data1) {\n // Regular expression to match the STATEcontent format.\n const regex = /(.*?)<\\/state>(.*)/;\n\n // Attempt to match the regex against the input data.\n const match = data1.match(regex);\n\n // If a match is found, extract the state (group 1) and content (group 2).\n if (match && match.length > 2) {\n return {\n state: match[1],\n content: match[2],\n };\n } else {\n // If no match is found, return null or an appropriate error object.\n return { state: null, content: null }; // Or return { state: null, content: null }; or throw an error.\n }\n}\n\nfunction main({data1}){\n const extractedContent = extractStateAndContent(data1);\n const state = extractedContent.state;\n const content = extractedContent.content;\n return {\n content: content,\n state: state\n };\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "string", + "canEdit": true, + "key": "data1", + "label": "data1", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "mCWJYYru8qPR", + "answerText" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "state", + "valueType": "string", + "label": "state", + "valueDesc": "", + "description": "" + }, + { + "id": "udJb1g6GMwOx", + "valueType": "string", + "type": "dynamic", + "key": "content", + "label": "content" + } + ], + "catchError": true + }, + { + "nodeId": "q3IxKaOW5gdV", + "name": "解析状态#9", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 9495, + "y": 495 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function extractStateAndContent(data1) {\n // Regular expression to match the STATEcontent format.\n const regex = /(.*?)<\\/state>(.*)/;\n\n // Attempt to match the regex against the input data.\n const match = data1.match(regex);\n\n // If a match is found, extract the state (group 1) and content (group 2).\n if (match && match.length > 2) {\n return {\n state: match[1],\n content: match[2],\n };\n } else {\n // If no match is found, return null or an appropriate error object.\n return { state: null, content: null }; // Or return { state: null, content: null }; or throw an error.\n }\n}\n\nfunction main({data1}){\n const extractedContent = extractStateAndContent(data1);\n const state = extractedContent.state;\n const content = extractedContent.content;\n return {\n content: content,\n state: state\n };\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "string", + "canEdit": true, + "key": "data1", + "label": "data1", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "k318kWi02OU9", + "answerText" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "state", + "valueType": "string", + "label": "state", + "valueDesc": "", + "description": "" + }, + { + "id": "udJb1g6GMwOx", + "valueType": "string", + "type": "dynamic", + "key": "content", + "label": "content" + } + ], + "catchError": true + }, + { + "nodeId": "kbba6T0A0cME", + "name": "Assigned Reply#2", + "intro": "This module can directly reply with a specified content. Commonly used for guidance or prompts. Non-string content will be converted to string for output.", + "avatar": "core/workflow/template/reply", + "flowNodeType": "answerNode", + "position": { + "x": 2091.8142617646504, + "y": 1167.9675625033396 + }, + "version": "481", + "inputs": [ + { + "key": "text", + "renderTypeList": [ + "textarea", + "reference" + ], + "valueType": "any", + "required": true, + "label": "common:core.module.input.label.Response content", + "description": "common:core.module.input.description.Response content", + "placeholder": "common:core.module.input.description.Response content", + "value": "{{$VARIABLE_NODE_ID.status_code$}}所有信息以收集完毕,正在转接人工,请稍候", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "isRichText": false, + "maxLength": 100000 + } + ], + "outputs": [] + }, + { + "nodeId": "mzDpCKsdDlZd", + "name": "Assigned Reply#3", + "intro": "This module can directly reply with a specified content. Commonly used for guidance or prompts. Non-string content will be converted to string for output.", + "avatar": "core/workflow/template/reply", + "flowNodeType": "answerNode", + "position": { + "x": 1873.8183718024648, + "y": 1684.8778651206744 + }, + "version": "481", + "inputs": [ + { + "key": "text", + "renderTypeList": [ + "textarea", + "reference" + ], + "valueType": "any", + "required": true, + "label": "common:core.module.input.label.Response content", + "description": "common:core.module.input.description.Response content", + "placeholder": "common:core.module.input.description.Response content", + "value": "{{$VARIABLE_NODE_ID.status_code$}}正在转接人工,请稍候", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "isRichText": false, + "maxLength": 100000 + } + ], + "outputs": [] + }, + { + "nodeId": "xWEWw9JV6GFa", + "name": "Assigned Reply#3", + "intro": "This module can directly reply with a specified content. Commonly used for guidance or prompts. Non-string content will be converted to string for output.", + "avatar": "core/workflow/template/reply", + "flowNodeType": "answerNode", + "position": { + "x": 555, + "y": -2010 + }, + "version": "481", + "inputs": [ + { + "key": "text", + "renderTypeList": [ + "textarea", + "reference" + ], + "valueType": "any", + "required": true, + "label": "common:core.module.input.label.Response content", + "description": "common:core.module.input.description.Response content", + "placeholder": "common:core.module.input.description.Response content", + "value": "1001您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "isRichText": false, + "maxLength": 100000 + } + ], + "outputs": [] + }, + { + "nodeId": "mErEqe061uAd", + "name": "Variable Update#6", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 585, + "y": -2505 + }, + "version": "481", + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "is_first_message" + ], + "value": [ + "", + "" + ], + "valueType": "boolean", + "renderType": "input" + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + }, + { + "nodeId": "ffV7U6t2Wrkc", + "name": "Condition#9", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 436.7701670253358, + "y": -1391.7660436128006 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "is_first_message" + ], + "condition": "equalTo", + "value": "true" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "dDTqgmBFNDPh0DwX", + "name": "Condition#10", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 3075, + "y": -3165 + }, + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "zl8sNWrf" + ], + "condition": "equalTo", + "value": "true", + "valueType": "input" + } + ] + } + ], + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "valueDesc": "", + "description": "" + } + ] + }, + { + "nodeId": "znnXN7FuRHfCwJmw", + "name": "文本内容提取事故信息", + "intro": "Can extract specified data from text, such as SQL statements, search keywords, code, etc.", + "avatar": "core/workflow/template/extractJson", + "flowNodeType": "contentExtract", + "showStatus": true, + "position": { + "x": 4140, + "y": -3510.019607132754 + }, + "version": "4.9.2", + "inputs": [ + { + "key": "model", + "renderTypeList": [ + "selectLLMModel", + "reference" + ], + "label": "common:core.module.input.label.aiModel", + "required": true, + "valueType": "string", + "llmModelType": "extractFields", + "value": [ + "VARIABLE_NODE_ID", + "mC3bEH2A" + ], + "debugLabel": "", + "toolDescription": "", + "selectedTypeIndex": 1 + }, + { + "key": "description", + "renderTypeList": [ + "textarea", + "reference" + ], + "valueType": "string", + "label": "workflow:extraction_requirements_description", + "description": "workflow:extraction_requirements_description_detail", + "placeholder": "workflow:extraction_requirements_placeholder", + "value": "你是交通事故信息提取器。根据本轮用户输入及对话历史提取有明确证据的字段,新旧值比较由后续更新表单节点处理。\n\n目前时间为:\n{{$VARIABLE_NODE_ID.cTime$}}\n\n目前表单为:\n{{$VARIABLE_NODE_ID.bNJkeXot$}}\n\n# 可更新字段(仅允许这些 key)\n- has_injury: boolean,是否有人受伤\n- has_non_motor_vehicle_hit: boolean,本次事故是否涉及非机动车,不区分车辆归属、碰撞方向或事故中的主被动角色。\n- accident_time: string,事故发生时间\n- is_on_scene: boolean,报警人是否在事故现场\n- accident_cause: string,根据用户已明确的事故经过归纳的分类或说明。机动车分类为追尾、变道、未让行、违反信号、逆行、倒车、溜车、撞停、超速、其他;明确涉及非机动车的事故统一使用“其他:”加简短经过说明。\n- vehicle_count: number,事故车辆数量(机动车辆数)\n\n# 最高原则(必须遵守)\n1. **只根据用户在对话中的明确表述提取,允许结合上下文进行语义归纳、指代消解和字段映射,禁止补充用户未表达的事实。** 提取口径与事故信息采集节点一致,用户已明确表达的信息不要求使用字段名称、分类名称或标准问答措辞。\n2. 对话里没问到、用户也没主动明确说过的字段,**一律不返回**。\n3. 不得因为“报警人在描述事故细节”“口吻像在现场”“常识上应该在现场”等理由,自行填写 `is_on_scene` 或其他字段。\n4. 目前表单不构成用户证据,不得据此或按流程默认假设补齐未知字段;历史相对时间已有明确换算值且用户未更正时可沿用,不重新换算。\n\n# 明确证据要求(逐字段)\n只有出现可直接对应的明确证据时才可提取;否则跳过该字段。\n\n- has_injury:用户明确说有人伤/无人伤(如“有人受伤了”“人没事”“没有流血”)。仅说“车坏了”“撞得很严重”不等于人伤。\n- has_non_motor_vehicle_hit:结合历史识别所有参与方,自身或其他非机动车参与即为true;明确排除全部非机动车才为false。否认其他车辆或某个被撞对象,不否认已明确的自身非机动车;只有明确更正车辆类型或参与事实才覆盖旧值。真实矛盾或是否参与不明时不更新。\n- accident_time:用户明确给出可换算的时间,或“现在/刚刚/刚才”等相对时间。未给时间不返回。\n- is_on_scene:仅当用户明确回答在/不在现场(如“我还在现场”“我已经离开了”“不在现场”)才返回。 \n **禁止**因未询问现场、或用户只是在描述事故,就默认 `true`。\n- accident_cause:以事故信息采集节点的事故经过口径为准。用户已清楚说明事故如何发生,即可根据其明确表达的事实分类或概括,不要求额外说明事故致因、违法原因或责任。明确涉及非机动车的事故统一返回“其他:”加简短经过说明,不套用机动车分类;其余机动车事故能明确归入既有分类时返回对应分类,经过清楚但无法归类时返回“其他:”加简短经过说明。说明只概括用户已表达的参与方、行为或碰撞关系。经过缺失、含糊或矛盾而无法确定时,不返回该字段;不得仅因致因未明确或无法归类而遗漏已明确的经过。\n- vehicle_count:仅统计本次事故的机动车数量,非机动车不计入。用户明确描述自身非机动车单方撞固定物时,机动车数量记录为0,不再追问数量;按完整事故经过识别该场景,不要求额外使用“单方”或“没有其他车辆”的固定措辞。仅说明车辆类型或仅提到固定物不适用;历史或本轮明确涉及其他车辆、碰撞关系不清或前后矛盾时,不按该场景填0。其他事故按用户明确的机动车数量提取;明确全部车辆均为非机动车时也返回0,否则参与范围或数量未明确时不返回。用户明确更正车辆类型或数量时同步更新,不将“涉及非机动车”一概映射为0。\n\n# 提取规则\n1. 逐字段检查用户已明确表达的事实,有证据即可返回,不由模型过滤与目前表单相同的值。历史可用于指代消解、承接问答及补回遗漏;以用户最新明确更正为准,更正影响其他字段的表述时同步修正。AI的提问、复述、流程状态和转人工决定本身不构成事实证据。\n2. 不确定的字段单独跳过,不影响其他明确字段的提取。有直接证据的字段必须返回,不得因其他字段未知、当前问题尚未回答或本轮即将分流而遗漏。\n3. 布尔字段:按用户完整语义及历史判断肯定或否定的对象,不按孤立否定词或AI提问直接赋值;明确肯定→true,明确否定→false,含糊或矛盾不更新。\n4. accident_time 格式固定:YYYY年M月D日H点m分 \n 示例:2026年6月17日10点51分 \n - 本轮新提供的相对时间按当前系统时间换算;“X分钟前”为当前时间减X分钟,只有“现在”直接取当前时间。\n - 历史相对时间已有明确换算值且未更正时沿用该值,不随本轮时间重新换算;无可靠换算基准或无法精确到分则不返回。\n5. vehicle_count 按上述机动车数量规则提取,允许0。accident_cause 按上述事故经过规则提取;使用“其他”时统一输出“其他:”加简短经过说明,保留为一个字符串字段,不新增说明字段,不在“其他”与原意简述之间任选其一。禁止根据事故结果推断用户未说明的违法行为、事故致因或责任。\n\n# 反例(这些情况都不得提取)\n- 没问“是否在事故现场”,用户也没说在不在 → 不返回 is_on_scene\n- 用户说“两车追尾”,未说人伤 → 不返回 has_injury\n- 用户说“刚才撞了”,未说非机动车 → 不返回 has_non_motor_vehicle_hit\n- 仅从语气/场景感觉“应该在现场” → 禁止返回 is_on_scene:true\n\n# 输出要求\nformUpdate 必须是对象,键仅限可更新字段;无可提取字段时为{}。不要将对象序列化为字符串,不输出解释。\n\n输出示例:\n{\"accident_time\":\"2026年6月17日10点51分\",\"vehicle_count\":2}\n{\"has_injury\":false,\"has_non_motor_vehicle_hit\":true}\n{}", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "history", + "renderTypeList": [ + "numberInput", + "reference" + ], + "valueType": "chatHistory", + "label": "common:core.module.input.label.chat history", + "description": "workflow:max_dialog_rounds", + "required": true, + "min": 0, + "max": 50, + "value": 50, + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "content", + "renderTypeList": [ + "reference", + "textarea" + ], + "label": "workflow:text_to_extract", + "required": true, + "valueType": "string", + "toolDescription": "Content to Retrieve", + "value": [ + "448745", + "userChatInput" + ], + "debugLabel": "" + }, + { + "key": "extractKeys", + "renderTypeList": [ + "custom" + ], + "label": "", + "valueType": "any", + "description": "workflow:target_fields_description", + "value": [ + { + "valueType": "object", + "required": true, + "defaultValue": "", + "desc": "表单更新数据", + "key": "formUpdate", + "enum": "" + } + ], + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "success", + "key": "success", + "label": "workflow:full_field_extraction", + "required": true, + "description": "Returns true when all fields are fully extracted (success includes model extraction or using default values)", + "valueType": "boolean", + "type": "static", + "valueDesc": "" + }, + { + "id": "fields", + "key": "fields", + "label": "workflow:complete_extraction_result", + "required": true, + "description": "A JSON string, e.g., {\"name\":\"YY\",\"Time\":\"2023/7/2 18:00\"}", + "valueType": "string", + "type": "static", + "valueDesc": "" + }, + { + "id": "system_error_text", + "key": "system_error_text", + "type": "error", + "valueType": "string", + "label": "workflow:error_text", + "valueDesc": "", + "description": "" + }, + { + "id": "t304GMFxQwoG9Gqk", + "key": "formUpdate", + "label": "Extraction Results-formUpdate", + "valueType": "object", + "type": "static" + } + ], + "catchError": false + }, + { + "nodeId": "ywRJRfv7ytNbITE9", + "name": "更新表单", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 5160, + "y": -3465 + }, + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "value": "js", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "valueType": "string", + "value": "function main({ form, formUpdate }) {\n // 1. 规范化 form:必须是普通对象,否则用空对象\n const baseForm =\n form && typeof form === \"object\" && !Array.isArray(form) ? form : {};\n\n // 2. 规范化 formUpdate:LLM 可能返回 JSON 字符串,需先解析\n let updateData = formUpdate;\n if (typeof updateData === \"string\") {\n try {\n updateData = JSON.parse(updateData);\n } catch (e) {\n updateData = {};\n }\n }\n if (!updateData || typeof updateData !== \"object\" || Array.isArray(updateData)) {\n updateData = {};\n }\n\n // 判断是否为有效更新值(空字符串 / null / undefined 视为无效)\n function hasMeaningfulValue(v) {\n return !(v === undefined || v === null || v === \"\");\n }\n\n // 比较新旧值是否相同(数字与数字字符串视为相等,如 2 与 \"2\")\n function isSameValue(a, b) {\n if (a === b) return true;\n if (a == null || b == null) return a === b;\n // 空字符串表示未填写,不能与数值 0 视为相同\n if (a === \"\" || b === \"\") return false;\n if (\n (typeof a === \"number\" || typeof b === \"number\") &&\n !Number.isNaN(Number(a)) &&\n !Number.isNaN(Number(b))\n ) {\n return Number(a) === Number(b);\n }\n return String(a).trim() === String(b).trim();\n }\n\n // 3. 合并完整表单,并收集真正发生变化的字段\n const formNew = { ...baseForm }; // 完整合并后的表单(写回变量)\n const changed = {}; // 仅包含实际更新的字段(给前端)\n\n // 只遍历表单已有字段,LLM 多提取的无关 key 直接忽略\n for (const key of Object.keys(baseForm)) {\n if (!Object.prototype.hasOwnProperty.call(updateData, key)) continue;\n\n const next = updateData[key];\n\n // 无效值:不更新\n if (!hasMeaningfulValue(next)) continue;\n\n // 与现有值相同:筛掉,不计入更新\n if (isSameValue(baseForm[key], next)) continue;\n\n // 真正有变化:写入完整表单,并记入变更集\n formNew[key] = next;\n changed[key] = next;\n }\n\n return {\n formNew, // 完整合并结果 → 更新 {{$VARIABLE_NODE_ID.bNJkeXot$}}\n formUpdate: changed // 仅变更字段 → 下游 / SSE 推送\n };\n}", + "debugLabel": "", + "toolDescription": "" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "object", + "canEdit": true, + "key": "formUpdate", + "label": "formUpdate", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "znnXN7FuRHfCwJmw", + "t304GMFxQwoG9Gqk" + ] + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "object", + "canEdit": true, + "key": "form", + "label": "form", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "VARIABLE_NODE_ID", + "bNJkeXot" + ] + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "valueDesc": "", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "valueType": "string", + "type": "error", + "valueDesc": "", + "description": "" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "formNew", + "valueType": "object", + "label": "formNew", + "valueDesc": "", + "description": "" + }, + { + "id": "vk38Nc", + "type": "dynamic", + "key": "formUpdate", + "label": "formUpdate", + "valueType": "object", + "valueDesc": "", + "description": "" + } + ], + "catchError": false + }, + { + "nodeId": "zbwLMJrAICiypHwX", + "name": "Variable Update#7", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 6420, + "y": -3030 + }, + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "bNJkeXot" + ], + "value": [ + "ywRJRfv7ytNbITE9", + "qLUQfhG0ILRX" + ], + "valueType": "object", + "renderType": "reference" + } + ], + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + }, + { + "nodeId": "hg1kXAGZQXdySAwt", + "name": "Condition#11", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 6060, + "y": -525 + }, + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "kEF7CNwD" + ], + "condition": "equalTo", + "value": "direct", + "valueType": "input" + } + ] + } + ], + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "valueDesc": "", + "description": "" + } + ] + }, + { + "nodeId": "wzURZDVRotLrao1G", + "name": "Condition#12", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 5985, + "y": 630 + }, + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "kEF7CNwD" + ], + "condition": "equalTo", + "value": "direct", + "valueType": "input" + } + ] + } + ], + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "valueDesc": "", + "description": "" + } + ] + }, + { + "nodeId": "hSsBFAZ03mSHSUQm", + "name": "单车拍照#6", + "intro": "AI Large Model Chat", + "avatar": "core/workflow/template/aiChat", + "flowNodeType": "chatNode", + "showStatus": true, + "position": { + "x": 8415, + "y": -975 + }, + "version": "4.9.7", + "inputs": [ + { + "key": "model", + "renderTypeList": [ + "settingLLMModel", + "reference" + ], + "label": "common:core.module.input.label.aiModel", + "valueType": "string", + "value": [ + "VARIABLE_NODE_ID", + "mC3bEH2A" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "selectedTypeIndex": 1 + }, + { + "key": "temperature", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "maxToken", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "isResponseAnswerText", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": true, + "valueType": "boolean", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatQuoteRole", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "value": "system", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteTemplate", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quotePrompt", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatVision", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatReasoning", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatTopP", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatStopSign", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatResponseFormat", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatJsonSchema", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "systemPrompt", + "renderTypeList": [ + "textarea", + "reference" + ], + "max": 3000, + "valueType": "string", + "label": "common:core.ai.Prompt", + "description": "common:core.app.tip.systemPromptTip", + "placeholder": "common:core.app.tip.chatNodeSystemPromptTip", + "value": "# 角色\n\n你现在进入了**事故现场拍照引导**阶段。你的角色是一名严谨、清晰的AI现场勘查引导员。你的任务是严格按照预设流程引导当事人。在此期间,你的行为模式会根据具体步骤发生改变。\n\n# 最高原则(不可违背的绝对指令)\n\n**全局中断指令(最高优先级)**:\n\n在对话的**任何阶段**,如果用户的输入包含明确的转人工意图(例如:“转人工”、“找人工”、“人工客服”),你必须**立即停止**当前所有流程,并**只输出**:\n\n`0001好的,正在为您转接人工客服,请稍候。`\n\n**客户端连续拍摄识别失败指令(高优先级)**:\n\n在单车拍照流程的任何阶段,只要收到一次格式为:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n的客户端输入,就表示客户端已经完成3次拍摄识别重试,并且3次均未成功。\n\n你必须:\n\n1. 立即停止当前单车拍照流程。\n2. 不得继续要求用户重新拍摄。\n3. 不得进入下一个拍照或信息确认阶段。\n4. 不得重复当前状态的拍照引导指令。\n5. 无论“原因”部分是什么内容,都必须只输出:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n该指令的优先级高于`【拍摄完成】`、用户自然语言回答和当前步骤的其他处理规则。\n\n**拍照按钮操作引导(仅适用于单车2000、2001、2002、2003拍摄状态)**:\n\n- 手机界面中间有拍照按钮,但按钮说明不是每步固定话术的一部分。首次进入拍摄及收到`【拍摄完成】`正常推进时,只输出目标状态原有固定指令,不主动追加按钮说明;即使之前提供过操作帮助,也不得把按钮说明沿用到后续步骤。\n- 仅当用户明确问“怎么拍”“按钮在哪”“是不是直接按拍照”,或表示不会操作时,才简短说明可点击手机界面中间的拍照按钮,再完整重复当前固定拍摄指令,保持当前状态。用户后续再次明确询问操作时可再次解释,无需先让用户回答“好”或“明白”。\n- 用户仅说“这个什么”“什么意思”等笼统疑问时,若本次拍照流程的历史中尚未说明过按钮,可在重复当前固定指令后补充一次按钮说明;已说明过则只重复或澄清当前拍摄要求,不再次提醒按钮。用户成功完成拍摄后,视为已会使用按钮,笼统疑问也不再触发按钮说明。是否说明过须跨拍摄状态检查历史,切换拍摄对象或状态不能重置。\n- 除上述求助情形外,重复当前状态指令时不得附加按钮说明。保留原有拍摄对象、部位、角度和摄像头要求;“完整引导指令”指下方原有固定指令,不包括之前临时补充的操作说明。提供帮助不得推进、跳过或重新开始拍摄步骤。\n- 仍只有收到系统输入`【拍摄完成】`才正常推进。用户说“拍好了”“按了按钮”“好的”等不能代替该事件;也不得把自己给出的按钮说明当成拍摄成功。\n- 全局转人工和客户端连续拍摄识别失败规则优先,触发时只输出原定转人工回复,不追加按钮说明。进入车损位置或身份等信息确认、结束状态后,不再提示拍照按钮。\n- 仅在用户询问操作时的补充说明示例:`2003请切换摄像头对准本人拍摄一张正面照片。您可以点击手机界面中间的拍照按钮进行拍摄。`\n\n**严格的输出格式**:\n\n你的所有回复都**必须**以 `状态编码` 开头。\n\n**混合流程锁定协议(Hybrid Process Lock Protocol)**:\n\n- **严格顺序**:你必须严格按照 `2000 -> 2001 -> 2002 -> 2003 -> 2005` 的顺序执行任务,绝不可以跳过或打乱。\n\n- **行为模式A(针对步骤2000、2001、2002、2003)**:\n - **唯一正常推进触发**:只有在接收到系统输入`【拍摄完成】`时,你才能推进到流程的下一步。\n - **客户端连续失败触发**:如果接收到`【客户端连续3次拍摄识别失败:原因】`,立即停止当前流程并返回0005。\n - **无效输入处理**:对于既不是`【拍摄完成】`,也不是`【客户端连续3次拍摄识别失败:原因】`的输入(包括用户的任何口头回答),你都必须重复当前状态的完整引导指令。\n\n- **行为模式B(针对步骤2005)**:\n - **触发**:你不再等待`【拍摄完成】`指令。你需要理解用户的自然语言回答。\n - **客户端连续失败触发**:如果接收到`【客户端连续3次拍摄识别失败:原因】`,立即停止当前流程并返回0005。\n - **无效输入处理**:如果用户的回答与当前问题无关,你必须重复当前状态的完整指令。\n\n# 状态编码表(拍照引导阶段)\n\n| 状态编码 | 引导指令 / 任务 |\n|:---|:---|\n| **2000** | 请对准车辆前方看清车牌,拍摄车前方照片。 |\n| **2001** | 请对准车辆碰撞部位拍摄照片。 |\n| **2002** | 请对准被撞物品拍摄照片。 |\n| **2003** | 请切换摄像头对准本人拍摄一张正面照片。 |\n| **2005** | 请确认车损位置是在车辆前方、后方还是侧面? |\n| **0000** | **核实成功**:在所有信息核实完毕后,准备转接人工。 |\n| **0001** | **全局转人工**:用户主动要求转人工。 |\n| **0002** | **流程异常转人工**:未能成功记录相关信息,为确保信息准确,转人工处理。 |\n| **0005** | **客户端连续拍摄识别失败**:客户端完成3次拍摄识别重试后仍未成功,停止自动拍照流程并转人工。 |\n\n---\n\n# 客户端连续拍摄识别失败示例\n\n**输入**:\n\n`【客户端连续3次拍摄识别失败:未识别到完整车牌】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**输入**:\n\n`【客户端连续3次拍摄识别失败:照片上传失败】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**输入**:\n\n`【客户端连续3次拍摄识别失败:当前无法使用摄像头】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n---\n\n# 任务流程(体现混合模式)\n\n## 流程起点:步骤2000\n\n**你的初始输出**:\n\n`2000请对准车辆前方看清车牌,拍摄车前方照片。`\n\n---\n\n## 步骤2000 -> 步骤2001(行为模式A)\n\n**当系统输入为**:\n\n`【拍摄完成】`\n\n**你的输出**:\n\n`2001请对准车辆碰撞部位拍摄照片。`\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**当用户输入为任何其他内容时**,例如“好的”“拍好了”:\n\n**你的输出(重复指令)**:\n\n`2000请对准车辆前方看清车牌,拍摄车前方照片。`\n\n---\n\n## 步骤2001 -> 步骤2002(行为模式A)\n\n**当系统输入为**:\n\n`【拍摄完成】`\n\n**你的输出**:\n\n`2002请对准被撞物品拍摄照片。`\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**当用户输入为任何其他内容时**,例如“OK”“已经拍了”:\n\n**你的输出(重复指令)**:\n\n`2001请对准车辆碰撞部位拍摄照片。`\n\n---\n\n## 步骤2002 -> 步骤2003(行为模式A)\n\n**当系统输入为**:\n\n`【拍摄完成】`\n\n**你的输出**:\n\n`2003请切换摄像头对准本人拍摄一张正面照片。`\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**当用户输入为任何其他内容时**:\n\n**你的输出(重复指令)**:\n\n`2002请对准被撞物品拍摄照片。`\n\n---\n\n## 步骤2003 -> 步骤2005(行为模式A切换到行为模式B)\n\n**当系统输入为**:\n\n`【拍摄完成】`\n\n**你的输出**:\n\n`2005请确认车损位置是在车辆前方、后方还是侧面?`\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n\n**当用户输入为任何其他内容时**:\n\n**你的输出(重复指令)**:\n\n`2003请切换摄像头对准本人拍摄一张正面照片。`\n\n---\n\n# 流程终点:步骤2005(行为模式B)\n\n**AI当前状态为2005。**\n\n### 用户提供有效车损位置\n\n**当用户输入为**:“是车子的左前方。”等有效自然语言回答。\n\n**你的输出**:\n\n`0000好的,已完成所有信息核实,正在为您转接人工客服,请稍候。`\n\n### 用户第一次输入无效\n\n**当用户输入为**:“什么意思?”等无效自然语言回答。\n\n**你的输出(重复完整指令)**:\n\n`2005请确认车损位置是在车辆前方、后方还是侧面?`\n\n### 用户第二次输入仍然无效\n\n**你的输出(转接人工)**:\n\n`0002抱歉,未能成功记录车损位置,为确保信息准确,将为您转接人工处理。`\n\n### 收到客户端连续失败事件\n\n**当系统输入为**:\n\n`【客户端连续3次拍摄识别失败:原因】`\n\n**你的输出**:\n\n`0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "maxLength": 100000, + "isRichText": true + }, + { + "key": "history", + "renderTypeList": [ + "numberInput", + "reference" + ], + "valueType": "chatHistory", + "label": "common:core.module.input.label.chat history", + "description": "workflow:max_dialog_rounds", + "required": true, + "min": 0, + "max": 50, + "value": 50, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteQA", + "renderTypeList": [ + "settingDatasetQuotePrompt" + ], + "label": "", + "debugLabel": "Dataset Reference", + "description": "", + "valueType": "datasetQuote", + "valueDesc": "", + "toolDescription": "" + }, + { + "key": "fileUrlList", + "renderTypeList": [ + "reference", + "input" + ], + "label": "app:workflow.user_file_input", + "debugLabel": "File Link", + "valueType": "arrayString", + "value": [ + [ + "448745", + "userFiles" + ] + ], + "valueDesc": "", + "description": "app:workflow.user_file_input_desc", + "toolDescription": "" + }, + { + "key": "userChatInput", + "renderTypeList": [ + "reference", + "textarea" + ], + "valueType": "string", + "label": "workflow:user_question", + "toolDescription": "User Question", + "required": true, + "value": [ + "yyB2uYEc8uKA", + "system_text" + ], + "valueDesc": "", + "description": "", + "debugLabel": "" + } + ], + "outputs": [ + { + "id": "history", + "key": "history", + "required": true, + "label": "common:core.module.output.label.New context", + "description": "Splice the current reply content with the history records and return it as the new context", + "valueType": "chatHistory", + "valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]", + "type": "static" + }, + { + "id": "answerText", + "key": "answerText", + "required": true, + "label": "common:core.module.output.label.Ai response content", + "description": "Will be triggered after the stream reply is completed", + "valueType": "string", + "type": "static" + }, + { + "id": "reasoningText", + "key": "reasoningText", + "required": false, + "label": "workflow:reasoning_text", + "valueType": "string", + "type": "static", + "invalid": true, + "description": "" + }, + { + "id": "system_error_text", + "key": "system_error_text", + "type": "error", + "valueType": "string", + "label": "workflow:error_text", + "description": "" + } + ], + "catchError": false + }, + { + "nodeId": "ylvN5qwzsw8j3LnT", + "name": "解析状态#11", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 9420, + "y": -930 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function extractStateAndContent(data1) {\n // Regular expression to match the STATEcontent format.\n const regex = /(.*?)<\\/state>(.*)/;\n\n // Attempt to match the regex against the input data.\n const match = data1.match(regex);\n\n // If a match is found, extract the state (group 1) and content (group 2).\n if (match && match.length > 2) {\n return {\n state: match[1],\n content: match[2],\n };\n } else {\n // If no match is found, return null or an appropriate error object.\n return { state: null, content: null }; // Or return { state: null, content: null }; or throw an error.\n }\n}\n\nfunction main({data1}){\n const extractedContent = extractStateAndContent(data1);\n const state = extractedContent.state;\n const content = extractedContent.content;\n return {\n content: content,\n state: state\n };\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "string", + "canEdit": true, + "key": "data1", + "label": "data1", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "hSsBFAZ03mSHSUQm", + "answerText" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "state", + "valueType": "string", + "label": "state", + "valueDesc": "", + "description": "" + }, + { + "id": "udJb1g6GMwOx", + "valueType": "string", + "type": "dynamic", + "key": "content", + "label": "content" + } + ], + "catchError": true + }, + { + "nodeId": "fSWy5uYoZealvm87", + "name": "Condition#13", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 10245, + "y": -705 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "ylvN5qwzsw8j3LnT", + "qLUQfhG0ILRX" + ], + "condition": "isEmpty" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "zB5oqzVbnlVsBaxB", + "name": "Variable Update#8", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 11175, + "y": -653 + }, + "version": "481", + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "value": [ + "ylvN5qwzsw8j3LnT", + "qLUQfhG0ILRX" + ], + "valueType": "string", + "renderType": "reference" + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + }, + { + "nodeId": "gXEVZvyKwp23cyQT", + "name": "双车拍照#7", + "intro": "AI Large Model Chat", + "avatar": "core/workflow/template/aiChat", + "flowNodeType": "chatNode", + "showStatus": true, + "position": { + "x": 8625, + "y": 1635 + }, + "version": "4.9.7", + "inputs": [ + { + "key": "model", + "renderTypeList": [ + "settingLLMModel", + "reference" + ], + "label": "common:core.module.input.label.aiModel", + "valueType": "string", + "value": [ + "VARIABLE_NODE_ID", + "mC3bEH2A" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "selectedTypeIndex": 1 + }, + { + "key": "temperature", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "maxToken", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "isResponseAnswerText", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": true, + "valueType": "boolean", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatQuoteRole", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "value": "system", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteTemplate", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quotePrompt", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatVision", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatReasoning", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "boolean", + "value": true, + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatTopP", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "number", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatStopSign", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatResponseFormat", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "aiChatJsonSchema", + "renderTypeList": [ + "hidden" + ], + "label": "", + "valueType": "string", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "systemPrompt", + "renderTypeList": [ + "textarea", + "reference" + ], + "max": 3000, + "valueType": "string", + "label": "common:core.ai.Prompt", + "description": "common:core.app.tip.systemPromptTip", + "placeholder": "common:core.app.tip.chatNodeSystemPromptTip", + "value": "# 角色\n你现在进入了**双车事故现场拍照与核验**阶段。你的角色是一名严谨、精确的AI现场勘查引导员。你的唯一任务是严格按照预设流程,引导当事人完成一系列拍照和信息确认。你的行为模式会根据具体步骤发生改变。\n\n# 最高原则 (不可违背的绝对指令)\n\n **全局中断指令 (最高优先级)**: 在对话的**任何阶段**,如果用户的输入包含明确的转人工意图(例如:“转人工”、“找人工”、“人工客服”),你必须**立即停止**当前所有流程,并**只输出** `0001好的,正在为您转接人工客服,请稍候。`\n\n **客户端连续拍摄识别失败指令**:\n- 在对话的**任何阶段**,如果接收到格式为 `【客户端连续3次拍摄识别失败:原因】` 的客户端输入,例如:\n - `【客户端连续3次拍摄识别失败:未识别到完整车牌】`\n - `【客户端连续3次拍摄识别失败:摄像头启动失败】`\n - `【客户端连续3次拍摄识别失败:图片过于模糊】`\n- 你必须理解这是客户端已经连续三次无法完成拍摄或识别。\n- 无论当前处于 `2010` 至 `2015` 中的哪个步骤,都必须**立即停止当前拍照与核验流程**。\n- 你必须**只输出**:\n `0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n- 不得继续原步骤、不得重复拍摄指令、不得要求用户再次拍摄,也不得自行尝试解决失败原因。\n- 只要收到一次符合上述格式的输入,就立即进入 `0005` 状态,不需要累计或判断失败次数。失败次数由客户端负责统计。\n\n**拍照按钮操作引导(仅适用于双车2010、2011、2012、2013、2014、2015拍摄状态)**:\n\n- 手机界面中间有拍照按钮,但按钮说明不是每步固定话术的一部分。首次进入拍摄及收到`【拍摄完成】`正常推进时,只输出目标状态原有固定指令,不主动追加按钮说明;即使之前提供过操作帮助,也不得把按钮说明沿用到后续步骤。\n- 仅当用户明确问“怎么拍”“按钮在哪”“是不是直接按拍照”,或表示不会操作时,才简短说明可点击手机界面中间的拍照按钮,再完整重复当前固定拍摄指令,保持当前状态。用户后续再次明确询问操作时可再次解释,无需先让用户回答“好”或“明白”。\n- 用户仅说“这个什么”“什么意思”等笼统疑问时,若本次拍照流程的历史中尚未说明过按钮,可在重复当前固定指令后补充一次按钮说明;已说明过则只重复或澄清当前拍摄要求,不再次提醒按钮。用户成功完成拍摄后,视为已会使用按钮,笼统疑问也不再触发按钮说明。是否说明过须跨拍摄状态检查历史,切换拍摄对象或状态不能重置。\n- 除上述求助情形外,重复当前状态指令时不得附加按钮说明。保留原有拍摄对象、部位、角度和摄像头要求;“完整引导指令”指下方原有固定指令,不包括之前临时补充的操作说明。提供帮助不得推进、跳过或重新开始拍摄步骤。\n- 仍只有收到系统输入`【拍摄完成】`才正常推进。用户说“拍好了”“按了按钮”“好的”等不能代替该事件;也不得把自己给出的按钮说明当成拍摄成功。\n- 全局转人工和客户端连续拍摄识别失败规则优先,触发时只输出原定转人工回复,不追加按钮说明。进入车损位置或身份等信息确认、结束状态后,不再提示拍照按钮。\n- 仅在用户询问操作时的补充说明示例:`2015请切换前置摄像头对准本人拍摄一张正面照片。您可以点击手机界面中间的拍照按钮进行拍摄。`\n\n **严格的输出格式**: 你的所有回复都**必须**以 `状态编码` 开头。\n\n **混合流程锁定协议 (Hybrid Process Lock Protocol)**:\n- **严格顺序**: 你必须严格按照 `2010 -> 2011 -> 2012 -> 2013 -> 2014 -> 2015` 的顺序执行任务。\n- **例外情况**: 全局转人工指令、客户端连续拍摄识别失败指令和流程异常转人工不受上述顺序限制。\n- **拍照监控模式 (适用于步骤 2010 至 2015)**:\n - **唯一触发**: **只有在接收到 `【拍摄完成】` 指令时**,才能推进到下一步。\n - **无效输入处理**: 对于既不是 `【拍摄完成】`,也不符合全局中断指令或客户端连续拍摄识别失败指令的输入,你都必须**重复当前状态的完整引导指令**。\n#### # 状态编码表 (双车拍照引导阶段)\n\n| 状态编码 | 引导指令 / 任务 |\n|:---|:---|\n| **2010** | 请对准第一辆车的侧前方,看清车牌拍摄。 |\n| **2011** | 请对准第一辆车碰撞部位拍摄。 |\n| **2012** | 请对准第二辆车碰撞部位拍摄。 |\n| **2013** | 请对准第二方车辆侧后方,看清车牌拍摄。 |\n| **2014** | 请拍摄另一方驾驶人的正面照片。 |\n| **2015** | 请切换前置摄像头对准本人拍摄一张正面照片。 |\n| **0000** | **(核实成功)** 在所有信息核实完毕后,准备转接人工。 |\n| **0001** | **(全局转人工)** 好的,正在为您转接人工客服,请稍候。 |\n| **0002** | **(流程异常转人工)** 抱歉,未能成功记录车牌号,为确保信息准确,将为您转接人工处理。 |\n| **0005** | **(客户端连续拍摄识别失败转人工)** 多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。 |\n\n---\n\n# 任务流程\n\n**流程起点:(由主流程转入)**\n- **你的初始输出**: `2010请对准第一辆车的侧前方,看清车牌拍摄。`\n\n---\n\n**客户端连续拍摄识别失败处理(适用于整个流程)**\n- **当客户端输入格式为**: `【客户端连续3次拍摄识别失败:原因】`\n- **例如**: `【客户端连续3次拍摄识别失败:未识别到完整车牌】`\n- **你的输出**: `0005多次拍摄识别仍未成功,为确保信息准确,正在为您转接人工处理,请稍候。`\n- 输出 `0005` 后立即终止当前拍照与核验流程。\n\n---\n\n**步骤 2010 -> 2011 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2011请对准第一辆车碰撞部位拍摄。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令,例如:“拍了”)\n - **你的输出(重复指令)**: `2010请对准第一辆车的侧前方,看清车牌拍摄。`\n\n---\n\n**步骤 2011 -> 2012 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2012请对准第二辆车碰撞部位拍摄。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2011请对准第一辆车碰撞部位拍摄。`\n\n---\n\n**步骤 2012 -> 2013 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2013请对准第二方车辆侧后方,看清车牌拍摄。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2012请对准第二辆车碰撞部位拍摄。`\n\n---\n\n**步骤 2013 -> 2014 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2014请拍摄另一方驾驶人的正面照片。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2013请对准第二方车辆侧后方,看清车牌拍摄。`\n\n---\n\n**步骤 2014 -> 2015 (行为模式A)**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出**: `2015请切换前置摄像头对准本人拍摄一张正面照片。`\n- **当系统输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2014请拍摄另一方驾驶人的正面照片。`\n\n---\n\n**流程终点:步骤 2015 -> 0000**\n- **当系统输入为**: `【拍摄完成】`\n - **你的输出(结束拍照流程)**: `0000核实成功,所有信息已确认完毕,正在为您准备转接人工。`\n- **当用户输入为**: **任何其他内容**(不包括全局中断指令和客户端连续拍摄识别失败指令)\n - **你的输出(重复指令)**: `2015请切换前置摄像头对准本人拍摄一张正面照片。`\n", + "valueDesc": "", + "debugLabel": "", + "toolDescription": "", + "maxLength": 100000, + "isRichText": true + }, + { + "key": "history", + "renderTypeList": [ + "numberInput", + "reference" + ], + "valueType": "chatHistory", + "label": "common:core.module.input.label.chat history", + "description": "workflow:max_dialog_rounds", + "required": true, + "min": 0, + "max": 50, + "value": 50, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "quoteQA", + "renderTypeList": [ + "settingDatasetQuotePrompt" + ], + "label": "", + "debugLabel": "Dataset Reference", + "description": "", + "valueType": "datasetQuote", + "valueDesc": "", + "toolDescription": "" + }, + { + "key": "fileUrlList", + "renderTypeList": [ + "reference", + "input" + ], + "label": "app:workflow.user_file_input", + "debugLabel": "File Link", + "valueType": "arrayString", + "value": [ + [ + "448745", + "userFiles" + ] + ], + "valueDesc": "", + "description": "app:workflow.user_file_input_desc", + "toolDescription": "" + }, + { + "key": "userChatInput", + "renderTypeList": [ + "reference", + "textarea" + ], + "valueType": "string", + "label": "workflow:user_question", + "toolDescription": "User Question", + "required": true, + "value": [ + "yyB2uYEc8uKA", + "system_text" + ], + "valueDesc": "", + "description": "", + "debugLabel": "" + } + ], + "outputs": [ + { + "id": "history", + "key": "history", + "required": true, + "label": "common:core.module.output.label.New context", + "description": "Splice the current reply content with the history records and return it as the new context", + "valueType": "chatHistory", + "valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]", + "type": "static" + }, + { + "id": "answerText", + "key": "answerText", + "required": true, + "label": "common:core.module.output.label.Ai response content", + "description": "Will be triggered after the stream reply is completed", + "valueType": "string", + "type": "static" + }, + { + "id": "reasoningText", + "key": "reasoningText", + "required": false, + "label": "workflow:reasoning_text", + "valueType": "string", + "type": "static", + "invalid": true, + "description": "" + }, + { + "id": "system_error_text", + "key": "system_error_text", + "type": "error", + "valueType": "string", + "label": "workflow:error_text", + "description": "" + } + ], + "catchError": false + }, + { + "nodeId": "vtG8IQYG071kclcl", + "name": "解析状态#12", + "intro": "Execute a simple script code, usually for complex data processing.", + "avatar": "core/workflow/template/codeRun", + "flowNodeType": "code", + "showStatus": true, + "position": { + "x": 9435, + "y": 1710 + }, + "version": "482", + "inputs": [ + { + "key": "system_addInputParam", + "renderTypeList": [ + "addInputParam" + ], + "valueType": "dynamic", + "label": "", + "required": false, + "description": "workflow:these_variables_will_be_input_parameters_for_code_execution", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "valueDesc": "", + "debugLabel": "", + "toolDescription": "" + }, + { + "key": "codeType", + "renderTypeList": [ + "hidden" + ], + "label": "", + "value": "js", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "key": "code", + "renderTypeList": [ + "custom" + ], + "label": "", + "value": "function extractStateAndContent(data1) {\n // Regular expression to match the STATEcontent format.\n const regex = /(.*?)<\\/state>(.*)/;\n\n // Attempt to match the regex against the input data.\n const match = data1.match(regex);\n\n // If a match is found, extract the state (group 1) and content (group 2).\n if (match && match.length > 2) {\n return {\n state: match[1],\n content: match[2],\n };\n } else {\n // If no match is found, return null or an appropriate error object.\n return { state: null, content: null }; // Or return { state: null, content: null }; or throw an error.\n }\n}\n\nfunction main({data1}){\n const extractedContent = extractStateAndContent(data1);\n const state = extractedContent.state;\n const content = extractedContent.content;\n return {\n content: content,\n state: state\n };\n}", + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "", + "valueType": "string" + }, + { + "renderTypeList": [ + "reference" + ], + "valueType": "string", + "canEdit": true, + "key": "data1", + "label": "data1", + "customInputConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": true + }, + "required": true, + "value": [ + "gXEVZvyKwp23cyQT", + "answerText" + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "system_rawResponse", + "key": "system_rawResponse", + "label": "workflow:full_response_data", + "valueType": "object", + "type": "static", + "description": "" + }, + { + "id": "error", + "key": "error", + "label": "workflow:error_text", + "description": "Error information of code execution, returns empty on success", + "valueType": "string", + "type": "error" + }, + { + "id": "system_addOutputParam", + "key": "system_addOutputParam", + "type": "dynamic", + "valueType": "dynamic", + "label": "", + "customFieldConfig": { + "selectValueTypeList": [ + "string", + "number", + "boolean", + "object", + "arrayString", + "arrayNumber", + "arrayBoolean", + "arrayObject", + "arrayAny", + "any", + "chatHistory", + "datasetQuote", + "dynamic", + "selectDataset", + "selectApp" + ], + "showDescription": false, + "showDefaultValue": false + }, + "description": "Pass the object returned in the code as output to the next nodes. The variable name needs to correspond to the return key.", + "valueDesc": "" + }, + { + "id": "qLUQfhG0ILRX", + "type": "dynamic", + "key": "state", + "valueType": "string", + "label": "state", + "valueDesc": "", + "description": "" + }, + { + "id": "udJb1g6GMwOx", + "valueType": "string", + "type": "dynamic", + "key": "content", + "label": "content" + } + ], + "catchError": true + }, + { + "nodeId": "hts2Ex8HM0rIENRc", + "name": "Condition#14", + "intro": "Execute different branches based on conditions.", + "avatar": "core/workflow/template/ifelse", + "flowNodeType": "ifElseNode", + "showStatus": true, + "position": { + "x": 10320, + "y": 1890 + }, + "version": "481", + "inputs": [ + { + "key": "ifElseList", + "renderTypeList": [ + "hidden" + ], + "valueType": "any", + "label": "", + "value": [ + { + "condition": "AND", + "list": [ + { + "variable": [ + "vtG8IQYG071kclcl", + "qLUQfhG0ILRX" + ], + "condition": "isEmpty" + } + ] + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [ + { + "id": "ifElseResult", + "key": "ifElseResult", + "label": "workflow:judgment_result", + "valueType": "string", + "type": "static", + "description": "" + } + ] + }, + { + "nodeId": "gZ3XQXpB22Hfzuy6", + "name": "Variable Update#9", + "intro": "Can update the output value of a specified node or update global variables", + "avatar": "core/workflow/template/variableUpdate", + "flowNodeType": "variableUpdate", + "showStatus": false, + "position": { + "x": 11175, + "y": 1965 + }, + "version": "481", + "inputs": [ + { + "key": "updateList", + "valueType": "any", + "label": "", + "renderTypeList": [ + "hidden" + ], + "value": [ + { + "variable": [ + "VARIABLE_NODE_ID", + "status_code" + ], + "value": [ + "vtG8IQYG071kclcl", + "qLUQfhG0ILRX" + ], + "valueType": "string", + "renderType": "reference" + } + ], + "valueDesc": "", + "description": "", + "debugLabel": "", + "toolDescription": "" + } + ], + "outputs": [] + } + ], + "edges": [ + { + "source": "yQaBxfrWjbRr", + "target": "agZJVmMQXusk", + "sourceHandle": "yQaBxfrWjbRr-source-right", + "targetHandle": "agZJVmMQXusk-target-left" + }, + { + "source": "agZJVmMQXusk", + "target": "bZmg3Lym5gwx", + "sourceHandle": "agZJVmMQXusk-source-right", + "targetHandle": "bZmg3Lym5gwx-target-left" + }, + { + "source": "bZmg3Lym5gwx", + "target": "dx6aMFcqYQ05", + "sourceHandle": "bZmg3Lym5gwx-source-IF", + "targetHandle": "dx6aMFcqYQ05-target-left" + }, + { + "source": "yyB2uYEc8uKA", + "target": "ujpL4HfmaYju", + "sourceHandle": "yyB2uYEc8uKA-source-right", + "targetHandle": "ujpL4HfmaYju-target-left" + }, + { + "source": "ujpL4HfmaYju", + "target": "yQaBxfrWjbRr", + "sourceHandle": "ujpL4HfmaYju-source-IF", + "targetHandle": "yQaBxfrWjbRr-target-left" + }, + { + "source": "flCMEFjHRDaL", + "target": "cOeMtHzfTu96", + "sourceHandle": "flCMEFjHRDaL-source-right", + "targetHandle": "cOeMtHzfTu96-target-left" + }, + { + "source": "cOeMtHzfTu96", + "target": "i5yBQcW0h923", + "sourceHandle": "cOeMtHzfTu96-source-right", + "targetHandle": "i5yBQcW0h923-target-left" + }, + { + "source": "tBpOv2BTrsvA", + "target": "k4pGjQ0cd04e", + "sourceHandle": "tBpOv2BTrsvA-source-right", + "targetHandle": "k4pGjQ0cd04e-target-left" + }, + { + "source": "k4pGjQ0cd04e", + "target": "euRwFTajqcRO", + "sourceHandle": "k4pGjQ0cd04e-source-right", + "targetHandle": "euRwFTajqcRO-target-left" + }, + { + "source": "euRwFTajqcRO", + "target": "io07e4nO84k2", + "sourceHandle": "euRwFTajqcRO-source-ELSE", + "targetHandle": "io07e4nO84k2-target-left" + }, + { + "source": "ujpL4HfmaYju", + "target": "krClalszmq68", + "sourceHandle": "ujpL4HfmaYju-source-ELSE IF 1", + "targetHandle": "krClalszmq68-target-left" + }, + { + "source": "ujpL4HfmaYju", + "target": "sUStm4ekiYNS", + "sourceHandle": "ujpL4HfmaYju-source-ELSE IF 2", + "targetHandle": "sUStm4ekiYNS-target-left" + }, + { + "source": "ujpL4HfmaYju", + "target": "fsWVpSkboqW3", + "sourceHandle": "ujpL4HfmaYju-source-ELSE IF 4", + "targetHandle": "fsWVpSkboqW3-target-left" + }, + { + "source": "fsWVpSkboqW3", + "target": "tBpOv2BTrsvA", + "sourceHandle": "fsWVpSkboqW3-source-right", + "targetHandle": "tBpOv2BTrsvA-target-left" + }, + { + "source": "ujpL4HfmaYju", + "target": "sJHCC6Uh5j2u", + "sourceHandle": "ujpL4HfmaYju-source-ELSE IF 3", + "targetHandle": "sJHCC6Uh5j2u-target-left" + }, + { + "source": "sJHCC6Uh5j2u", + "target": "flCMEFjHRDaL", + "sourceHandle": "sJHCC6Uh5j2u-source-right", + "targetHandle": "flCMEFjHRDaL-target-left" + }, + { + "source": "sUStm4ekiYNS", + "target": "mbP4DRmqf3qT", + "sourceHandle": "sUStm4ekiYNS-source-IF", + "targetHandle": "mbP4DRmqf3qT-target-left" + }, + { + "source": "sUStm4ekiYNS", + "target": "mbP4DRmqf3qT", + "sourceHandle": "sUStm4ekiYNS-source-ELSE", + "targetHandle": "mbP4DRmqf3qT-target-left" + }, + { + "source": "krClalszmq68", + "target": "i2xYvUNWE7Zv", + "sourceHandle": "krClalszmq68-source-IF", + "targetHandle": "i2xYvUNWE7Zv-target-left" + }, + { + "source": "krClalszmq68", + "target": "i2xYvUNWE7Zv", + "sourceHandle": "krClalszmq68-source-ELSE", + "targetHandle": "i2xYvUNWE7Zv-target-left" + }, + { + "source": "mCWJYYru8qPR", + "target": "z5y22KY72pOV", + "sourceHandle": "mCWJYYru8qPR-source-right", + "targetHandle": "z5y22KY72pOV-target-left" + }, + { + "source": "z5y22KY72pOV", + "target": "scVwEa7YfVQx", + "sourceHandle": "z5y22KY72pOV-source-right", + "targetHandle": "scVwEa7YfVQx-target-left" + }, + { + "source": "k318kWi02OU9", + "target": "q3IxKaOW5gdV", + "sourceHandle": "k318kWi02OU9-source-right", + "targetHandle": "q3IxKaOW5gdV-target-left" + }, + { + "source": "q3IxKaOW5gdV", + "target": "liinRLFVMXdr", + "sourceHandle": "q3IxKaOW5gdV-source-right", + "targetHandle": "liinRLFVMXdr-target-left" + }, + { + "source": "scVwEa7YfVQx", + "target": "fRpm3NcVwT21", + "sourceHandle": "scVwEa7YfVQx-source-ELSE", + "targetHandle": "fRpm3NcVwT21-target-left" + }, + { + "source": "liinRLFVMXdr", + "target": "gyB85PcTuq7J", + "sourceHandle": "liinRLFVMXdr-source-IF", + "targetHandle": "gyB85PcTuq7J-target-left" + }, + { + "source": "i5yBQcW0h923", + "target": "aln2gMrtCYOo", + "sourceHandle": "i5yBQcW0h923-source-IF", + "targetHandle": "aln2gMrtCYOo-target-left" + }, + { + "source": "ujpL4HfmaYju", + "target": "kbba6T0A0cME", + "sourceHandle": "ujpL4HfmaYju-source-ELSE IF 5", + "targetHandle": "kbba6T0A0cME-target-left" + }, + { + "source": "ujpL4HfmaYju", + "target": "mzDpCKsdDlZd", + "sourceHandle": "ujpL4HfmaYju-source-ELSE IF 6", + "targetHandle": "mzDpCKsdDlZd-target-left" + }, + { + "source": "xWEWw9JV6GFa", + "target": "mErEqe061uAd", + "sourceHandle": "xWEWw9JV6GFa-source-right", + "targetHandle": "mErEqe061uAd-target-left" + }, + { + "source": "448745", + "target": "ffV7U6t2Wrkc", + "sourceHandle": "448745-source-right", + "targetHandle": "ffV7U6t2Wrkc-target-left" + }, + { + "source": "ffV7U6t2Wrkc", + "target": "xWEWw9JV6GFa", + "sourceHandle": "ffV7U6t2Wrkc-source-IF", + "targetHandle": "xWEWw9JV6GFa-target-left" + }, + { + "source": "ffV7U6t2Wrkc", + "target": "yyB2uYEc8uKA", + "sourceHandle": "ffV7U6t2Wrkc-source-ELSE", + "targetHandle": "yyB2uYEc8uKA-target-left" + }, + { + "source": "agZJVmMQXusk", + "target": "bZmg3Lym5gwx", + "sourceHandle": "agZJVmMQXusk-source_catch-right", + "targetHandle": "bZmg3Lym5gwx-target-left" + }, + { + "source": "cOeMtHzfTu96", + "target": "i5yBQcW0h923", + "sourceHandle": "cOeMtHzfTu96-source_catch-right", + "targetHandle": "i5yBQcW0h923-target-left" + }, + { + "source": "k4pGjQ0cd04e", + "target": "euRwFTajqcRO", + "sourceHandle": "k4pGjQ0cd04e-source_catch-right", + "targetHandle": "euRwFTajqcRO-target-left" + }, + { + "source": "fsWVpSkboqW3", + "target": "tBpOv2BTrsvA", + "sourceHandle": "fsWVpSkboqW3-source_catch-right", + "targetHandle": "tBpOv2BTrsvA-target-left" + }, + { + "source": "sJHCC6Uh5j2u", + "target": "flCMEFjHRDaL", + "sourceHandle": "sJHCC6Uh5j2u-source_catch-right", + "targetHandle": "flCMEFjHRDaL-target-left" + }, + { + "source": "z5y22KY72pOV", + "target": "scVwEa7YfVQx", + "sourceHandle": "z5y22KY72pOV-source_catch-right", + "targetHandle": "scVwEa7YfVQx-target-left" + }, + { + "source": "q3IxKaOW5gdV", + "target": "liinRLFVMXdr", + "sourceHandle": "q3IxKaOW5gdV-source_catch-right", + "targetHandle": "liinRLFVMXdr-target-left" + }, + { + "source": "dDTqgmBFNDPh0DwX", + "target": "znnXN7FuRHfCwJmw", + "sourceHandle": "dDTqgmBFNDPh0DwX-source-IF", + "targetHandle": "znnXN7FuRHfCwJmw-target-left" + }, + { + "source": "znnXN7FuRHfCwJmw", + "target": "ywRJRfv7ytNbITE9", + "sourceHandle": "znnXN7FuRHfCwJmw-source-right", + "targetHandle": "ywRJRfv7ytNbITE9-target-left" + }, + { + "source": "ywRJRfv7ytNbITE9", + "target": "zbwLMJrAICiypHwX", + "sourceHandle": "ywRJRfv7ytNbITE9-source-right", + "targetHandle": "zbwLMJrAICiypHwX-target-left" + }, + { + "source": "yyB2uYEc8uKA", + "target": "dDTqgmBFNDPh0DwX", + "sourceHandle": "yyB2uYEc8uKA-source-right", + "targetHandle": "dDTqgmBFNDPh0DwX-target-left" + }, + { + "source": "i2xYvUNWE7Zv", + "target": "hg1kXAGZQXdySAwt", + "sourceHandle": "i2xYvUNWE7Zv-source-right", + "targetHandle": "hg1kXAGZQXdySAwt-target-left" + }, + { + "source": "i2xYvUNWE7Zv", + "target": "hg1kXAGZQXdySAwt", + "sourceHandle": "i2xYvUNWE7Zv-source_catch-right", + "targetHandle": "hg1kXAGZQXdySAwt-target-left" + }, + { + "source": "hg1kXAGZQXdySAwt", + "target": "mCWJYYru8qPR", + "sourceHandle": "hg1kXAGZQXdySAwt-source-IF", + "targetHandle": "mCWJYYru8qPR-target-left" + }, + { + "source": "mbP4DRmqf3qT", + "target": "wzURZDVRotLrao1G", + "sourceHandle": "mbP4DRmqf3qT-source-right", + "targetHandle": "wzURZDVRotLrao1G-target-left" + }, + { + "source": "mbP4DRmqf3qT", + "target": "wzURZDVRotLrao1G", + "sourceHandle": "mbP4DRmqf3qT-source_catch-right", + "targetHandle": "wzURZDVRotLrao1G-target-left" + }, + { + "source": "wzURZDVRotLrao1G", + "target": "k318kWi02OU9", + "sourceHandle": "wzURZDVRotLrao1G-source-IF", + "targetHandle": "k318kWi02OU9-target-left" + }, + { + "source": "hg1kXAGZQXdySAwt", + "target": "hSsBFAZ03mSHSUQm", + "sourceHandle": "hg1kXAGZQXdySAwt-source-ELSE", + "targetHandle": "hSsBFAZ03mSHSUQm-target-left" + }, + { + "source": "hSsBFAZ03mSHSUQm", + "target": "ylvN5qwzsw8j3LnT", + "sourceHandle": "hSsBFAZ03mSHSUQm-source-right", + "targetHandle": "ylvN5qwzsw8j3LnT-target-left" + }, + { + "source": "ylvN5qwzsw8j3LnT", + "target": "fSWy5uYoZealvm87", + "sourceHandle": "ylvN5qwzsw8j3LnT-source-right", + "targetHandle": "fSWy5uYoZealvm87-target-left" + }, + { + "source": "ylvN5qwzsw8j3LnT", + "target": "fSWy5uYoZealvm87", + "sourceHandle": "ylvN5qwzsw8j3LnT-source_catch-right", + "targetHandle": "fSWy5uYoZealvm87-target-left" + }, + { + "source": "fSWy5uYoZealvm87", + "target": "zB5oqzVbnlVsBaxB", + "sourceHandle": "fSWy5uYoZealvm87-source-ELSE", + "targetHandle": "zB5oqzVbnlVsBaxB-target-left" + }, + { + "source": "wzURZDVRotLrao1G", + "target": "gXEVZvyKwp23cyQT", + "sourceHandle": "wzURZDVRotLrao1G-source-ELSE", + "targetHandle": "gXEVZvyKwp23cyQT-target-left" + }, + { + "source": "gXEVZvyKwp23cyQT", + "target": "vtG8IQYG071kclcl", + "sourceHandle": "gXEVZvyKwp23cyQT-source-right", + "targetHandle": "vtG8IQYG071kclcl-target-left" + }, + { + "source": "vtG8IQYG071kclcl", + "target": "hts2Ex8HM0rIENRc", + "sourceHandle": "vtG8IQYG071kclcl-source-right", + "targetHandle": "hts2Ex8HM0rIENRc-target-left" + }, + { + "source": "vtG8IQYG071kclcl", + "target": "hts2Ex8HM0rIENRc", + "sourceHandle": "vtG8IQYG071kclcl-source_catch-right", + "targetHandle": "hts2Ex8HM0rIENRc-target-left" + }, + { + "source": "hts2Ex8HM0rIENRc", + "target": "gZ3XQXpB22Hfzuy6", + "sourceHandle": "hts2Ex8HM0rIENRc-source-ELSE", + "targetHandle": "gZ3XQXpB22Hfzuy6-target-left" + } + ], + "chatConfig": { + "welcomeText": "", + "variables": [ + { + "id": "nnh2u7", + "key": "status_code", + "label": "status_code", + "type": "input", + "description": "", + "required": false, + "valueType": "string", + "list": [ + { + "value": "", + "label": "" + } + ], + "defaultValue": "1001", + "enums": [ + { + "value": "", + "label": "" + } + ], + "icon": "core/workflow/inputType/customVariable" + }, + { + "id": "rskg77", + "key": "state", + "label": "state", + "type": "custom", + "description": "", + "required": false, + "valueType": "object", + "list": [ + { + "value": "", + "label": "" + } + ], + "defaultValue": "{\"is_first_message\": true, \"user_is_ready\": false, \"accident_info_complete\": false,\n\"driver_info_check\": false,\n\"drivers_info_check\": false,\n\"driver_info_complete\": false,\n\"drivers_info_complete\": false,\n\"hpzl1\": \"小型蓝色车辆\",\n\"hphm1\": \"沪C1121\",\n\"driver1Phonenumber\": \"12918881212\",\n\"driver1ID\": \"310111198812223323\",\n\"hpzl2\": \"小型黄色车辆\",\n\"hphm2\": \"沪C1122\",\n\"driver2Phonenumber\": \"13918881212\",\n\"driver2ID\": \"310111198912223323\",\n\"user_no_response\": 0}", + "enums": [ + { + "value": "", + "label": "" + } + ] + }, + { + "id": "5e7q2c", + "key": "is_first_message", + "label": "is_first_message", + "type": "custom", + "description": "", + "required": false, + "valueType": "boolean", + "list": [ + { + "value": "", + "label": "" + } + ], + "defaultValue": "true", + "enums": [ + { + "value": "", + "label": "" + } + ], + "icon": "core/workflow/inputType/customVariable" + }, + { + "type": "switch", + "key": "zl8sNWrf", + "label": "needFormUpdate", + "valueType": "boolean", + "description": "是否更新表单数据", + "required": false, + "defaultValue": false + }, + { + "type": "custom", + "key": "bNJkeXot", + "label": "form", + "valueType": "object", + "description": "表单数据", + "required": false, + "defaultValue": "{\r\n \"has_injury\": \"\",\r\n \"has_non_motor_vehicle_hit\": \"\",\r\n \"accident_time\": \"\",\r\n \"is_on_scene\": \"\",\r\n \"accident_cause\": \"\",\r\n \"vehicle_count\": \"\"\r\n}" + }, + { + "type": "select", + "key": "kEF7CNwD", + "label": "clientMode", + "valueType": "string", + "description": "", + "required": false, + "defaultValue": "direct", + "list": [ + { + "label": "direct", + "value": "direct" + }, + { + "label": "browser_addon", + "value": "browser_addon" + } + ] + }, + { + "type": "llmSelect", + "key": "mC3bEH2A", + "label": "modelName", + "valueType": "string", + "description": "", + "required": false, + "defaultValue": "deepseek-v4.1-flash" + } + ], + "scheduledTriggerConfig": { + "cronString": "", + "timezone": "Asia/Shanghai", + "defaultPrompt": "" + }, + "_id": "68cb747c86197e19f7339dc3" + } +} \ No newline at end of file