深入 Spring AI 聊天补全:ChatClient、PromptTemplate、Advisor 一网打尽!

🔥 深入 Spring AI 聊天补全:ChatClient、PromptTemplate、Advisor 一网打尽!

导读 :还在用裸 HTTP 调大模型?Spring AI 的 ChatClient 让你像写流式代码一样调用 AI!今天深度拆解 Prompt 构建、模板渲染、顾问链------看完这篇,你的 AI 应用架构直接上一个台阶!


🎯 前言:为什么你必须搞懂 ChatClient?

上一章我们跑通了第一个 Spring AI 应用,但只停留在 "Hello World" 级别。真正落地生产时,你需要:系统角色设定、提示词模板化、Token 用量监控、请求响应拦截 ......这些 Spring AI 早就给你封装好了!今天这篇,把 ChatClient 的底层到高阶用法,全部盘清楚!建议先收藏,代码可以直接抄!


📌 核心概念速览

在 Spring AI 里,一次对话请求从发起到返回,要经过这么一条链路:

css 复制代码
你的代码 → ChatClient → [Advisor链] → ChatModel → LLM API → ChatResponse → 你的代码

关键角色分工:

组件 职责 类比
ChatClient 面向开发者的流畅 API 入口 餐厅前台
ChatModel 底层与 LLM 交互的模型接口 后厨大厨
Prompt 封装消息列表 + 选项的请求对象 菜单 + 口味备注
Message 单条消息(用户/系统/助手/工具) 菜单上的一道菜
Advisor 请求/响应的拦截处理链 服务员、质检员

🧱 第一节:底层接口长啥样?

1.1 通用 Model 接口

Spring AI 设计了一套通用接口,所有 AI 模型都遵循这套契约:

java 复制代码
public interface Model<TReq extends ModelRequest<?>,
                       TRes extends ModelResponse<?>> {
    TRes call(TReq request);
}

人话翻译 :不管什么模型(GPT、Claude、Qwen),调用方式统一是 call(请求) → 返回 响应。这就是 Spring 的优雅------统一抽象,屏蔽差异

ModelRequest 管请求(指令 + 选项),ModelResponse 管响应(结果 + 元数据)。ModelResult 里又分 output(AI 生成的内容)和 metadata(生成过程的元数据)。

1.2 ChatModel:专门聊大天的 Model

java 复制代码
public interface ChatModel extends Model<Prompt, ChatResponse> {
    ChatResponse call(Prompt prompt);
    ChatOptions getDefaultOptions();
}

ChatModel 特化了请求类型为 Prompt,响应类型为 ChatResponse。每个模型实现(如 OllamaChatModel)都有自己的默认选项。


💬 第二节:Message 的四种身份

一次完整的对话,消息分为四种角色:

类型 英文 作用 场景
用户消息 USER 人说的 "帮我写个排序算法"
助手消息 ASSISTANT AI 回的 生成的代码/回答
系统消息 SYSTEM 给 AI 的"人设" "你是一个 Java 专家"
工具消息 TOOL 函数调用结果 查询天气后的返回数据

核心洞察 :系统消息(SystemMessage)是控制 AI 行为的"隐形遥控器"。默认的系统消息是 "You're a helpful assistant.",太泛了!生产环境一定要自定义!

java 复制代码
Prompt prompt = new Prompt(List.of(
    new SystemMessage("You are a Java programming expert."),
    new UserMessage(input)
));

💡 经验之谈:系统消息写得好,AI 输出质量直接翻倍。把系统消息当成"岗位说明书"来写!


🎛️ 第三节:ChatOptions 调参指南

大模型不是黑盒,有很多可调参数。Spring AI 通过 ChatOptions 暴露这些能力:

java 复制代码
public interface ChatOptions extends ModelOptions {
    String getModel();           // 模型名称
    Double getFrequencyPenalty();// 频率惩罚(防重复)
    Integer getMaxTokens();      // 最大输出 token 数
    Double getPresencePenalty(); // 主题惩罚(防跑题)
    List<String> getStopSequences();// 停止词
    Float getTemperature();      // 温度(创造性 vs 确定性)
    Float getTopP();             // 核采样
    Integer getTopK();           // Top-K 采样
}

3.1 全局默认配置(application.yaml)

yaml 复制代码
spring:
  ai:
    ollama:
      chat:
        options:
          model: qwen3:0.6b
          temperature: 0.7   # 0=保守,1=放飞

3.2 请求级覆盖(代码里动态调)

java 复制代码
Prompt prompt = new Prompt(input, 
    OllamaChatOptions.builder()
        .temperature(0.1)           // 这次要严谨
        .stop(List.of("Observation:"))
        .build()
);

⚠️ 注意 :请求级覆盖需要底层 ChatModel 支持合并选项。大部分官方实现都支持,但自定义模型要留意。


📊 第四节:获取 Token 用量,别被账单吓哭

调云模型是按 Token 计费的,拿到响应后第一件事------看用了多少 Token!

java 复制代码
ChatResponse response = chatClient.prompt()
    .user(input)
    .call()
    .chatResponse();

// AI 回复的文本
String content = response.getResult().getOutput().getText();

// Token 用量详情
Usage usage = response.getMetadata().getUsage();
int promptTokens = usage.getPromptTokens();      // 输入用了多少
int completionTokens = usage.getCompletionTokens();// 输出用了多少
long totalTokens = usage.getTotalTokens();       // 总计

ChatResponseMetadata 里还有啥?

  • RateLimit:限流信息(还剩多少次调用、什么时候重置)
  • Usage:Token 消耗明细
  • PromptMetadata:提示词过滤元数据

🔥 生产必做:把 Token 用量打到日志/监控里,账单异常时能第一时间发现!


🛠️ 第五节:ChatClient 流畅 API 实战

ChatClient 是 Spring AI 给开发者的"糖衣炮弹",链式调用爽到飞起:

5.1 最简用法

java 复制代码
String content = chatClient.prompt()
    .system("You are a master chef.")   // 设定人设
    .user("How to cook fish?")          // 用户问题
    .call()                              // 发送请求
    .content();                          // 取文本结果

5.2 已有 Prompt 对象?直接喂

java 复制代码
Prompt prompt = new Prompt("hello");
String content = chatClient.prompt(prompt).call().content();

5.3 ChatClient.Builder 预置默认值

java 复制代码
ChatClient chatClient = builder
    .defaultSystem("你是一个专业的技术翻译")  // 默认系统消息
    .defaultAdvisors(loggingAdvisor)       // 默认顾问
    .defaultOptions(options)               // 默认选项
    .build();

Builder 方法速查表

方法 作用
defaultUser 预设默认用户消息
defaultSystem 预设默认系统消息
defaultTools 预设可用工具
defaultOptions 预设聊天选项
defaultAdvisors 预设顾问链

📝 第六节:PromptTemplate 提示词模板------告别字符串拼接

6.1 为什么需要模板?

想象一个烹饪建议服务,用户只输入 "鱼",但发给 AI 的提示词应该是:"如何在 5 分钟内做一道美味的鱼?" 这种"用户输入 + 固定模板"的场景,用 PromptTemplate 最合适。

6.2 模板文件 + 变量替换

src/main/resources/prompts/cooking.st

css 复制代码
How to cook {dish} in 5 minutes?

Java 代码:

java 复制代码
@Value("classpath:/prompts/cooking.st")
private Resource promptResource;

public String chat(String dish) {
    Prompt prompt = new PromptTemplate(promptResource)
        .create(Map.of("dish", dish));
    return chatClient.prompt(prompt).call().content();
}

最佳实践:模板放资源文件里,和代码解耦。改提示词不用重新编译!

6.3 模板 + 系统消息组合技

java 复制代码
Message userMessage = new PromptTemplate(promptResource)
    .createMessage(Map.of("dish", dish));

Prompt prompt = new Prompt(List.of(
    new SystemMessage("You are a master chef."),
    userMessage
));

6.4 Builder 模式 + 默认值

java 复制代码
PromptTemplate template = PromptTemplate.builder()
    .resource(promptResource)
    .variables(Map.of("value", "hello"))  // 默认值
    .build();

// 运行时覆盖
Prompt prompt = template.create(Map.of("value", "world"));

6.5 自定义模板引擎

默认用 StringTemplate({var} 语法),不喜欢?自己写一个:

java 复制代码
public interface TemplateRenderer 
    extends BiFunction<String, Map<String, Object>, String> {
    String apply(String template, Map<String, Object> variables);
}

// 使用时
PromptTemplate template = PromptTemplate.builder()
    .resource(promptResource)
    .renderer(myCustomRenderer)  // 注入自定义渲染器
    .build();

🛡️ 第七节:Advisor 顾问链------Spring AI 的"中间件"

这是 Spring AI 最有架构感 的设计!Advisor 类似 Spring 的 AOP 拦截器,在请求到达模型前后插入通用逻辑。

7.1 核心接口

java 复制代码
public interface CallAdvisor extends Advisor {
    ChatClientResponse adviseCall(
        ChatClientRequest request, 
        CallAdvisorChain chain
    );
}

执行原理:多个 Advisor 组成链条,每个 Advisor 可以:

  1. 修改请求(ChatClientRequest
  2. 调用下一个 Advisor(chain.nextCall()
  3. 修改响应(ChatClientResponse
  4. 直接返回,短路后续执行

7.2 写个日志顾问,调试神器

java 复制代码
public class LoggingAdvisor implements CallAdvisor {
    private static final Logger LOGGER = 
        LoggerFactory.getLogger("ChatClient.debugger");

    @Override
    public ChatClientResponse adviseCall(
            ChatClientRequest request, 
            CallAdvisorChain chain) {
        debug(request);                    // 记录请求
        var response = chain.nextCall(request); // 继续执行
        debug(response);                   // 记录响应
        return response;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;  // 放最后执行
    }
}

启用日志application.yaml):

yaml 复制代码
logging:
  level:
    ChatClient.debugger: DEBUG

🚀 效果:每次 AI 调用的请求体和响应体,以 JSON 格式完整打印。排查问题时的救命稻草!

7.3 修改请求:重写用户文本

java 复制代码
public class RewriteUserTextAdvisor implements CallAdvisor {
    @Override
    public ChatClientResponse adviseCall(
            ChatClientRequest request, CallAdvisorChain chain) {
        if (request.context().containsKey("updated_user_text")) {
            String updatedText = (String) request.context()
                .get("updated_user_text");
            // 使用 mutate 构建新的不可变请求
            var mutatedRequest = request.mutate()
                .prompt(request.prompt()
                    .augmentUserMessage(updatedText))
                .build();
            return chain.nextCall(mutatedRequest);
        }
        return chain.nextCall(request);
    }
}

REST 控制器里使用

java 复制代码
@RestController
public class RewriteController {
    private final ChatClient chatClient;
    
    public RewriteController(ChatClient.Builder builder) {
        this.chatClient = builder
            .defaultAdvisors(new RewriteUserTextAdvisor())
            .build();
    }

    @PostMapping("/rewrite")
    public String rewrite(@RequestBody Request req) {
        return chatClient.prompt()
            .user(req.input())
            .advisors(spec -> spec.param(
                "updated_user_text", 
                StringUtils.trimToEmpty(req.updatedInput())
            ))
            .call()
            .content();
    }
}

7.4 两种启用方式

方式 代码 生效范围
全局默认 builder.defaultAdvisors(advisor) 该 Builder 创建的所有 ChatClient
单次请求 .prompt().advisors(advisor).call() 仅当前请求

7.5 顾问顺序很重要!

Advisor 继承自 Ordered,数字越小越先执行。Spring AI 内置顾问有固定顺序,自定义顾问建议用大于 0 的值,避免冲突。


🔄 第八节:递归顾问------Spring AI 1.1 的王炸特性

Spring AI 1.1 之前,顾问链每个 Advisor 只能执行一次。1.1 引入了递归顾问,让链的一部分可以重复执行------这就能实现复杂的 Agent 工作流!

8.1 核心:copy 方法

CallAdvisorChain.copy(this) 可以复制当前 Advisor 之后的剩余链条,生成一个子链反复调用。

8.2 实战:让 AI 讲三次笑话

java 复制代码
public class TellJokeAdvisor implements CallAdvisor {
    @Override
    public ChatClientResponse adviseCall(
            ChatClientRequest request, CallAdvisorChain chain) {
        var responses = new ArrayList<String>();
        for (int i = 0; i < 3; i++) {
            // 修改提示词:让 AI 讲笑话
            var prompt = request.prompt().augmentUserMessage(
                userMsg -> userMsg.mutate()
                    .text("Tell a joke about " + userMsg.getText())
            );
            // 复制子链并调用
            var response = chain.copy(this)
                .nextCall(request.mutate()
                    .prompt(prompt.build())
                    .build())
                .chatResponse();
            responses.add(response.getResult().getOutput().getText());
        }
        // 合并三次结果返回
        return ChatClientResponse.builder()
            .chatResponse(ChatResponse.builder()
                .generations(List.of(new Generation(
                    AssistantMessage.builder()
                        .content(String.join("\n", responses))
                        .build())))
            .build());
    }
}

调用效果:输入 "programmer",返回 3 个关于程序员的笑话!

🔥 应用场景 :递归顾问是构建 Agent 评估-优化循环多轮工具调用 等高级模式的底层基础设施。玩转这个,你的 AI 应用就从"问答机器人"升级为"智能代理"!


🧠 总结:一张图看懂 Spring AI 聊天补全

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│                        ChatClient                            │
│  .prompt() → .system()/.user() → .call()/.stream()         │
└────────────────────┬────────────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────────────┐
│                    Advisor Chain                             │
│  [LoggingAdvisor] → [RewriteAdvisor] → [TellJokeAdvisor]   │
│     记录日志          修改请求          递归执行              │
└────────────────────┬────────────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────────────┐
│                    ChatModel.call(Prompt)                    │
│  ┌─────────────────────────────────────────────┐            │
│  │  Prompt                                      │            │
│  │  ├─ SystemMessage (人设/指令)               │            │
│  │  ├─ UserMessage (用户问题)                  │            │
│  │  └─ ChatOptions (temperature/model等)       │            │
│  └─────────────────────────────────────────────┘            │
└────────────────────┬────────────────────────────────────────┘
                     │ HTTP
┌────────────────────▼────────────────────────────────────────┐
│                      LLM API                                 │
└────────────────────┬────────────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────────────┐
│                   ChatResponse                               │
│  ├─ Generation → AssistantMessage (文本)                    │
│  ├─ Usage (Prompt/Completion/Total Tokens)                  │
│  └─ RateLimit (限流信息)                                    │
└─────────────────────────────────────────────────────────────┘

💬 互动时间

你在用 Spring AI 时遇到过哪些坑?

  • A:Prompt 模板管理混乱
  • B:Advisor 顺序搞不清,调试到崩溃
  • C:Token 用量监控没做,月底账单爆炸
  • D:其他,评论区见

这篇文章干货密度很高,建议收藏⭐反复看!有问题评论区留言,我会逐一回复!转发给正在学 AI 开发的 Java 朋友,一起进步! 🚀

📚 下章预告stream 流式响应------让 AI 回答像打字一样实时呈现,用户体验直接拉满!关注不迷路~

相关推荐
IVEN_1 小时前
Hermes Agent 接入 Kimi Coding 套餐:修复 Vision 图像分析功能
人工智能
Bode_20021 小时前
AI时代制造企业创新的需要的关键技术
人工智能
Arvid1 小时前
Transformer 隐藏的另一半:Attention 之后,大模型靠什么变聪明?
人工智能
极客老王说Agent1 小时前
实在Agent委外加工智能化管控方案与落地案例:重构2026制造业协同新范式
人工智能·ai·chatgpt
璞华Purvar1 小时前
VC PE投资管理系统选型的核心考量因素有哪些?(2026选型指南)
大数据·运维·人工智能
图特摩斯科技1 小时前
不用OWL/RDF!Function 和 Action 在本体智能平台中的重要性体现
人工智能·知识图谱·本体论·palantir·ontology
飞哥数智坊1 小时前
“AI 做事,人做主”,值得好好琢磨
人工智能·ai编程
广州服务器托管1 小时前
[2026.5.12][IT工坊]WIN11.26300.8376专业工作站版[PIIS]中简 深度优化
运维·人工智能·windows·计算机网络·可信计算技术