docs: position addrkit as a universal address service

- core = address normalization/resolution (universal, no EDDS coupling)
- "events by house" (outages) framed as an optional plugin module
- README: positioning, API table marks /v1/outages optional, selfcheck section,
  updated typo numbers and LLM variant-guard note
This commit is contained in:
Maksim Totmin
2026-10-09 21:40:46 +07:00
parent 58272c5f9d
commit 59a8b2c164
+54 -15
View File
@@ -1,13 +1,29 @@
# addrkit
**Детерминированный сервис нормализации и поиска адресов** с подключаемым
внешним слоем отключений. Разбирает вольные реплики (дроби, литеры, корпуса,
строения, опечатки), сопоставляет их с каноническим реестром и отдаёт ответ
**только из данных** — LLM не участвует в пути корректности.
**Универсальный детерминированный сервис нормализации и поиска адресов.**
Разбирает вольные реплики (дроби, литеры, корпуса, строения, опечатки),
сопоставляет их с каноническим реестром и отдаёт ответ **только из данных** —
LLM не участвует в пути корректности.
Ядро продукта — **только адрес**: оно не привязано ни к ЕДДС, ни к отключениям.
Опциональный модуль «**события по дому**» (отключения, аварии, плановые работы —
данные, привязанные к конкретному дому) подключается отдельно; ЕДДС — лишь один
из его потребителей.
Проект на Go, живёт офлайн (один статический бинарь + SQLite), без MCP в
рантайме.
## Позиционирование
- **Ядро (универсальное):** нормализация, структурирование и поиск адреса по
реестру, статусы `found/partial/ambiguous/not_found`, версия данных.
Годится для колл-центра, доставки, CRM, аналитики, любого робота.
- **Опциональный модуль «события по дому»:** получает канонический адрес из
ядра и отдаёт события, **привязанные к этому дому** (совпадение дома, дроби,
литеры и корпуса; строение игнорируется). Источник — plug-in
(`outages.Source`): сегодня ArcGIS-портал ЕДДС, завтра любой фид (ES, SQL,
API). Ядро про события ничего не знает.
## Зачем
Существующая логика поиска адреса в диалоговом сценарии страдала от
@@ -22,13 +38,13 @@
```
cmd/addrkitd сервис (HTTP)
cmd/addrkit CLI: seed | verify | resolve
cmd/addrkit CLI: seed | verify | resolve | eval | selfcheck
internal/
domain/address нормализация и разбор адреса (чистые функции)
domain/match сопоставление с индексом, статусы
domain/outages выборка отключений по дому (чистая логика)
domain/match сопоставление с индексом, статусы ← ядро
domain/outages «события по дому»: фильтр по дому ← опциональный модуль
adapters/registrystore SQLite (modernc, без CGO)
adapters/arcgis клиент портала (timeout/retry)
adapters/arcgis клиент источника событий (timeout/retry) ← пример plug-in
adapters/llm опциональный rewriter (OpenAI-совместимый)
adapters/httpapi версионированный JSON API
config конфиг + ${VAR}
@@ -41,7 +57,7 @@ internal/
"2-я Станционная улица 1/1 стр."
-> address.Parse -> {ordinal:2, name:станционная, house:1, frac:1}
-> match.Resolve -> exact street -> exact house -> FOUND
-> (опц.) outages.Source -> фильтр по дому -> ответ
-> (опц. модуль «события по дому») -> фильтр по дому -> ответ
```
## Быстрый старт
@@ -64,16 +80,17 @@ curl -s localhost:6171/v1/outages -d '{"address":"станционная 30а к
CLI без сервиса:
```bash
./bin/addrkit resolve -db registry.db "кошурникова 11/1"
./bin/addrkit verify -db registry.db
./bin/addrkit resolve -db registry.db "кошурникова 11/1"
./bin/addrkit verify -db registry.db
./bin/addrkit selfcheck -db registry.db
```
## API v1
| Метод | Путь | Назначение |
|---|---|---|
| POST | `/v1/resolve` | адрес → `{status, address, district, confidence, candidates[]}` |
| POST | `/v1/outages` | адрес → канонический адрес + отключения по дому |
| POST | `/v1/resolve` | **[ядро]** адрес → `{status, address, district, confidence, candidates[]}` |
| POST | `/v1/outages` | **[опц. модуль]** адрес → канонический адрес + события/отключения по дому |
| GET | `/v1/healthz` | живость |
| GET | `/v1/readyz` | готовность (индекс загружен) |
| GET | `/v1/version` | версия данных |
@@ -100,7 +117,12 @@ CLI без сервиса:
`partial`, а не «похожий» дом.
- **LLM — опция, выключена по умолчанию.** `adapters/llm` лишь предлагает
варианты записи адреса; результат всегда берётся из реестра, поэтому модель
не может «выдумать» адрес.
не может «выдумать» адрес. Вариант принимается только если он исправляет
*написание* улицы, не меняя слова, порядковый и дом (`compatibleVariant`).
- **Ядро универсально, события по дому — опциональный модуль.** Ядро
(`domain/address`, `domain/match`) не знает ни про ЕДДС, ни про отключения;
источник событий — plug-in (`outages.Source`), поэтому модуль переиспользуем
для любых данных, привязанных к дому.
- **Данные.** Сервис владеет своим индексом, построенным из дампа реестра.
Источник истины и регламент обновления — предмет отдельного согласования.
- **Часовой пояс.** Даты отключений трактуются в `Asia/Novosibirsk` (UTC+7).
@@ -145,7 +167,7 @@ ADDRKIT_REGISTRY=/path/to/address_registry.ndjson \
| type_front | `улица Станционная 1` | 100 % |
| corpus_synonym | `корпус` вместо `к` | 100 % |
| structure_drop | без `стр.` | 100 % |
| **typo** | опечатка в названии улицы | ~70–75 % (остальное — `ambiguous`) |
| **typo** | опечатка в названии улицы | ~78–82 % (остальное — `ambiguous`) |
| drop_ordinal / drop_letter / drop_fraction | потеря `2-я` / литеры / дроби | потери (lossy) |
Ключевой результат: **0 случаев, когда сервис подставил неверный дом**
@@ -153,6 +175,23 @@ ADDRKIT_REGISTRY=/path/to/address_registry.ndjson \
распознаются, либо честно уходят в `ambiguous`/`not_found`, но не превращаются
в чужой адрес.
С подключённым LLM-rewriter'ом (опция, `llm.enabled`) точность на опечатках
поднимается до **~90 %** при том же нуле неверных адресов (вариант модели
принимается только если исправляет лишь *написание* улицы — см. «Принятые
решения»).
## Самопроверка (`addrkit selfcheck`)
Прогоняет **весь** реестр на самого себя (не выборку):
```bash
./bin/addrkit selfcheck -db registry.db
```
На реестре 72 825 записей: **72 811 (99.98 %) резолвятся точно**, 14 — `partial`
(битые форматы дома: `27с1`, `32 к 1/4-1/6`, `112с729`), 0 `not_found`,
0 неверных.
## Локальный dev
```bash