领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多更新:(2024 年 4 月 6 日) 添加了使用新的流畅 ChatClient API 进行结构化输出的代码片段。
更新:(2024 年 5 月 17 日) 为 BeanOutputConverter 添加了 泛型 支持。
科学研究的是事物之间断裂的部分和碎片,并假设它们之间存在连续性,而艺术则只研究事物之间的连续性,并假设它们之间存在断裂的部分和碎片。——罗伯特·M·派尔斯
大型语言模型 (LLM) 生成结构化输出的能力对于依赖可靠解析输出值的后续应用程序非常重要。开发人员希望能够快速将 AI 模型的结果转换为数据类型(例如 JSON、XML 或 Java 类),以便将其传递给应用程序中的其他函数和方法。
Spring AI 的结构化输出转换器
有助于将 LLM 输出转换为结构化格式。如下所示,此方法围绕 LLM 文本补全端点运行。
使用通用补全 API 从大型语言模型 (LLM) 生成结构化输出需要仔细处理输入和输出。结构化输出转换器在 LLM 调用前后都发挥着至关重要的作用,确保获得所需的输出结构。
在 LLM 调用之前,转换器会将格式说明附加到提示中,为模型提供有关生成所需输出结构的明确指导。这些说明充当蓝图,使模型的响应符合指定的格式。
在 LLM 调用之后,转换器获取模型的输出文本并将其转换为结构化类型的实例。此转换过程涉及解析原始文本输出并将其映射到相应的结构化数据表示形式,例如 JSON、XML 或特定于域的数据结构。
请注意,AI 模型不能保证按要求返回结构化输出。它可能无法理解提示或无法按要求生成结构化输出。
提示:如果您不想深入了解 API 的细节,可以跳过下一段,直接跳转到使用转换器部分。
StructuredOutputConverter
接口定义如下:
public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider {
}
它使用目标结构化类型T
进行参数化,并结合了 Spring 的Converter<String, T>接口和FormatProvider
接口。
public interface FormatProvider {
String getFormat();
}
下图说明了数据流通过结构化输出 API 组件的方式。
FormatProvider
为 AI 模型提供文本说明,以格式化生成的文本输出,以便Converter
可以将其解析为目标类型T
。例如,格式可以是这样的:
Your response should be in JSON format.
The data structure for the JSON should match this Java class: java.util.HashMap
Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
格式说明通常使用PromptTemplate附加到用户输入的末尾,例如:
StructuredOutputConverter outputConverter = ...
String userInputTemplate = """
... user text input ....
{format}
"""; // user input with a "format" placeholder.
Prompt prompt = new Prompt(
new PromptTemplate(
userInputTemplate,
Map.of(..., "format", outputConverter.getFormat()) // replace the "format" placeholder with the converter's format.
).createMessage());
Converter<String, T>
负责将模型输出文本转换为目标T
类型的实例。
目前,Spring AI 提供了AbstractConversionServiceOutputConverter
、AbstractMessageOutputConverter
、BeanOutputConverter
、MapOutputConverter
和ListOutputConverter
实现。
AbstractConversionServiceOutputConverter<T>
- 提供了一个预配置的GenericConversionService,用于将 LLM 输出转换为所需的格式。没有提供默认的FormatProvider
实现。AbstractMessageOutputConverter<T>
- 提供了一个预配置的MessageConverter,用于将 LLM 输出转换为所需的格式。没有提供默认的FormatProvider
实现。FormatProvider
实现,该实现指示 AI 模型生成符合从指定的 Java 类派生的DRAFT_2020_12
、JSON Schema
的 JSON 响应。随后,它使用ObjectMapper
将 JSON 输出反序列化为目标类的 Java 对象实例。AbstractMessageOutputConverter
的功能,并包含一个FormatProvider
实现,该实现指导 AI 模型生成符合 RFC8259 的 JSON 响应。此外,它还包含一个转换器实现,该实现使用提供的MessageConverter
将 JSON 有效负载转换为java.util.Map<String, Object>
实例。AbstractConversionServiceOutputConverter
,并包含一个针对逗号分隔列表输出量身定制的FormatProvider
实现。转换器实现使用提供的ConversionService
将模型文本输出转换为java.util.List
。后续部分提供了有关利用可用转换器生成结构化输出的详细指南。源代码可在spring-ai-structured-output-demo存储库中找到。
以下示例演示了如何使用BeanOutputConverter
生成演员的电影作品列表。
表示演员电影作品的目标记录:
record ActorsFilms(String actor, List<String> movies) {
}
以下是如何使用新的流畅 ChatClient API 利用 BeanOutputConverter:
ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()
.user(u -> u.text("Generate the filmography of 5 movies for {actor}.")
.param("actor", "Tom Hanks"))
.call()
.entity(ActorsFilms.class);
或者直接使用低级 ChatModel API:
String userInputTemplate = """
Generate the filmography of 5 movies for {actor}.
{format}
""";
BeanOutputConverter<ActorsFilms> beanOutputConverter = new BeanOutputConverter<>(ActorsFilms.class);
String format = beanOutputConverter.getFormat();
String actor = "Tom Hanks";
Prompt prompt = new Prompt(
new PromptTemplate(userInputTemplate, Map.of("actor", actor, "format", format)).createMessage());
Generation generation = chatClient.call(prompt).getResult();
ActorsFilms actorsFilms = beanOutputConverter.convert(generation.getOutput().getContent());
使用ParameterizedTypeReference
构造函数来指定更复杂的 target class 结构。例如,表示演员及其电影作品列表:
List<ActorsFilms> actorsFilms = ChatClient.create(chatModel).prompt()
.user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
.call()
.entity(new ParameterizedTypeReference<List<ActorsFilms>>() {
});
或者直接使用低级 ChatModel API:
BeanOutputConverter<List<ActorsFilms>> outputConverter = new BeanOutputConverter<>(
new ParameterizedTypeReference<List<ActorsFilms>>() { });
String format = outputConverter.getFormat();
String template = """
Generate the filmography of 5 movies for Tom Hanks and Bill Murray.
{format}
""";
Prompt prompt = new Prompt(new PromptTemplate(template, Map.of("format", format)).createMessage());
Generation generation = chatClient.call(prompt).getResult();
List<ActorsFilms> actorsFilms = outputConverter.convert(generation.getOutput().getContent());
以下代码片段演示了如何使用MapOutputConverter
生成数字列表。
Map<String, Object> result = ChatClient.create(chatModel).prompt()
.user(u -> u.text("Provide me a List of {subject}")
.param("subject", "an array of numbers from 1 to 9 under they key name 'numbers'"))
.call()
.entity(new ParameterizedTypeReference<Map<String, Object>>() {
});
或者直接使用低级 ChatModel API:
MapOutputConverter mapOutputConverter = new MapOutputConverter();
String format = mapOutputConverter.getFormat();
String userInputTemplate = """
Provide me a List of {subject}
{format}
""";
PromptTemplate promptTemplate = new PromptTemplate(userInputTemplate,
Map.of("subject", "an array of numbers from 1 to 9 under they key name 'numbers'", "format", format));
Prompt prompt = new Prompt(promptTemplate.createMessage());
Generation generation = chatClient.call(prompt).getResult();
Map<String, Object> result = mapOutputConverter.convert(generation.getOutput().getContent());
以下代码片段演示了如何使用ListOutputConverter
生成冰淇淋口味列表。
List<String> flavors = ChatClient.create(chatModel).prompt()
.user(u -> u.text("List five {subject}")
.param("subject", "ice cream flavors"))
.call()
.entity(new ListOutputConverter(new DefaultConversionService()));
或者直接使用低级 ChatModel API:
ListOutputConverter listOutputConverter = new ListOutputConverter(new DefaultConversionService());
String format = listOutputConverter.getFormat();
String userInputTemplate = """
List five {subject}
{format}
""";
PromptTemplate promptTemplate = new PromptTemplate(userInputTemplate,
Map.of("subject", "ice cream flavors", "format", format));
Prompt prompt = new Prompt(promptTemplate.createMessage());
Generation generation = this.chatClient.call(prompt).getResult();
List<String> list = listOutputConverter.convert(generation.getOutput().getContent());
LLM 生成结构化输出的能力使开发人员能够与下游应用程序无缝集成。结构化输出转换器
促进了这一过程,确保将模型输出可靠地解析为 JSON 或 Java 类等结构化格式。
本文提供了诸如 BeanOutputConverter
、MapOutputConverter
和 ListOutputConverter
等转换器的实际使用示例,展示了为各种数据类型生成结构化输出。
总之,Spring AI 的结构化输出转换器为开发人员提供了一个强大的解决方案,使他们能够利用 LLM 的强大功能,同时通过结构化输出格式确保其应用程序的兼容性和可靠性。