当前位置:Java -> Java中的AI:使用Spring Boot和LangChain构建ChatGPT克隆
许多用于AI应用程序开发的库通常是用Python或JavaScript编写的。好消息是,其中有几个库也具有Java API。在本教程中,我将向您展示如何使用Spring Boot、LangChain和Hilla构建ChatGPT克隆。
本教程将涵盖简单的同步聊天完成和更高级的流式完成,以提供更好的用户体验。
您可以在我的GitHub存储库中找到示例的源代码。
OPENAI_API_KEY
环境变量中首先,使用Hilla CLI创建一个新的Hilla项目。这将创建一个带有React前端的Spring Boot项目。
npx @hilla/cli init ai-assistant
在您的IDE中打开生成的项目。然后,将LangChain4j依赖添加到pom.xml
文件中:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>0.22.0</version> <!-- TODO: use latest version -->
</dependency>
我们将从简单的同步聊天完成开始使用LangChain4j。在这种情况下,我们希望调用OpenAI聊天完成API并获得单个响应。我们还希望跟踪最多1000个聊天历史记录标记。
在com.example.application.service
包中,创建一个ChatService.java
类,内容如下:
@BrowserCallable
@AnonymousAllowed
public class ChatService {
@Value("${openai.api.key}")
private String OPENAI_API_KEY;
private Assistant assistant;
interface Assistant {
String chat(String message);
}
@PostConstruct
public void init() {
var memory = TokenWindowChatMemory.withMaxTokens(1000, new OpenAiTokenizer("gpt-3.5-turbo"));
assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(OpenAiChatModel.withApiKey(OPENAI_API_KEY))
.chatMemory(memory)
.build();
}
public String chat(String message) {
return assistant.chat(message);
}
}
@BrowserCallable
使该类可在前端使用。@AnonymousAllowed
允许匿名用户调用方法。@Value
从OPENAI_API_KEY
环境变量中注入OpenAI API密钥。Assistant
是我们将用于调用聊天API的接口。init()
用1000标记的内存和gpt-3.5-turbo
模型初始化助手。chat()
是我们将从前端调用的方法。通过在IDE中运行Application.java
或使用默认的Maven目标启动应用:
mvn
这将为前端生成TypeScript类型和服务方法。
接下来,打开frontend
文件夹中的App.tsx
并使用以下内容更新它:
export default function App() {
const [messages, setMessages] = useState<MessageListItem[]>([]);
async function sendMessage(message: string) {
setMessages((messages) => [
...messages,
{
text: message,
userName: "You",
},
]);
const response = await ChatService.chat(message);
setMessages((messages) => [
...messages,
{
text: response,
userName: "Assistant",
},
]);
}
return (
<div className="p-m flex flex-col h-full box-border">
<MessageList items={messages} className="flex-grow" />
<MessageInput onSubmit={(e) => sendMessage(e.detail.value)} />
</div>
);
}
MessageList
和MessageInput
组件。sendMessage()
将消息添加到消息列表,并在ChatService
类上调用chat()
方法。当接收到响应时,它将被添加到消息列表中。现在,您拥有一个使用OpenAI聊天API并跟踪聊天历史记录的工作聊天应用程序。它非常适合短消息,但对于长回答来说速度较慢。为了提高用户体验,我们可以使用流式完成,以显示响应的方式为其实时显示。
让我们更新ChatService
类,以使用流式完成:
@BrowserCallable
@AnonymousAllowed
public class ChatService {
@Value("${openai.api.key}")
private String OPENAI_API_KEY;
private Assistant assistant;
interface Assistant {
TokenStream chat(String message);
}
@PostConstruct
public void init() {
var memory = TokenWindowChatMemory.withMaxTokens(1000, new OpenAiTokenizer("gpt-3.5-turbo"));
assistant = AiServices.builder(Assistant.class)
.streamingChatLanguageModel(OpenAiStreamingChatModel.withApiKey(OPENAI_API_KEY))
.chatMemory(memory)
.build();
}
public Flux<String> chatStream(String message) {
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
assistant.chat(message)
.onNext(sink::tryEmitNext)
.onComplete(sink::tryEmitComplete)
.onError(sink::tryEmitError)
.start();
return sink.asFlux();
}
}
代码大部分与以前相同,但存在一些重要的区别:
Assistant
现在返回TokenStream
而不是String
。init()
使用streamingChatLanguageModel()
而不是 chatLanguageModel()
。chatStream()
返回Flux<String>
而不是String
。使用以下内容更新App.tsx
:
export default function App() {
const [messages, setMessages] = useState<MessageListItem[]>([]);
function addMessage(message: MessageListItem) {
setMessages((messages) => [...messages, message]);
}
function appendToLastMessage(chunk: string) {
setMessages((messages) => {
const lastMessage = messages[messages.length - 1];
lastMessage.text += chunk;
return [...messages.slice(0, -1), lastMessage];
});
}
async function sendMessage(message: string) {
addMessage({
text: message,
userName: "You",
});
let first = true;
ChatService.chatStream(message).onNext((chunk) => {
if (first && chunk) {
addMessage({
text: chunk,
userName: "Assistant",
});
first = false;
} else {
appendToLastMessage(chunk);
}
});
}
return (
<div className="p-m flex flex-col h-full box-border">
<MessageList items={messages} className="flex-grow" />
<MessageInput onSubmit={(e) => sendMessage(e.detail.value)} />
</div>
);
}
模板与以前相同,但是我们处理响应的方式不同。我们不会等待接收响应,而是开始监听响应的块。当收到第一个块时,我们将其添加为新消息。当接收到后续块时,我们将其附加到上一个消息。
重新运行应用程序,您会看到响应正在实时显示。
如您所见,LangChain使得在Java和Spring Boot中构建LLM动力的AI应用程序变得非常简单。
基本设置完成后,您可以通过链接本文中提到的LangChain4j GitHub页面上的示例来扩展功能,链式操作,添加外部工具等。在Hilla文档中了解更多有关Hilla的信息。
推荐阅读: 百度面经(26)
本文链接: Java中的AI:使用Spring Boot和LangChain构建ChatGPT克隆