refactor!: make addrkit a multi-city address normalizer

- remove the events/outages module, ArcGIS adapter, portal config and event statuses
- add a configurable city dictionary/parser and make City part of StreetKey
- seed: read city/city_name, namespace IDs by city, keep structures distinct
- fix reliability: schema v2 with fail-closed load, deterministic ordering,
  input limits, config validation, atomic DB publish, LLM response caps
- add read-only shadow mode comparing the old ES path with addrkit
- breaking: registry.db must be re-seeded; config has no portal section
This commit is contained in:
Maksim Totmin
2026-10-27 14:35:51 +07:00
parent a0b4cb2b14
commit 17ff5c591f
38 changed files with 2978 additions and 1164 deletions
+53 -31
View File
@@ -1,10 +1,12 @@
openapi: 3.1.0
info:
title: addrkit
version: "1.0.0"
version: "2.0.0"
description: >
Детерминированный сервис нормализации и поиска адресов. Ответ всегда
берётся из реестра; LLM не участвует в пути корректности.
Детерминированный многогородской сервис нормализации и поиска адресов.
Ответ всегда берётся из реестра; LLM не участвует в пути корректности.
Сервис не знает ни про отключения, ни про другие события: это отдельная
задача, которая может использовать addrkit как нормализатор.
servers:
- url: http://localhost:6171
paths:
@@ -21,7 +23,15 @@ paths:
properties:
address:
type: string
example: "2-я Станционная улица 1/1 стр."
maxLength: 512
example: "нск, 2-я Станционная улица 1/1 стр."
city:
type: string
maxLength: 128
description: >
Необязательная подсказка. Переопределяет город, найденный
в тексте адреса.
example: "Новосибирск"
responses:
"200":
description: Результат сопоставления
@@ -31,26 +41,54 @@ paths:
$ref: "#/components/schemas/ResolveResult"
"400":
description: Некорректный запрос
/v1/outages:
/v1/city:
post:
summary: Отключения по адресу (после строгого сопоставления дома)
summary: Нормализовать только название города
description: >
Отдельный сценарий для проектов, которым нужны названия городов, а не
улицы. Возвращает канонический город по имени или алиасу.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [address]
required: [query]
properties:
address:
query:
type: string
maxLength: 128
example: "нск"
responses:
"200":
description: Канонический адрес и отключения
description: Результат
content:
application/json:
schema:
$ref: "#/components/schemas/OutagesResult"
type: object
required: [status]
properties:
status:
type: string
enum: [found, not_found]
city: { type: string }
"400":
description: Некорректный запрос
/v1/cities:
get:
summary: Канонические города, присутствующие в индексе
responses:
"200":
description: Список городов
content:
application/json:
schema:
type: object
required: [cities]
properties:
cities:
type: array
items: { type: string }
/v1/healthz:
get:
summary: Живость
@@ -78,13 +116,16 @@ components:
schemas:
Status:
type: string
enum: [found, partial, ambiguous, not_found, portal_timeout, infra_error]
enum: [found, partial, ambiguous, not_found]
Candidate:
type: object
required: [address, score]
properties:
address: { type: string }
city: { type: string }
district: { type: string }
house: { type: string }
structure: { type: string }
score: { type: number, format: double }
ResolveResult:
type: object
@@ -92,29 +133,10 @@ components:
properties:
status: { $ref: "#/components/schemas/Status" }
address: { type: string }
city: { type: string }
district: { type: string }
confidence: { type: number, format: double }
candidates:
type: array
items: { $ref: "#/components/schemas/Candidate" }
data_version: { type: string }
Outage:
type: object
properties:
type: { type: string }
system: { type: string }
address: { type: string }
start_date: { type: string }
end_date: { type: string }
action: { type: string }
OutagesResult:
type: object
properties:
status: { $ref: "#/components/schemas/Status" }
address: { type: string }
district: { type: string }
outages:
type: array
items: { $ref: "#/components/schemas/Outage" }
as_of: { type: string }
data_version: { type: string }