从Spring微服务到RAG智能客服:互联网大厂Java面试三轮实战问答
从Spring微服务到RAG智能客服:互联网大厂Java面试三轮实战问答
场景:某互联网大厂「智能客服 + 电商」中台一面。面试官严肃冷静,候选人小Y号称“Java十年经验”,其实略显水……
一、业务背景设定
公司主营电商 + 本地生活服务(外卖、到店服务),同时建设统一智能客服系统:
- Web / App 前端接入统一网关
- 订单、支付、商品、用户、风控等服务为典型 Spring Cloud 微服务
- 消息队列使用 Kafka;缓存使用 Redis;搜索用 Elasticsearch
- 智能客服接入大模型,采用 RAG(检索增强生成)方案,对接向量数据库
- 部署使用 Docker + Kubernetes,监控使用 Prometheus + Grafana + ELK
下面进入三轮面试对话。
二、第一轮:基础能力 & Web 层(偏入门,连贯场景:下单 + 查询 + 简单客服)
第一轮 · 问题 1:Spring Boot MVC 基础
面试官: 我们的电商下单服务是 Spring Boot 开发的,用户提交订单时,会调用一个 REST 接口。请用代码或口头说明:
- 用 Spring MVC 写一个下单接口(
placeOrder),支持POST /api/orders,请求体是 JSON。 - 简单说说 Spring MVC 处理一次 HTTP 请求的大致流程。
小Y:
(自信)这个简单!
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@PostMapping
public ResponseEntity<OrderResponse> placeOrder(@RequestBody OrderRequest request) {
// 略… 假装调用 Service
OrderResponse resp = new OrderResponse();
resp.setOrderId(123L);
resp.setStatus("CREATED");
return ResponseEntity.ok(resp);
}
}
流程的话,大概就是:
- 请求进来,被某个 Dispatcher… 那个啥拦住
- 然后找到 Controller
- 调用方法返回 JSON
- 再返回给浏览器
具体就…差不多这样。
面试官: 代码风格还行,流程说得有点“差不多”,不过至少知道 DispatcherServlet 的大概职责。等会儿详细再聊。
第一轮 · 问题 2:参数校验与异常处理
面试官: 下单接口中,必须校验:
- 商品 ID 必须 > 0
- 数量必须在 1~99
- 用户未登录要返回 401
- 你会怎样在 Spring 中做参数校验?
- 全局异常你会怎么统一处理?
小Y: (认真思考)可以用 @Valid 和 @NotNull、@Min 之类的注解,然后…全局异常就写一个类,@ControllerAdvice 搞一下就好了,里面写 @ExceptionHandler,统一返回 JSON。细节我平时都让 IDEA 自动生成的。
面试官: 思路对的,虽然“IDEA 自动生成”这话暴露了一点什么。
第一轮 · 问题 3:简单缓存 - Redis
面试官: 用户下单之后会频繁查询订单详情,我们会做缓存。请回答:
- 你会怎么用 Redis 给订单详情加缓存?请简述伪代码或关键步骤。
- 如何避免缓存与数据库强一致问题?(比如订单刚创建,缓存里还是旧数据)
小Y: 可以用 Spring Cache 啊,加个 @Cacheable 就行,比如:
@Cacheable(cacheNames = "order", key = "#orderId")
public OrderDTO getOrder(Long orderId) {
return orderRepository.findById(orderId)
.orElseThrow(() -> new NotFoundException());
}
然后更新的时候用 @CacheEvict,一致性的话…咱就尽量让缓存短一点,TTL 短一点,问题就不大(略心虚)。
面试官: 短 TTL 只是缓解,并不能叫方案。等会儿给你一个标准版本。
第一轮 · 问题 4:单元测试 - JUnit5 + Mockito
面试官: 你的 OrderService 里有一个 createOrder 方法,依赖 InventoryService 检查库存:
public Order createOrder(CreateOrderRequest req) {
if (!inventoryService.checkStock(req.getSkuId(), req.getQuantity())) {
throw new OutOfStockException();
}
return orderRepository.save(...);
}
请问:
- 用 JUnit 5 + Mockito 写一个简单的单元测试怎么写?
- 为什么要 mock
InventoryService?
小Y: (很熟练)
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private InventoryService inventoryService;
@Mock
private OrderRepository orderRepository;
@InjectMocks
private OrderService orderService;
@Test
void createOrder_ok() {
when(inventoryService.checkStock(1L, 2)).thenReturn(true);
when(orderRepository.save(any())).thenReturn(new Order());
Order order = orderService.createOrder(new CreateOrderRequest(1L, 2));
assertNotNull(order);
}
}
mock 是为了不连真实库存系统嘛,不然测个方法还要连一堆服务,太慢了。
面试官: 这一题表现不错。
第一轮 · 问题 5:简单客服接口
面试官: 我们有个简单客服接口,用来检索 FAQ(还没接大模型):
- 前端传入用户问题
- 后端从数据库 / Elasticsearch 中找匹配答案
你会怎么设计这个 REST 接口?需要考虑:
- 接口路径、请求方法
- 如何兼容后续接入大模型(比如返回结构要能扩展)
小Y: 接口就 /api/support/faq/search,POST,请求体带 question。返回用 JSON 包一层 data,里面有一个 answer 字段。以后接大模型就再加个 modelAnswer 字段之类的。反正 JSON 可以随便扩展。
面试官: 思路还算 OK,能想到响应结构要预留扩展就不错。
三、第二轮:微服务 & 消息队列 & 搜索(场景:订单异步处理 + 物流追踪 + 简单推荐)
第二轮 · 问题 1:Spring Cloud 微服务拆分
面试官: 现在我们把系统拆成:订单服务、库存服务、支付服务、物流服务。使用 Spring Cloud + Spring Boot:
- 你如何做服务发现与注册?
- 若用 Spring Cloud Gateway 做统一网关,大致配置会是怎样?
小Y: (略紧张)服务发现可以用 Eureka 啊,或者 Consul。我一般就是 @EnableEurekaClient 一加,yml 里配个地址就能注册。网关的话,在 application.yml 里写 route,比如:
spring:
cloud:
gateway:
routes:
- id: order
uri: lb://order-service
predicates:
- Path=/api/orders/**
反正就是 LB 那个协议,自动通过注册中心发现服务,就不用手写地址了。
面试官: 核心要点提到了。
第二轮 · 问题 2:Kafka 异步处理 & 订单状态
面试官: 用户下单后,我们会通过 Kafka 异步处理:
- 下单成功 -> 发送
order_created事件 - 风控、积分、推荐等订阅这个事件
请回答:
- 在 Java 里用 Spring Kafka 写一个简单 Producer & Consumer 的代码结构。
- 如何保证消息“不丢、不重”尽量可控?
小Y: Producer:
@Service
public class OrderEventProducer {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendOrderCreated(String orderId) {
kafkaTemplate.send("order_created", orderId);
}
}
Consumer:
@Service
public class OrderEventConsumer {
@KafkaListener(topics = "order_created", groupId = "order-service")
public void onMessage(String message) {
// 处理
}
}
保证不丢嘛,就开 acks=all,然后再开重试。至于不重…这个…那就幂等处理一下嘛,比如消费的时候先查一下是否处理过(含糊)。
面试官: “幂等”是关键词,但实现细节你没讲清,等会儿答案部分补充。
第二轮 · 问题 3:Redis 缓存穿透、击穿、雪崩
面试官: 你的订单查询接口现在访问量很大,我们使用 Redis 缓存订单数据,但出现:
- 大量请求同一热点订单,导致缓存失效瞬间打爆 DB
- 还有一些不存在的订单 ID 被频繁查询
- 这分别是什么问题?
- 你会怎么解决?可以提到 Redis、Caffeine、Spring Cache 等组合。
小Y: 热点订单那个是缓存击穿吧?不存在订单的是缓存穿透。解决的话:
- 穿透就可以缓存空值,或者用布隆过滤器
- 击穿就…加个互斥锁,或者用本地缓存 Caffeine 顶一下
雪崩就是一起过期,就…别一起过期就好了,随机 TTL(说到这里很兴奋)。
面试官: 这一题讲得挺系统的。
第二轮 · 问题 4:Elasticsearch 订单 & 物流查询
面试官: 我们把订单和物流轨迹写入 Elasticsearch,用户可以搜索订单号、快递单号、状态等。
- 你会选择用 Spring Data Elasticsearch 还是直接用 RestHighLevelClient?为什么?
- 用 Java 代码做一个简单的“订单号精确匹配 + 状态筛选”查询伪代码。
小Y: 我更喜欢 Spring Data Elasticsearch,写 Repository 比较爽。RestHighLevelClient 太啰嗦了。查询的话:
BoolQueryBuilder query = boolQuery()
.must(termQuery("orderId", orderId))
.filter(termQuery("status", status));
然后用 template 查一下就好了。具体代码我就不背了,反正差不多那味。
面试官: 知道 Bool Query 基本就行。
第二轮 · 问题 5:监控 & 日志链路
面试官: 微服务这么多,订单调用支付、支付调用风控、风控又调第三方。我们用:
- 日志:Logback + SLF4J,集中收集到 ELK
- 链路追踪:Spring Cloud Sleuth + Zipkin / Jaeger
- 指标:Micrometer + Prometheus + Grafana
- 你会如何在代码里打日志,保证可以串起一条跨服务链路?
- 对接口的 QPS、99 分位延迟,你会如何暴露指标?
小Y: 日志就 log.info("orderId={}, status={}", orderId, status);,Sleuth 会在 MDC 里放 traceId 和 spanId,然后 ELK 里就可以按 traceId 搜索。指标的话用 Micrometer:
Timer timer = Timer.builder("http.server.requests").register(meterRegistry);
然后…Spring Boot Actuator 会暴露到 /actuator/prometheus,Prometheus 拉就行。
面试官: 概念是对的,虽然代码细节略糊。
四、第三轮:RAG 智能客服 & AI 场景(场景:RAG + 智能客服 + 风控)
第三轮 · 问题 1:RAG 智能客服整体架构
面试官: 我们现在做一个“智能客服系统”,支持:
- 基于企业文档(退款规则、物流政策)做问答
- 接入大模型(例如 OpenAI / 自研)
- 使用 RAG(检索增强生成),避免大模型“胡说八道”(Hallucination)
当前技术栈:Spring Boot、Spring AI、向量数据库(比如 Milvus / Redis 向量索引)、检索增强生成、Agent 任务编排。
请用自己的理解描述一下:
- RAG 的基本流程
- 为什么能减少幻觉
小Y: (有点慌)RAG 就是…先检索再生成嘛。用户问问题,我们先把问题向量化,去 Milvus 查相似文档,然后把这些文档塞给大模型,让它参考着回答。这样大模型就有“资料”看,就不乱编了(讲得比较口语化)。
面试官: 基本概念 OK,不过细节和专业术语略少。
第三轮 · 问题 2:向量化 & 向量数据库
面试官: 在我们的系统中:
- 文档加载后切分成片段(chunk)
- 使用 Embedding 模型(OpenAI Embedding 或本地 Ollama 模型)向量化
- 存入 Milvus / Chroma / Redis Vector
Java 服务通过 Spring AI 或自定义客户端访问向量数据库。
- 请简述 Java 服务向量检索的关键步骤。
- 如果要支持“多租户企业文档问答”,向量数据表设计要注意什么?
小Y: 步骤…差不多就是:
- 用户问题通过 HTTP 发到我们 Java 服务
- Java 服务调用 Embedding 模型,把问题变成向量
- 把向量发给向量数据库查相似向量
- 拿到文档片段,再拼 prompt 给大模型
- 返回答案
多租户的话…向量表里加个 tenantId 吧,查询的时候加过滤。还可以建索引分库啥的(有点泛)。
面试官: 核心点是 tenantId 过滤,至少说到了。
第三轮 · 问题 3:Agent & 工具调用(Tool Execution Framework)
面试官: 我们的智能客服不仅要回答问题,还要能“帮用户干活”:
- 查询订单状态(调用订单服务 REST)
- 发起退款申请
- 查询风控结果
我们用的是“Agent + 工具(Tools)”模式,大模型可根据需要调用后端工具。请回答:
- 在 Java 服务中,如何设计一个统一的“工具调用标准化”接口?(比如定义一个
Tool接口) - 如何在会话内保存“聊天记忆”(Conversation Memory),让 Agent 知道上下文?
小Y: 工具的话可以写个接口:
public interface Tool {
String getName();
ToolResult execute(String input);
}
每个工具实现这个接口,比如 OrderQueryTool,RefundTool。然后大模型那边就传一个 toolName 和参数,我们这边找到对应的 Tool 执行。至于会话记忆…就 Redis 存一下对话历史吧,按 sessionId 存一个列表,或者写数据库。具体怎么切分我就不深究了。
面试官: 方向没问题,不过现实里要细到 schema 和过期策略等。
第三轮 · 问题 4:AI 幻觉 & 风控场景
面试官: 有用户问:“我这个订单是不是已经通过人工风控审核了?”
智能客服系统必须:
- 不能瞎说“已经通过”,必须查风控服务
- 如果风控服务超时,要合理降级
- 你会如何在 Java 微服务层面,使用 Resilience4j 或 Spring Cloud 提供的能力做熔断 / 超时控制?
- 如何在 Prompt 里约束大模型“不能编造风控结论”?
小Y: Resilience4j 我知道有个 @CircuitBreaker 还有 @TimeLimiter。代码就:
@CircuitBreaker(name = "riskService", fallbackMethod = "fallback")
public RiskResult queryRisk(Long orderId) {
return restTemplate.getForObject(...);
}
Prompt 约束就…在提示词里说“如果查不到,就说查不到,不要乱编”,差不多就行(显得有点敷衍)。
面试官: 技术点提到了,不过表现出你对 Prompt Engineering 细节不太熟。
第三轮 · 问题 5:复杂工作流 & 多步任务
面试官: 有一个“复杂投诉流程”:
-
用户说明问题
-
Agent 判断是否属于物流问题
-
若是,调用物流服务获取轨迹
-
如发现异常,再调用补偿服务发券
-
整个过程需要记录在审计日志中
-
你会如何在 Java 中实现一个多步工作流?可以提到:状态机 / 工作流引擎 / 编排服务。
-
如何保证每一步的幂等性和可追踪性?
小Y: 这个啊,我会写一个“大 Service”,里面 if else,把几个步骤串起来,中间打日志就行了。太复杂了就用个…嗯…比如 Spring State Machine 或者搞个流程引擎吧(泛泛而谈)。幂等性的话,每步写个表,记录执行过没,日志就写 ELK 里呗。
面试官: 听起来你在真实项目里可能没做过完整的编排。好,今天就先到这。
五、面试官结束语
面试官: 今天三轮的问题,你基础还可以,很多高级问题回答得比较模糊。我们回去会综合评估一下你的匹配度,你先回去等通知吧,有结果 HR 会联系你。
小Y(心里 OS): “回去等通知”……这是要凉的节奏啊。
六、详细答案解析(业务场景 + 技术点)
下面是上述问题的系统化答案,按模块整理,方便小白学习。
1. Spring Boot MVC 基础 & 参数校验
1)一次请求的大致流程
- 请求进入 Servlet 容器(Tomcat/Jetty/Undertow)
- 由
DispatcherServlet作为前端控制器接管 - 通过
HandlerMapping根据 URL、HTTP 方法找到对应的 Controller 方法 - 使用
HandlerAdapter调用 Controller - 入参解析:
@RequestBody对 JSON 使用HttpMessageConverter(Jackson/Gson)反序列化@PathVariable、@RequestParam从路径 / 查询参数中解析
- 返回值通过
HttpMessageConverter序列化为 JSON - 统一写回 HttpServletResponse
2)参数校验
- 引入依赖:Hibernate Validator(Spring Boot 已集成)
- 在 VO / DTO 上加约束注解:
public class OrderRequest {
@NotNull
@Min(1)
private Long skuId;
@NotNull
@Min(1)
@Max(99)
private Integer quantity;
}
- Controller 参数加
@Valid或@Validated:
public ResponseEntity<OrderResponse> placeOrder(@Valid @RequestBody OrderRequest req) {...}
- 未登录处理:
- 使用 Spring Security 进行认证,未认证访问接口返回 401
3)全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResponse handleValidation(MethodArgumentNotValidException ex) {
// 提取字段错误信息
}
@ExceptionHandler(BusinessException.class)
public ErrorResponse handleBiz(BusinessException ex) {...}
@ExceptionHandler(Exception.class)
public ErrorResponse handleOthers(Exception ex) {...}
}
2. Redis 缓存策略(穿透 / 击穿 / 雪崩)
1)三大问题定义
- 缓存穿透:大量请求查询“根本不存在”的数据,缓存中没有,直接打爆数据库
- 缓存击穿:热点 key 在失效瞬间,大量并发请求打到数据库
- 缓存雪崩:大量 key 在同一时间过期,导致瞬间缓存失效
2)解决方案
-
穿透:
- 缓存空值(如
null或特定标记),并设置较短 TTL - 使用布隆过滤器(Redis + Guava)拦截不存在的 key
- 缓存空值(如
-
击穿:
- “互斥锁 + 双查”策略:
// 伪代码
value = redis.get(key)
if (value == null) {
if (tryLock(key-lock)) {
// 再查一次
value = redis.get(key)
if (value == null) {
value = db.query()
redis.set(key, value, ttl)
}
unlock
} else {
// 睡眠短时间后重试
}
}
-
使用本地缓存(Caffeine)做短期保护:Redis 失效后仍可读到最近值
-
雪崩:
- 设置随机 TTL(例如基础 TTL + 随机偏移)
- 拆分不同业务 key 的过期时间
3)Spring Cache 组合
- 使用
@Cacheable+@CacheEvict管理缓存 - 引入 Caffeine 做一级缓存,Redis 做二级缓存(需要自定义 CacheManager 或组合方案)
3. Kafka 消息可靠性 & 幂等消费
1)不丢消息
- Producer:
- 配置
acks=all,retries合理设置 - 开启
idempotence(幂等生产)
- 配置
- Broker:
- 使用多副本(replication.factor >= 3)
- 合理设置
min.insync.replicas
2)不乱消费(顺序 & 幂等)
- 消费端使用同一 Partition 保证单 key 顺序
- 幂等消费策略:
- 每条消息带唯一 ID(比如订单号+事件类型),消费前检查 DB 或缓存
- 消费逻辑要可重复:
- 更新时用“幂等更新”:
update ... where version = ?或on conflict do nothing - 避免非幂等操作(如直接
++整数),可以改为从状态表计算
- 更新时用“幂等更新”:
3)Spring Kafka 使用注意
- 设置手动提交 offset(
AckMode.MANUAL),在业务成功后提交 - 若消费失败:
- 使用
Dead Letter Topic记录失败消息 - 配合重试机制(如 Spring Retry)
- 使用
4. Elasticsearch 基本用法(订单 / 物流查询)
1)Spring Data Elasticsearch vs RestHighLevelClient
- Spring Data Elasticsearch:
- 提供 Repository 抽象,快速开发
- 适合简单 CRUD、条件查询
- RestHighLevelClient(或新的 Java API Client):
- 灵活、支持 ES 新特性
- 写法较复杂
在大多数业务场景下,可以先用 Spring Data Elasticsearch;对查询性能 / DSL 定制要求高的,可直接使用官方客户端。
2)简单查询示例
SearchRequest request = new SearchRequest("orders");
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("orderId", orderId))
.filter(QueryBuilders.termQuery("status", status));
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
.query(boolQuery)
.from(0)
.size(10);
request.source(sourceBuilder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
5. 监控与日志:Logback + ELK + Micrometer + Zipkin/Jaeger
1)日志链路追踪
- 使用 Spring Cloud Sleuth:
- 自动生成 traceId、spanId
- 注入到 MDC 中
- 日志格式中输出 traceId:
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg traceId=%X{traceId}%n</pattern>
- ELK(Filebeat/Logstash + Elasticsearch + Kibana)收集日志
- 按 traceId 查询整个调用链
2)指标监控
- Spring Boot Actuator + Micrometer:
- 内置 HTTP 请求指标:
http.server.requests - 数据库连接池、JVM、GC 指标
- 内置 HTTP 请求指标:
- Prometheus:
- 配置抓取
/actuator/prometheus
- 配置抓取
- Grafana:
- 基于 Prometheus 指标绘制 QPS、延迟(P95/P99)、错误率
6. RAG(检索增强生成)核心流程
1)离线阶段:文档构建
- 文档加载(Doc Loader):
- 支持 PDF、DOC、HTML、内部知识库等
- 文本切分(Chunking):
- 按段落/标题切分成合适长度(如 500~1000 字)
- 向量化(Embedding):
- 调用 Embedding 模型(OpenAI、Ollama、本地模型)
- 存储到向量数据库:
- Milvus / Chroma / Redis 向量索引
- 字段:
id,tenantId,text,vector,metadata(主题、文档类型)
2)在线阶段:问答流程
- 用户问题 -> Java 服务(Spring Boot Controller)
- Java 服务调用 Embedding 模型,将问题向量化
- 调用向量数据库做 KNN 检索:
- 过滤条件:
tenantId、文档类型 - 返回 TopK 文档片段
- 过滤条件:
- Java 服务构建 Prompt:
- 包含用户问题
- 包含检索到的文档片段(context)
- 附加系统指令(禁止胡编)
- 调用大模型(通过 Spring AI / gRPC / HTTP)
- 返回答案 + 引用文档(Sources)
3)为什么能减少幻觉
- 模型在生成时参考“真实业务文档”,减少依赖纯记忆
- Prompt 中明确要求“只能基于提供的 context 回答”
- 可以在输出中附加来源文档,让用户可验证
7. 多租户向量数据库设计
核心点:数据隔离 + 权限控制
表(或集合)关键字段示例:
id:向量记录 IDtenantId:租户 IDdocId:文档 IDchunkId:文档片段 IDvector:向量metadata:JSON(标题、标签、权限)
查询时:
- 必须带
tenantId过滤条件 - 可根据权限字段(如角色)再过滤
8. Agent & Tool 调用框架(Tool Execution Framework)
1)统一 Tool 接口设计
public interface Tool {
String getName(); // 工具名称
ToolSchema getSchema(); // 入参出参的描述(便于自动生成给大模型)
ToolResult execute(ToolRequest request);
}
- 每个业务工具(如订单查询、退款申请、物流查询)实现该接口
- 通过
ToolRegistry统一管理:
public class ToolRegistry {
private final Map<String, Tool> tools = new ConcurrentHashMap<>();
public void register(Tool tool) {
tools.put(tool.getName(), tool);
}
public Tool get(String name) {
return tools.get(name);
}
}
- 大模型侧只需要知道工具名称 + JSON Schema,就可以根据需要发起调用
2)聊天会话内存(Conversation Memory)
- 方案:
- 短期会话:Redis List / Stream
- 长期记忆:存数据库 + 定期向量化
- Key 设计:
chat:{sessionId} - 值:最近 N 轮问答记录(User/Assistant)
- Agent 处理:
- 取最近几轮对话作为上下文拼接到 Prompt
9. Resilience4j 熔断 / 限流 / 降级
1)基本使用
@CircuitBreaker(name = "riskService", fallbackMethod = "fallbackRisk")
@TimeLimiter(name = "riskService")
public CompletableFuture<RiskResult> queryRisk(Long orderId) {
return CompletableFuture.supplyAsync(() ->
restTemplate.getForObject(url, RiskResult.class)
);
}
public CompletableFuture<RiskResult> fallbackRisk(Long orderId, Throwable ex) {
// 返回“待确认”状态,告知用户稍后再查
}
2)风控场景降级策略
- 若风控接口超时:
- 返回“正在审核中,请稍后再查”等安全提示
- 不得随意回答“已通过/已拒绝”
- 在 Prompt 中明确:
- 若未获取风控结果,必须回复“结果暂不可用”,并引导用户稍后查看
10. 复杂工作流 & 审计
1)实现思路
- 简单场景:
- 用一个 Orchestrator Service(编排服务)
- 每个步骤封装成独立 Service / 方法
- 通过状态机控制流程
- 复杂场景:
- 引入工作流引擎(如 Camunda、Flowable、Activiti)
- 定义 BPMN 流程:接入 Agent 任务作为节点
2)幂等性 & 审计
- 幂等:
- 为每个投诉流程定义全局
flowId - 每个步骤执行前检测是否已完成
- 为每个投诉流程定义全局
- 审计日志:
- 建立
audit_log表:flowId,step,operator(系统/Agent/人工),timestamp,input,output
- 写入到 DB + ELK,方便追踪
- 建立
七、小结
这次面试场景,从:
- Spring Boot 基础 Web 开发、参数校验、单元测试
- 到 Redis 缓存、Kafka 异步、Elasticsearch 搜索、监控与链路追踪
- 再到 RAG 智能客服、向量数据库、多租户文档问答、Agent 工具调用、Resilience4j 降级
形成了一条完整的“电商 + 智能客服”技术链路。
对初学者而言,可以顺着这个故事,进一步去动手:
- 写一个简单下单服务(Spring Boot + MySQL + Redis)
- 用 Kafka 做订单事件通知
- 用 Elasticsearch 做订单搜索
- 再尝试接入一个简单的 RAG 问答系统(比如用本地向量库 + 开源大模型)
把故事里的内容变成自己的 Demo,印象会比看文档更深。
更多推荐



所有评论(0)