从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 接口。请用代码或口头说明:

  1. 用 Spring MVC 写一个下单接口(placeOrder),支持 POST /api/orders,请求体是 JSON。
  2. 简单说说 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);
    }
}

流程的话,大概就是:

  1. 请求进来,被某个 Dispatcher… 那个啥拦住
  2. 然后找到 Controller
  3. 调用方法返回 JSON
  4. 再返回给浏览器

具体就…差不多这样。

面试官: 代码风格还行,流程说得有点“差不多”,不过至少知道 DispatcherServlet 的大概职责。等会儿详细再聊。


第一轮 · 问题 2:参数校验与异常处理

面试官: 下单接口中,必须校验:

  • 商品 ID 必须 > 0
  • 数量必须在 1~99
  • 用户未登录要返回 401
  1. 你会怎样在 Spring 中做参数校验?
  2. 全局异常你会怎么统一处理?

小Y: (认真思考)可以用 @Valid@NotNull@Min 之类的注解,然后…全局异常就写一个类,@ControllerAdvice 搞一下就好了,里面写 @ExceptionHandler,统一返回 JSON。细节我平时都让 IDEA 自动生成的。

面试官: 思路对的,虽然“IDEA 自动生成”这话暴露了一点什么。


第一轮 · 问题 3:简单缓存 - Redis

面试官: 用户下单之后会频繁查询订单详情,我们会做缓存。请回答:

  1. 你会怎么用 Redis 给订单详情加缓存?请简述伪代码或关键步骤。
  2. 如何避免缓存与数据库强一致问题?(比如订单刚创建,缓存里还是旧数据)

小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(...);
}

请问:

  1. 用 JUnit 5 + Mockito 写一个简单的单元测试怎么写?
  2. 为什么要 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 接口?需要考虑:

  1. 接口路径、请求方法
  2. 如何兼容后续接入大模型(比如返回结构要能扩展)

小Y: 接口就 /api/support/faq/searchPOST,请求体带 question。返回用 JSON 包一层 data,里面有一个 answer 字段。以后接大模型就再加个 modelAnswer 字段之类的。反正 JSON 可以随便扩展。

面试官: 思路还算 OK,能想到响应结构要预留扩展就不错。


三、第二轮:微服务 & 消息队列 & 搜索(场景:订单异步处理 + 物流追踪 + 简单推荐)

第二轮 · 问题 1:Spring Cloud 微服务拆分

面试官: 现在我们把系统拆成:订单服务、库存服务、支付服务、物流服务。使用 Spring Cloud + Spring Boot:

  1. 你如何做服务发现与注册?
  2. 若用 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 事件
  • 风控、积分、推荐等订阅这个事件

请回答:

  1. 在 Java 里用 Spring Kafka 写一个简单 Producer & Consumer 的代码结构。
  2. 如何保证消息“不丢、不重”尽量可控?

小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 被频繁查询
  1. 这分别是什么问题?
  2. 你会怎么解决?可以提到 Redis、Caffeine、Spring Cache 等组合。

小Y: 热点订单那个是缓存击穿吧?不存在订单的是缓存穿透。解决的话:

  • 穿透就可以缓存空值,或者用布隆过滤器
  • 击穿就…加个互斥锁,或者用本地缓存 Caffeine 顶一下

雪崩就是一起过期,就…别一起过期就好了,随机 TTL(说到这里很兴奋)。

面试官: 这一题讲得挺系统的。


第二轮 · 问题 4:Elasticsearch 订单 & 物流查询

面试官: 我们把订单和物流轨迹写入 Elasticsearch,用户可以搜索订单号、快递单号、状态等。

  1. 你会选择用 Spring Data Elasticsearch 还是直接用 RestHighLevelClient?为什么?
  2. 用 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
  1. 你会如何在代码里打日志,保证可以串起一条跨服务链路?
  2. 对接口的 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 任务编排。

请用自己的理解描述一下:

  1. RAG 的基本流程
  2. 为什么能减少幻觉

小Y: (有点慌)RAG 就是…先检索再生成嘛。用户问问题,我们先把问题向量化,去 Milvus 查相似文档,然后把这些文档塞给大模型,让它参考着回答。这样大模型就有“资料”看,就不乱编了(讲得比较口语化)。

面试官: 基本概念 OK,不过细节和专业术语略少。


第三轮 · 问题 2:向量化 & 向量数据库

面试官: 在我们的系统中:

  • 文档加载后切分成片段(chunk)
  • 使用 Embedding 模型(OpenAI Embedding 或本地 Ollama 模型)向量化
  • 存入 Milvus / Chroma / Redis Vector

Java 服务通过 Spring AI 或自定义客户端访问向量数据库。

  1. 请简述 Java 服务向量检索的关键步骤。
  2. 如果要支持“多租户企业文档问答”,向量数据表设计要注意什么?

小Y: 步骤…差不多就是:

  1. 用户问题通过 HTTP 发到我们 Java 服务
  2. Java 服务调用 Embedding 模型,把问题变成向量
  3. 把向量发给向量数据库查相似向量
  4. 拿到文档片段,再拼 prompt 给大模型
  5. 返回答案

多租户的话…向量表里加个 tenantId 吧,查询的时候加过滤。还可以建索引分库啥的(有点泛)。

面试官: 核心点是 tenantId 过滤,至少说到了。


第三轮 · 问题 3:Agent & 工具调用(Tool Execution Framework)

面试官: 我们的智能客服不仅要回答问题,还要能“帮用户干活”:

  • 查询订单状态(调用订单服务 REST)
  • 发起退款申请
  • 查询风控结果

我们用的是“Agent + 工具(Tools)”模式,大模型可根据需要调用后端工具。请回答:

  1. 在 Java 服务中,如何设计一个统一的“工具调用标准化”接口?(比如定义一个 Tool 接口)
  2. 如何在会话内保存“聊天记忆”(Conversation Memory),让 Agent 知道上下文?

小Y: 工具的话可以写个接口:

public interface Tool {
    String getName();
    ToolResult execute(String input);
}

每个工具实现这个接口,比如 OrderQueryToolRefundTool。然后大模型那边就传一个 toolName 和参数,我们这边找到对应的 Tool 执行。至于会话记忆…就 Redis 存一下对话历史吧,按 sessionId 存一个列表,或者写数据库。具体怎么切分我就不深究了。

面试官: 方向没问题,不过现实里要细到 schema 和过期策略等。


第三轮 · 问题 4:AI 幻觉 & 风控场景

面试官: 有用户问:“我这个订单是不是已经通过人工风控审核了?”

智能客服系统必须:

  • 不能瞎说“已经通过”,必须查风控服务
  • 如果风控服务超时,要合理降级
  1. 你会如何在 Java 微服务层面,使用 Resilience4j 或 Spring Cloud 提供的能力做熔断 / 超时控制?
  2. 如何在 Prompt 里约束大模型“不能编造风控结论”?

小Y: Resilience4j 我知道有个 @CircuitBreaker 还有 @TimeLimiter。代码就:

@CircuitBreaker(name = "riskService", fallbackMethod = "fallback")
public RiskResult queryRisk(Long orderId) {
    return restTemplate.getForObject(...);
}

Prompt 约束就…在提示词里说“如果查不到,就说查不到,不要乱编”,差不多就行(显得有点敷衍)。

面试官: 技术点提到了,不过表现出你对 Prompt Engineering 细节不太熟。


第三轮 · 问题 5:复杂工作流 & 多步任务

面试官: 有一个“复杂投诉流程”:

  1. 用户说明问题

  2. Agent 判断是否属于物流问题

  3. 若是,调用物流服务获取轨迹

  4. 如发现异常,再调用补偿服务发券

  5. 整个过程需要记录在审计日志中

  6. 你会如何在 Java 中实现一个多步工作流?可以提到:状态机 / 工作流引擎 / 编排服务。

  7. 如何保证每一步的幂等性和可追踪性?

小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=allretries 合理设置
    • 开启 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 指标
  • Prometheus:
    • 配置抓取 /actuator/prometheus
  • Grafana:
    • 基于 Prometheus 指标绘制 QPS、延迟(P95/P99)、错误率

6. RAG(检索增强生成)核心流程

1)离线阶段:文档构建

  1. 文档加载(Doc Loader):
    • 支持 PDF、DOC、HTML、内部知识库等
  2. 文本切分(Chunking):
    • 按段落/标题切分成合适长度(如 500~1000 字)
  3. 向量化(Embedding):
    • 调用 Embedding 模型(OpenAI、Ollama、本地模型)
  4. 存储到向量数据库:
    • Milvus / Chroma / Redis 向量索引
    • 字段:id, tenantId, text, vector, metadata(主题、文档类型)

2)在线阶段:问答流程

  1. 用户问题 -> Java 服务(Spring Boot Controller)
  2. Java 服务调用 Embedding 模型,将问题向量化
  3. 调用向量数据库做 KNN 检索:
    • 过滤条件:tenantId、文档类型
    • 返回 TopK 文档片段
  4. Java 服务构建 Prompt:
    • 包含用户问题
    • 包含检索到的文档片段(context)
    • 附加系统指令(禁止胡编)
  5. 调用大模型(通过 Spring AI / gRPC / HTTP)
  6. 返回答案 + 引用文档(Sources)

3)为什么能减少幻觉

  • 模型在生成时参考“真实业务文档”,减少依赖纯记忆
  • Prompt 中明确要求“只能基于提供的 context 回答”
  • 可以在输出中附加来源文档,让用户可验证

7. 多租户向量数据库设计

核心点:数据隔离 + 权限控制

表(或集合)关键字段示例:

  • id:向量记录 ID
  • tenantId:租户 ID
  • docId:文档 ID
  • chunkId:文档片段 ID
  • vector:向量
  • 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,方便追踪

七、小结

这次面试场景,从:

  1. Spring Boot 基础 Web 开发、参数校验、单元测试
  2. 到 Redis 缓存、Kafka 异步、Elasticsearch 搜索、监控与链路追踪
  3. 再到 RAG 智能客服、向量数据库、多租户文档问答、Agent 工具调用、Resilience4j 降级

形成了一条完整的“电商 + 智能客服”技术链路。

对初学者而言,可以顺着这个故事,进一步去动手:

  • 写一个简单下单服务(Spring Boot + MySQL + Redis)
  • 用 Kafka 做订单事件通知
  • 用 Elasticsearch 做订单搜索
  • 再尝试接入一个简单的 RAG 问答系统(比如用本地向量库 + 开源大模型)

把故事里的内容变成自己的 Demo,印象会比看文档更深。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐