ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

智能快递地址解析API:地址清洗与结构化实战

智能快递地址解析API:地址清洗与结构化实战

做快递物流或者电商后端的朋友,应该都体会过一种痛:用户填写的收货地址,根本不是人话。同一个小区,能写出“XX花园东门对面巷子进去20米”“XX苑三期12栋旁便利店后面”这种魔幻描述;一条路名能写出简体、繁体、带括号、不带括号四五种版本;地址里还经常混着收件人姓名和手机号,甚至还有一句“别放驿站,放丰巢谢谢”。这些原始地址如果不处理,直接落到分单、路由和运营分析系统里,绝对是一场灾难。

我之前在某公司做物流基础服务的时候,就负责搭建过一套智能快递地址解析API,核心能力就两个:地址清洗和地址结构化。简单说,输入一段乱七八糟的原始地址,输出规范、干净、拆好字段的标准地址:省、市、区、街道、路名、门牌号、POI参考点、收件人、电话,全部拆开摆好。这套服务上线后,分单准确率、运费计算准确率、区域统计报表的可信度,都有了质的提升。这篇博客就把这个项目的完整思路、核心细节和踩过的坑一次性讲透,后端开发、数据工程师、物流系统设计的朋友都可以直接拿去参考。

1. 为什么要做地址清洗与结构化

1.1 快递场景下的地址乱象

先看看我们当时从订单表里抽出来的真实数据(脱敏后大概这个画风):

张三 138****5678 广东深圳南山区科技园南X路XX号T2栋802 李四,收货地址:北京市朝阳区XX路一号院2号楼,电话:139...... 王五 上海 浦东新区 张江 碧波路xx弄x支弄x号 赵六送到XX小区东门往前走50米右手边便利店代收(不要放丰巢)

这些地址的共同特点是:非结构化、噪声多、信息错位。有的把省市区写到一行但没标点,有的路名和小区名混在一起,有的把收件人姓名写在最前面,有的把联系电话藏在地址最后。如果直接把这些字符串用于网点分拣,系统只能靠模糊匹配,结果就是经常分错网点,快递员多跑冤枉路,用户投诉率上升。

我见过最头疼的是那种“口语化地址”。比如“就在那个大润发对面”“XX中学旁边的小巷子”。这类地址里,真正有效的结构化信息可能只有“XX中学”,其余都是方位补充词。如何处理这些词,直接决定了清洗策略的设计方向。粗暴删除会丢信息,全保留又干扰匹配。这个问题我在第三节展开讲。

1.2 地址结构化解决了什么问题

把地址从一段文本变成结构化字段,解决的不只是“看得爽”,而是实实在在的业务问题。

第一是智能分单。快递网点覆盖的是“省—市—区—街道”这样一层层的区域,如果地址里只有“XX路”而没有省市区,系统就需要靠历史知识或者上下文推断。结构化成字段后,可以直接通过“城市+区县+街道”链路匹配到对应揽投部/代理点。

第二是运费计算。很多电商件的运费按照省份和城市划分首重续重档位。一个仅凭关键词“新疆/西藏”做粗粒度判断的系统,遇到“内蒙古”“广西”这种简称就会判断失误。结构化之后,每个字段独立参与定价规则,准确率自然就上来了。

第三是数据分析。区域销售分析、热力图展示、乡镇覆盖率统计这类场景,都必须依赖省市区县街道的规整字段。否则运营同学拿着报表问“为什么XX区的单量掉了20%”,你查了半天发现是同一个区被写成了三个不同名称,根本对不上数。

第四是C端体验优化。用户新填地址时,可以调用解析接口自动补全省市区,甚至提示“这个地址是否和之前保存的某个地址相似”,减少重复输入。这类功能上线后,收货地址填写页的转化率是有肉眼可见提升的。

所以说,地址清洗和结构化不是炫技,是整个物流履约链路的地基。地基没打牢,上面盖什么都要裂。

2. 核心技术方案选型与整体设计

2.1 为什么选“规则+词典+模型”的混合路线

确定要做之后,第一个问题就是技术选型。我当时对比了三套路线:

  • 纯正则清洗:写几百条正则覆盖各种“脏”写法,上手快,但维护成本巨大,且遇到没见过的写法直接阵亡。
  • 纯机器学习(序列标注):端到端用模型做中文地址分词和标签识别,看起来高级,但冷启动需要大量标注数据,而且线上case难解释、难做人工干预。
  • 规则+词典+少量模型混合:把确定性高的部分用规则、词典精准搞定,把模糊、残缺的边界case交给模型兜底。

最终选择了混合路线。

为什么?至少有四个原因。第一,中文地址本身就有很强规律:省市区、街道、路、门牌是一层比一层细的层次结构,这种结构用词典和层级匹配非常容易表达。第二,规则和词典天然可视、可控,出了问题可以直接查原因修词表,而模型出错了只能干瞪眼。第三,冷启动成本低,词典从公开的行政区划数据就能起步,而标注一套高质量的训练集需要好几个人的工作量。第四,混合方案可以渐进式升级——初期规则粗暴够用,线上积累疑难case之后再训练模型补位,风险可控。

如果用生活类比,正则就像一把精确到一颗齿的钥匙,词典就像一套查字典就能找到答案的题库,而模型更像一个阅读了百万篇文章后学会猜意思的人。做地址解析,手里最好同时有钥匙、题库、和那个会猜意思的人。

2.2 API整体架构与模块划分

我们的API整体大致分六个模块,一条链路走下来:

原始文本进入 -> 预处理模块(编码统一、去噪、信息抽取) -> 清洗模块(冗余词识别、非法字符清理、电话/姓名剥离) -> 分词与候选识别(词典匹配、正向最大匹配、候选词图) -> 行政区划匹配(省-市-区-街道四级父子链校验) -> 街道门牌与POI识别(路名、门牌号、小区/商圈/地标) -> 结构化输出(标准字段 + 置信度 + 业务纠错标志)

这个链路最关键的设计思路是“先粗后细、逐层锁定”。一开始不要试图一步到位地给出“省市区街道”全部字段,而是先把最粗的省识别出来,再在这个省的候选集里去匹配市,再在市里匹配区。一旦某层出现歧义(比如“朝阳区”有几个城市都有),就把候选集传递到下层,让下层结合“路名甚至POI”反向消除歧义。

还有一个容易被忽略的设计:双通道输入。解析地址时不光要看地址文本,还要看用户可能额外提供的手机号和姓名。手机号有11位数字的强规律,可以非常稳定地抽离,姓名则可以通过常用姓氏词表识别。把这两个信息从地址串里抽出来之后,剩下的才是真正要处理的“地址主体”,噪声少一大半。

2.3 技术栈与性能指标设计

架构定下来之后,技术栈反而没有那么多纠结。核心服务我用的是Java(Spring Boot),因为公司现有中间件、监控、网关都是Java体系,接入成本最低;离线数据和模型训练部分用Python,方便做数据处理和实验。

内存中加载行政区划词典和自定义词库,数据结构用的是前缀树(Trie)加哈希索引结合。前缀树负责高效匹配“以某某开头”的词条,哈希索引负责精准查询词条别名和父子关系。整套词典压缩后大概占几十MB内存,完全可以直接常驻内存。

性能目标当时定的是:单条地址解析P99耗时小于50ms,QPS支持2000+。这个目标对于纯CPU计算、无外部依赖的解析服务来说是完全可以达成的,后面实测也确实稳定在30ms左右。做得不好的多是那些还没开始就想着上GPU和大模型的方案——一个字符串匹配任务,用火箭炮打蚊子。

3. 地址清洗与结构化的核心细节

3.1 预处理:统一编码、去噪、信息剥离

预处理是整个解析的地基。很多开发者容易忽视这个环节,上来就做分词或者模型推理,结果省市区边界找不准,问题出在“广东”被写成“guangdong”、“深圳”写成“shen zhen”。

我们预处理管线按顺序做几件事:

  • 全角转半角。把“ABC”转为“ABC”,把全角逗号、空格统一成半角。
  • 大小写统一。英文部分统一小写,因为后续匹配词典用的是小写索引。
  • 数字规范化。把常见中文数字词(如“一栋”的“一”)转为阿拉伯数字“1”,但这里要小心“一号院”这类词不能机械转换,所以需要结合上下文规则,或者只对明确跟在“栋/号楼/室/单元/号”前面的数字做转换。
  • 手机号抽取。按11位数字、1开头、第二位通常是3/5/6/7/8/9的规则,从字符串中抽离。
  • 姓名剥离。用姓氏词表扫描文本头部,如果发现“X先生/女士/先生/小姐”等称谓,或词表姓氏后紧跟2-3个常用字,就当作姓名抽出来。
  • 去Emoji、控制字符、连续标点等乱码。

预处理这一步做完,字符串基本从“张三 138****5678 广东深圳南山区科技园南路某某号”变成了“广东深圳南山区科技园南路某某号”,干净得一塌糊涂。这里有个重要经验:预处理宁可保守,不要激进。比如数字规范化,如果拿不准“一号”到底是路牌号还是“一号楼”里的序号,就不要硬转,保留原样丢给后面的上下文识别。

3.2 行政区划词典与自定义词库的构建

结构化最核心的资产不是算法,而是词库。我们内部有句话:地址解析的护城河,一半在词库,另一半在词库的更新机制。

行政区划词典包含四级:省(含直辖市、自治区)、市、区县、街道乡镇。这四级数据不是一次建好就完事的,因为行政区划会有调整:撤县设区、新增高新区/经开区、街道合并。我们就吃过一次亏:某新设的高新区上线半年了,线上解析还把它识别成“未知区县”,导致该区域所有件都分到了错误网点。后来我们建了行政区划数据定时同步机制,每季度从权威渠道拉取更新,并做增量diff,自动生成变更日志。

除了官方区划,自定义词库才是真正的“脏活”。包括:

  • 别名库/简称库:“内蒙古”是“内蒙古自治区”的简称,“新疆”对应“新疆维吾尔自治区”;“苏州园区”其实指“苏州工业园区”。
  • POI库:大型小区、商圈、写字楼、学校、医院、公司园区。这些词往往不是官方区划,但深入民心。比如“中关村”严格说不是一条路也不是一个区,但用户写地址时总带着它。
  • 历史错误写法库:真实订单里出现的“广洲”“深玔”这类错别字。这类词需要从历史解析失败的case里捞出来,人工确认后进库。

词库不是建完就能用,还要考虑匹配性能和歧义。行政区划词典我们构建成树形结构,省节点下面挂市节点,市节点下面挂区县节点,这样在做层级匹配时可以不断收敛范围,避免在几百万词里盲目查找。

3.3 匹配算法:层级锁定、候选池与回溯纠错

拿到清洗后的文本,接下来就是结构化主流程。我按我们的实际实现描述一下。

第一步,省份识别。从文本头部开始,或全文中寻找第一个命中的省份词。如果识别到“广东”“广东省”,直接锁定;如果识别到别名“粤”,也要能通过别名库映射出来。需要注意“内蒙古”以“内”开头,别和“内环”混淆。这一步因为收录了省全称、简称、别名,所以绝大多数情况能稳定命中。

第二步,市级识别。在省份锁定的前提下,从剩余文本中查找城市词。这里有个坑:如果省没识别出来,但识别到了市(比如“深圳市南山区”,用户没写省),就需要用市→省的映射反推省。

第三步,区县识别。类似地,在市级锁定后查找区县词。区县在识别过程中其实是最容易出歧义的:“朝阳区”既有北京市朝阳区也有某某市朝阳区。所以我们的策略是“区县候选池”而不是“单点锁定”。识别到区县后,如果父级省市尚未完全确定,会保留多个候选路径,进入后续的街道和POI匹配。

第四步,街道/路名/门牌识别。在这个层级,规则和正则开始发力。路名一般以“路、街、大道、巷、弄、巷”结尾;门牌号是连续的数字+“号”字,也可能有“XX幢XX室”。我们会在区县候选池的基础上,用“路名+门牌号”的组合去反向验证和消歧。比如两个候选市都有“科技园南路”,如果一个市的候选区里恰好有“科技园”这个POI关键词,那么这个候选路径胜出。

第五步,POI识别。如果地址里包含“某商场”“某花园”“某大厦”等POI词,就进行匹配。POI匹配的一个重要特点是它常常可以替代门牌号,比如“XX花园1栋802”这种地址里“XX花园”就是定位核心,门牌号是辅助。

这套流程框架下,核心技巧是不要一根筋走到底,时刻允许“M候选并行+向下验证”。多数结构化错误,都源于前一层一个错误的单一选择把后一层带偏了。只要候选路径上同时存在多个可能,就不要轻易杀掉。

3.4 模型辅助:序列标注兜底

规则和词典能覆盖大量正常case,可总有一批“活在边缘”的地址,比如“周村北头朝阳巷12号隔壁第二个铁门”,这种词表和规则大概率都无能为力。为此我们训练了一个中文地址序列标注模型。

模型任务定义成:对文本中每个字符打标签,标签集合包括:省(B-PROV/I-PROV)、市、区、街道、路、门牌号、POI、姓名、电话、噪声词等。模型结构我们最终选了轻量的BiLSTM+CRF,没有上来就上预训练大模型,原因是标注数据量只有几万条,BiLSTM+CRF已经够用,且推理速度快,CPU上跑一条只要几毫秒。

训练数据来源是历史订单抽样,做了三步清洗:第一步格式清洗,第二步人工标注(一个项目组四个人标,交叉验证一致性),第三步脱敏——所有真实姓名、电话、门牌号一律替换成虚拟占位符,防止模型学到个人隐私。

在线上落地的时候,模型不是主角,而是兜底和交叉验证。规则模块给出一个解析候选,模型给出另一个候选,两者不一致时用置信度决定信谁。如果两边都低置信度,就把case丢到人工审核队列,工程师每周统一处理一回,把问题case补充到词典或者标注集里。

这套“规则主、模型辅”的架构,让我们的地址解析在常规case上精度高、速度快,在刁钻case上又有兜底能力,是比较成熟的工程解法。

4. 实操过程与核心模块实现

4.1 API接口定义与交互流程

API设计要兼顾功能完整性和接入方的易用性。我们对外暴露了POST /api/address/parse,请求与响应如下(字段做了精简,但思路一致):

{ "text": "张三138****5678广东深圳市南山区科技园南路某某大厦T2栋802", "options": { "auto_complete": true, "return_candidates": false } }

响应:结构化字段 + 置信度。

{ "code": 0, "data": { "province": "广东省", "city": "深圳市", "district": "南山区", "street": "科技园南路", "street_number": "某某号", "building": "某某大厦T2栋802", "poi": "", "contact_name": "张三", "contact_mobile": "138****5678", "confidence": 0.98, "raw_text": "张三138****5678广东深圳市南山区科技园南路某某大厦T2栋802", "error_hint": "" } }

接口的options里,auto_complete控制是否启用省市区自动补全(当用户缺省省市时,通过历史数据和区划映射推算),return_candidates控制是否返回多候选结果。从实践来看,这两个开关很有用:面对G端客户时,他们往往希望看到“为什么这个地址被分到这里”,此时把候选列表返回去,产品就能把中间过程展示成“智能分析”,信任度大增。

错误码设计上,我建议至少暴露三类:一类是输入非法(text为空或者长度异常),一类是解析失败(无法识别出任何省市区),一类是低置信度(识别出来了但信心不足)。快递公司的下游系统会用不同的错误码触发不同的业务流,比如解析失败走人工录单,低置信度走二次确认页。

4.2 核心处理流程的伪代码实现

我写一段伪代码,把主干逻辑展示出来,方便你复刻整套思路:

class AddressParser: def __init__(self, trie, aliases, pois): self.trie = trie # 行政区划前缀树 self.aliases = aliases # 简称/别名映射 self.pois = pois # POI词库 def parse(self, text: str) -> dict: # 1. 预处理 text = preprocess(text) # 全角转半角、去字符、数字规范 # 2. 信息剥离:电话、姓名,剩下地址主体 mobile = extract_mobile(text) name = extract_name(text) addr = remove_mobile_name(text, mobile, name) # 3. 地址主体清洗:去噪声、去冗余描述 cleaned, extra_desc = self.clean(addr) # 4. 层级匹配:省 -> 市 -> 区 -> 街道 province_candidates = self.match_level(cleaned, "province") if not province_candidates: province_candidates = self.infer_province_from_city(cleaned) city_candidates = [] district_candidates = [] street_candidates = [] for prov in province_candidates: for city in self.match_level_candidate(cleaned, "city", prov): city_candidates.append(city) for dist in self.match_level_candidate(cleaned, "district", city): district_candidates.append(dist) for street in self.match_level_candidate(cleaned, "street", dist): street_candidates.append(street) # 5. 门牌号/POI 识别 number = extract_street_number(cleaned) poi = self.match_poi(cleaned) # 6. 候选评分与最优路径选择 best = self.score_and_select( province_candidates, city_candidates, district_candidates, street_candidates, number, poi ) return build_result(best, mobile, name, extra_desc)

这里面有两个点我觉得值得单独拿出来说。

第一,为什么省市区匹配要用“候选列表”而不是“一次命中”?举个例子:“高新区”在很多城市都有,如果用户只写了“XX市高新区XX路”,没有更细的POI或者街道信息,那这个地址其实无法唯一确定。如果强行锁定一个区,后面所有逻辑都会基于错误前提。保留候选列表,结合门牌和POI逐步过滤,才是稳妥做法。

第二,为什么要在清洗之后才做层级匹配?我们有个惨痛教训:一开始顺序反了,先匹配省市区再做清洗,结果“省”和“电话”粘在一起“广东省138”,省市匹配直接失败。调整为先剥离电话、姓名,再清洗,最后匹配,整体准确率立刻提升了两个百分点。

4.3 性能优化与并发设计

解析服务本身是CPU密集型的字符串操作,优化空间主要在词典加载和避免重复计算。

我们用两种缓存:热词缓存和结果缓存。热词缓存是解析过程中常用的省市区词条,放在本地内存中,省去频繁查树的消耗;结果缓存是对用户输入的原始地址做哈希,如果完全相同的地址在短时间内重复解析,直接返回上一次结果,这个命中率在电商场景下非常高,因为很多用户反复提交同一段地址。

服务部署了多副本,背后挂了分布式缓存(Redis)用于全局去重和限流计数。限流策略用的是令牌桶,容量按单机2000QPS设定,超过后排队而不是直接拒绝。解析服务属于基础服务,最怕的是下游电商大促瞬时流量把它打爆,因此我们提供了降级开关:当负载超过阈值时,自动关闭POI识别和模型兜底,只做省市区街道的基础解析,保证核心字段可用。

线上监控方面,最重要的两个指标不是平均耗时,而是P99耗时和“低置信度占比”。平均耗时会被缓存抖掉,P99能真实反映最差情况;低置信度占比则能直接预警词典或模型是否需要更新。有一次我们区划词典更新不及时,低置信度占比在无提示情况下从3%涨到11%,监控告警触发后一查,果然是某新区在词典里还是老名字。

4.4 清洗模块的细节实现

清洗模块是整个项目中我认为最“手艺活”的部分。说几个容易做错的细节。

一是方位词和补充描述的处理。用户写“某小区东门往前走50米右手边便利店代收”,这里“东门往前走50米右手边便利店”是有效的配送说明,但“东门”里也包含“东”这个方位词。我们最终的策略是:创建一个“补充描述字段”,把这类描述性文字单独存到extra_desc字段里,同时从主体地址中剔除,防止干扰结构化。注意,“便利店”这类词同时又是POI候选,因此不能简单当噪声删掉。实际处理时,只有在“清洗后是否还保留有效定位信息”这个判断为否时,才把描述信息当作传送给快递员的口令保留。

二是符号统一不是无脑替换。全角逗号“,”可以统一成半角空格,但像“3单元502室”里的“-”、电话里“138-1234-5678”的连字符,则要在剥离电话之前处理。如果先把“-”变成空格,电话号码就碎了,后续抽电话的正则就会漏。因此符号处理必须分场景:电话内连字符保留,地址分隔符统一成空格。

三是不完整文本的容错。总有一些地址输入时就残缺,比如“朝阳区望京街道”,没有省也没有市。这种地址按照严格规则根本没法唯一确定,但我们不会直接判失败,而是输出省份和城市为空、光区县街道字段,然后附带一个flag提示“省市缺失”。这样下游系统可以按区县维度做模糊分单,等快递员上门再实际确认,比直接让用户重新输入要好。

5. 常见问题与排查技巧实录

5.1 地址解析高频问题速查表

我把这一年多收集的高频问题整理成一个表格,基本覆盖了常见坑。

问题现象根本原因解决办法
“内蒙古”被识别成“内”+“蒙古”粗粒度词典没有覆盖省份全称行政区划词典收录“内蒙古自治区”全称,并建立别名映射
“朝阳区”被分到错误的市多个城市都有同名区县保留区县候选集,结合后续街道/POI反向消歧
“XX广场”被当成广场路名POI和路名存在词义重叠构建“路名后缀词”优先级更高的匹配规则
“张江 碧波路xx弄”没被识别出街道用户省略了市/区,直接写街道支持通过街道词反查区县候选,做跨层级关联
“广东省”写成“广东”没问题,但“广西壮族自治区”写成“广西”后,全称匹配失效别名映射不完整全称、简称、别名、历史错误写法统一维护在同一张映射表
地址里出现“不要放丰巢”被塞进POI字段业务备注混在地址里增加“指令/备注”识别规则,按明显意图词归类

这些case之所以值得记下来,是因为它们不是孤例,做数据分析时能发现每个问题背后都跟着上千条类似订单。所以每次修复一个问题,就等于挽救了一大批业务数据。

5.2 从线上复盘总结的避坑技巧

做地址解析这一年多,最大的体会是:地址解析不是一锤子买卖,而是持续迭代的数据工程。

第一坑是贪心正则。早期为了追求一次解析率,我写了大量超长正则以覆盖各种地址变体。结果正则之间相互纠缠,改一个case崩掉三个case。后来推倒重来,把每种正则限定到最小职责范围:手机号正则只管电话,门牌号正则只管数字+号,彼此隔离,再用代码主线串联。重构后,系统维护性好了不止一个量级。

第二坑是忽略了省市区简称之外的口语化前缀。比如“芜湖经济技术开发区”是正式名称,但很多用户会写“芜湖经开区”甚至“芜开区”。这些简称不在官方行政区划库里,却在订单里高频出现。后来我们把历史订单里出现频率超过阈值但解析失败的词全部拉出来,人工筛选后进别名表,这个动作让整个解析成功率跳升了约4个百分点。

第三坑是模型评估只看整体准确率。我们一开始用“整条地址解析全对的比率”作为评估指标,结果发现模型天天在涨这个指标,但真实业务投诉没有明显改善。后来改成字段级评估(省市区分开算准确率召回率),才发现问题是区县识别一直做得不够好,但被省市的准确率掩盖了。字段级评估上线后,针对性优化区县消歧逻辑,问题才真正解决。

5.3 多音字与二义性地名处理

地址解析里有个很隐蔽的问题:多音字。比如“重庆”读作chongqing,如果用户输入拼音或者混了拼音,模型就可能吃瘪;再比如“解.放”被标点截断,词典匹配时找不到完整词。我们的处理是:在预处理阶段把文本中所有中英文混杂的字符统一映射到拼音token,同时把常见多音字在词典里构建全读音索引,这样“重庆”不管是汉字还是拼音输入都能识别。

还有一个更常见的二义性是行政区划名称的“嵌套”。比如“吉林省吉林市”,用户写“吉林市XX路”,系统识别了“吉林”作为省份,结果把“吉林市”里的“市”当成冗余词丢了。我们专门维护了一张“省名与市名重叠”的表,遇到“吉林市”这种组合时,优先匹配市级层级,再反查省级。这个坑不填的话,东三省的件几乎都会分错。

6. 影响范围与落地场景

6.1 快递网点智能分单

分单是结构化最直接的应用。以前我们的分单系统拿原始地址去匹配网点,日常准确率只能靠关键词硬蒙,经常把“XX区”和“XX经济技术开发区”搞混。接入地址解析服务后,分单引擎直接用结构化后的省市区街道字段做匹配,再辅助POI信息(比如“某大厦”直接定位到最近站点),分单准确率稳定提升到接近99%。

分单模块还支持自定义网点映射表,不同网点的覆盖范围可以按街道甚至小区配置。例如某网点覆盖“A小区、B写字楼”,当地址解析结果命中“A小区”POI时,直接锁定该网点,根本不用走街道匹配。这种精细度是原始地址根本做不到的。

6.2 运营分析与数据质量治理

把地址解析能力接入数仓ETL之后,经典的运营问题全部有了答案:哪些城市/区县的订单增量最大?哪些街道的退货率异常高?某新城区铺设门店后,周边多少存量用户能受益?这些分析以前做不到,不是BI工具不行,而是地址字段根本没法按统一的行政区划透视。结构化之后,分组聚合、地理编码、地图可视化一气呵成。

这里有个额外收益:通过解析积累的“地址质量分”指标,还能反向指导前端填单体验。收件地址质量太低的订单,会让快递员多跑路,让网点多付费。我们把这类订单识别出来,在前端提示“当前地址信息可能不完整,请补充门牌号”,用户补全率提升了近六成。

6.3 能力复用与平台化输出

地址解析做成了内部基础服务后,几乎每个业务线都想接。客服工单系统拿它来识别用户报修的地址,会员系统拿它来做收货地址标准化,营销系统拿它来分析新用户所在区域。可以说,这套能力一旦平台化,就是公司基础设施的一部分,能延伸到所有跟位置、收货、履约相关的场景。

平台化的另外一个价值是数据沉淀。每次解析都带着置信度回流,低置信度case自动进入审核队列,审核结果反哺词典和模型,形成“解析-反馈-优化”的闭环。这比任何一次性的算法升级都重要,因为它让系统的效果随着时间越来越好。

最后再讲一个我个人很深的体会:做地址解析这类系统,真正的门槛从来不是算法,而是对数据的耐心。你要愿意一点点抠词表,一遍遍看失败的case,一次次忍受“这次改了 5 个case 却有 2 个新 case 变差”的反复。但正是这些枯燥的打磨,才让系统从能用变成好用。如果你现在也在做类似的基础服务,记住一句话:先跑通,再跑准,最后跑精。别一上来就追求完美,没有一轮轮真实数据喂出来的词库和模型,什么完美都是空中楼阁。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进