SpringAI(GA):ChatClient调用链路解读

335 阅读14分钟

原文链接地址# SpringAI(GA):ChatClient调用链路解读

教程说明

说明:本教程将采用2025年5月20日正式的GA版,给出如下内容

  1. 核心功能模块的快速上手教程
  2. 核心功能模块的源码级解读
  3. Spring ai alibaba增强的快速上手教程 + 源码级解读

版本:JDK21 + SpringBoot3.4.5 + SpringAI 1.0.0 + SpringAI Alibaba最新

将陆续完成如下章节教程。本章是第一章(chat初体验)下的其中一部分源码解读—ChatClient解读

  • 目前已完成第一、二章源码解读部分

微信推文往届解读可参考:

获取更好的观赏体验,可付费获取飞书云文档Spring AI最新教程权限,目前39.9,随着内容不断完善,会逐步涨价。

注:M6版快速上手教程+源码解读飞书云文档已免费提供

[!TIP] ChatClient 端设置 advisors、ChatOptions、用户提示信息、系统提示信息、工具等信息,构建 DefaultChatClient.DefaultChatClientRequestSpec,再利用 DefaultChatClientUtils 将其转换为 ChatClientRequest

AdvisorChain 链调用一系列的增强器Advisor 基础,每个增强器输入是 ChatClientRequest,输出 ChatClientResponse(其中必定会用到的是 ChatModelCallAdvisor 或 ChatModelStreamAdvisor)

ChatClient

类的说明:面向对话式 AI 模型的客户端接口,提供了系列的 API 与 AI 会话模型交互,该接口封装了请求构建、调用、响应处理等流畅,支持同步、流式调用

方法说明

方法名称
描述
create(静态方法)
由ChatModel、观测信息等创建 ChatClient 实例
builder(静态方法)
由ChatModel、观测信息等创建 ChatClient.Builder实例
mutate
复制当前客户端配置,生成新的ChatClient.Builder实例
prompt
构建ChatClientRequestSpec实例

内部接口类说明

接口类
方法名称
描述
Builder
(全局的ChatClient配置)
defaultAdvisors
设置advisors
defaultOptions
设置ChatOptions
defaultUser
设置用户提示信息
defaultSystem
设置系统提示信息
defaultTemplateRenderer
设置模版渲染器,用于处理字符串的占位符
defaultToolNames
根据工具名称获取工具配置
defaultTools
根据实例获取工具配置
defaultToolCallbacks
根据ToolCallback获取工具配置
defaultToolContext
工具的上下文
clone
复制当前客户端配置,生成新的ChatClient.Builder实例
build
构建最终的ChatClient实例
ChatClientRequestSpec
(当前的ChatClient配置)
advisors
设置advisors
options
设置ChatOptions
user
设置用户提示信息
system
设置系统提示信息
templateRenderer
设置模版渲染器,用于处理字符串的占位符
toolNames
根据工具名称获取工具配置
tools
根据实例获取工具配置
toolCallbacks
根据ToolCallback获取工具配置
toolContext
工具的上下文
mutate
复制当前客户端配置,生成新的ChatClient.Builder实例
messages
添加Message
call
同步调用
stream
流式调用
PromptUserSpec
(用户提示信息的构建规范)
text
设置用户文本内容
param
设置参数
params
设置参数
media
设置多媒体内容(如图片)
PromptSystemSpec
(系统提示信息的构建规范)
text
设置系统指令
param
设置参数
params
设置参数
AdvisorSpec
(设置增强器)
param
设置增强器中会用到的一些参数配置
advisors
添加增强器
CallResponseSpec
entity
将响应体转换为指定类型
chatClientResponse
原始响应对象+请求时的上下文内容
chatResponse
原始的响应对象
content
响应的文本内容
responseEntity
获取封装了响应头和body的对象

chatClientResponse
流式的原始响应对象+请求时的上下文内容
StreamResponseSpec
chatResponse
流式的原始的响应对象

content
流式的响应的文本内容
public interface ChatClient {
    static ChatClient create(ChatModel chatModel) {
        return create(chatModel, ObservationRegistry.NOOP);
    }

    static ChatClient create(ChatModel chatModel, ObservationRegistry observationRegistry) {
        return create(chatModel, observationRegistry, (ChatClientObservationConvention)null);
    }

    static ChatClient create(ChatModel chatModel, ObservationRegistry observationRegistry, @Nullable ChatClientObservationConvention observationConvention) {
        Assert.notNull(chatModel, "chatModel cannot be null");
        Assert.notNull(observationRegistry, "observationRegistry cannot be null");
        return builder(chatModel, observationRegistry, observationConvention).build();
    }

    static Builder builder(ChatModel chatModel) {
        return builder(chatModel, ObservationRegistry.NOOP, (ChatClientObservationConvention)null);
    }

    static Builder builder(ChatModel chatModel, ObservationRegistry observationRegistry, @Nullable ChatClientObservationConvention customObservationConvention) {
        Assert.notNull(chatModel, "chatModel cannot be null");
        Assert.notNull(observationRegistry, "observationRegistry cannot be null");
        return new DefaultChatClientBuilder(chatModel, observationRegistry, customObservationConvention);
    }

    ChatClientRequestSpec prompt();

    ChatClientRequestSpec prompt(String content);

    ChatClientRequestSpec prompt(Prompt prompt);

    Builder mutate();

    public interface AdvisorSpec {
        AdvisorSpec param(String k, Object v);

        AdvisorSpec params(Map<String, Object> p);

        AdvisorSpec advisors(Advisor... advisors);

        AdvisorSpec advisors(List<Advisor> advisors);
    }

    public interface Builder {
        Builder defaultAdvisors(Advisor... advisor);

        Builder defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer);

        Builder defaultAdvisors(List<Advisor> advisors);

        Builder defaultOptions(ChatOptions chatOptions);

        Builder defaultUser(String text);

        Builder defaultUser(Resource text, Charset charset);

        Builder defaultUser(Resource text);

        Builder defaultUser(Consumer<PromptUserSpec> userSpecConsumer);

        Builder defaultSystem(String text);

        Builder defaultSystem(Resource text, Charset charset);

        Builder defaultSystem(Resource text);

        Builder defaultSystem(Consumer<PromptSystemSpec> systemSpecConsumer);

        Builder defaultTemplateRenderer(TemplateRenderer templateRenderer);

        Builder defaultToolNames(String... toolNames);

        Builder defaultTools(Object... toolObjects);

        Builder defaultToolCallbacks(ToolCallback... toolCallbacks);

        Builder defaultToolCallbacks(List<ToolCallback> toolCallbacks);

        Builder defaultToolCallbacks(ToolCallbackProvider... toolCallbackProviders);

        Builder defaultToolContext(Map<String, Object> toolContext);

        Builder clone();

        ChatClient build();
    }

    public interface CallPromptResponseSpec {
        String content();

        List<String> contents();

        ChatResponse chatResponse();
    }

    public interface CallResponseSpec {
        @Nullable
        <T> T entity(ParameterizedTypeReference<T> type);

        @Nullable
        <T> T entity(StructuredOutputConverter<T> structuredOutputConverter);

        @Nullable
        <T> T entity(Class<T> type);

        ChatClientResponse chatClientResponse();

        @Nullable
        ChatResponse chatResponse();

        @Nullable
        String content();

        <T> ResponseEntity<ChatResponse, T> responseEntity(Class<T> type);

        <T> ResponseEntity<ChatResponse, T> responseEntity(ParameterizedTypeReference<T> type);

        <T> ResponseEntity<ChatResponse, T> responseEntity(StructuredOutputConverter<T> structuredOutputConverter);
    }

    public interface ChatClientRequestSpec {
        Builder mutate();

        ChatClientRequestSpec advisors(Consumer<AdvisorSpec> consumer);

        ChatClientRequestSpec advisors(Advisor... advisors);

        ChatClientRequestSpec advisors(List<Advisor> advisors);

        ChatClientRequestSpec messages(Message... messages);

        ChatClientRequestSpec messages(List<Message> messages);

        <T extends ChatOptions> ChatClientRequestSpec options(T options);

        ChatClientRequestSpec toolNames(String... toolNames);

        ChatClientRequestSpec tools(Object... toolObjects);

        ChatClientRequestSpec toolCallbacks(ToolCallback... toolCallbacks);

        ChatClientRequestSpec toolCallbacks(List<ToolCallback> toolCallbacks);

        ChatClientRequestSpec toolCallbacks(ToolCallbackProvider... toolCallbackProviders);

        ChatClientRequestSpec toolContext(Map<String, Object> toolContext);

        ChatClientRequestSpec system(String text);

        ChatClientRequestSpec system(Resource textResource, Charset charset);

        ChatClientRequestSpec system(Resource text);

        ChatClientRequestSpec system(Consumer<PromptSystemSpec> consumer);

        ChatClientRequestSpec user(String text);

        ChatClientRequestSpec user(Resource text, Charset charset);

        ChatClientRequestSpec user(Resource text);

        ChatClientRequestSpec user(Consumer<PromptUserSpec> consumer);

        ChatClientRequestSpec templateRenderer(TemplateRenderer templateRenderer);

        CallResponseSpec call();

        StreamResponseSpec stream();
    }

    public interface PromptSystemSpec {
        PromptSystemSpec text(String text);

        PromptSystemSpec text(Resource text, Charset charset);

        PromptSystemSpec text(Resource text);

        PromptSystemSpec params(Map<String, Object> p);

        PromptSystemSpec param(String k, Object v);
    }

    public interface PromptUserSpec {
        PromptUserSpec text(String text);

        PromptUserSpec text(Resource text, Charset charset);

        PromptUserSpec text(Resource text);

        PromptUserSpec params(Map<String, Object> p);

        PromptUserSpec param(String k, Object v);

        PromptUserSpec media(Media... media);

        PromptUserSpec media(MimeType mimeType, URL url);

        PromptUserSpec media(MimeType mimeType, Resource resource);
    }

    public interface StreamPromptResponseSpec {
        Flux<ChatResponse> chatResponse();

        Flux<String> content();
    }

    public interface StreamResponseSpec {
        Flux<ChatClientResponse> chatClientResponse();

        Flux<ChatResponse> chatResponse();

        Flux<String> content();
    }
}

DefaultChatClient

ChatClient 接口的默认实现类,用于构建和执行与 AI 聊天模型交互的请求

  1. 内部类 DefaultChatClientRequestSpec 实现了 ChatClient.ChatClientRequestSpec:新增 ChatModelCallAdvisor
public static class DefaultChatClientRequestSpec implements ChatClient.ChatClientRequestSpec {

        private BaseAdvisorChain buildAdvisorChain() {
            this.advisors.add(ChatModelCallAdvisor.builder().chatModel(this.chatModel).build());
            this.advisors.add(ChatModelStreamAdvisor.builder().chatModel(this.chatModel).build());
            return DefaultAroundAdvisorChain.builder(this.observationRegistry).pushAll(this.advisors).templateRenderer(this.templateRenderer).build();
        }
    }
  1. 内部类 DefaultPromptSystemSpec 实现 ChatClient.PromptSystemSpec:设置用户文本内容、参数
  2. 内部类 DefaultPromptSystemSpec 实现 ChatClient.PromptSystemSpec:设置系统文本内容、参数
  3. 内部类 DefaultAdvisorSpec 实现 ChatClient.AdvisorSpec:设置 Advisor,及其 advisor 中用到的参数
  4. 内部类 DefaultCallResponseSpec 实现 ChatClient.CallResponseSpec:通过 doGetObservableChatClientResponse 方法发起请求,调用一系列的 BaseAdvisorChain
public static class DefaultCallResponseSpec implements ChatClient.CallResponseSpec {
    private final ChatClientRequest request;
    private final BaseAdvisorChain advisorChain;
    private final ObservationRegistry observationRegistry;
    private final ChatClientObservationConvention observationConvention;


    private ChatClientResponse doGetObservableChatClientResponse(ChatClientRequest chatClientRequest, @Nullable String outputFormat) {
    if (outputFormat != null) {
        chatClientRequest.context().put(ChatClientAttributes.OUTPUTFORMAT.getKey(), outputFormat);
    }

    ChatClientObservationContext observationContext = ChatClientObservationContext.builder().request(chatClientRequest).advisors(this.advisorChain.getCallAdvisors()).stream(false).format(outputFormat).build();
    Observation observation = ChatClientObservationDocumentation.AICHATCLIENT.observation(this.observationConvention, DefaultChatClient.DEFAULTCHATCLIENTOBSERVATIONCONVENTION, () -> observationContext, this.observationRegistry);
    ChatClientResponse chatClientResponse = (ChatClientResponse)observation.observe(() -> this.advisorChain.nextCall(chatClientRequest));
    return chatClientResponse != null ? chatClientResponse : ChatClientResponse.builder().build();
    }
}
  1. 内部类 DefaultStreamResponseSpec 实现 ChatClient.StreamResponseSpec:通过 doGetObservableFluxChatResponse 方法发起请求,调用一系列的 BaseAdvisorChain
public static class DefaultStreamResponseSpec implements ChatClient.StreamResponseSpec {
    private final ChatClientRequest request;
    private final BaseAdvisorChain advisorChain;
    private final ObservationRegistry observationRegistry;
    private final ChatClientObservationConvention observationConvention;

    private Flux<ChatClientResponse> doGetObservableFluxChatResponse(ChatClientRequest chatClientRequest) {
        return Flux.deferContextual((contextView) -> {
            ChatClientObservationContext observationContext = ChatClientObservationContext.builder().request(chatClientRequest).advisors(this.advisorChain.getStreamAdvisors()).stream(true).build();
            Observation observation = ChatClientObservationDocumentation.AICHATCLIENT.observation(this.observationConvention, DefaultChatClient.DEFAULTCHATCLIENTOBSERVATIONCONVENTION, () -> observationContext, this.observationRegistry);
            observation.parentObservation((Observation)contextView.getOrDefault("micrometer.observation", (Object)null)).start();
            Flux var10000 = this.advisorChain.nextStream(chatClientRequest);
            Objects.requireNonNull(observation);
            return var10000.doOnError(observation::error).doFinally((s) -> observation.stop()).contextWrite((ctx) -> ctx.put("micrometer.observation", observation));
        });
    }
}

完整代码如下

package org.springframework.ai.chat.client;

public class DefaultChatClient implements ChatClient {
    private static final ChatClientObservationConvention DEFAULTCHATCLIENTOBSERVATIONCONVENTION = new DefaultChatClientObservationConvention();
    private static final TemplateRenderer DEFAULTTEMPLATERENDERER = StTemplateRenderer.builder().build();
    private final DefaultChatClientRequestSpec defaultChatClientRequest;

    public DefaultChatClient(DefaultChatClientRequestSpec defaultChatClientRequest) {
        Assert.notNull(defaultChatClientRequest, "defaultChatClientRequest cannot be null");
        this.defaultChatClientRequest = defaultChatClientRequest;
    }

    public ChatClient.ChatClientRequestSpec prompt() {
        return new DefaultChatClientRequestSpec(this.defaultChatClientRequest);
    }

    public ChatClient.ChatClientRequestSpec prompt(String content) {
        Assert.hasText(content, "content cannot be null or empty");
        return this.prompt(new Prompt(content));
    }

    public ChatClient.ChatClientRequestSpec prompt(Prompt prompt) {
        Assert.notNull(prompt, "prompt cannot be null");
        DefaultChatClientRequestSpec spec = new DefaultChatClientRequestSpec(this.defaultChatClientRequest);
        if (prompt.getOptions() != null) {
            spec.options(prompt.getOptions());
        }

        if (prompt.getInstructions() != null) {
            spec.messages(prompt.getInstructions());
        }

        return spec;
    }

    public ChatClient.Builder mutate() {
        return this.defaultChatClientRequest.mutate();
    }

    public static class DefaultPromptUserSpec implements ChatClient.PromptUserSpec {
        private final Map<String, Object> params = new HashMap();
        private final List<Media> media = new ArrayList();
        @Nullable
        private String text;

        public ChatClient.PromptUserSpec media(Media... media) {
            Assert.notNull(media, "media cannot be null");
            Assert.noNullElements(media, "media cannot contain null elements");
            this.media.addAll(Arrays.asList(media));
            return this;
        }

        public ChatClient.PromptUserSpec media(MimeType mimeType, URL url) {
            Assert.notNull(mimeType, "mimeType cannot be null");
            Assert.notNull(url, "url cannot be null");

            try {
                this.media.add(Media.builder().mimeType(mimeType).data(url.toURI()).build());
                return this;
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            }
        }

        public ChatClient.PromptUserSpec media(MimeType mimeType, Resource resource) {
            Assert.notNull(mimeType, "mimeType cannot be null");
            Assert.notNull(resource, "resource cannot be null");
            this.media.add(Media.builder().mimeType(mimeType).data(resource).build());
            return this;
        }

        public ChatClient.PromptUserSpec text(String text) {
            Assert.hasText(text, "text cannot be null or empty");
            this.text = text;
            return this;
        }

        public ChatClient.PromptUserSpec text(Resource text, Charset charset) {
            Assert.notNull(text, "text cannot be null");
            Assert.notNull(charset, "charset cannot be null");

            try {
                this.text(text.getContentAsString(charset));
                return this;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public ChatClient.PromptUserSpec text(Resource text) {
            Assert.notNull(text, "text cannot be null");
            this.text(text, Charset.defaultCharset());
            return this;
        }

        public ChatClient.PromptUserSpec param(String key, Object value) {
            Assert.hasText(key, "key cannot be null or empty");
            Assert.notNull(value, "value cannot be null");
            this.params.put(key, value);
            return this;
        }

        public ChatClient.PromptUserSpec params(Map<String, Object> params) {
            Assert.notNull(params, "params cannot be null");
            Assert.noNullElements(params.keySet(), "param keys cannot contain null elements");
            Assert.noNullElements(params.values(), "param values cannot contain null elements");
            this.params.putAll(params);
            return this;
        }

        @Nullable
        protected String text() {
            return this.text;
        }

        protected Map<String, Object> params() {
            return this.params;
        }

        protected List<Media> media() {
            return this.media;
        }
    }

    public static class DefaultPromptSystemSpec implements ChatClient.PromptSystemSpec {
        private final Map<String, Object> params = new HashMap();
        @Nullable
        private String text;

        public ChatClient.PromptSystemSpec text(String text) {
            Assert.hasText(text, "text cannot be null or empty");
            this.text = text;
            return this;
        }

        public ChatClient.PromptSystemSpec text(Resource text, Charset charset) {
            Assert.notNull(text, "text cannot be null");
            Assert.notNull(charset, "charset cannot be null");

            try {
                this.text(text.getContentAsString(charset));
                return this;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public ChatClient.PromptSystemSpec text(Resource text) {
            Assert.notNull(text, "text cannot be null");
            this.text(text, Charset.defaultCharset());
            return this;
        }

        public ChatClient.PromptSystemSpec param(String key, Object value) {
            Assert.hasText(key, "key cannot be null or empty");
            Assert.notNull(value, "value cannot be null");
            this.params.put(key, value);
            return this;
        }

        public ChatClient.PromptSystemSpec params(Map<String, Object> params) {
            Assert.notNull(params, "params cannot be null");
            Assert.noNullElements(params.keySet(), "param keys cannot contain null elements");
            Assert.noNullElements(params.values(), "param values cannot contain null elements");
            this.params.putAll(params);
            return this;
        }

        @Nullable
        protected String text() {
            return this.text;
        }

        protected Map<String, Object> params() {
            return this.params;
        }
    }

    public static class DefaultAdvisorSpec implements ChatClient.AdvisorSpec {
        private final List<Advisor> advisors = new ArrayList();
        private final Map<String, Object> params = new HashMap();

        public ChatClient.AdvisorSpec param(String key, Object value) {
            Assert.hasText(key, "key cannot be null or empty");
            Assert.notNull(value, "value cannot be null");
            this.params.put(key, value);
            return this;
        }

        public ChatClient.AdvisorSpec params(Map<String, Object> params) {
            Assert.notNull(params, "params cannot be null");
            Assert.noNullElements(params.keySet(), "param keys cannot contain null elements");
            Assert.noNullElements(params.values(), "param values cannot contain null elements");
            this.params.putAll(params);
            return this;
        }

        public ChatClient.AdvisorSpec advisors(Advisor... advisors) {
            Assert.notNull(advisors, "advisors cannot be null");
            Assert.noNullElements(advisors, "advisors cannot contain null elements");
            this.advisors.addAll(List.of(advisors));
            return this;
        }

        public ChatClient.AdvisorSpec advisors(List<Advisor> advisors) {
            Assert.notNull(advisors, "advisors cannot be null");
            Assert.noNullElements(advisors, "advisors cannot contain null elements");
            this.advisors.addAll(advisors);
            return this;
        }

        public List<Advisor> getAdvisors() {
            return this.advisors;
        }

        public Map<String, Object> getParams() {
            return this.params;
        }
    }

    public static class DefaultCallResponseSpec implements ChatClient.CallResponseSpec {
        private final ChatClientRequest request;
        private final BaseAdvisorChain advisorChain;
        private final ObservationRegistry observationRegistry;
        private final ChatClientObservationConvention observationConvention;

        public DefaultCallResponseSpec(ChatClientRequest chatClientRequest, BaseAdvisorChain advisorChain, ObservationRegistry observationRegistry, ChatClientObservationConvention observationConvention) {
            Assert.notNull(chatClientRequest, "chatClientRequest cannot be null");
            Assert.notNull(advisorChain, "advisorChain cannot be null");
            Assert.notNull(observationRegistry, "observationRegistry cannot be null");
            Assert.notNull(observationConvention, "observationConvention cannot be null");
            this.request = chatClientRequest;
            this.advisorChain = advisorChain;
            this.observationRegistry = observationRegistry;
            this.observationConvention = observationConvention;
        }

        public <T> ResponseEntity<ChatResponse, T> responseEntity(Class<T> type) {
            Assert.notNull(type, "type cannot be null");
            return this.doResponseEntity(new BeanOutputConverter(type));
        }

        public <T> ResponseEntity<ChatResponse, T> responseEntity(ParameterizedTypeReference<T> type) {
            Assert.notNull(type, "type cannot be null");
            return this.doResponseEntity(new BeanOutputConverter(type));
        }

        public <T> ResponseEntity<ChatResponse, T> responseEntity(StructuredOutputConverter<T> structuredOutputConverter) {
            Assert.notNull(structuredOutputConverter, "structuredOutputConverter cannot be null");
            return this.doResponseEntity(structuredOutputConverter);
        }

        protected <T> ResponseEntity<ChatResponse, T> doResponseEntity(StructuredOutputConverter<T> outputConverter) {
            Assert.notNull(outputConverter, "structuredOutputConverter cannot be null");
            ChatResponse chatResponse = this.doGetObservableChatClientResponse(this.request, outputConverter.getFormat()).chatResponse();
            String responseContent = getContentFromChatResponse(chatResponse);
            if (responseContent == null) {
                return new ResponseEntity(chatResponse, (Object)null);
            } else {
                T entity = (T)outputConverter.convert(responseContent);
                return new ResponseEntity(chatResponse, entity);
            }
        }

        @Nullable
        public <T> T entity(ParameterizedTypeReference<T> type) {
            Assert.notNull(type, "type cannot be null");
            return (T)this.doSingleWithBeanOutputConverter(new BeanOutputConverter(type));
        }

        @Nullable
        public <T> T entity(StructuredOutputConverter<T> structuredOutputConverter) {
            Assert.notNull(structuredOutputConverter, "structuredOutputConverter cannot be null");
            return (T)this.doSingleWithBeanOutputConverter(structuredOutputConverter);
        }

        @Nullable
        public <T> T entity(Class<T> type) {
            Assert.notNull(type, "type cannot be null");
            BeanOutputConverter<T> outputConverter = new BeanOutputConverter(type);
            return (T)this.doSingleWithBeanOutputConverter(outputConverter);
        }

        @Nullable
        private <T> T doSingleWithBeanOutputConverter(StructuredOutputConverter<T> outputConverter) {
            ChatResponse chatResponse = this.doGetObservableChatClientResponse(this.request, outputConverter.getFormat()).chatResponse();
            String stringResponse = getContentFromChatResponse(chatResponse);
            return (T)(stringResponse == null ? null : outputConverter.convert(stringResponse));
        }

        public ChatClientResponse chatClientResponse() {
            return this.doGetObservableChatClientResponse(this.request);
        }

        @Nullable
        public ChatResponse chatResponse() {
            return this.doGetObservableChatClientResponse(this.request).chatResponse();
        }

        @Nullable
        public String content() {
            ChatResponse chatResponse = this.doGetObservableChatClientResponse(this.request).chatResponse();
            return getContentFromChatResponse(chatResponse);
        }

        private ChatClientResponse doGetObservableChatClientResponse(ChatClientRequest chatClientRequest) {
            return this.doGetObservableChatClientResponse(chatClientRequest, (String)null);
        }

        private ChatClientResponse doGetObservableChatClientResponse(ChatClientRequest chatClientRequest, @Nullable String outputFormat) {
            if (outputFormat != null) {
                chatClientRequest.context().put(ChatClientAttributes.OUTPUTFORMAT.getKey(), outputFormat);
            }

            ChatClientObservationContext observationContext = ChatClientObservationContext.builder().request(chatClientRequest).advisors(this.advisorChain.getCallAdvisors()).stream(false).format(outputFormat).build();
            Observation observation = ChatClientObservationDocumentation.AICHATCLIENT.observation(this.observationConvention, DefaultChatClient.DEFAULTCHATCLIENTOBSERVATIONCONVENTION, () -> observationContext, this.observationRegistry);
            ChatClientResponse chatClientResponse = (ChatClientResponse)observation.observe(() -> this.advisorChain.nextCall(chatClientRequest));
            return chatClientResponse != null ? chatClientResponse : ChatClientResponse.builder().build();
        }

        @Nullable
        private static String getContentFromChatResponse(@Nullable ChatResponse chatResponse) {
            return (String)Optional.ofNullable(chatResponse).map(ChatResponse::getResult).map(Generation::getOutput).map(AbstractMessage::getText).orElse((Object)null);
        }
    }

    public static class DefaultStreamResponseSpec implements ChatClient.StreamResponseSpec {
        private final ChatClientRequest request;
        private final BaseAdvisorChain advisorChain;
        private final ObservationRegistry observationRegistry;
        private final ChatClientObservationConvention observationConvention;

        public DefaultStreamResponseSpec(ChatClientRequest chatClientRequest, BaseAdvisorChain advisorChain, ObservationRegistry observationRegistry, ChatClientObservationConvention observationConvention) {
            Assert.notNull(chatClientRequest, "chatClientRequest cannot be null");
            Assert.notNull(advisorChain, "advisorChain cannot be null");
            Assert.notNull(observationRegistry, "observationRegistry cannot be null");
            Assert.notNull(observationConvention, "observationConvention cannot be null");
            this.request = chatClientRequest;
            this.advisorChain = advisorChain;
            this.observationRegistry = observationRegistry;
            this.observationConvention = observationConvention;
        }

        private Flux<ChatClientResponse> doGetObservableFluxChatResponse(ChatClientRequest chatClientRequest) {
            return Flux.deferContextual((contextView) -> {
                ChatClientObservationContext observationContext = ChatClientObservationContext.builder().request(chatClientRequest).advisors(this.advisorChain.getStreamAdvisors()).stream(true).build();
                Observation observation = ChatClientObservationDocumentation.AICHATCLIENT.observation(this.observationConvention, DefaultChatClient.DEFAULTCHATCLIENTOBSERVATIONCONVENTION, () -> observationContext, this.observationRegistry);
                observation.parentObservation((Observation)contextView.getOrDefault("micrometer.observation", (Object)null)).start();
                Flux var10000 = this.advisorChain.nextStream(chatClientRequest);
                Objects.requireNonNull(observation);
                return var10000.doOnError(observation::error).doFinally((s) -> observation.stop()).contextWrite((ctx) -> ctx.put("micrometer.observation", observation));
            });
        }

        public Flux<ChatClientResponse> chatClientResponse() {
            return this.doGetObservableFluxChatResponse(this.request);
        }

        public Flux<ChatResponse> chatResponse() {
            return this.doGetObservableFluxChatResponse(this.request).mapNotNull(ChatClientResponse::chatResponse);
        }

        public Flux<String> content() {
            return this.doGetObservableFluxChatResponse(this.request).mapNotNull(ChatClientResponse::chatResponse).map((r) -> r.getResult() != null && r.getResult().getOutput() != null && r.getResult().getOutput().getText() != null ? r.getResult().getOutput().getText() : "").filter(StringUtils::hasLength);
        }
    }

    public static class DefaultChatClientRequestSpec implements ChatClient.ChatClientRequestSpec {
        private final ObservationRegistry observationRegistry;
        private final ChatClientObservationConvention observationConvention;
        private final ChatModel chatModel;
        private final List<Media> media;
        private final List<String> toolNames;
        private final List<ToolCallback> toolCallbacks;
        private final List<Message> messages;
        private final Map<String, Object> userParams;
        private final Map<String, Object> systemParams;
        private final List<Advisor> advisors;
        private final Map<String, Object> advisorParams;
        private final Map<String, Object> toolContext;
        private TemplateRenderer templateRenderer;
        @Nullable
        private String userText;
        @Nullable
        private String systemText;
        @Nullable
        private ChatOptions chatOptions;

        DefaultChatClientRequestSpec(DefaultChatClientRequestSpec ccr) {
            this(ccr.chatModel, ccr.userText, ccr.userParams, ccr.systemText, ccr.systemParams, ccr.toolCallbacks, ccr.messages, ccr.toolNames, ccr.media, ccr.chatOptions, ccr.advisors, ccr.advisorParams, ccr.observationRegistry, ccr.observationConvention, ccr.toolContext, ccr.templateRenderer);
        }

        public DefaultChatClientRequestSpec(ChatModel chatModel, @Nullable String userText, Map<String, Object> userParams, @Nullable String systemText, Map<String, Object> systemParams, List<ToolCallback> toolCallbacks, List<Message> messages, List<String> toolNames, List<Media> media, @Nullable ChatOptions chatOptions, List<Advisor> advisors, Map<String, Object> advisorParams, ObservationRegistry observationRegistry, @Nullable ChatClientObservationConvention observationConvention, Map<String, Object> toolContext, @Nullable TemplateRenderer templateRenderer) {
            this.media = new ArrayList();
            this.toolNames = new ArrayList();
            this.toolCallbacks = new ArrayList();
            this.messages = new ArrayList();
            this.userParams = new HashMap();
            this.systemParams = new HashMap();
            this.advisors = new ArrayList();
            this.advisorParams = new HashMap();
            this.toolContext = new HashMap();
            Assert.notNull(chatModel, "chatModel cannot be null");
            Assert.notNull(userParams, "userParams cannot be null");
            Assert.notNull(systemParams, "systemParams cannot be null");
            Assert.notNull(toolCallbacks, "toolCallbacks cannot be null");
            Assert.notNull(messages, "messages cannot be null");
            Assert.notNull(toolNames, "toolNames cannot be null");
            Assert.notNull(media, "media cannot be null");
            Assert.notNull(advisors, "advisors cannot be null");
            Assert.notNull(advisorParams, "advisorParams cannot be null");
            Assert.notNull(observationRegistry, "observationRegistry cannot be null");
            Assert.notNull(toolContext, "toolContext cannot be null");
            this.chatModel = chatModel;
            this.chatOptions = chatOptions != null ? chatOptions.copy() : (chatModel.getDefaultOptions() != null ? chatModel.getDefaultOptions().copy() : null);
            this.userText = userText;
            this.userParams.putAll(userParams);
            this.systemText = systemText;
            this.systemParams.putAll(systemParams);
            this.toolNames.addAll(toolNames);
            this.toolCallbacks.addAll(toolCallbacks);
            this.messages.addAll(messages);
            this.media.addAll(media);
            this.advisors.addAll(advisors);
            this.advisorParams.putAll(advisorParams);
            this.observationRegistry = observationRegistry;
            this.observationConvention = observationConvention != null ? observationConvention : DefaultChatClient.DEFAULTCHATCLIENTOBSERVATIONCONVENTION;
            this.toolContext.putAll(toolContext);
            this.templateRenderer = templateRenderer != null ? templateRenderer : DefaultChatClient.DEFAULTTEMPLATERENDERER;
        }

        @Nullable
        public String getUserText() {
            return this.userText;
        }

        public Map<String, Object> getUserParams() {
            return this.userParams;
        }

        @Nullable
        public String getSystemText() {
            return this.systemText;
        }

        public Map<String, Object> getSystemParams() {
            return this.systemParams;
        }

        @Nullable
        public ChatOptions getChatOptions() {
            return this.chatOptions;
        }

        public List<Advisor> getAdvisors() {
            return this.advisors;
        }

        public Map<String, Object> getAdvisorParams() {
            return this.advisorParams;
        }

        public List<Message> getMessages() {
            return this.messages;
        }

        public List<Media> getMedia() {
            return this.media;
        }

        public List<String> getToolNames() {
            return this.toolNames;
        }

        public List<ToolCallback> getToolCallbacks() {
            return this.toolCallbacks;
        }

        public Map<String, Object> getToolContext() {
            return this.toolContext;
        }

        public TemplateRenderer getTemplateRenderer() {
            return this.templateRenderer;
        }

        public ChatClient.Builder mutate() {
            DefaultChatClientBuilder builder = (DefaultChatClientBuilder)ChatClient.builder(this.chatModel, this.observationRegistry, this.observationConvention).defaultTemplateRenderer(this.templateRenderer).defaultToolCallbacks(this.toolCallbacks).defaultToolContext(this.toolContext).defaultToolNames(StringUtils.toStringArray(this.toolNames));
            if (StringUtils.hasText(this.userText)) {
                builder.defaultUser((u) -> u.text(this.userText).params(this.userParams).media((Media[])this.media.toArray(new Media[0])));
            }

            if (StringUtils.hasText(this.systemText)) {
                builder.defaultSystem((s) -> s.text(this.systemText).params(this.systemParams));
            }

            if (this.chatOptions != null) {
                builder.defaultOptions(this.chatOptions);
            }

            builder.addMessages(this.messages);
            return builder;
        }

        public ChatClient.ChatClientRequestSpec advisors(Consumer<ChatClient.AdvisorSpec> consumer) {
            Assert.notNull(consumer, "consumer cannot be null");
            DefaultAdvisorSpec advisorSpec = new DefaultAdvisorSpec();
            consumer.accept(advisorSpec);
            this.advisorParams.putAll(advisorSpec.getParams());
            this.advisors.addAll(advisorSpec.getAdvisors());
            return this;
        }

        public ChatClient.ChatClientRequestSpec advisors(Advisor... advisors) {
            Assert.notNull(advisors, "advisors cannot be null");
            Assert.noNullElements(advisors, "advisors cannot contain null elements");
            this.advisors.addAll(Arrays.asList(advisors));
            return this;
        }

        public ChatClient.ChatClientRequestSpec advisors(List<Advisor> advisors) {
            Assert.notNull(advisors, "advisors cannot be null");
            Assert.noNullElements(advisors, "advisors cannot contain null elements");
            this.advisors.addAll(advisors);
            return this;
        }

        public ChatClient.ChatClientRequestSpec messages(Message... messages) {
            Assert.notNull(messages, "messages cannot be null");
            Assert.noNullElements(messages, "messages cannot contain null elements");
            this.messages.addAll(List.of(messages));
            return this;
        }

        public ChatClient.ChatClientRequestSpec messages(List<Message> messages) {
            Assert.notNull(messages, "messages cannot be null");
            Assert.noNullElements(messages, "messages cannot contain null elements");
            this.messages.addAll(messages);
            return this;
        }

        public <T extends ChatOptions> ChatClient.ChatClientRequestSpec options(T options) {
            Assert.notNull(options, "options cannot be null");
            this.chatOptions = options;
            return this;
        }

        public ChatClient.ChatClientRequestSpec toolNames(String... toolNames) {
            Assert.notNull(toolNames, "toolNames cannot be null");
            Assert.noNullElements(toolNames, "toolNames cannot contain null elements");
            this.toolNames.addAll(List.of(toolNames));
            return this;
        }

        public ChatClient.ChatClientRequestSpec toolCallbacks(ToolCallback... toolCallbacks) {
            Assert.notNull(toolCallbacks, "toolCallbacks cannot be null");
            Assert.noNullElements(toolCallbacks, "toolCallbacks cannot contain null elements");
            this.toolCallbacks.addAll(List.of(toolCallbacks));
            return this;
        }

        public ChatClient.ChatClientRequestSpec toolCallbacks(List<ToolCallback> toolCallbacks) {
            Assert.notNull(toolCallbacks, "toolCallbacks cannot be null");
            Assert.noNullElements(toolCallbacks, "toolCallbacks cannot contain null elements");
            this.toolCallbacks.addAll(toolCallbacks);
            return this;
        }

        public ChatClient.ChatClientRequestSpec tools(Object... toolObjects) {
            Assert.notNull(toolObjects, "toolObjects cannot be null");
            Assert.noNullElements(toolObjects, "toolObjects cannot contain null elements");
            this.toolCallbacks.addAll(Arrays.asList(ToolCallbacks.from(toolObjects)));
            return this;
        }

        public ChatClient.ChatClientRequestSpec toolCallbacks(ToolCallbackProvider... toolCallbackProviders) {
            Assert.notNull(toolCallbackProviders, "toolCallbackProviders cannot be null");
            Assert.noNullElements(toolCallbackProviders, "toolCallbackProviders cannot contain null elements");

            for(ToolCallbackProvider toolCallbackProvider : toolCallbackProviders) {
                this.toolCallbacks.addAll(List.of(toolCallbackProvider.getToolCallbacks()));
            }

            return this;
        }

        public ChatClient.ChatClientRequestSpec toolContext(Map<String, Object> toolContext) {
            Assert.notNull(toolContext, "toolContext cannot be null");
            Assert.noNullElements(toolContext.keySet(), "toolContext keys cannot contain null elements");
            Assert.noNullElements(toolContext.values(), "toolContext values cannot contain null elements");
            this.toolContext.putAll(toolContext);
            return this;
        }

        public ChatClient.ChatClientRequestSpec system(String text) {
            Assert.hasText(text, "text cannot be null or empty");
            this.systemText = text;
            return this;
        }

        public ChatClient.ChatClientRequestSpec system(Resource text, Charset charset) {
            Assert.notNull(text, "text cannot be null");
            Assert.notNull(charset, "charset cannot be null");

            try {
                this.systemText = text.getContentAsString(charset);
                return this;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public ChatClient.ChatClientRequestSpec system(Resource text) {
            Assert.notNull(text, "text cannot be null");
            return this.system(text, Charset.defaultCharset());
        }

        public ChatClient.ChatClientRequestSpec system(Consumer<ChatClient.PromptSystemSpec> consumer) {
            Assert.notNull(consumer, "consumer cannot be null");
            DefaultPromptSystemSpec systemSpec = new DefaultPromptSystemSpec();
            consumer.accept(systemSpec);
            this.systemText = StringUtils.hasText(systemSpec.text()) ? systemSpec.text() : this.systemText;
            this.systemParams.putAll(systemSpec.params());
            return this;
        }

        public ChatClient.ChatClientRequestSpec user(String text) {
            Assert.hasText(text, "text cannot be null or empty");
            this.userText = text;
            return this;
        }

        public ChatClient.ChatClientRequestSpec user(Resource text, Charset charset) {
            Assert.notNull(text, "text cannot be null");
            Assert.notNull(charset, "charset cannot be null");

            try {
                this.userText = text.getContentAsString(charset);
                return this;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public ChatClient.ChatClientRequestSpec user(Resource text) {
            Assert.notNull(text, "text cannot be null");
            return this.user(text, Charset.defaultCharset());
        }

        public ChatClient.ChatClientRequestSpec user(Consumer<ChatClient.PromptUserSpec> consumer) {
            Assert.notNull(consumer, "consumer cannot be null");
            DefaultPromptUserSpec us = new DefaultPromptUserSpec();
            consumer.accept(us);
            this.userText = StringUtils.hasText(us.text()) ? us.text() : this.userText;
            this.userParams.putAll(us.params());
            this.media.addAll(us.media());
            return this;
        }

        public ChatClient.ChatClientRequestSpec templateRenderer(TemplateRenderer templateRenderer) {
            Assert.notNull(templateRenderer, "templateRenderer cannot be null");
            this.templateRenderer = templateRenderer;
            return this;
        }

        public ChatClient.CallResponseSpec call() {
            BaseAdvisorChain advisorChain = this.buildAdvisorChain();
            return new DefaultCallResponseSpec(DefaultChatClientUtils.toChatClientRequest(this), advisorChain, this.observationRegistry, this.observationConvention);
        }

        public ChatClient.StreamResponseSpec stream() {
            BaseAdvisorChain advisorChain = this.buildAdvisorChain();
            return new DefaultStreamResponseSpec(DefaultChatClientUtils.toChatClientRequest(this), advisorChain, this.observationRegistry, this.observationConvention);
        }

        private BaseAdvisorChain buildAdvisorChain() {
            this.advisors.add(ChatModelCallAdvisor.builder().chatModel(this.chatModel).build());
            this.advisors.add(ChatModelStreamAdvisor.builder().chatModel(this.chatModel).build());
            return DefaultAroundAdvisorChain.builder(this.observationRegistry).pushAll(this.advisors).templateRenderer(this.templateRenderer).build();
        }
    }
}

DefaultChatClientUtils

类作用:用来将 DefaultChatClient.DefaultChatClientRequestSpec 转换为 ChatClientRequest

  1. 处理系统提示
  2. 处理用户提示
  3. 处理工具调用选项
package org.springframework.ai.chat.client;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

final class DefaultChatClientUtils {
    private DefaultChatClientUtils() {
    }

    static ChatClientRequest toChatClientRequest(DefaultChatClient.DefaultChatClientRequestSpec inputRequest) {
        Assert.notNull(inputRequest, "inputRequest cannot be null");
        List<Message> processedMessages = new ArrayList();
        String processedSystemText = inputRequest.getSystemText();
        if (StringUtils.hasText(processedSystemText)) {
            if (!CollectionUtils.isEmpty(inputRequest.getSystemParams())) {
                processedSystemText = PromptTemplate.builder().template(processedSystemText).variables(inputRequest.getSystemParams()).renderer(inputRequest.getTemplateRenderer()).build().render();
            }

            processedMessages.add(new SystemMessage(processedSystemText));
        }

        if (!CollectionUtils.isEmpty(inputRequest.getMessages())) {
            processedMessages.addAll(inputRequest.getMessages());
        }

        String processedUserText = inputRequest.getUserText();
        if (StringUtils.hasText(processedUserText)) {
            if (!CollectionUtils.isEmpty(inputRequest.getUserParams())) {
                processedUserText = PromptTemplate.builder().template(processedUserText).variables(inputRequest.getUserParams()).renderer(inputRequest.getTemplateRenderer()).build().render();
            }

            processedMessages.add(UserMessage.builder().text(processedUserText).media(inputRequest.getMedia()).build());
        }

        ChatOptions processedChatOptions = inputRequest.getChatOptions();
        if (processedChatOptions instanceof ToolCallingChatOptions toolCallingChatOptions) {
            if (!inputRequest.getToolNames().isEmpty()) {
                Set<String> toolNames = ToolCallingChatOptions.mergeToolNames(new HashSet(inputRequest.getToolNames()), toolCallingChatOptions.getToolNames());
                toolCallingChatOptions.setToolNames(toolNames);
            }

            if (!inputRequest.getToolCallbacks().isEmpty()) {
                List<ToolCallback> toolCallbacks = ToolCallingChatOptions.mergeToolCallbacks(inputRequest.getToolCallbacks(), toolCallingChatOptions.getToolCallbacks());
                ToolCallingChatOptions.validateToolCallbacks(toolCallbacks);
                toolCallingChatOptions.setToolCallbacks(toolCallbacks);
            }

            if (!CollectionUtils.isEmpty(inputRequest.getToolContext())) {
                Map<String, Object> toolContext = ToolCallingChatOptions.mergeToolContext(inputRequest.getToolContext(), toolCallingChatOptions.getToolContext());
                toolCallingChatOptions.setToolContext(toolContext);
            }
        }

        return ChatClientRequest.builder().prompt(Prompt.builder().messages(processedMessages).chatOptions(processedChatOptions).build()).context(new ConcurrentHashMap(inputRequest.getAdvisorParams())).build();
    }
}

AdvisorChain

AdvisorChain 链调用一系列的增强器 Advisor 基础,每个增强器输入是 ChatClientRequest,输出 ChatClientResponse(其中必定会用到的是 ChatModelCallAdvisor 或 ChatModelStreamAdvisor)

  • ChatModelCallAdvisor 触发 ChatModel 的 call 方法
  • ChatModelStreamAdvisor 触发 ChatModel 的 stream 方法

ChatModel

package org.springframework.ai.chat.model;

import java.util.Arrays;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.Model;
import reactor.core.publisher.Flux;

public interface ChatModel extends Model<Prompt, ChatResponse>, StreamingChatModel {
    default String call(String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        Generation generation = this.call(prompt).getResult();
        return generation != null ? generation.getOutput().getText() : "";
    }

    default String call(Message... messages) {
        Prompt prompt = new Prompt(Arrays.asList(messages));
        Generation generation = this.call(prompt).getResult();
        return generation != null ? generation.getOutput().getText() : "";
    }

    ChatResponse call(Prompt prompt);

    default ChatOptions getDefaultOptions() {
        return ChatOptions.builder().build();
    }

    default Flux<ChatResponse> stream(Prompt prompt) {
        throw new UnsupportedOperationException("streaming is not supported");
    }
}

不同厂商实现各种的 ChaModel,但实现逻辑基本以 OpenAI 作为官方实现

pom 引入对应依赖

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-model-openai</artifactId>
</dependency>

OpenAiChatModel

各字段说明

字段名
类型
描述
defaultOptions
OpenAiChatOptions
请求参数配置,如temperature、最大 token 数等
retryTemplate
RetryTemplate
用于执行重试逻辑,适用于网络不稳定或 API 限流等
openAiApi
OpenAiApi
封装OpenAI官方API的调用接口
observationRegistry
ObservationRegistry
用于注册和记录观测日志,便于监控和分析调用过程
toolCallingManager
ToolCallingManager
工具调用管理器,用于解析并执行工具调
toolExecutionEligibilityPredicate
ToolExecutionEligibilityPredicate
判断是否需要执行工具调用的断言函数
observationConvention
ChatModelObservationConvention
自定义观测日志格式的约定对象

对外暴露的方法

方法名
描述
call
发起一次同步请求,返回完整的 ChatResponse,实际调用内部的internalCall方法
internalCall
1. 构建OpenAI请求对象
2. 创建观测上下文
3. 执行带观测的模型调用
4. 执行OpenAI接口调用
5. 解析模型返回的choices
6. 将每个choice转换为Generation对象,构建完整的
7. 提取限流信息(RateLimit)
8. 计算token使用量
9. 构建最终的ChatResponse并设置上下文
10. 工具调用处理
stream
发起一次流式请求,返回Flux,实际调用内部的internalStream方法
internalStream
1. 使用Flux.deferContextual延迟执行,保持上下文一致性
2. 构建OpenAI流式请求对象
3. 发起流式 API 调用,获取 chunk 数据
4. 创建角色映射表,解决 chunk 中 role 缺失问题
5. 创建观测上下文
6. 启动观测操作
7. 将 chunk 转换为 ChatCompletion 标准格式
8. 转换为 ChatResponse 并构建生成内
9. 处理 usage 字段(仅最终 chunk 包含完整 usage)
10. 工具调用处理
11. 聚合消息流并设置响应
getDefaultOptions
回当前模型使用的默认请求参数,OpenAiChatOptions
setObservationConvention
设置自定义的观测日志格式化规则
mutate
复制OpenAiChatModel实例

OpenAiApi

各字段说明

字段名
类型
描述
baseUrl
String
OpenAI API 的基础 URL,默认为 "https://api.openai.com"
apiKey
ApiKey
认证密钥
headers
MultiValueMap
自定义 HTTP 请求头,例如用户自定义的身份信息等
completionsPath
String
Chat Completion 接口路径,默认为 /v1/chat/completions
embeddingsPath
String
Embedding 接口路径,默认为 /v1/embeddings
responseErrorHandler
ResponseErrorHandler
响应错误处理器,默认处理异常逻辑
restClient
RestClient
同步请求客户端,用于非流式请求
webClient
WebClient
异步/响应式请求客户端,用于流式请求
chunkMerger
OpenAiStreamFunctionCallingHelper
流式函数调用合并器,用于处理多个 chunk 中的 functioncall 数据

对外暴露的方法

方法名
描述
chatCompletionEntity
发送同步请求获取完整的 Chat Completion 响应
chatCompletionStream
发起流式请求,接收分块响应(chunk)
embeddings
调用 OpenAI Embedding 接口,生成文本或 token 数组的向量表示

内部枚举类说明

枚举类
描述
ChatModel
支持的聊天模型
ChatCompletionFinishReason
模型停止生成的原因
EmbeddingModel
支持的Embedding模型
OutputModality
模型输出的范式

完整代码如下