使用langchain4j调用大模型写个聊天助手

使用langchain4j调用大模型写个聊天助手

    正在检查是否收录...

LangChain4j是一款基于Java的高效、灵活的AI大模型应用框架,专为简化Java应用程序与LLMs(大语言模型)的集成而设计。它提供统一API和模块化设计,支持多种LLM提供商和嵌入模型,以及丰富的工具箱,如AI服务和RAG(检索增强生成)。LangChain4j通过简化集成过程,降低开发成本,助力开发者快速构建和部署AI应用。langchain4j还提供了openAI部分接口免费测试的能力,可以在没有key的情况下学习使用大模型。


UI代码在文末

1、导入相关包

<!-- openai包 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>0.32.0</version> </dependency> <!-- 高级工具包,如ai service --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>0.32.0</version> </dependency> <!-- 日志工具 --> <dependency> <groupId>org.tinylog</groupId> <artifactId>tinylog-impl</artifactId> <version>2.6.2</version> </dependency> <dependency> <groupId>org.tinylog</groupId> <artifactId>slf4j-tinylog</artifactId> <version>2.6.2</version> </dependency> 

langchain4j是一个还在完善的库,最新版本请查看官网: Get Started | LangChain4j

jdk版本最好使用17,因为它还有一个基于 Spring Boot 3.2 版本,它最低支持jdk17

2、helloword入门

import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.openai.OpenAiChatModel; .... public static void main(String[] args) { ChatLanguageModel model = OpenAiChatModel.withApiKey("demo"); String answer = model.generate("你是谁"); System.out.println("answer:" + answer); } 

控制台会输出:

answer:我是一个智能助手,可以回答您的问题并提供帮助。您有什么需要我帮忙的吗?

代码里的 demo 是langchain4j给开发者免费使用可调用openai模型(gpt-3.5-turbo)的key,不要在生产环境使用哦。

如果引入的是智普包,那么使用ZhipuAiChatModel替换即可

如果你有自己的key,代码如下配置:

 public static void main(String[] args) { // 替换成你自己的URL和key就行 ChatLanguageModel model = OpenAiChatModel.builder() .baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build(); String answer = model.generate("你是谁,我是小飞"); System.out.println("answer:" + answer); String answer2 = model.generate("请问我是谁"); System.out.println("answer2:" + answer2); } 

控制台输出:

answer:很高兴认识你,小飞。我是一个智能助手,可以回答你的问题和提供帮助。有什么我可以帮助你的吗?
answer2:很抱歉,我无法知道您是谁,因为我是一个虚拟助手,无法直接获取您的身份信息。您可以介绍一下自己,让我更了解您吗?

为什么会这样呢?

由于大模型是无状态的,你要让它知道上下文信息,则需要把你们的会话历史记录也发给它,否则每次都是一次新的会话。langchain4j可以使用ChatMemory管理会话。

3、AiServices和ChatMessage

AiServices 封装了与 LLM 交互的复杂性,使得开发者能够以更自然、更面向对象的方式来与 LLMs 进行交互。

ChatMessage是对话中的一个消息的抽象表示,它包含了4个实现类:UserMessage(用户消息)、AiMessage(大模型回复消息)、SystemMessage(系统消息,如应用的角色和能力)、ToolExecutionResultMessage(用于调用本地应用,可扩展大模型能力)

package org.example; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.service.*; public class Test { interface Writer{ @SystemMessage("请扮演一名作家,根据输入的文章题目写一篇{{num}}字以内的作文") String write(@UserMessage String text, @V("num") int num); } public static void main(String[] args) { ChatLanguageModel chatLanguageModel = OpenAiChatModel.builder() .baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build(); // 使用代理模式创建一个作家对象 Writer writer = AiServices.create(Writer.class, chatLanguageModel); String content = writer.write("我的爸爸", 100); System.out.println(content); } } 

控制台输出:

我的爸爸是我生命中最重要的人。他不仅是我生活中的导师,还是我永远的支持者。每当我遇到困难时,他总是在我身边给予我鼓励和指导。他教会我坚强,教会我勇敢,也教会我如何面对生活的挑战。他是我心中的英雄,是我无法取代的存在。我爱我的爸爸,他是我生命中最坚定的依靠,也是我永远的骄傲。

4、使用ChatMemory管理上下文

4.1 两种ChatMemory的使用方式

ChatMemory主要用于管理和维护聊天过程中的消息记忆,使得大型语言模型(LLM)能够模拟记住对话上下文的能力

LangChain4j 提供了两种 ChatMemory 的实现方式,如MessageWindowChatMemory 和 TokenWindowChatMemory 。这些实现方式可以根据不同的需求进行选择:

MessageWindowChatMemory:保留最新的 N 条消息并删除旧消息。由于每条消息可以包含不同数量的令牌,因此这种实现方式主要用于快速原型设计。 TokenWindowChatMemory:保留最新的 N 个令牌,并根据配置的token删除较旧的消息,能够更精确地控制上下文窗口的大小。
package org.example; import dev.langchain4j.memory.ChatMemory; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.service.*; public class Test2 { interface NamingMaster { String talk(String desc); } public static void main(String[] args) { ChatLanguageModel model = OpenAiChatModel.builder() .apiKey("demo").baseUrl("http://langchain4j.dev/demo/openai/v1").build(); // 最多保存10条会话 ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10); NamingMaster namingMaster = AiServices.builder(NamingMaster.class) .chatLanguageModel(model).chatMemory(chatMemory).build(); System.out.println(namingMaster.talk("帮我取一个很有中国文化内涵的女孩名字")); System.out.println("-----------------"); System.out.println(namingMaster.talk("换一个")); } } 

控制台输出:

岚儿 (Lánér)

-----------------

芷若 (Zhǐruò)

由于使用了ChatMemory管理会话,大模型就理解了 “换一个” 的含义。

如果想指定最大的token数,如下:

// 设置最大token数为1000 ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(1000, new OpenAiTokenizer("gpt-3.5-turbo")); 

token的计算是与模型有关的,所以这里要指定当前使用的是哪个模型。

4.2 memoryId的使用

如果应用需要多个人使用,每个人都要有自己的会话,否则上下文就串了,可以使用memoryId来做区分。

package org.example; import dev.langchain4j.memory.chat.TokenWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.model.openai.OpenAiTokenizer; import dev.langchain4j.service.*; public class Test3 { interface NamingMaster { String talk(@MemoryId String userId, @UserMessage String desc); } public static void main(String[] args) { ChatLanguageModel model = OpenAiChatModel.builder() .apiKey("demo").baseUrl("http://langchain4j.dev/demo/openai/v1").build(); NamingMaster namingMaster = AiServices.builder(NamingMaster.class) .chatLanguageModel(model) .chatMemoryProvider(uerId -> TokenWindowChatMemory .builder().id(uerId).maxTokens(1000, new OpenAiTokenizer("gpt-3.5-turbo")).build()) .build(); // 未设置memoryId 默认为 default System.out.println(namingMaster.talk("user1", "帮我取一个很有中国文化内涵的女孩名字")); System.out.println("-----------------"); System.out.println(namingMaster.talk("user2", "换一个")); } } 

控制台输出:

岚娜 (Lan Na)

-----------------

换一个什么?请问你需要什么样的帮助呢?

可以看到user2给大模型发消息: “换一个” ,大模型当做了一个新的会话了,这样就与user1隔离了。

5、大模型与外部系统交互

当大模型需要调用外部工具来获取特定信息或执行特定任务时,需要使用Tool,它提供了这些工具的必要信息,使得大模型能够理解工具,然后正确地调用它 。

使用场景

例如,在构建聊天机器人或智能助手时,大模型可能需要调用天气API来获取实时天气信息,或者调用数据库查询API来获取用户数据。在这些情况下,Tool可以帮助大模型准确地描述和调用这些外部工具。

package org.example; import dev.langchain4j.agent.tool.Tool; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.service.*; import java.time.LocalDateTime; public class Test4 { interface Assistant { String chat(String userMessage); } static class MyTools { @Tool("获取当前日期") public static String dateUtil(String onUse) { return LocalDateTime.now().toString(); } } public static void main(String[] args) { // 上面测试的key在此处不支持 ChatLanguageModel model = OpenAiChatModel.builder() .apiKey("your key").baseUrl("your base url").build(); Assistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(model).tools(new MyTools()) .chatMemory(MessageWindowChatMemory.withMaxMessages(10)).build(); String date = assistant.chat("获取今天的日期"); System.out.println(date); String date2 = assistant.chat("获取后天的日期"); System.out.println(date2); } } 
gpt-3.5-turbo本身是无法获取今天的日期的

控制台输出:

今天:今天的日期是2024年7月11日。
2024-07-11 18:20:24 [main] dev.langchain4j.agent.tool.DefaultToolExecutor.execute()
DEBUG: About to execute ToolExecutionRequest { id = “call_BjJCXo78e6NeRrMrScp0A3Ip”, name = “dateUtil”, arguments = “{“arg0”:“after tomorrow”}” } for memoryId default
2024-07-11 18:20:24 [main] dev.langchain4j.agent.tool.DefaultToolExecutor.execute()
DEBUG: Tool execution result: 2024-07-11T18:20:24.931279900
后天:后天的日期是2024年7月13日。

注意 工具 相关的 demo key不支持,包括Streaming和生成图片同样也不支持,需要申请自己的key。

其他工具相关demo或者其他api请移步官方demo:langchain4j demo

6、生成图片

使用ImageModel调用调用大模型可以生成图片。

package org.example; import dev.langchain4j.data.image.Image; import dev.langchain4j.model.image.ImageModel; import dev.langchain4j.model.openai.OpenAiImageModel; import dev.langchain4j.model.output.Response; public class Test5 { public static void main(String[] args) { ImageModel model = OpenAiImageModel.builder().baseUrl("your base url").apiKey("your key").build(); Response<Image> response = model.generate("小狗在森林里觅食"); System.out.println(response.content().url()); } } 

会返回一个临时链接,也可返回base64(response.content().base64Data())

7、其他国内大模型

国内的智谱 https://www.zhipuai.cn/

<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-zhipu-ai</artifactId> <version>0.32.0</version> </dependency> 

国内的百度

<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-qianfan</artifactId> <version>0.32.0</version> </dependency> 

查看官方其他支持大模型: Language Models | LangChain4j

8、写个应用

接下来,使用以上知识来做一个聊天助手:

8.1 配置spring boot相关包和配置文件

<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId> <version>0.32.0</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> <version>0.32.0</version> </dependency> 

application.properties

langchain4j.open-ai.chat-model.api-key=填写你的key langchain4j.open-ai.chat-model.base-url=填写你的URL # langchain4j.open-ai.streaming-chat-model.api-key= server.port=9000 server.servlet.context-path=/ai 

8.2 控制器ChatController

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Map; @RestController public class ChatController { ChatService chatService; @Autowired ChatController(ChatService chatService) { this.chatService = chatService; } @PostMapping("/reply") public String replyAssistant(@RequestBody Map<String, String> map) { String uid = map.get("uid"); String msg = map.get("msg"); System.out.println("userId:" + uid + " msg:" + msg); String result = chatService.speak(uid, msg); System.out.println(result); return result; } } 

8.3 接口ChatMaster

小应用体验好不好,除了大模型本身的能力外,关键在于提示词,通过优化提示词可以让大模型程序更智能。提示词我常用如下4种优化方式:

1、给大模型一个角色。比如你想让大模型生成孩子能理解的答案,可以告诉大模型它是一位幼儿园老师;

2、给大模型一个参考示例。如果你需要的格式是有要求的,比如每行需要有emoji表情包,那你可以把一个完成的示例发给大模型,让它参考输出;

3、让大模型一步一步解答。由于大模型是根据前面的提示词生成后面的提示词,对于逻辑比较复杂的问题时,往往出错,这个时候让它一步一步解答会有更好的结果;

4、告诉大模型它是提示词优化专家,让它帮你优化。

最后你要描述清楚你的问题,根据输出的结果不断调整提示词

import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; public interface ChatMaster { // 以下提示词仅供参考 @SystemMessage("你是一位恋爱大师,名叫贝贝。和你对话的是一位女孩,请根据上下文进行分析,然后以男生的角度进行回话。风格要幽默、有趣、体贴、温柔,适当扩展话题,让对话轻松愉快") String speak(@MemoryId String userId, @UserMessage String desc); } 

8.4 服务类ChatService

import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.service.AiServices; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class ChatService { private final ChatLanguageModel chatLanguageModel; @Autowired ChatService(ChatLanguageModel chatLanguageModel) { this.chatLanguageModel = chatLanguageModel; } private ChatMaster chatMaster; @PostConstruct public void init() { chatMaster = AiServices.builder(ChatMaster.class).chatLanguageModel(chatLanguageModel) // .chatMemoryProvider(uerId -> MessageWindowChatMemory.builder().id(uerId).maxMessages(20).build()) .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10)) .build(); } public String speak(String userId, String desc) { return chatMaster.speak(userId, desc); } } 

8.5 界面部分

前端vue3,使用uniapp开发,使用组件来显示头像,依赖了uni-ui,可以使用图片代替

<template> <view class="ai-container"> <view class="main"> <view v-for="(message, index) in messages" :class="['record-item',index%2===1?'reverse':'']"> <view class="portrait"> <!-- <image v-if="index%2===0" src="https://blog.csdn.net/dan_seek/static/img/icon-chatgpt.png"></image> --> <uni-icons v-if="index%2===0" type="chat-filled" size="30" color="#fff"></uni-icons> <uni-icons v-else type="person-filled" size="30" color="#fff"></uni-icons> </view> <view @longpress="handleLongPress(message)">{{message}}</view> </view> </view> <view class="foot-box"> <view class="text-box"> <textarea v-model="userMessage" placeholder="请输入女生给你的回复" /> </view> <view class="send-btn-box"> <button @click="sendMsg" type="primary" size="mini" :loading="loading">发送</button> </view> </view> </view> </template> <script setup> import { ref } from 'vue' const messages = ref(['我是一个帮你回复消息的AI,请把她回复给你的消息发给我,我帮你回复!长按可复制消息']) let uid = generateRandomString(8) let userMessage = ref('') let loading = ref(false) // 调用后台接口的函数 function sendMsg() { if(loading.value){ return } let userMessageVal = userMessage.value userMessage.value = '' if (!userMessageVal) { uni.showToast({ title: '请输入你的回复', icon: 'none' }) return } loading.value = true messages.value = [...messages.value, userMessageVal] uni.request({ url: 'https://xxx.xxx.cn/ai/reply', method: 'POST', data: { uid: uid, msg: userMessageVal }, success: (res) => { loading.value = false console.log('res:', res) if (res.statusCode === 200) { const aiMessage = res.data // 将新数据添加到数组中 messages.value = [...messages.value, aiMessage] } else { console.error('数据加载失败', res); } }, fail: (err) => { console.error('请求失败:', err); } }); } function handleLongPress(textToCopy) { uni.setClipboardData({ data: textToCopy, success: () => { uni.showToast({ title: '复制成功', icon: 'success' }); }, fail: () => { uni.showToast({ title: '复制失败', icon: 'none' }); } }); } function generateRandomString(length) { let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; const charactersLength = characters.length; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } </script> <script> export default { data() { return { } }, methods: { } } </script> <style lang="scss"> .ai-container { display: flex; flex-direction: column; background-color: #fafafa; height: 100vh; /* #ifdef H5 */ height: calc(100vh - 44px); /* #endif */ .main { flex: 1; overflow-y: auto; padding: 10px; margin-top: 5px; .record-item { display: flex; margin-bottom: 15px; &.reverse { flex-direction: row-reverse; .content, .portrait { background-color: $theme-color-rgba; } } .portrait { display: flex; justify-content: center; align-items: center; width: 40px; min-width: 40px; height: 40px; background-color: #e8e8e8; image { width: 30px; height: 30px; } } .content { display: flex; align-items: center; margin: 0 10px; padding: 5px 10px; font-size: 14px; background-color: white; border-radius: 5px; } } } .foot-box { display: flex; background-color: white; height: 120px; .text-box { display: flex; flex: 1; textarea { margin: 10px 0px 10px 15px; padding: 10px; height: 80px; width: 100%; font-size: 14px; background-color: #fafafa; border-radius: 5px; } } .send-btn-box { display: flex; align-items: center; justify-content: center; .uni-btn { margin: 0 15px; padding: 0; width: 60px; } } } } </style> 

购买的key没用完,可以扫码微微体验一下,无需注册,返回不了消息就是key用完了。请无视小程序本身的功能。

总结

chatlangchainopenai大模型stemtokenurlapiatsicon提示词assistant上下文gptllm2024writerjavagpt-3script
  • 本文作者:李琛
  • 本文链接: https://wapzz.net/post-19476.html
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
本站部分内容来源于网络转载,仅供学习交流使用。如涉及版权问题,请及时联系我们,我们将第一时间处理。
文章很赞!支持一下吧 还没有人为TA充电
为TA充电
还没有人为TA充电
0
  • 支付宝打赏
    支付宝扫一扫
  • 微信打赏
    微信扫一扫
感谢支持
文章很赞!支持一下吧
关于作者
2.3W+
5
0
1
WAP站长官方

如何确保AI写作质量上乘? 笔灵AI,强大模型支撑,精准高效

上一篇

Zed AI有哪些功能免费吗?人工智能代码生成怎么使用方法详细教程指南

下一篇
  • 复制图片
按住ctrl可打开默认菜单