AI Services
简单使用
java
public static StreamingChatModel STREAMING_BASE_MODEL = OpenAiStreamingChatModel.builder()
.baseUrl("https://api.deepseek.com")
.apiKey(System.getenv("DS_API_KEY"))
.modelName("deepseek-v4-flash")
.logRequests(true)
.logResponses(true)
.build();
java
interface Assistant {
String chat(String userMessage);
}
java
@Test
void test01() {
/**
* 您将接口的Class与底层组件一起提供给AiServices,然后AiServices会创建一个实现该接口的代理对象。
* 目前这一过程使用的是反射机制,但我们也在考虑其他替代方案。
* 这个代理对象会处理所有输入和输出的转换。在本例中,输入是一个简单的String,但我们使用的是以ChatMessage作为输入的ChatModel。
* 因此,AiService会自动将其转换为UserMessage并调用ChatModel。
* 由于chat方法的输出类型是String,当ChatModel返回AiMessage后,该消息会在chat方法返回之前被转换为String类型。
*/
Assistant assistant = AiServices.create(Assistant.class, BASE_MODEL);
String answer = assistant.chat("Hello");
System.out.println(answer);
}
添加SystemMessage
方式一:通过注解,但是注解可以是文本,也可以从配置文件获取
java
interface Friend {
//@SystemMessage("你是一个AI建模专家,只能回答关于脑建立的建模问题。")
@SystemMessage(fromResource = "my-prompt-template.txt")
String chat(String userMessage);
}
@Test
void test02() {
Friend friend = AiServices.create(Friend.class, BASE_MODEL);
String answer = friend.chat("腿抽筋怎么办");
System.out.println(answer);
// 我专注于脑建立的建模问题,无法提供医疗建议。建议你咨询医生或查看可靠的健康资源来处理腿抽筋。如果你有关于脑建模(例如神经网络、认知架构等)的问题,我很乐意帮助!
}
方式二:通过代码配置(可以给不同的chatMemoryId提供不同的SystemMessage)
java
@Test
void test03() {
Friend friend = AiServices.builder(Friend.class)
.chatModel(BASE_MODEL)
.systemMessageProvider(chatMemoryId -> "你是一个AI建模专家,只能回答关于脑建立的建模问题。")
.build();
String answer = friend.chat("腿抽筋怎么办");
System.out.println(answer);
// 抱歉,我无法回答关于腿抽筋的医学建议。。。。。。。。。
}
在运行时修改系统提示词, 使用 System Message Transformer
java
@Test
void test04() {
Friend friend = AiServices.builder(Friend.class)
.chatModel(BASE_MODEL)
.systemMessageProvider(chatMemoryId -> "你是我的好朋友,请用俚语回答.")
.systemMessageTransformer(systemMessage -> systemMessage + " Today's date is " + LocalDate.now() + ".")
.build();
String answer = friend.chat("腿抽筋怎么办");
System.out.println(answer);
}
请求的部分json如下:
json
{
"role" : "system",
"content" : "你是我的好朋友,请用俚语回答. Today's date is 2026-05-12."
}
获取上下文参数
java
@Test
void test05() {
Friend friend = AiServices.builder(Friend.class)
.chatModel(BASE_MODEL)
.systemMessageProvider(chatMemoryId -> "你是我的好朋友,请用俚语回答.")
.systemMessageTransformer((systemMessage, context) ->
systemMessage + " Tenant: " + context.invocationParameters().get("tenant") + ".")
.build();
String answer = friend.chat("腿抽筋怎么办");
System.out.println(answer);
}
// "content" : "你是我的好朋友,请用俚语回答. Tenant: null."
UserMessage
java
interface Friend {
@UserMessage("你是我的好朋友,请用俚语回答. {{it}}")
String chat(String userMessage);
}
java
interface Friend {
@UserMessage("You are a good friend of mine. Answer using slang. {{message}}")
String chat(@V("message") String userMessage);
}
配置 ChatMemory
java
@Test
void test01() {
//RedisChatMemoryStore chatMemoryStore = new RedisChatMemoryStore("192.168.6.15:6379", 6349, 11, userId, "redis@2025");
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.id(UUID.randomUUID().toString())
.chatMemoryStore(new InMemoryChatMemoryStore())
.maxMessages(10).build();
Friend friend = AiServices.builder(Friend.class)
.chatModel(BASE_MODEL)
.systemMessageProvider(chatMemoryId -> "你是一个智能助手")
.chatMemory(chatMemory)
.build();
List<String> questions = new ArrayList<>();
questions.add("北京大学是211么?");
questions.add("是985么?");
questions.add("是双一流么?");
for (String userMessage : questions) {
String answer = friend.chat(userMessage);
System.out.println(answer);
}
}
通过程序对 ChatRequest 重写
java
/**
* 通过程序对 ChatRequest 重写
*/
@Test
void test02() {
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(BASE_MODEL)
.chatRequestTransformer(new UnaryOperator<ChatRequest>() {
@Override
public ChatRequest apply(ChatRequest chatRequest) {
List<ChatMessage> messages = chatRequest.messages();
System.out.println(messages.getLast().toString());
return chatRequest;
}
}) // Configures the transformation function to be applied to the ChatRequest
.build();
String res = assistant.chat("你好");
System.out.println(res);
}
获取ChatRequest同时也需要获取ChatMemory,不过这里存在问题
java
/**
* 如果需要同时访问 ChatMemory 来实现所需的 ChatRequest 转换,
* 还可以通过配置 chatRequestTransformer 方法并传入一个
* BiFunction<ChatRequest, Object, ChatRequest> 来实现,
* 其中传递给该函数的第二个参数是 memory ID。
*/
@Test
void test03() {
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.id(UUID.randomUUID().toString())
.chatMemoryStore(new InMemoryChatMemoryStore())
.maxMessages(10).build();
System.out.println(chatMemory.id()); // 这里输出的uuid字符串
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(BASE_MODEL)
.chatMemory(chatMemory)
.chatRequestTransformer(new BiFunction<ChatRequest, Object, ChatRequest>() {
/**
*
* @param chatRequest ChatRequest
* @param o memoryID
* @return
*/
@Override
public ChatRequest apply(ChatRequest chatRequest, Object o) {
System.out.println(o.toString()); // 这里输出的是default,和上面的ChatMemoryId 不一样
//todo: 这里需要排查为什么????
return chatRequest;
}
})
.build();
String res = assistant.chat("你好");
System.out.println(res);
}
请求的时候携带请求参数
java
interface AssistantWithChatParams {
String chat(@UserMessage String userMessage, ChatRequestParameters params);
}
java
@Test
void test04() {
AssistantWithChatParams assistant = AiServices.builder(AssistantWithChatParams.class)
.chatModel(BASE_MODEL) // or whichever model
.build();
ChatRequestParameters customParams = ChatRequestParameters.builder()
.temperature(0.85)
.build();
String answer = assistant.chat("Hi there!", customParams);
}
SystemMessage 和 UserMessage 注解混合
java
interface Friend {
@UserMessage("你是我的好朋友,请用俚语回答. {{it}}")
String chat(String userMessage);
@SystemMessage("Given a name of a country, {{answerInstructions}}")
@UserMessage("{{country}}")
String chat(@V("answerInstructions") String answerInstructions, @V("country") String country);
}
多模态
java
String chat(@UserMessage AudioContent audio, @UserMessage ImageContent image);
返回类型
java
interface Assistant {
String chat(String message);
@UserMessage("产生一个给定主题的文章的大纲: {{it}}")
Result<List<String>> generateOutlineFor(String topic);
}
@Test
void test05() {
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(BASE_MODEL) // or whichever model
.build();
Result<List<String>> result = assistant.generateOutlineFor("软件架构设计");
List<String> outline = result.content();
TokenUsage tokenUsage = result.tokenUsage();
List<Content> sources = result.sources();
List<ToolExecution> toolExecutions = result.toolExecutions();
FinishReason finishReason = result.finishReason();
}
返回pojo
java
import dev.langchain4j.service.UserMessage;
import lombok.Data;
import java.time.LocalDate;
interface PersonExtractor {
@UserMessage("Extract information about a person from {{it}}")
Person extractPersonFrom(String text);
}
@Data
class Person {
String firstName;
String lastName;
LocalDate birthDate;
Address address;
}
@Data
class Address {
String street;
Integer streetNumber;
String city;
}
测试
java
@Test
void test06() {
PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, BASE_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);
//Person(firstName=John, lastName=Doe, birthDate=1968-07-04, address=Address(street=Whispering Pines Avenue, streetNumber=345, city=Springfield))
System.out.println(person);
}
大模型的JSON模式: the LLM will be forced to respond with a valid JSON.
java
public static final ChatModel BASE_MODEL = OpenAiChatModel.builder()
.baseUrl("https://api.deepseek.com")
.apiKey(System.getenv("DS_API_KEY"))
.modelName("deepseek-v4-flash")
.strictJsonSchema(true) // 指定为json格式
.logRequests(true)
.logResponses(true)
.build();
streaming
java
interface Assistant {
TokenStream chat(String message);
}
java
@Test
void test01() {
Assistant assistant = AiServices.create(Assistant.class, STREAMING_BASE_MODEL);
TokenStream tokenStream = assistant.chat("给我讲个笑话");
CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
tokenStream
.onPartialResponse((String partialResponse) -> System.out.println(partialResponse))
//.onPartialThinking((PartialThinking partialThinking) -> System.out.println(partialThinking))
//.onRetrieved((List<Content> contents) -> System.out.println(contents))
//.onIntermediateResponse((ChatResponse intermediateResponse) -> System.out.println(intermediateResponse))
// This will be invoked every time a new partial tool call (usually containing a single token of the tool's arguments) is available.
//.onPartialToolCall((PartialToolCall partialToolCall) -> System.out.println(partialToolCall))
// This will be invoked right before a tool is executed. BeforeToolExecution contains ToolExecutionRequest (e.g. tool name, tool arguments, etc.)
//.beforeToolExecution((BeforeToolExecution beforeToolExecution) -> System.out.println(beforeToolExecution))
// This will be invoked right after a tool is executed. ToolExecution contains ToolExecutionRequest and tool execution result.
//.onToolExecuted((ToolExecution toolExecution) -> System.out.println(toolExecution))
.onCompleteResponse((ChatResponse response) -> futureResponse.complete(response))
.onError((Throwable error) -> futureResponse.completeExceptionally(error))
.start();
futureResponse.join(); // Blocks the main thread until the streaming process (running in another thread) is complete
}
返回Flux
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
<version>1.14.1-beta24</version>
</dependency>
java
interface Assistant {
//TokenStream chat(String message);
Flux<String> chat(String message);
}
java
@Test
void test01() {
Assistant assistant = AiServices.create(Assistant.class, STREAMING_BASE_MODEL);
Flux<String> chat = assistant.chat("给我讲个笑话,200字以上");
CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
chat.doOnNext(System.out::print).subscribe(); // 触发执行
futureResponse.join();
}