addrkit: quality evaluation harness + parser hardening
- add internal/eval: deterministic citizen-style perturbation harness (typos, case, word order, abbreviations, filler words, dropped units) - parser: strip all street-type tokens, filler words (город/дом/д/область), trailing apartment (кв N); handle letter-before-fraction and corpus letters - matching: token-aware typo-tolerant street similarity; wider ambiguity margin to surface near-collisions instead of guessing - seed: correct parse-rate accounting, structure-aware de-duplication - CLI: 'addrkit eval' command; README with measured results Measured on the real 73,936-row registry: 99.98% parsed, 0 wrong-house safety violations across 34k+ perturbed cases.
This commit is contained in:
@@ -8,10 +8,12 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/adapters/registrystore"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/domain/match"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/eval"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/seed"
|
||||
)
|
||||
|
||||
@@ -27,6 +29,8 @@ func main() {
|
||||
os.Exit(runVerify(os.Args[2:]))
|
||||
case "resolve":
|
||||
os.Exit(runResolve(os.Args[2:]))
|
||||
case "eval":
|
||||
os.Exit(runEval(os.Args[2:]))
|
||||
case "help", "-h", "--help":
|
||||
usage()
|
||||
os.Exit(0)
|
||||
@@ -44,6 +48,7 @@ Usage:
|
||||
addrkit seed -in registry.ndjson -out registry.db [-version 2026-10-09]
|
||||
addrkit verify -db registry.db
|
||||
addrkit resolve -db registry.db "2-я Станционная улица 1/1 стр."
|
||||
addrkit eval -db registry.db [-sample 500] [-seed 1]
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -144,3 +149,31 @@ func runResolve(args []string) int {
|
||||
_ = enc.Encode(res)
|
||||
return 0
|
||||
}
|
||||
|
||||
// runEval generates citizen-style perturbations from the seeded registry and
|
||||
// reports how well the resolver copes. Results are deterministic per -seed.
|
||||
func runEval(args []string) int {
|
||||
fs := flag.NewFlagSet("eval", flag.ExitOnError)
|
||||
db := fs.String("db", "", "SQLite database")
|
||||
sample := fs.Int("sample", 500, "number of registry entries to sample")
|
||||
seedVal := fs.Int64("seed", 1, "random seed")
|
||||
_ = fs.Parse(args)
|
||||
if *db == "" {
|
||||
fmt.Fprintln(os.Stderr, "eval: -db is required")
|
||||
return 2
|
||||
}
|
||||
_, entries, err := registrystore.Load(*db)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "eval: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
ix := match.NewIndex("eval", entries, match.DefaultOptions())
|
||||
rng := rand.New(rand.NewSource(*seedVal))
|
||||
cases := eval.Generate(entries, *sample, rng)
|
||||
report := eval.Run(ix, cases)
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
_ = enc.Encode(report)
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user