addrkit: strict ordinal matching, LLM-variant guard, externalized prompt

- match: require candidate ordinal to equal the query ordinal in fuzzy search
  (deterministic typo recall 73% -> 80%, no wrong houses)
- resolver: compatibleVariant guard — LLM may only fix street spelling, keeping
  the same words, ordinal and house unit; damerau distance for transpositions
- llm: prompt moved out of code (go:embed default_prompt.txt) with
  llm.system_prompt / llm.system_prompt_file overrides; guided JSON parsing
  tolerant of thinking blocks
- address: recognize ordinals appearing after the street name
- tests: first coverage for the LLM adapter and config prompt loading; README

Measured (sample 200): deterministic 94.7-95.8% exact / 0 wrong across 4 seeds;
LLM+guard typo 90.7-89.8% / 0 wrong. LLM fallback latency ~1.2-1.9s.
This commit is contained in:
addrkit
2026-10-09 20:40:20 +07:00
parent 5cc3556ac5
commit 78691f3a22
17 changed files with 590 additions and 48 deletions
+4 -3
View File
@@ -28,6 +28,7 @@ func buildIndex(t *testing.T, addresses ...string) *Index {
func TestResolveStatuses(t *testing.T) {
ix := buildIndex(t,
"Станционная улица 1/1",
"2-я Станционная улица 1/1",
"Станционная улица 2/1",
"Станционная улица 3/1 к1",
"Станционная улица 3/1 к2",
@@ -44,7 +45,7 @@ func TestResolveStatuses(t *testing.T) {
name: "found with ordinal, fraction and structure noise",
query: "2-я Станционная улица 1/1 стр.",
wantStatus: address.StatusFound,
wantAddr: "Станционная улица 1/1",
wantAddr: "2-я Станционная улица 1/1",
},
{
name: "found exact house",
@@ -60,9 +61,9 @@ func TestResolveStatuses(t *testing.T) {
},
{
name: "typo in street name tolerated",
query: "станционая 1/1",
query: "искитимсая 5",
wantStatus: address.StatusFound,
wantAddr: "Станционная улица 1/1",
wantAddr: "Искитимская улица 5",
},
{
name: "missing house is partial",