LangChain4j实战:模型参数配置、多模态、流式输出、聊天记忆、提示词工程全解析

news/2025/11/5 16:27:35/文章来源:https://www.cnblogs.com/shenStudy/p/19194052

LangChain4j实战:模型参数配置、多模态、流式输出、聊天记忆、提示词工程全解析

前提

后面用于演示的代码环境为: JDK-21,apache-maven-3.6.2,spring-boot和langchain4j的版本如下面pom文件所示

    <properties><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>21</java.version><!--Spring Boot--><spring-boot.version>3.5.3</spring-boot.version><!--LangChain4J--><langchain4j.version>1.7.1</langchain4j.version><!--LangChain4J community--><langchain4j-community.version>1.7.1-beta14</langchain4j-community.version></properties><dependencyManagement><dependencies><!--Spring Boot--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><!--LangChain4J--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-bom</artifactId><version>${langchain4j.version}</version><type>pom</type><scope>import</scope></dependency><!--langchain4j-community--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-bom</artifactId><version>${langchain4j-community.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>21</source><target>21</target></configuration></plugin></plugins></build>

模型参数配置

根据选择的模型和提供商,可以调整很多参数,这里以OpenAI API的参数为例进行讲解

LangChain4j提供了模型构建器,我们可以使用构建器模式设置模型的参数,由于参数很多这里重点讲日志打印,监听机制,重试机制,超时机制的参数配置

1

基本使用的模型配置

如果仅是使用模型,那么只需要设置基本参数-大模型请求地址,模型名称,个人密钥

@Configuration
public class LLMConfig {@Beanpublic ChatModel chatModelQwen() {return OpenAiChatModel.builder()            //api-key.apiKey(System.getenv("aliyunQwen-apiKey"))//调用模型名.modelName("qwen-plus")//调用阿里云百炼平台大模型的url.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}
}

日志打印配置

通过模型配置参数实现打印请求大模型与大模型返回的日志

前提

需要有SLF4J日志后端依赖,调整全局日志打印级别并指定langchain4j包的日志打印级别,配置文件如下

<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.8</version>
</dependency>
logging:level:# 设置全局日志级别为INFOroot: INFO# 设置特定包的日志打印级别为DEBUGdev.langchain4j: DEBUG

打印日志的模型配置

@Configuration
public class LLMConfig {@Bean(value = "qwen")public ChatModel chatModelQwen() {return OpenAiChatModel.builder()//api-key.apiKey(System.getenv("aliyunQwen-apiKey"))//调用模型名.modelName("qwen-plus")//调用阿里云百炼平台大模型的url.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")//日志配置-打印请求日志.logRequests(true)//日志配置-打印返回日志.logResponses(true).build();}
}

监听机制配置

大模型(ChatModel或StreamingChatModel)允许配置ChatModelListener来监听事件,如向LLM发送请求时,收到响应时,异常时

构建监听实现类

@Slf4j
public class TestChatModelListener implements ChatModelListener {/*** 向 LLM 发送的请求时的监听事件** @param requestContext The request context.*/@Overridepublic void onRequest(ChatModelRequestContext requestContext) {// 使用Hutool工具库中的id生成工具生成UUIDString uuid = IdUtil.simpleUUID();requestContext.attributes().put("traceId", uuid);log.info("[请求监听] 请求参数 requestContext: " + requestContext.attributes().toString());}/*** 来自 LLM 的响应时的监听事件** @param responseContext The response context.*/@Overridepublic void onResponse(ChatModelResponseContext responseContext) {log.info("[响应监听] 返回结果 responseContext: " + responseContext.attributes().toString());}/*** 错误的监听事件** @param errorContext The error context.*/@Overridepublic void onError(ChatModelErrorContext errorContext) {log.error("[异常监听] 请求异常 errorContext: " + errorContext);}
}

监听机制的模型配置

@Configuration
public class LLMConfig {@Bean(value = "qwen")public ChatModel chatModelQwen() {return OpenAiChatModel.builder()//api-key.apiKey(System.getenv("aliyunQwen-apiKey"))//调用模型名.modelName("qwen-plus-2025-04-28")//调用阿里云百炼平台大模型的url.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")//监听配置.listeners(List.of(new TestChatModelListener())).build();}
}

重试机制配置

即调用失败时的重试次数,默认是调用两次

2

@Configuration
public class LLMConfig {@Bean(value = "qwen")public ChatModel chatModelQwen() {return OpenAiChatModel.builder()//api-key.apiKey(System.getenv("aliyunQwen-apiKey"))//调用模型名.modelName("qwen-plus-2025-04-28")//调用阿里云百炼平台大模型的url.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")//重试配置.maxRetries(3).build();}
}

超时机制配置

即配置连接超时时间和读超时时间,默认是连接超时时间为15s,读超时时间为60s

@Configuration
public class LLMConfig {@Bean(value = "qwen")public ChatModel chatModelQwen() {return OpenAiChatModel.builder()//api-key.apiKey(System.getenv("aliyunQwen-apiKey"))//调用模型名.modelName("qwen-plus-2025-04-28")//调用阿里云百炼平台大模型的url.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")//请求超时配置.timeout(Duration.ofSeconds(2)).build();}
}

多模态

大模型的能力不仅仅局限于文本生成,还可以应用于视图理解,图片生成,语音识别,语音合成,视频生成等等,这里主要演示LangChain4j调用大模型的视图理解与图片生成的能力

前提

首先需要选取具体视图理解与图片生成的大模型,下面是大模型的配置类与依赖pom文件示例

pom依赖

    <dependencies><!--快速构建一个基于 Spring MVC 的 Web 应用程序而预置的一组依赖项的集合--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--一个通过注解在编译时自动生成 Java 样板代码的库--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--hutool Java工具库--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.40</version></dependency><!--对 Spring Boot 应用程序进行全面测试而预置的一组测试相关依赖项的集合--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!--LangChain4J openAI集成依赖--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId></dependency><!--LangChain4J 高级AI服务API依赖--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId></dependency><!--DashScope-阿里云开发的平台与langchain4j集成--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-dashscope</artifactId></dependency></dependencies>

大模型配置类

@Configuration
public class LLMConfig {@Beanpublic ChatModel chatLanguageModel() {return OpenAiChatModel.builder()//api-key.apiKey(System.getenv("aliyunQwen-apiKey"))//调用模型名.modelName("qwen3-vl-plus")//调用阿里云百炼平台大模型的url.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}/*** 文本生成图片模型-通义万相** @return 通义万相模型*/@Beanpublic WanxImageModel wanxImageModel() {return WanxImageModel.builder().apiKey(System.getenv("aliyunQwen-apiKey")).modelName("wan2.5-t2i-preview").build();}
}

视图理解的控制层接口

视图理解功能需要将图片转换为字节数组并通过base64编码转换为字符串后与文本提示词一起传输给大模型进行分析

@Slf4j
@RestController
@RequestMapping(value = "/image")
public class ImageModelController {@Autowiredprivate ChatModel chatModel;@Value("classpath:static/image/TencentQuarterlyReport.png")private Resource resource;/*** 多模态调用,采用文本加图片的形式进行图片理解** @return 图片理解内容* @throws IOException IO异常*/@GetMapping("/readImage")public String readImageContent() throws IOException {String modelResult = null;//图片转码,通过Base64编码将图片转换为字符串byte[] byteArray = resource.getContentAsByteArray();String base64Str = Base64.getEncoder().encodeToString(byteArray);//多模块提示词,既包含文本也包含图片,同时发送给大模型进行处理UserMessage userMessage = UserMessage.from(TextContent.from("从下面图片中分析图片中内容,提炼出中心主旨"),ImageContent.from(base64Str, "image/png"));//API调用ChatResponse chatResponse = chatModel.chat(userMessage);//解析响应体,从ChatResponse中获取AI大模型的回复modelResult = chatResponse.aiMessage().text();log.info("大模型返回结果为: {}", modelResult);return modelResult;}
}

图片生成的控制层接口

图片生成的输入和文本生成的输入类似,均是输入提示词,图片生成大模型以通义万相为例会返回一个有效期24小时的图像下载链接

@Slf4j
@RestController
@RequestMapping(value = "/image")
public class WanxImageController {@Autowiredprivate WanxImageModel wanxImageModel;/*** 基于通义万相模型调用接口生成图片** @return 图片URL*/@GetMapping(value = "/imageCreate/one")public String imageCreateOne() {Response<Image> response = wanxImageModel.generate("生成一幅水墨画");log.info("[生成图片]生成图片的url为:{}", response.content().url());return response.content().url().toString();}/*** 在不构建模型配置的情况下通过dashscope依赖类生成图片** @return 图片URL*/@GetMapping(value = "/imageCreate/two")public String imageCreateTwo() {String prompt = "一副典雅庄重的对联悬挂于厅堂之中,房间是个安静古典的中式布置,桌子上放着一些青花瓷,对联上左书“义本生知人机同道善思新”,右书“通云赋智乾坤启数高志远”, 横批“智启通义”,字体飘逸,中间挂在一着一副中国风的画作,内容是岳阳楼。";Map<String, Object> parameters = new HashMap<>();parameters.put("prompt_extend", true);parameters.put("watermark", true);ImageSynthesisParam param =ImageSynthesisParam.builder().apiKey(System.getenv("aliyunQwen-apiKey")).model("qwen-image").prompt(prompt).n(1).size("1328*1328").parameters(parameters).build();ImageSynthesis imageSynthesis = new ImageSynthesis();ImageSynthesisResult result = null;try {log.info("---同步调用,请等待任务执行----");result = imageSynthesis.call(param);} catch (ApiException | NoApiKeyException e) {throw new RuntimeException(e.getMessage());}String jsonResult = JsonUtils.toJson(result);log.info(jsonResult);return jsonResult;}
}

流式输出

LLM一次生成一个标记(token),因此很多LLM提供商提供了一种方式,可以逐个标记地流式传输响应,而不是等待整个文本生成完毕。这显著改善了用户体验,因为用户不需要等待未知的时间,几乎可以立即开始阅读响应。

前提

流式输出中用到了响应时编程的核心类Flux,因此需要简单讲解一下Flux类,以及需要的依赖和配置文件

Flux类

Flux是io.projectreactor响应式编程库的核心类,用于表示0到N个元素的异步序列,可以发射0个,1个或多个元素,支持背压(Backpressure)。适用场景: 如处理数据库多条记录,消息队列,实时时间流等。

pom依赖

    <dependencies><!--快速构建一个基于 Spring MVC 的 Web 应用程序而预置的一组依赖项的集合--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--一个通过注解在编译时自动生成 Java 样板代码的库--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--LangChain4J openAI集成依赖(低级API依赖)--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId></dependency><!--LangChain4J 高级AI服务API依赖--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId></dependency><!--LangChain4J 响应式编程依赖(AI服务使用Flux)--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-reactor</artifactId></dependency><!--hutool Java工具库--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.40</version></dependency></dependencies>

配置文件

server:servlet:# 设置响应的字符编码,避免流式返回输出乱码encoding:charset: UTF-8enabled: trueforce: true

低阶LLM API的流式输出

对于ChatModel和LanguageModel接口,有相应的StreamingChatLanguageModel和StreamingLanguageModel接口。这些接口有类似的API,但可以流式传输响应。它们接受StreamingChatResponseHandler接口实现作为参数

3

public interface StreamingChatResponseHandler {// 当生成下一个部分文本响应时default void onPartialResponse(String partialResponse) {}default void onPartialResponse(PartialResponse partialResponse, PartialResponseContext context) {}// 当生成下一个部分思考/推理文本时default void onPartialThinking(PartialThinking partialThinking) {}default void onPartialThinking(PartialThinking partialThinking, PartialThinkingContext context) {}// 当生成下一个部分工具调用时default void onPartialToolCall(PartialToolCall partialToolCall) {}default void onPartialToolCall(PartialToolCall partialToolCall, PartialToolCallContext context) {}// 当LLLM完成单个工具调用的流处理时default void onCompleteToolCall(CompleteToolCall completeToolCall) {}// 当LLM完成生成时void onCompleteResponse(ChatResponse completeResponse);// d当出现错误时void onError(Throwable error);
}

高阶LLM API的流式输出

可以直接使用Flux

/*** 声明式AI服务业务接口*/
public interface ChatAssistant {/*** 普通对话接口** @param prompt 提示词* @return 模型返回结果*/String chat(String prompt);/*** 流式返回对话接口** @param prompt 提示词* @return 模型返回结果*/Flux<String> chatFlux(String prompt);
}

低阶与高阶LLM API流式输出示例

大模型配置类

@Configuration
public class LLMConfig {/*** 普通对话模型配置** @return chatLanguageModel*/@Bean(value = "qwen")public ChatModel chatLanguageModelQwen() {return OpenAiChatModel.builder().apiKey(System.getenv("aliyunQwen-apiKey")).modelName("qwen3-next-80b-a3b-instruct").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}@Bean(value = "streamQwen")public StreamingChatModel streamingChatLanguageModelQwen() {return OpenAiStreamingChatModel.builder().apiKey(System.getenv("aliyunQwen-apiKey")).modelName("qwen3-next-80b-a3b-instruct").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}@Bean(value = "chatAssistant")public ChatAssistant chatAssistant(StreamingChatModel chatLanguageModel) {return AiServices.create(ChatAssistant.class, chatLanguageModel);}
}

流式输出的控制层接口

@Slf4j
@RestController
@RequestMapping(value = "/streamChat")
public class StreamingChatController {/*** 低阶的LLM API*/@Autowired@Qualifier("streamQwen")private StreamingChatModel streamQwen;/*** 高阶的LLM API(自己封装接口)*/@Autowired@Qualifier("chatAssistant")private ChatAssistant chatAssistant;/*** 低阶LLM API 流式返回到页面* @param prompt 提示词* @return 异步序列*/@GetMapping(value = "/chatOne")public Flux<String> chatOne(@RequestParam(value = "prompt", defaultValue = "你是谁") String prompt) {return Flux.create(emitter -> streamQwen.chat(prompt, new StreamingChatResponseHandler() {@Overridepublic void onPartialResponse(String partialResponse) {emitter.next(partialResponse);}@Overridepublic void onCompleteResponse(ChatResponse completeResponse) {emitter.complete();}@Overridepublic void onError(Throwable error) {emitter.error(error);}}));}/*** 低阶LLM API 流式返回到后端* @param prompt 提示词*/@GetMapping(value = "/chatTwo")public void chatTwo(@RequestParam(value = "prompt", defaultValue = "你是谁") String prompt) {streamQwen.chat(prompt, new StreamingChatResponseHandler() {@Overridepublic void onPartialResponse(String partialResponse) {System.out.println(partialResponse);}@Overridepublic void onCompleteResponse(ChatResponse completeResponse) {System.out.println("==response over:" + completeResponse);}@Overridepublic void onError(Throwable error) {error.printStackTrace();}});}/*** 高阶LLM API 直接调用封装接口流式返回到页面* @param prompt 提示词 * @return 异步序列*/@GetMapping(value = "/chatThree")public Flux<String> chatThree(@RequestParam(value = "prompt", defaultValue = "你是谁") String prompt) {return chatAssistant.chatFlux(prompt);}}

聊天记忆

聊天记忆是指大模型在对话过程中,能够记住,理解并利用之前交流过的内容,来影响后续回答的能力。是大模型从一个"知识问答工具"进化为"智能对话伙伴"的关键技术。它通过巧妙地结合"短期记忆"(上下文窗口)和"长期记忆"(外部知识库),让对话变得连贯、智能、个性化。

记忆与历史的区别

历史:
历史保持用户和AI之间的所有消息完整无缺。历史是用户在UI中看到的内容。它代表实际对话内容。记忆:
记忆保存了一些信息,这些信息呈现给LLM,使其表现得就像"记住"了对话一样。记忆与历史截然不同。根据使用的记忆算法,它可以以各种方式修改历史:淘汰一些消息,总结多条消息,总结单独的消息,从消息中删除不重要的细节,向消息中注入额外信息等等。注:当前LangChain4j只提供"记忆",而不是"历史"。

淘汰策略

采用淘汰策略的原因

  • 为了适应LLM的上下文窗口。LLM一次可以处理的令牌(token)数量是有上限的。在某些时候,对话可能会超过这个限制。在这种情况下,应该淘汰一些消息。通常,最旧的消息会被淘汰,但如果需要,可以实现更复杂的算法。
  • 控制成本。每个令牌(token)都有成本,使每次调用LLM的费用逐渐增加。淘汰不必要的消息可以降低成本。
  • 控制延迟。发送给LLM的令牌(token)越多,处理它们所需的时间就越长。

LangChain4j提供的2种淘汰策略

4

  • MessageWindowChatMemory:作为滑动窗口运行,保留最近的N条消息,并淘汰不再适合的旧消息。然而,由于每条消息可能包含不同数量的令牌,MessageWindowChatMemory主要用于快速原型设计。
  • TokenWindowChatMemory: 同样作为滑动窗口运行,但专注于保留最近的N个令牌(token),根据需要淘汰旧消息。消息是不可分割的。如果一条消息不适合,它会被完全淘汰。TokenWindowChatMemory需要TokenCountEstimator来计数每个ChatMessage中的令牌(Token)。

淘汰策略实现代码示例

pom依赖

    <dependencies><!--快速构建一个基于 Spring MVC 的 Web 应用程序而预置的一组依赖项的集合--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--一个通过注解在编译时自动生成 Java 样板代码的库--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--LangChain4J openAI集成依赖(低级API依赖)--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId></dependency><!--LangChain4J 高级AI服务API依赖--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId></dependency><!--LangChain4J 响应式编程依赖(AI服务使用Flux)--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-reactor</artifactId></dependency><!--hutool Java工具库--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.40</version></dependency></dependencies>

声明式AI服务业务接口

构建普通对话接口与带有记忆缓存功能的对话接口,用于后续调用对比

普通对话接口
public interface ChatAssistant {/*** 普通对话接口** @param prompt 提示词* @return 模型返回结果*/String chat(String prompt);
}
带有记忆缓存功能的对话接口
public interface ChatMemoryAssistant {/*** 带有记忆缓存的聊天接口** @param userId 用户id* @param prompt 提示词* @return 大模型返回内容*/String chatWithMemory(@MemoryId Long userId, @UserMessage String prompt);
}

大模型配置类

包含普通对话的AI服务实例与具备记忆功能不同淘汰策略的两种AI服务实例

@Configuration
public class LLMConfig {/*** 普通对话模型配置** @return chatLanguageModel*/@Bean(value = "qwen")public ChatModel chatLanguageModelQwen() {return OpenAiChatModel.builder().apiKey(System.getenv("aliyunQwen-apiKey")).modelName("qwen3-next-80b-a3b-instruct").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}/*** 基于低阶模型实例构建高阶使用的AIServices实例** @param chatModel 对话模型* @return AIServices实例*/@Bean(value = "chat")public ChatAssistant chatAssistant(ChatModel chatModel) {return AiServices.create(ChatAssistant.class, chatModel);}/*** 构建高阶AIServices实例(基于保留消息数的滑动窗口)** @param chatModel 对话模型* @return AIServices实例*/@Bean(value = "chatMemoryWithMessageWindow")public ChatMemoryAssistant chatMemoryWithMessageWindow(ChatModel chatModel) {return AiServices.builder(ChatMemoryAssistant.class).chatModel(chatModel)//根据memoryId构建一个ChatMemory,保留最多100条消息.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(100)).build();}/*** 构建高阶AIServices实例(基于保留Token数的滑动窗口)** @param chatModel 对话模型* @return AIServices实例*/@Bean(value = "chatMemoryWithTokenWindow")public ChatMemoryAssistant chatMemoryWithTokenWindow(ChatModel chatModel) {// 构建默认的token分词器TokenCountEstimator openAiTokenizer = new OpenAiTokenCountEstimator("gpt-4");return AiServices.builder(ChatMemoryAssistant.class).chatModel(chatModel)//根据memoryId构建一个ChatMemory,保留最多100条消息.chatMemoryProvider(memoryId -> TokenWindowChatMemory.withMaxTokens(1000, openAiTokenizer)).build();}
}

提供大模型调用的控制层接口

@Slf4j
@RestController
@RequestMapping(value = "/memoryChat")
public class ChatMemoryController {@Autowired@Qualifier("chat")private ChatAssistant chat;@Autowired@Qualifier("chatMemoryWithMessageWindow")private ChatMemoryAssistant chatMemoryWithMessageWindow;@Autowired@Qualifier("chatMemoryWithTokenWindow")private ChatMemoryAssistant chatMemoryWithTokenWindow;/*** 普通对话** @return 调用结果*/@GetMapping(value = "/chatOne")public String chatOne() {String answerOne = chat.chat("你好,我的名字叫张三");log.info("answerOne: {}", answerOne);String answerTwo = chat.chat("我的名字叫什么");log.info("answerTwo: {}", answerTwo);return "success : " + DateUtil.now() + "<br> \n\n answerOne: " + answerOne + "<br> \n\n answerTwo: " + answerTwo;}/*** 基于保留消息数的滑动窗口淘汰策略的带有记忆对话** @return 调用结果*/@GetMapping(value = "/chatTwo")public String chatTwo() {chatMemoryWithMessageWindow.chatWithMemory(1L, "你好,我的名字叫小明");String answerOne = chatMemoryWithMessageWindow.chatWithMemory(1L, "我的名字叫什么");log.info("answerOne: {}", answerOne);chatMemoryWithMessageWindow.chatWithMemory(3L, "你好,我的名字叫小红");String answerTwo = chatMemoryWithMessageWindow.chatWithMemory(3L, "我的名字叫什么");log.info("answerTwo: {}", answerTwo);return "chatMemoryWithMessageWindow success : " + DateUtil.now() + "<br> \n\n answerOne: " + answerOne + "<br> \n\n answerTwo: " + answerTwo;}/*** 基于保留Token数的滑动窗口淘汰策略的带有记忆对话** @return 调用结果*/@GetMapping(value = "/chatThree")public String chatThree() {chatMemoryWithTokenWindow.chatWithMemory(1L, "你好,我的名字叫小明");String answerOne = chatMemoryWithTokenWindow.chatWithMemory(1L, "我的名字叫什么");log.info("answerOne: {}", answerOne);chatMemoryWithTokenWindow.chatWithMemory(5L, "你好,我的名字叫小红");String answerTwo = chatMemoryWithTokenWindow.chatWithMemory(5L, "我的名字叫什么");log.info("answerTwo: {}", answerTwo);return "chatMemoryWithTokenWindow success : " + DateUtil.now() + "<br> \n\n answerOne: " + answerOne + "<br> \n\n answerTwo: " + answerTwo;}
}

聊天记忆持久化

默认情况下,ChatMemory实现在内存中存储ChatMessage。如果需要持久化,可以实现自定义的ChatMemoryStore,将ChatMessage存储在你选择的任何持久化存储中

通过redis实现聊天记忆持久化示例

由于声明式AI服务业务接口和控制层接口与上面的代码类似就不做展示了

pom依赖
    <dependencies><!--快速构建一个基于 Spring MVC 的 Web 应用程序而预置的一组依赖项的集合--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--一个通过注解在编译时自动生成 Java 样板代码的库--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--LangChain4J openAI集成依赖(低级API依赖)--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId></dependency><!--LangChain4J 高级AI服务API依赖--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId></dependency><!--LangChain4J 响应式编程依赖(AI服务使用Flux)--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-reactor</artifactId></dependency><!--hutool Java工具库--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.40</version></dependency><!-- springboot 集成redis数据源依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency></dependencies>
配置文件
server:port: 18190servlet:# 设置响应的字符编码,避免流式返回输出乱码encoding:charset: UTF-8enabled: trueforce: true
spring:data:# redis配置文件redis:host: localhostport: 6379database: 0connect-timeout: 10stimeout: 10spassword: 123456
redis配置类
@Slf4j
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactor) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactor);//设置key序列化方式stringredisTemplate.setKeySerializer(new StringRedisSerializer());//设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;}
}
自定义的ChatMemoryStore类
@Component
public class RedisChatMemoryStore implements ChatMemoryStore {public static final String CHAT_MEMORY_STORE_PREFIX = "CHAT_MEMORY:";@Autowiredprivate RedisTemplate<String, String> redisTemplate;/*** 实现通过内存ID从持久化存储中获取所有消息* @param memoryId The ID of the chat memory.* @return 消息集合*/@Overridepublic List<ChatMessage> getMessages(Object memoryId) {String messageValue = redisTemplate.opsForValue().get(CHAT_MEMORY_STORE_PREFIX + memoryId);return ChatMessageDeserializer.messagesFromJson(messageValue);}/*** 实现通过内存ID更新持久化存储中的所有消息* @param memoryId The ID of the chat memory.* @param messages List of messages for the specified chat memory, that represent the current state of the {@link ChatMemory}.*                 Can be serialized to JSON using {@link ChatMessageSerializer}.*/@Overridepublic void updateMessages(Object memoryId, List<ChatMessage> messages) {redisTemplate.opsForValue().set(CHAT_MEMORY_STORE_PREFIX + memoryId,ChatMessageSerializer.messagesToJson(messages));}/*** 实现通过内存ID删除持久化存储中的所有消息* @param memoryId The ID of the chat memory.*/@Overridepublic void deleteMessages(Object memoryId) {redisTemplate.delete(CHAT_MEMORY_STORE_PREFIX + memoryId);}
}
大模型配置类
@Configuration
public class LLMConfig {@Autowiredprivate RedisChatMemoryStore redisChatMemoryStore;/*** 普通对话模型配置** @return chatModel*/@Bean(value = "qwen")public ChatModel chatLanguageModelQwen() {return OpenAiChatModel.builder().apiKey(System.getenv("aliyunQwen-apiKey")).modelName("qwen3-next-80b-a3b-instruct").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}@Beanpublic ChatPersistenceAssistant chatPersistenceAssistant(ChatModel chatModel) {ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder().id(memoryId).maxMessages(1000).chatMemoryStore(redisChatMemoryStore).build();return AiServices.builder(ChatPersistenceAssistant.class).chatModel(chatModel).chatMemoryProvider(chatMemoryProvider).build();}}

提示词工程

在日常使用大模型的过程中,我们常常会遇到这样的场景:简单地向AI提问"写个故事",结果往往是一篇泛泛而谈,缺乏方向的内容;而如果我们换一种方式,给出"请写一个关于小狗勇闯森林的童话故事,要正能量,用小学生能听懂的语言",AI的输出便立刻贴近我们的预期。这种差异,正是普通提问与提示词(Prompt)之间的区别--前者往往模糊、随意;后者则清洗、结构化,包含任务目标、角色设定、语气风格、格式要求等关键信息。

普通提问就像与AI随意聊天,期待它"猜出"我们的需求;而提示词更像是一份精准的"说明书",让AI明确"做什么、怎么做"。通过精心设计的提示词,我们可以显著提升AI生成的内容的质量和针对性,这就是提示词工程的核心价值。

提示词的演进历程

  1. 简单的纯提示词提问问题
  2. 引入占位符(Prompt Template)以动态插入内容
  3. 多角色消息: 将消息分为不同角色(如用户、助手、系统等),设置功能边界,增强交互的复杂性和上下文感知能力。

ChatMessage的4种类型

目前有五种类型的聊天消息,但其中一种CustomMessage仅支持Ollama,因此一般说4种

5

UserMessage

这是来自用户的消息。用户可以是应用程序的最终用户,也可以是应用程序本身。

UserMessage包含的属性为:
contents: 消息内容。根据LLM支持的模式,它可以包含一个文本或其他形式。
name: 用户名。并不是所有的模型提供者都支持它。
attributes: 附加属性。这些属性不发送到模型,但是存储在聊天记忆(ChatMemory)当中。

AiMessage

该消息是由AI生成的,是对已发送消息的响应。

AiMessage包含的属性为:
text: 文本内容。
thinking: 思考/推理内容。
toolExecutionRequests: 执行工具的请求。
attributes: 额外的属性,通常是特定于提供的程序的。

ToolExecutionResultMessage

ToolExecutionResultMessage是ToolExecutionRequest的结果。

SystemMessage

来自系统的消息。通常,作为开发人员应该定义此消息的内容。通常,你会在这里写一些说明,说明LLM在这次对话中的角色,它应该如何表现,以什么方式回答,等等。LLM被训练得比其他类型的消息更关注SystemMessage,所以要小心,最后不要让最终用户自由地定义或注入一些输入到SystemMessage。通常,它位于对话的开始。

CustomMessage

这是一个自定义消息,可以包含任意属性。此消息类型目前仅支持Ollama。

提示词工程示例:打造专业的限定能力范围的AI助手

本示例通过三种方式来体现多消息角色的使用

pom依赖

    <dependencies><!--快速构建一个基于 Spring MVC 的 Web 应用程序而预置的一组依赖项的集合--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--一个通过注解在编译时自动生成 Java 样板代码的库--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--LangChain4J openAI集成依赖(低级API依赖)--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId></dependency><!--LangChain4J 高级AI服务API依赖--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId></dependency><!--LangChain4J 响应式编程依赖(AI服务使用Flux)--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-reactor</artifactId></dependency><!--hutool Java工具库--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.40</version></dependency></dependencies>

声明式AI服务业务接口

设计为计算机领域助手

public interface ComputerChatAssistant {@SystemMessage("你是一位专业的计算机领域知识顾问,只回答计算机领域相关的问题" +"输出限制: 禁止回答非计算机领域的问题,直接返回'我只能回答计算机领域相关的问题'")@UserMessage("请回答以下问题: {{question}} , 字数控制在{{length}}以内")String chat(@V("question") String question, @V("length") int length);@SystemMessage("你是一位专业的计算机领域知识顾问,只回答计算机领域相关的问题" +"输出限制: 禁止回答非计算机领域的问题,直接返回'我只能回答计算机领域相关的问题'")String chat(ComputerPrompt prompt);
}

提示词业务实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@StructuredPrompt("根据{{language}}语言, 解答以下问题: {{question}}")
public class ComputerPrompt {private String language;private String question;
}

大模型配置类

@Configuration
public class LLMConfig {@Bean(value = "qwen")public ChatModel chatModelQwen() {return OpenAiChatModel.builder().apiKey(System.getenv("aliyunQwen-apiKey")).modelName("qwen3-next-80b-a3b-instruct").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}@Bean(value = "computerChatAssistant")public ComputerChatAssistant computerChatAssistant(ChatModel chatModel) {return AiServices.create(ComputerChatAssistant.class, chatModel);}
}

提供大模型调用的控制层接口

@Slf4j
@RestController
@RequestMapping(value = "/chatPrompt")
public class ChatPromptController {@Autowired@Qualifier("computerChatAssistant")private ComputerChatAssistant computerChatAssistant;@Autowired@Qualifier("qwen")private ChatModel qwen;/*** 方式一:@SystemMessage+@UserMessage+@V** @return 大模型返回结果*/@GetMapping(value = "/chatMethod1")public String chatMethod1() {String answer1 = computerChatAssistant.chat("计算机由什么组成", 1000);log.info("answer1: {}", answer1);String answer2 = computerChatAssistant.chat("苹果富含哪些营养", 1000);log.info("answer2: {}", answer2);return "success :" + DateUtil.now() + "<br> \n\n answer1: " + answer1 + "<br> \n\n answer2: " + answer2;}/*** 方式二:@SystemMessage + 带有@StructuredPrompt的业务实体类** @return*/@GetMapping(value = "/chatMethod2")public String chatMethod2() {ComputerPrompt computerPrompt = new ComputerPrompt();computerPrompt.setLanguage("Java");computerPrompt.setQuestion("数据基本类型有哪些");String answer = computerChatAssistant.chat(computerPrompt);log.info("answer: {}", answer);return "success :" + DateUtil.now() + "<br> \n\n answer: " + answer;}/*** 方式三: PromptTemplate+Prompt** @return*/@GetMapping(value = "/chatMethod3")public String chatMethod3() {String role = "金融专家";String question = "股票如何选购";//1.构建提示词模版PromptTemplate promptTemplate = PromptTemplate.from("你是一个{{role}}助手,回答以下内容{{question}}");//2.由提示词模版生成提示词Prompt prompt = promptTemplate.apply(Map.of("role", role, "question", question));//3.提示词生成UserMessageUserMessage userMessage = prompt.toUserMessage();//4.调用大模型ChatResponse chatResponse = qwen.chat(userMessage);log.info("chatResponse: {}", chatResponse);return "success :" + DateUtil.now() + "<br> \n\n chatResponse: " + chatResponse.aiMessage().text();}
}

参考资料

https://docs.langchain4j.dev/tutorials/model-parameters

https://docs.langchain4j.dev/tutorials/logging

https://docs.langchain4j.dev/tutorials/observability

https://bailian.console.aliyun.com/?tab=doc#/doc/?type=model&url=2848513

https://docs.langchain4j.dev/tutorials/response-streaming

https://docs.langchain4j.dev/tutorials/chat-memory

https://docs.langchain4j.dev/tutorials/chat-and-language-models

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/956912.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kafka-ui-docker-compose.yml

version: 3.8services:kafka-ui:image: artifacts.iflytek.com/docker-repo/provectuslabs/kafka-uicontainer_name: kafka-uiports:- "18080:8080"restart: unless-stoppedenvironment:- DYNAMIC_CONFIG_EN…

场景和使用的模型类型

目录背景和价值一、推理型智能体(绑定reasoning模型)二、基础型智能体(绑定basic模型)三、设计逻辑总结参考资料 背景和价值 以下是 deer-flow 的大模型配置如下链接 https://github.com/bytedance/deer-flow/blob…

lprm命令 – 移除打印队列中的任务

lprm命令来自于英文词组”Line printer remove“的缩写,其功能是用于移除打印队列中的任务,使用lprm命令来移除尚未完成的,正放在打印机贮列之中的打印任务。 语法格式: lprm [参数] 任务编号常用参数:-E 强制加密…

2025 年 11 月隐框幕墙精致钢厂家推荐排行榜,隐框幕墙精致钢型材,幕墙精致钢构件,精致钢幕墙材料公司推荐,专业品质与结构安全之选

2025 年 11 月隐框幕墙精致钢厂家推荐排行榜:专业品质与结构安全之选 随着建筑行业对幕墙系统安全性、美观性和耐久性要求的不断提升,隐框幕墙精致钢作为现代建筑幕墙的核心支撑材料,其质量与性能日益受到行业关注。…

[笔记]欧拉图

定义欧拉路径是每条边恰好经过一次的路径;存在欧拉路径的图是半欧拉图。 欧拉回路是每条边恰好经过一次的回路;存在欧拉回路的图是欧拉图。判定无向图是欧拉图\(\iff\)非零度节点连通,所有节点度数为偶。此时起点可…

基于粒子群算法(PSO)的灰度图像阈值分割及多适应度函数实现

1. 算法框架设计 %% 核心流程 1. 图像预处理 → 2. PSO参数初始化 → 3. 适应度函数计算 → 4. 粒子群迭代 → 5. 阈值输出2. PSO参数设置 % 粒子群参数 n_particles = 30; % 粒子数量 max_iter = 100; % 最大…

遍插茱萸少一N

你们开 ipart 不带 n 是吧?我把你们挂到 AI 学会上炒作一番好不好?明年就让你加倍 neural network 起来,改叫 ACGNN 如何?后年就是 ACG4N,最后就变成 ACG8N,学界被这种自娱自乐的小圈子产物塞满,彻底完 G8 蛋了…

小狗

小狗WSY MT 二哈

VScode输出控制台中文显示乱码解决方法(仅限于Python)

1、右键点击“我的电脑”(或“此电脑”),选择“属性”; 2、点击“高级系统设置”选项; 3、在弹出的系统属性窗口中,点击“环境变量”按钮; 4、在“系统变量”或“用户变量”下,点击“新建”来创建一个新的变量; 5…

高教工具箱使用说明书.docx

高教工具箱使用说明 一. 联接多个Excel表 1.适用场景:期末要汇总成绩了,而你的平时成绩、期中成绩和期末成绩存在三个不同的excel表里,而且学生排列的顺序是不同的。这时候,你可以使用此功能,把它们联结成一个Ex…

2025石家庄设计公司权威排行推荐榜:石家庄vi设计公司,石家庄画册设计公司品牌实力与创意服务的全景评测

引言 当前石家庄设计行业呈现蓬勃发展态势,但市场乱象同样不容忽视:设计水平参差不齐,部分公司缺乏专业设计团队和系统化服务流程,导致设计方案同质化严重,无法满足企业品牌建设与市场传播需求;服务质量良莠不齐…

2025年博物馆数字展厅制造企业权威推荐榜单:智能化展厅/企业展厅LED屏/企业展示展厅源头厂家精选

在数字化浪潮的推动下,博物馆展厅已从传统的静态陈列空间,升级为集VR/AR、AI、数字孪生等前沿技术于一体的沉浸式智能交互载体。行业数据显示,超过75%的新建展厅已集成至少三种数字互动技术,沉浸式体验项目的平均观…

基于直方图均衡化的图像去雾算法改进

一、传统直方图均衡化的局限性全局处理缺陷 无法处理局部光照不均(如浓雾区域与清澈区域并存) 容易导致过曝或细节丢失(如天空区域过度拉伸) 颜色失真问题 直接对RGB通道处理会破坏色彩平衡 暗通道估计偏差导致大气…

2025年现代候车亭企业权威推荐榜单:现代公交候车亭/现代公交站台/现代公交站亭源头厂家精选

随着城市智能化建设与公共交通服务的持续升级,现代候车亭作为城市家具的重要组成,其市场需求显著增长。现代候车亭以其智能化服务功能、耐用材质与人性化设计,广泛应用于城市干道、公交枢纽及社区周边等场景。本文将…

2025年水利铸铁闸门厂家权威推荐榜单:弧形铸铁闸门/抓斗式清污机/铸铁闸门源头厂家精选

在水利工程、农田灌溉及水电站建设领域,铸铁闸门作为水工建筑物的关键控制设备,其密封性、耐腐蚀性及结构强度直接影响水资源调控效率与工程安全。根据水利行业统计数据显示,2025年全国水利建设投资规模预计突破1.2…

智能充气泵方案:无线充气泵pcba的研发设计

充气泵PCBA(Printed Circuit Board Assembly)是现代充气设备的核心组件,负责控制充气泵的运行、监测气压、管理电源等功能。随着智能家居、汽车、户外运动等领域的快速发展,充气泵PCBA的设计与制造技术也在不断进步…

大屏动态交互总结

大屏动态交互总结大屏内容一般多且杂,根据设计图布局可分成左中右三栏或者左右两栏。 父组件 Index.vue 包含 左中右三栏。父组件作为“状态管理中心的“总指挥”它不关心具体数据怎么画成图表或表格,那是“执行部…

win7 系统完美运行. net 8 程序 所需依赖总结

经过数十次安装总结,基本上所有奇怪的情况都遇到了,最终总结出了下面的依赖清单,需要安装顺序从上到下依次安装。首先安装 [.net framwork 4.5.2],防止框架过老。安装下面的系统安全补丁更新系统的证书,防止在安装…

CONNECT 与 TLS 构建可治理边界

CONNECT 隧道机制 CONNECT 是 HTTP 协议中用于建立到目标主机与端口的隧道方法。客户端首先向出站节点发起 CONNECT 请求,指定目标域名与端口。隧道建立后,客户端与目标站点直接进行 TLS 握手,出站节点仅转发加密数…