AI Services:结构化输出

如果你希望从大语言模型(LLM)获得结构化输出(例如,一个复杂的 Java 对象,而非包含在 String 中的非结构化文本),你可以将 AI 服务方法的返回类型从 String 更改为其他支持的类型。

下面通过三个示例来体验一下结构化输出的魅力……

注意:关于结构化输出的更多信息后续会单独详细介绍。

示例:boolean 作为返回类型

下面示例演示返回一个布尔值:

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
}

示例:Enum 作为返回类型

下面示例将演示返回枚举类型:

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 作为返回类型

下面示例将演示如何返回一个 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 知识请阅读后续教程……

 

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号