АДРЕСНЫЙ РЕЖИМ - стабилизация LLM fallback якорей и периода
This commit is contained in:
parent
2d95ce6332
commit
7dd6607ded
|
|
@ -0,0 +1,25 @@
|
|||
"idx","question","reply_type","trace_id","detected_intent","query_shape","mcp_call_status","rows_matched","limited_reason_category","llm_decomposition_applied","llm_decomposition_reason","fallback_rule_hit","tool_gate_decision","runtime_readiness","period_from","period_to","as_of_date","anchor_type","anchor_value_raw","anchor_value_resolved","assistant_reply_head"
|
||||
"1","СЃРІРє РґРѕРєРё Р·Р° 20РіРѕРґ покеж","factual","address-sugd3wlUri","list_documents_by_counterparty","DOCUMENT_LIST","matched_non_empty","3","","True","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-01-01","2020-12-31","","counterparty","свк","Группа СВК","Собран список документов по контрагенту (live address lane)."
|
||||
"2","СЃРІРє 20 РіРѕРґ - покажи РґРѕРєРё плс","factual","address--4sSq56ihd","list_documents_by_counterparty","DOCUMENT_LIST","matched_non_empty","3","","True","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-01-01","2020-12-31","","counterparty","свк","Группа СВК","Собран список документов по контрагенту (live address lane)."
|
||||
"3","что РїРѕ СЃРІРє Р·Р° 2020 РіРѕРґ выведи РІСЃРµ РґРѕРєРё плиз что есть","partial_coverage","address-MUkQr3-8cm","list_documents_by_counterparty","DOCUMENT_LIST","materialized_but_not_anchor_matched","0","missing_anchor","True","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-01-01","2020-12-31","","counterparty","что","что","Для точного адресного поиска не хватает обязательного якоря."
|
||||
"4","какие документы РїРѕ контрагенту СЃРІРє Р·Р° РІСЃРµ время","factual","address-vfIW3ugGJ3","list_documents_by_counterparty","DOCUMENT_LIST","matched_non_empty","26","","True","fallback_rule_applied_after_llm_error","documents_counterparty_all_time_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","","","","counterparty","свк","Группа СВК","Собран список документов по контрагенту (live address lane)."
|
||||
"5","РґРѕРєРё РїРѕ СЃРІРє СЃ 01.07.2020 РїРѕ 31.07.2020","factual","address-OheS6P-gDn","list_documents_by_counterparty","DOCUMENT_LIST","matched_non_empty","2","","True","fallback_rule_applied_after_llm_error","documents_counterparty_month_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-07-01","2020-07-31","","counterparty","свк","Группа СВК","Собран список документов по контрагенту (live address lane)."
|
||||
"6","СЃРІРє июль 2020 какие РґРѕРєРё есть","factual","address-DCuYSZOqzh","list_documents_by_counterparty","DOCUMENT_LIST","matched_non_empty","2","","True","fallback_rule_applied_after_llm_error","documents_counterparty_month_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-07-01","2020-07-31","","counterparty","свк","Группа СВК","Собран список документов по контрагенту (live address lane)."
|
||||
"7","покажи сальдо РїРѕ 60.01 РЅР° 31.07.20","factual","address-aW8e72ada6","account_balance_snapshot","AGGREGATE_LOOKUP","matched_non_empty","200","","True","fallback_rule_applied_after_llm_error","balance_account_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","","","2026-04-01","account","60.01","60.01","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"8","остаток 60 на 2020.05","factual","address-o-EiCUq_sW","account_balance_snapshot","AGGREGATE_LOOKUP","matched_non_empty","6","","True","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-05-01","2020-05-31","2020-05-31","account","60","60","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"9","какой остаток по счету 60 на 2020 май","factual","address-jjAFfbUg3l","account_balance_snapshot","AGGREGATE_LOOKUP","matched_non_empty","6","","True","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-05-01","2020-05-31","2020-05-31","account","60","60","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"10","60 счет остаток РЅР° май 2020 покажи","factual","address-zp57Rg1ax2","account_balance_snapshot","AGGREGATE_LOOKUP","matched_non_empty","6","","True","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-05-01","2020-05-31","2020-05-31","account","60","60","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"11","какой остаток по счету 60 на 2020-05-31","factual","address-UdMlO0oihD","account_balance_snapshot","AGGREGATE_LOOKUP","matched_non_empty","6","","True","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-05-01","2020-05-31","2020-05-31","account","60","60","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"12","остаток по 60 на май 2020, не за весь 2020","factual","address-C9kv5RSpvX","account_balance_snapshot","AGGREGATE_LOOKUP","matched_non_empty","6","","True","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-05-01","2020-05-31","2020-05-31","account","60","60","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"13","покаж РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РґРѕРєРё Р·Р° 2020","factual","address-nAM6RcOew7","list_documents_by_contract","DOCUMENT_LIST","matched_non_empty","233","","True","fallback_rule_applied_after_llm_error","documents_contract_year_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-01-01","2020-12-31","","contract","1-","1-","Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
"14","какие документы РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 Р·Р° РІСЃРµ время","factual","address-0KPk8HnFoG","list_documents_by_contract","DOCUMENT_LIST","matched_non_empty","120","","True","fallback_rule_applied_after_llm_error","documents_contract_all_time_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","","","","contract","1-","1-","Собран список документов по договору (live address lane)."
|
||||
"15","есть ли долг РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РЅР° 2020-07-31","partial_coverage","address-NAspBEx-B2","account_balance_snapshot","AGGREGATE_LOOKUP","no_raw_rows","0","empty_match","True","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-07-01","2020-07-31","2020-07-31","account","07","07","В live-данных по текущему фильтру записи не найдены."
|
||||
"16","какие хвосты РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РЅР° дату 31.07.2020","partial_coverage","address-9UG7ycCWej","account_balance_snapshot","UNKNOWN","no_raw_rows","0","empty_match","False","error:Failed to extract output_text from /responses payload.","","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-07-01","2020-07-31","2020-07-31","account","07","07","В live-данных по текущему фильтру записи не найдены."
|
||||
"17","какие платежи были РїРѕ СЃРІРє РІ 2020","partial_coverage","address-db6MWzmaK3","list_documents_by_counterparty","DOCUMENT_LIST","materialized_but_not_anchor_matched","0","missing_anchor","True","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-01-01","2020-12-31","","counterparty","были","были","Для точного адресного поиска не хватает обязательного якоря."
|
||||
"18","были ли поступления РѕС‚ СЃРІРє Р·Р° июль 2020","partial_coverage","address-kj2wxmasp5","list_documents_by_counterparty","DOCUMENT_LIST","materialized_but_not_anchor_matched","0","missing_anchor","True","fallback_rule_applied_after_llm_error","documents_counterparty_month_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-07-01","2020-07-31","","counterparty","были","были","Для точного адресного поиска не хватает обязательного якоря."
|
||||
"19","покажи списания СЃ расчетного счета РїРѕ СЃРІРє Р·Р° 2020","partial_coverage","address-5ZApJI8tq_","bank_operations_by_counterparty","DOCUMENT_LIST","materialized_but_not_anchor_matched","0","missing_anchor","True","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-01-01","2020-12-31","","counterparty","списания","списания","Для точного адресного поиска не хватает обязательного якоря."
|
||||
"20","СЃРІРє Р·Р° 2020 покаж РІСЃРµ поступления","factual","address-b8vcHTo3Mb","list_documents_by_counterparty","DOCUMENT_LIST","matched_non_empty","3","","True","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-01-01","2020-12-31","","counterparty","свк","Группа СВК","Собран список документов по контрагенту (live address lane)."
|
||||
"21","покажи документы РїРѕ СЃРІРє Р·Р° 2020","factual","address-3plzT4WuiP","list_documents_by_counterparty","DOCUMENT_LIST","matched_non_empty","3","","True","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-01-01","2020-12-31","","counterparty","свк","Группа СВК","Собран список документов по контрагенту (live address lane)."
|
||||
"22","а теперь только за май 2020","factual","address-ZAbNn9yMro","list_documents_by_counterparty","UNKNOWN","matched_non_empty","26","","False","not_address_like","","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-05-01","2020-05-31","","counterparty","свк","Группа СВК","По окну 2020-05-01..2020-05-31 строк не найдено; показаны ближайшие доступные данные 2020-07-27..2021-11-10."
|
||||
"23","а по счету 60.01 на ту же дату","factual","address-8AhrNbM9OG","account_balance_snapshot","UNKNOWN","matched_non_empty","5","","False","error:Failed to extract output_text from /responses payload.","","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2020-05-01","2020-05-31","2020-05-31","account","60.01","60.01","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"24","бля епт покажи РґРѕРєРё РїРѕ СЃРІРє Р·Р° 20Р№","factual","address-t3_otb_RVf","list_documents_by_counterparty","DOCUMENT_LIST","matched_non_empty","26","","True","fallback_rule_applied_after_llm_error","documents_counterparty_rewrite","run_address_lane","LIVE_QUERYABLE_WITH_LIMITS","2026-01-01","2026-04-01","","counterparty","свк","Группа СВК","По окну 2026-01-01..2026-04-01 строк не найдено; показаны ближайшие доступные данные 2020-07-27..2021-11-10."
|
||||
|
|
|
@ -0,0 +1,554 @@
|
|||
[
|
||||
{
|
||||
"idx": 1,
|
||||
"question": "свк доки за 20год покеж",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-sugd3wlUri",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 3,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"question": "свк 20 год - покажи доки плс",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address--4sSq56ihd",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 3,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"question": "что по свк за 2020 год выведи все доки плиз что есть",
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-MUkQr3-8cm",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"rows_matched": 0,
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "что",
|
||||
"anchor_value_resolved": "что",
|
||||
"assistant_reply_head": "Для точного адресного поиска не хватает обязательного якоря."
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"question": "какие документы по контрагенту свк за все время",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-vfIW3ugGJ3",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 26,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_all_time_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "",
|
||||
"period_to": "",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"question": "РґРѕРєРё РїРѕ СЃРІРє СЃ 01.07.2020 РїРѕ 31.07.2020",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-OheS6P-gDn",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 2,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_month_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-07-01",
|
||||
"period_to": "2020-07-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"question": "свк июль 2020 какие доки есть",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-DCuYSZOqzh",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 2,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_month_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-07-01",
|
||||
"period_to": "2020-07-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"question": "покажи сальдо по 60.01 на 31.07.20",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-aW8e72ada6",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "AGGREGATE_LOOKUP",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 200,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_account_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "",
|
||||
"period_to": "",
|
||||
"as_of_date": "2026-04-01",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "60.01",
|
||||
"anchor_value_resolved": "60.01",
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"question": "остаток 60 на 2020.05",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-o-EiCUq_sW",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "AGGREGATE_LOOKUP",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 6,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "2020-05-31",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"question": "какой остаток по счету 60 на 2020 май",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-jjAFfbUg3l",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "AGGREGATE_LOOKUP",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 6,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "2020-05-31",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"question": "60 счет остаток на май 2020 покажи",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-zp57Rg1ax2",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "AGGREGATE_LOOKUP",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 6,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "2020-05-31",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"question": "какой остаток по счету 60 на 2020-05-31",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-UdMlO0oihD",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "AGGREGATE_LOOKUP",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 6,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "2020-05-31",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"question": "остаток по 60 на май 2020, не за весь 2020",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-C9kv5RSpvX",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "AGGREGATE_LOOKUP",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 6,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "2020-05-31",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"question": "покаж РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РґРѕРєРё Р·Р° 2020",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-nAM6RcOew7",
|
||||
"detected_intent": "list_documents_by_contract",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 233,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_contract_year_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "contract",
|
||||
"anchor_value_raw": "1-",
|
||||
"anchor_value_resolved": "1-",
|
||||
"assistant_reply_head": "Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"question": "какие документы РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 Р·Р° РІСЃРµ время",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-0KPk8HnFoG",
|
||||
"detected_intent": "list_documents_by_contract",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 120,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_contract_all_time_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "",
|
||||
"period_to": "",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "contract",
|
||||
"anchor_value_raw": "1-",
|
||||
"anchor_value_resolved": "1-",
|
||||
"assistant_reply_head": "Собран список документов по договору (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"question": "есть ли долг РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РЅР° 2020-07-31",
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-NAspBEx-B2",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "AGGREGATE_LOOKUP",
|
||||
"mcp_call_status": "no_raw_rows",
|
||||
"rows_matched": 0,
|
||||
"limited_reason_category": "empty_match",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-07-01",
|
||||
"period_to": "2020-07-31",
|
||||
"as_of_date": "2020-07-31",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "07",
|
||||
"anchor_value_resolved": "07",
|
||||
"assistant_reply_head": "В live-данных по текущему фильтру записи не найдены."
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"question": "какие хвосты РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РЅР° дату 31.07.2020",
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-9UG7ycCWej",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "UNKNOWN",
|
||||
"mcp_call_status": "no_raw_rows",
|
||||
"rows_matched": 0,
|
||||
"limited_reason_category": "empty_match",
|
||||
"llm_decomposition_applied": false,
|
||||
"llm_decomposition_reason": "error:Failed to extract output_text from /responses payload.",
|
||||
"fallback_rule_hit": "",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-07-01",
|
||||
"period_to": "2020-07-31",
|
||||
"as_of_date": "2020-07-31",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "07",
|
||||
"anchor_value_resolved": "07",
|
||||
"assistant_reply_head": "В live-данных по текущему фильтру записи не найдены."
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"question": "какие платежи были по свк в 2020",
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-db6MWzmaK3",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"rows_matched": 0,
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "были",
|
||||
"anchor_value_resolved": "были",
|
||||
"assistant_reply_head": "Для точного адресного поиска не хватает обязательного якоря."
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"question": "были ли поступления от свк за июль 2020",
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-kj2wxmasp5",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"rows_matched": 0,
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_month_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-07-01",
|
||||
"period_to": "2020-07-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "были",
|
||||
"anchor_value_resolved": "были",
|
||||
"assistant_reply_head": "Для точного адресного поиска не хватает обязательного якоря."
|
||||
},
|
||||
{
|
||||
"idx": 19,
|
||||
"question": "покажи списания с расчетного счета по свк за 2020",
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-5ZApJI8tq_",
|
||||
"detected_intent": "bank_operations_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"rows_matched": 0,
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "списания",
|
||||
"anchor_value_resolved": "списания",
|
||||
"assistant_reply_head": "Для точного адресного поиска не хватает обязательного якоря."
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"question": "свк за 2020 покаж все поступления",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-b8vcHTo3Mb",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 3,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"question": "покажи документы по свк за 2020",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-3plzT4WuiP",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 3,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"question": "а теперь только за май 2020",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-ZAbNn9yMro",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "UNKNOWN",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 26,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": false,
|
||||
"llm_decomposition_reason": "not_address_like",
|
||||
"fallback_rule_hit": "",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "По окну 2020-05-01..2020-05-31 строк не найдено; показаны ближайшие доступные данные 2020-07-27..2021-11-10."
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"question": "а по счету 60.01 на ту же дату",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-8AhrNbM9OG",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"query_shape": "UNKNOWN",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 5,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": false,
|
||||
"llm_decomposition_reason": "error:Failed to extract output_text from /responses payload.",
|
||||
"fallback_rule_hit": "",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "2020-05-31",
|
||||
"anchor_type": "account",
|
||||
"anchor_value_raw": "60.01",
|
||||
"anchor_value_resolved": "60.01",
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"question": "бля епт покажи доки по свк за 20й",
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-t3_otb_RVf",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"query_shape": "DOCUMENT_LIST",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"rows_matched": 26,
|
||||
"limited_reason_category": "",
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_rewrite",
|
||||
"tool_gate_decision": "run_address_lane",
|
||||
"runtime_readiness": "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
"period_from": "2026-01-01",
|
||||
"period_to": "2026-04-01",
|
||||
"as_of_date": "",
|
||||
"anchor_type": "counterparty",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"assistant_reply_head": "По окну 2026-01-01..2026-04-01 строк не найдено; показаны ближайшие доступные данные 2020-07-27..2021-11-10."
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
"idx","question","reply_type","mcp_call_status","limited_reason_category","detected_intent","llm_decomposition_reason","fallback_rule_hit","anchor_value_raw","anchor_value_resolved","rows_matched","assistant_reply_head"
|
||||
"1","свк доки за 20год покеж","factual","matched_non_empty","","list_documents_by_counterparty","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","свк","Группа СВК","3","Собран список документов по контрагенту (live address lane)."
|
||||
"2","свк 20 год - покажи доки плс","factual","matched_non_empty","","list_documents_by_counterparty","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","свк","Группа СВК","3","Собран список документов по контрагенту (live address lane)."
|
||||
"3","что по свк за 2020 год выведи все доки плиз что есть","factual","matched_non_empty","","list_documents_by_counterparty","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","свк","Группа СВК","3","Собран список документов по контрагенту (live address lane)."
|
||||
"4","какие документы по контрагенту свк за все время","partial_coverage","error","execution_error","list_documents_by_counterparty","fallback_rule_applied_after_llm_error","documents_counterparty_all_time_rewrite","свк","свк","0","Не удалось выполнить адресный live-запрос в V1."
|
||||
"5","доки по свк с 01.07.2020 по 31.07.2020","factual","matched_non_empty","","list_documents_by_counterparty","fallback_rule_applied_after_llm_error","documents_counterparty_month_rewrite","свк","Группа СВК","2","Собран список документов по контрагенту (live address lane)."
|
||||
"6","свк июль 2020 какие доки есть","factual","matched_non_empty","","list_documents_by_counterparty","fallback_rule_applied_after_llm_error","documents_counterparty_month_rewrite","свк","Группа СВК","2","Собран список документов по контрагенту (live address lane)."
|
||||
"7","покажи сальдо по 60.01 на 31.07.20","factual","matched_non_empty","","account_balance_snapshot","fallback_rule_applied_after_llm_error","balance_account_rewrite","60.01","60.01","200","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"8","остаток 60 на 2020.05","factual","matched_non_empty","","account_balance_snapshot","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","60","60","6","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"9","какой остаток по счету 60 на 2020 май","factual","matched_non_empty","","account_balance_snapshot","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","60","60","6","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"10","60 счет остаток на май 2020 покажи","factual","matched_non_empty","","account_balance_snapshot","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","60","60","6","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"11","какой остаток по счету 60 на 2020-05-31","factual","matched_non_empty","","account_balance_snapshot","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","60","60","6","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"12","остаток по 60 на май 2020, не за весь 2020","factual","matched_non_empty","","account_balance_snapshot","fallback_rule_applied_after_llm_error","balance_month_period_rewrite","60","60","6","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"13","покаж по договору 1-<2D><>/2020 доки за 2020","factual","matched_non_empty","","list_documents_by_contract","fallback_rule_applied_after_llm_error","documents_contract_year_rewrite","1-","1-","233","Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
"14","какие документы по договору 1-<2D><>/2020 за все время","factual","matched_non_empty","","list_documents_by_contract","fallback_rule_applied_after_llm_error","documents_contract_all_time_rewrite","1-","1-","120","Собран список документов по договору (live address lane)."
|
||||
"15","есть ли долг по договору 1-<2D><>/2020 на 2020-07-31","partial_coverage","materialized_but_filtered_out_by_recipe","recipe_visibility_gap","list_documents_by_contract","error:Failed to extract output_text from /responses payload.","","1-<2D><>/2020 на 2020-07-31","1-<2D><>/2020 на 2020-07-31","0","Текущий live recipe не дает нужную видимость данных для этого сценария."
|
||||
"16","какие хвосты по договору 1-<2D><>/2020 на дату 31.07.2020","partial_coverage","materialized_but_not_anchor_matched","missing_anchor","list_documents_by_contract","error:Failed to extract output_text from /responses payload.","","1-<2D><>/2020 на дату 31","1-<2D><>/2020 на дату 31","0","Для точного адресного поиска не хватает обязательного якоря."
|
||||
"17","какие платежи были по свк в 2020","factual","matched_non_empty","","bank_operations_by_counterparty","fallback_rule_applied_after_llm_error","bank_operations_counterparty_year_rewrite","свк","Группа СВК","3","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"18","были ли поступления от свк за июль 2020","factual","matched_non_empty","","bank_operations_by_counterparty","fallback_rule_applied_after_llm_error","bank_operations_counterparty_month_rewrite","свк","Группа СВК","2","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"19","покажи списания с расчетного счета по свк за 2020","factual","matched_non_empty","","bank_operations_by_counterparty","fallback_rule_applied_after_llm_error","bank_operations_counterparty_year_rewrite","свк","Группа СВК","3","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"20","свк за 2020 покаж все поступления","factual","matched_non_empty","","bank_operations_by_counterparty","fallback_rule_applied_after_llm_error","bank_operations_counterparty_year_rewrite","свк","Группа СВК","3","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"21","покажи документы по свк за 2020","factual","matched_non_empty","","list_documents_by_counterparty","fallback_rule_applied_after_llm_error","documents_counterparty_year_rewrite","свк","Группа СВК","3","Собран список документов по контрагенту (live address lane)."
|
||||
"22","а теперь только за май 2020","partial_coverage","materialized_but_not_anchor_matched","missing_anchor","list_documents_by_counterparty","not_address_like","","свк","свк","0","Для точного адресного поиска не хватает обязательного якоря."
|
||||
"23","а по счету 60.01 на ту же дату","factual","matched_non_empty","","account_balance_snapshot","error:Failed to extract output_text from /responses payload.","","60.01","60.01","5","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"24","бля епт покажи доки по свк за 20й","factual","matched_non_empty","","list_documents_by_counterparty","fallback_rule_applied_after_llm_error","documents_counterparty_rewrite","свк","Группа СВК","26","По окну 2026-01-01..2026-04-01 строк не найдено; показаны ближайшие доступные данные 2020-07-27..2021-11-10."
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
[
|
||||
{
|
||||
"idx": 1,
|
||||
"question": "свк доки за 20год покеж",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 3,
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"question": "свк 20 год - покажи доки плс",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 3,
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"question": "что по свк за 2020 год выведи все доки плиз что есть",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 3,
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"question": "какие документы по контрагенту свк за все время",
|
||||
"reply_type": "partial_coverage",
|
||||
"mcp_call_status": "error",
|
||||
"limited_reason_category": "execution_error",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_all_time_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "свк",
|
||||
"rows_matched": 0,
|
||||
"assistant_reply_head": "Не удалось выполнить адресный live-запрос в V1."
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"question": "доки по свк с 01.07.2020 по 31.07.2020",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_month_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 2,
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"question": "свк июль 2020 какие доки есть",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_month_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 2,
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"question": "покажи сальдо по 60.01 на 31.07.20",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_account_rewrite",
|
||||
"anchor_value_raw": "60.01",
|
||||
"anchor_value_resolved": "60.01",
|
||||
"rows_matched": 200,
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"question": "остаток 60 на 2020.05",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"rows_matched": 6,
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"question": "какой остаток по счету 60 на 2020 май",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"rows_matched": 6,
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"question": "60 счет остаток на май 2020 покажи",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"rows_matched": 6,
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"question": "какой остаток по счету 60 на 2020-05-31",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"rows_matched": 6,
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"question": "остаток по 60 на май 2020, не за весь 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "balance_month_period_rewrite",
|
||||
"anchor_value_raw": "60",
|
||||
"anchor_value_resolved": "60",
|
||||
"rows_matched": 6,
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"question": "покаж по договору 1-<2D><>/2020 доки за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_contract",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_contract_year_rewrite",
|
||||
"anchor_value_raw": "1-",
|
||||
"anchor_value_resolved": "1-",
|
||||
"rows_matched": 233,
|
||||
"assistant_reply_head": "Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"question": "какие документы по договору 1-<2D><>/2020 за все время",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_contract",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_contract_all_time_rewrite",
|
||||
"anchor_value_raw": "1-",
|
||||
"anchor_value_resolved": "1-",
|
||||
"rows_matched": 120,
|
||||
"assistant_reply_head": "Собран список документов по договору (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"question": "есть ли долг по договору 1-<2D><>/2020 на 2020-07-31",
|
||||
"reply_type": "partial_coverage",
|
||||
"mcp_call_status": "materialized_but_filtered_out_by_recipe",
|
||||
"limited_reason_category": "recipe_visibility_gap",
|
||||
"detected_intent": "list_documents_by_contract",
|
||||
"llm_decomposition_reason": "error:Failed to extract output_text from /responses payload.",
|
||||
"fallback_rule_hit": "",
|
||||
"anchor_value_raw": "1-<2D><>/2020 на 2020-07-31",
|
||||
"anchor_value_resolved": "1-<2D><>/2020 на 2020-07-31",
|
||||
"rows_matched": 0,
|
||||
"assistant_reply_head": "Текущий live recipe не дает нужную видимость данных для этого сценария."
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"question": "какие хвосты по договору 1-<2D><>/2020 на дату 31.07.2020",
|
||||
"reply_type": "partial_coverage",
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"detected_intent": "list_documents_by_contract",
|
||||
"llm_decomposition_reason": "error:Failed to extract output_text from /responses payload.",
|
||||
"fallback_rule_hit": "",
|
||||
"anchor_value_raw": "1-<2D><>/2020 на дату 31",
|
||||
"anchor_value_resolved": "1-<2D><>/2020 на дату 31",
|
||||
"rows_matched": 0,
|
||||
"assistant_reply_head": "Для точного адресного поиска не хватает обязательного якоря."
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"question": "какие платежи были по свк в 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "bank_operations_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "bank_operations_counterparty_year_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 3,
|
||||
"assistant_reply_head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"question": "были ли поступления от свк за июль 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "bank_operations_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "bank_operations_counterparty_month_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 2,
|
||||
"assistant_reply_head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 19,
|
||||
"question": "покажи списания с расчетного счета по свк за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "bank_operations_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "bank_operations_counterparty_year_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 3,
|
||||
"assistant_reply_head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"question": "свк за 2020 покаж все поступления",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "bank_operations_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "bank_operations_counterparty_year_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 3,
|
||||
"assistant_reply_head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"question": "покажи документы по свк за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_year_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 3,
|
||||
"assistant_reply_head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"question": "а теперь только за май 2020",
|
||||
"reply_type": "partial_coverage",
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "not_address_like",
|
||||
"fallback_rule_hit": "",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "свк",
|
||||
"rows_matched": 0,
|
||||
"assistant_reply_head": "Для точного адресного поиска не хватает обязательного якоря."
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"question": "а по счету 60.01 на ту же дату",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"llm_decomposition_reason": "error:Failed to extract output_text from /responses payload.",
|
||||
"fallback_rule_hit": "",
|
||||
"anchor_value_raw": "60.01",
|
||||
"anchor_value_resolved": "60.01",
|
||||
"rows_matched": 5,
|
||||
"assistant_reply_head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"question": "бля епт покажи доки по свк за 20й",
|
||||
"reply_type": "factual",
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": "",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"llm_decomposition_reason": "fallback_rule_applied_after_llm_error",
|
||||
"fallback_rule_hit": "documents_counterparty_rewrite",
|
||||
"anchor_value_raw": "свк",
|
||||
"anchor_value_resolved": "Группа СВК",
|
||||
"rows_matched": 26,
|
||||
"assistant_reply_head": "По окну 2026-01-01..2026-04-01 строк не найдено; показаны ближайшие доступные данные 2020-07-27..2021-11-10."
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
"idx","question","reply_type","mcp","limited","intent","anchor_raw","anchor_resolved","fallback","head"
|
||||
"1","свк доки за 20год покеж","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"2","свк 20 год - покажи доки плс","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"3","что по свк за 2020 год выведи все доки плиз что есть","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"4","какие документы по контрагенту свк за все время","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_all_time_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"5","доки по свк с 01.07.2020 по 31.07.2020","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_month_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"6","свк июль 2020 какие доки есть","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_month_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"7","покажи сальдо по 60.01 на 31.07.20","factual","matched_non_empty","","account_balance_snapshot","60.01","60.01","balance_account_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"8","остаток 60 на 2020.05","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"9","какой остаток по счету 60 на 2020 май","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"10","60 счет остаток на май 2020 покажи","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"11","какой остаток по счету 60 на 2020-05-31","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"12","остаток по 60 на май 2020, не за весь 2020","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"13","покаж по договору 1-<2D><>/2020 доки за 2020","factual","matched_non_empty","","list_documents_by_contract","1-","1-","documents_contract_year_rewrite","По окну 2020-01-01..2020-12-31 строк не найдено; показаны ближайшие доступные данные 2020-09-30..2022-09-30."
|
||||
"14","какие документы по договору 1-<2D><>/2020 за все время","factual","matched_non_empty","","list_documents_by_contract","1-","1-","documents_contract_all_time_rewrite","Собран список документов по договору (live address lane)."
|
||||
"15","есть ли долг по договору 1-<2D><>/2020 на 2020-07-31","factual","matched_non_empty","","list_documents_by_contract","1-<2D><>/2020","1-<2D><>/2020","","Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
"16","какие хвосты по договору 1-<2D><>/2020 на дату 31.07.2020","factual","matched_non_empty","","list_documents_by_contract","1-<2D><>/2020","1-<2D><>/2020","","Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
"17","какие платежи были по свк в 2020","factual","matched_non_empty","","bank_operations_by_counterparty","свк","Группа СВК","bank_operations_counterparty_year_rewrite","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"18","были ли поступления от свк за июль 2020","factual","matched_non_empty","","bank_operations_by_counterparty","свк","Группа СВК","bank_operations_counterparty_month_rewrite","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"19","покажи списания с расчетного счета по свк за 2020","factual","matched_non_empty","","bank_operations_by_counterparty","свк","Группа СВК","bank_operations_counterparty_year_rewrite","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"20","свк за 2020 покаж все поступления","factual","matched_non_empty","","bank_operations_by_counterparty","свк","Группа СВК","bank_operations_counterparty_year_rewrite","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"21","покажи документы по свк за 2020","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"22","а теперь только за май 2020","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","","По окну 2020-05-01..2020-05-31 строк не найдено; показаны ближайшие доступные данные 2020-07-27..2021-11-10."
|
||||
"23","а по счету 60.01 на ту же дату","factual","matched_non_empty","","account_balance_snapshot","60.01","60.01","","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"24","бля епт покажи доки по свк за 20й","partial_coverage","no_raw_rows","empty_match","list_documents_by_counterparty","свк","свк","documents_counterparty_rewrite","В live-данных по текущему фильтру записи не найдены."
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
[
|
||||
{
|
||||
"idx": 1,
|
||||
"question": "свк доки за 20год покеж",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"question": "свк 20 год - покажи доки плс",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"question": "что по свк за 2020 год выведи все доки плиз что есть",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"question": "какие документы по контрагенту свк за все время",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_all_time_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"question": "доки по свк с 01.07.2020 по 31.07.2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_month_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"question": "свк июль 2020 какие доки есть",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_month_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"question": "покажи сальдо по 60.01 на 31.07.20",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60.01",
|
||||
"anchor_resolved": "60.01",
|
||||
"fallback": "balance_account_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"question": "остаток 60 на 2020.05",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"question": "какой остаток по счету 60 на 2020 май",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"question": "60 счет остаток на май 2020 покажи",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"question": "какой остаток по счету 60 на 2020-05-31",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"question": "остаток по 60 на май 2020, не за весь 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"question": "покаж по договору 1-<2D><>/2020 доки за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_contract",
|
||||
"anchor_raw": "1-",
|
||||
"anchor_resolved": "1-",
|
||||
"fallback": "documents_contract_year_rewrite",
|
||||
"head": "По окну 2020-01-01..2020-12-31 строк не найдено; показаны ближайшие доступные данные 2020-09-30..2022-09-30."
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"question": "какие документы по договору 1-<2D><>/2020 за все время",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_contract",
|
||||
"anchor_raw": "1-",
|
||||
"anchor_resolved": "1-",
|
||||
"fallback": "documents_contract_all_time_rewrite",
|
||||
"head": "Собран список документов по договору (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"question": "есть ли долг по договору 1-<2D><>/2020 на 2020-07-31",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_contract",
|
||||
"anchor_raw": "1-<2D><>/2020",
|
||||
"anchor_resolved": "1-<2D><>/2020",
|
||||
"fallback": "",
|
||||
"head": "Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"question": "какие хвосты по договору 1-<2D><>/2020 на дату 31.07.2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_contract",
|
||||
"anchor_raw": "1-<2D><>/2020",
|
||||
"anchor_resolved": "1-<2D><>/2020",
|
||||
"fallback": "",
|
||||
"head": "Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"question": "какие платежи были по свк в 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "bank_operations_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "bank_operations_counterparty_year_rewrite",
|
||||
"head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"question": "были ли поступления от свк за июль 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "bank_operations_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "bank_operations_counterparty_month_rewrite",
|
||||
"head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 19,
|
||||
"question": "покажи списания с расчетного счета по свк за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "bank_operations_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "bank_operations_counterparty_year_rewrite",
|
||||
"head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"question": "свк за 2020 покаж все поступления",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "bank_operations_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "bank_operations_counterparty_year_rewrite",
|
||||
"head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"question": "покажи документы по свк за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"question": "а теперь только за май 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "",
|
||||
"head": "По окну 2020-05-01..2020-05-31 строк не найдено; показаны ближайшие доступные данные 2020-07-27..2021-11-10."
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"question": "а по счету 60.01 на ту же дату",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60.01",
|
||||
"anchor_resolved": "60.01",
|
||||
"fallback": "",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"question": "бля епт покажи доки по свк за 20й",
|
||||
"reply_type": "partial_coverage",
|
||||
"mcp": "no_raw_rows",
|
||||
"limited": "empty_match",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "свк",
|
||||
"fallback": "documents_counterparty_rewrite",
|
||||
"head": "В live-данных по текущему фильтру записи не найдены."
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
"idx","question","reply_type","mcp","limited","intent","anchor_raw","anchor_resolved","fallback","head"
|
||||
"1","свк доки за 20год покеж","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"2","свк 20 год - покажи доки плс","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"3","что по свк за 2020 год выведи все доки плиз что есть","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"4","какие документы по контрагенту свк за все время","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_all_time_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"5","доки по свк с 01.07.2020 по 31.07.2020","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_month_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"6","свк июль 2020 какие доки есть","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_month_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"7","покажи сальдо по 60.01 на 31.07.20","factual","matched_non_empty","","account_balance_snapshot","60.01","60.01","balance_account_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"8","остаток 60 на 2020.05","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"9","какой остаток по счету 60 на 2020 май","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"10","60 счет остаток на май 2020 покажи","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"11","какой остаток по счету 60 на 2020-05-31","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"12","остаток по 60 на май 2020, не за весь 2020","factual","matched_non_empty","","account_balance_snapshot","60","60","balance_month_period_rewrite","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"13","покаж по договору 1-<2D><>/2020 доки за 2020","factual","matched_non_empty","","list_documents_by_contract","1-","1-","documents_contract_year_rewrite","Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
"14","какие документы по договору 1-<2D><>/2020 за все время","factual","matched_non_empty","","list_documents_by_contract","1-","1-","documents_contract_all_time_rewrite","Собран список документов по договору (live address lane)."
|
||||
"15","есть ли долг по договору 1-<2D><>/2020 на 2020-07-31","factual","matched_non_empty","","list_documents_by_contract","1-<2D><>/2020","1-<2D><>/2020","","Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
"16","какие хвосты по договору 1-<2D><>/2020 на дату 31.07.2020","factual","matched_non_empty","","list_documents_by_contract","1-<2D><>/2020","1-<2D><>/2020","","Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
"17","какие платежи были по свк в 2020","factual","matched_non_empty","","bank_operations_by_counterparty","свк","Группа СВК","bank_operations_counterparty_year_rewrite","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"18","были ли поступления от свк за июль 2020","factual","matched_non_empty","","bank_operations_by_counterparty","свк","Группа СВК","bank_operations_counterparty_month_rewrite","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"19","покажи списания с расчетного счета по свк за 2020","factual","matched_non_empty","","bank_operations_by_counterparty","свк","Группа СВК","bank_operations_counterparty_year_rewrite","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"20","свк за 2020 покаж все поступления","factual","matched_non_empty","","bank_operations_by_counterparty","свк","Группа СВК","bank_operations_counterparty_year_rewrite","Собран список банковских операций по контрагенту (live address lane)."
|
||||
"21","покажи документы по свк за 2020","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
"22","а теперь только за май 2020","partial_coverage","materialized_but_not_anchor_matched","missing_anchor","list_documents_by_counterparty","свк","свк","","Для точного адресного поиска не хватает обязательного якоря."
|
||||
"23","а по счету 60.01 на ту же дату","factual","matched_non_empty","","account_balance_snapshot","60.01","60.01","","Адресный срез по счету собран (по движениям live MCP)."
|
||||
"24","бля епт покажи доки по свк за 20й","factual","matched_non_empty","","list_documents_by_counterparty","свк","Группа СВК","documents_counterparty_year_rewrite","Собран список документов по контрагенту (live address lane)."
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
[
|
||||
{
|
||||
"idx": 1,
|
||||
"question": "свк доки за 20год покеж",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"question": "свк 20 год - покажи доки плс",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"question": "что по свк за 2020 год выведи все доки плиз что есть",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"question": "какие документы по контрагенту свк за все время",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_all_time_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"question": "доки по свк с 01.07.2020 по 31.07.2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_month_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"question": "свк июль 2020 какие доки есть",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_month_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"question": "покажи сальдо по 60.01 на 31.07.20",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60.01",
|
||||
"anchor_resolved": "60.01",
|
||||
"fallback": "balance_account_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"question": "остаток 60 на 2020.05",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"question": "какой остаток по счету 60 на 2020 май",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"question": "60 счет остаток на май 2020 покажи",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"question": "какой остаток по счету 60 на 2020-05-31",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"question": "остаток по 60 на май 2020, не за весь 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60",
|
||||
"anchor_resolved": "60",
|
||||
"fallback": "balance_month_period_rewrite",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"question": "покаж по договору 1-<2D><>/2020 доки за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_contract",
|
||||
"anchor_raw": "1-",
|
||||
"anchor_resolved": "1-",
|
||||
"fallback": "documents_contract_year_rewrite",
|
||||
"head": "Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"question": "какие документы по договору 1-<2D><>/2020 за все время",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_contract",
|
||||
"anchor_raw": "1-",
|
||||
"anchor_resolved": "1-",
|
||||
"fallback": "documents_contract_all_time_rewrite",
|
||||
"head": "Собран список документов по договору (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"question": "есть ли долг по договору 1-<2D><>/2020 на 2020-07-31",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_contract",
|
||||
"anchor_raw": "1-<2D><>/2020",
|
||||
"anchor_resolved": "1-<2D><>/2020",
|
||||
"fallback": "",
|
||||
"head": "Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"question": "какие хвосты по договору 1-<2D><>/2020 на дату 31.07.2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_contract",
|
||||
"anchor_raw": "1-<2D><>/2020",
|
||||
"anchor_resolved": "1-<2D><>/2020",
|
||||
"fallback": "",
|
||||
"head": "Период сохранен. Глубина live-выборки автоматически расширена до 1000 строк."
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"question": "какие платежи были по свк в 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "bank_operations_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "bank_operations_counterparty_year_rewrite",
|
||||
"head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"question": "были ли поступления от свк за июль 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "bank_operations_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "bank_operations_counterparty_month_rewrite",
|
||||
"head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 19,
|
||||
"question": "покажи списания с расчетного счета по свк за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "bank_operations_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "bank_operations_counterparty_year_rewrite",
|
||||
"head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"question": "свк за 2020 покаж все поступления",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "bank_operations_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "bank_operations_counterparty_year_rewrite",
|
||||
"head": "Собран список банковских операций по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"question": "покажи документы по свк за 2020",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"question": "а теперь только за май 2020",
|
||||
"reply_type": "partial_coverage",
|
||||
"mcp": "materialized_but_not_anchor_matched",
|
||||
"limited": "missing_anchor",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "свк",
|
||||
"fallback": "",
|
||||
"head": "Для точного адресного поиска не хватает обязательного якоря."
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"question": "а по счету 60.01 на ту же дату",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "account_balance_snapshot",
|
||||
"anchor_raw": "60.01",
|
||||
"anchor_resolved": "60.01",
|
||||
"fallback": "",
|
||||
"head": "Адресный срез по счету собран (по движениям live MCP)."
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"question": "бля епт покажи доки по свк за 20й",
|
||||
"reply_type": "factual",
|
||||
"mcp": "matched_non_empty",
|
||||
"limited": "",
|
||||
"intent": "list_documents_by_counterparty",
|
||||
"anchor_raw": "свк",
|
||||
"anchor_resolved": "Группа СВК",
|
||||
"fallback": "documents_counterparty_year_rewrite",
|
||||
"head": "Собран список документов по контрагенту (live address lane)."
|
||||
}
|
||||
]
|
||||
|
|
@ -312,6 +312,18 @@ function cleanupAnchorValue(value: string): string {
|
|||
return "";
|
||||
}
|
||||
|
||||
// Remove trailing as-of qualifiers often captured by broad contract/counterparty regexes:
|
||||
// "<anchor> на 2020-07-31", "<anchor> на дату 31.07.2020", "<anchor> as of 2020-07-31".
|
||||
const asOfTailPattern =
|
||||
/\s+(?:на\s+(?:дат[ауеы]\s+)?\d{1,4}[./-]\d{1,2}(?:[./-]\d{1,4})?|as\s+of\s+\d{1,4}[./-]\d{1,2}(?:[./-]\d{1,4})?)(?:\s+|$)[\s\S]*$/iu;
|
||||
if (asOfTailPattern.test(normalized)) {
|
||||
return normalized.replace(asOfTailPattern, "").trim();
|
||||
}
|
||||
const asOfTruncatedTailPattern = /\s+на\s+дат[ауеы]\s+\d{1,2}(?:\s+|$)[\s\S]*$/iu;
|
||||
if (asOfTruncatedTailPattern.test(normalized)) {
|
||||
return normalized.replace(asOfTruncatedTailPattern, "").trim();
|
||||
}
|
||||
|
||||
// Remove trailing period qualifiers that can be swallowed by broad anchor regexes:
|
||||
// "<counterparty> с 2020-07-01 по 2020-07-31", "<counterparty> from 2020-07-01 to 2020-07-31"
|
||||
const periodTailPattern =
|
||||
|
|
@ -386,6 +398,29 @@ function extractLooseByAnchorValue(text: string): string | undefined {
|
|||
return token;
|
||||
}
|
||||
|
||||
function extractContractTokenHeuristic(text: string): string | undefined {
|
||||
const source = String(text ?? "");
|
||||
const explicit = source.match(/(?:№|#|n)\s*([a-zа-яё0-9][a-zа-яё0-9./_-]{1,})/iu);
|
||||
if (explicit) {
|
||||
const token = String(explicit[1] ?? "").trim();
|
||||
if (token.length >= 2) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
const slashLike = source.match(/\b(\d{1,6}[./_-]\d{1,6}(?:[./_-]\d{1,6})?)\b/iu);
|
||||
if (!slashLike) {
|
||||
return undefined;
|
||||
}
|
||||
const token = String(slashLike[1] ?? "").trim();
|
||||
if (!token) {
|
||||
return undefined;
|
||||
}
|
||||
if (/^(?:19|20)\d{2}[./-](?:0?[1-9]|1[0-2])$/.test(token)) {
|
||||
return undefined;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
function isLikelyCounterpartyToken(rawToken: string): boolean {
|
||||
const token = String(rawToken ?? "").trim();
|
||||
const lowered = token.toLowerCase();
|
||||
|
|
@ -577,6 +612,9 @@ function requiredFiltersByIntent(intent: AddressIntent): Array<keyof AddressFilt
|
|||
if (intent === "list_documents_by_counterparty" || intent === "bank_operations_by_counterparty") {
|
||||
return ["counterparty"];
|
||||
}
|
||||
if (intent === "list_documents_by_contract" || intent === "bank_operations_by_contract") {
|
||||
return ["contract"];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -632,6 +670,13 @@ export function extractAddressFilters(userMessage: string, intent: AddressIntent
|
|||
if (contractMatch) {
|
||||
filters.contract = cleanupAnchorValue(String(contractMatch[1]));
|
||||
}
|
||||
if (!filters.contract && (intent === "list_documents_by_contract" || intent === "bank_operations_by_contract")) {
|
||||
const heuristicContract = extractContractTokenHeuristic(text);
|
||||
if (heuristicContract) {
|
||||
filters.contract = cleanupAnchorValue(heuristicContract);
|
||||
warnings.push("contract_anchor_derived_from_heuristic_token");
|
||||
}
|
||||
}
|
||||
|
||||
const periodRange = extractPeriodRange(text);
|
||||
if (periodRange.period_from) {
|
||||
|
|
@ -678,7 +723,8 @@ export function extractAddressFilters(userMessage: string, intent: AddressIntent
|
|||
|
||||
// For document/bank lists we default to a short recent window if no explicit period was provided.
|
||||
if (
|
||||
(intent === "list_documents_by_counterparty" || intent === "bank_operations_by_counterparty") &&
|
||||
(intent === "list_documents_by_counterparty" ||
|
||||
intent === "bank_operations_by_counterparty") &&
|
||||
!filters.period_from &&
|
||||
!filters.period_to &&
|
||||
!hasAllTimeHint(text)
|
||||
|
|
|
|||
|
|
@ -103,6 +103,46 @@ const BANK_OPERATIONS_BY_COUNTERPARTY_HINTS = [
|
|||
"поступлен",
|
||||
"движени"
|
||||
];
|
||||
const DOCUMENTS_BY_CONTRACT_HINTS = [
|
||||
"documents by contract",
|
||||
"docs by contract",
|
||||
"show documents by contract",
|
||||
"list documents by contract",
|
||||
"документы по договору",
|
||||
"доки по договору",
|
||||
"док по договору",
|
||||
"документы договор",
|
||||
"договор"
|
||||
];
|
||||
const BANK_OPERATIONS_BY_CONTRACT_HINTS = [
|
||||
"bank operations by contract",
|
||||
"bank payments by contract",
|
||||
"payment orders by contract",
|
||||
"transactions by contract",
|
||||
"bank ops by contract",
|
||||
"банковские операции по договору",
|
||||
"платежи по договору",
|
||||
"выписка по договору"
|
||||
];
|
||||
|
||||
const BANK_OPERATION_CORE_HINTS = [
|
||||
"банков",
|
||||
"выписк",
|
||||
"платеж",
|
||||
"платёж",
|
||||
"оплат",
|
||||
"списан",
|
||||
"поступлен",
|
||||
"движени",
|
||||
"транзак",
|
||||
"bank",
|
||||
"payment",
|
||||
"payments",
|
||||
"transaction",
|
||||
"transactions",
|
||||
"statement",
|
||||
"wire"
|
||||
];
|
||||
|
||||
function hasAny(text: string, patterns: string[]): boolean {
|
||||
return patterns.some((item) => text.includes(item));
|
||||
|
|
@ -198,6 +238,72 @@ function hasPartyAnchorMention(text: string): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function hasContractAnchorMention(text: string): boolean {
|
||||
return (
|
||||
text.includes("договор") ||
|
||||
text.includes("дог.") ||
|
||||
text.includes("contract") ||
|
||||
text.includes("dogovor")
|
||||
);
|
||||
}
|
||||
|
||||
function hasContractNumberLikeToken(text: string): boolean {
|
||||
if (/(?:^|[\s([{])(?:№|#|n)\s*[a-zа-яё0-9][a-zа-яё0-9./_-]{1,}(?=$|[\s,.;:!?)\]}])/iu.test(text)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const rawTokens = text
|
||||
.split(/[\s,;:!?()[\]{}"«»]+/u)
|
||||
.map((token) => token.replace(/^[^\p{L}\p{N}#№]+|[^\p{L}\p{N}./_-]+$/gu, "").trim())
|
||||
.filter((token) => token.length > 0);
|
||||
|
||||
for (const rawToken of rawTokens) {
|
||||
const token = String(rawToken ?? "").trim();
|
||||
if (!/^\d{1,6}[./_-]\d{1,6}(?:[./_-]\d{1,6})?$/u.test(token)) {
|
||||
continue;
|
||||
}
|
||||
if (!token) {
|
||||
continue;
|
||||
}
|
||||
const parts = token.split(/[./_-]+/u).map((part) => Number(part));
|
||||
if (!parts.every((part) => Number.isFinite(part))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parts.length === 2) {
|
||||
const [a, b] = parts;
|
||||
const yearFirst = a >= 1900 && a <= 2099 && b >= 1 && b <= 12;
|
||||
const yearSecond = b >= 1900 && b <= 2099 && a >= 1 && a <= 12;
|
||||
if (yearFirst || yearSecond) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parts.length === 3) {
|
||||
const [a, b, c] = parts;
|
||||
const ymd = a >= 1900 && a <= 2099 && b >= 1 && b <= 12 && c >= 1 && c <= 31;
|
||||
const dmy = c >= 1900 && c <= 2099 && a >= 1 && a <= 31 && b >= 1 && b <= 12;
|
||||
if (ymd || dmy) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasContractAnchorSignal(text: string): boolean {
|
||||
if (hasContractAnchorMention(text)) {
|
||||
return true;
|
||||
}
|
||||
// Allow short forms like "15/24" for follow-up prompts if document/bank signal exists.
|
||||
return hasContractNumberLikeToken(text) && hasDocsOrBankSignal(text);
|
||||
}
|
||||
|
||||
function hasLooseByAnchorMention(text: string): boolean {
|
||||
const match = text.match(/(?:^|\s)по\s+([a-zа-яё][a-zа-яё0-9._-]{1,})(?=[\s,.;:!?)]|$)/iu);
|
||||
if (!match) {
|
||||
|
|
@ -260,6 +366,20 @@ function hasDocsOrBankSignal(text: string): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function hasBankOperationSignal(text: string): boolean {
|
||||
return hasAny(text, BANK_OPERATION_CORE_HINTS) || hasAny(text, BANK_OPERATIONS_BY_COUNTERPARTY_HINTS) || hasAny(text, BANK_OPERATIONS_BY_CONTRACT_HINTS);
|
||||
}
|
||||
|
||||
function hasDocumentSignal(text: string): boolean {
|
||||
return (
|
||||
text.includes("док") ||
|
||||
text.includes("доки") ||
|
||||
text.includes("документ") ||
|
||||
text.includes("docs") ||
|
||||
text.includes("documents")
|
||||
);
|
||||
}
|
||||
|
||||
function hasHeuristicCounterpartyAnchor(text: string): boolean {
|
||||
if (!hasDocsOrBankSignal(text)) {
|
||||
return false;
|
||||
|
|
@ -329,6 +449,28 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
|
|||
};
|
||||
}
|
||||
|
||||
if (
|
||||
hasContractAnchorSignal(text) &&
|
||||
hasBankOperationSignal(text)
|
||||
) {
|
||||
return {
|
||||
intent: "bank_operations_by_contract",
|
||||
confidence: "medium",
|
||||
reasons: ["bank_ops_by_contract_signal_detected"]
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
hasContractAnchorSignal(text) &&
|
||||
(hasAny(text, DOCUMENTS_BY_CONTRACT_HINTS) || hasDocumentSignal(text))
|
||||
) {
|
||||
return {
|
||||
intent: "list_documents_by_contract",
|
||||
confidence: "medium",
|
||||
reasons: ["documents_by_contract_signal_detected"]
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
hasAny(text, BANK_OPERATIONS_BY_COUNTERPARTY_HINTS) &&
|
||||
(hasPartyAnchorMention(text) || hasLooseByAnchorMention(text) || hasHeuristicCounterpartyAnchor(text))
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ interface AddressTryHandleOptions {
|
|||
|
||||
const ACCOUNT_SCOPE_FIELDS_CHECKED = ["account_dt", "account_kt", "registrator", "analytics"] as const;
|
||||
const ACCOUNT_SCOPE_MATCH_STRATEGY = "account_code_regex_plus_alias_map_v1" as const;
|
||||
const ADDRESS_ANCHOR_RECOVERY_LIMIT = 1000;
|
||||
const PARTY_ANCHOR_STOPWORDS = new Set([
|
||||
"ооо",
|
||||
"ао",
|
||||
|
|
@ -48,6 +49,35 @@ const PARTY_ANCHOR_STOPWORDS = new Set([
|
|||
"по",
|
||||
"by"
|
||||
]);
|
||||
const LOW_QUALITY_PARTY_ANCHOR_TOKENS = new Set([
|
||||
"что",
|
||||
"чо",
|
||||
"были",
|
||||
"был",
|
||||
"была",
|
||||
"было",
|
||||
"ли",
|
||||
"какие",
|
||||
"какой",
|
||||
"покажи",
|
||||
"показать",
|
||||
"выведи",
|
||||
"списания",
|
||||
"списание",
|
||||
"поступления",
|
||||
"поступление",
|
||||
"доки",
|
||||
"документ",
|
||||
"документы",
|
||||
"документов",
|
||||
"банковские",
|
||||
"операции",
|
||||
"платежи",
|
||||
"платеж",
|
||||
"платежи",
|
||||
"плс",
|
||||
"please"
|
||||
]);
|
||||
const ACCOUNT_ALIAS_MAP: Record<string, string[]> = {
|
||||
"51": ["расчетный счет", "расчетные счета", "bank account"],
|
||||
"52": ["валютный счет", "валютные счета", "currency account"],
|
||||
|
|
@ -152,6 +182,30 @@ function matchesAnchorText(searchable: string, anchor: string): boolean {
|
|||
});
|
||||
}
|
||||
|
||||
function isLikelyLowQualityPartyAnchor(value: string | null | undefined): boolean {
|
||||
const normalized = normalizeSearchText(String(value ?? ""));
|
||||
if (!normalized) {
|
||||
return true;
|
||||
}
|
||||
const tokens = normalized.split(" ").filter(Boolean);
|
||||
if (tokens.length === 0) {
|
||||
return true;
|
||||
}
|
||||
const meaningfulTokens = tokens.filter((token) => {
|
||||
if (token.length < 2) {
|
||||
return false;
|
||||
}
|
||||
if (PARTY_ANCHOR_STOPWORDS.has(token) || LOW_QUALITY_PARTY_ANCHOR_TOKENS.has(token)) {
|
||||
return false;
|
||||
}
|
||||
if (/^(?:19|20)\d{2}$/.test(token)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return meaningfulTokens.length === 0;
|
||||
}
|
||||
|
||||
function normalizeAccountToken(value: string): string {
|
||||
const source = String(value ?? "").trim().replace(",", ".");
|
||||
const match = source.match(/(\d{2})(?:\.(\d{1,2}))?/);
|
||||
|
|
@ -370,13 +424,13 @@ function applyAddressFilters(rows: NormalizedAddressRow[], filters: AddressFilte
|
|||
}
|
||||
|
||||
function applyIntentSpecificFilter(intent: AddressIntent, rows: NormalizedAddressRow[]): NormalizedAddressRow[] {
|
||||
if (intent === "bank_operations_by_counterparty") {
|
||||
if (intent === "bank_operations_by_counterparty" || intent === "bank_operations_by_contract") {
|
||||
const bankDocPattern =
|
||||
/(?:списаниесрасчетногосчета|поступлениенарасчетныйсчет|списание с расчетного счета|поступление на расчетный счет|bank|payment|wire|statement)/i;
|
||||
return rows.filter((row) => bankDocPattern.test(row.registrator.toLowerCase()));
|
||||
}
|
||||
|
||||
if (intent === "list_documents_by_counterparty") {
|
||||
if (intent === "list_documents_by_counterparty" || intent === "list_documents_by_contract") {
|
||||
const documentPattern =
|
||||
/(?:документ|реализац|поступлен|счет[-\s]?фактур|акт|накладн|payment|invoice|document|sale|purchase|bank)/i;
|
||||
return rows.filter((row) => documentPattern.test(row.registrator.toLowerCase()) || row.analytics.length > 0);
|
||||
|
|
@ -402,7 +456,21 @@ function canAutoBroadenPeriodWindow(intent: AddressIntent, filters: AddressFilte
|
|||
if (!hasExplicitPeriodWindow(filters)) {
|
||||
return false;
|
||||
}
|
||||
return intent === "list_documents_by_counterparty" || intent === "bank_operations_by_counterparty";
|
||||
return (
|
||||
intent === "list_documents_by_counterparty" ||
|
||||
intent === "bank_operations_by_counterparty" ||
|
||||
intent === "list_documents_by_contract" ||
|
||||
intent === "bank_operations_by_contract"
|
||||
);
|
||||
}
|
||||
|
||||
function isDocumentOrBankIntent(intent: AddressIntent): boolean {
|
||||
return (
|
||||
intent === "list_documents_by_counterparty" ||
|
||||
intent === "bank_operations_by_counterparty" ||
|
||||
intent === "list_documents_by_contract" ||
|
||||
intent === "bank_operations_by_contract"
|
||||
);
|
||||
}
|
||||
|
||||
function toIsoDatePrefix(value: string | null): string | null {
|
||||
|
|
@ -959,6 +1027,124 @@ export class AddressQueryService {
|
|||
});
|
||||
}
|
||||
|
||||
if (
|
||||
filteredRows.length === 0 &&
|
||||
isDocumentOrBankIntent(intent.intent) &&
|
||||
(stageStatus === "materialized_but_not_anchor_matched" || stageStatus === "materialized_but_filtered_out_by_recipe")
|
||||
) {
|
||||
const currentLimit =
|
||||
typeof filters.extracted_filters.limit === "number" && Number.isFinite(filters.extracted_filters.limit)
|
||||
? Math.max(1, Math.trunc(filters.extracted_filters.limit))
|
||||
: plan.limit;
|
||||
if (currentLimit < ADDRESS_ANCHOR_RECOVERY_LIMIT) {
|
||||
const expandedLimitFilters: AddressFilterSet = {
|
||||
...filters.extracted_filters,
|
||||
limit: ADDRESS_ANCHOR_RECOVERY_LIMIT
|
||||
};
|
||||
const expandedSelection = selectAddressRecipe(intent.intent, expandedLimitFilters);
|
||||
if (expandedSelection.selected_recipe && expandedSelection.missing_required_filters.length === 0) {
|
||||
const expandedPlan = buildAddressRecipePlan(expandedSelection.selected_recipe, expandedLimitFilters);
|
||||
if (expandedPlan.limit > currentLimit) {
|
||||
const expandedMcp = await executeAddressMcpQuery({
|
||||
query: expandedPlan.query,
|
||||
limit: expandedPlan.limit
|
||||
});
|
||||
if (!expandedMcp.error) {
|
||||
const expandedRawRows = toNormalizedRows(expandedMcp.raw_rows);
|
||||
const expandedScopedRows = applyAccountScopeFilter(expandedRawRows, expandedPlan.account_scope);
|
||||
const expandedAccountScopeFallbackApplied =
|
||||
expandedPlan.account_scope_mode === "preferred" &&
|
||||
expandedPlan.account_scope.length > 0 &&
|
||||
expandedRawRows.length > 0 &&
|
||||
expandedScopedRows.length === 0;
|
||||
const expandedNormalizedRows = expandedAccountScopeFallbackApplied ? expandedRawRows : expandedScopedRows;
|
||||
let expandedAnchor = resolvePrimaryAnchor(intent.intent, expandedLimitFilters);
|
||||
expandedAnchor = refineAnchorFromRows(expandedAnchor, expandedNormalizedRows);
|
||||
const expandedFiltersForMatching: AddressFilterSet =
|
||||
expandedAnchor.anchor_type === "counterparty" && expandedAnchor.anchor_value_resolved
|
||||
? { ...expandedLimitFilters, counterparty: expandedAnchor.anchor_value_resolved }
|
||||
: expandedAnchor.anchor_type === "contract" && expandedAnchor.anchor_value_resolved
|
||||
? { ...expandedLimitFilters, contract: expandedAnchor.anchor_value_resolved }
|
||||
: expandedLimitFilters;
|
||||
const expandedAccountScopeAudit = buildAccountScopeAudit({
|
||||
intent: intent.intent,
|
||||
filters: expandedFiltersForMatching,
|
||||
accountScope: expandedPlan.account_scope,
|
||||
rowsBeforeScope: expandedRawRows.length,
|
||||
rowsAfterScope: expandedNormalizedRows.length
|
||||
});
|
||||
const expandedAnchorFilter = applyAddressFilters(expandedNormalizedRows, expandedFiltersForMatching);
|
||||
const expandedRowsByAnchor = expandedAnchorFilter.rows;
|
||||
const expandedFilteredRows = applyIntentSpecificFilter(intent.intent, expandedRowsByAnchor);
|
||||
if (expandedFilteredRows.length > 0) {
|
||||
const expandedRowDiagnostics = deriveRowStageDiagnostics(
|
||||
expandedMcp.raw_rows,
|
||||
expandedNormalizedRows.length,
|
||||
expandedNormalizedRows.length
|
||||
);
|
||||
const expandedStageStatus = deriveMcpStageStatus({
|
||||
rawRowsReceived: expandedMcp.raw_rows.length,
|
||||
rowsMaterialized: expandedNormalizedRows.length,
|
||||
rowsAnchorMatched: expandedRowsByAnchor.length,
|
||||
rowsMatched: expandedFilteredRows.length
|
||||
});
|
||||
const expandedFactual = composeFactualReply(intent.intent, expandedFilteredRows);
|
||||
const expandedPrefix = `Период сохранен. Глубина live-выборки автоматически расширена до ${expandedPlan.limit} строк.`;
|
||||
const expandedLimitations = [...filters.warnings, "query_limit_auto_expanded_for_anchor_recovery"];
|
||||
const expandedReasons = [...baseReasons, "query_limit_auto_expanded_for_anchor_recovery"];
|
||||
return {
|
||||
handled: true,
|
||||
reply_text: `${expandedPrefix}\n${expandedFactual.text}`,
|
||||
reply_type: inferReplyType(expandedFactual.responseType),
|
||||
response_type: expandedFactual.responseType,
|
||||
debug: {
|
||||
detected_mode: mode.mode,
|
||||
detected_mode_confidence: mode.confidence,
|
||||
query_shape: shape.shape,
|
||||
query_shape_confidence: shape.confidence,
|
||||
detected_intent: intent.intent,
|
||||
detected_intent_confidence: intent.confidence,
|
||||
extracted_filters: filters.extracted_filters,
|
||||
missing_required_filters: [],
|
||||
selected_recipe: expandedSelection.selected_recipe.recipe_id,
|
||||
mcp_call_status_legacy: toLegacyMcpStatus(expandedStageStatus),
|
||||
account_scope_mode: expandedPlan.account_scope_mode,
|
||||
account_scope_fallback_applied: expandedAccountScopeFallbackApplied,
|
||||
anchor_type: expandedAnchor.anchor_type,
|
||||
anchor_value_raw: expandedAnchor.anchor_value_raw,
|
||||
anchor_value_resolved: expandedAnchor.anchor_value_resolved,
|
||||
resolver_confidence: expandedAnchor.resolver_confidence,
|
||||
ambiguity_count: expandedAnchor.ambiguity_count,
|
||||
match_failure_stage: "none",
|
||||
match_failure_reason: null,
|
||||
mcp_call_status: expandedStageStatus,
|
||||
rows_fetched: expandedMcp.fetched_rows,
|
||||
raw_rows_received: expandedMcp.raw_rows.length,
|
||||
rows_after_account_scope: expandedNormalizedRows.length,
|
||||
rows_after_recipe_filter: expandedRowsByAnchor.length,
|
||||
rows_materialized: expandedNormalizedRows.length,
|
||||
rows_matched: expandedFilteredRows.length,
|
||||
raw_row_keys_sample: expandedRowDiagnostics.rawRowKeysSample,
|
||||
materialization_drop_reason: expandedRowDiagnostics.materializationDropReason,
|
||||
account_token_raw: expandedAccountScopeAudit.accountTokenRaw,
|
||||
account_token_normalized: expandedAccountScopeAudit.accountTokenNormalized,
|
||||
account_scope_fields_checked: expandedAccountScopeAudit.accountScopeFieldsChecked,
|
||||
account_scope_match_strategy: expandedAccountScopeAudit.accountScopeMatchStrategy,
|
||||
account_scope_drop_reason: expandedAccountScopeAudit.accountScopeDropReason,
|
||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
||||
limited_reason_category: null,
|
||||
response_type: expandedFactual.responseType,
|
||||
limitations: expandedLimitations,
|
||||
reasons: expandedReasons
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredRows.length === 0 && canAutoBroadenPeriodWindow(intent.intent, filters.extracted_filters)) {
|
||||
const autoBroadenedFilters: AddressFilterSet = { ...filters.extracted_filters };
|
||||
delete autoBroadenedFilters.period_from;
|
||||
|
|
@ -1071,32 +1257,52 @@ export class AddressQueryService {
|
|||
const isVisibilityGapCandidate =
|
||||
hadBaseRows &&
|
||||
hadAnchorMatchedRows &&
|
||||
(intent.intent === "list_documents_by_counterparty" || intent.intent === "bank_operations_by_counterparty");
|
||||
(intent.intent === "list_documents_by_counterparty" ||
|
||||
intent.intent === "bank_operations_by_counterparty" ||
|
||||
intent.intent === "list_documents_by_contract" ||
|
||||
intent.intent === "bank_operations_by_contract");
|
||||
const isAnchorMismatch = stageStatus === "materialized_but_not_anchor_matched";
|
||||
const isRecipeFilteredOut = stageStatus === "materialized_but_filtered_out_by_recipe";
|
||||
const isFollowupAnchorCarryover =
|
||||
Array.isArray(filters.warnings) &&
|
||||
(filters.warnings.includes("counterparty_from_followup_context") ||
|
||||
filters.warnings.includes("contract_from_followup_context"));
|
||||
const isLowQualityPartyAnchor =
|
||||
(anchor.anchor_type === "counterparty" || anchor.anchor_type === "contract") &&
|
||||
isLikelyLowQualityPartyAnchor(anchor.anchor_value_raw);
|
||||
const anchorMismatchCategory: AddressLimitedReasonCategory =
|
||||
isFollowupAnchorCarryover || !isLowQualityPartyAnchor ? "empty_match" : "missing_anchor";
|
||||
const category: AddressLimitedReasonCategory = isAnchorMismatch
|
||||
? "missing_anchor"
|
||||
? anchorMismatchCategory
|
||||
: isRecipeFilteredOut
|
||||
? "recipe_visibility_gap"
|
||||
: isVisibilityGapCandidate
|
||||
? "recipe_visibility_gap"
|
||||
: "empty_match";
|
||||
const reasonText = isAnchorMismatch
|
||||
? anchorMismatchCategory === "missing_anchor"
|
||||
? "якорь контрагента/договора не найден в материализованных live-строках"
|
||||
: "по указанному якорю и фильтрам в live-выборке нет строк"
|
||||
: isRecipeFilteredOut
|
||||
? "строки по якорю найдены, но отфильтрованы intent-specific recipe"
|
||||
: isVisibilityGapCandidate
|
||||
? "в текущем live recipe нет достаточной document/bank видимости после фильтрации"
|
||||
: "по выбранным фильтрам в live-выборке нет строк";
|
||||
const nextStep = isAnchorMismatch
|
||||
? anchorMismatchCategory === "missing_anchor"
|
||||
? "уточните контрагента точным именем или добавьте ИНН/договор"
|
||||
: "уточните период или снимите часть фильтров"
|
||||
: isRecipeFilteredOut
|
||||
? "сузьте период, уточните контрагента или документный тип"
|
||||
: isVisibilityGapCandidate
|
||||
? "нужен специализированный recipe для document/bank контуров или более точный документный anchor"
|
||||
: "уточните период, контрагента, договор или снимите часть фильтров";
|
||||
const limitations = isAnchorMismatch
|
||||
? ["anchor_not_matched_after_materialization"]
|
||||
? [
|
||||
anchorMismatchCategory === "missing_anchor"
|
||||
? "anchor_not_matched_after_materialization"
|
||||
: "no_rows_for_anchor_after_materialization"
|
||||
]
|
||||
: isRecipeFilteredOut
|
||||
? ["rows_filtered_out_by_recipe_after_anchor_match"]
|
||||
: [
|
||||
|
|
|
|||
|
|
@ -108,6 +108,26 @@ const BASE_RECIPES: AddressRecipeDefinition[] = [
|
|||
account_scope_mode: "preferred",
|
||||
query_template: "bank_docs"
|
||||
},
|
||||
{
|
||||
recipe_id: "address_documents_by_contract_v1",
|
||||
intent: "list_documents_by_contract",
|
||||
purpose: "Collect contract-related document lines from movements",
|
||||
required_filters: ["contract"],
|
||||
optional_filters: ["period_from", "period_to", "as_of_date", "organization", "counterparty", "limit", "sort"],
|
||||
default_limit: 100,
|
||||
account_scope: ["60", "62", "76", "51", "52"],
|
||||
account_scope_mode: "preferred"
|
||||
},
|
||||
{
|
||||
recipe_id: "address_bank_operations_by_contract_v1",
|
||||
intent: "bank_operations_by_contract",
|
||||
purpose: "Collect bank operation candidates by contract from movement lines",
|
||||
required_filters: ["contract"],
|
||||
optional_filters: ["period_from", "period_to", "as_of_date", "organization", "counterparty", "limit", "sort"],
|
||||
default_limit: 100,
|
||||
account_scope: ["51", "52"],
|
||||
account_scope_mode: "preferred"
|
||||
},
|
||||
{
|
||||
recipe_id: "address_documents_forming_balance_v1",
|
||||
intent: "documents_forming_balance",
|
||||
|
|
@ -241,8 +261,10 @@ function buildMovementAccountCondition(filters: AddressFilterSet): string | null
|
|||
}
|
||||
|
||||
function shouldBoostLimitForAllTimeCounterparty(filters: AddressFilterSet): boolean {
|
||||
const hasCounterparty = typeof filters.counterparty === "string" && filters.counterparty.trim().length > 0;
|
||||
if (!hasCounterparty) {
|
||||
const hasAnchor =
|
||||
(typeof filters.counterparty === "string" && filters.counterparty.trim().length > 0) ||
|
||||
(typeof filters.contract === "string" && filters.contract.trim().length > 0);
|
||||
if (!hasAnchor) {
|
||||
return false;
|
||||
}
|
||||
const hasPeriod = Boolean(
|
||||
|
|
@ -254,7 +276,12 @@ function shouldBoostLimitForAllTimeCounterparty(filters: AddressFilterSet): bool
|
|||
}
|
||||
|
||||
function maxLimitForIntent(intent: AddressIntent): number {
|
||||
if (intent === "list_documents_by_counterparty" || intent === "bank_operations_by_counterparty") {
|
||||
if (
|
||||
intent === "list_documents_by_counterparty" ||
|
||||
intent === "bank_operations_by_counterparty" ||
|
||||
intent === "list_documents_by_contract" ||
|
||||
intent === "bank_operations_by_contract"
|
||||
) {
|
||||
return ADDRESS_MAX_LIMIT_EXTENDED;
|
||||
}
|
||||
return ADDRESS_MAX_LIMIT_DEFAULT;
|
||||
|
|
@ -292,7 +319,10 @@ export function buildAddressRecipePlan(
|
|||
? Math.max(1, Math.min(maxLimit, Math.trunc(filters.limit)))
|
||||
: recipe.default_limit;
|
||||
const boostedLimit =
|
||||
(recipe.intent === "list_documents_by_counterparty" || recipe.intent === "bank_operations_by_counterparty") &&
|
||||
(recipe.intent === "list_documents_by_counterparty" ||
|
||||
recipe.intent === "bank_operations_by_counterparty" ||
|
||||
recipe.intent === "list_documents_by_contract" ||
|
||||
recipe.intent === "bank_operations_by_contract") &&
|
||||
shouldBoostLimitForAllTimeCounterparty(filters)
|
||||
? Math.max(baseLimit, maxLimit)
|
||||
: (recipe.intent === "account_balance_snapshot" || recipe.intent === "documents_forming_balance") &&
|
||||
|
|
|
|||
|
|
@ -116,6 +116,18 @@ export function composeFactualReply(
|
|||
};
|
||||
}
|
||||
|
||||
if (intent === "list_documents_by_contract") {
|
||||
const lines = [
|
||||
"Собран список документов по договору (live address lane).",
|
||||
`Строк отобрано: ${rows.length}.`,
|
||||
...formatTopRows(rows, rows.length)
|
||||
];
|
||||
return {
|
||||
responseType: "FACTUAL_LIST",
|
||||
text: lines.join("\n")
|
||||
};
|
||||
}
|
||||
|
||||
if (intent === "bank_operations_by_counterparty") {
|
||||
const lines = [
|
||||
"Собран список банковских операций по контрагенту (live address lane).",
|
||||
|
|
@ -128,6 +140,18 @@ export function composeFactualReply(
|
|||
};
|
||||
}
|
||||
|
||||
if (intent === "bank_operations_by_contract") {
|
||||
const lines = [
|
||||
"Собран список банковских операций по договору (live address lane).",
|
||||
`Строк отобрано: ${rows.length}.`,
|
||||
...formatTopRows(rows, rows.length)
|
||||
];
|
||||
return {
|
||||
responseType: "FACTUAL_LIST",
|
||||
text: lines.join("\n")
|
||||
};
|
||||
}
|
||||
|
||||
const title =
|
||||
intent === "list_payables_counterparties"
|
||||
? "Срез обязательств (payables) собран по движениям с account scope 60/76."
|
||||
|
|
|
|||
|
|
@ -119,6 +119,16 @@ function mergeFollowupFilters(
|
|||
}
|
||||
}
|
||||
|
||||
if (intent === "list_documents_by_contract" || intent === "bank_operations_by_contract") {
|
||||
if (!toNonEmptyString(merged.contract)) {
|
||||
const inheritedContract = previousContract ?? (followupContext.previous_anchor_type === "contract" ? previousAnchorValue : null);
|
||||
if (inheritedContract) {
|
||||
merged.contract = inheritedContract;
|
||||
reasons.push("contract_from_followup_context");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (intent === "account_balance_snapshot" || intent === "documents_forming_balance") {
|
||||
if (!toNonEmptyString(merged.account)) {
|
||||
const inheritedAccount = previousAccount ?? (followupContext.previous_anchor_type === "account" ? previousAnchorValue : null);
|
||||
|
|
@ -184,7 +194,9 @@ function resolveMissingRequiredFilters(intent: AddressIntent, filters: AddressFi
|
|||
account_balance_snapshot: ["account", "as_of_date"],
|
||||
documents_forming_balance: ["account", "as_of_date"],
|
||||
list_documents_by_counterparty: ["counterparty"],
|
||||
bank_operations_by_counterparty: ["counterparty"]
|
||||
bank_operations_by_counterparty: ["counterparty"],
|
||||
list_documents_by_contract: ["contract"],
|
||||
bank_operations_by_contract: ["contract"]
|
||||
};
|
||||
const required = requiredByIntent[intent] ?? [];
|
||||
return required.filter((key) => {
|
||||
|
|
|
|||
|
|
@ -134,6 +134,18 @@ export function resolvePrimaryAnchor(intent: AddressIntent, filters: AddressFilt
|
|||
}
|
||||
}
|
||||
|
||||
if (intent === "list_documents_by_contract" || intent === "bank_operations_by_contract") {
|
||||
if (contract) {
|
||||
return {
|
||||
anchor_type: "contract",
|
||||
anchor_value_raw: contract,
|
||||
anchor_value_resolved: contract,
|
||||
resolver_confidence: "medium",
|
||||
ambiguity_count: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (counterparty) {
|
||||
return {
|
||||
anchor_type: "counterparty",
|
||||
|
|
@ -208,4 +220,3 @@ export function refineAnchorFromRows(anchor: AnchorResolutionDebug, rows: Resolv
|
|||
ambiguity_count: candidates.length - 1
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1828,9 +1828,24 @@ const ADDRESS_PREDECOMPOSE_NOISE_TOKENS = new Set([
|
|||
"show",
|
||||
"list",
|
||||
"выведи",
|
||||
"что",
|
||||
"чо",
|
||||
"которые",
|
||||
"какие",
|
||||
"какой",
|
||||
"были",
|
||||
"был",
|
||||
"была",
|
||||
"было",
|
||||
"ли",
|
||||
"списания",
|
||||
"списание",
|
||||
"поступления",
|
||||
"поступление",
|
||||
"расчетного",
|
||||
"расчётного",
|
||||
"счета",
|
||||
"счёта",
|
||||
"есть",
|
||||
"est",
|
||||
"kakie",
|
||||
|
|
@ -1910,6 +1925,8 @@ const ADDRESS_MONTH_ALIAS_MAP = {
|
|||
dec: "12"
|
||||
};
|
||||
const ADDRESS_DOCS_SIGNAL_PATTERN = /(?:док|доки|документ|документы|документов|docs?|documents?|bank|выписк|плат[её]ж|оплат|поступлен|списан|операц)/i;
|
||||
const ADDRESS_BANK_SIGNAL_PATTERN = /(?:bank|банк|банков|выписк|плат[её]ж|оплат|поступлен|списан|операц|расчетн)/i;
|
||||
const ADDRESS_CONTRACT_SIGNAL_PATTERN = /(?:договор(?:а|у|ом|е)?|\bcontract\b)/iu;
|
||||
const ADDRESS_BALANCE_SIGNAL_PATTERN = /(?:остат|сальдо|баланс|взаиморасч|долг|saldo|balance)/i;
|
||||
const ADDRESS_ALL_TIME_PATTERN = /(?:за\s+вс[её]\s+время|за\s+весь\s+период|за\s+всю\s+истори(?:ю|и)|for\s+all\s+time|all\s+time|entire\s+period|full\s+history)/iu;
|
||||
function normalizeAddressMonthAliasToken(token) {
|
||||
|
|
@ -1975,14 +1992,21 @@ function extractAddressFallbackYear(text) {
|
|||
return fullYearMatch[1];
|
||||
}
|
||||
const shortYearMatch = source.match(/(?:^|[^0-9])(\d{2})\s*(?:г(?:од|ода)?|г|год|year|god)(?=$|[^a-zа-яё0-9])/iu);
|
||||
if (!shortYearMatch) {
|
||||
return null;
|
||||
}
|
||||
if (shortYearMatch) {
|
||||
const shortYear = Number(shortYearMatch[1]);
|
||||
if (!Number.isFinite(shortYear) || shortYear < 0 || shortYear > 99) {
|
||||
if (Number.isFinite(shortYear) && shortYear >= 0 && shortYear <= 99) {
|
||||
return String(2000 + shortYear);
|
||||
}
|
||||
}
|
||||
const shortOrdinalYearMatch = source.match(/(?:^|[^0-9])(\d{2})\s*(?:[-\s]?(?:й|ый|ой|th))(?=$|[^a-zа-яё0-9])/iu);
|
||||
if (!shortOrdinalYearMatch) {
|
||||
return null;
|
||||
}
|
||||
return String(2000 + shortYear);
|
||||
const shortOrdinalYear = Number(shortOrdinalYearMatch[1]);
|
||||
if (!Number.isFinite(shortOrdinalYear) || shortOrdinalYear < 0 || shortOrdinalYear > 99) {
|
||||
return null;
|
||||
}
|
||||
return String(2000 + shortOrdinalYear);
|
||||
}
|
||||
function extractAddressFallbackMonthYear(text) {
|
||||
const source = String(text ?? "");
|
||||
|
|
@ -1998,14 +2022,14 @@ function extractAddressFallbackMonthYear(text) {
|
|||
const year = numericMonthYear[2];
|
||||
return `${year}-${month}`;
|
||||
}
|
||||
const namedMonthYear = source.match(/\b([a-zа-яё]+)\s+(20\d{2})\b/iu);
|
||||
const namedMonthYear = source.match(/(?:^|[^a-zа-яё0-9])([a-zа-яё]+)\s+(20\d{2})(?=$|[^a-zа-яё0-9])/iu);
|
||||
if (namedMonthYear) {
|
||||
const month = normalizeAddressMonthAliasToken(namedMonthYear[1]);
|
||||
if (month) {
|
||||
return `${namedMonthYear[2]}-${month}`;
|
||||
}
|
||||
}
|
||||
const yearNamedMonth = source.match(/\b(20\d{2})\s+([a-zа-яё]+)\b/iu);
|
||||
const yearNamedMonth = source.match(/(?:^|[^a-zа-яё0-9])(20\d{2})\s+([a-zа-яё]+)(?=$|[^a-zа-яё0-9])/iu);
|
||||
if (yearNamedMonth) {
|
||||
const month = normalizeAddressMonthAliasToken(yearNamedMonth[2]);
|
||||
if (month) {
|
||||
|
|
@ -2014,7 +2038,42 @@ function extractAddressFallbackMonthYear(text) {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
function extractAddressFallbackAccountToken(text) {
|
||||
const source = String(text ?? "");
|
||||
const explicitMatch = source.match(/(?:сч[её]т(?:а|у|ом|е)?|account)\D{0,12}(\d{2}(?:[.,]\d{1,2})?)/iu);
|
||||
if (explicitMatch && explicitMatch[1]) {
|
||||
return String(explicitMatch[1]).replace(",", ".");
|
||||
}
|
||||
const tokenPattern = /\b(\d{2}(?:[.,]\d{1,2})?)\b/giu;
|
||||
let match = tokenPattern.exec(source);
|
||||
while (match) {
|
||||
const raw = String(match[1] ?? "");
|
||||
const start = match.index;
|
||||
const end = start + raw.length;
|
||||
const prev = start > 0 ? source[start - 1] : " ";
|
||||
const next = end < source.length ? source[end] : " ";
|
||||
if (!/[./-]/.test(prev) && !/[./-]/.test(next) && !/\d/.test(prev) && !/\d/.test(next)) {
|
||||
return raw.replace(",", ".");
|
||||
}
|
||||
match = tokenPattern.exec(source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function pickAddressFallbackCounterpartyToken(text) {
|
||||
const source = String(text ?? "");
|
||||
const byAnchor = source.match(/(?:^|[\s,.;:!?()\-])(?:по|от)\s+([a-zа-яё][a-zа-яё0-9._-]{1,})(?=$|[\s,.;:!?()\-])/iu);
|
||||
if (byAnchor && byAnchor[1]) {
|
||||
const byToken = String(byAnchor[1]).trim();
|
||||
const normalizedByToken = byToken.toLowerCase();
|
||||
if (byToken &&
|
||||
!ADDRESS_PREDECOMPOSE_NOISE_TOKENS.has(normalizedByToken) &&
|
||||
!/^\d{2}(?:\.\d{1,2})?$/.test(normalizedByToken) &&
|
||||
!/^(?:19|20)\d{2}$/.test(normalizedByToken) &&
|
||||
!/^(?:янв|фев|мар|апр|май|июн|июл|авг|сен|сент|окт|ноя|дек|january|february|march|april|may|june|july|august|september|october|november|december)/i.test(normalizedByToken) &&
|
||||
!/^(?:договор|договора|договору|договором|договоре|contract)$/.test(normalizedByToken)) {
|
||||
return byToken;
|
||||
}
|
||||
}
|
||||
const candidates = extractAddressAnchorTokens(text);
|
||||
for (const token of candidates) {
|
||||
const normalized = String(token ?? "").toLowerCase();
|
||||
|
|
@ -2027,10 +2086,44 @@ function pickAddressFallbackCounterpartyToken(text) {
|
|||
if (/^(?:янв|фев|мар|апр|май|июн|июл|авг|сен|сент|окт|ноя|дек|january|february|march|april|may|june|july|august|september|october|november|december)/i.test(normalized)) {
|
||||
continue;
|
||||
}
|
||||
if (/^(?:договор|договора|договору|договором|договоре|contract)$/.test(normalized)) {
|
||||
continue;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function extractAddressFallbackContractToken(text) {
|
||||
const source = String(text ?? "");
|
||||
const patterns = [
|
||||
/(?:договор(?:а|у|ом|е)?|\bcontract\b)\s*(?:№|#|n|no\.?)?\s*([a-zа-я0-9][a-zа-я0-9/_-]{1,})/iu,
|
||||
/(?:№|#|n|no\.?)\s*([a-zа-я0-9][a-zа-я0-9/_-]{1,})\s*(?:договор(?:а|у|ом|е)?|\bcontract\b)/iu
|
||||
];
|
||||
for (const pattern of patterns) {
|
||||
const match = pattern.exec(source);
|
||||
if (!match || !match[1]) {
|
||||
continue;
|
||||
}
|
||||
const candidate = String(match[1]).replace(/^[^a-zа-я0-9]+|[^a-zа-я0-9/_-]+$/giu, "");
|
||||
if (!candidate || candidate.length < 2) {
|
||||
continue;
|
||||
}
|
||||
if (/^(?:19|20)\d{2}$/.test(candidate)) {
|
||||
continue;
|
||||
}
|
||||
if (/^(?:19|20)\d{2}[./-](?:0?[1-9]|1[0-2])(?:[./-](?:0?[1-9]|[12]\d|3[01]))?$/.test(candidate)) {
|
||||
continue;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
if (ADDRESS_CONTRACT_SIGNAL_PATTERN.test(source)) {
|
||||
const generic = source.match(/\b([a-zа-я0-9]{1,10}[/-][a-zа-я0-9]{1,10}(?:[/-][a-zа-я0-9]{1,10})?)\b/iu);
|
||||
if (generic && generic[1]) {
|
||||
return generic[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function resolveAddressDeterministicFallback(userMessage, sanitizedUserMessage) {
|
||||
const sourceRaw = compactWhitespace(repairAddressMojibake(String(userMessage ?? "")));
|
||||
const source = compactWhitespace(String(sanitizedUserMessage ?? sourceRaw).toLowerCase());
|
||||
|
|
@ -2040,9 +2133,10 @@ function resolveAddressDeterministicFallback(userMessage, sanitizedUserMessage)
|
|||
const monthYear = extractAddressFallbackMonthYear(source);
|
||||
const year = extractAddressFallbackYear(source);
|
||||
const allTime = ADDRESS_ALL_TIME_PATTERN.test(source);
|
||||
const accountMatch = source.match(/\b(\d{2}(?:[.,]\d{1,2})?)\b/);
|
||||
const account = accountMatch ? String(accountMatch[1]).replace(",", ".") : null;
|
||||
const account = extractAddressFallbackAccountToken(source);
|
||||
const docsSignal = ADDRESS_DOCS_SIGNAL_PATTERN.test(source);
|
||||
const bankSignal = ADDRESS_BANK_SIGNAL_PATTERN.test(source);
|
||||
const contractSignal = ADDRESS_CONTRACT_SIGNAL_PATTERN.test(source);
|
||||
const balanceSignal = ADDRESS_BALANCE_SIGNAL_PATTERN.test(source);
|
||||
if (balanceSignal && account) {
|
||||
let periodClause = "";
|
||||
|
|
@ -2064,23 +2158,25 @@ function resolveAddressDeterministicFallback(userMessage, sanitizedUserMessage)
|
|||
}
|
||||
}
|
||||
if (docsSignal) {
|
||||
const counterparty = pickAddressFallbackCounterpartyToken(source);
|
||||
if (counterparty) {
|
||||
if (contractSignal) {
|
||||
const contract = extractAddressFallbackContractToken(sourceRaw || source);
|
||||
if (contract) {
|
||||
let periodClause = "";
|
||||
let rule = "documents_counterparty_rewrite";
|
||||
let rule = bankSignal ? "bank_operations_contract_rewrite" : "documents_contract_rewrite";
|
||||
if (allTime) {
|
||||
periodClause = " за все время";
|
||||
rule = "documents_counterparty_all_time_rewrite";
|
||||
rule = bankSignal ? "bank_operations_contract_all_time_rewrite" : "documents_contract_all_time_rewrite";
|
||||
}
|
||||
else if (monthYear) {
|
||||
periodClause = ` за ${monthYear}`;
|
||||
rule = "documents_counterparty_month_rewrite";
|
||||
rule = bankSignal ? "bank_operations_contract_month_rewrite" : "documents_contract_month_rewrite";
|
||||
}
|
||||
else if (year) {
|
||||
periodClause = ` за ${year} год`;
|
||||
rule = "documents_counterparty_year_rewrite";
|
||||
rule = bankSignal ? "bank_operations_contract_year_rewrite" : "documents_contract_year_rewrite";
|
||||
}
|
||||
const candidate = compactWhitespace(`документы по контрагенту ${counterparty}${periodClause}`);
|
||||
const subject = bankSignal ? "банковские операции" : "документы";
|
||||
const candidate = compactWhitespace(`${subject} по договору ${contract}${periodClause}`);
|
||||
if (candidate && candidate !== sourceRaw.toLowerCase()) {
|
||||
return {
|
||||
candidate,
|
||||
|
|
@ -2089,6 +2185,35 @@ function resolveAddressDeterministicFallback(userMessage, sanitizedUserMessage)
|
|||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const counterparty = pickAddressFallbackCounterpartyToken(source);
|
||||
if (counterparty) {
|
||||
let periodClause = "";
|
||||
const subject = bankSignal ? "банковские операции" : "документы";
|
||||
const rulePrefix = bankSignal ? "bank_operations_counterparty" : "documents_counterparty";
|
||||
let rule = `${rulePrefix}_rewrite`;
|
||||
if (allTime) {
|
||||
periodClause = " за все время";
|
||||
rule = `${rulePrefix}_all_time_rewrite`;
|
||||
}
|
||||
else if (monthYear) {
|
||||
periodClause = ` за ${monthYear}`;
|
||||
rule = `${rulePrefix}_month_rewrite`;
|
||||
}
|
||||
else if (year) {
|
||||
periodClause = ` за ${year} год`;
|
||||
rule = `${rulePrefix}_year_rewrite`;
|
||||
}
|
||||
const candidate = compactWhitespace(`${subject} по контрагенту ${counterparty}${periodClause}`);
|
||||
if (candidate && candidate !== sourceRaw.toLowerCase()) {
|
||||
return {
|
||||
candidate,
|
||||
rule
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (source !== sourceRaw.toLowerCase() && isAddressLlmPreDecomposeCandidate(source)) {
|
||||
return {
|
||||
candidate: source,
|
||||
|
|
@ -2620,12 +2745,13 @@ export class AssistantService {
|
|||
};
|
||||
this.sessions.appendItem(sessionId, userItem);
|
||||
const finalizeAddressLaneResponse = (addressLane, effectiveAddressUserMessage, carryoverMeta = null, llmPreDecomposeMeta = null) => {
|
||||
const safeAddressReply = String((0, answerComposer_1.sanitizeAssistantReplyForUserFacing)(addressLane.reply_text) ?? "").trim() || String(addressLane.reply_text ?? "");
|
||||
const debug = buildAddressDebugPayload(addressLane.debug, llmPreDecomposeMeta);
|
||||
const assistantItem = {
|
||||
message_id: `msg-${(0, nanoid_1.nanoid)(10)}`,
|
||||
session_id: sessionId,
|
||||
role: "assistant",
|
||||
text: addressLane.reply_text,
|
||||
text: safeAddressReply,
|
||||
reply_type: addressLane.reply_type,
|
||||
created_at: new Date().toISOString(),
|
||||
trace_id: debug.trace_id,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ export type AddressIntent =
|
|||
| "open_items_by_counterparty_or_contract"
|
||||
| "list_documents_by_counterparty"
|
||||
| "bank_operations_by_counterparty"
|
||||
| "list_documents_by_contract"
|
||||
| "bank_operations_by_contract"
|
||||
| "documents_forming_balance"
|
||||
| "unknown";
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { extractAddressFilters } from "../src/services/addressFilterExtractor";
|
|||
import { AddressQueryService } from "../src/services/addressQueryService";
|
||||
import { buildAddressRecipePlan, selectAddressRecipe } from "../src/services/addressRecipeCatalog";
|
||||
import { runAddressDecomposeStage } from "../src/services/address_runtime/decomposeStage";
|
||||
import { composeFactualReply } from "../src/services/address_runtime/composeStage";
|
||||
|
||||
describe("address query shape classifier", () => {
|
||||
it("classifies explain question as deep-shape", () => {
|
||||
|
|
@ -35,6 +36,36 @@ describe("address query shape classifier", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("address compose stage utf8 headers", () => {
|
||||
it("renders readable russian header for contract document list", () => {
|
||||
const reply = composeFactualReply("list_documents_by_contract", [
|
||||
{
|
||||
period: "2020-10-15T13:34:53Z",
|
||||
registrator: "Списание с расчетного счета 00000000246",
|
||||
account_dt: "66.02",
|
||||
account_kt: "51",
|
||||
amount: 30819.47,
|
||||
analytics: []
|
||||
}
|
||||
]);
|
||||
expect(reply.text).toContain("Собран список документов по договору (live address lane).");
|
||||
});
|
||||
|
||||
it("renders readable russian header for contract bank operations", () => {
|
||||
const reply = composeFactualReply("bank_operations_by_contract", [
|
||||
{
|
||||
period: "2020-10-15T13:34:53Z",
|
||||
registrator: "Списание с расчетного счета 00000000246",
|
||||
account_dt: "66.02",
|
||||
account_kt: "51",
|
||||
amount: 30819.47,
|
||||
analytics: []
|
||||
}
|
||||
]);
|
||||
expect(reply.text).toContain("Собран список банковских операций по договору (live address lane).");
|
||||
});
|
||||
});
|
||||
|
||||
describe("address intent resolver expansion (M2.3a)", () => {
|
||||
it("resolves documents by counterparty intent", () => {
|
||||
const result = resolveAddressIntent("show documents by counterparty Alfa from 2020-07-01 to 2020-07-31");
|
||||
|
|
@ -66,6 +97,23 @@ describe("address intent resolver expansion (M2.3a)", () => {
|
|||
expect(result.intent).toBe("bank_operations_by_counterparty");
|
||||
});
|
||||
|
||||
it("resolves documents by contract intent", () => {
|
||||
const result = resolveAddressIntent("Покажи документы по договору 15/24 за 2020");
|
||||
expect(result.intent).toBe("list_documents_by_contract");
|
||||
});
|
||||
|
||||
it("resolves bank operations by contract intent", () => {
|
||||
const result = resolveAddressIntent("Покажи банковские операции по договору 15/24");
|
||||
expect(result.intent).toBe("bank_operations_by_contract");
|
||||
});
|
||||
|
||||
it("resolves bank operations by contract for normalized phrase with linked contract wording", () => {
|
||||
const result = resolveAddressIntent(
|
||||
"Показать банковские операции (счета 51, 60, 62) связанные с договором 15/24."
|
||||
);
|
||||
expect(result.intent).toBe("bank_operations_by_contract");
|
||||
});
|
||||
|
||||
it("keeps bank_operations_by_counterparty even when account hints are present", () => {
|
||||
const result = resolveAddressIntent("Показать банковские операции (счета 51, 62) для контрагента СВК за 2020 год");
|
||||
expect(result.intent).toBe("bank_operations_by_counterparty");
|
||||
|
|
@ -287,6 +335,49 @@ describe("address filter extraction for balance drilldown", () => {
|
|||
expect(result.warnings).toContain("period_derived_from_year_range_phrase");
|
||||
});
|
||||
|
||||
it("extracts contract and year period for contract document list", () => {
|
||||
const result = extractAddressFilters(
|
||||
"Покажи документы по договору 15/24 за 2020 год",
|
||||
"list_documents_by_contract"
|
||||
);
|
||||
expect(result.extracted_filters.contract).toBe("15/24");
|
||||
expect(result.extracted_filters.period_from).toBe("2020-01-01");
|
||||
expect(result.extracted_filters.period_to).toBe("2020-12-31");
|
||||
expect(result.warnings).toContain("period_derived_from_year_phrase");
|
||||
});
|
||||
|
||||
it("cuts trailing as-of date from contract anchor", () => {
|
||||
const result = extractAddressFilters(
|
||||
"Покажи документы по договору 1-ПМ/2020 на дату 31.07.2020",
|
||||
"list_documents_by_contract"
|
||||
);
|
||||
expect(result.extracted_filters.contract).toBe("1-ПМ/2020");
|
||||
expect(result.extracted_filters.period_from).toBe("2020-07-01");
|
||||
expect(result.extracted_filters.period_to).toBe("2020-07-31");
|
||||
});
|
||||
|
||||
it("does not force 90-day default window for by-contract query without explicit period", () => {
|
||||
const result = extractAddressFilters(
|
||||
"Покажи документы по договору 15/24",
|
||||
"list_documents_by_contract"
|
||||
);
|
||||
expect(result.extracted_filters.contract).toBe("15/24");
|
||||
expect(result.extracted_filters.period_from).toBeUndefined();
|
||||
expect(result.extracted_filters.period_to).toBeUndefined();
|
||||
expect(result.warnings).not.toContain("period_defaulted_last_90_days");
|
||||
});
|
||||
|
||||
it("extracts heuristic contract token for noisy contract phrase", () => {
|
||||
const result = extractAddressFilters(
|
||||
"доки 15/24 за 2020",
|
||||
"list_documents_by_contract"
|
||||
);
|
||||
expect(result.extracted_filters.contract).toBe("15/24");
|
||||
expect(result.warnings).toContain("contract_anchor_derived_from_heuristic_token");
|
||||
expect(result.extracted_filters.period_from).toBe("2020-01-01");
|
||||
expect(result.extracted_filters.period_to).toBe("2020-12-31");
|
||||
});
|
||||
|
||||
it("extracts multiline year range period from phrase", () => {
|
||||
const result = extractAddressFilters(
|
||||
"Какие документы по СВК за 2000 - 2025\n год?",
|
||||
|
|
@ -334,13 +425,24 @@ describe("address query limited taxonomy and stage diagnostics", () => {
|
|||
expect(result?.debug.mcp_call_status).toBe("skipped");
|
||||
});
|
||||
|
||||
it("returns unsupported for not-implemented contract document list intent", async () => {
|
||||
it("routes contract document list intent into address recipe", async () => {
|
||||
const service = new AddressQueryService();
|
||||
const result = await service.tryHandle("show documents by contract 15/24");
|
||||
expect(result?.handled).toBe(true);
|
||||
expect(result?.response_type).toBe("LIMITED_WITH_REASON");
|
||||
expect(result?.debug.limited_reason_category).toBe("unsupported");
|
||||
expect(result?.debug.mcp_call_status).toBe("skipped");
|
||||
expect(result?.debug.detected_intent).toBe("list_documents_by_contract");
|
||||
expect(result?.debug.selected_recipe).toBe("address_documents_by_contract_v1");
|
||||
expect(result?.debug.limited_reason_category).not.toBe("unsupported");
|
||||
expect(result?.debug.mcp_call_status).not.toBe("skipped");
|
||||
});
|
||||
|
||||
it("routes bank operations by contract intent into address recipe", async () => {
|
||||
const service = new AddressQueryService();
|
||||
const result = await service.tryHandle("Покажи банковские операции по договору 15/24");
|
||||
expect(result?.handled).toBe(true);
|
||||
expect(result?.debug.detected_intent).toBe("bank_operations_by_contract");
|
||||
expect(result?.debug.selected_recipe).toBe("address_bank_operations_by_contract_v1");
|
||||
expect(result?.debug.limited_reason_category).not.toBe("unsupported");
|
||||
expect(result?.debug.mcp_call_status).not.toBe("skipped");
|
||||
});
|
||||
|
||||
it("includes resolver and row-stage diagnostics", async () => {
|
||||
|
|
@ -461,6 +563,24 @@ describe("address decompose stage follow-up carryover", () => {
|
|||
expect(result?.baseReasons).toContain("as_of_date_from_followup_context");
|
||||
expect(result?.baseReasons).toContain("address_followup_context_applied");
|
||||
});
|
||||
|
||||
it("does not downgrade inherited follow-up anchor to missing_anchor when period has no rows", async () => {
|
||||
const service = new AddressQueryService();
|
||||
const seed = await service.tryHandle("покажи документы по свк за 2020");
|
||||
expect(seed?.handled).toBe(true);
|
||||
const followup = await service.tryHandle("а теперь только за май 2020", {
|
||||
followupContext: {
|
||||
previous_intent: (seed?.debug.detected_intent as any) ?? "list_documents_by_counterparty",
|
||||
previous_filters: seed?.debug.extracted_filters,
|
||||
previous_anchor_type: (seed?.debug.anchor_type as any) ?? "counterparty",
|
||||
previous_anchor_value: seed?.debug.anchor_value_resolved ?? seed?.debug.anchor_value_raw ?? null
|
||||
}
|
||||
});
|
||||
expect(followup?.handled).toBe(true);
|
||||
if (followup?.reply_type === "partial_coverage") {
|
||||
expect(followup?.debug.limited_reason_category).not.toBe("missing_anchor");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("address recipe catalog counterparty filtering", () => {
|
||||
|
|
|
|||
|
|
@ -215,4 +215,391 @@ describe("assistant address llm pre-decompose candidate preference", () => {
|
|||
expect(response.debug?.sanitized_user_message).toContain("свк");
|
||||
expect(response.debug?.tool_gate_decision).toBe("run_address_lane");
|
||||
});
|
||||
|
||||
it("keeps contract anchor in deterministic fallback when llm output is unusable", async () => {
|
||||
const calls: Array<{ message: string }> = [];
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn(async (message: string) => {
|
||||
calls.push({ message });
|
||||
return buildAddressLaneResult(message);
|
||||
})
|
||||
} as any;
|
||||
|
||||
const normalizerService = {
|
||||
normalize: vi.fn(async () => ({
|
||||
trace_id: "norm-predecompose-contract-docs",
|
||||
ok: true,
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
user_message_raw: "Покажи документы по договору 15/24 за 2020",
|
||||
message_in_scope: true,
|
||||
scope_confidence: "medium",
|
||||
contains_multiple_tasks: false,
|
||||
fragments: []
|
||||
},
|
||||
raw_model_output: null,
|
||||
validation: { passed: true, errors: [] },
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
latency_ms: 10,
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
request_count_for_case: 1
|
||||
}))
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const service = new AssistantService(
|
||||
normalizerService,
|
||||
sessions as any,
|
||||
{} as any,
|
||||
{ persistSession: vi.fn() } as any,
|
||||
addressQueryService
|
||||
);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: `asst-predecompose-contract-docs-${Date.now()}`,
|
||||
user_message: "Покажи документы по договору 15/24 за 2020",
|
||||
llmProvider: "local",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual");
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0].message).toBe("документы по договору 15/24 за 2020 год");
|
||||
expect(response.debug?.llm_decomposition_applied).toBe(true);
|
||||
expect(response.debug?.llm_decomposition_reason).toBe("fallback_rule_applied_after_llm");
|
||||
expect(response.debug?.fallback_rule_hit).toBe("documents_contract_year_rewrite");
|
||||
});
|
||||
|
||||
it("keeps bank-by-contract intent in deterministic fallback when llm output is unusable", async () => {
|
||||
const calls: Array<{ message: string }> = [];
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn(async (message: string) => {
|
||||
calls.push({ message });
|
||||
return buildAddressLaneResult(message);
|
||||
})
|
||||
} as any;
|
||||
|
||||
const normalizerService = {
|
||||
normalize: vi.fn(async () => ({
|
||||
trace_id: "norm-predecompose-contract-bank",
|
||||
ok: true,
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
user_message_raw: "Покажи банковские операции по договору 15/24",
|
||||
message_in_scope: true,
|
||||
scope_confidence: "medium",
|
||||
contains_multiple_tasks: false,
|
||||
fragments: []
|
||||
},
|
||||
raw_model_output: null,
|
||||
validation: { passed: true, errors: [] },
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
latency_ms: 10,
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
request_count_for_case: 1
|
||||
}))
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const service = new AssistantService(
|
||||
normalizerService,
|
||||
sessions as any,
|
||||
{} as any,
|
||||
{ persistSession: vi.fn() } as any,
|
||||
addressQueryService
|
||||
);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: `asst-predecompose-contract-bank-${Date.now()}`,
|
||||
user_message: "Покажи банковские операции по договору 15/24",
|
||||
llmProvider: "local",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual");
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0].message).toBe("банковские операции по договору 15/24");
|
||||
expect(response.debug?.llm_decomposition_applied).toBe(true);
|
||||
expect(response.debug?.llm_decomposition_reason).toBe("fallback_rule_applied_after_llm");
|
||||
expect(response.debug?.fallback_rule_hit).toBe("bank_operations_contract_rewrite");
|
||||
});
|
||||
|
||||
it("keeps month scope for balance fallback in 'year month' phrasing", async () => {
|
||||
const calls: Array<{ message: string }> = [];
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn(async (message: string) => {
|
||||
calls.push({ message });
|
||||
return buildAddressLaneResult(message);
|
||||
})
|
||||
} as any;
|
||||
|
||||
const normalizerService = {
|
||||
normalize: vi.fn(async () => ({
|
||||
trace_id: "norm-predecompose-balance-year-month",
|
||||
ok: true,
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
user_message_raw: "Какой остаток по счету 60 на 2020 май",
|
||||
message_in_scope: true,
|
||||
scope_confidence: "medium",
|
||||
contains_multiple_tasks: false,
|
||||
fragments: []
|
||||
},
|
||||
raw_model_output: null,
|
||||
validation: { passed: true, errors: [] },
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
latency_ms: 10,
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
request_count_for_case: 1
|
||||
}))
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const service = new AssistantService(
|
||||
normalizerService,
|
||||
sessions as any,
|
||||
{} as any,
|
||||
{ persistSession: vi.fn() } as any,
|
||||
addressQueryService
|
||||
);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: `asst-predecompose-balance-year-month-${Date.now()}`,
|
||||
user_message: "Какой остаток по счету 60 на 2020 май",
|
||||
llmProvider: "local",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual");
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0].message).toBe("остаток по счету 60 на 2020-05");
|
||||
expect(response.debug?.llm_decomposition_applied).toBe(true);
|
||||
expect(response.debug?.llm_decomposition_reason).toBe("fallback_rule_applied_after_llm");
|
||||
expect(response.debug?.fallback_rule_hit).toBe("balance_month_period_rewrite");
|
||||
});
|
||||
|
||||
it("does not pick service words as counterparty anchor in noisy docs query", async () => {
|
||||
const calls: Array<{ message: string }> = [];
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn(async (message: string) => {
|
||||
calls.push({ message });
|
||||
return buildAddressLaneResult(message);
|
||||
})
|
||||
} as any;
|
||||
|
||||
const normalizerService = {
|
||||
normalize: vi.fn(async () => ({
|
||||
trace_id: "norm-predecompose-noisy-counterparty",
|
||||
ok: true,
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
user_message_raw: "что по свк за 2020 год выведи все доки плиз что есть",
|
||||
message_in_scope: true,
|
||||
scope_confidence: "medium",
|
||||
contains_multiple_tasks: false,
|
||||
fragments: []
|
||||
},
|
||||
raw_model_output: null,
|
||||
validation: { passed: true, errors: [] },
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
latency_ms: 10,
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
request_count_for_case: 1
|
||||
}))
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const service = new AssistantService(
|
||||
normalizerService,
|
||||
sessions as any,
|
||||
{} as any,
|
||||
{ persistSession: vi.fn() } as any,
|
||||
addressQueryService
|
||||
);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: `asst-predecompose-noisy-counterparty-${Date.now()}`,
|
||||
user_message: "что по свк за 2020 год выведи все доки плиз что есть",
|
||||
llmProvider: "local",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual");
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0].message).toBe("документы по контрагенту свк за 2020 год");
|
||||
expect(response.debug?.llm_decomposition_reason).toBe("fallback_rule_applied_after_llm");
|
||||
expect(response.debug?.fallback_rule_hit).toBe("documents_counterparty_year_rewrite");
|
||||
});
|
||||
|
||||
it("rewrites payment-style counterparty phrasing to bank operations", async () => {
|
||||
const calls: Array<{ message: string }> = [];
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn(async (message: string) => {
|
||||
calls.push({ message });
|
||||
return buildAddressLaneResult(message);
|
||||
})
|
||||
} as any;
|
||||
|
||||
const normalizerService = {
|
||||
normalize: vi.fn(async () => ({
|
||||
trace_id: "norm-predecompose-bank-counterparty",
|
||||
ok: true,
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
user_message_raw: "какие платежи были по свк в 2020",
|
||||
message_in_scope: true,
|
||||
scope_confidence: "medium",
|
||||
contains_multiple_tasks: false,
|
||||
fragments: []
|
||||
},
|
||||
raw_model_output: null,
|
||||
validation: { passed: true, errors: [] },
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
latency_ms: 10,
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
request_count_for_case: 1
|
||||
}))
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const service = new AssistantService(
|
||||
normalizerService,
|
||||
sessions as any,
|
||||
{} as any,
|
||||
{ persistSession: vi.fn() } as any,
|
||||
addressQueryService
|
||||
);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: `asst-predecompose-bank-counterparty-${Date.now()}`,
|
||||
user_message: "какие платежи были по свк в 2020",
|
||||
llmProvider: "local",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual");
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0].message).toBe("банковские операции по контрагенту свк за 2020 год");
|
||||
expect(response.debug?.llm_decomposition_reason).toBe("fallback_rule_applied_after_llm");
|
||||
expect(response.debug?.fallback_rule_hit).toBe("bank_operations_counterparty_year_rewrite");
|
||||
});
|
||||
|
||||
it("normalizes short ordinal year like '20й' in noisy docs phrasing", async () => {
|
||||
const calls: Array<{ message: string }> = [];
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn(async (message: string) => {
|
||||
calls.push({ message });
|
||||
return buildAddressLaneResult(message);
|
||||
})
|
||||
} as any;
|
||||
|
||||
const normalizerService = {
|
||||
normalize: vi.fn(async () => ({
|
||||
trace_id: "norm-predecompose-short-ordinal-year",
|
||||
ok: true,
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
user_message_raw: "бля епт покажи доки по свк за 20й",
|
||||
message_in_scope: true,
|
||||
scope_confidence: "medium",
|
||||
contains_multiple_tasks: false,
|
||||
fragments: []
|
||||
},
|
||||
raw_model_output: null,
|
||||
validation: { passed: true, errors: [] },
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
latency_ms: 10,
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
request_count_for_case: 1
|
||||
}))
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const service = new AssistantService(
|
||||
normalizerService,
|
||||
sessions as any,
|
||||
{} as any,
|
||||
{ persistSession: vi.fn() } as any,
|
||||
addressQueryService
|
||||
);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: `asst-predecompose-short-ordinal-year-${Date.now()}`,
|
||||
user_message: "бля епт покажи доки по свк за 20й",
|
||||
llmProvider: "local",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual");
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0].message).toBe("документы по контрагенту свк за 2020 год");
|
||||
expect(response.debug?.llm_decomposition_reason).toBe("fallback_rule_applied_after_llm");
|
||||
expect(response.debug?.fallback_rule_hit).toBe("documents_counterparty_year_rewrite");
|
||||
});
|
||||
|
||||
it("does not treat date fragments as account in balance fallback", async () => {
|
||||
const calls: Array<{ message: string }> = [];
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn(async (message: string) => {
|
||||
calls.push({ message });
|
||||
return buildAddressLaneResult(message);
|
||||
})
|
||||
} as any;
|
||||
|
||||
const normalizerService = {
|
||||
normalize: vi.fn(async () => ({
|
||||
trace_id: "norm-predecompose-date-not-account",
|
||||
ok: true,
|
||||
normalized: {
|
||||
schema_version: "normalized_query_v2_0_2",
|
||||
user_message_raw: "есть ли долг по договору 1-ПМ/2020 на 2020-07-31",
|
||||
message_in_scope: true,
|
||||
scope_confidence: "medium",
|
||||
contains_multiple_tasks: false,
|
||||
fragments: []
|
||||
},
|
||||
raw_model_output: null,
|
||||
validation: { passed: true, errors: [] },
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
latency_ms: 10,
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
request_count_for_case: 1
|
||||
}))
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const service = new AssistantService(
|
||||
normalizerService,
|
||||
sessions as any,
|
||||
{} as any,
|
||||
{ persistSession: vi.fn() } as any,
|
||||
addressQueryService
|
||||
);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: `asst-predecompose-date-not-account-${Date.now()}`,
|
||||
user_message: "есть ли долг по договору 1-ПМ/2020 на 2020-07-31",
|
||||
llmProvider: "local",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual");
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0].message).not.toContain("остаток по счету 07");
|
||||
expect(calls[0].message.toLowerCase()).toContain("договору 1-пм/2020");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue