一: AI Services介绍
LangChain4j提供了很多基础的组件,每次使用都需要你编写大量的样板代码,LangChain4j就提供了一个高级的组件AI Services,它可以简化与大模型(LLM)和其他组件交互的复杂度。让开发者更专注于业务逻辑,而不是底层的实现细节。
通过AiServices来封装聊天模型API,实现会话记忆,工具调用,搜索增强,内容审查等功能,并提供简单灵活的用户接口 DefaultAiServices是其默认实现类型,通过动态代理的方式实现用户定义的服务接口
二: AI Services功能
- 处理LLM的输入
- 解析LLM的输出
- Chat memory 聊天信息存储器
- Tools 工具调用
- RAG 检索增强生成
三: AI Services工作原理
我们定义一个接口,并将接口的class和LangChain4j的基础组件一起传给AiServices,而AiServices则通过反射创建一个实现此接口的Jdk代理对象。当我们使用这个代理对象去执行方法时,就会去执行代理逻辑,组装UserMessage和SystemMessage对象,然后去调用大模型的接口,返回结果.
四: AI Services实战
注解介绍:
- @UserMessage: 用户消息
- @SystemMessage: 系统消息,当@SystemMessage和AiServices.systemMessageProvider(Function) 都配置时, @SystemMessage 优先。
- @V: 提示模板变量,其值将被注入到通过@UserMessage,@SystemMessage和AiServices.systemMessageProvider定义的提示模版中.
- @MemoryId: 带有 @MemoryId 注释的方法参数的值将用于查找属于该用户/会话的内存。带有 @MemoryId 注释的参数可以是任何类型,前提是它已正确实现 equals() 和 hashCode() 方法.
- @Moderate: 和ModerationModel配合使用,对方法的输入进行审核,看是否涉及敏感,不安全的内容.
综合案例
基于AiService实现智能文章小助手
当我们需要实现智能文章小助手,根据输入的文章标题,得到一篇文章时, 首先需要加上@SystemMessage注解, 告诉大模型让他做什么事情,此时,我们输入文章标题时,大模型才会返回结果给我们. 如果我们需要检测用户输入的文章标题是否包含敏感词,可以使用@Moderate和ModerationModel来支持这个功能, 这个功能没有在智普Ai有找到,所以用了openAi的ModerationModel.
注意事项:
方法参数大于两个的时候, 每个入参都需要包括@UserMessage,@SystemMessage,@V和
@MemoryId注解中至少一个注解.不然就会报错.
案例代码
public class AiServiceDemo {
interface Writer {
@SystemMessage("请扮演一名作家,根据输入的文章题目写一篇{{count}}字以内的作文")
@Moderate
String write(@UserMessage String title, @V("count") Integer count);
}
public static void main(String[] args) {
ZhipuAiChatModel zhipuAiModel = ZhipuAiChatModel.builder()
.apiKey("智普spikey")
.build();
OpenAiModerationModel openAiModel = OpenAiModerationModel.builder()
.apiKey("demo")
.build();
Writer writer = AiServices.builder(Writer.class)
.chatLanguageModel(zhipuAiModel)
.moderationModel(openAiModel)
.build();
//String result = writer.write("我最爱的人",200);
String result = writer.write("我要杀了你",200);
System.out.println(result);
}
}
五: AI Services结构化输出
AI Services支持以下返回类型:
String
AiMessage
boolean
/Boolean
byte
/Byte
/short
/Short
/int
/Integer
/BigInteger
/long
/Long
/float
/Float
/double
/Double
/BigDecimal
Date
/LocalDate
/LocalTime
/LocalDateTime
List<String>
/Set<String>
- Any
Enum
- Any custom POJO
- Result<T>
Enum
和 boolean
作为返回类型
public class AiServiceDemo2 {
enum Sentiment {
POSITIVE, NEUTRAL, NEGATIVE
}
interface SentimentAnalyzer {
@UserMessage("Analyze sentiment of {{it}}")
Sentiment analyzeSentimentOf(String text);
@UserMessage("Does {{it}} has a positive sentiment?")
boolean isPositive(String text);
}
public static void main(String[] args) {
ZhipuAiChatModel zhipuAiModel = ZhipuAiChatModel.builder()
.apiKey("智普apikey")
.build();
SentimentAnalyzer sentimentAnalyzer = AiServices.create(SentimentAnalyzer.class, zhipuAiModel);
Sentiment sentiment = sentimentAnalyzer.analyzeSentimentOf("这是好的吗!");// POSITIVE
System.out.println(sentiment);
boolean positive = sentimentAnalyzer.isPositive("It's awful!");
System.out.println(positive);
}
}
自定义POJO作为返回类型
@Data
public class Person {
String firstName;
String lastName;
LocalDate birthDate;
Address address;
}
@Data
public class Address {
String street;
Integer streetNumber;
String city;
}
public class AiServiceDemo {
interface PersonExtractor {
@UserMessage("Extract information about a person from {{it}}")
Person extractPersonFrom(String text);
}
public static void main(String[] args) {
ZhipuAiChatModel zhipuAiModel = ZhipuAiChatModel.builder()
.apiKey("智普apikey")
.build();
PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, zhipuAiModel);
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);
}
}