Spring AI 面试题库
生成时间: 2026-05-21 23:47 题目数量: 10题
本轮 Spring AI 面试题库(带答案)
【面试题】1. Spring AI中ChatClient与直接使用ChatModel有什么区别?为什么推荐使用ChatClient?
【标准答案】ChatModel是底层模型接口,直接调用AI模型返回响应,但需要自己处理Prompt组装、历史消息管理等。而ChatClient是Spring AI 1.0推出的统一入口,提供了Fluent API风格,内置了Advisor机制、ChatMemory自动管理、Observability监控等能力。
举个例子,直接用ChatModel需要手动拼接系统提示词和用户消息,用ChatClient只需要链式调用.prompt().system("你是个助手").user("问题").call(),代码更清晰。更重要的是,ChatClient能自动集成Observability指标,方便监控调用耗时、token消耗等生产必需数据。企业项目建议统一走ChatClient,后续扩展Advisor拦截器也更方便。
【面试题】2. RAG场景下TokenTextSplitter的作用是什么?如何避免chunk切分导致语义丢失?
【标准答案】TokenTextSplitter负责把长文档切分成固定token大小的chunk,默认按字符数切分,避免超模型上下文限制。
避免语义丢失有两个关键点:一是设置合理的overlap参数,比如chunk大小500、overlap 50,这样相邻chunk有重叠部分,跨chunk的信息不会断裂。二是根据文档类型选择合适的splitter,代码文件用按段落切分,技术文档用按章节切分。实际项目中我会在embedding前先做语义切分预览,看看每个chunk是否完整表达一个概念,必要时调整chunk大小或自定义分隔符。另外,还可以在chunk中附加文档元数据,比如章节标题,帮助检索时保持上下文。
【面试题】3. Spring AI中如何实现Function Calling?@Bean注册函数的底层原理是什么?
【标准答案】实现Function Calling非常简单,只需要在Spring配置类中用@Bean定义一个返回Function类型的方法,Spring AI会自动扫描并注册。比如定义一个查询天气的函数:
@Bean
public Function<WeatherRequest, WeatherResponse> weatherFunction() {
return request -> weatherService.query(request.city());
}底层原理是Spring AI通过反射读取函数入参类的结构,自动生成JSON Schema,然后把Schema发给模型。模型判断需要调用函数时返回tool_call指令,Spring AI的调度器捕获后执行函数、把结果回填到对话历史,再次调用模型生成最终回答。整个过程对业务代码透明,开发者只需要定义函数签名,调度流程由框架完成。
【面试题】4. 多模型切换时,spring.ai.*配置如何管理不同provider的API Key和参数?
【标准答案】Spring AI统一了配置前缀为spring.ai,不同provider用子前缀区分,比如spring.ai.openai.api-key、spring.ai.anthropic.api-key、spring.ai.ollama.base-url。
生产环境推荐用外部化配置管理:API Key放环境变量或Vault等密钥管理服务,application.yml只写通用参数。多模型切换有两种方式:一是通过spring.ai.model.default配置默认模型,二是在代码中注入多个ChatModel实现类,按业务场景选择调用。比如客服用OpenAI,研发助手用Claude,代码补全用本地Ollama。关键是要做好API Key隔离和降级策略,某个provider超时能自动切换备用模型。
【面试题】5. BeanOutputConverter如何实现结构化输出?有什么常见坑?
【标准答案】BeanOutputConverter是Spring AI结构化输出的核心工具,它能把LLM的文本响应自动映射成Java对象。原理是先根据目标POJO类生成JSON Schema,作为约束条件发给模型,然后解析模型返回的JSON字符串反序列化。
常见坑有三个:第一,POJO字段命名要规范,驼峰命名模型识别更准确;第二,Schema里必须有字段描述,模型才能理解字段含义;第三,复杂嵌套对象容易解析失败,建议先用简单对象测试,逐步增加复杂度。另外,模型有时候会返回多余文本,比如"以下是JSON数据:",需要设置format参数告诉模型只返回纯JSON。实际项目我会加一层校验,解析失败时重试一次。
【面试题】6. ChatMemory的InMemoryChatMemory在生产环境有什么问题?如何持久化对话历史?
【标准答案】InMemoryChatMemory是默认的内存实现,单机场景够用,但生产环境有两个问题:一是应用重启对话历史丢失,二是无法支持多实例水平扩展,用户请求路由到不同实例拿不到历史消息。
持久化方案有三种:第一,基于Redis实现自定义ChatMemory,把对话历史按会话ID存到Redis,适合有Redis基础设施的项目;第二,用关系数据库存Message列表,适合需要审计对话内容的场景;第三,集成Spring AI官方提供的VectorStore持久化方案,结合RAG一起用。我推荐Redis方案,读写快、支持过期时间,还能和分布式Session集成。另外要注意对话历史不能无限增长,需要实现窗口截断策略,只保留最近N轮对话。
【面试题】7. QuestionAnswerAdvisor在RAG架构中的作用是什么?与手动实现similaritySearch有什么区别?
【标准答案】QuestionAnswerAdvisor是Spring AI封装的RAG Advisor,它能自动完成"问题→向量检索→答案生成"的完整流程。你只需要配置VectorStore和文档集合,Advisor会自动把用户问题转成embedding、做相似度检索、把检索结果拼到提示词里发给模型。
与手动similaritySearch的区别在于:手动方式需要自己写三步逻辑——embedding查询、检索文档、构造带上下文的Prompt。而Advisor方式只要一行配置,代码量少很多。更重要的是,Advisor能和ChatMemory、日志监控等其他Advisor组合使用,实现拦截器链式调用。比如可以在QuestionAnswerAdvisor前面加一个PromptLoggingAdvisor记录每次检索的文档内容。企业级应用推荐用Advisor,维护成本低。
【面试题】8. Spring AI项目部署到微服务架构,如何设计API Key管理和熔断限流策略?
【标准答案】API Key管理要遵循三个原则:第一,严禁硬编码,用环境变量或密钥管理服务注入;第二,按模型provider隔离Key,不同业务用不同Key,避免单Key泄露影响全局;第三,定期轮换,至少季度级别更换一次。推荐用Vault或云厂商的密钥管理服务,应用启动时动态拉取。
熔断限流分两层:对下游AI模型的调用用Resilience4j或Sentinel做熔断,设置超时时间(比如30秒)、失败比例阈值(比如50%),触发熔断后返回兜底响应。对上游用户请求用令牌桶或漏桶算法限流,避免突发流量打崩模型API配额。实际项目我会配置多级降级:首选模型超时→切换备用模型→返回缓存答案→返回友好提示。监控指标用Micrometer暴露,配合Prometheus告警。
【面试题】9. Spring AI 1.0正式版的Advisor架构相比早期版本有什么改进?如何自定义Advisor?
【标准答案】Spring AI 1.0把Advisor从实验特性升级为核心架构。早期版本通过拦截器模式扩展,配置分散且不好组合。新版本的Advisor采用责任链模式,每个Advisor可以拦截请求和响应两阶段,多个Advisor按顺序执行,还能通过@Order控制顺序。
自定义Advisor非常简单,继承RequestResponseAdvisor接口,实现aroundCall方法,在调用前后插入逻辑。比如自定义一个TokenCountAdvisor统计每次调用消耗的token数:
public class TokenCountAdvisor implements RequestResponseAdvisor {
@Override
public AdvisedResponse aroundCall(AdvisedRequest request, CallChain chain) {
AdvisedResponse response = chain.next(request);
metricsService.recordToken(response.tokenUsage());
return response;
}
}注册时用@Bean定义,Spring AI会自动扫描并加入拦截链。这种设计让横切关注点(日志、监控、审计)与业务逻辑解耦,维护性大幅提升。
【面试题】10. Spring AI的Streaming响应如何实现?前端如何优雅处理流式输出?
【标准答案】Spring AI的流式响应基于Reactor的Flux实现,ChatClient调用.stream()方法返回Flux<String>,每收到一个token就推送一次。后端用WebFlux的SseEmitter或StreamingResponseBody包装Flux,通过HTTP SSE协议推给前端。
前端处理流式输出推荐用EventSource或fetch + ReadableStream。关键点有三个:第一,实时渲染每个token,用打字机效果提升体验;第二,处理错误重连,SSE连接断开后自动重连;第三,支持中断,用户点击停止按钮能取消请求。实际项目我会用React的StreamingText组件,配合Markdown渲染,这样模型返回代码块或列表时格式正确。另外要注意,流式输出不适合需要完整响应后处理的场景,比如结构化解析,这时候还是要用同步调用。