Skip to content

Spring AI 面试题库 ​

生成时间: 2026-05-22 22:56 题目数量: 10道


本轮 Spring AI 面试题库(带答案) ​

【面试题】1. ChatClient 和 ChatModel 有什么区别?实际项目怎么选? ​

【标准答案】 这两个是不同层级的抽象。ChatModel 是底层接口,直接跟模型厂商对接,你给它 Prompt,它返回 ChatResponse,是最原始的调用方式。ChatClient 是高层门面,在 ChatModel 之上封装了 Fluent API、Advisor 机制、对话记忆这些高级能力。

实际项目我首选 ChatClient,因为它的链式调用特别舒服,比如:

java
chatClient.prompt()
    .user("分析这段代码")
    .advisors(new QuestionAnswerAdvisor(vectorStore))
    .call()
    .content();

一行搞定 RAG 增强。除非你要做深度定制,比如自己实现调度逻辑,才考虑直接用 ChatModel。Spring AI 1.0 之后,官方也明确推荐用 ChatClient 作为统一入口。


【面试题】2. Advisor 机制是什么?能解决什么问题? ​

【标准答案】 Advisor 是 Spring AI 的拦截器架构,类似 Spring MVC 的 HandlerInterceptor。它在请求前后的切面中注入逻辑,比如:

  • 请求前:把历史对话塞进 Prompt、从向量库检索相关文档
  • 响应后:记录日志、转换输出格式

典型场景:

  1. ChatMemoryAdvisor:自动管理对话历史,不用每次手动拼接 Message 列表
  2. QuestionAnswerAdvisor:RAG 场景下,根据用户问题自动检索向量库,把相关文档作为上下文注入

核心接口是 CallAroundAdvisor 和 StreamAroundAdvisor,前者处理同步调用,后者处理流式响应。实现 aroundCall 方法,拿到 AdvisedRequest 做增强,再调用 nextAroundCall 传递给下一个 Advisor。


【面试题】3. VectorStore 这么多实现(PGVector、Redis、Pinecone、Chroma、Milvus),生产环境怎么选? ​

【标准答案】 选型看三个维度:数据规模、部署模式、团队能力。

PGVector:

  • 优势:团队熟悉 PostgreSQL,运维成本低,事务支持好
  • 劣势:大规模向量检索性能一般,适合中小规模(百万级以内)
  • 场景:已有 PG 基础设施,向量只是辅助功能

Milvus:

  • 优势:专为向量设计,十亿级检索性能强,支持多种索引(IVF、HNSW)
  • 劣势:独立集群,运维复杂
  • 场景:大规模向量检索,专门的向量数据库场景

Redis Stack:

  • 优势:团队熟悉,支持向量+缓存一体化,延迟低
  • 劣势:内存成本高,适合热数据
  • 场景:对延迟敏感,数据量不大

Pinecone:

  • 优势:全托管,零运维,快速上手
  • 劣势:成本高,数据出海外有合规风险
  • 场景:海外项目,快速验证

我的经验:中小项目直接 PGVector,省运维;大规模项目 Milvus;海外快速上 Pinecone。


【面试题】4. Function Calling 的 tool_call 调度流程是怎样的?底层原理是什么? ​

【标准答案】 整个流程分三步:

第一步:模型决策 用户问题 + 可用工具列表发送给模型,模型判断是否需要调工具。如果需要,返回 tool_call 类型的响应,包含函数名和参数。

第二步:函数执行 Spring AI 自动解析 tool_call,从 Spring 容器找到对应 Bean,传入参数执行。这里关键是 JSON Schema 自动生成——框架扫描 @Bean 注册的函数,根据方法签名生成 Schema,模型根据 Schema 填参数。

第三步:结果回填 执行结果封装成 tool 类型的 Message,连同原始对话一起再发给模型,模型给出最终回答。

底层原理:Spring AI 通过 FunctionCallback 接口封装函数,FunctionCallbackWrapper 负责参数解析和结果转换。配置时用 @Bean 注册,框架自动扫描并注入到 ChatClient。


【面试题】5. BeanOutputConverter 怎么用?有什么坑? ​

【标准答案】 BeanOutputConverter 用于把模型输出转换成 Java 对象,核心方法:

java
BeanOutputConverter<Order> converter = new BeanOutputConverter<>(Order.class);
Order order = converter.convert(response.getContent());

使用要点:

  1. POJO 必须有无参构造和 setter/getter
  2. 字段名要和 JSON key 一致,或用 @JsonProperty 映射
  3. 嵌套对象也能自动映射,但类型要匹配

常见坑:

  1. Schema 不准确:框架生成的 JSON Schema 有时不够精确,复杂对象可能映射失败。建议手动指定 Schema:
    java
    BeanOutputConverter<Order> converter = 
        new BeanOutputConverter<>(Order.class, customSchema);
  2. 模型幻觉字段:模型可能返回多余字段,ObjectMapper 默认不报错,但业务逻辑可能异常。建议配置 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
  3. 空值处理:模型可能返回 null,业务代码要防御

【面试题】6. ChatMemory 对话记忆怎么持久化?InMemoryChatMemory 有什么局限? ​

【标准答案】 Spring AI 的 ChatMemory 接口定义了对话历史管理规范:

java
public interface ChatMemory {
    void add(String conversationId, List<Message> messages);
    List<Message> get(String conversationId, int lastN);
    void clear(String conversationId);
}

InMemoryChatMemory 局限:

  • 数据只存内存,应用重启就丢失
  • 单机,无法横向扩展
  • 适合单轮会话或临时缓存

持久化方案: 实现自定义 ChatMemory,底层用数据库存储:

java
public class JdbcChatMemory implements ChatMemory {
    private JdbcTemplate jdbc;
    
    @Override
    public void add(String conversationId, List<Message> messages) {
        messages.forEach(msg -> {
            jdbc.update(
                "INSERT INTO chat_memory (conv_id, role, content, created_at) VALUES (?,?,?,?)",
                conversationId, msg.getMessageType().name(), msg.getContent(), Instant.now()
            );
        });
    }
    
    @Override
    public List<Message> get(String conversationId, int lastN) {
        return jdbc.query(
            "SELECT role, content FROM chat_memory WHERE conv_id = ? ORDER BY created_at DESC LIMIT ?",
            (rs, i) -> new ChatMessage(rs.getString("role"), rs.getString("content")),
            conversationId, lastN
        );
    }
}

生产环境建议:Redis(热数据)+ MySQL(冷数据),配合 TTL 自动清理。


【面试题】7. 多模型对接怎么实现切换?spring.ai.* 配置怎么配? ​

【标准答案】 Spring AI 通过 spring.ai.* 配置前缀实现多模型切换。

配置示例(OpenAI):

yaml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      base-url: https://api.openai.com
      chat:
        options:
          model: gpt-4
          temperature: 0.7

切换到 Ollama:

yaml
spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        options:
          model: llama3

多模型并存: 如果项目里要同时支持多个模型,可以定义多个 ChatModel Bean:

java
@Bean
public ChatModel openAiChat(OpenAiChatProperties props) {
    return new OpenAiChatModel(props);
}

@Bean
public ChatModel ollamaChat(OllamaChatProperties props) {
    return new OllamaChatModel(props);
}

业务代码里按需注入:

java
@Autowired @Qualifier("openAiChat")
private ChatModel openAi;

@Autowired @Qualifier("ollamaChat")
private ChatModel ollama;

动态切换:用路由策略,根据请求参数或用户配置选择模型:

java
public ChatModel resolveModel(String modelType) {
    return switch (modelType) {
        case "openai" -> openAi;
        case "ollama" -> ollama;
        default -> openAi;
    };
}

【面试题】8. 流式响应(Streaming)怎么实现?有什么注意事项? ​

【标准答案】 Spring AI 的流式响应用 Flux<String> 返回:

java
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String question) {
    return chatClient.prompt()
        .user(question)
        .stream()
        .content();
}

核心注意点:

  1. 背压处理: WebFlux 的 Flux 自带背压机制,客户端消费慢时自动降低生产速度。但要注意:如果上游模型响应很快,下游消费慢,可能导致内存积压。建议用 limitRate() 控制速率:

    java
    .stream().content().limitRate(10)
  2. 超时控制: 流式响应超时比较特殊,不能简单用 timeout(),建议用 takeUntilOther():

    java
    Flux<String> timeoutSignal = Flux.interval(Duration.ofSeconds(30));
    stream.content().takeUntilOther(timeoutSignal);
  3. 错误恢复: 流式中断要优雅降级:

    java
    .onErrorResume(e -> {
        log.error("Stream error", e);
        return Flux.just("抱歉,生成中断");
    })
  4. 前端对接: SSE 格式,前端用 EventSource 接收:

    javascript
    const source = new EventSource('/chat/stream?question=xxx');
    source.onmessage = e => console.log(e.data);

【面试题】9. Spring AI 1.0 相比之前的版本有哪些重大变化? ​

【标准答案】 Spring AI 1.0 是正式版,相比 M 系列(Milestones)有几个核心变化:

1. ChatClient 统一入口: 之前版本 ChatModel 和 StreamingChatModel 分离,1.0 之后统一用 ChatClient 的 call() 和 stream() 方法,API 更一致。

2. Advisor 架构稳定: 之前 Advisor 是实验特性,1.0 正式稳定。CallAroundAdvisor 和 StreamAroundAdvisor 接口固化,官方提供 ChatMemoryAdvisor、QuestionAnswerAdvisor 等实现。

3. VectorStore 接口统一: 之前不同向量库接口有差异,1.0 统一为:

java
public interface VectorStore {
    void add(List<Document> documents);
    List<Document> similaritySearch(String query);
    List<Document> similaritySearch(SearchRequest request);
}

新增 SearchRequest 支持分页、过滤、阈值等参数。

4. 配置前缀标准化: spring.ai.* 正式成为配置规范,所有模型配置统一前缀:

  • spring.ai.openai.*
  • spring.ai.ollama.*
  • spring.ai.anthropic.*

5. 结构化输出增强: BeanOutputConverter 支持更复杂的 POJO,新增 entity() 方法直接映射:

java
Order order = chatClient.prompt()
    .user("解析订单:" + text)
    .call()
    .entity(Order.class);

【面试题】10. 设计一个智能客服系统,要支持多轮对话、知识库检索、订单查询,用 Spring AI 怎么架构? ​

【标准答案】 我会分四层设计:

第一层:接入层

  • WebFlux 暴露两个端点:/chat(同步)、/chat/stream(流式)
  • 前端用 SSE 接收流式响应
  • 请求参数:userId、sessionId、question

第二层:对话编排层(核心)

java
@Service
public class CustomerServiceOrchestrator {
    
    private final ChatClient chatClient;
    private final VectorStore vectorStore;
    
    public String chat(String userId, String sessionId, String question) {
        return chatClient.prompt()
            .user(question)
            // 对话记忆
            .advisors(new MessageChatMemoryAdvisor(chatMemory, sessionId, 10))
            // RAG 增强
            .advisors(new QuestionAnswerAdvisor(vectorStore))
            // 工具调用
            .functions("queryOrder", "queryLogistics")
            .call()
            .content();
    }
}

第三层:工具层

  • queryOrder:查询订单状态(Bean 注册,参数:订单号)
  • queryLogistics:查询物流信息(Bean 注册,参数:运单号)
  • knowledgeSearch:知识库检索(Advisor 自动处理)

第四层:数据层

  • ChatMemory:Redis 存储,按 sessionId 分 key,TTL 24h
  • VectorStore:Milvus 存知识库文档,PGVector 存临时数据
  • 业务库:MySQL 存订单、物流数据

核心流程:

  1. 用户问题进来
  2. ChatMemoryAdvisor 自动注入历史对话
  3. QuestionAnswerAdvisor 检索知识库,把相关文档注入 Prompt
  4. 模型判断是否需要调工具(查订单/物流)
  5. Function Calling 执行查询,结果回填
  6. 模型生成最终回复
  7. 历史记录存入 Redis

性能优化:

  • 流式响应,减少首字延迟
  • 向量库索引优化(HNSW)
  • 对话历史压缩(只保留关键信息)
  • 工具调用结果缓存(热点订单)

题目统计: 共10道面试题,覆盖技术基础(2题)、架构设计(3题)、场景实操(3题)、版本更新(1题)、综合应用(1题)