news 2026/5/26 6:59:03

Day2-LangChain4j-模型参数-视觉处理-流式输出-记忆化工程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Day2-LangChain4j-模型参数-视觉处理-流式输出-记忆化工程

1.模型参数:

1.日志配置


修改applicaiton.yml的配置,打开日志配置,必须要设置成EDBUG模式:


修改LLMConfig类:

@Configuration public class LLMConfig { @Bean(name = "qwen") public ChatModel chatModelQwen() { return OpenAiChatModel.builder() .apiKey(System.getProperty("qwen-api")) .modelName("qwen-max") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") //日志级别设置为debug才有效 .logRequests(true) .logResponses(true) .build(); } @Bean(name = "deepseek") public ChatModel chatModelDeepseek() { return OpenAiChatModel.builder() .apiKey(System.getProperty("deepseek-api")) .modelName("deepseek-chat") .baseUrl("https://api.deepseek.com/v1") .build(); } @Bean public ChatAssistant chatAssistantQwen(@Qualifier("qwen") ChatModel chatModel) { return AiServices.create(ChatAssistant.class, chatModel); } }

2.对话模型监听器listener

首先定义一个监听器类继承LangChain4j的原生接口ChatModelListener:

@Slf4j public class TestChatModelListener implements ChatModelListener { @Override public void onRequest(ChatModelRequestContext requestContext) { ChatModelListener.super.onRequest(requestContext); } @Override public void onResponse(ChatModelResponseContext responseContext) { ChatModelListener.super.onResponse(responseContext); } @Override public void onError(ChatModelErrorContext errorContext) { ChatModelListener.super.onError(errorContext); } }

以下是接口的源码,与AOP的环绕通知有点类似:

public interface ChatModelListener { default void onRequest(ChatModelRequestContext requestContext) { } default void onResponse(ChatModelResponseContext responseContext) { } default void onError(ChatModelErrorContext errorContext) { } }

同样地在大模型配置类中,注意官网要求配置多个监听器,所以用List

@Bean(name = "qwen") public ChatModel chatModelQwen() { return OpenAiChatModel.builder() .apiKey(System.getProperty("qwen-api")) .modelName("qwen-max") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") //日志级别设置为debug才有效 .logRequests(true) .logResponses(true) .listeners(List.of(new TestChatModelListener())) .build(); }

按照LangChain4j的要求:

3.重试机制(默认重试3次):

@Bean(name = "qwen") public ChatModel chatModelQwen() { return OpenAiChatModel.builder() .apiKey(System.getProperty("qwen-api")) .modelName("qwen-max") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") //日志级别设置为debug才有效 .logRequests(true) .logResponses(true) .listeners(List.of(new TestChatModelListener())) //最大重试次数 .maxRetries(3) .build(); }

4.超时机制:

设置超时时间,只要超时,对大模型的请求就会中断。

@Bean(name = "qwen") public ChatModel chatModelQwen() { return OpenAiChatModel.builder() .apiKey(System.getProperty("qwen-api")) .modelName("qwen-max") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") //日志级别设置为debug才有效 .logRequests(true) .logResponses(true) .listeners(List.of(new TestChatModelListener())) //最大重试次数 .maxRetries(3) //如果没有收到响应超过设置时间,请求就会中断 .timeout(Duration.ofSeconds(3)) .build(); }

2.视觉理解:

首先需要转化大模型,选择专门处理图片的大模型。

LangChain4j原生处理图像:

@RestController @Slf4j public class ImageModelController { @Autowired private ChatModel chatModel; @Value("classpath:static/images.mi.jpg") private Resource resource; @GetMapping(value = "/image/call") public String readImageContent() throws Exception { //变成一个字节数组,通过Base64编码将图片转化为字符串 byte[] byteArray = resource.getContentAsByteArray(); String base64Data = Base64.getEncoder().encodeToString(byteArray); //提示词指定:结合ImageContent和TextContent一起发送到模型进行处理 UserMessage userMessage = UserMessage.from(TextContent.from("从下面图片获取来源网站名称"), ImageContent.from(base64Data, "image/jpg")); //API调用:使用OpenAiChatModel来构建请求,并通过chat()方法调用模型。 //请求内容包括文本提示和图片,模型会根据输入返回分析结果。 ChatResponse chatResponse = chatModel.chat(userMessage); //从ChatResponse中获取AI大模型的回复,打印出处理后的结果。 return chatResponse.aiMessage().text(); } }

LangChain4j引入第三方平台和自已整合,相当于给LangChain加入外挂,首先在Day1的父工程下引入阿里云百炼平台依赖管理清单:

<langchain4j-community.version>1.0.1-beta6</langchain4j-community.version>

在管理版本依赖中加入:

<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-community-bom</artifactId> <version>${langchain4j-community.version}</version> <type>pom</type> <scope>import</scope> </dependency>

子工程里:

<!--DashScope (Qwen)接入阿里云百炼平台 https://docs.langchain4j.dev/integrations/language-models/dashscope --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId> </dependency>

ChatModel是LangChain4j原生,引入阿里百炼平台后就用阿里百炼的大模型,在LLMConfig配置类中添加万相图片的大模型:

@Bean public WanxImageModel wanxImageModel() { return WanxImageModel.builder() .apiKey(System.getProperty("qwen-api")) .modelName("wanx2.1-t2i-turbo") //地址默认写好了 .build(); }

在controller中可以添加用户提示词。

@RestController public class WanxImageController { @Resource private WanxImageModel wanxImageModel; @GetMapping(value = "/image/create") public String createImage() throws Exception { System.out.println(wanxImageModel); Response<Image> imageResponse = wanxImageModel.generate("美女"); System.out.println(imageResponse.content().url()); return imageResponse.content().url().toString(); } @GetMapping(value = "/image/create3") public String createImageContent3() throws IOException { String prompt = "近景镜头,18岁的中国女孩,古代服饰,圆脸,正面看着镜头"; ImageSynthesisParam param = ImageSynthesisParam.builder() .apiKey(System.getenv("aliQwen-api")) .model(ImageSynthesis.Models.WANX_V1) .prompt(prompt) .style("<watercolor>") .n(1) .size("1024*1024") .build(); ImageSynthesis imageSynthesis = new ImageSynthesis(); ImageSynthesisResult result = null; try { System.out.println("---sync call, please wait a moment----"); result = imageSynthesis.call(param); } catch (ApiException | NoApiKeyException e){ throw new RuntimeException(e.getMessage()); } return JsonUtils.toJson(result); } }

返回结果为一个网址,在浏览器打开即可看到。

3.大模型的流式返回Flux:

如果不用流式返回,那么大模型返回的内容就会等待很久,导致性能下降,用户体验感就很差,所以我们希望能有一点内容就返回一点内容。

前置知识点:SpringBoot:响应式编程。

引入流式输出依赖:

<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-reactor</artifactId> </dependency>

在ChatAssistant接口中:

public interface ChatAssistant { String chat(String prompt); Flux<String> chatFlux(String prompt); }

LLMConfig配置类中:

@Configuration public class LLMConfig { //低阶api @Bean(name = "qwen") public ChatModel chatModelQwen() { return OpenAiChatModel.builder() .apiKey(System.getenv("qwen-api")) .modelName("qwen-plus") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") .build(); } @Bean public StreamingChatModel streamingChatModel(){ return OpenAiStreamingChatModel.builder() .apiKey(System.getenv("qwen-api")) .modelName("qwen-plus") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") .build(); } //高阶api @Bean public ChatAssistant chatAssistant(StreamingChatModel streamingChatModel){ return AiServices.create(ChatAssistant.class, streamingChatModel); } }

控制层代码:

@RestController @Slf4j public class StreamingChatModelController { //直接使用 low-level LLM API @Resource private StreamingChatModel streamingChatLanguageModel; //自己封装接口使用 high-level LLM API @Resource private ChatAssistant chatAssistant; @GetMapping(value = "/chatstream/chat") public Flux<String> chat(@RequestParam("prompt") String prompt) { System.out.println("---come in chat"); return Flux.create(emitter -> { streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler() { @Override public void onPartialResponse(String partialResponse) { emitter.next(partialResponse); } @Override public void onCompleteResponse(ChatResponse completeResponse) { emitter.complete(); } @Override public void onError(Throwable throwable) { emitter.error(throwable); } }); }); } /*可以看到Token的用量和响应式间等信息*/ @GetMapping(value = "/chatstream/chat2") public void chat2(@RequestParam(value = "prompt", defaultValue = "南京有什么好吃的") String prompt) { System.out.println("---come in chat2"); streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler() { @Override public void onPartialResponse(String partialResponse) { System.out.println(partialResponse); } @Override public void onCompleteResponse(ChatResponse completeResponse) { System.out.println("---response over: " + completeResponse); } @Override public void onError(Throwable throwable) { throwable.printStackTrace(); } }); } //高阶API @GetMapping(value = "/chatstream/chat3") public Flux<String> chat3(@RequestParam(value = "prompt", defaultValue = "杭州有什么好吃的") String prompt) { System.out.println("---come in chat3"); return chatAssistant.chatFlux(prompt); } }

4.记忆化工程

与大模型的对话中,每次prompt都会被大模型记住,相当于记忆对话,增强用户体验。

LangChain提供了两种现成的实现,有两种淘汰策略。

两种主要的ChatMemory实现类,MessageWindowChatMemory和TokenWindowChatMemory。
一定要设置响应的字节编码:

server: port: 9008 servlet: encoding: charset: utf-8 enabled: true force: true

新建ChatMemoryAssistant接口需要传入两个参数,用户Id和用户提示词:

public interface ChatMemoryAssistant { String chatWithMemory(@MemoryId Long userId, @UserMessage String message); }

两种主要的ChatMemory实现类,MessageWindowChatMemory和TokenWindowChatMemory。

@Configuration public class LLMConfig { //低阶api @Bean(name = "qwen") public ChatModel chatModelQwen() { return OpenAiChatModel.builder() .apiKey(System.getenv("qwen-api")) .modelName("qwen-long") .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1") .build(); } //高阶API @Bean(name = "chat") public ChatAssistant chatAssistant(ChatModel chatModel) { return AiServices.create(ChatAssistant.class, chatModel); } @Bean(name = "chatWithMemory") public ChatMemoryAssistant chatMemoryAssistant(ChatModel chatModel) { return AiServices //另外一个高阶API创建大模型,不再用create .builder(ChatMemoryAssistant.class) .chatModel(chatModel) //按照memoryId对应创建一个chatMemory,函数接口用Lambda表达式,窗口里面最多保留多少条聊天记录 .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(100)) .build(); } @Bean(name = "chatWithToken") public ChatMemoryAssistant chatTokenAssistant(ChatModel chatModel) { //TokenCountEstiMator默认的token分词器,需要结合TokenSizer计算ChatMessage的token数量 TokenCountEstimator estimator = new OpenAiTokenCountEstimator("gpt-4"); return AiServices .builder(ChatMemoryAssistant.class) .chatModel(chatModel) .chatMemoryProvider(memoryId -> TokenWindowChatMemory.withMaxTokens(1000, estimator)) .build(); } }

controller层中测试:

@RestController public class ChatMemoryController { @Resource(name = "chat") private ChatAssistant chatAssistant; @Resource(name = "chatMessageWindowChatMemory") private ChatMemoryAssistant chatMessageWindowChatMemory; @Resource(name = "chatTokenWindowChatMemory") private ChatMemoryAssistant chatTokenWindowChatMemory; //没有实现记忆功能 @GetMapping(value = "/chatmemory/test1") public String chat() { String answer1 = chatAssistant.chat("你好,我的名字叫呵呵"); System.out.println("answer1返回结果:" + answer1); String answer2 = chatAssistant.chat("我的名字是什么"); System.out.println("answer2返回结果:" + answer2); return "success : " + DateUtil.now() + "<br> \n\n answer1: " + answer1 + "<br> \n\n answer2: " + answer2; } @GetMapping(value = "/chatmemory/test2") public String chatMessageWindowChatMemory() { chatMessageWindowChatMemory.chatWithMemory(1L, "你好!我的名字是Java."); String answer1 = chatMessageWindowChatMemory.chatWithMemory(1L, "我的名字是什么"); System.out.println("answer1返回结果:" + answer1); chatMessageWindowChatMemory.chatWithMemory(3L, "你好!我的名字是C++"); String answer2 = chatMessageWindowChatMemory.chatWithMemory(3L, "我的名字是什么"); System.out.println("answer2返回结果:" + answer2); return "chatMessageWindowChatMemory success : " + DateUtil.now() + "<br> \n\n answer1: " + answer1 + "<br> \n\n answer2: " + answer2; } @GetMapping(value = "/chatmemory/test3") public String chatTokenWindowChatMemory() { chatTokenWindowChatMemory.chatWithMemory(1L, "你好!我的名字是mysql"); String answer1 = chatTokenWindowChatMemory.chatWithMemory(1L, "我的名字是什么"); System.out.println("answer1返回结果:" + answer1); chatTokenWindowChatMemory.chatWithMemory(3L, "你好!我的名字是oracle"); String answer2 = chatTokenWindowChatMemory.chatWithMemory(3L, "我的名字是什么"); System.out.println("answer2返回结果:" + answer2); return "chatTokenWindowChatMemory success : " + DateUtil.now() + "<br> \n\n answer1: " + answer1 + "<br> \n\n answer2: " + answer2; } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 6:58:04

youtube-dl 多平台视频下载工具

文章目录youtube-dl 多平台视频下载工具youtube-dl 多平台视频下载工具 ytdl-org 开源的视频下载工具 youtube-dl&#xff0c;累计获得 140272 个 Star。 youtube-dl 是一个命令行程序&#xff0c;用于从 YouTube 及其他数百个视频网站下载视频内容。它基于 Python 开发&#…

作者头像 李华
网站建设 2026/5/26 6:53:44

番茄小说下载器终极指南:快速构建个人数字图书馆的完整教程

番茄小说下载器终极指南&#xff1a;快速构建个人数字图书馆的完整教程 【免费下载链接】Tomato-Novel-Downloader 番茄小说下载器不精简版 项目地址: https://gitcode.com/gh_mirrors/to/Tomato-Novel-Downloader 番茄小说下载器是一款基于Rust开发的高性能跨平台小说下…

作者头像 李华
网站建设 2026/5/26 6:52:29

从V2000到V3000:手把手教你用Python处理Mol/SDF文件中的‘版本陷阱’

从V2000到V3000&#xff1a;Python解析化学结构文件的版本兼容实战化学信息学领域最基础也最令人头疼的问题之一&#xff0c;就是处理不同版本的Mol/SDF文件格式。当你从PubChem下载了500个化合物数据&#xff0c;脚本却因为遇到V3000格式而崩溃时&#xff0c;这种痛苦我深有体…

作者头像 李华
网站建设 2026/5/26 6:47:32

AI视觉赋能智慧矿山:新一代安全防控体系解决方案

摘要&#xff1a;传统矿山安全防控高度依赖人工巡检与被动监控&#xff0c;存在预警滞后、盲区较多、人为误差大等痛点&#xff0c;难以应对井下复杂安全风险。为解决矿山安全事故高发、隐患难排查等行业难题&#xff0c;本文提出基于AI视觉的智慧矿山安全防控解决方案&#xf…

作者头像 李华