LangChain4j AiServices 教程
在本文中,我们将探讨以下内容:
使用 LangChain4j AiServices 与 LLM 进行交互。
如何提出问题并将回答映射到不同的格式?
以不同的格式总结给定的文本。
分析给定文本的情绪。
示例代码存储库
可以在 GitHub 存储库中找到本文的示例代码
LangChain4j 教程系列
您可以查看本系列中的其他文章:
到目前为止,我们已经看到使用 chatLanguageModel.generate(question) 方法提问,该方法将响应返回为 String。 但是,使用 AiServices,我们可以创建一个 Java 接口,该接口以特定格式接收输入并返回输出。
使用 AiServices 与 LLM 交互
让我们看看如何使用 AiServices 与 LLM 进行交互。
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
public class AiServicesDemo {
interface Assistant {
String chat(String message);
}
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(2);
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.chatMemory(chatMemory)
.build();
String question = "What are all the movies directed by Quentin Tarantino?";
String answer = assistant.chat(question);
System.out.println("Answer: " + answer);
}
}
我们创建了一个名为 Assistant 的界面,其中包含一个方法聊天,该聊天接收消息并返回响应。 我们使用 AiServices.builder() 创建了 Assistant 的实例,并使用 ChatMemory 来记住对话的上下文。 LangChain4j 动态地为 Assistant 接口提供实现。
到目前为止,这看起来与以前的方法相比并没有好多少。 但是,当我们想要将请求和响应映射到特定格式时,AiServices 的真正力量就来了。
让我们再看一个例子。
interface FunnyAssistant {
@UserMessage("Tell me a joke about {{it}}")
String tellMeAJokeAbout(String subject);
}
FunnyAssistant funnyAssistant = AiServices.builder(FunnyAssistant.class)
.chatLanguageModel(model)
.build();
String answer = funnyAssistant.tellMeAJokeAbout("Python");
System.out.println("Joke about [Python]: " + answer);
//Joke about [Python]: Why do programmers prefer Python over C++? Because they don't like snakes... or brackets!
在此示例中,我们仅将主语作为输入传递,并使用@UserMessage注释形成完整的问题。 当我们查看客户端代码 funnyAssistant.tellMeAJokeAbout(“Python”)时, 它看起来像一个常规的方法调用,没有任何 AI 特定的逻辑。
由于只有一个参数,并且我们没有用 @V(“name”) 显式命名它,因此默认情况下它被命名为“it”。
让我们再看几个例子。
interface FunnyAssistant {
@UserMessage("Tell me a joke about {{it}}")
String tellMeAJokeAbout(String subject);
//@SystemMessage("You are a friendly, polite chat assistant")
@SystemMessage("You are a sarcastic and funny chat assistant")
String chat(String message);
@SystemMessage("You are an IT consultant who just replies \"It depends\" to every question")
String ask(String question);
}
FunnyAssistant funnyAssistant = AiServices.builder(FunnyAssistant.class)
.chatLanguageModel(model).build();
String answer = funnyAssistant.chat("Do you think you are smarter than humans?");
System.out.println("Answer: " + answer);
//Answer: Oh, definitely not. I mean, I may know a lot of random facts and have access to vast amounts of information, but I still can't tie my own shoelaces. So, I think humans have the upper hand on that one.
answer = funnyAssistant.ask("Do we need to use Microservices?");
System.out.println("Answer: " + answer);
//Answer: It depends
answer = funnyAssistant.ask("Is Node.js better than Python?");
System.out.println("Answer: " + answer);
//Answer: It depends
这很有趣:-)嘲笑 IT 顾问并让聊天助手讽刺。
但关键是,您可以使用@SystemMessage告诉 AI 模型有关助手的角色以及如何应对。 您也可以在同一方法上同时使用 @UserMessage 和 @SystemMessage。
总结给定文本
生成式 AI 的一个很好的用例是分析大文本并对其进行总结。 让我们看看如何使用 AiServices 总结给定的文本。
interface Summarizer {
@UserMessage("Give a summary of {{name}} in 3 bullet points using the following information:\n\n {{info}}")
String summarize(@V("name") String name, @V("info") String info);
}
Summarizer summarizer = AiServices.builder(Summarizer.class).chatLanguageModel(model).build();
String aboutSiva = """
Siva, born on 25 June 1983, is a software architect working in India.
He started his career as a Java developer on 26 Oct 2006 and worked with other languages like
Kotlin, Go, JavaScript, Python too.
He authored "Beginning Spring Boot 3" book with Apress Publishers.
He has also written "PrimeFaces Beginners Guide" and "Java Persistence with MyBatis 3" books with PacktPub.
""";
String answer = summarizer.summarize("Siva", aboutSiva);
System.out.println("[About Siva]: \n" + answer);
//[About Siva]:
//- Siva is a software architect born on 25 June 1983 in India, who started his career as a Java developer in 2006 and has experience working with other languages like Kotlin, Go, JavaScript, and Python.
//- He authored the book "Beginning Spring Boot 3" with Apress Publishers, as well as "PrimeFaces Beginners Guide" and "Java Persistence with MyBatis 3" with PacktPub.
//- Siva has a strong background in software development and has contributed to multiple books in the field of programming and software development.
在这个例子中,给出的文本被总结了,但对我来说,它看起来不是很令人印象深刻。
让我们看看如何以特定格式总结给定的文本。
interface Summarizer {
@UserMessage("""
Give a person summary in the following format:
Name: ...
Date of Birth: ...
Profession: ...
Books Authored: ...
Use the following information:
{{info}}
""")
String summarizeInFormat(@V("info") String info);
}
answer = summarizer.summarizeInFormat(aboutSiva);
System.out.println("[About Siva]:\n" + answer);
这给了我以下回应:
[About Siva]:
Name: Siva
Date of Birth: 25 June 1983
Profession: Software Architect
Books Authored:
1. "Beginning Spring Boot 3" with Apress Publishers
2. "PrimeFaces Beginners Guide" with PacktPub
3. "Java Persistence with MyBatis 3" with PacktPub
这看起来不错。我们可以要求 AI 模型以特定格式总结给定文本。
让我们尝试以 JSON 格式总结文本。
interface Summarizer {
@UserMessage("""
Summarize the the following information in JSON format having name, date of birth,
experience in years and books authored as keys:
{{info}}
""")
String summarizeAsJSON(@V("info") String info);
}
answer = summarizer.summarizeAsJSON(aboutSiva);
System.out.println("[About Siva]:\n" + answer);
这给了我以下回应:
[About Siva in JSON Format]: {
"name": "Siva",
"date_of_birth": "25 June 1983",
"experience_in_years": 15,
"books_authored": [
"Beginning Spring Boot 3 (Apress Publishers)",
"PrimeFaces Beginners Guide (PacktPub)",
"Java Persistence with MyBatis 3 (PacktPub)"
]
}
这很好,但到目前为止,我们收到的是字符串格式的响应。 如何将响应作为 Java 对象获取?让我们试试看。
record Person(String name,
LocalDate dateOfBirth,
int experienceInYears,
List<String> booksAuthored) {
}
interface Summarizer {
@UserMessage("""
Give a person summary as of {{current_date}}
Using the following information:
{{info}}
""")
Person summarizeAsBean(@V("info") String info);
}
Person person = summarizer.summarizeAsBean(aboutSiva);
System.out.println("[About Siva]:\n" + person);
这种方法成功地提取了详细信息并将值映射到人员记录中,并给了我以下响应:
[About Siva]:
Person[name=Siva, dateOfBirth=1983-06-25, experienceInYears=17, booksAuthored=[Beginning Spring Boot 3, PrimeFaces Beginners Guide, Java Persistence with MyBatis 3]]
使用 StructuredPrompt 将 Java 对象映射到 UserMessage
我们已经看到了将响应映射到不同格式的不同方法。 我们还可以使用@StructuredPrompt注解将作为 Java 对象传递的输入映射到用户消息,如下所示:
@StructuredPrompt("Give summary of {{name}} as of {{current_date}} using the following information:\n\n{{info}}")
record PersonSummaryPrompt(String name, String info) {
}
interface Summarizer {
Person summarize(PersonSummaryPrompt prompt);
}
PersonSummaryPrompt prompt = new PersonSummaryPrompt("Siva", aboutSiva);
Person person = summarizer.summarize(prompt);
System.out.println("[About Siva]\n: " + person);
这给了我以下回应:
[About Siva]:
Person[name=Siva, dateOfBirth=1983-06-25, experienceInYears=17, booksAuthored=[Beginning Spring Boot 3, PrimeFaces Beginners Guide, Java Persistence with MyBatis 3]]
情绪分析
生成式 AI 的另一个用例是情感分析。 让我们看看如何使用 AiServices 分析给定文本的情绪。
enum Sentiment {
POSITIVE, NEGATIVE, SARCASTIC
}
interface SentimentAnalyzer {
Sentiment analyze(String text);
}
SentimentAnalyzer sentimentAnalyzer = AiServices.builder(SentimentAnalyzer.class)
.chatLanguageModel(model).build();
Sentiment sentiment = analyzer.analyze("I love Java programming language");
System.out.println("Sentiment: " + sentiment);
//Sentiment: POSITIVE
sentiment = analyzer.analyze("I am sick of writing if err != nil for every 3rd line of code");
System.out.println("Sentiment: " + sentiment);
//Sentiment: NEGATIVE
sentiment = analyzer.analyze("You seem to like ten layers of abstractions in your code, don't you?");
System.out.println("Sentiment: " + sentiment);
//Sentiment: SARCASTIC
我们已经了解了如何使用 AiServices 与 LLM 进行交互,以及如何将请求和响应映射到不同的格式。 但 AiServices 还有很多功能,例如 RAG、工具、共享或每用户聊天记忆等。
结论
在本文中,我们以一段静态文本作为输入,并以不同的格式对其进行了总结。 但在现实世界中,我们可能需要分析存储在文本文件、网页或数据库中的大文本。
我们将探讨如何使用 LangChain4j Retrieval-Augmented Generation (RAG) 来使用我们自己的知识库 以回答下一篇文章中的问题。