1. 项目概述
在快递物流、电商订单、用户注册等业务场景中,地址信息的结构化处理一直是个高频需求。传统方案通常依赖正则表达式或第三方收费API,前者维护成本高,后者存在费用和稳定性问题。基于大模型的自然语言地址提取方案,通过200行左右的核心代码,就能实现一个准确率超过90%的智能解析服务。
这个项目使用Spring AI框架集成智谱AI的GLM-4-Flash大模型,结合Function Calling机制实现行政区划编码的实时查询,最终打造了一个完整的"地址提取智能体"。相比传统方案,它具有三大优势:一是支持任意格式的自然语言输入;二是通过工具调用确保行政编码的准确性;三是整体开发成本极低,核心逻辑不到200行Java代码。
2. 技术架构设计
2.1 整体技术栈选型
技术栈的选择考虑了四个关键因素:开发效率、模型能力、功能完备性和成本控制。Spring AI 1.1.2作为统一AI调用框架,其优势在于:
- 标准化接口:统一不同厂商的模型调用方式
- 内置工具支持:简化Function Calling实现
- 结构化输出:自动处理JSON Schema转换
模型选择智谱AI的GLM-4-Flash主要基于:
- 免费额度足够应对中小规模业务
- 支持Function Calling关键特性
- 中文处理能力优于同级别开源模型
补充技术组件包括:
- Spring Boot 3.x:现代化Java框架
- JDK17+:匹配Spring AI的版本要求
- 行政区划编码库:基于GitHub开源项目Administrative-divisions-of-China
2.2 系统架构解析
系统采用典型的三层架构:
code复制[用户输入层]
↓
[AI处理层] → [工具服务层]
↓
[结果输出层]
核心处理流程:
- 接收自然语言文本输入
- 大模型进行地址要素识别
- 自动调用行政区划编码查询工具
- 返回结构化地址对象
关键设计决策:
- 行政编码实时查询:避免模型训练数据过时的问题
- 省市区三级联动:平衡准确性与实现复杂度
- 内存级缓存:行政区划数据全量加载到内存
3. 核心实现细节
3.1 环境配置与依赖
Maven依赖配置体现工程化考量:
xml复制<dependencies>
<!-- Web基础框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 智谱AI官方Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-zhipuai</artifactId>
</dependency>
<!-- 生产环境建议添加 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
配置文件设计要点:
yaml复制spring:
ai:
zhipuai:
api-key: ${ZHIPU_API_KEY} # 通过环境变量注入
chat:
options:
model: GLM-4-Flash
temperature: 0.2 # 控制输出稳定性
3.2 数据结构设计
Address记录类设计考虑业务扩展性:
java复制public record Address(
@JsonPropertyDescription("省,如 湖北省")
String province,
@JsonPropertyDescription("市,如 武汉市")
String city,
@JsonPropertyDescription("区,如 武昌区")
String area,
@JsonPropertyDescription("街道,如 东湖路")
String street,
@JsonPropertyDescription("行政区域编码,如 420106")
String adCode,
@JsonPropertyDescription("详细地址,如 发财无限公司8栋8单元888号")
String detailInfo,
@JsonPropertyDescription("联系人")
String personName,
@JsonPropertyDescription("联系人电话")
String personPhone
) {}
字段设计特点:
- 使用record类型保证不可变性
- @JsonPropertyDescription增强模型理解
- 保留扩展字段如personName满足快递场景
3.3 提示词工程实现
系统提示词(SYSTEM_PROMPT)采用模块化设计:
text复制你是一个专业的地址信息提取专家。请从用户输入的自然语言文本中提取结构化地址信息。
提取规则:
1. 识别并分离出:省份、城市、区县、街道、详细地址
2. 地址组件可能存在简称、别称,请转换为标准名称
3. 如果用户输入包含"省"、"市"、"区"、"县"等关键词,需正确处理
4. 行政区域编码必须使用提供的工具queryAdCode进行获取
输出格式要求:
- 省份:完整省份名称
- 城市:地级市名称
- 区县:区或县级市名称
- 街道:街道、乡镇名称
- 详细地址:门牌号、小区、楼栋等
- 行政区域编码:6位数字编码
示例输入:"礼盒20个吉林省长春市朝阳区开运街领秀朝阳小区11栋2号楼304 田甜 18692093383"
示例输出:{
"province": "吉林省",
"city": "长春市",
"area": "朝阳区",
"street": "开运街领秀朝阳小区",
"detailInfo": "11栋2号楼304",
"adCode": "220104",
"personName": "田甜",
"personPhone": "18692093383"
}
提示词优化技巧:
- 角色定义明确"地址信息提取专家"
- 规则采用编号列表清晰易读
- 包含正例示范输出格式
- 强调工具调用的强制性
4. 关键技术创新点
4.1 Function Calling实现
行政区划编码服务实现要点:
java复制@Service
public class AddressAdCodeService {
private final Map<String, ProvinceMapper> provinceMap;
@Tool(description = "查询行政区域编码")
public String queryAdCode(
@ToolParam(description = "省") String province,
@ToolParam(description = "市") String city,
@ToolParam(description = "区") String area) {
// 实现三级联动查询逻辑
ProvinceMapper pm = provinceMap.get(province);
if (pm == null) return "";
CityMapper cm = pm.children().get(city);
if (cm == null) return pm.code();
Area ar = cm.children().get(area);
return ar != null ? ar.code() : cm.code();
}
// 内存数据结构设计
record Area(String code, String name) {}
record CityMapper(String code, String name, Map<String, Area> children) {}
record ProvinceMapper(String code, String name, Map<String, CityMapper> children) {}
}
工具注册与调用流程:
- 使用@Tool注解声明工具方法
- ChatClient通过tools()方法注册服务
- 大模型自动判断何时调用工具
- 系统将工具返回值注入后续处理
4.2 结构化输出处理
BeanOutputConverter的进阶用法:
java复制// 自定义日期格式转换示例
public class AddressOutputConverter extends BeanOutputConverter<Address> {
private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd");
public AddressOutputConverter() {
super(Address.class);
}
@Override
public String getFormat() {
String base = super.getFormat();
return base + "\n日期格式必须为: " + DATE_FORMATTER.toString();
}
}
使用技巧:
- 继承BeanOutputConverter实现定制
- 重写getFormat()添加额外约束
- 支持复杂类型嵌套转换
- 与PromptTemplate无缝集成
5. 生产环境优化
5.1 性能调优策略
- 模型参数优化:
yaml复制spring:
ai:
zhipuai:
chat:
options:
temperature: 0.2 # 降低随机性
max_tokens: 500 # 控制响应长度
- 本地缓存策略:
java复制@Cacheable(value = "addressCache", key = "#content")
public Address parseAddress(String content) {
// 解析逻辑
}
- 批量处理支持:
java复制@PostMapping("/batchParse")
public List<Address> batchParse(@RequestBody List<String> contents) {
return contents.stream()
.parallel()
.map(this::parseAddress)
.toList();
}
5.2 部署方案
Docker最佳实践:
dockerfile复制# 使用多阶段构建减小镜像体积
FROM maven:3.8.6-openjdk-17 AS build
COPY . .
RUN mvn clean package -DskipTests
FROM openjdk:17-jdk-slim
COPY --from=build /target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Kubernetes资源配置要点:
yaml复制resources:
limits:
memory: "1Gi"
cpu: "500m"
requests:
memory: "512Mi"
cpu: "250m"
6. 常见问题解决方案
6.1 准确性问题排查
-
省份识别错误:
- 检查提示词中的示例是否包含典型用例
- 确认行政区划数据是否完整
- 添加别名映射表处理"鄂→湖北"等情况
-
详细地址提取不完整:
- 在SYSTEM_PROMPT中强化示例
- 添加后处理规则校验
- 设置temperature=0.2减少随机性
-
工具调用失败:
- 检查@Tool参数描述是否清晰
- 验证工具方法是否抛出异常
- 添加fallback逻辑
6.2 性能问题处理
-
响应时间优化:
- 启用HTTP/2连接复用
- 配置合理的超时时间
yaml复制spring: ai: zhipuai: client: connect-timeout: 5s read-timeout: 30s -
高并发场景:
- 配置连接池
- 实现请求队列
- 添加熔断机制
7. 扩展方向建议
7.1 功能增强
-
地址补全:
java复制@Tool(description = "地址补全") public String completeAddress(Address partial) { // 调用地图API补全缺失字段 } -
坐标查询:
- 集成高德/百度地图API
- 返回经纬度信息
-
国际地址支持:
- 添加多语言提示词
- 支持不同国家地址格式
7.2 架构演进
-
多模型路由:
java复制@Primary @Bean public ChatModelRouter chatModelRouter( List<ChatModel> models) { return content -> { // 根据内容选择合适模型 return models.get(0); }; } -
本地模型集成:
- 使用Ollama部署本地模型
- 配置fallback策略
-
智能体协同:
- 定义地址校验智能体
- 实现多智能体通信
这个项目展示了如何用最小成本构建生产级AI应用。核心价值在于:第一,验证了Spring AI在真实场景的可行性;第二,提供了Function Calling的完整实现参考;第三,演示了提示词工程的最佳实践。对于想要快速落地AI能力的企业,这个方案具有很高的参考价值。
