当前位置: 首页 > article >正文

LangChain4j 提取用户提问信息结构化输出 | LangChain4j

结构化输出 | LangChain4j

术语说明

“结构化输出”这个词有多种含义,可以指以下两种情况:

  1. 大语言模型(LLM)生成结构化格式输出的一般能力(本页涵盖的内容)。
  2. OpenAI 的结构化输出功能,该功能适用于响应格式和工具(函数调用)。

许多 LLM 和 LLM 提供商支持以结构化格式生成输出,通常是 JSON 格式。这些输出可以轻松映射到 Java 对象,并在应用程序的其他部分中使用。

例如,假设我们有一个 Person 类:

record Person(String name, int age, double height, boolean married) {}

我们的目标是从非结构化文本中提取一个 Person 对象,例如:

John 42 岁,过着独立的生活。他身高 1.75 米,充满自信。目前未婚,他享受专注于个人目标和兴趣的自由。

根据 LLM 和 LLM 提供商的不同,可以通过以下四种方式实现这一目标(从最可靠到最不可靠):

  1. JSON Schema
  2. 工具(函数调用)
  3. 提示 + JSON 模式
  4. 提示

一些 LLM 提供商(目前包括 OpenAI、Google AI Gemini 和 Ollama)允许为期望的输出指定 JSON Schema。您可以在“JSON Schema”列中查看所有支持的 LLM 提供商。

当在请求中指定 JSON Schema 时,LLM 应生成符合此 Schema 的输出。

注意事项

请注意,JSON Schema 是在请求中的专用属性中指定的,不需要在提示中包含任何自由形式的指令(例如系统或用户消息)。

LangChain4j 在低级别的 ChatLanguageModel API 和高级别的 AI Service API 中都支持 JSON Schema 功能。

在低级别的 ChatLanguageModel API 中,可以通过使用与 LLM 提供商无关的 ResponseFormatJsonSchema 来指定 JSON Schema:

ResponseFormat responseFormat = ResponseFormat.builder()
    .type(JSON) // 类型可以是 TEXT(默认)或 JSON
    .jsonSchema(JsonSchema.builder()
        .name("Person") // OpenAI 需要为 Schema 指定名称
        .rootElement(JsonObjectSchema.builder()
            .addStringProperty("name")
            .addIntegerProperty("age")
            .addNumberProperty("height")
            .addBooleanProperty("married")
            .required("name", "age", "height", "married") // 必需字段必须显式指定,否则视为可选
            .build())
        .build())
    .build();
示例代码
UserMessage userMessage = UserMessage.from("""
John 42 岁,过着独立的生活。他身高 1.75 米,充满自信。目前未婚,他享受专注于个人目标和兴趣的自由。
""");

ChatRequest chatRequest = ChatRequest.builder()
    .responseFormat(responseFormat)
    .messages(userMessage)
    .build();

ChatLanguageModel chatModel = OpenAiChatModel.builder()
    .apiKey(System.getenv("OPENAI_API_KEY"))
    .modelName("gpt-4o-mini")
    .logRequests(true)
    .logResponses(true)
    .build();

ChatResponse chatResponse = chatModel.chat(chatRequest);
String output = chatResponse.aiMessage().text();
System.out.println(output); // {"name":"John","age":42,"height":1.75,"married":false}

Person person = new ObjectMapper().readValue(output, Person.class);
System.out.println(person); // Person[name=John, age=42, height=1.75, married=false]
注意事项
  1. 在大多数情况下,根元素必须是 JsonObjectSchema 类型,但 Gemini 还允许 JsonEnumSchemaJsonArraySchema
  2. 必需属性必须显式指定;否则,它们被视为可选。

JSON Schema 的结构通过 JsonSchemaElement 接口定义,具有以下子类型:

  • JsonObjectSchema - 用于对象类型。
  • JsonStringSchema - 用于字符串、字符/Character 类型。
  • JsonIntegerSchema - 用于整数/Integer、长整型/Long、BigInteger 类型。
  • JsonNumberSchema - 用于浮点数/Float、双精度数/Double、BigDecimal 类型。
  • JsonBooleanSchema - 用于布尔值/Boolean 类型。
  • JsonEnumSchema - 用于枚举类型。
  • JsonArraySchema - 用于数组和集合(例如 List、Set)。
  • JsonReferenceSchema - 支持递归(例如,Person 包含一个 Set<Person> 类型的 children 字段)。
  • JsonAnyOfSchema - 支持多态性(例如,Shape 可以是 Circle 或 Rectangle)。

使用 AI Services 实现相同功能

接口定义:

interface PersonExtractor {
    Person extractPersonFrom(String text);
}

创建 ChatLanguageModel:

ChatLanguageModel chatModel = OpenAiChatModel.builder()
    .apiKey(System.getenv("OPENAI_API_KEY"))
    .modelName("gpt-4o-mini")
    .responseFormat("json_schema")
    .strictJsonSchema(true)
    .logRequests(true)
    .logResponses(true)
    .build();

或者使用 Azure OpenAI:

ChatLanguageModel chatModel = AzureOpenAiChatModel.builder()
    .endpoint(System.getenv("AZURE_OPENAI_URL"))
    .apiKey(System.getenv("AZURE_OPENAI_API_KEY"))
    .deploymentName("gpt-4o-mini")
    .strictJsonSchema(true)
    .supportedCapabilities(Set.of(RESPONSE_FORMAT_JSON_SCHEMA))
    .logRequestsAndResponses(true)
    .build();

或者使用 Google AI Gemini:

ChatLanguageModel chatModel = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
    .modelName("gemini-1.5-flash")
    .responseFormat(ResponseFormat.JSON)
    .logRequestsAndResponses(true)
    .build();

或者使用 Ollama:

ChatLanguageModel chatModel = OllamaChatModel.builder()
    .baseUrl("http://localhost:11434")
    .modelName("llama3.1")
    .supportedCapabilities(RESPONSE_FORMAT_JSON_SCHEMA)
    .logRequests(true)
    .logResponses(true)
    .build();

创建 AI Service:

PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, chatModel);

String text = """
John 42 岁,过着独立的生活。他身高 1.75 米,充满自信。目前未婚,他享受专注于个人目标和兴趣的自由。
""";

Person person = personExtractor.extractPersonFrom(text);
System.out.println(person); // Person[name=John, age=42, height=1.75, married=false]

笔记:

[1] - 在 Quarkus 或 Spring Boot 应用中,无需显式创建 ChatLanguageModel 和 AI Service,因为这些 Bean 会自动创建。更多详情:对于 Quarkus,请参阅 此处; 对于 Spring Boot,请参阅 此处。

[2] - 这是为 OpenAI 启用 JSON Schema 功能的必要条件,详见 这里。

[3] - 这是为 Azure OpenAI 启用 JSON Schema 功能的必要条件,详见 这里。

[4] - 这是为 Google AI Gemini 启用 JSON Schema 功能的必要条件,详见 这里。

[5] - 这是为 Ollama 启用 JSON Schema 功能的必要条件,详见 这里。

当满足以下所有条件时:

  • AI Service 方法返回一个 POJO。
  • 使用的 ChatLanguageModel 支持 JSON Schema 功能。
  • 已在使用的 ChatLanguageModel 上启用了 JSON Schema 功能。

那么,基于指定的返回类型,ResponseFormat 将自动生成带有 JsonSchema 的格式。

注意:

确保在配置 ChatLanguageModel 时显式启用 JSON Schema 功能,因为默认情况下它是禁用的。

生成的 JsonSchema 的名称是返回类型的简单名称(getClass().getSimpleName()),在这种情况下为:“Person”。

一旦 LLM 返回响应,输出将被解析为对象并从 AI Service 方法返回。

注意:

虽然我们正在逐步迁移到 Jackson,但目前 AI Service 中仍使用 Gson 来解析输出,因此 POJO 上的 Jackson 注解不会生效。

您可以在这里和这里找到许多支持的用例示例。


添加描述

如果 LLM 未提供所需的输出,可以通过在类和字段上添加 @Description 注解来向 LLM 提供更多指令和正确输出的示例。例如:

@Description("一个人")
record Person(
    @Description("人的名字,包括姓和名,例如:John Doe") String name,
    @Description("人的年龄,例如:42") int age,
    @Description("人的身高(米),例如:1.78") double height,
    @Description("此人是否已婚,例如:false") boolean married) {
}

局限性

在使用 JSON Schema 与 AI Services 时,存在一些限制:

  • 它仅适用于支持的 OpenAI、Azure OpenAI、Google AI Gemini 和 Ollama 模型。
  • 需要在配置 ChatLanguageModel 时显式启用 JSON Schema 功能。
  • 它不支持流模式(streaming mode)。
  • 目前,它仅适用于返回类型为单个 POJO 或 Result 的情况。如果需要其他类型(如 List、枚举等),请将其包装到 POJO 中。我们正在努力支持更多返回类型。
  • POJO 可以包含:
    • 标量/简单类型(如 String、int/Integer、double/Double、boolean/Boolean 等)。
    • 枚举。
    • 嵌套 POJO。
    • List、Set 和 T[],其中 T 是标量、枚举或 POJO。
  • 在生成的 JsonSchema 中,所有字段和子字段都被自动标记为必需字段,目前无法将其设置为可选。
  • 如果 LLM 不支持 JSON Schema 功能、未启用该功能或返回类型不是 POJO,AI Service 将回退到提示模式(prompting)。
  • 递归目前仅由 OpenAI 和 Azure OpenAI 支持。
  • 尚不支持多态性。返回的 POJO 及其嵌套的 POJO 必须是具体类;接口或抽象类不受支持。

工具(函数调用)

此方法假设通过工具生成结构化输出。在请求发送给 LLM 时,指定了单个工具,并且工具参数描述了所需输出的结构。一旦 LLM 返回包含 ToolExecutionRequest 的 AiMessage,ToolExecutionRequest.arguments() 中的 JSON 字符串将被解析为 POJO。

更多信息即将发布。

在此期间,请阅读 此部分 和 这篇文章。


提示 + JSON 模式

更多信息即将发布。

在此期间,请阅读 此部分 和 这篇文章。


提示模式

在使用提示模式时,需要在系统或用户消息中以自由文本形式指定所需输出的格式,并希望 LLM 能够遵守。这种方法相当不可靠。如果 LLM 和 LLM 提供商支持上述方法,则最好使用它们。


相关教程

数据提取:让 LLM 输出 JSON 内容的多种方法,作者 Guillaume Laforge。


http://www.kler.cn/a/570410.html

相关文章:

  • 散户如何实现自动化交易下单——篇1:体系介绍与获取同花顺资金账户和持仓信息
  • ArcGIS Pro实战技巧:如何利用进行科学选房分析
  • 什么是JTAG、SWD?
  • DataWorks (数据工厂)介绍
  • 第39天:安全开发-JavaEE应用SpringBoot框架Actuator监控泄漏Swagger自动化
  • iOS逆向工程专栏 第13篇:iOS动态分析基础
  • VMware虚拟机导入VirtualBox
  • 【前端基础】Day 8 H5C3提高
  • iOS基础开发知识速览 - 理解你要逆向的目标
  • 生成一个日期时间序列,从‘2024-12-03‘开始,每小时递增 oracle 转为达梦
  • 第2章 windows故障排除(网络安全防御实战--蓝军武器库)
  • 【LeetCode】279. 完全平方数
  • 【MySQL数据库】SQL语法基础--DQL(入门级)
  • 在CentOS 7上为YUM安装的Nginx添加模块及第三方模块stream
  • 如何把图片或者图片地址存到 MySQL 数据库中以及如何将这些图片数据通过 JSP 显示在网页中
  • 数据标注/AI训练师技术图谱与学习路径
  • 前端模块化管理深度解析:从混沌到秩序的全链路实践指南
  • 探索Elasticsearch:文档的CRUD
  • 【C++动态规划 子集状态压缩】2002. 两个回文子序列长度的最大乘积|1869
  • 计算机毕业设计SpringBoot+Vue.js疫苗发布和接种预约系统(源码+文档+PPT+讲解)