# Address Architecture Contract V1 Дата: 2026-04-01 ## 1) Зачем документ Этот контракт фиксирует архитектурные границы `address_query`-контура, чтобы система оставалась переносимой между разными 1С-базами и не обрастала company-specific логикой. Контракт обязателен для всех следующих инкрементов (`M2.4+`), рефакторов и новых intent/recipe. ## 2) Непересекаемые принципы - `MCP/live-first`: основной источник фактов - live MCP. - `MSP-only` в runtime: production path работает через MCP/MSP; snapshot - только controlled fallback. - `snapshot` допускается только как явный fallback с reason code, а не как скрытая подмена. - `runtime = data-agnostic`: никаких хардкодов под конкретную компанию. - `acceptance = data-aware`: positive-кейсы можно подбирать на текущей базе только для проверки. - `false_factual_rate = 0`: factual-ответ только при подтвержденных `rows_matched > 0`. - `whitelist execution only`: никаких свободных NL->query генераторов. ## 3) Канонический pipeline ## Stage A: Decompose Назначение: интерпретация текста вопроса, без обращения к данным компании. Выход stage: - `question_mode` - `query_shape` - `intent_candidates` - `anchors_raw` - `time_scope_raw` - `filters_raw` - `decomposition_plan` (опционально, для compound) Запрещено на этапе Decompose: - резолвить реальные объекты базы (контрагентов, договоры, документы); - тянуть company-specific словари; - генерировать запросы к 1С. ## Stage B: Resolve Назначение: привязка raw-якорей к живым объектам через MCP. Выход stage: - `anchor_type` - `anchor_value_raw` - `anchor_value_resolved` - `resolver_confidence` - `ambiguity_count` Правило: - если якорь не подтвержден, runtime не выдумывает факт и идет в `LIMITED_WITH_REASON`. ## Stage C: Execute Назначение: выполнение только через recipe whitelist. Правила: - `intent -> recipe_id` только из каталога; - fixed `limit/sort/window` политика; - `MCP` read-only; - `MSP/MCP-only` execution path в production; - snapshot fallback только явный. Диагностика по стадиям: - `no_raw_rows` - `raw_rows_received_but_not_materialized` - `materialized_but_not_anchor_matched` - `materialized_but_filtered_out_by_recipe` - `matched_non_empty` - `error` ## Stage D: Compose Назначение: финальный ответ строго по execution-результату. Правила: - factual только из `rows_matched`; - если пусто - `LIMITED_WITH_REASON` с конкретной причиной; - без reasoning-галлюцинаций и без “догадки по смыслу”. ## 4) Политика словарей Разрешено (статически в коде): - доменная типовая лексика (`доки`, `остаток`, `договор`, `дебиторка` и т.д.); - правила парсинга дат/периодов/счетов; - stop-слова и служебные alias-правила. Запрещено: - хранить в коде списки компаний, ИНН, договоров, документов конкретной базы; - пополнять глобальные normalization-библиотеки живыми entity-именами; - строить скрытые “памяти компании” вне runtime-сессии. Допустимо: - использовать runtime-сессионный контекст диалога (`followup context`) без записи в глобальные словари. ## 5) Критерии переносимости между компаниями Система считается переносимой, если: - новая база подключается без code change в resolver/intent logic; - меняются только live-данные MCP, а не кодовые словари; - question-bank остается валиден (с ожидаемыми различиями factual/limited по данным). ## 6) Антипаттерны (нельзя делать) - Добавлять company alias map в `src/services/*` с реальными названиями контрагентов. - Перекладывать проблему резолвинга в hardcoded `if company == ...`. - Подмешивать deep-analysis ответ в address factual-блок без явного route handoff. - Поднимать “временные” exceptions, которые ломают stage-контракт. ## 7) Техническая дисциплина кода Новая логика должна ложиться в явные stage-модули: - decompose - resolve - execute - compose - diagnostics Если функция не относится к stage - это smell и повод к вынесению. ## 8) Как подключать LLM decompose LLM на первом этапе нужен не для “знания компании”, а для структурной интерпретации вопроса. LLM должен возвращать схему-stage-output: - intent candidates - shape - anchor spans - time scope - filter hints - confidence Дальше все company-specific подтверждается только Resolver/Execute через MCP. Итог: - LLM decompose уменьшает NLP-хрупкость; - не требует жирных живых словарей компаний; - не нарушает data-agnostic принцип runtime.