Java 生态终于有了统一的 AI 门面
之前接 OpenAI 要手搓 HTTP,接 Azure 又是一套,模型一换代码重写。团队里三个人各自封装了一遍,接口还不一样。Spring AI 在 2024 年初推出 1.0 里程碑版本,把各家模型、向量库抽象成统一接口。我拿它重构了工单助手,迁移成本比预期低,三个人的代码也统一了。不过必须说明,它目前还是早期版本,没到成熟稳定阶段。
ChatClient:统一的对话入口
1.0 里程碑里 ChatClient 是核心门面,写法很 Spring,流式和非流式都支持:
ChatClient client = ChatClient.builder(openAiModel).build();
String ans = client.prompt()
.user("订单 A123 的状态?")
.call()
.content();
换模型只需换底层 ChatModel 实现,业务代码不动。我们同时接了 OpenAI 和本地 Ollama,靠配置切换,测试环境用本地模型零成本,CI 里也能跑通。
Advisor:可复用的横切逻辑
Advisor 类似 Spring 的拦截器,能在请求前后插逻辑。我们用它做两件大事:
- QuestionAnswerAdvisor:把检索到的上下文自动拼进 prompt,实现 RAG,问答质量明显提升;
- 自定义 Advisor 做 Token 统计和敏感词过滤,统一埋点:
Advisor usageAdvisor = (request, next) -> {
var res = next.next(request);
log.info("tokens={}", res.metadata().get("usage"));
return res;
};
client.prompt().advisors(usageAdvisor).user("...").call();
模型抽象与向量存储集成
Spring AI 把 EmbeddingModel、VectorStore 也做了抽象。RAG 流程一气呵成,不用自己拼 prompt:
VectorStore store = new SimpleVectorStore(embeddingModel);
store.add(List.of(new Document("退货政策...", Map.of("src","policy"))));
// 检索增强
String ctx = store.similaritySearch("怎么退货").get(0).getText();
client.prompt().user(u -> u.text("根据上下文回答:{c}\n问题:{q}")
.param("c", ctx).param("q", question)).call();
向量存储接口统一了 Redis、PGVector、ES 等后端,换存储不换业务代码,我们后来从 SimpleVectorStore 迁到 RedisVectorStore,业务零改动。
早期版本的坑
必须说明,Spring AI 1.0 还是里程碑/早期版本,远没到成熟稳定,API 变动较频。我们踩到:Advisor 链顺序不同结果差异大;某些模型的 function calling 支持不全;文档落后于代码,得读源码。生产用建议锁定版本号,升级前跑回归,别追最新里程碑。
小结
Spring AI 让 Java 开发者第一次有了"AI 领域的 Spring Data"般的统一感:ChatClient 管对话、Advisor 管横切、VectorStore 管知识。虽还是早期版本不够成熟,但方向对,值得在内部工具和小场景先落地,等它更稳再上核心链路。我们已经在工单助手和内部知识库两个场景用上了。