文章目录
- 一、这种分层策略的核心思想
- 二、为什么这种分层是合理的
- 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 负责 RAG;Spring Boot Starter 负责配置与装配。也就是说,这种分层不是“官方规定写法”,但它是顺着官方能力模型自然长出来的工程化结构。(GitHub)
一、这种分层策略的核心思想
这套分层策略的本质,不是“把代码拆成很多包”,而是把一个 AI 应用拆成几类稳定职责:
- 接口入口职责:收请求、返回响应
- 业务编排职责:决定这次请求走哪条链路
- Agent 运行职责:把模型、工具、记忆、检索拼起来
- 知识检索职责:负责文档入库、检索、上下文构造
- 提示词职责:负责 prompt 的存储、选择、渲染
- 模型适配职责:负责具体厂商接入
- 配置装配职责:负责通过 Spring Boot properties 把组件组起来
LangChain4j 官方文档其实就是按这个思路提供能力的:AI Services 处理输入输出转换,并支持 chat memory、tools、RAG;Spring 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 wiring
- profiles
这种设计的意义是:
业务代码不直接 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 厂商。
所以你把这一层拆成:
ChatModelProviderStreamingChatModelProviderEmbeddingModelProvider- Provider 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(...)