资讯详情

资讯详情

从零读懂 AI 智能客服(一):模块职责与 SSE 聊天链路(后端架构)

本文主题先建立后端全局认知再走通一次 AI 对话从请求入口到 SSE 返回的主链路适合读者刚接手 Spring Boot 多模块项目希望系统理解 AI 客服后端的 Java 开发者代码基线当前学习分支源码快照这里是yurenpai27届开发者主要学习 Java 后端与 AI 应用开发。这里记录真实项目中的代码调用链、Agent/RAG 工程化、问题排查和开发复盘。个人理念阅读大型项目时先建立地图再进入街道比一开始扎进某个类更高效。写在前面先说结论这个后端不是由多个互不相关的服务拼起来的而是一个以 Maven 多模块组织、由 Spring Boot 统一装配的模块化单体。普通业务遵循 Controller、Service、Mapper 的经典链路AI 对话则在此基础上增加了上下文解析、场景路由、模型适配、RAG、SSE 和消息持久化等能力。本文解决四个问题根工程、公共模块、业务模块、启动模块和扩展模块分别负责什么普通 CRUD 与 AI 对话链路为什么复杂度不同一次聊天请求如何从ChatRequest进入ChatServiceFacade固定回复和大模型回复最终如何通过 SSE 返回并保存。说明内容基于当前学习分支的源码快照进行静态分析不同分支或后续版本的目录与实现可能发生变化。代码说明除明确标注为完整源码外文中的代码片段均根据当前源码提炼为简化示意只保留与本节有关的字段、注解或调用关系文中的“已核对”表示完成静态源码核对不等同于运行测试通过。一、先建立后端整体架构认知后端根目录D:\Tools\java\aiwork\tst-pharma-main-backend\ ├─ pom.xml ├─ tst-pharma-admin\ ├─ tst-pharma-common\ ├─ tst-pharma-modules\ ├─ tst-pharma-extend\ ├─ docs\ └─ logs\这不是四个互不相关的后端项目而是一个Maven 多模块的 Spring Boot 单体应用可以先记住这张关系图根 pom.xml │ ┌──────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ tst-pharma-common tst-pharma-modules tst-pharma-extend 公共基础能力 业务模块 独立扩展服务 │ │ └──────────┬───────┘ ▼ tst-pharma-admin 组装并启动主应用依赖方向大致是common ↑ modules ↑ admin也就是业务模块依赖公共模块 启动模块依赖业务模块不能反过来让common 依赖 chat system 依赖 admin否则很容易出现循环依赖。二、根pom.xml是干什么的文件D:\Tools\java\aiwork\tst-pharma-main-backend\pom.xml它的打包方式是packagingpom/packaging说明它自己不直接生成可运行的业务 JAR主要负责三件事。1. 聚合模块modulesmoduletst-pharma-admin/modulemoduletst-pharma-common/modulemoduletst-pharma-extend/modulemoduletst-pharma-modules/module/modules在根目录执行 Maven 构建时会按照模块依赖关系一起编译。2. 统一版本当前主要技术版本包括Java 17 Spring Boot 3.5.8 MyBatis-Plus 3.5.14 Sa-Token 1.44.0 Redisson 3.51.0 LangChain4j 1.13.0 Weaviate Client 1.19.6子模块不需要分别写一遍版本号。3. 统一构建规则例如Maven 编译插件 测试插件 Spring Boot 依赖管理 不同环境的 Maven Profile因此根pom.xml可以理解为整个后端工程的“总目录和统一规则”。小鱼点睛根 POM 负责统一规则和聚合构建tst-pharma-admin才是把业务模块装入同一个 Spring Boot 应用的启动者。聚合构建和运行时装配不是一回事。三、tst-pharma-admin启动和装配层目录D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-admin\ ├─ pom.xml └─ src\main\ ├─ java\com\tst\pharma\ │ ├─ TstPharmaApplication.java │ ├─ TstPharmaServletInitializer.java │ ├─ config\ │ │ └─ MapperConflictResolver.java │ └─ controller\ │ ├─ AuthController.java │ ├─ CaptchaController.java │ └─ IndexController.java └─ resources\ ├─ application.yml ├─ application-dev.yml ├─ application-prod.yml ├─ logback-plus.xml └─ banner.txt它为什么业务代码很少因为它不是主要业务模块而是把其他模块装到一起然后启动 Spring Boot。它的pom.xml依赖了tst-pharma-system tst-pharma-generator tst-pharma-chat tst-pharma-workflow tst-pharma-aiflow所以最后运行的是tst-pharma-admin但是实际加载进去的业务包括系统管理 AI 聊天 知识库 业务工作流 AI 流程编排 代码生成启动类文件D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-admin\src\main\java\com\tst\pharma\TstPharmaApplication.java核心注解SpringBootApplicationpublicclassTstPharmaApplication{}启动类位于com.tst.pharma而其他模块中的类也基本位于com.tst.pharma.xxx因此 Spring Boot 启动后会扫描依赖中的ControllerRestControllerServiceComponentConfiguration这就是为什么ChatController明明不在admin目录中启动admin后仍然能访问。当前启动类的特殊行为当前main()启动之前执行了killPortProcess(6039);含义是启动程序 ↓ 检查 6039 端口 ↓ 如果被占用 ↓ 在 Windows 上通过 taskkill 强制结束占用进程 ↓ 再启动 Spring Boot这不是 Spring Boot 标准行为而是项目自定义行为。所以以后发现某个使用 6039 的 Java 进程被结束需要先想到这里。四、tst-pharma-common公共基础能力层目录D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-common\它不是一个单独业务而是一组可复用的公共组件。必须优先掌握tst-pharma-common-core ├─ 公共返回对象 R ├─ 异常 ├─ 工具类 └─ 基础常量 tst-pharma-common-web ├─ BaseController ├─ Web 配置 ├─ 全局异常处理 └─ Web 过滤器 tst-pharma-common-mybatis ├─ BaseMapperPlus ├─ PageQuery ├─ TableDataInfo ├─ MyBatis-Plus 配置 └─ 数据库公共实体 tst-pharma-common-satoken ├─ 登录用户获取 ├─ 权限认证 └─ LoginHelper tst-pharma-common-redis ├─ RedisUtils ├─ Redisson └─ 缓存公共能力 tst-pharma-common-sse ├─ SseEmitterManager ├─ SseMessageUtils ├─ SSE 事件对象 └─ SSE 连接管理 tst-pharma-common-chat ├─ ChatRequest ├─ ChatModelVo ├─ IChatService ├─ IChatModelService └─ 工作流与聊天共用接口用到的时候再深入common-json common-log common-tenant common-doc common-encrypt common-security common-idempotent common-sensitive当前阶段不用逐个阅读common-excel common-mail common-sms common-oss common-social common-websocket common-job common-translation你要注意common不是“杂物目录”而是可以被多个业务模块复用、且不应该依赖具体业务的基础组件。例如 SSE 连接不只聊天模块能用所以放在tst-pharma-common-sse而不是直接塞进tst-pharma-chat五、tst-pharma-modules主要业务代码目录D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-modules\ ├─ tst-pharma-system\ ├─ tst-pharma-chat\ ├─ tst-pharma-aiflow\ ├─ tst-pharma-workflow\ └─ tst-pharma-generator\1.tst-pharma-system主要负责用户 角色 菜单 部门 岗位 字典 系统参数 租户 登录日志 操作日志它属于后台系统基础业务。2.tst-pharma-chat这是我们现在最需要掌握的模块负责聊天会话 聊天消息 模型配置 模型供应商 模型调用 系统提示词 客服场景分类 医疗风险路由 上下文实体识别 固定回复 SSE 流式响应 知识库 文档切片 Embedding 向量检索 RAG Rerank MCP 智能体 可观测性因此它不是一个简单的聊天 CRUD 模块。3.tst-pharma-aiflow负责AI 节点 模型节点 知识库节点 条件节点 节点输入输出 AI 流程编排它解决的是多个 AI 节点怎样按照流程执行。4.tst-pharma-workflow负责普通业务流程例如审批 流程节点 流程实例 任务流转 业务办理注意区分aiflow AI 能力和节点的编排 workflow 传统业务审批和流程流转5.tst-pharma-generator负责读取数据库表结构 生成 Entity 生成 BO、VO 生成 Mapper 生成 Service 生成 Controller 生成前端 CRUD 页面它主要是开发辅助工具。六、tst-pharma-extend独立扩展服务目录D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-extend\ ├─ tst-pharma-monitor-admin\ └─ tst-pharma-snailjob-server\分别用于monitor-admin └─ Spring Boot Admin 服务监控 snailjob-server └─ 定时任务和分布式任务调度它们与admin不完全相同。admin是主业务应用入口而这两个更接近独立辅助服务。你当前学习聊天代码时只需要知道它们的用途不需要深入。七、一个普通业务模块的标准分层用系统参数配置作为例子。源码结构D:\Tools\java\aiwork\tst-pharma-main-backend\ └─ tst-pharma-modules\tst-pharma-system\src\main\java\com\tst\pharma\system\ ├─ controller\system\ │ └─ SysConfigController.java ├─ service\ │ ├─ ISysConfigService.java │ └─ impl\ │ └─ SysConfigServiceImpl.java ├─ mapper\ │ └─ SysConfigMapper.java └─ domain\ ├─ SysConfig.java ├─ bo\ │ └─ SysConfigBo.java └─ vo\ └─ SysConfigVo.java标准调用关系HTTP 请求 ↓ Controller ↓ Service 接口 ↓ ServiceImpl ↓ Mapper ↓ MyBatis-Plus ↓ MySQL返回过程MySQL 查询结果 ↓ Entity ↓ 对象转换 ↓ VO ↓ Controller ↓ JSON ↓ 前端每一种类的职责ControllerSysConfigController.java负责接收 HTTP 请求 校验参数 检查权限 调用 Service 包装返回结果例如GetMapping(/list)publicTableDataInfoSysConfigVolist(SysConfigBoconfig,PageQuerypageQuery){returnconfigService.selectPageConfigList(config,pageQuery);}Controller 不应该直接写 SQL也不应该堆很多复杂业务判断。Service 接口ISysConfigService.java负责定义系统参数模块能提供哪些业务能力例如查询配置 新增配置 修改配置 删除配置 刷新缓存ServiceImplSysConfigServiceImpl.java负责真正实现业务逻辑检查配置键是否重复 读写数据库 更新缓存 校验内置配置能否删除MapperSysConfigMapper.java负责数据库访问publicinterfaceSysConfigMapperextendsBaseMapperPlusSysConfig,SysConfigVo{}因为继承了BaseMapperPlus很多基础 CRUD 不用重复编写。EntitySysConfig.java对应数据库表TableName(sys_config)publicclassSysConfigextendsTenantEntity{}主要用于数据库字段映射 MyBatis-Plus 查询 数据库新增修改BOSysConfigBo.javaBO 是 Business Object主要接收业务输入。例如NotBlank(message参数名称不能为空)privateStringconfigName;它可以带参数校验 查询条件 业务输入字段VOSysConfigVo.javaVO 是 View Object用于返回前端。它只暴露前端需要的数据避免把数据库 Entity 直接返回。八、为什么tst-pharma-chat比普通 CRUD 复杂聊天模块顶级结构D:\Tools\java\aiwork\tst-pharma-main-backend\ └─ tst-pharma-modules\tst-pharma-chat\src\main\java\com\tst\pharma\ ├─ agent\ ├─ config\ ├─ constant\ ├─ controller\ ├─ domain\ ├─ enums\ ├─ factory\ ├─ mapper\ ├─ mcp\ ├─ observability\ └─ service\普通 CRUD 只需要Controller Service Mapper Entity、BO、VOAI 聊天还要解决使用哪个模型 模型是哪家供应商 请求是否属于医疗高风险 是否属于客服范围 是否直接固定回复 是否需要查询知识库 是否需要把历史消息交给模型 怎样流式返回 什么时候保存用户消息 什么时候保存助手消息 模型出错后怎样关闭 SSE 同一用户开多个窗口怎样隔离所以额外出现了factory ├─ 根据供应商选择模型适配实现 provider / chat impl ├─ 构建不同供应商的 ChatModel prompt ├─ 系统提示词 scene ├─ 客服场景识别和路由 scene\context ├─ 最近消息和上下文实体解析 retrieval ├─ 检索编排 vector ├─ 向量库操作 embed ├─ 文本向量化 rerank ├─ 检索结果重排序 observability ├─ 模型调用和检索过程观测 mcp ├─ 外部工具能力小鱼点睛AI 聊天没有抛弃 Controller、Service 和 Mapper而是在传统持久化链路之上增加了上下文、路由、模型、RAG 与 SSE 编排。复杂度来自新增的决策和传输职责而不是目录名称本身。九、common-chat和tst-pharma-chat的区别这两个名字很容易混淆。tst-pharma-common-chat公共聊天协议和接口例如ChatRequest ChatModelVo IChatService IChatModelService RoleType文件D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-common\tst-pharma-common-chat\src\main\java\com\tst\pharma\common\chat\domain\dto\request\ChatRequest.javatst-pharma-chat聊天业务的真正实现例如ChatController ChatServiceFacade ChatMessageServiceImpl ChatSceneRouter 知识库检索 模型供应商实现这样设计的主要好处是workflow、aiflow 等其他模块 可以只依赖 common-chat 中的接口 而不必直接依赖完整的 chat 实现模块这是在降低跨模块耦合。十、开始走完整聊天链路先看完整地图前端发送 POST /chat/send │ ▼ Sa-Token 登录认证、Web 过滤器 │ ▼ ChatRequest 反序列化和参数校验 │ ▼ ChatController.sseChat() │ ▼ ChatServiceFacade.sseChat() │ ├─ 校验图片地址 ├─ 获取服务端登录用户 ├─ 获取登录 Token ├─ 生成当前窗口 SSE 标识 ├─ 构建轻量上下文 ├─ 执行客服场景路由 ├─ 创建 SSE 连接 └─ 保存用户消息 │ ▼ 根据路由结果分流 │ ┌─────────┼─────────────┐ │ │ │ ▼ ▼ ▼ 固定回复 工作流/思考模式 普通模型聊天 │ │ │ ├─ 查询模型配置 │ ├─ 加入系统提示词 │ ├─ 加载历史消息 │ ├─ 可选知识库 RAG │ ├─ 可选图片 │ ├─ 选择模型供应商 │ └─ 调用流式大模型 │ │ ▼ ▼ 发送固定 SSE onPartialResponse 保存助手消息 │ 发送 done ▼ 关闭连接 持续发送 SSE content │ ▼ onCompleteResponse │ 保存完整助手消息 发送 done 关闭 SSE十一、第一站ChatRequest文件D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-common\tst-pharma-common-chat\src\main\java\com\tst\pharma\common\chat\domain\dto\request\ChatRequest.java主要分成两类字段。前端传入的字段model ├─ 使用的模型名称 content ├─ 用户输入内容 imageUrls ├─ 用户上传的图片最多 4 张 sessionId ├─ 会话 ID knowledgeId ├─ 知识库 ID appId ├─ 应用 ID uuid ├─ 当前聊天窗口 ID enableThinking ├─ 是否开启深度思考 enableWorkFlow ├─ 是否使用工作流 workFlowRunner ├─ 工作流参数 isResume、reSumeRunner └─ 工作流人机交互恢复参数其中NotEmptyprivateStringmodel;NotEmptyprivateStringcontent;会经过Valid校验。后端运行时补充的字段chatModelVo ├─ 后端从数据库查到的模型完整配置 emitter ├─ 当前 SSE 连接 userId ├─ 服务端登录用户 ID tokenValue ├─ 服务端 Token sseConnectionId ├─ 当前窗口的 SSE 唯一标识 contextMessages └─ 最终交给模型的完整上下文尤其要记住userId 和 tokenValue 不能信任前端传入值 必须由后端登录状态获取当前代码确实是通过LoginHelper.getUserId(); StpUtil.getTokenValue();获得。十二、第二站ChatController文件D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-modules\tst-pharma-chat\src\main\java\com\tst\pharma\controller\chat\ChatController.java核心代码非常简单PostMapping(/send)ResponseBodypublicSseEmittersseChat(RequestBodyValidChatRequestchatRequest){returnchatService.sseChat(chatRequest);}它只做三件事1. 接收 POST /chat/send 2. 把 JSON 转成 ChatRequest 并校验 3. 调用 ChatServiceFacade这里没有写场景判断、模型调用、知识库查询这是正确的。因为 Controller 的职责只是HTTP 边界不应该成为业务逻辑中心。十三、第三站ChatServiceFacade.sseChat()文件D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-modules\tst-pharma-chat\src\main\java\com\tst\pharma\service\chat\impl\ChatServiceFacade.java这是聊天主链路的编排中心。它不是普通的 CRUD Service而是一个Facade 门面服务把认证、上下文、路由、SSE、知识库、模型调用和消息保存串起来。核心执行顺序如下。第一步校验图片validateImageUrls(chatRequest.getImageUrls());检查图片数量 URL 长度 URL 协议 不安全地址第二步获取登录用户LonguserIdgetCurrentUserId();StringtokenValuegetCurrentTokenValue();不是从请求体直接取用户身份。第三步生成 SSE 连接标识StringsseConnectionIdbuildSseConnectionId(tokenValue,chatRequest);优先使用Token uuid没有uuid时使用Token sessionId如果两者都没有当前代码会兼容旧调用并退回到仅使用Token。因此新前端应尽量稳定传入uuid或sessionId避免同一 Token 下的连接相互替换。这样同一个登录用户可以同时打开多个聊天窗口互不关闭对方的 SSE 连接。关系是userId └─ sseConnectionId A └─ sseConnectionId B └─ sseConnectionId C第四步构建轻量路由上下文ChatRoutingContext routingContext chatContextResolver.resolve(chatRequest, userId);文件D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-modules\tst-pharma-chat\src\main\java\com\tst\pharma\service\chat\scene\context\ChatContextResolver.java它只读取当前输入 当前图片 最近最多 20 条文本消息 上一条助手回复 订单号、运单号、处方号等上下文实体 药品名称它明确不会做不调用大模型 不调用 RAG 不调用业务接口 不发送 SSE产生ChatRoutingContext ├─ currentContent ├─ hasCurrentImages ├─ recentMessages ├─ entities └─ lastAssistantMessage第五步客服场景路由ChatRouteResult routeResult chatSceneRouter.route(routingContext);路由器D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-modules\tst-pharma-chat\src\main\java\com\tst\pharma\service\chat\scene\ChatSceneRouter.java当前真实顺序是1. 医疗风险识别 2. 订单、物流、处方、售后、人工等业务分类 3. 客服服务范围判断 4. 范围外连续追问判断 5. 上下文指代和信息不足判断 6. 决定固定回复还是继续请求模型路由结果ChatRouteResult ├─ sceneType ├─ riskLevel ├─ action ├─ matchedRule └─ replyTemplate常见actionDIRECT_REPLY ├─ 医疗风险固定回复 SCOPE_GUIDE ├─ 范围外引导 CLARIFY ├─ 信息不足要求用户补充 BUSINESS_PLACEHOLDER ├─ 业务接口暂未真正接通时的占位回复 HUMAN_GUIDE ├─ 引导人工客服 CONTINUE_CHAT └─ 继续进入系统提示词、RAG 和大模型第六步建立 SSESseEmitteremittersseEmitterManager.connect(userId,sseConnectionId);连接管理器位于D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-common\tst-pharma-common-sse\src\main\java\com\tst\pharma\common\sse\core\SseEmitterManager.java它维护的结构相当于Map用户ID,MapSSE连接标识,SseEmitter所以一个用户可以拥有多个窗口连接。第七步保存用户消息chatMessageService.saveChatMessage( userId, chatRequest.getSessionId(), chatRequest.getContent(), RoleType.USER.getName(), chatRequest.getModel() );无论后面进入固定回复 工作流 普通大模型用户原始消息都先统一保存一次。小鱼点睛聊天入口的关键顺序是先从服务端取得可信身份再建立用户隔离的 SSE 标识和路由上下文最后保存原始消息并分流。前端传来的userId或 Token 不能作为认证依据。十四、两条最重要的后续分支分支 A固定回复例如医疗紧急情况 严重不良反应 停药换药 范围外问题 上下文不明确 人工客服 业务接口占位调用FixedReplyService链路replyTemplate ↓ FixedReplyTemplateProvider ↓ 获得固定安全文案 ↓ SSE 发送 content ↓ 保存助手消息 ↓ SSE 发送 done ↓ 关闭连接这条链路不查询模型配置 不调用知识库 不调用大模型分支 B继续模型聊天只有路由动作是CONTINUE_CHAT才进入模型链路ChatModelVo chatModelVo requireChatModel(chatRequest);然后构建最终上下文SystemMessage ↓ 历史对话 ↓ 经过 RAG 和图片增强的当前 UserMessage具体顺序1. 加入全局系统提示词 2. 如果有 knowledgeId执行知识库 RAG 3. 如果有图片添加图片内容 4. 从数据库加载会话历史 5. 移除刚保存的重复用户消息 6. 把增强后的当前用户消息放到最后之后AbstractChatService chatService chatServiceFactory.getOriginalService(providerCode);工厂根据模型供应商选择实现OpenAI 兼容供应商 Ollama 通义 智谱 其他自定义供应商再构建StreamingChatModel streamingChatModel chatService.buildStreamingChatModel( chatModelVo, chatRequest );最终调用streamingChatModel.chat(contextMessages, handler);十五、模型返回后的 SSE 链路模型每返回一个片段就触发onPartialResponse(StringpartialResponse)处理片段追加到 StringBuilder ↓ SSE 发送 content 事件 ↓ 前端逐字显示模型返回完成后触发onCompleteResponse(ChatResponse completeResponse)处理取出完整助手回复 ↓ 保存助手消息到数据库 ↓ 发送 done 事件 ↓ 关闭 SSE 连接出现异常时记录后端异常 ↓ 向前端发送固定安全错误文案 ↓ 关闭 SSE 连接前端不会直接看到模型供应商的原始异常、密钥或内部接口信息。十六、阅读时先记住这三张图后端模块图common → 公共积木 modules → 业务实现 admin → 组装并启动 extend → 独立辅助服务普通 CRUD 图Controller → Service → ServiceImpl → Mapper → MySQLAI 聊天图Controller → ChatServiceFacade → 上下文解析 → 场景路由 → 固定回复 / 工作流 / 大模型 → SSE → 消息保存十七、接下来阅读源码的正确顺序阅读下一阶段源码时不建议直接从 872 行的ChatServiceFacade第一行读到最后一行。建议按下面顺序第 1 组请求入口 ├─ ChatRequest.java ├─ ChatController.java └─ ChatServiceFacade.sseChat() 第 2 组前置路由 ├─ ChatRoutingContext.java ├─ ChatContextResolver.java ├─ ChatSceneRouter.java ├─ ChatRouteResult.java ├─ ChatRouteAction.java └─ ChatSceneType.java 第 3 组固定回复 ├─ FixedReplyService.java ├─ FixedReplyTemplateProvider.java ├─ FixedReplySseSender.java └─ DefaultFixedReplySseSender.java 第 4 组模型调用 ├─ AiCustomerSystemPromptProvider.java ├─ ChatServiceFactory.java ├─ AbstractChatService.java └─ 各供应商 ChatService 实现 第 5 组知识库 ├─ KnowledgeRetrievalService.java ├─ CustomVectorRetriever.java ├─ VectorStoreService.java └─ KnowledgeInfoService 第 6 组消息和 SSE ├─ ChatMessageServiceImpl.java ├─ PersistentChatMemoryStore.java ├─ SseEmitterManager.java └─ SseMessageUtils.java下一步最适合从第一组的三个文件逐行走重点搞明白前端到底传了什么 后端补充了什么 路由发生在什么时候 用户消息为什么在调用模型前保存 SSE 为什么要使用 userId uuid/sessionId 隔离这三点理解后再进入ChatSceneRouter整个项目的聊天主线就不会乱了。总结本文围绕“先建立后端全局认知再走通一次 AI 对话从请求入口到 SSE 返回的主链路”主要分析了后端各一级模块的职责与依赖方向普通 CRUD 和 AI 聊天链路的结构差异从 ChatRequest、Controller、Facade 到 SSE 的关键执行顺序固定回复、模型调用和消息落库之间的关系整个过程可以概括为前端请求 → ChatController → ChatServiceFacade → 上下文与场景路由 → 固定回复 / 模型调用 → SSE → 消息持久化真正读懂 AI 客服后端不是记住所有类名而是先分清“谁接请求、谁做决策、谁执行回复、谁负责传输、谁负责保存”。当前进度[OK] 已经完成已完成根 POM、启动模块、公共模块和聊天主链路的静态源码核对已整理普通 CRUD 与 AI 对话两条阅读路线[TODO] 后续继续下一篇继续拆解 Maven 依赖、Spring Bean 装配和跨模块接口桥梁后续再逐行分析 ChatRequest、ChatController 与 ChatServiceFacade 的入口代码小鱼点睛真正读懂 AI 客服后端不是记住所有类名而是先分清“谁接请求、谁做决策、谁执行回复、谁负责传输、谁负责保存”。下一篇下一篇将继续分析“Java 多模块项目如何协作Maven 依赖、Spring 注入与运行时装配”把本文建立的结构认知继续落到具体代码和对象流上。这篇文章是我在真实项目学习过程中的阶段性记录。不同项目的命名和目录可能不同但判断职责边界、依赖方向和数据生命周期的方法可以复用。如果内容中还有遗漏欢迎一起交流。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →