1. 什么是 AiServices?
AiServices 是 LangChain4j 提供的一种 "高层级声明式抽象"。
它的核心思想是:
你只需要定义一个 Java 接口 (Interface),并加上简单的注解。LangChain4j 框架会在运行时利用 动态代理 技术,自动帮你生成这个接口的实现类。
对比一下:
-
以前 (ChatLanguageModel): 你得手动写:"请把这句话翻译成英文:" +
text。 -
现在 (AiServices): 你定义一个方法
String translate(String text),框架自动帮你填 Prompt,发请求,解析结果。
2. AiServices 有什么好处?
-
代码解耦 (Clean Code): 业务逻辑(接口)和 AI 实现细节彻底分离。你不需要在业务代码里到处拼接字符串。
-
类型安全 (Structured Output): AI 不再只能返回 String。它可以直接返回
boolean、Date、Enum、List甚至自定义的Person对象。框架会自动提示 AI 输出 JSON 并帮你反序列化。 -
功能集成 (All-in-One): 它不仅仅是对话,它把 RAG (知识库) 、ChatMemory (记忆) 、Tools (工具调用) 、Audit (审计) 全部整合在一起。你只需要在构建时配置一下,接口就立刻拥有了这些能力。
3. 核心注解与方法
在使用 AiServices 时,我们主要打交道的是以下几个注解:
| 注解 | 作用 | 示例 |
|---|---|---|
@SystemMessage |
给 AI 设定"人设"或"系统指令"。 | @SystemMessage("你是一个专业的法律顾问") |
@UserMessage |
定义发给 AI 的具体 Prompt 模板。 | @UserMessage("请将 {``{text}} 翻译成 {``{lang}}") |
@V |
绑定方法参数到模板变量中。 | translate(@V("text") String t, @V("lang") String l) |
@MemoryId |
区分不同用户的记忆(多轮对话用)。 | chat(@MemoryId int userId, String msg) |
4. 实战 Demo:打造一个"情感分析与翻译助手"
我们继续在你现有的 AiTest 类基础上扩展。这次我们不直接调用 chatLanguageModel.generate,而是创建一个接口。
第一步:定义接口 (在同一个文件或新建文件)
这个接口定义了我们希望 AI 完成的两个功能:
-
翻译功能(带参数模板)。
-
情感分析功能(要求返回布尔值,看看 AI 是不是真的懂类型转换)。
java
// 定义一个内部接口方便测试,实际开发中建议单独建文件
interface SmartAssistant {
// 功能 1: 简单的对话,设定人设
@SystemMessage("你是一个说话像古代诗人的助手")
String chat(String userMessage);
// 功能 2: 带参数的模板
// {{message}} 和 {{language}} 会被参数自动替换
@UserMessage("请将以下内容:'{{message}}' 翻译成目标语言:{{language}}")
String translate(@dev.langchain4j.service.V("message") String message,
@dev.langchain4j.service.V("language") String language);
// 功能 3: 结构化输出 (直接返回 boolean)
@UserMessage("以下这段话是正面的积极评价吗?请只回答 true 或 false:{{text}}")
boolean isPositive(@dev.langchain4j.service.V("text") String text);
}
第二步:编写测试用例 (AiServicesTest)
将以下代码添加到你的测试类中(或者替换原来的 test 方法)。我们会复用你之前配置好的 apiKey 等变量。
java
package com.qcby.langchain4jdemo;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices; // 引入核心类
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class AiServicesTest {
@Value("${aliyun.ai.key}")
private String apiKey;
@Value("${aliyun.ai.apiUrl}")
private String url;
@Value("${aliyun.ai.model}")
private String modelName;
// 1. 定义接口 (实际项目中通常放在单独的 .java 文件里)
interface SmartAssistant {
@SystemMessage("你是一个精通多国语言的翻译官,但在闲聊时非常幽默。")
String chat(String msg);
@UserMessage("请将这段文字 '{{msg}}' 翻译成 {{lang}},只输出翻译结果,不要废话。")
String translate(@V("msg") String message, @V("lang") String targetLanguage);
@UserMessage("请判断这句话的情感是否是积极的?文本:{{text}}")
boolean analyzeSentiment(@V("text") String text); // 注意返回值是 boolean
}
@Test
public void testAiService() {
// 2. 准备底层模型 (和之前一样)
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.baseUrl(url)
.modelName(modelName)
.build();
// 3. 【核心步骤】创建 AiService 代理实例
// 这一步相当于 MyBatis 的 SqlSession.getMapper(UserMapper.class)
SmartAssistant assistant = AiServices.builder(SmartAssistant.class)
.chatLanguageModel(model) // 绑定模型引擎
.build();
// 4. 像调用普通 Java 方法一样调用 AI
System.out.println("--- 测试 1: 闲聊 ---");
String chatResult = assistant.chat("你好,你会写代码吗?");
System.out.println(chatResult);
System.out.println("\n--- 测试 2: 翻译 (参数替换) ---");
String transResult = assistant.translate("Hello World", "中文");
System.out.println("翻译结果: " + transResult);
System.out.println("\n--- 测试 3: 结构化输出 (Boolean) ---");
boolean isHappy = assistant.analyzeSentiment("LangChain4j 真是太好用了,我爱死它了!");
boolean isSad = assistant.analyzeSentiment("今天的代码全是 Bug,我很绝望。");
System.out.println("第一句是积极的吗? " + isHappy);
System.out.println("第二句是积极的吗? " + isSad);
}
}
5. 代码深度解析
当你运行 assistant.translate("Hello World", "中文") 时,LangChain4j 在后台默默做了这些事:
-
模板解析: 它读取
@UserMessage注解,发现里面有两个变量{``{msg}}和{``{lang}}。 -
参数填充: 它把你的参数
"Hello World"填入{``{msg}},把"中文"填入{``{lang}}。 -
Prompt 构建: 最终生成的 Prompt 是:
请将这段文字 'Hello World' 翻译成 中文,只输出翻译结果,不要废话。 -
发送请求: 带着
@SystemMessage定义的人设,把 Prompt 发给阿里云。 -
结果解析: 拿到 AI 的回复字符串,直接返回给你。
而对于 analyzeSentiment 方法:
-
AI 可能返回了
"Yes"或"True"或"是的"。 -
AiServices会尝试把这些自然语言自动解析成 Java 的boolean值 (true/false)。
下一步建议
一旦你跑通了这个 Demo,你就掌握了 LangChain4j 最核心的用法。
接下来的进阶玩法是:
-
ChatMemory : 现在的
SmartAssistant是没记性的,你上一句说你是谁,下一句它就忘了。你可以给 builder 加上.chatMemory(...)。 -
Structured Output (POJO) : 尝试定义一个
Person类,让 AI 从一段文本里提取姓名和年龄,直接返回Person对象。