Spring AI 面试题库
生成时间:2026-05-21 20:59
本轮 Spring AI 面试题库(带答案)
【面试题】1. Spring AI中ChatClient和ChatModel有什么区别?什么时候用哪个?
【标准答案】 这是个很好的架构设计问题。简单说,ChatModel是底层抽象,ChatClient是高层封装。
ChatModel直接对接各家AI厂商的API,比如OpenAIChatModel、OllamaChatModel,它负责发请求、收响应,是最基础的调用入口。你用它就得自己拼Prompt、自己处理响应。
ChatClient是Spring AI 1.0推出的统一入口,它内部用的是ChatModel,但给你封装了Fluent API,比如chatClient.prompt().user("问题").call().content(),链式调用更优雅。
什么时候用ChatClient?
- 大部分业务场景,用ChatClient就够了,代码简洁、可读性好
- 需要Advisor机制拦截处理(比如加对话记忆、日志、安全校验)
- 需要用Fluent API快速实现功能
什么时候用ChatModel?
- 需要深度定制,比如自己实现重试逻辑、自己封装Advisor
- 需要直接访问底层模型能力,比如获取token使用量、原始响应头
- 迁移老代码,原来用的是ChatModel
实际项目里,90%的情况用ChatClient,特殊场景才下沉到ChatModel。
【面试题】2. Spring AI的Advisor机制是怎么工作的?能解决什么问题?
【标准答案】 Advisor是Spring AI 1.0的核心架构之一,类似于Spring AOP的切面,但专门针对AI调用链。
工作原理: 每个Advisor可以在调用前和调用后拦截:
aroundCall:拦截同步调用aroundStream:拦截流式调用getOrder:定义执行顺序
调用链是这样的:ChatClient收到请求 → 按Order顺序执行所有Advisor的前置逻辑 → 调用ChatModel → 按逆序执行Advisor的后置逻辑 → 返回结果。
能解决什么问题?
- 对话记忆:MessageChatMemoryAdvisor在请求前自动注入历史消息,在响应后保存新消息
- RAG检索:QuestionAnswerAdvisor在请求前先查向量库,把相关知识拼到Prompt里
- 安全校验:请求前检查用户输入是否有敏感词、是否越权
- 日志监控:记录每次调用的耗时、token数、成功失败
- 限流熔断:在Advisor里集成Resilience4j,实现调用量控制
怎么自定义Advisor? 实现CallAroundAdvisor接口,重写aroundCall方法,在里面写拦截逻辑。比如:
public class LoggingAdvisor implements CallAroundAdvisor {
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
long start = System.currentTimeMillis();
AdvisedResponse response = chain.nextAroundCall(advisedRequest);
long duration = System.currentTimeMillis() - start;
log.info("AI调用耗时: {}ms", duration);
return response;
}
}Advisor让AI调用的横切关注点可以复用,不用在每个业务代码里重复写。
【面试题】3. Spring AI做RAG时,VectorStore选型要考虑哪些因素?PGVector、Redis、Pinecone怎么选?
【标准答案】 这是个很实际的架构决策问题,没有标准答案,要看你的场景。
选型考虑因素:
数据量级
- 几万条:Redis、PGVector都行
- 百万级以上:Pinecone、Milvus、Chroma这种专门的向量库更合适
查询频率和延迟要求
- 高频低延迟:Pinecone(托管服务,全球CDN加速)、Redis(内存级)
- 低频可接受稍慢:PGVector(磁盘存储)
部署方式
- 不想运维:Pinecone全托管,开箱即用
- 必须私有化:PGVector(复用现有PostgreSQL)、Milvus(自建)
成本
- 已有PostgreSQL:PGVector零额外成本,装个插件就行
- 大规模高并发:Pinecone按调用量计费,成本可控
- Redis:占内存,向量数据量大了成本会上去
生态和稳定性
- Pinecone:成熟稳定,Spring AI官方支持好
- PGVector:PostgreSQL生态,适合已有PG基础架构的团队
- Milvus:开源社区活跃,但自建有运维成本
我的建议:
- 小项目、原型验证:PGVector,复用现有数据库,省事
- 中型项目、要稳定:Pinecone,托管服务不用运维
- 大厂、私有化要求:Milvus,自己掌控数据
- 已有Redis基础设施:用Redis Vector,统一技术栈
关键是不要过度设计,从简单开始,量级上来再迁移。
【面试题】4. Spring AI的Function Calling是怎么实现的?@Bean注册函数后,模型怎么知道该调哪个?
【标准答案】 Function Calling是让AI模型能"动手做事"的关键机制。Spring AI的实现分三步:
第一步:注册函数 写一个普通的Java方法,用@Bean注册到Spring容器:
@Bean
public Function<WeatherRequest, WeatherResponse> weatherFunction() {
return request -> {
// 调用天气API
return new WeatherResponse("北京晴天,25度");
};
}第二步:自动生成JSON Schema Spring AI会自动扫描这个Function,读取泛型参数类型(WeatherRequest),生成JSON Schema,包含:
- 函数名(默认是bean名)
- 函数描述(从方法名或@Description注解提取)
- 参数结构(参数名、类型、是否必填)
这个Schema会作为tools参数发给AI模型。
第三步:模型决策和调度 用户问"北京今天天气怎么样",模型看到有weatherFunction可用,判断该调它,返回tool_call指令。Spring AI收到后:
- 解析tool_call,拿到函数名和参数
- 从Spring容器找到对应的Function bean
- 反序列化参数为WeatherRequest对象
- 调用函数,拿到返回值
- 把结果作为tool消息发回给模型
- 模型生成最终回答:"北京今天晴天,气温25度"
关键点:
- 你不用写调度逻辑,Spring AI自动完成
- 函数描述很重要,模型靠它判断什么时候该调
- 可以注册多个函数,模型自己选
- 支持多轮tool_call,一个函数的结果可以触发另一个函数
这就是为什么Spring AI的Function Calling这么好用,你只需要写业务函数,其他都交给框架。
【面试题】5. Spring AI的BeanOutputConverter有什么坑?怎么避免结构化输出失败?
【标准答案】 BeanOutputConverter能把AI返回的文本自动转成Java对象,听起来很美好,但确实有坑。
常见坑:
Schema生成不准确 如果POJO字段名不清晰,生成的JSON Schema描述模糊,模型不知道该填什么,容易乱填或漏填。
解决办法:给字段加@Description注解,明确说明每个字段的含义和格式要求。
模型输出不稳定 同一个Prompt,模型有时候返回完整JSON,有时候返回带解释的文本,导致解析失败。
解决办法:在Prompt里明确要求"只返回JSON,不要任何解释",用format方法把Schema注入Prompt。
复杂嵌套结构处理差 如果POJO里有List、Map、嵌套对象,模型容易搞错结构,尤其是数组为空时。
解决办法:简化结构,避免深层嵌套;或者用Map<String, Object>兜底,自己再转。
特殊字符和格式问题 JSON里的引号、换行符可能被模型转义或不转义,导致解析失败。
解决办法:用entity()方法替代BeanOutputConverter,它更稳定。
中文字段名支持差 有些模型对中文字段名支持不好,生成的JSON可能乱码。
解决办法:字段名用英文,@Description里写中文描述。
最佳实践:
// 推荐:直接用entity()
Person person = chatClient.prompt()
.user(u -> u.text("提取信息:{text}").param("text", text))
.call()
.entity(Person.class);
// 如果用BeanOutputConverter,记得加描述
@Bean
public static class Person {
@Description("姓名,2-4个中文字符")
private String name;
@Description("年龄,正整数")
private Integer age;
}结构化输出这块,1.0版本比早期版本稳定多了,但还是要写好Prompt和Schema,不要完全依赖自动转换。
【面试题】6. Spring AI的ChatMemory怎么持久化?生产环境怎么设计对话记忆方案?
【标准答案】 ChatMemory是对话体验的关键,但默认的InMemoryChatMemory重启就没了,生产环境必须持久化。
持久化方案:
数据库存储 最常见的方案。Spring AI提供ChatMemoryRepository接口,可以自己实现:
javapublic class JdbcChatMemoryRepository implements ChatMemoryRepository { public void add(String conversationId, Message message) { jdbcTemplate.update( "INSERT INTO chat_memory (conv_id, role, content, created_at) VALUES (?, ?, ?, ?)", conversationId, message.getMessageType(), message.getContent(), new Date() ); } }表结构设计:conversation_id、message_type、content、created_at、metadata。
Redis存储 适合高频访问场景。用Redis的List存储消息,conversation_id作为key:
javapublic void add(String conversationId, Message message) { redisTemplate.opsForList().rightPush( "chat:" + conversationId, message.getContent() ); }设置过期时间,避免无限增长。
混合方案
- 最近10条消息放Redis,快速读取
- 历史消息存数据库,需要时再查
- 定期归档到对象存储
生产环境设计要点:
消息数量控制 不能无限存消息,token数会爆。通常保留最近20-50条,或者用滑动窗口。
去重和压缩 相似消息去重,长消息压缩摘要,节省token和存储。
用户隔离 每个用户/会话独立的conversation_id,互不干扰。
性能优化
- 分页加载历史消息
- 缓存热点会话
- 异步写入数据库
合规性
- 敏感消息加密存储
- 按法规要求定期清理
- 用户可删除自己的对话记录
Spring AI的MessageChatMemoryAdvisor会自动调用ChatMemory的add和get方法,你只需要实现持久化层,上层逻辑不用改。
【面试题】7. Spring AI怎么实现多模型切换?同一个项目里怎么同时用OpenAI和通义千问?
【标准答案】 多模型切换是Spring AI的强项,配置层面和代码层面都能做。
配置方式:
application.yml里配置多个模型:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.openai.com
dashscope:
api-key: ${DASHSCOPE_API_KEY}Spring AI会自动创建两个ChatModel的bean:openAiChatModel和dashscopeChatModel。
代码层切换:
@Service
public class AiService {
@Autowired
private ChatModel openAiChatModel;
@Autowired
private ChatModel dashscopeChatModel;
public String chatWithOpenAi(String question) {
return openAiChatModel.call(new Prompt(question)).getResult().getOutput().getContent();
}
public String chatWithDashscope(String question) {
return dashscopeChatModel.call(new Prompt(question)).getResult().getOutput().getContent();
}
}动态切换:
如果要根据用户或场景动态选模型,可以用Map注入:
@Service
public class AiRouterService {
private final Map<String, ChatModel> chatModels;
public AiRouterService(List<ChatModel> models) {
this.chatModels = models.stream()
.collect(Collectors.toMap(
m -> m.getClass().getSimpleName().replace("ChatModel", "").toLowerCase(),
Function.identity()
));
}
public String chat(String model, String question) {
ChatModel chatModel = chatModels.get(model);
if (chatModel == null) {
throw new IllegalArgumentException("不支持的模型: " + model);
}
return chatModel.call(new Prompt(question)).getResult().getOutput().getContent();
}
}配置前缀切换(Spring AI 1.0):
Spring AI 1.0统一了配置前缀spring.ai.*,你可以:
spring:
ai:
model: openai # 默认用openai运行时通过环境变量切换:SPRING_AI_MODEL=dashscope
实际应用场景:
- 国内用户用通义千问,国外用户用OpenAI
- 简单问题用小模型(快、便宜),复杂问题用大模型
- 某模型故障时,自动切换备用模型
Spring AI的多模型支持很灵活,关键是设计好路由策略,别让模型选型变成if-else地狱。
【面试题】8. Spring AI做RAG时,TokenTextSplitter怎么配置分块策略?太大太小有什么问题?
【标准答案】 分块是RAG的关键,分得好不好直接影响检索效果。
TokenTextSplitter的配置参数:
TokenTextSplitter splitter = new TokenTextSplitter(
500, // defaultChunkSize: 每块最大token数
100, // minChunkSizeChars: 每块最小字符数
50, // minChunkLengthToEmbed: 太小的块不处理
1000, // maxNumChunks: 最大块数,防爆炸
true // keepSeparator: 是否保留分隔符
);分块太大的问题:
- 检索时噪声多,相关性差,相似度分数被稀释
- 超过模型的context window,被截断
- 每块信息量太大,模型抓不住重点
分块太小的问题:
- 语义完整性破坏,一句话拆成两块,上下文断裂
- 检索命中率下降,关键词可能分布在多块
- 向量库压力增大,块数爆炸,查询变慢
最佳实践:
根据内容类型调整
- 技术文档:500-800 tokens,通常一个概念一个段落
- 法律合同:1000-1500 tokens,条款完整性更重要
- 新闻文章:300-500 tokens,新闻段落短
设置合适的重叠 TokenTextSplitter默认没有重叠,可以自己实现滑动窗口:
java// 伪代码:每块重叠20% List<String> chunks = new ArrayList<>(); for (int i = 0; i < text.length(); i += chunkSize - overlap) { chunks.add(text.substring(i, Math.min(i + chunkSize, text.length()))); }重叠能保证边界处的上下文不丢失,推荐10-20%。
按语义分块 更高级的做法是按段落、章节分,而不是按token硬切。可以先用LLM做摘要,再按摘要分块。
监控检索效果 生产环境要监控:
- 每块的平均token数
- 检索命中率(召回的块是否包含答案)
- 用户反馈的相关性评分
分块没有万能公式,要根据自己的数据调优。我的经验是先从默认500开始,看检索效果再调整。
【面试题】9. Spring AI项目怎么接入Micrometer做监控?关键指标有哪些?
【标准答案】 Spring AI天然集成Micrometer,开箱就能用,但得知道看什么指标。
接入步骤:
添加依赖:
xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>配置暴露端点:
yamlmanagement: endpoints: web: exposure: include: prometheus, health, metrics访问/metrics端点,能看到Spring AI自动注册的指标。
关键指标:
调用次数
spring.ai.chat.model.calls:ChatModel调用次数- 按模型分类:OpenAI、通义千问各有计数
延迟和耗时
spring.ai.chat.model.latency:P50、P95、P99耗时- 关注P99是否超过用户可接受的等待时间(通常3-5秒)
Token使用量
spring.ai.chat.model.tokens.prompt:输入token数spring.ai.chat.model.tokens.completion:输出token数- 按用户、按模型分类统计成本
错误率
spring.ai.chat.model.errors:调用失败次数- 按错误类型分类:超时、限流、模型错误
向量库指标
spring.ai.vectorstore.search.latency:向量检索耗时spring.ai.vectorstore.insert.count:向量插入数量
告警配置:
# Prometheus告警规则示例
groups:
- name: spring-ai
rules:
- alert: HighErrorRate
expr: rate(spring_ai_chat_model_errors[5m]) > 0.1
for: 2m
annotations:
summary: "AI调用错误率过高"
- alert: HighLatency
expr: histogram_quantile(0.99, rate(spring_ai_chat_model_latency_bucket[5m])) > 5
annotations:
summary: "AI调用P99延迟超过5秒"可视化面板:
推荐用Grafana,导入Spring Boot官方dashboard,加上AI相关指标的面板:
- 实时QPS曲线
- Token消耗柱状图
- 错误率饼图
- 各模型性能对比
监控这块,Spring AI做得挺好,指标都有,关键是把告警和可视化搭起来,出了问题第一时间知道。
【面试题】10. Spring AI 1.0相比之前版本有什么重大更新?对开发者意味着什么?
【标准答案】 Spring AI 1.0是里程碑版本,架构和API都有大变化。
重大更新:
ChatClient统一入口 之前调用AI有好几种方式,ChatModel、AiClient、PromptTemplate,容易混乱。1.0统一成ChatClient,Fluent API风格,代码更清晰:
javachatClient.prompt() .system("你是一个助手") .user("你好") .call() .content();Advisor架构 之前对话记忆、RAG、安全校验都要自己实现拦截逻辑。1.0引入Advisor机制,像Spring AOP一样,横切关注点可插拔。写一个Advisor,到处复用。
VectorStore接口统一 之前各家向量库的API不一致,切换成本高。1.0统一了VectorStore接口,增删改查方法标准化,切换向量库只需要换个bean。
spring.ai.*配置前缀 配置项统一了,之前是spring.openai.、spring.ollama.,现在都是spring.ai.*前缀,多模型配置更清晰:
yamlspring: ai: openai: api-key: xxx dashscope: api-key: xxxStructured Output增强 BeanOutputConverter更稳定,entity()方法一行代码实现结构化输出。Schema生成更智能,复杂对象也能处理。
Observability内置 之前监控要自己埋点。1.0内置Micrometer支持,自动上报调用次数、延迟、token数。
对开发者意味着什么?
- 代码更简洁:ChatClient的Fluent API,代码可读性提升
- 扩展更容易:Advisor让横切逻辑可插拔,代码不耦合
- 切换成本低:VectorStore接口统一,换个向量库改个配置就行
- 运维更友好:监控指标内置,搭Grafana dashboard半小时搞定
迁移建议:
如果是新项目,直接用1.0,别回头看旧版本。
如果是老项目要升级,注意:
- ChatModel还能用,但推荐迁移到ChatClient
- PromptTemplate被ChatClient的prompt()方法替代
- 检查配置文件,改成spring.ai.*前缀
- 自定义的拦截逻辑,改成Advisor实现
Spring AI 1.0是成熟的标志,API稳定了,架构清晰了,可以放心在生产环境大规模使用。
本轮题目统计:
- 技术基础:题1、题4、题5
- 架构设计:题2、题6、题7
- 场景实操:题3、题8
- 性能优化:题9
- 版本更新:题10
覆盖考点: ChatClient、ChatModel、Advisor、RAG、VectorStore、Function Calling、BeanOutputConverter、ChatMemory、多模型切换、TokenTextSplitter、Micrometer监控、Spring AI 1.0新特性