惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Cyberwarzone
Cyberwarzone
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
W
WeLiveSecurity
博客园 - 叶小钗
Hugging Face - Blog
Hugging Face - Blog
Security Latest
Security Latest
Scott Helme
Scott Helme
TaoSecurity Blog
TaoSecurity Blog
N
Netflix TechBlog - Medium
爱范儿
爱范儿
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
Google Developers Blog
F
Fortinet All Blogs
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Google Online Security Blog
Google Online Security Blog
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
The GitHub Blog
The GitHub Blog
Spread Privacy
Spread Privacy
S
Secure Thoughts
T
Tailwind CSS Blog
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
N
News and Events Feed by Topic
SecWiki News
SecWiki News
S
Security @ Cisco Blogs
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
L
Lohrmann on Cybersecurity
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
IT之家
IT之家
人人都是产品经理
人人都是产品经理
Attack and Defense Labs
Attack and Defense Labs
Hacker News - Newest:
Hacker News - Newest: "LLM"
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
T
The Blog of Author Tim Ferriss
H
Hacker News: Front Page
T
Tenable Blog
C
CERT Recently Published Vulnerability Notes
D
DataBreaches.Net
阮一峰的网络日志
阮一峰的网络日志
Help Net Security
Help Net Security
博客园_首页
S
Securelist
罗磊的独立博客

博客园 - work hard work smart

使用 LangChain + Hugging Face 构建文本向量化服务 SQLAlchemy 使用详解 Python 中使用 Elasticsearch 的完整指南 Qdrant 向量数据库使用指南 OpenEvals 快速入门:LLM 评估指南 DeepEval 快速入门:LLM 应用评估指南 LangSmith 批量评估完全指南 Qwen-Agent 入门指南:快速构建智能体应用 LangSmith 集成实战:从追踪到评估的完整指南 初识 go-zero:一款让你写后端更规范、更高效的 Go 微服务框架 RAG 中为什么需要 Rerank,以及如何使用 Rerank LangChain4j RAG 核心组件与组合方式 如何使用 Elasticsearch 进行全文检索和向量检索 MinerU Docker 部署指南 5 分钟上手:为 Cline 配置一个免费的 MCP 天气服务 Neo4j 图数据库安装与 Spring Boot 集成实战指南 LangFuse 实战指南:用 @observe 三行代码给 LLM 应用加上全链路追踪 Function Call 深度解析:让大模型从"嘴炮"到"实干"的技术革命 Spring AI 提示词模板实战:告别硬编码,实现提示词工程化管理 LangChain4j 实战指南:用 Java 轻松构建 AI 应用 Spring AI 对话短期记忆实战:让大模型拥有"记忆力" Spring AI 提示词工程实战:让大模型更懂你的意图 Spring AI Alibaba DashScopeChatModel 实战 Spring 中 SSE 流式输出的多种实现方式详解 OpenSandbox 实战指南:为 AI Agent 构建安全的代码执行沙箱 在本机启动 LangGraph 开发服务器:完整指南 DeepAgents中Backend的奥秘:让AI Agent拥有文件操作能力 为什么选择 Go 开发 Web 接口?从入门到实践 智能搜索DeepAgent笔记 RAG学习笔记2--系统查询流程 RAG学习笔记1--系统文件导入流程 百炼 WebSearch 快速入门指南 Python 连接 MongoDB 完整指南:从连接配置到增删改查实战 一行命令搞定 MongoDB 开发环境:Docker Compose 部署 + 可视化管理 使用 Attu 可视化管理 Milvus 向量数据库 在 Windows Docker 中快速安装 Milvus 2.5.6 minio使用 Spark 集群搭建 hadoop集群安装 Spring AI Alibaba 入门实战 Windows 安装 OpenClaw 实战指南 MyBatis 核心流程和原理 Idea中安装Claude code插件 Spark 编程 使用Matplotlib 绘制直方图 Flink安装部署 Flume安装 查找导致cpu过高的代码方法 JVisualVM监控远程Java进程 jmap jacoco多模块生成java单元测试报告实践 arthas 使用demo LockSupport Exchanger CyclicBarrier CountDownLatch 手把手教你用python开始第一个机器学习项目
Spring AI ChatClient 深度解析:优雅构建大模型应用的利器
work hard work smart · 2026-05-30 · via 博客园 - work hard work smart

摘要:ChatClient 是 Spring AI 提供的高级 API,通过链式调用和 Builder 模式,让大模型调用变得更加优雅和灵活。本文结合实际项目代码,全面讲解 ChatClient 的使用技巧和最佳实践。

一、为什么需要 ChatClient?

在使用 Spring AI 开发大模型应用时,我们有两种主要方式:

方式 1:直接使用 ChatModel

@Autowired
private ChatModel chatModel;

public String call(String message) {
    return chatModel.call(message);
}

缺点

  • 功能相对底层,需要手动构建 Prompt
  • 缺少统一的链式调用 API
  • Advisor(拦截器)机制使用不便

方式 2:使用 ChatClient(推荐)

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultSystem("你是一个专业助手")
    .defaultAdvisors(new SimpleLoggerAdvisor())
    .build();

return chatClient.prompt(message).call().content();

优势

  • ✅ 链式调用,代码更优雅
  • ✅ Builder 模式,配置更灵活
  • ✅ 内置 Advisor 机制,支持日志、缓存等切面功能
  • ✅ 统一的 API 风格,降低学习成本

二、ChatClient 核心概念

1. ChatClient 的三层结构

ChatClient(客户端)
    ├── Builder(构建器)
    │   ├── defaultSystem()     - 默认系统提示词
    │   ├── defaultAdvisors()   - 默认拦截器
    │   └── defaultOptions()    - 默认模型参数
    │
    └── PromptSpec(提示词规格)
        ├── prompt()            - 构建提示词
        ├── system()            - 覆盖系统提示词
        ├── user()              - 设置用户消息
        ├── call()              - 同步调用
        └── stream()            - 流式调用

2. 生命周期管理

ChatClient 应该在应用启动时创建,整个生命周期复用:

@RestController
public class ChatController implements InitializingBean {
    
    @Autowired
    private ChatModel chatModel;
    
    private ChatClient chatClient;
    
    @Override
    public void afterPropertiesSet() {
        // 在 Bean 初始化后创建 ChatClient
        chatClient = ChatClient.builder(chatModel)
            .defaultSystem("你是一个 AI 助手")
            .build();
    }
}

三、ChatClient 五种调用方式

方式 1:最简单调用

直接传入字符串,使用默认配置:

@GetMapping("/simple")
public String simpleCall(String message) {
    return chatClient.prompt(message).call().content();
}

测试

curl "http://localhost:8000/client/simpleCall?message=介绍一下Spring AI"

执行流程

prompt(message) 
    → 构建 Prompt 对象
    → 添加 defaultSystem 提示词
    → 调用 ChatModel
    → 返回内容

方式 2:动态覆盖 System 提示词

在运行时动态修改系统提示词:

@GetMapping("/translator")
public String translateCall(String message) {
    return chatClient.prompt(message)
        .system("你是一个专业的翻译助手,请把中文翻译成英文")
        .call()
        .content();
}

场景:同一个 ChatClient,不同接口需要不同的角色设定。

方式 3:使用 user() 方法

分离用户消息的设置:

@GetMapping("/chat")
public String chatCall(String message) {
    return chatClient.prompt()
        .user(message)
        .call()
        .content();
}

与方式 1 的区别

  • prompt(message):一步到位
  • prompt().user(message):分步构建,更灵活

方式 4:使用 Prompt 对象

完全手动控制,最大灵活性:

@GetMapping("/advanced")
public String advancedCall(String message) {
    Prompt prompt = new Prompt(
        new SystemMessage("请详细回答我的问题"),
        new UserMessage(message)
    );
    
    return chatClient.prompt(prompt)
        .call()
        .content();
}

适用场景

  • 复杂的多消息对话
  • 需要精确控制消息顺序
  • 历史对话记录复用

方式 5:流式输出

实时返回结果,适合长文本:

@GetMapping("/stream")
public Flux<String> streamCall(String message) {
    return chatClient.prompt(message)
        .stream()
        .content();
}

前端调用

const response = await fetch('/client/stream?message=写一首诗');
const reader = response.body.getReader();

while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    console.log(new TextDecoder().decode(value));
}

四、ChatClient.Builder 深度配置

完整配置示例

@Override
public void afterPropertiesSet() throws Exception {
    chatClient = ChatClient.builder(dashScopeChatModel)
        // 1. 默认系统提示词
        .defaultSystem("请用英文回答问题")
        
        // 2. 默认拦截器(Advisor)
        .defaultAdvisors(
            new SimpleLoggerAdvisor()  // 日志记录
        )
        
        // 3. 默认模型参数
        .defaultOptions(
            DashScopeChatOptions.builder()
                .model("qwen-plus")
                .temperature(0.7)
                .maxTokens(2000)
                .build()
        )
        
        .build();
}

配置项详解

1. defaultSystem() - 默认系统提示词

.defaultSystem("""
    你是一个专业的 Java 开发专家,擅长 Spring Boot 和微服务架构。
    请用简洁的代码示例回答问题,并添加详细注释。
    """)

作用:所有调用都会自动带上这个系统提示词,无需重复设置。

2. defaultAdvisors() - 默认拦截器

Advisor 是 ChatClient 的核心扩展机制:

.defaultAdvisors(
    new SimpleLoggerAdvisor(),           // 记录日志
    new MessageChatMemoryAdvisor(),      // 对话记忆
    new VectorStoreAdvisor()             // 向量数据库检索
)

常用 Advisor

Advisor 功能 场景
SimpleLoggerAdvisor 打印请求和响应日志 开发调试
MessageChatMemoryAdvisor 自动管理对话历史 多轮对话
VectorStoreAdvisor RAG 向量检索 知识库问答

3. defaultOptions() - 默认模型参数

.defaultOptions(
    DashScopeChatOptions.builder()
        .model("qwen-plus")      // 模型名称
        .temperature(0.7)         // 创造性 (0-1)
        .maxTokens(2000)          // 最大输出长度
        .topP(0.9)                // 核采样参数
        .build()
)

支持的参数(以 DashScope 为例):

  • model:指定模型(qwen-turbo、qwen-plus、qwen-max)
  • temperature:创造性(0 严谨,1 创意)
  • maxTokens:最大 token 数
  • topP:核采样参数
  • stop:停止词列表

五、实战场景

场景 1:多角色 ChatClient

不同业务场景使用不同的 ChatClient:

@Configuration
public class ChatClientConfig {
    
    @Bean
    public ChatClient translatorClient(ChatModel chatModel) {
        return ChatClient.builder(chatModel)
            .defaultSystem("你是一个专业翻译,请把中文翻译成英文")
            .defaultOptions(DashScopeChatOptions.builder()
                .temperature(0.3)  // 翻译需要严谨
                .build())
            .build();
    }
    
    @Bean
    public ChatClient writerClient(ChatModel chatModel) {
        return ChatClient.builder(chatModel)
            .defaultSystem("你是一个创意作家,擅长写出优美的文字")
            .defaultOptions(DashScopeChatOptions.builder()
                .temperature(0.9)  // 写作需要创意
                .build())
            .build();
    }
}

使用

@RestController
public class MultiRoleController {
    
    @Autowired
    @Qualifier("translatorClient")
    private ChatClient translatorClient;
    
    @Autowired
    @Qualifier("writerClient")
    private ChatClient writerClient;
    
    @GetMapping("/translate")
    public String translate(String message) {
        return translatorClient.prompt(message).call().content();
    }
    
    @GetMapping("/write")
    public String write(String message) {
        return writerClient.prompt(message).call().content();
    }
}

场景 2:动态模型切换

运行时动态选择模型:

@GetMapping("/chat")
public String chat(String message, String model) {
    return chatClient.prompt(message)
        .options(DashScopeChatOptions.builder()
            .model(model)  // 动态指定模型
            .build())
        .call()
        .content();
}

测试

# 使用 qwen-plus
curl "http://localhost:8000/client/chat?message=你好&model=qwen-plus"

# 使用 deepseek-v3
curl "http://localhost:8000/client/chat?message=你好&model=deepseek-v3"

场景 3:对话记忆(多轮对话)

结合 ChatMemory 实现上下文对话:

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(
        new MessageChatMemoryAdvisor(
            new InMemoryChatMemory(),  // 内存存储
            "user-123",                 // 用户 ID
            10                          // 保留 10 条历史消息
        )
    )
    .build();

@GetMapping("/conversation")
public String conversation(String message) {
    return chatClient.prompt(message).call().content();
}

六、完整 Controller 示例

package cn.hollis.llm.llmentor.controller;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/client")
public class ChatClientDemoController implements InitializingBean {

    @Autowired
    private ChatModel dashScopeChatModel;

    private ChatClient chatClient;

    /**
     * 简单调用
     */
    @GetMapping("/simple")
    public String simpleCall(String message) {
        return chatClient.prompt(message).call().content();
    }

    /**
     * 覆盖默认 System 提示词
     */
    @GetMapping("/translator")
    public String translateCall(String message) {
        return chatClient.prompt(message)
            .system("你是一个翻译助手,请把中文翻译成英文")
            .call()
            .content();
    }

    /**
     * 使用 user() 方法
     */
    @GetMapping("/chat")
    public String chatCall(String message) {
        return chatClient.prompt()
            .user(message)
            .call()
            .content();
    }

    /**
     * 使用 Prompt 对象
     */
    @GetMapping("/advanced")
    public String advancedCall(String message) {
        Prompt prompt = new Prompt(
            new SystemMessage("请详细回答问题"),
            new UserMessage(message)
        );
        return chatClient.prompt(prompt).call().content();
    }

    /**
     * 流式输出
     */
    @GetMapping("/stream")
    public Flux<String> streamCall(String message) {
        return chatClient.prompt(message).stream().content();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        chatClient = ChatClient.builder(dashScopeChatModel)
            // 默认系统提示词
            .defaultSystem("请用英文回答问题")
            
            // 默认拦截器
            .defaultAdvisors(new SimpleLoggerAdvisor())
            
            // 默认模型参数
            .defaultOptions(
                DashScopeChatOptions.builder()
                    .temperature(0.7)
                    .build()
            )
            .build();
    }
}

七、最佳实践

1. ChatClient 应该复用

错误做法(每次请求都创建):

@GetMapping("/bad")
public String badPractice(String message) {
    ChatClient client = ChatClient.builder(chatModel).build();  // 每次都创建!
    return client.prompt(message).call().content();
}

正确做法(启动时创建,全局复用):

private ChatClient chatClient;

@Override
public void afterPropertiesSet() {
    chatClient = ChatClient.builder(chatModel).build();
}

2. 合理使用 Advisor

开发环境开启日志,生产环境关闭:

@Bean
public ChatClient chatClient(ChatModel chatModel, Environment env) {
    ChatClient.Builder builder = ChatClient.builder(chatModel);
    
    // 只在开发环境添加日志 Advisor
    if (env.acceptsProfiles(Profiles.of("dev"))) {
        builder.defaultAdvisors(new SimpleLoggerAdvisor());
    }
    
    return builder.build();
}

3. 分离配置和调用

@Configuration
public class ChatClientConfig {
    @Bean
    public ChatClient chatClient(ChatModel chatModel) {
        return ChatClient.builder(chatModel)
            .defaultSystem("你是一个 AI 助手")
            .defaultOptions(DashScopeChatOptions.builder()
                .temperature(0.7)
                .build())
            .build();
    }
}

@RestController
public class ChatController {
    @Autowired
    private ChatClient chatClient;  // 直接注入使用
    
    @GetMapping("/chat")
    public String chat(String message) {
        return chatClient.prompt(message).call().content();
    }
}

4. 错误处理

@GetMapping("/safe")
public String safeCall(String message) {
    try {
        return chatClient.prompt(message).call().content();
    } catch (Exception e) {
        log.error("调用大模型失败", e);
        return "抱歉,服务暂时不可用,请稍后重试";
    }
}

八、ChatClient vs ChatModel 对比

特性 ChatModel ChatClient
API 风格 底层 API 链式调用
配置方式 每次调用设置 Builder 预设
Advisor 支持 手动管理 内置支持
适用场景 简单调用、底层控制 企业级应用、复杂场景
代码优雅度 ⭐⭐⭐ ⭐⭐⭐⭐⭐
学习成本

九、总结

ChatClient 是 Spring AI 提供的高级 API,通过以下特性让大模型调用更优雅:

  1. 链式调用prompt().system().user().call().content()
  2. Builder 模式:一次性配置,全局复用
  3. Advisor 机制:日志、缓存、RAG 等切面功能
  4. 灵活覆盖:默认配置 + 运行时覆盖

推荐做法

  • 企业级项目优先使用 ChatClient
  • 简单测试可以使用 ChatModel
  • 合理配置 Advisor,提升开发效率

通过 ChatClient,我们可以用更少的代码、更清晰的结构,构建强大的大模型应用!


参考资源