接上篇文章,上次提到了ChatMessage、以及ChatMemory和一些大家看不懂的example,现在我们来到最重要的地方
AI Service
到目前为止,我们已经涵盖了底层组件,如聊天语言模型、聊天消息、聊天记忆等。在这个级别上工作是非常灵活的,给你完全的自由,但它也迫使你编写大量的样板代码。由于LLM驱动的应用程序通常不仅需要单个组件,而且需要多个组件协同工作(例如,提示模板、聊天内存、LLM、输出解析器、RAG组件:嵌入模型和存储),并且通常涉及多个交互,编排它们变得更加繁琐。
我们希望您关注业务逻辑,而不是低层的实现细节。因此,目前在Langchain4j中有两个高级概念可以帮助解决这个问题AI Services and Chains(Chains被遗弃了,所以只剩AI Services).
AI Service的一些处理操作
- 格式化大语言模型的输入
- 解析大语言模型输出
AI Service更高级的的处理操作
- 聊天记忆
- 工具
- 检索增强生成
AI Services可用于构建有状态聊天机器人,以促进来回交互,以及自动化流程,其中每个对LLM的调用都是隔离的。
最简单的AI Service
首先,我们用一个方法chat定义一个接口,它接受一个String作为输入并返回一个String。
interface IAiService {
String chat(String userMessage);
}
创建了我们的底层组件,这些组件将在我们的AI服务中使用。在这种情况下,我们只需要ChatLanguageModel:(下方所用的qianfanChatModel均来自此处,后边不再重复写)
QianfanChatModel qianfanChatModel = QianfanChatModel.builder()
.apiKey("apiKey")
.secretKey("secretKey")
.modelName("modelName")
.build();
最后,我们可以使用AiServices去create 一个我们的AI Service的一个实例:
IAiService aiService = AiService .create(IAiService .class, qianfanChatModel);
现在我们可以使用aiService:
String answer = aiService.chat("Hello");
System.out.println(answer); // Hello, how can I help you?
AI Service是如何工作的
你向AiServices提供接口的Class以及底层组件,AiServices创建一个实现这个接口的代理对象。目前,它使用反射,此代理对象处理输入和输出的所有转换。在本例中,输入是一个String,但我们使用的是一个以ChatMessage为输入的ChatLanguageModel。因此,AiService会自动将其转换为UserMessage并且反射到ChatLanguageModel。由于chat方法的输出类型是String,ChatLanguageModel处理后返回的是AiMessage,在从chat方法返回之前,它将被转换成一个字符串。
@SystemMessage与@UserMessage
在上一篇文章中,我们有提到ChatMessage,UserMessage和SystemMessage也被介绍到了。
我们这里所用到的是两个注解。
我们使用@SystemMessage注解,并添加了我们希望使用的系统提示符,这将被转换为幕后的SystemMessage,并与UserMessage一起发送到大语言模型中。
interface Chinese {
@SystemMessage("Please answer in Chinese. {{it}}")
String chat(String userMessage);
}
Chinese chinese = AiServices.create(Chinese.class, qianfanChatMode);
String answer = chinese.chat("Hello"); // 你好,***(中文回复)
上文中也有提到过,有的模型他并没有SystemMessage这个概念,或许我们可以使用@UserMessage来达到这个目的
interface Chinese {
@UserMessage("Please answer in Chinese. {{message}}")
String chat(@V("message") String userMessage);
}
Chinese chinese = AiServices.create(Chinese.class, qianfanChatMode);
String answer = chinese.chat("Hello"); // 你好,***(中文回复)
很明显,在@UserMessaged的实例中,我并不是简单的将SystemMessage改成了UserMessage,当然,也不是必须要改,只是说一下{{it}}我们可以自定义名称。
自定义实体类对象为返回类型
如果你想接收来自LLM的结构化输出,你可以将你的AlService方法的返回类型从String改为其他类型。目前,AIService支持以下返回类型:
class Person {
String firstName;
String lastName;
LocalDate birthDate;
@Override
public String toString() {
return "Person {" +
" firstName = \"" + firstName + "\"" +
", lastName = \"" + lastName + "\"" +
", birthDate = " + birthDate +
" }";
}
}
interface PersonExtractor {
@UserMessage("Extract information about a person from {{it}}")
Person extractPersonFrom(String text);
}
PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, model);
String text = """
In 1968, amidst the fading echoes of Independence Day,
a child named John arrived under the calm evening sky.
This newborn, bearing the surname Doe, marked the start of a new journey.
He was welcomed into the world at 345 Whispering Pines Avenue
a quaint street nestled in the heart of Springfield
an abode that echoed with the gentle hum of suburban dreams and aspirations.
""";
Person person = personExtractor.extractPersonFrom(text);
System.out.println(person); // // Person { firstName = "John", lastName = "Doe", birthDate = 1968-07-04 }
为模型启用日志记录
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(...)
.logRequests(true)
.logResponses(true)
.build();
在官方文档中logRequests()和logResponses()携带一个参数true即可,可是在我看来没什么两样(可能用法不对,也可能官方还存在缺陷@2024/5/26)
以及官方下面提到的JSON模式,我看到有贡献者给的代码例示中:
他都重写toString了,讨论responseFormat()有意义还重要麽?(小宇轻轻吐槽一下,确实有点搞不懂的说)。
预定义的问候语
让我们考虑一个简单的例子。我想为我的公司做一个聊天机器人。如果用户问候聊天机器人,我希望它使用预定义的问候语进行响应,而不依赖于LLM来生成问候语。如果用户问一个问题,我希望LLM生成使用公司(又名RAG)的内部知识库的响应。以下是如何将这项任务分解为两个独立的AI服务:
interface GreetingExpert {
@UserMessage("Is the following text a greeting? Text: {{it}}")
boolean isGreeting(String text);
}
interface ChatBot {
@SystemMessage("You are a polite chatbot of a company called Miles of Smiles.")
String reply(String userMessage);
}
class MilesOfSmiles {
private final GreetingExpert greetingExpert;
private final ChatBot chatBot;
...
public String handle(String userMessage) {
if (greetingExpert.isGreeting(userMessage)) {
return "Greetings from Miles of Smiles! How can I make your day better?";
} else {
return chatBot.reply(userMessage);
}
}
}
GreetingExpert greetingExpert = AiServices.create(GreetingExpert.class, llama2);
ChatBot chatBot = AiServices.builder(ChatBot.class)
.chatLanguageModel(gpt4)
.contentRetriever(milesOfSmilesContentRetriever)
.build();
MilesOfSmiles milesOfSmiles = new MilesOfSmiles(greetingExpert, chatBot);
String greeting = milesOfSmiles.handle("Hello");
System.out.println(greeting); // Greetings from Miles of Smiles! How can I make your day better?
String answer = milesOfSmiles.handle("Which services do you provide?");
System.out.println(answer); // At Miles of Smiles, we provide a wide range of services ...
上面是官方给的示例(直接复制粘贴下来的,并不能直接运行,大家需要自行修改),现在我使用的千帆大语言模型貌似并没有这种“情感方面”的判断我使用下面进行了一个小小的判断:(当然也不排除是我使用的情感词有问题)
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
interface SentimentAnalyzer {
@UserMessage("Analyze sentiment of {{it}}")
Sentiment analyzeSentimentOf(String text);
@UserMessage("它是消极的吗? {{it}}")
boolean isPositive(String text);
}
SentimentAnalyzer sentimentAnalyzer = AiServices.create(SentimentAnalyzer.class, chatLanguageModel);
Sentiment sentiment = sentimentAnalyzer.analyzeSentimentOf("This is good!");
System.out.println(sentiment); // POSITIVE 我男孩中有几个 good,great,bad,,前两个POSITIVE,bad为null,,词穷了,中文也没试几个
boolean positive = sentimentAnalyzer.isPositive("It's terrible!");
评论 (0)