使用 LangChain4j ChatMemory 的生成式 AI 对话
在本文中,我们将探讨以下内容:
如何使用LangChain4j ChatMemory和ConversationalChain实现对话式交互?
如何使用 PromptTemplate 提问?
示例代码存储库
可以在 GitHub 存储库中找到本文的示例代码
LangChain4j 教程系列
您可以查看本系列中的其他文章:
我假设您已经创建了一个 Java 项目,其对pom.xml具有以下依赖性:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>0.27.1</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>0.27.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.0</version>
</dependency>
ChatMemory需要什么?
一般来说,当我们人类进行对话时,我们会记住对话的背景,并用它来继续对话。
请看以下示例:
Person1: What are all the movies directed by Quentin Tarantino?
Person2: Pulp Fiction, Kill Bill, etc.
Person1: How old is he?
Person2: He is 60 years old.
在上面的对话中,Person2 会记住对话的上下文 并理解“他多大了?”问题中的“他”指的是昆汀·塔伦蒂诺。
让我们尝试使用 LangChain4j OpenAI LLM 实现上述对话。
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
public class OpenAIChatMemoryDemo {
public static void main(String[] args) {
String openAiKey = "demo";
//String openAiKey = System.getenv("OPENAI_API_KEY");
ChatLanguageModel model = OpenAiChatModel.withApiKey(openAiKey);
String answer = model.generate("What are all the movies directed by Quentin Tarantino?");
System.out.println(answer); // Pulp Fiction, Kill Bill, etc.
answer = model.generate("How old is he?");
System.out.println(answer);
}
}
运行上述代码时,将获得类似于以下内容的输出:
Answer 1: I'm sorry, I cannot answer that question without more context or information about the person you are referring to.
Answer 2: I'm sorry, without more context I am unable to determine who "he" is or his age. Can you please provide more information?
...
如您所见,ChatLanguageModel 不记得对话的上下文。
LangChain4j 提供了一种记住对话上下文的方法 使用 ConversationalChain 和 ChatMemory。
如何将ConversationalChain与ChatMemory一起使用?
来自 ConversationalChain 的 JavaDocs:
用于与指定的 ChatLanguageModel 进行对话的链,同时保持 对话。包括一个默认的 ChatMemory(最多包含 10 条消息的消息窗口), 可以覆盖。建议改用 AiServices,因为它功能更强大。
让我们看看如何将ConversationalChain与ChatMemory一起使用。
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
public class OpenAIChatMemoryDemo {
public static void main(String[] args) {
String openAiKey = "demo";
//String openAiKey = System.getenv("OPENAI_API_KEY");
ChatLanguageModel model = OpenAiChatModel.withApiKey(openAiKey);
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(20);
//ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(300, new OpenAiTokenizer(GPT_3_5_TURBO));
ConversationalChain chain = ConversationalChain.builder()
.chatLanguageModel(model)
.chatMemory(chatMemory)
.build();
String answer = chain.execute("What are all the movies directed by Quentin Tarantino?");
System.out.println(answer); // Pulp Fiction, Kill Bill, etc.
answer = chain.execute("How old is he?");
System.out.println(answer); // Quentin Tarantino was born on March 27, 1963, so he is currently 58 years old.
}
}
我们使用 MessageWindowChatMemory 创建了一个 ChatMemory 实例,最多包含 20 条消息。 除了使用 MessageWindowChatMemory,还可以使用具有指定最大令牌限制的 TokenWindowChatMemory。
然后,我们使用聊天模型和聊天记忆创建了 ConversationalChain 的实例。 现在,当您运行上述代码时,您将获得类似于以下内容的输出:
Reservoir Dogs (1992), Pulp Fiction (1994), Jackie Brown (1997), ...
Quentin Tarantino was born on March 27, 1963, so he is currently 58 years old.
因此,ConversationalChain 会记住对话的上下文并提供正确的答案。
如果你是一个敏锐的观察者,你可能已经注意到第二个问题“他多大了?”并没有提供昆汀·塔伦蒂诺的确切年龄。 它根据模型训练的出生日期和日期计算年龄,而不是基于当前日期。
您可以通过提供更多上下文来优化问题以获取确切的年龄。
answer = chain.execute("How old is he as of "+ LocalDate.now() + "?");
System.out.println(answer); //As of February 21, 2024, Quentin Tarantino would be 60 years old.
现在您将获得昆汀·塔伦蒂诺的正确年龄。 虽然您可以执行字符串连接来提供上下文,但 LangChain4j 提供了一种使用 PromptTemplate 提问的更好方法。
如何使用 PromptTemplate 提问?
PromptTemplate 是一种使用预定义模板(可选)使用占位符提问的方法。
ConversationalChain chain = ConversationalChain.builder()
.chatLanguageModel(model)
.chatMemory(chatMemory)
.build();
String answer = chain.execute("What are all the movies directed by Quentin Tarantino?");
System.out.println(answer); // Pulp Fiction, Kill Bill, etc.
Prompt prompt = PromptTemplate.from("How old is he as of {{current_date}}?").apply(Map.of());
answer = chain.execute(prompt.text());
System.out.println(answer); //As of February 21, 2024, Quentin Tarantino would be 60 years old.
我们使用 PromptTemplate 创建了一个带有占位符 {{current_date}} 的 Prompt 实例。 但是,我们没有传递 current_date 的值,因为特殊变量 {{current_date}}、{{current_time}} 和 {{current_date_time}} 分别自动填充 LocalDate.now()、LocalTime.now() 和 LocalDateTime.now()。
如果有其他占位符,则可以使用 Map 传递值。
Prompt prompt = PromptTemplate
.from("How old is {{name}} as of {{current_date}}?")
.apply(Map.of("name","Quentin Tarantino"));
PromptTemplates 提供了许多其他功能,我们将在以后的文章中探讨它们。
手动将消息添加到 ChatMemory
您也可以在不使用 ConversationalChain 的情况下手动将消息添加到 ChatMemory,
ChatLanguageModel model = OpenAiChatModel.withApiKey(openAiKey);
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(20);
chatMemory.add(UserMessage.userMessage("What are all the movies directed by Quentin Tarantino?"));
AiMessage answer = model.generate(chatMemory.messages()).content();
System.out.println(answer.text()); // Pulp Fiction, Kill Bill, etc.
chatMemory.add(answer);
chatMemory.add(UserMessage.userMessage("How old is he?"));
AiMessage answer2 = model.generate(chatMemory.messages()).content();
System.out.println(answer2.text()); // Quentin Tarantino was born on March 27, 1963, so he is currently 58 years old.
chatMemory.add(answer2);
我想不出您需要手动将消息添加到 ChatMemory 而不是 使用 ConversationalChain,但有这样的选项。
结论
在本文中,我们了解了如何使用 ConversationalChain 和 ChatMemory 来记住 对话的上下文。
从 ConversationalChain 的 JavaDocs 来看,建议改用 AiServices,因为它更强大。 在下一篇文章中,我们将探讨 AiServices 及其功能。