АДРЕСНЫЙ РЕЖИМ - ллм декомпоз
This commit is contained in:
parent
18e0f1364d
commit
b2d32f869c
3434
docs/ADDRESS/1.txt
3434
docs/ADDRESS/1.txt
File diff suppressed because it is too large
Load Diff
|
|
@ -35,6 +35,21 @@
|
|||
- `filter_extraction_audit.json`
|
||||
- `resolver_ambiguity_cases.md`
|
||||
|
||||
## Subwave M1.5 — L0 Hybrid Router Stabilization
|
||||
|
||||
Цель: стабилизировать входную интерпретацию на шумном вводе без раздувания словарей.
|
||||
|
||||
- LLM-first decompose в строгий JSON-контракт;
|
||||
- deterministic fallback (корни/подстроки, парсинг дат и счетов, шумоочистка);
|
||||
- режим `shadow` (без влияния на финальный ответ) и сбор trace;
|
||||
- переход в `soft-enable` только после прохождения регрессии.
|
||||
|
||||
Артефакты:
|
||||
|
||||
- `llm_router_shadow_report.json`
|
||||
- `fallback_trigger_audit.md`
|
||||
- `noisy_input_regression_results.md`
|
||||
|
||||
## Subwave M2 — Recipe + MCP Execution (Live-first)
|
||||
|
||||
Цель: связать intents с whitelist recipes и реальным MCP execution.
|
||||
|
|
@ -90,4 +105,5 @@ Acceptance gates:
|
|||
## Что делаем сразу
|
||||
|
||||
- Сегодня стартуем с `M0` и `M1` (контракты + классификация/фильтры).
|
||||
- После этого сразу поднимаем `M2` для 2 первых intents (`payables/receivables`).
|
||||
- После этого закрываем `M1.5` (shadow L0 router + регрессия на шумных вопросах).
|
||||
- И только затем поднимаем `M2` для 2 первых intents (`payables/receivables`).
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@
|
|||
## C. Договоры
|
||||
|
||||
- какие договоры не закрыты
|
||||
- что по договору 15/24
|
||||
- что по договору 1-ПМ/2020
|
||||
- есть ли долг по договору с Альфой
|
||||
- какие документы связаны с этим договором
|
||||
- покажи незакрытые договоры по контрагенту
|
||||
- какие хвосты по договору №15/24 на дату
|
||||
- какие хвосты по договору 1-ПМ/2020 на дату
|
||||
- есть ли открытые позиции по договору
|
||||
|
||||
## D. Документы
|
||||
|
|
@ -79,7 +79,27 @@
|
|||
- проверь, есть ли открытые позиции по договору
|
||||
- проверь, есть ли документы по контрагенту за июль 2020
|
||||
|
||||
## I. Robustness (шум, опечатки, сленг)
|
||||
|
||||
- свк доки за 20год покеж
|
||||
- свк 20 год - покажи доки плс
|
||||
- что по свк за 2020 год выведи все доки плиз что есть
|
||||
- какие доки есть по свк за 2021
|
||||
- какие документы по контрагенту свк за все время
|
||||
- покажи сальдо по 60.01 на 31.07.20
|
||||
- остаток 60 на 2020.05
|
||||
- какой остаток по счету 60 на 2020 май
|
||||
- 60 счет остаток на май 2020 покажи
|
||||
- свк за 2020 покаж все поступления
|
||||
- свк июль 2020 какие доки есть
|
||||
- доки по свк с 01.07.2020 по 31.07.2020
|
||||
- покаж по договору 1-ПМ/2020 доки за 2020
|
||||
- че висит по 60 счету на 31 07 2020
|
||||
- бля епт покажи доки по свк за 20й
|
||||
|
||||
## Правило маршрутизации
|
||||
|
||||
- если вопрос = factual lookup -> `address_query`
|
||||
- если вопрос = why/prove/causal diagnosis -> `deep_analysis`
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -41,15 +41,35 @@
|
|||
Новый high-level flow:
|
||||
|
||||
1. Входящий вопрос.
|
||||
2. Mode-classifier: `address_query` | `deep_analysis` | `unsupported`.
|
||||
3. Если `address_query`:
|
||||
2. L0 hybrid router: LLM decompose + deterministic fallback.
|
||||
3. Mode-classifier: `address_query` | `deep_analysis` | `unsupported`.
|
||||
4. Если `address_query`:
|
||||
- resolve `address_intent`;
|
||||
- extract+validate filters;
|
||||
- select recipe;
|
||||
- execute MCP (live-first);
|
||||
- normalize result schema;
|
||||
- return factual answer.
|
||||
4. Если не `address_query`: текущий deep path без изменений.
|
||||
5. Если не `address_query`: текущий deep path без изменений.
|
||||
|
||||
## 3.1) L0 Hybrid Router (stabilization layer)
|
||||
|
||||
Назначение: убрать хрупкость на шумном пользовательском вводе (опечатки, сленг, лишние слова), не раздувая словари.
|
||||
|
||||
Порядок работы:
|
||||
|
||||
1. Сначала запускаем LLM decompose в строгий JSON-контракт.
|
||||
2. Если LLM дал пустой/невалидный/неиспользуемый фрагмент, включается короткий deterministic fallback:
|
||||
- триггеры по корням/подстрокам (не giant словарь словоформ);
|
||||
- парсинг дат/периодов/счетов;
|
||||
- шумоочистка (служебные слова, междометия, мусорные хвосты).
|
||||
3. Если и fallback не дал валидный результат, возвращаем `LIMITED_WITH_REASON`, без выдумывания фактов.
|
||||
|
||||
Ограничения:
|
||||
|
||||
- без company-specific словарей в runtime;
|
||||
- без генерации SQL/1C-query в свободной форме;
|
||||
- только интерпретация вопроса + передача в whitelist recipes.
|
||||
|
||||
## 4) Встраивание по слоям
|
||||
|
||||
|
|
@ -58,6 +78,7 @@
|
|||
Изменения:
|
||||
|
||||
- добавить `question_mode` и `address_intent` в normalizer contract;
|
||||
- добавить L0 router contract (decompose output + fallback reason);
|
||||
- в `routeHintAdapter` добавить address-query rule set до deep route discipline.
|
||||
|
||||
Рекомендуемые файлы:
|
||||
|
|
@ -131,6 +152,11 @@
|
|||
- `rows_fetched`
|
||||
- `rows_matched`
|
||||
- `response_type`
|
||||
- `llm_decomposition_attempted`
|
||||
- `llm_decomposition_applied`
|
||||
- `llm_decomposition_reason`
|
||||
- `fallback_rule_hit`
|
||||
- `sanitized_user_message`
|
||||
|
||||
## 5) Fallback Rules
|
||||
|
||||
|
|
@ -160,9 +186,20 @@
|
|||
|
||||
1. `M0`: контракты (`question_mode`, `address_intent`, filter schema, recipe schema).
|
||||
2. `M1`: classifier + resolver + validator (без MCP execution).
|
||||
3. `M2`: MCP executor + 5 P0 recipe.
|
||||
4. `M3`: factual composer + debug payload + basic tests.
|
||||
5. `M4`: live rerun pack в `docs/ADDRESS/runs/...`.
|
||||
3. `M1.5`: L0 hybrid router (LLM-first + deterministic fallback) в shadow mode.
|
||||
4. `M2`: MCP executor + 5 P0 recipe.
|
||||
5. `M3`: factual composer + debug payload + basic tests.
|
||||
6. `M4`: live rerun pack в `docs/ADDRESS/runs/...`.
|
||||
|
||||
## 7.2) L0 rollout policy
|
||||
|
||||
1. `Shadow`: LLM decompose не влияет на ответ, только trace/audit.
|
||||
2. `Soft-enable`: LLM decompose влияет на routing только для P0 intents.
|
||||
3. `Full-enable`: LLM decompose + fallback включены для всего `address_query`.
|
||||
|
||||
Правило безопасности:
|
||||
|
||||
- любой сбой LLM должен откатываться в deterministic fallback, а не в random behavior.
|
||||
|
||||
## 7.1) Sprint B priority order (adapted to current reality)
|
||||
|
||||
|
|
@ -191,3 +228,5 @@ Rationale:
|
|||
- Address intents из P0 стабильно маршрутизируются в MCP/live-first lane.
|
||||
- Factual-ответы по P0 сценариям возвращаются в предсказуемом формате.
|
||||
- `false_factual_rate = 0`.
|
||||
- Нет silent-degradation: при провале LLM есть explainable fallback reason.
|
||||
- На шумном вводе нет ложного сдвига anchor (`counterparty`, `account`, `period`).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,617 @@
|
|||
{
|
||||
"summary": {
|
||||
"run_id": "2026-04-01_Address_1txt_LLM_Recheck",
|
||||
"source_file": "X:\\1C\\NDC_1C\\docs\\ADDRESS\\1.txt",
|
||||
"session_id": "asst-batch-1775070253876",
|
||||
"questions_total": 24,
|
||||
"reply_type_counts": {
|
||||
"partial_coverage": 12,
|
||||
"factual": 11,
|
||||
"clarification_required": 1
|
||||
},
|
||||
"llm_attempted_count": 22,
|
||||
"llm_applied_count": 22,
|
||||
"llm_reason_counts": {
|
||||
"normalized_fragment_applied": 22,
|
||||
"null": 1,
|
||||
"not_address_like": 1
|
||||
},
|
||||
"limited_reason_counts": {
|
||||
"empty_match": 1,
|
||||
"null": 12,
|
||||
"missing_anchor": 3,
|
||||
"unsupported": 8
|
||||
},
|
||||
"factual_like_count": 11,
|
||||
"partial_count": 12,
|
||||
"error_count": 0,
|
||||
"avg_elapsed_ms": 6858
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"idx": 1,
|
||||
"question": "свк доки за 20год покеж",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6384,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-UtQdVfe_-A",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"account": "20",
|
||||
"period_from": "2023-01-01",
|
||||
"period_to": "2023-12-31",
|
||||
"as_of_date": "2023-12-31"
|
||||
},
|
||||
"mcp_call_status": "no_raw_rows",
|
||||
"limited_reason_category": "empty_match",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "В live-данных по текущему фильтру записи не найдены.\nПричина: по выбранным фильтрам в live-выборке нет строк.\nЧто нужно уточнить: уточните период, контрагента, договор или снимите часть фильтров.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"question": "свк 20 год - покажи доки плс",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6153,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-oDaS9iJcKo",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"counterparty": "свк",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Собран список документов по контрагенту (live address lane).\nСтрок отобрано: 3.\n1. 2020-12-30T12:00:00Z | Поступление на расчетный счет 00000000044 от 30.12.2020 12:00:00 | 0 / 0 | 3248300 | аналитика: Группа СВК\n2. 2020-12-16T16:20:53Z | Поступление на расчетный счет 00000000043 от 16.12.2020 16:20",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"question": "что по свк за 2020 год выведи все доки плиз что есть",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 20518,
|
||||
"reply_type": "clarification_required",
|
||||
"trace_id": "0TVLQBjJoCBfzy",
|
||||
"detected_intent": null,
|
||||
"extracted_filters": null,
|
||||
"mcp_call_status": null,
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": false,
|
||||
"llm_decomposition_applied": false,
|
||||
"llm_decomposition_reason": null,
|
||||
"llm_provider_used": null,
|
||||
"assistant_reply_preview": "Коротко: Проблема подтверждается частично; для уверенного вывода нужны уточнения. Что сломано:\n- Есть признаки проблемы, но без уточнений по периоду и объекту вывод ненадежен. Почему это похоже на проблему:\n- Сигнал проблемы есть, но механизм подтвержден не полностью. На чем это основано:\n- Опора ча",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"question": "какие документы по контрагенту свк за все время",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6279,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-rGANrjNA-z",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"counterparty": "свк"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Собран список документов по контрагенту (live address lane).\nСтрок отобрано: 26.\n1. 2021-11-10T12:00:07Z | Поступление на расчетный счет 00000000013 от 10.11.2021 12:00:07 | 0 / 0 | 20000 | аналитика: Группа СВК\n2. 2021-09-29T12:00:03Z | Поступление на расчетный счет 00000000012 от 29.09.2021 12:00:",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"question": "РґРѕРєРё РїРѕ СЃРІРє СЃ 01.07.2020 РїРѕ 31.07.2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6720,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-LUCt7rb_A-",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-07-01",
|
||||
"period_to": "2020-07-31",
|
||||
"counterparty": "свк"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Собран список документов по контрагенту (live address lane).\nСтрок отобрано: 2.\n1. 2020-07-30T12:00:00Z | Поступление на расчетный счет 00000000029 от 30.07.2020 12:00:00 | 0 / 0 | 20000 | аналитика: Группа СВК\n2. 2020-07-27T12:00:00Z | Поступление на расчетный счет 00000000028 от 27.07.2020 12:00:0",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"question": "свк июль 2020 какие доки есть",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 7151,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-G7spbS8NqU",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"counterparty": "свк"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Собран список документов по контрагенту (live address lane).\nСтрок отобрано: 3.\n1. 2020-12-30T12:00:00Z | Поступление на расчетный счет 00000000044 от 30.12.2020 12:00:00 | 0 / 0 | 3248300 | аналитика: Группа СВК\n2. 2020-12-16T16:20:53Z | Поступление на расчетный счет 00000000043 от 16.12.2020 16:20",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"question": "покажи сальдо по 60.01 на 31.07.20",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6342,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-kO9TP1_8NF",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"account": "60.01",
|
||||
"as_of_date": "2020-07-31"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Адресный срез по счету собран (по движениям live MCP).\nСтрок отобрано: 200.\nСумма по отобранным движениям: 27938175.\n1. 2020-07-13T12:00:02Z | Поступление товаров и услуг 00000000029 от 13.07.2020 12:00:02 | 41.01 / 60.01 | 74400\n2. 2020-07-13T12:00:02Z | Поступление товаров и услуг 00000000029 от 1",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"question": "остаток 60 на 2020.05",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 5857,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-SHnHpGnqzB",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"account": "60",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "2020-05-31"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Адресный срез по счету собран (по движениям live MCP).\nСтрок отобрано: 6.\nСумма по отобранным движениям: 849499.\n1. 2020-05-22T10:14:43Z | Списание с расчетного счета 00000000148 от 22.05.2020 10:14:43 | 60.02 / 51 | 142299\n2. 2020-05-22T10:12:50Z | Списание с расчетного счета 00000000147 от 22.05.2",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"question": "какой остаток по счету 60 на 2020 май",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 7189,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-nICyNWFT0S",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"account": "60",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Адресный срез по счету собран (по движениям live MCP).\nСтрок отобрано: 138.\nСумма по отобранным движениям: 24061597.5.\n1. 2020-11-26T12:00:03Z | Поступление на расчетный счет 00000000040 от 26.11.2020 12:00:03 | 51 / 60.02 | 900000\n2. 2020-11-26T12:00:03Z | Поступление на расчетный счет 00000000040 ",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"question": "60 счет остаток на май 2020 покажи",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6107,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-Z1Ax_5iDve",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"account": "60",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Адресный срез по счету собран (по движениям live MCP).\nСтрок отобрано: 138.\nСумма по отобранным движениям: 24061597.5.\n1. 2020-11-26T12:00:03Z | Поступление на расчетный счет 00000000040 от 26.11.2020 12:00:03 | 51 / 60.02 | 900000\n2. 2020-11-26T12:00:03Z | Поступление на расчетный счет 00000000040 ",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"question": "какой остаток по счету 60 на 2020-05-31",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6005,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-v8rsGxAPk8",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"account": "60",
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31",
|
||||
"as_of_date": "2020-05-31"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Адресный срез по счету собран (по движениям live MCP).\nСтрок отобрано: 6.\nСумма по отобранным движениям: 849499.\n1. 2020-05-22T10:14:43Z | Списание с расчетного счета 00000000148 от 22.05.2020 10:14:43 | 60.02 / 51 | 142299\n2. 2020-05-22T10:12:50Z | Списание с расчетного счета 00000000147 от 22.05.2",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"question": "остаток по 60 на май 2020, не за весь 2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 7292,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-lSVsG6X5Df",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"account": "60",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31",
|
||||
"as_of_date": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Адресный срез по счету собран (по движениям live MCP).\nСтрок отобрано: 138.\nСумма по отобранным движениям: 24061597.5.\n1. 2020-11-26T12:00:03Z | Поступление на расчетный счет 00000000040 от 26.11.2020 12:00:03 | 51 / 60.02 | 900000\n2. 2020-11-26T12:00:03Z | Поступление на расчетный счет 00000000040 ",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"question": "покаж РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РґРѕРєРё Р·Р° 2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 8256,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-flmr5jto7X",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"counterparty": "Объясните",
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Для точного адресного поиска не хватает обязательного якоря.\nПричина: якорь контрагента/договора не найден в материализованных live-строках.\nЧто нужно уточнить: уточните контрагента точным именем или добавьте ИНН/договор.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"question": "какие документы РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 Р·Р° РІСЃРµ время",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 7122,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-Qo9bATsKBX",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"counterparty": "какие",
|
||||
"contract": "1-<2D><>/2020"
|
||||
},
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Для точного адресного поиска не хватает обязательного якоря.\nПричина: якорь контрагента/договора не найден в материализованных live-строках.\nЧто нужно уточнить: уточните контрагента точным именем или добавьте ИНН/договор.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"question": "есть ли долг РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РЅР° 2020-07-31",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 8152,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-cW17Pxr6W9",
|
||||
"detected_intent": "list_documents_by_counterparty",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"counterparty": "контракту",
|
||||
"period_from": "2020-07-01",
|
||||
"period_to": "2020-07-31"
|
||||
},
|
||||
"mcp_call_status": "materialized_but_not_anchor_matched",
|
||||
"limited_reason_category": "missing_anchor",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Для точного адресного поиска не хватает обязательного якоря.\nПричина: якорь контрагента/договора не найден в материализованных live-строках.\nЧто нужно уточнить: уточните контрагента точным именем или добавьте ИНН/договор.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"question": "какие хвосты РїРѕ РґРѕРіРѕРІРѕСЂСѓ 1-ПМ/2020 РЅР° дату 31.07.2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6338,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-327jT7VHz_",
|
||||
"detected_intent": "unknown",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-07-01",
|
||||
"period_to": "2020-07-31"
|
||||
},
|
||||
"mcp_call_status": "skipped",
|
||||
"limited_reason_category": "unsupported",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Этот запрос не подходит под address_query V1.\nПричина: intent пока не поддержан в address V1.\nЧто нужно уточнить: переформулируйте вопрос как адресный lookup по счету/контрагенту/договору.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"question": "какие платежи были по свк в 2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 5858,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-t5LrJK0Hqw",
|
||||
"detected_intent": "unknown",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "skipped",
|
||||
"limited_reason_category": "unsupported",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Этот запрос не подходит под address_query V1.\nПричина: intent пока не поддержан в address V1.\nЧто нужно уточнить: переформулируйте вопрос как адресный lookup по счету/контрагенту/договору.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"question": "были ли поступления от свк за июль 2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 5991,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-lzi3Np6sPw",
|
||||
"detected_intent": "unknown",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "skipped",
|
||||
"limited_reason_category": "unsupported",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Этот запрос не подходит под address_query V1.\nПричина: intent пока не поддержан в address V1.\nЧто нужно уточнить: переформулируйте вопрос как адресный lookup по счету/контрагенту/договору.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 19,
|
||||
"question": "покажи списания с расчетного счета по свк за 2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 5658,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-eUEHz-s3yq",
|
||||
"detected_intent": "unknown",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "skipped",
|
||||
"limited_reason_category": "unsupported",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Этот запрос не подходит под address_query V1.\nПричина: intent пока не поддержан в address V1.\nЧто нужно уточнить: переформулируйте вопрос как адресный lookup по счету/контрагенту/договору.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"question": "свк за 2020 покаж все поступления",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 5723,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-rwRwljpwaa",
|
||||
"detected_intent": "unknown",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "skipped",
|
||||
"limited_reason_category": "unsupported",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Этот запрос не подходит под address_query V1.\nПричина: intent пока не поддержан в address V1.\nЧто нужно уточнить: переформулируйте вопрос как адресный lookup по счету/контрагенту/договору.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"question": "покажи документы по свк за 2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6380,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-SJ9kbgZbNN",
|
||||
"detected_intent": "unknown",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-01-01",
|
||||
"period_to": "2020-12-31"
|
||||
},
|
||||
"mcp_call_status": "skipped",
|
||||
"limited_reason_category": "unsupported",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Этот запрос не подходит под address_query V1.\nПричина: intent пока не поддержан в address V1.\nЧто нужно уточнить: переформулируйте вопрос как адресный lookup по счету/контрагенту/договору.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"question": "а теперь только за май 2020",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 13,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-7V_8eBVTvl",
|
||||
"detected_intent": "unknown",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"period_from": "2020-05-01",
|
||||
"period_to": "2020-05-31"
|
||||
},
|
||||
"mcp_call_status": "skipped",
|
||||
"limited_reason_category": "unsupported",
|
||||
"llm_decomposition_attempted": false,
|
||||
"llm_decomposition_applied": false,
|
||||
"llm_decomposition_reason": "not_address_like",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Этот запрос не подходит под address_query V1.\nПричина: intent пока не поддержан в address V1.\nЧто нужно уточнить: переформулируйте вопрос как адресный lookup по счету/контрагенту/договору.",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"question": "а по счету 60.01 на ту же дату",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6675,
|
||||
"reply_type": "factual",
|
||||
"trace_id": "address-xrmQmT7kOg",
|
||||
"detected_intent": "account_balance_snapshot",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20,
|
||||
"account": "60.01",
|
||||
"as_of_date": "2026-04-01"
|
||||
},
|
||||
"mcp_call_status": "matched_non_empty",
|
||||
"limited_reason_category": null,
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Адресный срез по счету собран (по движениям live MCP).\nСтрок отобрано: 200.\nСумма по отобранным движениям: 31878451.5.\n1. 2022-04-18T12:00:00Z | Поступление товаров и услуг 00000000007 от 18.04.2022 12:00:00 | 97.21 / 60.01 | 14790\n2. 2022-04-18T12:00:00Z | Поступление товаров и услуг 00000000007 от",
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"question": "бля епт покажи доки по свк за 20й",
|
||||
"http_ok": true,
|
||||
"status": 200,
|
||||
"elapsed_ms": 6426,
|
||||
"reply_type": "partial_coverage",
|
||||
"trace_id": "address-ISmwa9nejq",
|
||||
"detected_intent": "unknown",
|
||||
"extracted_filters": {
|
||||
"sort": "period_desc",
|
||||
"limit": 20
|
||||
},
|
||||
"mcp_call_status": "skipped",
|
||||
"limited_reason_category": "unsupported",
|
||||
"llm_decomposition_attempted": true,
|
||||
"llm_decomposition_applied": true,
|
||||
"llm_decomposition_reason": "normalized_fragment_applied",
|
||||
"llm_provider_used": "local",
|
||||
"assistant_reply_preview": "Этот запрос не подходит под address_query V1.\nПричина: intent пока не поддержан в address V1.\nЧто нужно уточнить: переформулируйте вопрос как адресный lookup по счету/контрагенту/договору.",
|
||||
"error": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"run_id": "2026-04-01_Address_1txt_LLM_Recheck",
|
||||
"source_file": "X:\\1C\\NDC_1C\\docs\\ADDRESS\\1.txt",
|
||||
"session_id": "asst-batch-1775070253876",
|
||||
"questions_total": 24,
|
||||
"reply_type_counts": {
|
||||
"partial_coverage": 12,
|
||||
"factual": 11,
|
||||
"clarification_required": 1
|
||||
},
|
||||
"llm_attempted_count": 22,
|
||||
"llm_applied_count": 22,
|
||||
"llm_reason_counts": {
|
||||
"normalized_fragment_applied": 22,
|
||||
"null": 1,
|
||||
"not_address_like": 1
|
||||
},
|
||||
"limited_reason_counts": {
|
||||
"empty_match": 1,
|
||||
"null": 12,
|
||||
"missing_anchor": 3,
|
||||
"unsupported": 8
|
||||
},
|
||||
"factual_like_count": 11,
|
||||
"partial_count": 12,
|
||||
"error_count": 0,
|
||||
"avg_elapsed_ms": 6858
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# Address Query Runtime V1 - M2.3f LLM Decompose Coercion Layer
|
||||
|
||||
Date: 2026-04-01
|
||||
|
||||
## Goal
|
||||
|
||||
Stabilize local-LLM pre-decomposition without adding brittle dictionaries:
|
||||
|
||||
- accept semantically useful but schema-drifted local model JSON;
|
||||
- coerce it into strict `normalized_query_v2_0_2` contract;
|
||||
- keep deterministic route policy and guardrails unchanged.
|
||||
|
||||
## Implemented
|
||||
|
||||
1. Added coercion layer in normalizer pipeline before policy/validation:
|
||||
- `coerceNormalizedCandidateV2(...)`
|
||||
- `coerceFragmentV2(...)`
|
||||
- scalar coercion: booleans, numeric confidence, string arrays, IDs.
|
||||
|
||||
2. Field-level schema drift recovery:
|
||||
- `fragment_id: 1` -> `F1`;
|
||||
- `domain_relevance: true/false` -> `in_scope/out_of_scope`;
|
||||
- `business_scope: document_review` -> `company_specific_accounting`;
|
||||
- `candidate_labels: show_documents` -> `simple_factual`.
|
||||
|
||||
3. Time scope recovery from loose local payload:
|
||||
- supports `time_scope.period_type/year/month` shape;
|
||||
- maps to strict schema `time_scope.type/value/confidence`;
|
||||
- month payload becomes `YYYY-MM` (example: `2020-05`).
|
||||
|
||||
4. No architecture drift:
|
||||
- deterministic routing and execution policy unchanged;
|
||||
- no free-form SQL/query builder introduced;
|
||||
- no recipe whitelist widening.
|
||||
|
||||
## Verification
|
||||
|
||||
- `npm.cmd --prefix .\\llm_normalizer\\backend run test -- normalizerServiceCoercion.test.ts` -> PASS (`2` tests)
|
||||
- `npm.cmd --prefix .\\llm_normalizer\\backend run test -- addressQueryRuntimeM23.test.ts` -> PASS (`46` tests)
|
||||
- `npm.cmd --prefix .\\llm_normalizer\\backend run build` -> PASS
|
||||
|
||||
## Live Regression Focus (manual)
|
||||
|
||||
Run these in UI and inspect `technical_debug_payload_json`:
|
||||
|
||||
1. `svk doki za 20god pokezh`
|
||||
Expected: address lane preserved; no unsupported fallback due to malformed fragment schema.
|
||||
|
||||
2. `Kakoi ostatok po schetu 60 na 2020 mai`
|
||||
Expected: month interpreted as May 2020 window, not whole year/current date.
|
||||
|
||||
3. `svk 20 god - pokaji doki pls`
|
||||
Expected: noise tail ignored, anchor extracted from core phrase.
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"run_id": "2026-04-01_Address_Query_Runtime_V1_M2_3F_LLM_Decompose_Coercion_Layer",
|
||||
"comparison": {
|
||||
"baseline_ref": "2026-04-01_Address_Query_Runtime_V1_M2_3E_Stability_Hardening_AccountQueryScope",
|
||||
"current_ref": "workspace (after M2.3f coercion layer)"
|
||||
},
|
||||
"metrics": {
|
||||
"coercion_regression_cases": {
|
||||
"before": 0,
|
||||
"after": 2,
|
||||
"delta": 2
|
||||
},
|
||||
"address_m23_test_cases": {
|
||||
"before": 46,
|
||||
"after": 46,
|
||||
"delta": 0
|
||||
},
|
||||
"address_m23_test_failures": {
|
||||
"before": 0,
|
||||
"after": 0,
|
||||
"delta": 0
|
||||
},
|
||||
"llm_predecompose_schema_drift_handling": {
|
||||
"before": "best-effort salvage mainly at assistant side",
|
||||
"after": "normalizer-side coercion + strict schema policy",
|
||||
"delta": "stability_improved"
|
||||
}
|
||||
},
|
||||
"notes": [
|
||||
"new tests cover malformed local fragment payload and year-month coercion",
|
||||
"deterministic route and recipe selection policy remain unchanged",
|
||||
"build and existing address runtime suite remain green"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
llm_normalizer/backend/src/services/normalizerService.ts
|
||||
llm_normalizer/backend/tests/normalizerServiceCoercion.test.ts
|
||||
llm_normalizer/backend/dist/services/normalizerService.js
|
||||
|
||||
docs/ADDRESS/runs/2026-04-01_Address_Query_Runtime_V1_M2_3F_LLM_Decompose_Coercion_Layer/README.md
|
||||
docs/ADDRESS/runs/2026-04-01_Address_Query_Runtime_V1_M2_3F_LLM_Decompose_Coercion_Layer/run_summary.json
|
||||
docs/ADDRESS/runs/2026-04-01_Address_Query_Runtime_V1_M2_3F_LLM_Decompose_Coercion_Layer/before_after_metrics.json
|
||||
docs/ADDRESS/runs/2026-04-01_Address_Query_Runtime_V1_M2_3F_LLM_Decompose_Coercion_Layer/smoke_checks.md
|
||||
docs/ADDRESS/runs/2026-04-01_Address_Query_Runtime_V1_M2_3F_LLM_Decompose_Coercion_Layer/changed_files.txt
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"run_id": "2026-04-01_Address_Query_Runtime_V1_M2_3F_LLM_Decompose_Coercion_Layer",
|
||||
"date": "2026-04-01",
|
||||
"stage": "Address Query Runtime V1",
|
||||
"wave": "M2.3f",
|
||||
"goal": "LLM pre-decompose coercion layer for schema-drifted local outputs with strict v2_0_2 compatibility",
|
||||
"status": "COMPLETED",
|
||||
"scope": {
|
||||
"new_intents": false,
|
||||
"deep_analysis_changes": false,
|
||||
"focus": [
|
||||
"local LLM schema-drift coercion",
|
||||
"fragment-level type recovery",
|
||||
"time_scope loose-shape normalization",
|
||||
"no-route regression prevention for malformed local payloads"
|
||||
]
|
||||
},
|
||||
"checks": {
|
||||
"test_command_primary": "npm.cmd --prefix .\\llm_normalizer\\backend run test -- normalizerServiceCoercion.test.ts",
|
||||
"test_command_secondary": "npm.cmd --prefix .\\llm_normalizer\\backend run test -- addressQueryRuntimeM23.test.ts",
|
||||
"build_command": "npm.cmd --prefix .\\llm_normalizer\\backend run build",
|
||||
"tests_passed": 48,
|
||||
"tests_failed": 0
|
||||
},
|
||||
"guardrails": {
|
||||
"false_factual_rate_target": 0,
|
||||
"free_form_query_builder": "not_added",
|
||||
"whitelist_recipe_policy": "unchanged"
|
||||
},
|
||||
"key_changes": {
|
||||
"coercion_layer_added": true,
|
||||
"numeric_fragment_id_recovery": true,
|
||||
"boolean_domain_relevance_recovery": true,
|
||||
"local_business_scope_alias_recovery": true,
|
||||
"candidate_label_alias_recovery": true,
|
||||
"period_type_year_month_recovery": true,
|
||||
"routing_policy_changed": false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# Smoke Checks
|
||||
|
||||
## Backend tests
|
||||
|
||||
- Command: `npm.cmd --prefix .\\llm_normalizer\\backend run test -- normalizerServiceCoercion.test.ts`
|
||||
- Result: PASS
|
||||
- Details: `1 passed file`, `2 passed tests`, `0 failed`
|
||||
|
||||
- Command: `npm.cmd --prefix .\\llm_normalizer\\backend run test -- addressQueryRuntimeM23.test.ts`
|
||||
- Result: PASS
|
||||
- Details: `1 passed file`, `46 passed tests`, `0 failed`
|
||||
|
||||
## Build
|
||||
|
||||
- Command: `npm.cmd --prefix .\\llm_normalizer\\backend run build`
|
||||
- Result: PASS
|
||||
- Details: TypeScript build completed without errors.
|
||||
|
||||
## Manual spot-check focus
|
||||
|
||||
- Query: `ñâê äîêè çà 20ãîä ïîêåæ`
|
||||
- Expected: no unsupported/no-route caused by malformed local fragment schema.
|
||||
|
||||
- Query: `Êàêîé îñòàòîê ïî ñ÷åòó 60 íà 2020 ìàé`
|
||||
- Expected: month window (`2020-05-01..2020-05-31`) inferred consistently.
|
||||
|
||||
- Query: `ñâê 20 ãîä - ïîêàæè äîêè ïëñ`
|
||||
- Expected: noise tail ignored; anchor stays `ñâê`.
|
||||
Binary file not shown.
|
|
@ -0,0 +1,55 @@
|
|||
# eco-aip-backend
|
||||
|
||||
Minimal backend skeleton for AIP Ecology Analytics.
|
||||
|
||||
## Quick start
|
||||
1) Copy `.env.example` to `.env` and edit values if needed.
|
||||
2) Run:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
## Verify
|
||||
- API health:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9501/health
|
||||
```
|
||||
|
||||
- Summary (last 24h by default):
|
||||
|
||||
```bash
|
||||
curl "http://localhost:9501/aip/waqi/summary"
|
||||
```
|
||||
|
||||
- Anomalies (last 24h by default):
|
||||
|
||||
```bash
|
||||
curl "http://localhost:9501/aip/waqi/anomalies"
|
||||
```
|
||||
|
||||
- History (daily measurements, MSK date):
|
||||
|
||||
```bash
|
||||
curl "http://localhost:9501/aip/waqi/history?date=2025-12-22"
|
||||
```
|
||||
|
||||
- DB tables:
|
||||
|
||||
```bash
|
||||
docker compose exec db psql -U aip_user -d aip_ecology -c "\dt"
|
||||
```
|
||||
|
||||
- Worker ticks:
|
||||
|
||||
```bash
|
||||
docker compose logs -f worker
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Times are stored as UTC (`timestamptz`).
|
||||
- Schema is initialized from `db/init/001_init.sql` on first DB start.
|
||||
- Worker ingests WAQI bounds data using `WAQI_TOKEN` and bbox env values.
|
||||
- Database files are stored in `db/data` (bind mount).
|
||||
- Worker detects basic anomalies (delay/stuck/spike) using observed_ts and env thresholds.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
FROM python:3.11-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY app ./app
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,4 @@
|
|||
fastapi==0.110.0
|
||||
uvicorn==0.27.1
|
||||
psycopg2-binary==2.9.9
|
||||
httpx==0.27.0
|
||||
|
|
@ -0,0 +1 @@
|
|||
14
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue