【Spring AI 开发指南】ChatClient 基础、原理与实战案例

Spring AI 开发指南:ChatClient 基础、原理与实战案例


ChatClient是 Spring AI 与大语言模型交互的统一入口,通过流式 DSL 设计,兼顾简洁性与扩展性,支持多模型、可观测性、模板、历史上下文、结构化输出等高级特性,适用于从简单问答到复杂 Agent 系统的各类场景。

ChatClient 是一个用于与大语言模型通信的 API 接口,支持同步和流式编程模型,同时它也是 Spring AI 一切的起源。总体通过 ChatClientRequestSpecCallResponseSpecStreamResponseSpec 三个子接口定义来串联构造一个完整的聊天客户端。
returns
returns
returns
returns
uses via Consumer
uses via Consumer
uses via Consumer
builds
<<interface>>
ChatClient
+static create(ChatModel, ObservationRegistry, ChatClientObservationConvention, AdvisorObservationConvention) : : ChatClient
+static builder(ChatModel, ObservationRegistry, ChatClientObservationConvention, AdvisorObservationConvention) : : Builder
+prompt() : : ChatClientRequestSpec
+prompt(String) : : ChatClientRequestSpec
+prompt(Prompt) : : ChatClientRequestSpec
+mutate() : : Builder
<<interface>>
ChatClientRequestSpec
+mutate() : : Builder
+system(String) : : ChatClientRequestSpec
+system(Resource, Charset) : : ChatClientRequestSpec
+system(Consumer<PromptSystemSpec>) : : ChatClientRequestSpec
+user(String) : : ChatClientRequestSpec
+user(Consumer<PromptUserSpec>) : : ChatClientRequestSpec
+messages(Message[]) : : ChatClientRequestSpec
+advisors(Advisor[]) : : ChatClientRequestSpec
+tools(Object[]) : : ChatClientRequestSpec
+toolCallbacks(ToolCallback[]) : : ChatClientRequestSpec
+options(ChatOptions) : : ChatClientRequestSpec
+templateRenderer(TemplateRenderer) : : ChatClientRequestSpec
+call() : : CallResponseSpec
+stream() : : StreamResponseSpec
<<interface>>
CallResponseSpec
+entity(Class<T>) : : T
+entity(ParameterizedTypeReference<T>) : : T
+entity(StructuredOutputConverter<T>) : : T
+content() : : String
+chatResponse() : : ChatResponse
+chatClientResponse() : : ChatClientResponse
+responseEntity(Class<T>) : : ResponseEntity<ChatResponse, T>
<<interface>>
StreamResponseSpec
+content() : : Flux<String>
+chatResponse() : : Flux<ChatResponse>
+chatClientResponse() : : Flux<ChatClientResponse>
<<interface>>
PromptUserSpec
+text(String) : : PromptUserSpec
+text(Resource, Charset) : : PromptUserSpec
+param(String, Object) : : PromptUserSpec
+params(Map<String, Object>) : : PromptUserSpec
+media(MimeType, URL) : : PromptUserSpec
+media(Media[]) : : PromptUserSpec
+metadata(String, Object) : : PromptUserSpec
<<interface>>
PromptSystemSpec
+text(String) : : PromptSystemSpec
+text(Resource, Charset) : : PromptSystemSpec
+param(String, Object) : : PromptSystemSpec
+metadata(String, Object) : : PromptSystemSpec
<<interface>>
AdvisorSpec
+advisors(Advisor[]) : : AdvisorSpec
+params(Map<String, Object>) : : AdvisorSpec
+param(String, Object) : : AdvisorSpec
<<interface>>
Builder
+defaultSystem(String) : : Builder
+defaultUser(String) : : Builder
+defaultTools(Object[]) : : Builder
+defaultToolCallbacks(ToolCallback[]) : : Builder
+defaultAdvisors(Advisor[]) : : Builder
+defaultTemplateRenderer(TemplateRenderer) : : Builder
+clone() : : Builder
+build() : : ChatClient

1. 快速开始

Spring Boot 提供自动配置 Spring AI ChatClient,通过 ChatClient.Builder 装配一个开箱即用的 ChatClient 实例,如下代码所示,注入 ChatClient 后通过 user 方法设置用户消息内容,然后通过 call 方法向大语言模型发送请求,最后使用 content 方法将大语言模型的响应以字符串的形式返回。

java 复制代码
@RestController
@RequestMapping("/chat/client")
public class ChatClientController {

   private final ChatClient chatClient;

   public ChatClientController(ChatClient.Builder chatClientBuilder) {
      this.chatClient = chatClientBuilder.build();
   }

   @GetMapping("/call")
   public String call() {
      return chatClient
           .prompt()
           .user("我是提示词")
           .call()
           .content();
   }

}

请求到响应的流程如下所示:
用户代码: chatClient.prompt()
构造 Prompt: system/user/messages
模板渲染: 变量替换
Advisor 拦截: before()
调用 ChatModel: 生成响应
Advisor 拦截: after()
响应处理: content/entity/chatResponse
返回结果

2. 核心方法

ChatClient 接口有三类重要的方法定义,第一通过定义 createbuilder 两个静态方法来引导初始构建对象,第二通过定义三个重载的 prompt 来引导完成构建对象,第三通过定义一个 mutate 方法引导基于当前配置创建新对象(实现配置的复用与修改)。
<<interface>>
ChatClient
+static create(ChatModel, ObservationRegistry, ChatClientObservationConvention, AdvisorObservationConvention) : : ChatClient
+static builder(ChatModel, ObservationRegistry, ChatClientObservationConvention, AdvisorObservationConvention) : : Builder
+prompt() : : ChatClientRequestSpec
+prompt(String) : : ChatClientRequestSpec
+prompt(Prompt) : : ChatClientRequestSpec
+mutate() : : Builder

2.1 构造

ChatClient 接口提供 createbuilder 两种构造方式,从源码来看 create 最终也是调到了 builder 方法,最终 builder 通过 org.springframework.ai.chat.client.DefaultChatClientBuilder 构造一个 org.springframework.ai.chat.client.DefaultChatClient 对象提供服务。

java 复制代码
static ChatClient create(ChatModel chatModel, ObservationRegistry observationRegistry,
      @Nullable ChatClientObservationConvention chatClientObservationConvention,
      @Nullable AdvisorObservationConvention advisorObservationConvention) {
   Assert.notNull(chatModel, "chatModel cannot be null");
   Assert.notNull(observationRegistry, "observationRegistry cannot be null");
   return builder(chatModel, observationRegistry, chatClientObservationConvention, advisorObservationConvention)
      .build();
}

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

默认情况下 Spring AI 会通过 ChatClient.Builder 自动配置一个 ChatClient 以供使用,但是我们可以通过配置属性 spring.ai.chat.client.enabled=false 来禁用 ChatClient.Builder 自动配置。

2.1.1 不同模型类型的 ChatClient

我们对模型的需求是多样性的,对不同类型的任务使用不同的模型(例如,对于复杂推理使用强大的模型,对于简单任务使用更快、更便宜的模型),所以就存在装配不同模型类型 ChatClient 的需求。

java 复制代码
@Configuration
public class ChatClientConfig {

    @Bean
    public ChatClient openAiChatClient(OpenAiChatModel chatModel) {
        return ChatClient.create(chatModel);
    }

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

也可以在使用的时候根据当时需求通过 create 方法构建 ChatClient

java 复制代码
@RestController
@RequestMapping("/chat/client")
public class ChatClientController {

   @Autowired
   private ChatModel chatModel;

   @GetMapping("/call")
   public String call() {
      ChatClient chatClient = ChatClient.create(chatModel);
      return chatClient
           .prompt()
           .user("我是提示词")
           .call()
           .content();
   }

}

2.1.2 可观测性

ChatClient 提供的构造方法中可以传入 ObservationRegistryChatClientObservationConvention 这两个对象的属性,通过对象名称不难看出这两个都是辅助用于可观测性的配置属性。从 spring ai 依赖管理中可以看到引入了 Micrometer 组件,所以上述两个属性配置都是用于集成 Micrometer 的观测(Observation)机制。

ObservationRegistry

  • 作用:全局的观测注册表,是 Micrometer Observation 的核心组件。
  • 职责:
    1. 管理所有 Observation 实例的生命周期。
    2. 将观测数据(如指标、日志、追踪 span)发送给后端(如 Prometheus、Zipkin、OpenTelemetry)。
    3. 决定是否启用观测、应用哪些 ObservationConvention。
  • 如果不提供 ChatClient 会使用 ObservationRegistry.NOOP(即不记录任何观测数据)。

ChatClientObservationConvention

  • 作用:定义 ChatClient 执行过程中生成的命名、标签(low-cardinality tags)等行为的约定。
  • 职责:
    1. 自定义观测名称,实现 getName 方法(例如 ai.chatmyapp.llm.call)。
    2. 添加自定义上下文标签,如模型名称、用户 ID、请求类型等。
    3. 控制哪些信息被作为指标标签暴露(需注意避免高基数标签 getHighCardinalityKeyValues)。
  • 如果不提供,使用默认约定(DefaultChatClientObservationConvention)。
属性 作用 控制什么
ObservationRegistry 观测的运行时注册中心 是否记录、如何导出数据
ChatClientObservationConvention 观测的命名与标签约定 观测叫什么、带什么标签
是否必需 否(默认 NOOP) 否(默认 Default)
类比 日志系统(如 Logback) 日志格式模板(如 logback.xml)

示例

  1. 自定义 ChatClientObservationConvention
java 复制代码
public class CustomChatObservationConvention implements ChatClientObservationConvention {

    @Override
    public String getName() {
        return "ai.demo.llm.chat";
    }

    @Override
    public String getContextualName(ChatClientObservationContext context) {
        return "Call to LLM: " + context.getName();
    }

    @Override
    public KeyValues getLowCardinalityKeyValues(ChatClientObservationContext context) {
        return context.getAllKeyValues();
    }

    @Override
    public boolean supportsContext(Observation.Context context) {
        return ChatClientObservationConvention.super.supportsContext(context);
    }
}
  1. 引入 Observation
    可以通过 ChatClient#create 或者 ChatClent#builder 方法引入自定义的 Observation,同时可以使用 spring 自动装配的 ObservationRegistry 无需手动构造。
java 复制代码
@RestController
@RequestMapping("/chat/client")
public class ChatClientController {

   @Autowired
   private ChatModel chatModel;
   
   @Autowired
   private ObservationRegistry observationRegistry;

   @GetMapping("/call")
   public String call() {
      ChatClient chatClient = ChatClient.create(
             chatModel, observationRegistry, new CustomChatObservationConvention(), null);
      return chatClient
           .prompt()
           .user("我是提示词")
           .call()
           .content();
   }

}
  1. 启用 Micrometer 日志
yaml 复制代码
logging:
  level:
    io.micrometer.observation: TRACE

2.2 提示引导

ChatClient 提供无参、字符串 prompt 入参和 Prompt 对象入参三种重载的 prompt 方法创建提示,并返回 ChatClientRequestSpec 接口提供配置其它的属性,也可以直接再通过 call()stream() 执行。

  1. prompt(): 此无参数方法直接返回 ChatClientRequestSpec,使其进一步构建用户、系统和提示的其他部分。
  2. prompt(Prompt prompt): 此方法接受一个 org.springframework.ai.chat.prompt.Prompt 对象,可以通过个性化的方法创建一个 Prompt 对象来给 ChatClient 提供用户或者助手聊天记录、提示词,以及进一步控制大语言模型的温度等参数。
  3. prompt(String content): 提供一个简单字符串方法,其底层直接调用了 Prompt 类的构造器 new Prompt(content),直接将用户的文本内容传输给大语言模型。
java 复制代码
@RestController
@RequestMapping("/chat/client")
public class ChatClientController {

   @Autowired
   private ChatModel chatModel;

   @GetMapping("/call")
   public String call() {
      ChatClient chatClient = ChatClient.create(chatModel);
      
      // 1、prompt()
      String message = chatClient
           .prompt()
           .user("我是提示词")
           .call()
           .content();
           
      // 2、prompt(String prompt)
      String message = chatClient
           .prompt("我是提示词")
           .call()
           .content();
           
      // 3、prompt(Prompt prompt)
      ChatOptions chatOptions = ChatOptions.builder()
                  .temperature(0.9)
                  .maxTokens(1024)
                  .build();
      Prompt prompt = new Prompt("我是提示词", chatOptions);            
      String message = chatClient
           .prompt(prompt)
           .call()
           .content();
   }

}

2.3 变体

ChatClient 提供 mutate 方法,该方法定义返回一个 Builder,所以实现允许基于当前配置创建新实例,进而达到配置的复用与修改目的。mutate对比builder有如下差异:

  • builder:从零构建全新 ChatClient(如切换底层模型);
  • mutate:基于现有 ChatClient微调配置(如仅修改温度参数)。
java 复制代码
@RestController
@RequestMapping("/chat/client")
public class ChatClientController {

   @Autowired
   private ChatModel chatModel;

   @GetMapping("/call")
   public String call() {
      // 构建 ChatClient
      ChatClient chatClient = ChatClient.builder(chatModel).build();
      // 定义大语言模型参数配置
      ChatOptions chatOptions = ChatOptions.builder()
                      .temperature(0.9).maxTokens(1024).build();
      // 基于 chatClient,追加大语言模型参数配置,生成新对象
      ChatClient chatClientByOptions = chatClient
                      .mutate().defaultOptions(chatOptions).build();
      return chatClientByOptions.prompt("这是提示词").call().content();
   }

}

3. 请求构造器

ChatClient 无论通过哪种 prompt 方法都会返回一个 ChatClientRequestSpec 接口来完成一个 ChatClient 的请求构造。使用 ChatClientRequestSpec 可以进一步构造系统提示词、用户提示词、历史聊天记录、工具相关、大语言模型参数、Advisor 拦截和模板引擎等。
<<interface>>
ChatClientRequestSpec
+mutate() : : Builder
+system(String) : : ChatClientRequestSpec
+system(Resource, Charset) : : ChatClientRequestSpec
+system(Consumer<PromptSystemSpec>) : : ChatClientRequestSpec
+user(String) : : ChatClientRequestSpec
+user(Consumer<PromptUserSpec>) : : ChatClientRequestSpec
+messages(Message[]) : : ChatClientRequestSpec
+advisors(Advisor[]) : : ChatClientRequestSpec
+tools(Object[]) : : ChatClientRequestSpec
+toolCallbacks(ToolCallback[]) : : ChatClientRequestSpec
+options(ChatOptions) : : ChatClientRequestSpec
+templateRenderer(TemplateRenderer) : : ChatClientRequestSpec
+call() : : CallResponseSpec
+stream() : : StreamResponseSpec

3.1 提示词

3.1.1 基础提示配置(system/user)

ChatClientRequestSpec 接口提供了 systemuser 两个配置提示词的入口,并对其重载了多个方法实现以应对不同配置提示词的需求。

java 复制代码
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);

从源码定义来看 systemuser 的前三个方法都是一样的参数定义,只有最后一个略有不同,system 方法提供的是 PromptSystemSpec 接口入参,user 方法则提供的是 PromptUserSpec 接口入参。从前三个方法来看都是直接通过字符串的形式来配置一个提示,如下:

java 复制代码
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("");

chatClient.prompt().system("我是提示词").call().content();
chatClient.prompt().system(resource).call().content();
chatClient.prompt().system(resource, Charset.defaultCharset()).call().content();

chatClient.prompt().user("我是提示词").call().content();
chatClient.prompt().user(resource).call().content();
chatClient.prompt().user(resource, Charset.defaultCharset()).call().content();

3.1.2 高级提示定制(PromptSystemSpec/PromptUserSpec)

PromptSystemSpecPromptUserSpec 这两个接口则可以实现各个性化的配置需求,其都提供了通用的提示配置、参数填充配置和元数据配置,详情可以查看源码。

java 复制代码
String content = chatClient.prompt().user(u -> {
            u.text("你用{style}风格,写一篇300字的童话故事");
            u.param("style", "天真浪漫");
        }).call().content();

3.2 模板引擎

ChatClient 允许将用户和系统文本作为模板提供,其中变量在运行时被替换。其使用 PromptTemplate 类处理用户和系统文本,并根据给定的 TemplateRenderer 实现,在运行时用提供的值替换变量。默认情况下,Spring AI 使用 StTemplateRenderer 实现,该实现基于 Terence Parr 开发的开源 StringTemplate 引擎。同时 Spring AI 还提供了一个 NoOpTemplateRenderer,用于不需要模板处理的情况。

java 复制代码
String content = chatClient.prompt().user(u -> {
            u.text("你用{style}风格,写一篇300字的童话故事");
            u.param("style", "天真浪漫");
        }).call().content();

如果想要使用自己个性化的方式来处理变量,可以通过实现 org.springframework.ai.template.TemplateRenderer 接口实现自定义的模板引擎,并在构造 ChatClient 的时候通过 ChatClientRequestSpec 接口提供的 templateRenderer 方法注入。

同时你也可以基于现有的 StTemplateRenderer 实现来自定义完成自定义配置。例如,默认情况下,模板变量由 {} 语法标识。如果您打算在提示中包含 JSON,您可能需要使用不同的语法来避免与 JSON 语法冲突。那么就可以使用 <> 分隔符。

java 复制代码
String content = chatClient.prompt()
        .user(u -> {
            u.text("你用{style}风格,写一篇300字的童话故事");
            u.param("style", "天真浪漫");})
        .templateRenderer(StTemplateRenderer.builder()
            .startDelimiterToken('<')
            .endDelimiterToken('>')
            .build())
        .call()
        .content();

3.3 历史消息

ChatClientRequestSpec 接口除了提供 systemuser 方法来传递提示以外,还提供了 message 方法来配置各类想要传递给大语言模型的消息,从 org.springframework.ai.chat.messages.Message 接口实现来看可以传递用户历史消息、助手历史消息、工具返回消息和系统消息等,各类实现来看已经支持了各种想要传递的消息类型且支持批量消息。

java 复制代码
ChatClientRequestSpec messages(Message... messages);
ChatClientRequestSpec messages(List<Message> messages);

4. 响应处理

ChatClient 提供阻塞式和流式两种应答响应模式分别通过 ChatClientRequestSpec 接口的 call 方法和 stream 方法来引导,阻塞式响应返回 CallResponseSpec,流式响应返回 StreamResponseSpec 如下代码所示。

java 复制代码
CallResponseSpec call();
StreamResponseSpec stream();

4.1 阻塞式响应

在 ChatClient 上指定 call() 方法后响应的 CallResponseSpec 接口的定义。调用 call 方法实际上不会触发大语言模型执行,它只是告诉 Spring AI 使用阻塞式的方式调用。实际的大语言模型调用发生在 contentchatResponseresponseEntity 方法被调用时。

  1. String content(): 直接以字符串的形式返回大语言模型响应的内容。
  2. ChatResponse chatResponse(): 返回 ChatResponse 对象,该对象包含多个生成以及有关响应的元数据,例如用于创建响应的令牌数量。
  3. ChatClientResponse chatClientResponse(): 返回一个 ChatClientResponse 对象,该对象包含 ChatResponse 对象和 ChatClient 执行上下文,使您可以访问 Advisor 执行期间使用的额外数据(例如,RAG 流中检索到的相关文档)。
  4. entity() 返回 Java 类型
    • entity(ParameterizedTypeReference<T> type): 用于返回实体类型的 Collection。
    • entity(Class<T> type): 用于返回特定实体类型。
    • entity(StructuredOutputConverter<T> structuredOutputConverter): 用于指定 StructuredOutputConverter 的实例,将 String 转换为实体类型。
  5. responseEntity() 返回 ChatResponse 和 Java 类型。当您需要在一次调用中同时访问完整的 AI 模型响应(带元数据和生成)和结构化输出实体时,这非常有用。
    • responseEntity(Class<T> type): 用于返回包含完整 ChatResponse 对象和特定实体类型的 ResponseEntity。
    • responseEntity(ParameterizedTypeReference<T> type): 用于返回包含完整 ChatResponse 对象和实体类型 Collection 的 ResponseEntity。
    • responseEntity(StructuredOutputConverter<T> structuredOutputConverter): 用于返回包含完整 ChatResponse 对象和使用指定 StructuredOutputConverter 转换的实体的 ResponseEntity。

4.2 流式响应

在 ChatClient 上指定 stream() 方法后响应的 CallResponseSpec 接口的定义

  1. Flux<String> content(): 返回 AI 模型正在生成的字符串的 Flux。
  2. Flux<ChatResponse> chatResponse(): 返回 ChatResponse 对象的 Flux,该对象包含有关响应的其他元数据。
  3. Flux<ChatClientResponse> chatClientResponse(): 返回 ChatClientResponse 对象的 Flux,该对象包含 ChatResponse 对象和 ChatClient 执行上下文,使您可以访问 Advisor 执行期间使用的额外数据(例如,RAG 流中检索到的相关文档)。

5. 消息元数据

我们在 PromptSystemSpec 构造系统提示和 PromptUserSpec 构造用户提示这两个接口的定义中都看见了 metadata 的影子,甚至在 ChatResponse 响应接口中也看到其影子。这里的 metadata 元数据是 ChatClient 为了支持添加到用户和系统消息中,从而实现提供额外的上下文和消息,进而交给大语言模型或者响应处理。

用户提示添加元数据

java 复制代码
Map<String, Object> userMetadata = Map.of(
    "messageId", "msg-123",
    "userId", "user-456",
    "timestamp", System.currentTimeMillis()
);

String response = chatClient.prompt()
    .user(u -> u.text("我是提示词")
        .metadata(userMetadata))
    .call()
    .content();

系统提示添加元数据

java 复制代码
String response = chatClient.prompt()
    .system(s -> s.text("我是系统提示词")
        .metadata("version", "1.0")
        .metadata("model", "gpt-4"))
    .user("我是用户提示词")
    .call()
    .content();

使用元数据

元数据信息放在生成的 UserMessageSystemMessage 对象中,可以通过消息的 getMetadata 方法访问。可用在 Advisor 中以实现各种前后拦截。

java 复制代码
@Component
public class DemoAdvisor implements BaseAdvisor {

    @Override
    public int getOrder() {
        return 0; 
    }

    @Override
    public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
        // 读取用户提示添加的元数据信息
        chatClientRequest.prompt().getUserMessage().getMetadata();
        return chatClientRequest;
    }

    @Override
    public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
        return chatClientResponse;
    }
}

一键三连,让我的信心像气球一样膨胀!

相关推荐
极客小云2 小时前
【突发公共事件智能分析新范式:基于PERSIA框架与大模型的知识图谱构建实践】
大数据·人工智能·知识图谱
Fuly10242 小时前
如何评估LLM和Agent质量
人工智能
weisian1512 小时前
入门篇--知名企业-12-Stability AI:不止于“艺术”,这是一场开源AI的全面起义
人工智能·开源·stablility ai
五月君_3 小时前
Nuxt UI v4.3 发布:原生 AI 富文本编辑器来了,Vue 生态又添一员猛将!
前端·javascript·vue.js·人工智能·ui
wjykp3 小时前
109~111集成学习
人工智能·机器学习·集成学习
小程故事多_803 小时前
Spring AI 赋能 Java,Spring Boot 快速落地 LLM 的企业级解决方案
java·人工智能·spring·架构·aigc
努力的小雨3 小时前
从“Agent 元年”到 AI IDE 元年——2025 我与 Vibe Coding 的那些事儿
后端·程序员
xcLeigh3 小时前
AI的提示词专栏:写作助手 Prompt,从提纲到完整文章
人工智能·ai·prompt·提示词
QYR_113 小时前
热塑性复合树脂市场报告:行业现状、增长动力与未来机遇
大数据·人工智能·物联网