建表(追加到 sql/ai_clean.sql,另出存量库迁移脚本 sql/ai_clean_synonym_init.sql):
create table if not exists ai_clean_synonym (
id bigint primary key,
std_word varchar(100) not null, -- 标准词(组内规范形,如 交易金额)
alias_word varchar(100) not null, -- 别名(如 发生额)
remark varchar(200),
status smallint default 1, -- 1 启用 / 0 停用
create_by bigint,
create_time timestamp default current_timestamp,
update_time timestamp
);
create unique index if not exists uk_ai_clean_synonym on ai_clean_synonym (std_word, alias_word);
词组模型(已确认):星型「别名 → 标准词」,一个标准词挂任意多别名;同一归一化词全表只允许出现一次(Service 新增/修改时校验,撞组直接报错,杜绝一个别名归两组的归一歧义);不做链式传递(新词必须直接指到标准词);「一个词对应多个模板字段」的泛化场景不进表,交给模糊层(包含关系天然双向)。
初始化数据 = HeaderScorer 现有 24 条硬编码词条迁进表(on conflict do nothing,可重复执行),代码删除 SYNONYMS 常量。
代码(照 AiCleanBatch / TableInfoService 现成模式):
entity/AiCleanSynonym + mapper/AiCleanSynonymMapper。service/AiCleanSynonymService:list / create / update / delete / toggleStatus + 全局唯一校验;写操作事务 afterCommit 调 GlobalCache.rebuildGlobalCacheQuietly()(照 TableInfoService.refreshGlobalCacheAfterCommit 范本)。api/AiCleanSynonymController:/aiClean/synonym(GET 列表、POST 新增、POST 修改、POST 删除、POST 启停),Result 包装。
GlobalCache.initCache() 全量读 ai_clean_synonym(status=1),构建 volatile Map<String,String> synonymMap(key = normalize(alias_word),value = normalize(std_word),整体替换、空表保留旧快照,照 directionConfMap 写法)。loadSnapshotForTesting 扩展或 setSynonymsForTesting),单测不起 Spring 也能喂词对。HeaderScorer.canonicalForScore = synonymMap.getOrDefault(normalize(word), normalize(word));缓存未初始化时退化为现状。HeaderScorer 新增统一匹配函数 matchWord(fileWord, tplWord) 返回 EXACT / SIMILAR / MISS:
全量参与:coverage / jaccard / fieldCoverage / requiredCoverage / passesStrong 里的 set.contains 全部换成 matchWord——SIMILAR 与 EXACT 同权(含必填覆盖与出口 A 直通)。实现上把「集合 contains」改为「逐模板列在文件列集合找 EXACT/SIMILAR 命中」,加 per-call memo(15×15 短串比较 × 360 模板,微秒~毫秒级)。
歧义护栏:文件列模糊命中多个模板字段时(如 本方卡号/对方卡号 同在),歧义只允许存在于打分,绝不带进绑定(见四);EXACT 永远优先于 SIMILAR。
可观测:打分入口 log.debug 一行逐列命中明细(列#5 号牌号码=EXACT / 通信类别→通信类型=SYNONYM / 备注=MISS)。
现有 en∪cn 双域查表保持,查不到时追加模糊解析:对候选模板字段做 matchWord,唯一命中才把 bind 的 fieldNameEn 改写为该字段规范 en;命中 0 个或 ≥2 个一律丢弃该绑定(该列在 forAi 按原样 TEXT 保留,语义绑定交给模型腿)。EXACT 优先于 SIMILAR。
HeaderScorerTest:配置词条后 通信类型/通信类别 整表分数提升并直通;无词条时不命中(回归锚);编辑距离对、包含对;歧义绑定丢弃。AiTemplateMatcherTest:filterBinds 模糊唯一命中改写、歧义丢弃。mvn -pl ai-server test -Dtest="com.zsjz.ai.module.aiclean.**" 回归(152 用例基线)。通信类型=通信类别 词对 → 上传含该列差异的表验证直通与绑定;现有两个车辆 xls 回归不劣化。模糊命中进入出口 A 直通闸门意味着「像」可能被判成「就是」。既有安全垫不变:WEAK 不走 A、形状安全、出口 A 前抽样 verify(必填列错误率过高转人工)、置信度放行线、既有业务表可按文件清理。词条维护建议全词映射、避免过宽词对(如「类别→类型」单字级映射会把不相关模板拉高分);「一个词对应多个模板字段」的泛化匹配交给模糊层而非词条。