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的原生接口ChatModelListenerSlf4j 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) { } }同样地在大模型配置类中注意官网要求配置多个监听器所以用ListBean(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.version1.0.1-beta6/langchain4j-community.version在管理版本依赖中加入dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-community-bom/artifactId version${langchain4j-community.version}/version typepom/type scopeimport/scope /dependency子工程里!--DashScope (Qwen)接入阿里云百炼平台 https://docs.langchain4j.dev/integrations/language-models/dashscope -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-community-dashscope-spring-boot-starter/artifactId /dependencyChatModel是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); ResponseImage 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 groupIddev.langchain4j/groupId artifactIdlangchain4j-reactor/artifactId /dependency在ChatAssistant接口中public interface ChatAssistant { String chat(String prompt); FluxString 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 FluxString 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 FluxString 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; } }