本篇文章是关于LangChain4j框架以及 SpringBoot的入门指导材料,它的目的是帮助读者了解并初步掌握这两种技术的使用方法和基本概念,以便能够在相关的开发项目中运用它们。
LangChain4j
LangChain4j 框架是一个开源库,用于在我们的 Java 应用程序中集成大型语言模型。它的灵感来自于在 Python生态系统中很受欢迎的 LangChain,以实现简化的开发流程和 API。
你可以在其官方langchain4j-Github页面上查看Langchain4j 的功能和其他理论概念。 本SpringBoot教程旨在介绍Langchain4j Chat API,通过运行一些示例让你对其有理解。
Langchain4j API
LangChain4j 是围绕几个核心类 / 接口构建的,旨在处理与大语言模型交互的不同方面。
语言模型
语言模型是核心 API,它提供了与大语言模型交互、发送提示以及接收和解析其响应的方法。Langchain4J 提供了以下接口以与不同类型的大语言模型进行交互。
语言模型 | 描述备注 | LLM案例 |
---|---|---|
ChatLanguageModel | 表示一种具有聊天界面的语言模型,如 ChatGPT-3.5。它的方法接受一个或多个 ChatMessage 消息,并返回由该模型生成的 AiMessage。 | GPT-4 |
StreamingChatLanguageModel | 代表一种具有聊天界面的语言模型,该模型能够一次一个标记地流式传输响应。它允许在模型处理输入时实时接收响应。详细来说,首先提到这种语言模型有一个聊天界面,意味着可以通过这个界面与用户进行交互。"能够一次一个标记地流式传输响应",说明在生成回答的过程中,不是一次性给出完整的回答,而是逐个标记地逐步输出响应,这样可以让用户更快地看到部分结果。"允许在模型处理输入时实时接收响应"进一步强调了实时性,即当模型在处理用户输入的同时,用户就可以开始接收响应,而不必等到整个处理过程结束。 | GPT-4 Stream |
EmbeddingModel | 表示一种可以将文本转换为嵌入向量的模型。这些嵌入向量对于语义搜索、聚类和分类任务很有用。 | Ada Embedding |
ImageModel | 代表一种可以使用深度学习模型从文本描述中生成和编辑图像的模型。 | DALL-E |
ModerationModel | 表示一个能够进行分类并过滤文本中是否包含有害内容的模型。这个模型的主要功能是对文本进行分类,判断文本中是否存在有害内容,并且可以根据判断结果进行相应的过滤操作。例如,在一个社交媒体平台上,可以使用这样的模型来自动检测用户发布的内容是否包含辱骂、暴力、色情等有害信息,以便及时进行处理,维护平台的良好环境 | Moderation |
ScoringModel | 表示一种可用于根据各种标准(如情感、语法和相关性)对文本进行评分的模型。 | DistilBERT (finetuned on SST-2) |
消息类型
一条Chat Message是从模型发送或接收的文本,Langchain4j 支持以下消息类型:
- SystemMessage:大型语言模型在此次对话中的角色、应如何表现、以何种风格回答等的指令。
- UserMessage:表示来自用户的消息,通常以文本和图像的形式包含用户查询。
- AiMessage:表示由人工智能生成的消息,作为对 UserMessage 的回应。它可以包含纯文本回复,也可以包含执行工具的请求(ToolExecutionRequest,通常是一个用于获取实时数据的 API,如货币汇率、天气数据等)。
- ToolExecutionResultMessage:表示 ToolExecutionRequest 的结果。
内存对象
ChatMemory代表聊天对话的记忆(历史)。
在任何对话中,我们都必须将所有先前的消息提供给模型,并确保消息适合模型的上下文窗口。ChatMemory主要作为聊天消息的容器,还提供了诸如逐出策略和持久性等附加功能。
注意,Langchain4j 目前只提供"记忆",而不是"历史"。要维护整个历史记录,我们必须手动进行保存。
ChatMemory接口的主要实现
- MessageWindowChatMemory:滑动窗口,保留 N 个最近的消息,并清除不再适合的较旧消息。
- TokenWindowChatMemory :滑动窗口,保留最近的 N 个消息。
- 它不存储部分消息,因此即使只需要从消息中删除几个标记,也会从内存中完全删除该消息。
设置 API 密钥
在实现大模型调用的时候必须拥有对要通过 Spring AI 访问的大语言模型的 API 访问权限。项目 API 密钥(以前称为用户 API 密钥)是一个唯一的编码字符串,用于识别和验证用户或应用程序。
你可以在以下页面获取你的帐户密钥:platform.openai.com/api-keys。
SpringBoot Configuration配置
首先,第一个逻辑步骤是在应用程序中包含必要的依赖项。可以在 Maven 仓库中找到这些必要的依赖项**mvnrepository.com/artifact/de... 例如,为了与 OpenAI 的大语言模型进行交互,我们需要包含 "langchain4j-open-ai"。
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>0.31.0</version>
</dependency>
如果你正在使用Spring Boot应用程序,你也可以查看启动项目。这样,Spring Boot 的自动配置功能将在幕后创建必要的 bean,你可以直接开始使用它们。
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
<version>0.31.0</version>
</dependency>
当我们包含上述"langchain4j-open-ai-spring-boot-starter "依赖项时,Spring Boot 将自动创建并提供"ChatLanguageModel "和"StreamingChatLanguageModel" bean。
具体取决于 application.properties 文件中可用的属性。
ChatLanguageModel
properties
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
StreamingChatLanguageModel
properties
langchain4j.open-ai.streaming-chat-model.api-key=${OPENAI_API_KEY}
你可以在 AutoConfig.java 文件中找到所有可能自动配置的 bean 及其相关属性。
java
package dev.langchain4j.openai.spring;
import dev.langchain4j.http.client.HttpClientBuilder;
import dev.langchain4j.http.client.spring.restclient.SpringRestClient;
import dev.langchain4j.model.chat.listener.ChatModelListener;
import dev.langchain4j.model.openai.*;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.client.RestClient;
import static dev.langchain4j.openai.spring.Properties.PREFIX;
@AutoConfiguration(after = RestClientAutoConfiguration.class)
@EnableConfigurationProperties(Properties.class)
public class AutoConfig {
private static final String TASK_EXECUTOR_THREAD_NAME_PREFIX = "LangChain4j-OpenAI-";
private static final String CHAT_MODEL_HTTP_CLIENT_BUILDER = "openAiChatModelHttpClientBuilder";
private static final String STREAMING_CHAT_MODEL_HTTP_CLIENT_BUILDER = "openAiStreamingChatModelHttpClientBuilder";
private static final String STREAMING_CHAT_MODEL_TASK_EXECUTOR = "openAiStreamingChatModelTaskExecutor";
private static final String LANGUAGE_MODEL_HTTP_CLIENT_BUILDER = "openAiLanguageModelHttpClientBuilder";
private static final String STREAMING_LANGUAGE_MODEL_HTTP_CLIENT_BUILDER = "openAiStreamingLanguageModelHttpClientBuilder";
private static final String STREAMING_LANGUAGE_MODEL_TASK_EXECUTOR = "openAiStreamingLanguageModelTaskExecutor";
private static final String EMBEDDING_MODEL_HTTP_CLIENT_BUILDER = "openAiEmbeddingModelHttpClientBuilder";
private static final String MODERATION_MODEL_HTTP_CLIENT_BUILDER = "openAiModerationModelHttpClientBuilder";
private static final String IMAGE_MODEL_HTTP_CLIENT_BUILDER = "openAiImageModelHttpClientBuilder";
@Bean
@ConditionalOnProperty(PREFIX + ".chat-model.api-key")
OpenAiChatModel openAiChatModel(
@Qualifier(CHAT_MODEL_HTTP_CLIENT_BUILDER) HttpClientBuilder httpClientBuilder,
Properties properties,
ObjectProvider<ChatModelListener> listeners
) {
ChatModelProperties chatModelProperties = properties.chatModel();
return OpenAiChatModel.builder()
.httpClientBuilder(httpClientBuilder)
.baseUrl(chatModelProperties.baseUrl())
.apiKey(chatModelProperties.apiKey())
.organizationId(chatModelProperties.organizationId())
.projectId(chatModelProperties.projectId())
.modelName(chatModelProperties.modelName())
.temperature(chatModelProperties.temperature())
.topP(chatModelProperties.topP())
.stop(chatModelProperties.stop())
.maxTokens(chatModelProperties.maxTokens())
.maxCompletionTokens(chatModelProperties.maxCompletionTokens())
.presencePenalty(chatModelProperties.presencePenalty())
.frequencyPenalty(chatModelProperties.frequencyPenalty())
.logitBias(chatModelProperties.logitBias())
.responseFormat(chatModelProperties.responseFormat())
.supportedCapabilities(chatModelProperties.supportedCapabilities())
.strictJsonSchema(chatModelProperties.strictJsonSchema())
.seed(chatModelProperties.seed())
.user(chatModelProperties.user())
.strictTools(chatModelProperties.strictTools())
.parallelToolCalls(chatModelProperties.parallelToolCalls())
.store(chatModelProperties.store())
.metadata(chatModelProperties.metadata())
.serviceTier(chatModelProperties.serviceTier())
.defaultRequestParameters(OpenAiChatRequestParameters.builder()
.reasoningEffort(chatModelProperties.reasoningEffort())
.build())
.timeout(chatModelProperties.timeout())
.maxRetries(chatModelProperties.maxRetries())
.logRequests(chatModelProperties.logRequests())
.logResponses(chatModelProperties.logResponses())
.customHeaders(chatModelProperties.customHeaders())
.listeners(listeners.orderedStream().toList())
.build();
}
@Bean(CHAT_MODEL_HTTP_CLIENT_BUILDER)
@ConditionalOnProperty(PREFIX + ".chat-model.api-key")
@ConditionalOnMissingBean(name = CHAT_MODEL_HTTP_CLIENT_BUILDER)
HttpClientBuilder openAiChatModelHttpClientBuilder(ObjectProvider<RestClient.Builder> restClientBuilder) {
return SpringRestClient.builder()
.restClientBuilder(restClientBuilder.getIfAvailable(RestClient::builder))
// executor is not needed for no-streaming OpenAiChatModel
.createDefaultStreamingRequestExecutor(false);
}
@Bean
@ConditionalOnProperty(PREFIX + ".streaming-chat-model.api-key")
OpenAiStreamingChatModel openAiStreamingChatModel(
@Qualifier(STREAMING_CHAT_MODEL_HTTP_CLIENT_BUILDER) HttpClientBuilder httpClientBuilder,
Properties properties,
ObjectProvider<ChatModelListener> listeners
) {
ChatModelProperties chatModelProperties = properties.streamingChatModel();
return OpenAiStreamingChatModel.builder()
.httpClientBuilder(httpClientBuilder)
.baseUrl(chatModelProperties.baseUrl())
.apiKey(chatModelProperties.apiKey())
.organizationId(chatModelProperties.organizationId())
.projectId(chatModelProperties.projectId())
.modelName(chatModelProperties.modelName())
.temperature(chatModelProperties.temperature())
.topP(chatModelProperties.topP())
.stop(chatModelProperties.stop())
.maxTokens(chatModelProperties.maxTokens())
.maxCompletionTokens(chatModelProperties.maxCompletionTokens())
.presencePenalty(chatModelProperties.presencePenalty())
.frequencyPenalty(chatModelProperties.frequencyPenalty())
.logitBias(chatModelProperties.logitBias())
.responseFormat(chatModelProperties.responseFormat())
.seed(chatModelProperties.seed())
.user(chatModelProperties.user())
.strictTools(chatModelProperties.strictTools())
.parallelToolCalls(chatModelProperties.parallelToolCalls())
.store(chatModelProperties.store())
.metadata(chatModelProperties.metadata())
.serviceTier(chatModelProperties.serviceTier())
.defaultRequestParameters(OpenAiChatRequestParameters.builder()
.reasoningEffort(chatModelProperties.reasoningEffort())
.build())
.timeout(chatModelProperties.timeout())
.logRequests(chatModelProperties.logRequests())
.logResponses(chatModelProperties.logResponses())
.customHeaders(chatModelProperties.customHeaders())
.listeners(listeners.orderedStream().toList())
.build();
}
@Bean(STREAMING_CHAT_MODEL_HTTP_CLIENT_BUILDER)
@ConditionalOnProperty(PREFIX + ".streaming-chat-model.api-key")
@ConditionalOnMissingBean(name = STREAMING_CHAT_MODEL_HTTP_CLIENT_BUILDER)
HttpClientBuilder openAiStreamingChatModelHttpClientBuilder(
ObjectProvider<RestClient.Builder> restClientBuilder,
@Qualifier(STREAMING_CHAT_MODEL_TASK_EXECUTOR) AsyncTaskExecutor executor) {
return SpringRestClient.builder()
.restClientBuilder(restClientBuilder.getIfAvailable(RestClient::builder))
.streamingRequestExecutor(executor);
}
@Bean(STREAMING_CHAT_MODEL_TASK_EXECUTOR)
@ConditionalOnProperty(PREFIX + ".streaming-chat-model.api-key")
@ConditionalOnMissingBean(name = STREAMING_CHAT_MODEL_TASK_EXECUTOR)
@ConditionalOnClass(name = "io.micrometer.context.ContextSnapshotFactory")
AsyncTaskExecutor openAiStreamingChatModelTaskExecutorWithContextPropagation() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setThreadNamePrefix(TASK_EXECUTOR_THREAD_NAME_PREFIX);
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
return taskExecutor;
}
@Bean(STREAMING_CHAT_MODEL_TASK_EXECUTOR)
@ConditionalOnProperty(PREFIX + ".streaming-chat-model.api-key")
@ConditionalOnMissingBean(name = STREAMING_CHAT_MODEL_TASK_EXECUTOR)
@ConditionalOnMissingClass("io.micrometer.context.ContextSnapshotFactory")
AsyncTaskExecutor openAiStreamingChatModelTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setThreadNamePrefix(TASK_EXECUTOR_THREAD_NAME_PREFIX);
return taskExecutor;
}
@Bean
@ConditionalOnProperty(PREFIX + ".language-model.api-key")
OpenAiLanguageModel openAiLanguageModel(
@Qualifier(LANGUAGE_MODEL_HTTP_CLIENT_BUILDER) HttpClientBuilder httpClientBuilder,
Properties properties
) {
LanguageModelProperties languageModelProperties = properties.languageModel();
return OpenAiLanguageModel.builder()
.httpClientBuilder(httpClientBuilder)
.baseUrl(languageModelProperties.baseUrl())
.apiKey(languageModelProperties.apiKey())
.organizationId(languageModelProperties.organizationId())
.projectId(languageModelProperties.projectId())
.modelName(languageModelProperties.modelName())
.temperature(languageModelProperties.temperature())
.timeout(languageModelProperties.timeout())
.maxRetries(languageModelProperties.maxRetries())
.logRequests(languageModelProperties.logRequests())
.logResponses(languageModelProperties.logResponses())
.customHeaders(languageModelProperties.customHeaders())
.build();
}
@Bean(LANGUAGE_MODEL_HTTP_CLIENT_BUILDER)
@ConditionalOnProperty(PREFIX + ".language-model.api-key")
@ConditionalOnMissingBean(name = LANGUAGE_MODEL_HTTP_CLIENT_BUILDER)
HttpClientBuilder openAiLanguageModelHttpClientBuilder(ObjectProvider<RestClient.Builder> restClientBuilder) {
return SpringRestClient.builder()
.restClientBuilder(restClientBuilder.getIfAvailable(RestClient::builder))
// executor is not needed for no-streaming OpenAiLanguageModel
.createDefaultStreamingRequestExecutor(false);
}
@Bean
@ConditionalOnProperty(PREFIX + ".streaming-language-model.api-key")
OpenAiStreamingLanguageModel openAiStreamingLanguageModel(
@Qualifier(STREAMING_LANGUAGE_MODEL_HTTP_CLIENT_BUILDER) HttpClientBuilder httpClientBuilder,
Properties properties
) {
LanguageModelProperties languageModelProperties = properties.streamingLanguageModel();
return OpenAiStreamingLanguageModel.builder()
.httpClientBuilder(httpClientBuilder)
.baseUrl(languageModelProperties.baseUrl())
.apiKey(languageModelProperties.apiKey())
.organizationId(languageModelProperties.organizationId())
.projectId(languageModelProperties.projectId())
.modelName(languageModelProperties.modelName())
.temperature(languageModelProperties.temperature())
.timeout(languageModelProperties.timeout())
.logRequests(languageModelProperties.logRequests())
.logResponses(languageModelProperties.logResponses())
.customHeaders(languageModelProperties.customHeaders())
.build();
}
@Bean(STREAMING_LANGUAGE_MODEL_HTTP_CLIENT_BUILDER)
@ConditionalOnProperty(PREFIX + ".streaming-language-model.api-key")
@ConditionalOnMissingBean(name = STREAMING_LANGUAGE_MODEL_HTTP_CLIENT_BUILDER)
HttpClientBuilder openAiStreamingLanguageModelHttpClientBuilder(
@Qualifier(STREAMING_LANGUAGE_MODEL_TASK_EXECUTOR) AsyncTaskExecutor executor,
ObjectProvider<RestClient.Builder> restClientBuilder
) {
return SpringRestClient.builder()
.restClientBuilder(restClientBuilder.getIfAvailable(RestClient::builder))
.streamingRequestExecutor(executor);
}
@Bean(STREAMING_LANGUAGE_MODEL_TASK_EXECUTOR)
@ConditionalOnProperty(PREFIX + ".streaming-language-model.api-key")
@ConditionalOnMissingBean(name = STREAMING_LANGUAGE_MODEL_TASK_EXECUTOR)
@ConditionalOnClass(name = "io.micrometer.context.ContextSnapshotFactory")
AsyncTaskExecutor openAiStreamingLanguageModelTaskExecutorWithContextPropagation() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setThreadNamePrefix(TASK_EXECUTOR_THREAD_NAME_PREFIX);
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
return taskExecutor;
}
@Bean(STREAMING_LANGUAGE_MODEL_TASK_EXECUTOR)
@ConditionalOnProperty(PREFIX + ".streaming-language-model.api-key")
@ConditionalOnMissingBean(name = STREAMING_LANGUAGE_MODEL_TASK_EXECUTOR)
@ConditionalOnMissingClass("io.micrometer.context.ContextSnapshotFactory")
AsyncTaskExecutor openAiStreamingLanguageModelTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setThreadNamePrefix(TASK_EXECUTOR_THREAD_NAME_PREFIX);
return taskExecutor;
}
@Bean
@ConditionalOnProperty(PREFIX + ".embedding-model.api-key")
OpenAiEmbeddingModel openAiEmbeddingModel(
@Qualifier(EMBEDDING_MODEL_HTTP_CLIENT_BUILDER) HttpClientBuilder httpClientBuilder,
Properties properties
) {
EmbeddingModelProperties embeddingModelProperties = properties.embeddingModel();
return OpenAiEmbeddingModel.builder()
.httpClientBuilder(httpClientBuilder)
.baseUrl(embeddingModelProperties.baseUrl())
.apiKey(embeddingModelProperties.apiKey())
.organizationId(embeddingModelProperties.organizationId())
.projectId(embeddingModelProperties.projectId())
.modelName(embeddingModelProperties.modelName())
.dimensions(embeddingModelProperties.dimensions())
.maxSegmentsPerBatch(embeddingModelProperties.maxSegmentsPerBatch())
.user(embeddingModelProperties.user())
.timeout(embeddingModelProperties.timeout())
.maxRetries(embeddingModelProperties.maxRetries())
.logRequests(embeddingModelProperties.logRequests())
.logResponses(embeddingModelProperties.logResponses())
.customHeaders(embeddingModelProperties.customHeaders())
.build();
}
@Bean(EMBEDDING_MODEL_HTTP_CLIENT_BUILDER)
@ConditionalOnProperty(PREFIX + ".embedding-model.api-key")
@ConditionalOnMissingBean(name = EMBEDDING_MODEL_HTTP_CLIENT_BUILDER)
HttpClientBuilder openAiEmbeddingModelHttpClientBuilder(ObjectProvider<RestClient.Builder> restClientBuilder) {
return SpringRestClient.builder()
.restClientBuilder(restClientBuilder.getIfAvailable(RestClient::builder))
// executor is not needed for no-streaming OpenAiEmbeddingModel
.createDefaultStreamingRequestExecutor(false);
}
@Bean
@ConditionalOnProperty(PREFIX + ".moderation-model.api-key")
OpenAiModerationModel openAiModerationModel(
@Qualifier(MODERATION_MODEL_HTTP_CLIENT_BUILDER) HttpClientBuilder httpClientBuilder,
Properties properties
) {
ModerationModelProperties moderationModelProperties = properties.moderationModel();
return OpenAiModerationModel.builder()
.httpClientBuilder(httpClientBuilder)
.baseUrl(moderationModelProperties.baseUrl())
.apiKey(moderationModelProperties.apiKey())
.organizationId(moderationModelProperties.organizationId())
.projectId(moderationModelProperties.projectId())
.modelName(moderationModelProperties.modelName())
.timeout(moderationModelProperties.timeout())
.maxRetries(moderationModelProperties.maxRetries())
.logRequests(moderationModelProperties.logRequests())
.logResponses(moderationModelProperties.logResponses())
.customHeaders(moderationModelProperties.customHeaders())
.build();
}
@Bean(MODERATION_MODEL_HTTP_CLIENT_BUILDER)
@ConditionalOnProperty(PREFIX + ".moderation-model.api-key")
@ConditionalOnMissingBean(name = MODERATION_MODEL_HTTP_CLIENT_BUILDER)
HttpClientBuilder openAiModerationModelHttpClientBuilder(ObjectProvider<RestClient.Builder> restClientBuilder) {
return SpringRestClient.builder()
.restClientBuilder(restClientBuilder.getIfAvailable(RestClient::builder))
// executor is not needed for no-streaming OpenAiModerationModel
.createDefaultStreamingRequestExecutor(false);
}
@Bean
@ConditionalOnProperty(PREFIX + ".image-model.api-key")
OpenAiImageModel openAiImageModel(
@Qualifier(IMAGE_MODEL_HTTP_CLIENT_BUILDER) HttpClientBuilder httpClientBuilder,
Properties properties
) {
ImageModelProperties imageModelProperties = properties.imageModel();
return OpenAiImageModel.builder()
.httpClientBuilder(httpClientBuilder)
.baseUrl(imageModelProperties.baseUrl())
.apiKey(imageModelProperties.apiKey())
.organizationId(imageModelProperties.organizationId())
.projectId(imageModelProperties.projectId())
.modelName(imageModelProperties.modelName())
.size(imageModelProperties.size())
.quality(imageModelProperties.quality())
.style(imageModelProperties.style())
.user(imageModelProperties.user())
.responseFormat(imageModelProperties.responseFormat())
.timeout(imageModelProperties.timeout())
.maxRetries(imageModelProperties.maxRetries())
.logRequests(imageModelProperties.logRequests())
.logResponses(imageModelProperties.logResponses())
.customHeaders(imageModelProperties.customHeaders())
.build();
}
@Bean(IMAGE_MODEL_HTTP_CLIENT_BUILDER)
@ConditionalOnProperty(PREFIX + ".image-model.api-key")
@ConditionalOnMissingBean(name = IMAGE_MODEL_HTTP_CLIENT_BUILDER)
HttpClientBuilder openAiImageModelHttpClientBuilder(ObjectProvider<RestClient.Builder> restClientBuilder) {
return SpringRestClient.builder()
.restClientBuilder(restClientBuilder.getIfAvailable(RestClient::builder))
// executor is not needed for no-streaming OpenAiImageModel
.createDefaultStreamingRequestExecutor(false);
}
}
初始化ChatModel对象模型
虽然 Spring Boot 基于导入的依赖项提供了一个默认配置的 ChatModel 实现,并且它已经足够好了。但我们仍然可以仅通过定义必要的属性来覆盖自动配置。
例如,我们可以使用以下属性自定义 OpenAiChatModel bean对象。配置项主要用于对 LangChain4j 与 OpenAI 的聊天模型进行参数设置,从而确保能够与 OpenAI 的 API 进行交互,并且能根据需求对模型的行为和日志记录进行调整。
properties
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
langchain4j.open-ai.chat-model.model-name=gpt-3.5-turbo
langchain4j.open-ai.chat-model.temperature=0.7
langchain4j.open-ai.chat-model.log-requests = true
langchain4j.open-ai.chat-model.log-responses = true
这段配置设定了与 OpenAI gpt-3.5-turbo
模型进行交互所需的必要信息,涵盖 API 密钥、模型名称、输出随机性,同时还开启了请求和响应的日志记录功能,这对于开发和调试基于 LangChain4j 的应用程序具有重要意义。
配置分析介绍说明
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
- 功能 :此配置项用于设定与 OpenAI API 进行交互时所需的 API 密钥。
${OPENAI_API_KEY}
属于环境变量引用,这意味着你需要在运行代码的环境里设置名为OPENAI_API_KEY
的环境变量,该变量的值即为你的 OpenAI API 密钥。
- 作用:OpenAI API 会依据此密钥来识别和验证你的请求,保证只有拥有合法密钥的用户才能够调用其服务。
- 功能 :此配置项用于设定与 OpenAI API 进行交互时所需的 API 密钥。
langchain4j.open-ai.chat-model.model-name=gpt-3.5-turbo
- 功能 :该配置项指定了要使用的 OpenAI 聊天模型的名称,这里选用的是
gpt-3.5-turbo
。 - 作用 :
gpt-3.5-turbo
是 OpenAI 提供的一款性能卓越且价格较为实惠的聊天模型,能够处理多种自然语言处理任务,像文本生成、问答、对话等。
- 功能 :该配置项指定了要使用的 OpenAI 聊天模型的名称,这里选用的是
langchain4j.open-ai.chat-model.temperature=0.7
- 功能 :
temperature
是一个取值范围在 0 到 1 之间的参数,它主要用于控制模型输出的随机性。 - 作用:数值越接近 0,模型的输出就越确定、保守,倾向于选择概率较高的词汇;数值越接近 1,输出就越随机、富有创意,可能会产生更多新颖但也可能不太常规的内容。此处设置为 0.7,表明输出会在确定性和随机性之间取得一个相对平衡的状态。
- 功能 :
langchain4j.open-ai.chat-model.log-requests = true
- 功能:此配置项用于开启对发送给 OpenAI API 的请求进行日志记录的功能。
- 作用:开启后,系统会记录每次请求的详细信息,这有助于你进行调试和监控,例如查看请求的内容、参数等,从而分析请求是否符合预期。
langchain4j.open-ai.chat-model.log-responses = true
- 功能:该配置项用于开启对从 OpenAI API 接收到的响应进行日志记录的功能。
- 作用:开启后,系统会记录每次响应的详细信息,方便你查看模型的输出结果、分析响应的质量,以及在出现问题时进行排查。
通过JavaConfig创建ChatModel对象
不过,如果我们想以编程方式从头开始创建这个bean,我们可以使用 ChatModel.builder()来实现。
java
@Configuration
public class LlmConfig {
@Bean
OpenAiChatModel openAiChatModel() {
return OpenAiChatModel.builder()
.apiKey(${OPENAI_API_KEY})
.modelName("gpt-3.5-turbo")
.temperature("0.7")
.logRequests(true)
.logResponses(true)
.build();
}
}
调用LLM语言模型
一旦所需的LanguageModel bean准备就绪,我们就可以调用其 generate()方法来向大语言模型发送提示并接收大语言模型的响应。
java
@Autowired
ChatLanguageModel model;
@Bean(name = "mainApplicationRunner")
ApplicationRunner applicationRunner() {
String responseText = model.generate("你好,你怎么样?");
System.out.println(responseText);
}
程序输出
bash
你好!我只是一个计算机程序,所以我没有感情,但我在这里为你提供你需要的任何帮助。我今天能为你做什么?
如果我们在模型配置或属性文件中启用了请求和响应日志记录,我们可以在应用程序日志中验证发送的提示和接收的响应。
bash
2025-03-02T01:29:20.040+05:30 DEBUG 17668 --- [ restartedMain] d.a.openai4j.RequestLoggingInterceptor : Request:
- method: POST
- url: https://api.openai.com/v1/chat/completions
- headers: [Authorization: Bearer sk-pr...W9], [User-Agent: langchain4j-openai]
- body: {
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "你好,你怎么样?"
}
],
"temperature": 0.7
}
2024-06-02T01:29:21.815+05:30 DEBUG 17668 --- [ restartedMain] d.a.openai4j.ResponseLoggingInterceptor : Response:
- status code: 200
- headers: [...HIDDEN...]
- body: {
"id": "chatcmpl-9VPAn21JksWMomyYKZiXHf31maA4I",
"object": "chat.completion",
"created": 1717271961,
"model": "gpt-3.5-turbo-0125",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你好!我只是一个计算机程序,所以我没有感情,但我在这里为你提供你需要的任何帮助。我今天能为你做什么?"
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 35,
"total_tokens": 47
},
"system_fingerprint": null
}
发送系统和用户的提示词
接下来,我们正在创建系统消息和用户消息,并将提示发送给大语言模型。之后,我们接收大语言模型的响应并在控制台中打印它。
- 系统消息
- 设定模型行为和角色:为告知模型其角色、任务或特定的行为模式。
- 设定背景和约束条件:为模型提供与任务相关的背景知识、上下文信息或特定的规则限制。
- 用户消息
- 明确用户需求问题:向模型传达自己的具体需求、问题或请求的方式。
- 引导模型生成回复:为模型的输出提供了直接的引导。
我们可以创建不同的消息类型对象,并使用generate()
方法发送它们。
系统信息
主要是定义对应的AI机器人的角色和基本信息,并且他需要做的规范和规则
java
SystemMessage systemMessage = SystemMessage.from("你是一个有用的人工智能助手,能够帮助人们查找信息。你的名字是 Alexa。首先介绍你的名字并简要概括你将在一句话中提供的答案。接着,你应该回复用户的请求。最后感谢用户提出问题。");
用户信息
主要是定义和描述用户的需求概念以及写入回复信息的格式。
java
String userMessageTxt = "告诉我关于 {{place}} 的情况。以列表形式简要写出答案。";
最后执行对应的大模型信息调用内容处理。
java
UserMessage userMessage = UserMessage.from(userMessageTxt.replace("{{place}}", "USA"));
Response<AiMessage> response = chatLanguageModel.generate(systemMessage, userMessage);
System.out.println(response.content());
程序输出:日志记录处于开启状态,我们也可以检查请求和响应。
bash
2024-06-02T01:39:41.007+05:30 DEBUG 252 --- [ restartedMain] d.a.openai4j.RequestLoggingInterceptor : Request:
//...
- body: {
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "你是一个很有帮助的人工智能"
},
{
"role": "user",
"content": ""告诉我关于美国的情况。以列表形式简要写出答案。""
}
],
"temperature": 0.7
}
2025-03-02T01:39:43.491+05:30 DEBUG 252 --- [ restartedMain] d.a.openai4j.ResponseLoggingInterceptor : Response:
- status code: 200
- headers: [...]
- body: {
//...
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你好,我是Alexa,我将为你提供一份关于美国的简短列表......"
},
"logprobs": null,
"finish_reason": "stop"
}
],
//...
}