网站首页 > 技术文章 正文
学习在 Java 中使用 Spring Boot、LangChain 和 Hilla 构建 ChatGPT 克隆。涵盖同步聊天完成和高级流式处理完成。
许多用于 AI 应用程序开发的库主要是用 Python 或 JavaScript 编写的。好消息是,其中一些库也具有 Java API。在本教程中,我将向您展示如何使用 Spring Boot、LangChain 和 Hilla 构建 ChatGPT 克隆。
本教程将介绍简单的同步聊天完成和更高级的流式处理完成,以获得更好的用户体验。
已完成的源代码
您可以在我的 GitHub 存储库中找到该示例的源代码。
要求
- Java 17+
- Node 18+
- An OpenAI API key in an environment variableOPENAI_API_KEY
创建一个 Spring Boot 和 React 项目,添加 LangChain
首先,使用 Hilla CLI 创建一个新的 Hilla 项目。这将创建一个带有 React 前端的 Spring Boot 项目。
npx @hilla/cli init ai-assistant
在 IDE 中打开生成的项目。然后,将 LangChain4j 依赖项添加到文件中:pom.xml
.XML
<dependency>br
<groupId>dev.langchain4j</groupId>br
<artifactId>langchain4j</artifactId>br
<version>0.22.0</version> <!-- TODO: use latest version -->br
</dependency>
使用 LangChain 使用内存完成简单的 OpenAI 聊天
我们将通过简单的同步聊天完成开始探索 LangChain4j。在本例中,我们希望调用 OpenAI 聊天完成 API 并获得单个响应。我们还希望跟踪多达 1,000 个聊天记录的令牌。
在包中,创建一个包含以下内容的类:com.example.application.serviceChatService.java
@BrowserCallablebr
@AnonymousAllowedbr
public class ChatService {br
br
@Value("${openai.api.key}")br
private String OPENAI_API_KEY;br
br
private Assistant assistant;br
br
interface Assistant {br
String chat(String message);br
}br
br
@PostConstructbr
public void init() {br
var memory = TokenWindowChatMemory.withMaxTokens(1000, new OpenAiTokenizer("gpt-3.5-turbo"));br
assistant = AiServices.builder(Assistant.class)br
.chatLanguageModel(OpenAiChatModel.withApiKey(OPENAI_API_KEY))br
.chatMemory(memory)br
.build();br
}br
br
public String chat(String message) {br
return assistant.chat(message);br
}br
}
- @BrowserCallable使该类可用于前端。
- @AnonymousAllowed允许匿名用户调用这些方法。
- @Value从环境变量中注入 OpenAI API 密钥。OPENAI_API_KEY
- Assistant是我们将用于调用聊天 API 的接口。
- init()使用 1,000 个令牌的内存和模型初始化助手。gpt-3.5-turbo
- chat()是我们将从前端调用的方法。
通过在 IDE 中运行或使用默认的 Maven 目标来启动应用程序:Application.java
mvn
This will generate TypeScript types and service methods for the front end.
Next, open in the folder and update it with the following content:App.tsxfrontend
TypeScript-JSX
export default function App() {br
const [messages, setMessages] = useState<MessageListItem[]>([]);br
br
async function sendMessage(message: string) {br
setMessages((messages) => [br
...messages,br
{br
text: message,br
userName: "You",br
},br
]);br
br
const response = await ChatService.chat(message);br
setMessages((messages) => [br
...messages,br
{br
text: response,br
userName: "Assistant",br
},br
]);br
}br
br
return (br
<div className="p-m flex flex-col h-full box-border">br
<MessageList items={messages} className="flex-grow" />br
<MessageInput onSubmit={(e) => sendMessage(e.detail.value)} />br
</div>br
);br
}
- 我们使用 Hilla UI 组件库中的 和 组件。MessageListMessageInput
- sendMessage()将消息添加到消息列表中,并调用该类的方法。收到响应后,该响应将添加到消息列表中。chat()ChatService
您现在有一个使用 OpenAI 聊天 API 并跟踪聊天历史记录的工作聊天应用程序。它非常适合短消息,但对于长答案来说很慢。为了改善用户体验,我们可以改用流式处理完成,在收到响应时显示响应。
使用 LangChain 将 OpenAI 聊天完成与内存进行流式处理
让我们更新类以改用流式处理完成:ChatService
@BrowserCallablebr
@AnonymousAllowedbr
public class ChatService {br
br
@Value("${openai.api.key}")br
private String OPENAI_API_KEY;br
private Assistant assistant;br
br
interface Assistant {br
TokenStream chat(String message);br
}br
br
@PostConstructbr
public void init() {br
var memory = TokenWindowChatMemory.withMaxTokens(1000, new OpenAiTokenizer("gpt-3.5-turbo"));br
br
assistant = AiServices.builder(Assistant.class)br
.streamingChatLanguageModel(OpenAiStreamingChatModel.withApiKey(OPENAI_API_KEY))br
.chatMemory(memory)br
.build();br
}br
br
public Flux<String> chatStream(String message) {br
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();br
br
assistant.chat(message)br
.onNext(sink::tryEmitNext)br
.onComplete(sink::tryEmitComplete)br
.onError(sink::tryEmitError)br
.start();br
br
return sink.asFlux();br
}br
}
代码与以前基本相同,但有一些重要区别:
- Assistant现在返回 a 而不是 .TokenStreamString
- init()用代替 .streamingChatLanguageModel()chatLanguageModel()
- chatStream()返回 a 而不是 .Flux<String>String
使用以下内容进行更新:App.tsx
打字稿-JSX
export default function App() {br
const [messages, setMessages] = useState<MessageListItem[]>([]);br
br
function addMessage(message: MessageListItem) {br
setMessages((messages) => [...messages, message]);br
}br
br
function appendToLastMessage(chunk: string) {br
setMessages((messages) => {br
const lastMessage = messages[messages.length - 1];br
lastMessage.text += chunk;br
return [...messages.slice(0, -1), lastMessage];br
});br
}br
br
async function sendMessage(message: string) {br
addMessage({br
text: message,br
userName: "You",br
});br
br
let first = true;br
ChatService.chatStream(message).onNext((chunk) => {br
if (first && chunk) {br
addMessage({br
text: chunk,br
userName: "Assistant",br
});br
first = false;br
} else {br
appendToLastMessage(chunk);br
}br
});br
}br
br
return (br
<div className="p-m flex flex-col h-full box-border">br
<MessageList items={messages} className="flex-grow" />br
<MessageInput onSubmit={(e) => sendMessage(e.detail.value)} />br
</div>br
);br
}
模板与以前相同,但我们处理响应的方式不同。我们不是等待收到响应,而是开始侦听响应的块。当收到第一个块时,我们将其添加为新消息。当收到后续块时,我们将它们附加到最后一条消息中。
重新运行应用程序,您应该会看到响应在收到时显示。
结论
正如你所看到的,LangChain使得在Java和Spring Boot中构建LLM驱动的AI应用程序变得容易。
完成基本设置后,您可以按照本文前面链接的 LangChain4j GitHub 页面上的示例,通过链接操作、添加外部工具等来扩展功能。在 Hilla 文档中了解有关 Hilla 的更多信息。
原文标题:AI in Java: Building a ChatGPT Clone With Spring Boot and LangChain
原文链接:https://dzone.com/articles/ai-in-java-building-a-chatgpt-clone-with-spring-bo
作者:Marcus Hellberg
编译:LCR
猜你喜欢
- 2024-11-15 CSS3+JS实现静态圆形进度条(css 圆形进度条)
- 2024-11-15 前端必读榜——如何在React中用SpreadJS导入/导出Excel文件
- 2024-11-15 Svelte教程翻译(六、生命周期)(servelet生命周期阶段)
- 2024-11-15 uniapp(Vue) 实现可粘贴的 个性化验证码 输入框
- 2024-11-15 国产开源,GoLang也可以高效处理Excel的利器了
- 2024-11-15 css精髓:这些布局你都学废了吗?(css布局技术)
- 2024-11-15 仅用18行JavaScript实现一个倒数计时器
- 2024-11-15 Web上的图片技巧「前端篇」(web网页图片)
- 2024-11-15 55个JS代码让你轻松当大神(完整的js代码)
- 2024-11-15 导入vue的html、css、JavaScript的仿抖音的文字时钟代码解析
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-