feat: уточнение адреса — место происшествия не равно адресу заявителя (lct-35)
Главный предмет проверки в билетах заказчика — адрес: названный заявителем часто неверен, а настоящий добывается переспросом («ул. Станционная, 28» оказывается Королёвом). Памятка АРМ-112 называет это обычным делом. До этой правки оценка работала наоборот: ground_truth.address сравнивался с единственным значением факта, поэтому курсант, правильно переспросивший и записавший настоящий адрес, получал расхождение с эталоном, а записавший ориентир — зачёт. - Схема: у факта появились refined и refine_on, задаются только вместе. - Слот-автомат различает уточнение и повтор: повтор раздражает звонящего, уточнение — нет, оператор спросил о другом и получил другое. - Звонящий поправляется отдельной репликой, а не повторяет прежнее значение; офлайн-таблица получила секцию refine. - Оценка: метрика address_refined (E1, опрос). Сделана отдельной, а не правкой метрики address: «записал не тот адрес» и «не спросил» — разные навыки и разные компетенции в радаре. - Общие пункты чек-листа подключаются по требованию сценария: формулировки в checklists/common.yaml, сценарий объявляет пункт с тем же id без текста. Добавлять «уточните адрес» во все сценарии нельзя — неотработанный пункт штрафует за вопрос, которого сценарий не требовал. - scenarios/fire-private-house-l3.yaml — первый сценарий из библиотеки заказчика (билет 3, вызов 1). Риск механики проверен на живой модели: «Назовите адрес» и «Это точно Москва?» эмбеддинги не путают, тест это фиксирует. 125 тестов зелёных (12 новых), make typecheck чистый.
This commit is contained in:
parent
7212251112
commit
842a333d16
11 changed files with 381 additions and 8 deletions
|
|
@ -79,6 +79,23 @@ def _common_not_questions(root: Path) -> list[str]:
|
|||
return list(_read_yaml(common).get("not_questions", []))
|
||||
|
||||
|
||||
def _common_checklist(root: Path, wanted: set[str]) -> list[dict]:
|
||||
"""Общие пункты опроса — по требованию сценария, а не всем подряд.
|
||||
|
||||
В `common.yaml` лежат формулировки пунктов, которые не принадлежат ни одному
|
||||
классификатору: проверка адреса, контакты заявителя. Сценарий подключает их,
|
||||
объявив пункт с тем же `id` и без текста, — текст и примеры подставятся сюда.
|
||||
Насильно добавлять их во все сценарии нельзя: непрочитанный пункт чек-листа
|
||||
штрафует курсанта отметкой E1 за вопрос, которого сценарий не требовал.
|
||||
"""
|
||||
common = root / COMMON
|
||||
if not common.exists():
|
||||
return []
|
||||
return [
|
||||
item for item in _read_yaml(common).get("checklist", []) if item.get("id") in wanted
|
||||
]
|
||||
|
||||
|
||||
def load_file(path: Path, root: Path) -> Scenario:
|
||||
raw = _read_yaml(path)
|
||||
raw["not_questions"] = _common_not_questions(root) + list(raw.get("not_questions", []))
|
||||
|
|
@ -91,15 +108,17 @@ def load_file(path: Path, root: Path) -> Scenario:
|
|||
"и руками не пишется — иначе факты и эталон разъедутся"
|
||||
)
|
||||
|
||||
own = raw.get("checklist", [])
|
||||
base = _common_checklist(root, {item.get("id") for item in own})
|
||||
extends = raw.get("extends")
|
||||
if extends:
|
||||
base_path = root / extends
|
||||
if not base_path.exists():
|
||||
raise ScenarioError(f"{path.name}: чек-лист {extends} не найден")
|
||||
base = _read_yaml(base_path).get("checklist", [])
|
||||
base = base + _read_yaml(base_path).get("checklist", [])
|
||||
if base:
|
||||
raw["checklist"] = [
|
||||
item.model_dump(exclude_none=True)
|
||||
for item in _merge_checklist(base, raw.get("checklist", []))
|
||||
item.model_dump(exclude_none=True) for item in _merge_checklist(base, own)
|
||||
]
|
||||
|
||||
try:
|
||||
|
|
@ -118,13 +137,19 @@ def load_file(path: Path, root: Path) -> Scenario:
|
|||
if not item.question:
|
||||
raise ScenarioError(f"{path.name}: у пункта {item.id} нет текста вопроса")
|
||||
|
||||
item_ids = {item.id for item in scenario.checklist}
|
||||
for fact in scenario.facts:
|
||||
question_id = fact.reveal_on.question if fact.reveal_on else None
|
||||
if question_id and question_id not in {item.id for item in scenario.checklist}:
|
||||
if question_id and question_id not in item_ids:
|
||||
raise ScenarioError(
|
||||
f"{path.name}: факт {fact.id} раскрывается вопросом {question_id}, "
|
||||
"которого нет в чек-листе"
|
||||
)
|
||||
if fact.refine_on and fact.refine_on not in item_ids:
|
||||
raise ScenarioError(
|
||||
f"{path.name}: факт {fact.id} уточняется вопросом {fact.refine_on}, "
|
||||
"которого нет в чек-листе"
|
||||
)
|
||||
|
||||
return _derive_ground_truth(scenario)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue