【LangChain4j 06】【结构化输出】

文章目录

  • 一、前言
  • [二、JSON Schema](#二、JSON Schema)
    • [1. ChatModel](#1. ChatModel)
      • [1.1 基本使用](#1.1 基本使用)
      • [1.2 注意事项](#1.2 注意事项)
    • [2. Ai Service](#2. Ai Service)
      • [2.1 基本使用](#2.1 基本使用)
      • [2.1 高级特性](#2.1 高级特性)
        • [2.1.1 Required and Optional](#2.1.1 Required and Optional)
        • [2.2.2 Adding Description](#2.2.2 Adding Description)
      • [2.3 Limitations](#2.3 Limitations)
  • [三、Prompting + JSON Mode](#三、Prompting + JSON Mode)
  • 四、Prompting
  • 五、参考内容

一、前言

本系列内容来源于 LangChain4J 官网,内容稍作改删,仅做个人笔记使用。

本系列使用 LangChain4J 版本:

xml 复制代码
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-bom</artifactId>
            <version>1.8.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

本系列完整代码地址 :langchain4j-hwl


大语言模型(LLM)及其服务商普遍支持生成 JSON 格式的结构化输出,这类输出可便捷地映射为 Java 对象,进而在应用程序的其他模块中复用。

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

java 复制代码
record Person(String name, int age, double height, boolean married) {
}

我们需要从以下非结构化文本中提取出上述 Person 对象对应的信息:

java 复制代码
 约翰今年42岁,过着独立的生活。他身高1.75米,举止自信。目前未婚,他享有专注于个人目标和兴趣的自由。

针对上述需求,根据所使用的 LLM 及服务商不同,实现这一目标主要有三种方式:

  • JSON Schema(JSON 模式)
  • Prompting + JSON Mode(提示词 + JSON 模式)
  • Prompting(仅提示词)

下面我们一一来看。


二、JSON Schema

JSON Schema 就是给 JSON 数据写的「数据说明书 + 校验规则」,用来规定一段 JSON 必须长什么样、有哪些字段、字段是什么类型。

而部分大语言模型(LLM)服务商(当前涵盖 Amazon Bedrock、Azure OpenAI、Google AI Gemini、Mistral、Ollama 和 OpenAI)支持为目标输出指定 JSON Schema(JSON 模式)。

  1. 支持该功能的服务商可在 文档 中的 JSON Mode 列中查询。
  2. JSON模式是在向LLM提供商的API发出的请求中的一个专用属性中指定,不需要在提示词(例如,在系统消息或用户消息中)包含任何自由格式的指令。

LangChain4j在 low-levelChatModel APIhigh-levelAI Service API 中均支持JSON模式功能,下面我们具体来看。

1. ChatModel

1.1 基本使用

在使用 ChatModel API 时,我们可以在创建 ChatRequest 时通过 LLM 无关的 ResponseFormat 和 JsonSchema 来指定JSON模式,如下:

java 复制代码
	// json 对应对象
    public record Person(String name, int age, double height, boolean married) {
    }

    @SneakyThrows
    @Override
    public String chatByChatModel(String userMessage) {
        ResponseFormat responseFormat = ResponseFormat.builder()
                // 设置模型返回类型
                .type(ResponseFormatType.JSON)
                // 构建 jsonSchema
                .jsonSchema(JsonSchema.builder()
                        .name("Person") // OpenAI requires specifying the name for the schema
                        .rootElement(JsonObjectSchema.builder() // see [1] below
                                .addStringProperty("name")
                                .addIntegerProperty("age")
                                .addNumberProperty("height")
                                .addBooleanProperty("married")
                                .required("name", "age", "height", "married") // see [2] below
                                .build())
                        .build())
                .build();

        // 构建请求
        ChatRequest chatRequest = ChatRequest.builder()
                .responseFormat(responseFormat)
                .messages(UserMessage.from(userMessage))
                .build();
        // 发起请求
        ChatResponse chatResponse = chatModel.chat(chatRequest);

        // 打印响应内容
        String output = chatResponse.aiMessage().text();
        // {"name":"约翰","age":42,"height":1.75,"married":false}
        System.out.println(output);

        Person person = new ObjectMapper().readValue(output, Person.class);
        // Person[name=约翰, age=42, height=1.75, married=false]
        System.out.println(person);

        return person.toString();
    }

1.2 注意事项

JSON Schema 使用中有几个注意点, 如下:

  1. JSON Schema 的根元素默认必须是 JsonObjectSchema (对应 Java 中的对象/实体类,比如 Person 类);

    • Amazon Bedrock、Azure OpenAI 等主流服务商,支持 JsonRawSchema 作为根元素(允许直接传入自定义的完整 JSON Schema 字符串,无需用 LangChain4j 内置类型构建);
    • Google Gemini 额外支持 JsonEnumSchema(枚举类型)、JsonArraySchema(数组/集合类型)作为根元素(比如直接让 LLM 输出一个枚举数组,无需包裹在对象里)。
  2. 所有需要强制返回的属性(比如 Person 中的 name),必须通过 required() 显式声明;

    未声明的属性会被视为「可选」,LLM 可能不返回,Java 解析时会用默认值填充(比如 int 类型默认 0)。

  3. LangChain4j 把 JSON 类型和 Java 类型做了一一映射,每个子类型对应特定的 Java 数据类型,核心用途如下:

    子类型 核心作用
    JsonObjectSchema 对应 Java 实体类/记录(如 Person),支持嵌套属性(比如 Person 里包含 Address 对象)
    JsonStringSchemaJsonIntegerSchemaJsonNumberSchemaJsonBooleanSchema 映射 Java 基础类型/包装类(如 Stringintdoubleboolean
    JsonEnumSchema 映射 Java 枚举类型(如 Sentiment 枚举:POSITIVE/NEGATIVE)
    JsonArraySchema 映射 Java 集合/数组(如 List<String>Set<Person>
    JsonReferenceSchema 支持递归结构(比如「人物」包含「子女列表」,子女又是「人物」类型)
    JsonAnyOfSchema 支持多态(比如「形状」可以是「圆形」或「矩形」,两种不同结构)
    JsonNullSchema 支持可空类型(允许属性值为 null
    JsonRawSchema 自定义完整 JSON Schema(适配特殊场景,无需遵循 LangChain4j 内置类型规则)

2. Ai Service

2.1 基本使用

Ai Service 可以更方便的实现 JSON 功能,如下:

java 复制代码
 @Bean
 @Primary
 public OpenAiChatModel openAiChatModel() {
     return OpenAiChatModel.builder()
             .baseUrl("http://langchain4j.dev/demo/openai/v1")
             .apiKey("demo")
             // 启用JSON Schema
             .supportedCapabilities(Capability.RESPONSE_FORMAT_JSON_SCHEMA)
             .modelName("gpt-4o-mini")
             .build();
 }

@AiService(wiringMode = AiServiceWiringMode.EXPLICIT, chatModel = "openAiChatModel")
public interface PersonExtractor {
    /**
     * 从文本中提取Person对象
     *
     * @param text 包含Person信息的文本
     * @return 提取到的Person对象
     */
    StructuredLlmServiceImpl.Person extractPersonFrom(String text);
}

...
	// 调用
    @Override
    public String chatByAiService(String userMessage) {
        return personExtractor.extractPersonFrom(userMessage).toString();
    }

注 : 官方提供更多的模型使用示例,每个模型的使用方式可能各有不同,需要因模型而异。


2.1 高级特性

JSON Schema 存在一些高级特性,下面我们具体来看。

2.1.1 Required and Optional

LangChain4j 把 JSON Schema 所有字段默认设为「可选」,本质是规避 LLM 的「幻觉问题」:

  • 当 LLM 从文本中提取信息时,如果某个字段没有足够的上下文信息,不会强行编造虚假数据,而是直接忽略该字段; 如若文本只有「John」没有姓氏,LLM 不会虚构「John Doe」,只会返回 {"name":"John"},而非 {"name":"John","surname":"Doe"}。

需要注意的是:

  1. 对于 int、boolean 等基本类型的可选字段,即使 LLM 未返回该字段值,也会按 Java 规则填充默认值:
    • 如:int → 0、boolean → false、double → 0.0 等;
    • 举例:若 Person 有 int age 字段且为可选,文本无年龄信息时,返回 {"name":"John","age":0}。
  2. 开启 strictJsonSchema(true)(严格模式)本是为了强制 LLM 遵循 Schema 规则,但对可选的枚举字段仍有漏洞:
    • LLM 可能无视枚举限定值(比如枚举是 ["MAN","WOMAN"],却返回 "UNKNOWN");
    • 原因:枚举的可选性让 LLM 仍有「自由发挥」空间,严格模式无法完全约束。
  3. 如果想要一个字段为必填项,可以使用 @JsonProperty(required = true) 对其进行注解:
java 复制代码
	record Person(@JsonProperty(required = true) String name, String surname) {
	}
	
	interface PersonExtractor {
	    
	    Person extractPersonFrom(String text);
	}
  1. 当 JSON Schema 用于「工具调用(Function Calling)」时,规则完全相反:
    • 所有字段默认「必填」,LLM 必须提取所有字段值(哪怕需要编造),确保工具能拿到完整参数;
    • 这是因为工具调用对参数完整性要求高(比如调用「查询用户信息」工具,必须传 name 才能执行)。
2.2.2 Adding Description

当 LLM 生成的结构化输出不符合预期时(比如字段格式错误、取值不符合要求),可以给定义结构化数据的类(如 Person 记录类)和字段添加 @Description 注解:

  • 对类的注解(如 @Description("a person")):告诉 LLM 这个类整体代表什么含义;
  • 对字段的注解(如 @Description("person's age, for example: 42")):明确字段的含义、格式、示例值,引导 LLM 生成符合要求的字段值。

如下:

java 复制代码
@Description("a person")
record Person(@Description("person's first and last name, for example: John Doe") String name,
              @Description("person's age, for example: 42") int age,
              @Description("person's height in meters, for example: 1.78") double height,
              @Description("is person married or not, for example: false") boolean married) {
}

这些注解会被融入给 LLM 的提示词或 JSON Schema 中,提升输出准确性。


@Description 注解仅对类/记录类类的字段 有效,直接标注在枚举(enum)的枚举值上时(如 Priority 中的 CRITICAL/HIGH/LOW):

  • LangChain4j 会忽略这些注解,不会将其加入生成的 JSON Schema;
  • 也不会把这些描述传递给 LLM,无法通过这种方式引导 LLM 理解枚举值的含义。

如下:

java 复制代码
enum Priority {

    @Description("Critical issues such as payment gateway failures or security breaches.") // this is ignored
    CRITICAL,
    
    @Description("High-priority issues like major feature malfunctions or widespread outages.") // this is ignored
    HIGH,
    
    @Description("Low-priority issues such as minor bugs or cosmetic problems.") // this is ignored
    LOW
}

2.3 Limitations

JSON Schema + AI Services 方案的存在一些使用局限,具体来说如下:

  1. 仅能在 Amazon Bedrock、Azure OpenAI、Google AI Gemini、Mistral、Ollama、OpenAI 这些特定模型上使用,其他 LLM 模型不支持该特性。

  2. 配置 ChatModel 时必须显式开启 JSON Schema 支持(默认关闭),否则该功能不会生效。

  3. 如果你的场景需要 LLM 流式返回结果(比如实时输出内容),JSON Schema 方案无法使用,只能用其他方式。

  4. 数据类型有严格限制 : 支持的 POJO 内容:仅包含标量类型(String/int/boolean 等)、枚举、嵌套 POJO、以及元素为上述类型的集合/数组(List/Set/数组)。同时不支持的如下特性:

    • 递归仅 Azure OpenAI/Mistral/OpenAI 能用;
    • 完全不支持多态(不能用接口/抽象类,必须是具体的实体类);
    • 部分基础类型(如 byte/short/BigInteger 等)也不兼容 JSON Schema(但提示词方式支持)。
  5. 只要满足「模型不支持、功能未启用、类型不兼容」任一条件,AI Service 会自动放弃 JSON Schema,转而使用「提示词引导 LLM 输出」的方式(这种方式可靠性更低)。


三、Prompting + JSON Mode

官方还没给出具体内容

四、Prompting

Prompting (提示词) 方式是 LangChain4j 实现结构化输出的默认方案,只有当你显式启用「JSON Schema 功能」时,才会优先使用更可靠的 JSON Schema 方式,否则自动走提示词方式。

当未启用 JSON Schema 功能时,AI 服务会默认采用「提示词」方式:

  • 自动生成格式指令(比如要求 LLM 返回 JSON 格式),并把这些指令追加到用户消息(UserMessage)末尾;
  • LLM 按提示返回内容后,AI 服务会将返回结果解析成开发者期望的 Java 类型(如 Person 类、枚举等)。

这种纯靠提示词约束格式的方式可靠性低------LLM 可能忽略提示、返回格式不规范的内容(比如 JSON 语法错误),导致解析失败。因此文档明确建议:如果 LLM/服务商支持 JSON Schema 等更严谨的方式,优先使用前者。


下面是一些模型的特性支持情况

类型分类 JSON Schema 支持情况 Prompting 支持情况 典型场景
对象及对象集合
单个 POJO(如 Person 提取单条结构化数据(如用户信息)
POJO 集合(List<Person> 提取多条对象数据(如多用户列表)
枚举及枚举集合
单个枚举(如 Sentiment 分类结果(如情感倾向)
枚举集合(List<Sentiment> 多分类结果(如多段文本情感)
基础数据类型
常用类型(int/double/boolean 等) 提取数值、布尔值(如年龄、婚姻状态)
特殊类型(byte/BigInteger/short 处理高精度数值、字节数据
时间与键值对类型
时间类型(Date/LocalDate 等) 提取时间信息(如生日、创建时间)
Map<?,?>(键值对) 存储非固定结构的键值数据

下面是官方给的几个示例:

java 复制代码
record Person(String firstName, String lastName) {}

enum Sentiment {
    POSITIVE, NEGATIVE, NEUTRAL
}

interface Assistant {

    Person extractPersonFrom(String text);

    Set<Person> extractPeopleFrom(String text);

    Sentiment extractSentimentFrom(String text);

    List<Sentiment> extractSentimentsFrom(String text);

    List<String> generateOutline(String topic);

    boolean isSentimentPositive(String text);

    Integer extractNumberOfPeopleMentionedIn(String text);
}

最终的使用建议如下:

  • 若场景是「提取 POJO/枚举/常用基础类型」(如从文本中提取用户信息、分类标签):优先用 JSON Schema(可靠性更高,且能自动映射为 Java 对象)。
  • 若场景涉及「时间类型、MapBigInteger 等特殊类型」:只能用 Prompting,但需额外通过提示词约束格式(如明确要求时间格式为"yyyy-MM-dd"),并做好结果校验。
  • 若需「提取 POJO 集合」(如多用户列表):只能用 JSON Schema(Prompting 不支持)。

五、参考内容

  1. LangChain4J 官网
  2. 豆包
相关推荐
猫吻鱼2 天前
【LangChain4j 05】【RAG】
langchain4j
长路 ㅤ   20 天前
05、LangChain4j快速对接生图模型(百炼平台、智谱)
java开发·通义万相·智谱glm·langchain4j·ai生图
长路 ㅤ   23 天前
Milvus系列之02、Spring+Milvus实现RAG检索增强
向量数据库·apache tika·langchain4j·知识库构建·hanlp分词
长路 ㅤ   23 天前
适配AI平台的HTTP插件系统设计
langchain4j·ai插件系统·http工具调用·语雀知识库·coze插件设计
马克Markorg24 天前
SpringBoot + LangChain4j 打造企业级 RAG 智能知识库,多工具集成方案
spring boot·向量数据库·rag·qdrant·langchain4j·增强知识检索库
长路 ㅤ   1 个月前
长路的AI领域技术博客汇总文档
向量数据库·大模型部署·langchain4j·智能体agent·ai后端技术
小灵不想卷1 个月前
LangChain4j Low 和 Hight-level API
java·langchain4j
小灵不想卷1 个月前
LangChain4 初体验
java·langchain·langchain4j
小灵不想卷1 个月前
LangChain4j 多模态
java·langchain4j