1712 字
约 5 分钟
3
Spring AI ChatClient:多 Agent 系统的统一 LLM 调

Spring AI ChatClient:多 Agent 系统的统一 LLM 调用层

做 AI Demo 时,调用模型可能只需要:

chatModel.call(prompt);

但到了生产环境,一次 LLM 调用往往还需要:

模型选择
对话记忆
系统提示词
工具调用
MCP
日志
流式输出
结构化返回
用户画像

如果每个 Agent 都自己处理这些事情,代码很快就会重复。

所以 Spring AI 提供了 ChatClient,而真实项目通常还会在它上面再封装一层自己的 LlmCaller


1. ChatModel 和 ChatClient

先记住两者区别。

ChatModel

ChatModel 是底层模型接口。

可以理解为:

Prompt
 ↓
ChatModel
 ↓
DeepSeek / OpenAI / 智谱
 ↓
Response

它负责最基本的:

发送请求给模型并获得回答。


ChatClient

ChatClient 是对 ChatModel 的高级封装。

chatClient.prompt()
        .user("帮我推荐 Java 岗位")
        .call()
        .content();

它不仅能调用模型,还可以继续挂:

Prompt
ChatMemory
Advisor
Tools
Structured Output
Streaming
Model Options

所以可以简单理解为:

ChatModel 负责“调用模型”,ChatClient 负责“组织一次完整的模型调用”。


2. ChatClient 是一次 LLM 调用的总装平台

一个完整的调用可能长这样:

                  ChatClient
                      │
        ┌─────────────┼─────────────┐
        ↓             ↓             ↓
      Prompt       Advisors       Tools
                      │
                 ChatMemory
                      │
                      ↓
                  ChatModel
                      ↓
                     LLM
                      ↓
              Structured Output

也就是说,我们之前学的很多东西,最后都会汇聚到 ChatClient。

例如:

chatClient.prompt()
        .system(systemPrompt)
        .user(message)
        .tools(jobTools)
        .advisors(memoryAdvisor)
        .call()
        .entity(JobResult.class);

一次调用里已经同时包含:

系统提示词
用户输入
工具
记忆
结构化返回

3. Advisor 是什么?

Advisor 可以理解成:

ChatClient 调用链上的拦截器。

类似 Spring MVC 的 Interceptor

例如:

用户请求
   ↓
LoggerAdvisor
   ↓
MemoryAdvisor
   ↓
ReActAdvisor
   ↓
LLM

不同 Advisor 负责不同能力。

Memory Advisor

例如:

MessageChatMemoryAdvisor

负责:

调用前
→ 从 ChatMemory 读取历史

调用模型

调用后
→ 保存新的聊天记录

所以要区分:

ChatMemory
负责存历史

MemoryAdvisor
负责把历史加入模型调用

Logger Advisor

负责记录:

Prompt
Response
Token
调用耗时

方便线上排查问题。


ReAct Advisor

用于控制:

LLM
 ↓
Tool Call
 ↓
执行工具
 ↓
Tool Result
 ↓
继续 LLM

可以在工具调用前后加入:

权限校验
日志
耗时统计
异常处理
最大迭代次数

4. ChatClient 的几种返回方式

最普通的是文本:

String result = chatClient.prompt()
        .user("你好")
        .call()
        .content();

如果需要完整响应:

ChatResponse response = chatClient.prompt()
        .user("你好")
        .call()
        .chatResponse();

可以获取:

Token Usage
Metadata
Generation

如果业务需要 Java 对象:

UserInfo info = chatClient.prompt()
        .user(text)
        .call()
        .entity(UserInfo.class);

这就是 Structured Output。

如果要流式输出:

chatClient.prompt()
        .user(message)
        .stream()
        .content();

所以可以记成:

content()
→ String

chatResponse()
→ 完整模型响应

entity()
→ Java Object

stream()
→ 流式输出

5. 多模型场景怎么办?

实际系统可能同时接:

DeepSeek
智谱
OpenAI
Claude
视觉模型
文本模型

所以不能把 ChatClient 永久绑定死在一个模型上。

而是:

用户请求
 ↓
ModelResolver
 ↓
选择 ChatModel
 ↓
创建 ChatClient

甚至还可以自动判断:

普通文本
→ TEXT Model

包含图片
→ VISION Model

例如:

用户上传招聘截图
       ↓
检测到 Media
       ↓
选择 Vision Model
       ↓
ChatClient
       ↓
提取岗位信息

因此:

Model Routing 负责选模型,ChatClient 负责使用模型。


6. 为什么还要再封装 LlmCaller?

如果只有一个 Agent:

ChatClient

基本够用。

但如果有:

岗位推荐 Agent
简历优化 Agent
岗位采集 Agent
任务 Agent
聊天 Agent

每个 Agent 都需要重复:

选择模型
创建 ChatClient
加载 ChatMemory
设置 Conversation ID
加载 Tools
加载 Advisor
设置 System Prompt
加载用户画像

于是就会出现大量重复代码。

所以生产项目通常再抽象:

Agent
 ↓
LlmCaller
 ↓
ChatClient
 ↓
ChatModel
 ↓
LLM

业务 Agent 只调用:

llmCaller.call(user, prompt);

至于底下到底用了什么模型、什么工具、什么记忆,它不关心。


7. LlmCaller 可以逐层增强

可以设计成三层。

SimpleLlmCaller

负责:

模型选择
TEXT / VISION 判断
ChatClient 创建

适合:

意图分类
摘要
结构化提取

BizAgentLlmCaller

在基础能力上增加:

ChatMemory
Conversation ID
Tools
Advisor

适合普通业务 Agent。


UserLlmCaller

继续增加:

用户画像
个性化 System Prompt
MCP
文件工具
Shell 工具
完整 Agent 工具集

适合真正面向用户的智能 Agent。

整体可以理解成:

Simple
   ↓
Memory + Tools
   ↓
User Profile + MCP

能力逐层增加。


8. 为什么这种设计适合多 Agent?

假设岗位 Agent 写:

llmCaller.call(user, prompt);

它不用知道:

这个用户用 DeepSeek 还是智谱
有没有图片
ChatMemory 存在哪里
需要哪个 Conversation ID
工具怎么注册
MCP 怎么连接
ReAct 怎么循环

这些全部由基础设施层完成。

所以最终职责变成:

Agent
负责业务

LlmCaller
负责统一 AI 调用

ChatClient
负责调用编排

Advisor
负责增强调用链

ChatModel
负责适配底层模型

这就是比较清晰的分层。


9. 把前面的知识全部串起来

现在前面几篇可以放到一张图里:

                        User
                         ↓
                       Agent
                         ↓
                     LlmCaller
                         ↓
                     ChatClient
                         │
          ┌──────────────┼──────────────┐
          ↓              ↓              ↓
     Model Routing   ChatMemory      Function Call
          │              │              │
          ↓           Advisor            ↓
      ChatModel                         MCP
          │                              │
          └──────────────┬───────────────┘
                         ↓
                        LLM
                         ↓
                Structured Output
                         ↓
                    Java Object

分别解决:

Model Routing
→ 用哪个模型?

ChatMemory
→ 模型记住什么?

Advisor
→ 调用过程怎么增强?

Function Call
→ 模型能调用什么工具?

MCP
→ 外部工具怎么接进来?

Structured Output
→ 模型结果怎么变成 Java 对象?

ChatClient
→ 怎么把这些能力组装起来?

LlmCaller
→ 怎么让业务 Agent 不关心这些复杂度?

10. 最值得沉淀的一句话

ChatClient 是 Spring AI 的 LLM 调用编排层,而生产级多 Agent 系统通常会在 ChatClient 上再封装统一的 LlmCaller,将模型路由、ChatMemory、Tool Calling、Advisor、MCP 等公共能力下沉,让业务 Agent 只关注自己的业务逻辑。

所以你可以把 Spring AI 整套东西理解成:

前面几篇
= 学零件

ChatClient
= 把零件组装起来

LlmCaller
= 给业务提供一个简单的开关

以后你自己做 Spring AI 项目,看到多个 Agent 开始重复写 ChatClient.builder()tools()advisors() 的时候,就应该意识到:

该抽一个统一的 LLM 调用层了。

Spring AI ChatClient:多 Agent 系统的统一 LLM 调
http://www.clxhxhhr.top/posts/660/
作者
clxstart
发布于
2026-09-17
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。