目录

1.创建一个ChatClient

2.构建提示(Prompt)

3.处理响应

4.使用默认配置

5.使用 Advisors(顾问)

6.参数配置

7.综合示例


Spring AI 中的 ChatClient 是一个核心接口,它通过流畅的 API(Fluent API)简化了与大型语言模型(LLM)的交互过程,支持同步流式编程模型。

先看看最简单的对话:

@Resource
private ChatClient.Builder chatClient;

public String simpleChat(String message) {
    return chatClient.build()
                .prompt()
                .user(message)
                .call()
                .content();
}

我们可以从创建ChatClient、构建提示、处理响应、使用默认配置、使用Advisors、参数配置这几个方面来进行拓展~

1.创建一个ChatClient

1)使用 Spring Boot 自动配置(推荐)
Spring Boot 提供了开箱即用的自动配置,你可以直接注入 ChatClient.Builder 来构建 ChatClient 实例

@RestController
public class MyController {
    private final ChatClient chatClient;

    // 1.注入自动配置的 Builder
    public MyController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    // 2.使用特定的 ChatModel 配置 chatClient
//    public MyController(ChatModel dashscopeChatModel) {
//        this.chatClient = ChatClient.create(dashscopeChatModel);
//        // 或者使用 builder 方式
//        // this.chatClient = ChatClient.builder(dashscopeChatModel).build();
//    }

    @GetMapping("/ai")
    public String generateResponse(String userInput) {
        return chatClient.prompt()
                .user(userInput)
                .call()
                .content();
    }
}

在这种方式下,ChatClient.Builder 是一个原型(Prototype)Bean,每次注入时都会创建一个新的实例,通常基于应用配置文件中配置的默认模型(如 OpenAI)

2)编程式创建

当需要更精细的控制,或者应用中需要与多个不同的模型交互时,你可以编程式地创建 ChatClient

@Configuration
public class ChatClientConfig {
    // 假设已经通过 Spring Boot 自动配置了某个 ChatModel
    @Bean
    public ChatClient openAiChatClient(OpenAiChatModel chatModel) {
        return ChatClient.create(chatModel);
        // 或者使用 Builder 进行更多配置
        // return ChatClient.builder(chatModel).defaultSystem("你是一个助手").build();
    }

    @Bean
    public ChatClient anthropicChatClient(AnthropicChatModel chatModel) {
        return ChatClient.create(chatModel);
    }
}

在这种情况下,通常需要在配置文件中禁用默认的 ChatClient.Builder 自动配置:spring.ai.chat.client.enabled=false

2.构建提示(Prompt)

ChatClient 提供了灵活的链式调用来构建提示信息

1)基本用户提示

最简单的方式是直接发送用户消息。

String response = chatClient.prompt()
        .user("解释一下人工智能")
        .call()
        .content();

2)添加系统消息

系统消息用于引导模型的行为、角色设定或输出限制

String response = chatClient.prompt()
        .system("你是一个专业的技术文档翻译助手") // 固定系统消息
        .user("将 'Hello World' 翻译成中文")
        .call()
        .content();

3)带参数的系统消息

系统消息中可以包含占位符,并在运行时动态替换

String response = chatClient.prompt()
        .system(s -> s.text("以{style}风格回答").param("style", "古风"))
        .user("解释什么是人工智能")
        .call()
        .content();

4)使用预构建的 Prompt 对象

对于复杂的或需要复用的提示结构,可以预先构建 Prompt 对象

Prompt promptTemplate = Prompt.builder()
        .systemMessage("你是一个幽默的助手")
        .userMessage("讲一个{topic}相关的笑话")
        .build();

Prompt dynamicPrompt = promptTemplate.replaceParams(Map.of("topic", "程序员"));
ChatResponse response = chatClient.prompt(dynamicPrompt).call().chatResponse();

3.处理响应

ChatClient 提供了多种方法来处理和解析模型的响应

1)获取文本内容(content()

最直接的方式,返回生成的文本字符串。

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

2)获取完整响应对象(chatResponse()

返回 ChatResponse 对象,包含响应内容、元数据(如令牌使用情况)等信息

ChatResponse chatResponse = chatClient.prompt().user("讲个笑话").call().chatResponse();
String text = chatResponse.getResult().getOutput().getText();
// 获取元数据,如令牌消耗
// Metadata metadata = chatResponse.getMetadata();

3)映射到 Java 对象(entity()

如果模型输出是结构化的(如 JSON),可以将其自动映射到自定义的 Java 类或记录(Record)中

record Recipe(String dish, List<String> ingredients) {}

Recipe recipe = chatClient.prompt()
        .user("生成一份蔬菜沙拉食谱,以JSON格式输出")
        .call()
        .entity(Recipe.class);

4)流式响应(stream()

对于需要实时输出或处理较长文本的场景,可以使用流式响应,返回一个 Flux<String>

Flux<String> flux = chatClient.prompt()
        .user("讲述一个长篇故事")
        .stream()
        .content();
// 在 WebFlux 控制器中可以直接返回 Flux

4.使用默认配置

你可以在创建 ChatClient 时定义一些默认配置,简化每次请求的代码

1)默认系统提示词

这样,每次请求都会自动带上这个系统消息。

@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
    return builder
            .defaultSystem("你是一个乐于助人的助手,以海盗风格回答所有问题")
            .build();
}

2)默认模型选项

可以设置默认的模型参数,如温度(temperature)、最大令牌数(maxTokens)等

@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
    return builder
            .defaultOptions(OpenAiChatOptions.builder()
                    .model("gpt-3.5-turbo")
                    .temperature(0.7)
                    .maxTokens(1024)
                    .build())
            .build();
}

5.使用 Advisors(顾问)

Advisors 提供了一种拦截和增强 AI 交互的强大方式,常用于集成聊天记忆(Chat Memory) 和检索增强生成(RAG) 等功能

1)聊天记忆(多轮对话)

让模型记住之前的对话历史。聊天记忆通常通过 CONVERSATION_ID 来区分不同用户的对话

// 通常需要配置 ChatMemory Bean (如 InMemoryChatMemory)
ChatResponse response = chatClient.prompt()
        .user(userInput)
        .advisors(new MessageChatMemoryAdvisor(chatMemory)) // 添加聊天记忆顾问
        .call()
        .chatResponse();

2)检索增强生成(RAG)

从向量存储中检索相关信息来增强提示。

ChatResponse response = chatClient.prompt()
        .user(query)
        .advisors(new QuestionAnswerAdvisor(vectorStore)) // 添加 RAG 顾问
        .call()
        .chatResponse();

6.参数配置

在 application.yml 中,你可以配置底层模型的相关参数

1)openai:

spring:
  ai:
    openai:
      api-key: your-openai-api-key
      base-url: https://api.openai.com
      chat:
        options:
          model: gpt-3.5-turbo
          temperature: 0.7
          max-tokens: 1000

2)阿里百炼

spring:
  ai:
    dashscope:
      api-key: xxx
      chat:
        options:
          model: qwen-plus

7.综合示例

结合了系统消息、参数、流式响应和异常处理的示例:

@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultSystem("你是一个友好的助手,擅长用{style}风格回答问题。")
                .build();
    }

    @GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> chat(@RequestParam String message, 
                             @RequestParam(defaultValue = "通俗易懂") String style) {
        return chatClient.prompt()
                .system(s -> s.param("style", style)) // 动态设置风格参数
                .user(message)
                .stream()
                .content();
    }

    @PostMapping("/structured")
    public ResponseEntity<?> getStructuredRecipe(@RequestParam String dish) {
        try {
            record Recipe(String name, List<String> ingredients, String steps) {}
            Recipe recipe = chatClient.prompt()
                    .user("请提供一道{ dish }的详细菜谱,以JSON格式输出,包含菜名、食材列表和步骤。")
                    .param("dish", dish)
                    .call()
                    .entity(Recipe.class);
            return ResponseEntity.ok(recipe);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("生成菜谱时出错: " + e.getMessage());
        }
    }
}

看到这里了,如果对你有帮助,可以点个赞么~

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐