如果你希望从大语言模型(LLM)获得结构化输出(例如,一个复杂的 Java 对象,而非包含在 String 中的非结构化文本),你可以将 AI 服务方法的返回类型从 String 更改为其他支持的类型。
下面通过三个示例来体验一下结构化输出的魅力……
注意:关于结构化输出的更多信息后续会单独详细介绍。
下面示例演示返回一个布尔值:
package com.hxstrive.langchain4j.aiServices;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;
public class ReturnTypeBooleanDemo {
// 推荐:将OPEN_API_KEY设置成环境变量, 避免硬编码或随着代码泄露
// 注意,设置完环境变量记得重启IDEA,不然可能环境变量不会生效
private static final String API_KEY = System.getenv("OPEN_API_KEY");
// 定义业务接口
interface Assistant {
@UserMessage("{{it}} 有积极的情绪吗?")
boolean isPositive(String text);
}
public static void main(String[] args) {
// 创建 ChatModel 实现类(OpenAI 为例)
ChatModel chatModel = OpenAiChatModel.builder()
.baseUrl("https://api.xty.app/v1")
.apiKey(API_KEY)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.logRequests(true)
.logResponses(true)
.build();
// 使用 AiServices 创建服务
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.build();
// 发起对话
boolean answer = assistant.isPositive("太棒了!");
System.out.println("answer=" + answer); // answer=true
}
}执行示例,langchain4j 会自动追加“You must answer strictly in the following format: one of [true, false]”提示语,意思为“你必须严格按照以下格式回答:[true, false] 中的一个”。
请求日志如下:
16:39:07.695 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
"model" : "gpt-3.5-turbo",
"messages" : [ {
"role" : "user",
"content" : "太棒了! 有积极的情绪吗?\nYou must answer strictly in the following format: one of [true, false]"
} ],
"temperature" : 0.7,
"stream" : false
}下面示例将演示返回枚举类型:
package com.hxstrive.langchain4j.aiServices;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;
public class ReturnTypeEnumDemo {
// 推荐:将OPEN_API_KEY设置成环境变量, 避免硬编码或随着代码泄露
// 注意,设置完环境变量记得重启IDEA,不然可能环境变量不会生效
private static final String API_KEY = System.getenv("OPEN_API_KEY");
// 定义枚举
enum Priority {
CRITICAL, HIGH, LOW
}
// 定义业务接口
interface Assistant {
@UserMessage("分析以下问题的优先级: {{it}}")
Priority analyzePriority(String issueDescription);
}
public static void main(String[] args) {
// 创建 ChatModel 实现类(OpenAI 为例)
ChatModel chatModel = OpenAiChatModel.builder()
.baseUrl("https://api.xty.app/v1")
.apiKey(API_KEY)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.logRequests(true)
.logResponses(true)
.build();
// 使用 AiServices 创建服务
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.build();
// 发起对话
Priority priority = assistant.analyzePriority(
"主要的支付网关出现故障,客户无法处理交易。");
System.out.println("priority=" + priority); // priority=CRITICAL
}
}运行示例,LangChain4j 将自动追加“You must answer strictly with one of these enums:\nCRITICAL\nHIGH\nLOW”提示词,意思为“你必须严格使用以下枚举值之一来回答:\nCRITICAL\nHIGH\nLOW”。
请求日志:
16:45:34.129 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
"model" : "gpt-3.5-turbo",
"messages" : [ {
"role" : "user",
"content" : "分析以下问题的优先级: 主要的支付网关出现故障,客户无法处理交易。\nYou must answer strictly with one of these enums:\nCRITICAL\nHIGH\nLOW"
} ],
"temperature" : 0.7,
"stream" : false
}下面示例将演示如何返回一个 POJO Java 对象:
package com.hxstrive.langchain4j.aiServices;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.output.structured.Description;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;
import java.time.LocalDate;
public class ReturnTypePojoDemo {
// 推荐:将OPEN_API_KEY设置成环境变量, 避免硬编码或随着代码泄露
// 注意,设置完环境变量记得重启IDEA,不然可能环境变量不会生效
private static final String API_KEY = System.getenv("OPEN_API_KEY");
// 定义POJO
static class Person {
// 你可以添加一个可选描述,以帮助大语言模型(LLM)更好地理解
@Description("一个人的名字(名)")
String firstName;
String lastName;
LocalDate birthDate;
Address address;
@Override
public String toString() {
return "Person{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", birthDate=" + birthDate +
", address=" + address +
'}';
}
}
// 你可以添加一个可选描述,以帮助大语言模型(LLM)更好地理解
@Description("一个地址")
static class Address {
String street;
Integer streetNumber;
String city;
@Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", streetNumber=" + streetNumber +
", city='" + city + '\'' +
'}';
}
}
// 定义业务接口
interface Assistant {
@UserMessage("从{{it}}中提取一个人的信息")
Person extractPersonFrom(String text);
}
public static void main(String[] args) {
// 创建 ChatModel 实现类(OpenAI 为例)
ChatModel chatModel = OpenAiChatModel.builder()
.baseUrl("https://api.xty.app/v1")
.apiKey(API_KEY)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.logRequests(true)
.logResponses(true)
.build();
// 使用 AiServices 创建服务
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.build();
// 发起对话
String text = """
1968年,在独立日渐渐消散的余韵中,一个名叫约翰的孩子在宁静的夜空下降生。
这个新生儿姓多伊,开启了一段新的旅程。他出生在低语松大道345号,
这是一条位于斯普林菲尔德市中心的古雅街道,一处回荡着郊区梦想与抱负轻柔旋律的居所。
""";
Person person = assistant.extractPersonFrom(text);
System.out.println("person=" + person);
// person=Person{firstName='约翰', lastName='多伊', birthDate=1968-07-04,
// address=Address{street='低语松大道', streetNumber=345, city='斯普林菲尔德'}}
}
}运行示例,请求日志如下:
16:59:21.767 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
"model" : "gpt-3.5-turbo",
"messages" : [ {
"role" : "user",
"content" : "从 1968年,在独立日渐渐消散的余韵中,一个名叫约翰的孩子在宁静的夜空下降生。\n 这个新生儿姓多伊,开启了一段新的旅程。他出生在低语松大道345号,\n 这是一条位于斯普林菲尔德市中心的古雅街道,一处回荡着郊区梦想与抱负轻柔旋律的居所。\n中提取一个人的信息\nYou must answer strictly in the following JSON format: {\n\"firstName\": (一个人的名字(名); type: string),\n\"lastName\": (type: string),\n\"birthDate\": (type: date string (2023-12-31)),\n\"address\": (type: com.hxstrive.langchain4j.aiServices.ReturnTypePojoDemo$Address: {\n\"street\": (type: string),\n\"streetNumber\": (type: integer),\n\"city\": (type: string)\n})\n}"
} ],
"temperature" : 0.7,
"stream" : false
}请求日志中的用户消息多了一大串非我们自己编写的提示词,如下:
You must answer strictly in the following JSON format: {\n\"firstName\": (一个人的名字(名); type: string),\n\"lastName\": (type: string),\n\"birthDate\": (type: date string (2023-12-31)),\n\"address\": (type: com.hxstrive.langchain4j.aiServices.ReturnTypePojoDemo$Address: {\n\"street\": (type: string),\n\"streetNumber\": (type: integer),\n\"city\": (type: string)\n})\n}对提示语中的换行符进行换行,然后翻译为中文如下:
你必须严格按照以下JSON格式回答: {
\"firstName\": (一个人的名字(名); type: string),
\"lastName\": (type: string),
\"birthDate\": (type: date string (2023-12-31)),
\"address\": (type: com.hxstrive.langchain4j.aiServices.ReturnTypePojoDemo$Address: {
\"street\": (type: string),
\"streetNumber\": (type: integer),
\"city\": (type: string)
})
}看清了吗,要返回预期类型的数据,第一步是告诉 LLM 大模型应该返回什么样的格式,然后 LangChain4j 会通过代码解析该种格式,最后返回我们需要的类型。
看了上面几个示例,是不是有一些想法了,我们平时自己调用大模型也可以这样做,在用户消息后面,甚至是系统消息中定义我们自己的输出格式,严格通过代码解析数据,得到我们想要的数据。例如,让 LLM 返回 Properties 格式。
更多 LangChain4j 知识请阅读后续教程……