文章目录一、这种分层策略的核心思想二、为什么这种分层是合理的1因为官方把 AI Service 定义成“高层编排入口”2因为官方明确区分了“系统提示词、记忆、工具、RAG”这些横向能力3因为 Spring Boot starter 本来就鼓励“配置驱动 自动装配”三、按官方能力边界怎么理解你这几层1. web/controller 层HTTP 入口层2. application/service 层业务编排层3. agent 层智能体运行层4. tool 层可调用动作层5. memory/session 层会话状态层6. rag 层检索增强层7. prompt/template 层提示词治理层8. model 层模型适配层9. config 层配置与装配层四、这种分层策略的真正价值1避免 HTTP 层污染 AI 细节2让 AI Service 成为“智能体运行面”而不是“所有逻辑的垃圾桶”3让 RAG 能独立演进4让 prompt、memory、tools 成为独立治理对象5让模型和向量库具备可替换性五.举例LangChain4j 官方文档提供的能力边界非常支持这种按职责分层的策略AI Service 负责承接对话入口与编排Tools 负责可调用动作Chat Memory 负责会话记忆ContentRetriever / EmbeddingStore 负责 RAGSpring Boot Starter 负责配置与装配。也就是说这种分层不是“官方规定写法”但它是顺着官方能力模型自然长出来的工程化结构。(GitHub)一、这种分层策略的核心思想这套分层策略的本质不是“把代码拆成很多包”而是把一个 AI 应用拆成几类稳定职责接口入口职责收请求、返回响应业务编排职责决定这次请求走哪条链路Agent 运行职责把模型、工具、记忆、检索拼起来知识检索职责负责文档入库、检索、上下文构造提示词职责负责 prompt 的存储、选择、渲染模型适配职责负责具体厂商接入配置装配职责负责通过 Spring Boot properties 把组件组起来LangChain4j 官方文档其实就是按这个思路提供能力的AI Services 处理输入输出转换并支持 chat memory、tools、RAGSpring Boot starter 则负责把模型、向量库和 AI Service 自动装配起来。(GitHub)所以你这套分层本质上是在做上层按业务语义组织下层按 LangChain4j 能力边界实现。二、为什么这种分层是合理的1因为官方把 AI Service 定义成“高层编排入口”官方文档明确说AI Services 会处理最常见的操作输入格式化、输出解析并支持 chat memory、tools、RAG。这个定义决定了它天然适合放在你的agent 层而不是散落到 controller 或 service 的各个角落。(GitHub)对应到你的结构里就是VacuumAssistant声明 AI 能力接口AgentFacade / AgentOrchestrator封装 AI Service 的实际组装与调用这很符合官方能力模型因为 AI Service 本来就是“把底层组件聚合成一个高层接口”。(GitHub)2因为官方明确区分了“系统提示词、记忆、工具、RAG”这些横向能力在 AI Services 文档里SystemMessage/systemMessageProvider、ChatMemoryProvider、Tool、ContentRetriever都是独立机制。它们不是一个大而全的单体对象而是可以分别配置的能力。(GitHub)这直接支持你把系统拆成prompt/template层管 promptmemory/session层管会话状态和记忆tool层管工具rag层管检索也就是说你这个目录结构不是“人为过度拆分”而是把官方文档里已经分开的能力在工程代码里继续落实成清晰边界。(GitHub)3因为 Spring Boot starter 本来就鼓励“配置驱动 自动装配”官方 Spring Boot 文档明确写到starter 可以通过 properties 创建和配置 language models、embedding models、embedding stores 等核心组件同时还提供了 AI Services、RAG、Tools 的自动配置能力。(GitHub)这正好对应你单独放一个config层ConfigurationProperties: llm.*, rag.*, tools.*, session.*Bean wiringprofiles这种设计的意义是业务代码不直接 new 模型不直接 new 检索器而是通过配置与装配层统一注入。这和 Spring Boot LangChain4j 的推荐使用方式是一致的。(GitHub)三、按官方能力边界怎么理解你这几层下面我按你的目录逐层解释它们分别在官方文档里对应什么。1.web/controller层HTTP 入口层这一层不属于 LangChain4j 的核心能力但和 Spring Boot starter 的用法很契合。官方示例就是在RestController里注入ChatModel或 AI Service然后暴露/chat之类的接口。(GitHub)所以这层的定位很清晰接收 REST / SSE 请求做参数校验调应用层返回文本流或结构化结果它不应该负责选 prompt拼 RAG 上下文管 memoryId写工具副作用因此单独保留ChatController、AdminController是合理的。2.application/service层业务编排层这一层虽然不是 LangChain4j 官方术语但它是非常必要的工程层。原因在于官方的 AI Service 解决的是“如何调用 LLM tools memory RAG”但它不负责你的业务决策例如这次请求是普通问答还是报告生成是否需要返回 sources是否要做统一审计和 traceId流式和非流式是否走同一业务路径所以DialogueApplicationService的价值是在 LangChain4j 上面再加一层业务编排把“AI 能力调用”变成“应用用例执行”。这是典型的应用服务层职责。3.agent层智能体运行层这是和官方文档最贴近的一层。官方文档对 AI Service 的定义是把 Java 接口通过代理转成一个真正可运行的 Assistant并自动处理输入输出转换。systemMessageProvider、ChatMemoryProvider、tools(...)、contentRetriever(...)都是在这里挂进去的。(GitHub)所以VacuumAssistant对应 AI Service interfaceAgentFacade / AgentOrchestrator对应 AI Service 的组装与运行封装这一层适合承担“Agent Runtime”的责任绑定ChatModel绑定StreamingChatModel绑定 tools绑定 memory绑定 prompt strategy绑定 request interceptor换句话说这一层就是把官方文档里的这些配置点收口成一个统一运行面。(GitHub)4.tool层可调用动作层官方文档对 tools 的定位非常明确AI Service 可以配置 tools让 LLM 在需要时调用 Java 方法。(GitHub)因此tool层最合理的职责就是暴露给模型可调用的业务动作把外部服务适配成工具统一管理工具注册和拦截你的设计里VacuumRobotTools具体工具实现ToolRegistry工具注册与启停ToolInterceptors统一监控与副作用治理ExternalDataTool (SPI)外部数据来源抽象这是一种非常合理的工程化升级。因为官方只定义了“tool calling 能力”但没有要求你把所有工具都塞进一个类里。你把它分出 registry、interceptor、SPI正好解决后续扩展性问题。(GitHub)5.memory/session层会话状态层官方文档说明了两件非常重要的事如果要支持多用户/多会话就要用ChatMemoryProvider使用这种 memory 时要注意清理不再需要的 conversation避免 memory leak如果要访问内部 chat memories可以让接口扩展ChatMemoryAccess。(GitHub)这就直接支撑了你为什么要把 memory/session 单独拆出来SessionStateStore放业务态比如 CHAT / REPORTChatMemoryEvictionPolicy处理过期与清理ConcurrencyGuard(by memoryId)保护同一会话并发ConversationMetadata管理 trace、userId、channel 等附加元数据换句话说官方文档给了“记忆机制”你这里是在做“记忆机制的工程治理层”。这是很有必要的。(GitHub)6.rag层检索增强层官方 RAG 文档里把ContentRetriever定义得很清楚它负责从底层数据源取回有序的相关内容底层数据源可以是 embedding store、全文检索、混合检索、web search、SQL 等。EmbeddingStoreContentRetriever则是其中一种标准实现并支持maxResults、minScore和 filter。(GitHub)这说明 RAG 至少应该拆成两部分离线准备文档加载、切分、embedding、入库在线检索根据 query 召回 content再交给模型生成所以你这里把它拆成DocumentIngestionPipelineContentRetrieverRagSummarizeService是很符合官方能力边界的。尤其ContentRetriever应该单独成层不要直接把检索逻辑写死在 Controller 或 Tool 里因为官方本来就把它定义成一个独立接口抽象。(GitHub)7.prompt/template层提示词治理层官方 AI Services 文档里既支持静态SystemMessage也支持动态systemMessageProvider还支持从 resource 加载 prompt 模板。(GitHub)这意味着 prompt 不应该只是几段硬编码字符串而应该是一个可治理对象。因此你单独放一层PromptRepositoryPromptStrategyTemplateRenderer非常合理。这层的本质作用是把三件事拆开prompt 存哪prompt 怎么选prompt 变量怎么渲染而官方文档已经给了“资源文件 prompt 动态 provider”这两个基础锚点。(GitHub)8.model层模型适配层官方首页和项目介绍都强调LangChain4j 提供统一 API 来接入主流 LLM 和向量存储并且能和 Java 应用平滑集成。(LangChain4j)这意味着一个很自然的工程原则是业务层不要直接绑死 OpenAI / DashScope / 某一个 embedding 厂商。所以你把这一层拆成ChatModelProviderStreamingChatModelProviderEmbeddingModelProviderProvider adapters是正确的。这样切模型只改 adapter 和配置不会影响 application、agent、tool、rag 这些上层代码。这个思路和官方强调的“统一 API 接入不同模型与向量库”是一致的。(LangChain4j)9.config层配置与装配层这个层和 Spring Boot 官方集成文档最直接相关。因为文档明确写了 starter 会通过 properties 来创建和配置模型、embedding model、embedding store以及 AI Services、RAG、Tools 等。(GitHub)所以把配置集中到llm.*rag.*tools.*session.*再通过ConfigurationProperties和 Bean wiring 装起来是最标准的 Spring Boot 风格也最符合 LangChain4j starter 的使用方式。(GitHub)四、这种分层策略的真正价值如果只看目录这套结构像是“多加了几层”但从官方能力模型看它的价值很明确1避免 HTTP 层污染 AI 细节Spring Boot 示例里 controller 可以直接调模型但那只是最小示例不适合复杂项目。你的分层让 HTTP 层只管接口协议不碰 prompt、tool、RAG 细节。(GitHub)2让 AI Service 成为“智能体运行面”而不是“所有逻辑的垃圾桶”官方把 AI Service 设计成高层代理对象你把它放在 agent 层而不是 controller/service 到处直连是在顺着它的本意使用。(GitHub)3让 RAG 能独立演进官方把ContentRetriever单独抽象出来这天然支持你后续从 embedding 检索升级到 hybrid、SQL、web search而不用重写整条对话链路。(GitHub)4让 prompt、memory、tools 成为独立治理对象官方已经把这些能力独立出来你在工程里继续独立建层后面做审计、追踪、调优会轻松很多。(GitHub)5让模型和向量库具备可替换性官方强调统一 API 和对多模型/多向量存储的支持这就是 model/config 分层成立的根本依据。(LangChain4j)五.举例src/main/java/com/huacai/reactagent ├─ web │ ├─ ChatController.java // SSE REST 对话入口可复用现有 ChatController │ ├─ ReportController.java // 报告生成入口可选 │ └─ dto │ ├─ ChatRequest.java │ ├─ ChatResponse.java │ ├─ ReportRequest.java │ └─ ErrorResponse.java ├─ application │ ├─ DialogueApplicationService.java // 业务编排对话/报告/RAG 路由 │ └─ policy │ ├─ ModeDecisionPolicy.java // 可选意图判定或显式 mode │ └─ MemoryIdPolicy.java // memoryId 生成/校验策略 ├─ agent │ ├─ VacuumAssistant.java // AI Service interface可迁移现有 VacuumAssistant │ ├─ AgentFacade.java // 封装 AI Service 的调用、统一异常 │ └─ interceptor │ ├─ ChatRequestInterceptor.java // 类似 log_before_model 的统一拦截点 │ └─ ToolInvocationInterceptor.java // 统一工具监控替代散落 log ├─ tool │ ├─ VacuumRobotTools.java // 工具集合可迁移现有 VacuumRobotTools │ ├─ registry │ │ ├─ ToolRegistry.java // 工具注册与分组避免写死 │ │ └─ ToolSet.java // tool 分组基础工具/RAG/外部数据 │ └─ external │ ├─ ExternalDataClient.java // SPI外部数据来源适配 │ └─ CsvExternalDataClient.java // 现有 ExternalDataRepository 可迁移为实现 ├─ rag │ ├─ RagConfig.java // Bean wiring可迁移现有 RagConfig │ ├─ ingestion │ │ ├─ IngestionPipeline.java // scan/load/split/embed/upsert/fingerprint │ │ ├─ DocumentSource.java // classpath/filesystem │ │ └─ FingerprintStore.java // md5/mtime 去重对齐 Python md5.txt │ ├─ retrieval │ │ ├─ RagRetriever.java // 封装 ContentRetriever │ │ └─ RetrievedChunk.java // text metadata 统一结构 │ └─ summarize │ ├─ RagSummarizeService.java // 可迁移现有 RagSummarizeService │ └─ RagPromptRenderer.java // {input}/{context} 渲染与校验 ├─ prompt │ ├─ PromptRepository.java // 资源/文件系统加载可复用 VacuumPromptHolder 的思路 │ ├─ PromptKeys.java // MAIN / REPORT / RAG_SUMMARIZE │ └─ PromptStrategy.java // 根据 SessionState/场景选择系统提示词 ├─ model │ ├─ provider │ │ ├─ ChatModelProvider.java // SPI │ │ ├─ StreamingChatModelProvider.java // SPI │ │ └─ EmbeddingModelProvider.java // SPI │ └─ adapter │ ├─ DashScopeModelAdapter.java // 与 application.yml dashscope 对齐 │ └─ ... // 未来扩展 OpenAI/其他云厂商 ├─ session │ ├─ SessionStateStore.java // 替代 VacuumSessionStore支持状态机/ttl/clear │ ├─ SessionState.java // enum Mode { CHAT, REPORT, ... } │ ├─ MemoryConcurrencyGuard.java // 同 memoryId 并发保护文档风险项落地 │ └─ ChatMemoryEvictionService.java // eviction 策略与管理 API └─ config ├─ LlmProperties.java // yml 绑定 ├─ RagProperties.java ├─ ToolProperties.java └─ AgentAutoConfiguration.java // 统一装配AiServices.builder(...)