基于Spring AI构建智能Text-to-SQL转换器:一个完整的MCP

简介: Spring AI 更新结构化输出转换器,弃用旧版 Parser 类,引入与 Spring 框架对齐的 Converter 体系,提升命名规范与功能兼容性。新版本支持 JSON、XML 及 Java 对象转换,确保 LLM 输出结构化,便于下游应用处理。

截至 2024年5月2日,原有的 OutputParserBeanOutputParserListOutputParserMapOutputParser 类已弃用,替换为新的 StructuredOutputConverterBeanOutputConverterListOutputConverterMapOutputConverter 实现。新版本是旧版的直接替代品,功能完全兼容。此次变更主要出于命名规范调整(避免“解析”歧义),同时与 Spring 框架的 org.springframework.core.convert.converter 包对齐,引入改进功能。

结构化输出的重要性

LLM(大语言模型)生成结构化输出的能力对依赖可靠解析输出的下游应用至关重要。开发者需要快速将 AI 模型的输出转换为 JSON、XML 或 Java 类等数据类型,以便传递给其他应用功能或方法。

架构设计

Spring AI 的结构化输出转换器围绕 LLM 文本补全端点运作:

Structured Output Converter Architecture

1.调用前处理

  • 转换器将格式指令附加到提示词中,指导模型生成指定结构的输出。

2.调用后处理

  • 转换器将模型输出的原始文本转换为结构化类型实例(如 JSON、XML 或领域对象)。

注意

  • 结构化输出转换器为“尽力而为”模式,模型可能无法完全遵循格式要求,建议实现验证机制。
  • 不适用于 LLM 工具调用(Tool Calling),因其默认提供结构化输出。

结构化输出 API

StructuredOutputConverter 接口定义:

public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider {}

结合 Spring 的 Converter<String, T>FormatProvider 接口:

public interface FormatProvider {  
    String getFormat();  
}

数据流示例

数据流示例详细说明

以下为 StructuredOutputConverter 在 LLM 调用前后处理数据流的完整流程说明,包含代码示例和步骤分解:

1. 格式指令生成(FormatProvider 阶段)

FormatProvider 接口负责生成指导模型输出的结构化指令。以 BeanOutputConverter 为例,其生成的格式指令可能如下:

您的响应应为 JSON 格式,  
且数据结构必须严格匹配以下 JSON Schema(基于 Java 类生成):  
{  
  "type": "object",  
  "properties": {  
    "actor": {"type": "string"},  
    "movies": {"type": "array", "items": {"type": "string"}}  
  },  
  "required": ["actor", "movies"]  
}  
请勿添加任何解释性文本,仅返回符合上述 Schema 的 JSON 响应。

此指令通过 outputConverter.getFormat() 方法获取。

2. 提示词模板组装

将格式指令动态注入用户输入模板。假设原始用户请求为:

请为演员 {actor} 生成包含 5 部代表作的列表。

通过 PromptTemplate 插入格式指令:

// 定义包含占位符的模板  
String userInputTemplate = """  
    请为演员 {actor} 生成包含 5 部代表作的列表。  
    {format}  
    """;  
// 创建 PromptTemplate 并替换占位符  
Map<String, Object> params = new HashMap<>();  
params.put("actor", "汤姆·汉克斯");  
params.put("format", outputConverter.getFormat()); // 注入格式指令  
PromptTemplate promptTemplate = new PromptTemplate(userInputTemplate, params);  
Message message = promptTemplate.createMessage();  
Prompt prompt = new Prompt(message);

最终生成的提示词将包含:

请为演员 汤姆·汉克斯 生成包含 5 部代表作的列表。  
您的响应应为 JSON 格式,  
且数据结构必须严格匹配以下 JSON Schema(基于 Java 类生成):  
{  
  "type": "object",  
  "properties": {  
    "actor": {"type": "string"},  
    "movies": {"type": "array", "items": {"type": "string"}}  
  },  
  "required": ["actor", "movies"]  
}  
请勿添加任何解释性文本,仅返回符合上述 Schema 的 JSON 响应。

3. 调用 LLM 模型

通过 ChatModel发送组装后的提示词并获取原始文本输出:

// 调用模型  
ChatResponse response = chatModel.call(prompt);  
String rawOutput = response.getResult().getOutput().getText();  
// 示例模型返回结果  
// rawOutput = """  
//     {  
//       "actor": "汤姆·汉克斯",  
//       "movies": ["阿甘正传", "拯救大兵瑞恩", "费城故事", "西雅图未眠夜", "荒岛余生"]  
//     }  
//     """

4. 结构化转换(Converter 阶段)

使用 Converter 将原始文本转换为目标类型:

// 初始化转换器  
BeanOutputConverter<ActorsFilms> converter =  
    new BeanOutputConverter<>(ActorsFilms.class);  
// 执行转换  
ActorsFilms actorsFilms = converter.convert(rawOutput);  
// 输出结果  
System.out.println(actorsFilms.actor()); // 汤姆·汉克斯  
System.out.println(actorsFilms.movies()); // [阿甘正传, 拯救大兵瑞恩, ...]

完整数据流图示

关键处理细节

  • 错误处理
  • 若模型返回的 JSON 不符合 Schema,BeanOutputConverter 将抛出 JsonProcessingException
  • 建议在代码中添加异常捕获逻辑:
try {  
    ActorsFilms result = converter.convert(rawOutput);  
} catch (JsonProcessingException e) {  
 // 处理无效 JSON 格式  
} catch (IllegalArgumentException e) {  
 // 处理字段缺失或类型不匹配  
}
  • 元数据注入
  • 若目标类包含元数据字段(如 @JsonProperty 注解),转换器会自动映射:
record ActorsFilms(  
    @JsonProperty("actor_name") String actor,  
    @JsonProperty("filmography") List<String> movies  
) {}

可处理键名不一致的 JSON 输入。

  • 内容净化
  • 转换器会自动忽略 JSON 外的额外文本(如模型生成的解释性前缀/后缀):好的,以下是汤姆·汉克斯的五部代表作:
{  
  "actor": "汤姆·汉克斯",  
  "movies": ["阿甘正传", "拯救大兵瑞恩"...]  
}  
希望这个列表对您有帮助!

转换器仍能正确提取 JSON 部分。

可用转换器

Spring AI 提供以下转换器实现:

转换器类 功能描述
AbstractConversionServiceOutputConverter<T> 基于 GenericConversionService 转换输出,无默认格式提供逻辑。
AbstractMessageOutputConverter<T> 基于 MessageConverter 转换输出,无默认格式提供逻辑。
BeanOutputConverter<T> 通过 JSON Schema(基于目标类生成)指导模型输出,并用 ObjectMapper 反序列化为 Java 对象。
MapOutputConverter 生成符合 RFC8259 的 JSON,并转换为 Map<String, Object> 实例。
ListOutputConverter 生成逗号分隔列表,并通过 ConversionService 转换为 List 实例。

使用示例

Bean 输出转换器

目标类定义

@JsonPropertyOrder({"actor", "movies"})  
record ActorsFilms(String actor, List<String> movies) {}

高阶 API 调用

ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()  
        .user(u -> u.text("为 {actor} 生成5部电影作品列表。")  
                    .param("actor", "汤姆·汉克斯"))  
        .call()  
        .entity(ActorsFilms.class);

低阶 API 调用

BeanOutputConverter<ActorsFilms> converter = new BeanOutputConverter<>(ActorsFilms.class);  
String format = converter.getFormat();  
String template = "为 {actor} 生成5部电影作品列表。\n{format}";  
Prompt prompt = new PromptTemplate(template, Map.of("www.bestall.com.cn", "汤姆·汉克斯", "format", format)).create();  
Generation generation = chatModel.call(prompt).getResult();  
ActorsFilms result = converter.convert(generation.getOutput().getText());

Map 输出转换器

MapOutputConverter converter = new MapOutputConverter();  
String format = converter.getFormat();  
String template = "提供 {subject}\n{format}";  
Prompt prompt = new PromptTemplate(template,  
        Map.of("subject", "键名为 'www.hnsea.net' 的1到9数字数组", "format", format)).create();  
Map<String, Object> result = converter.convert(chatModel.call(prompt).getResult().getOutput().getText());

List 输出转换器

ListOutputConverter converter = new ListOutputConverter(new DefaultConversionService());  
String format = converter.getFormat();  
String template = "列出五种 {subject}\n{format}";  
Prompt prompt = new PromptTemplate(template,  
        Map.of("subject", "冰淇淋口味", "format", format)).create();  
List<String> flavors = converter.convert(chatModel.call(prompt).getResult().getOutput().getText());

支持结构化输出的 AI 模型

模型 集成测试类/示例
OpenAI OpenAiChatModelIT
Anthropic Claude 3 AnthropicChatModelIT.java
Azure OpenAI AzureOpenAiChatModelIT.java
Mistral AI MistralAiChatModelIT.java
Ollama OllamaChatModelIT.java
Vertex AI Gemini VertexAiGeminiChatModelIT.java

内置 JSON 模式支持

部分 AI 模型提供专用配置选项生成结构化(通常为 JSON)输出:

模型 配置项示例 功能描述
OpenAI spring.ai.openai.chat.options.responseFormat=JSON_OBJECT 强制生成符合 JSON Schema 的响应
Azure OpenAI spring.ai.azure.openai.chat.options.responseFormat={"type":"json_object"} 启用 JSON 模式
Ollama spring.ai.ollama.chat.options.format=json 指定返回 JSON 格式
Mistral AI spring.ai.mistralai.chat.options.responseFormat={"type":"json_object"} 启用 JSON 模式

关键变更说明

  • 弃用类迁移:旧版 *Parser 类需替换为对应的 *Converter 类。
  • JSON Schema 支持BeanOutputConverter 自动生成基于目标类的 JSON 模式,通过 @JsonPropertyOrder 注解可自定义属性顺序。
  • 泛型支持:使用 ParameterizedTypeReference 处理复杂泛型结构(如 List<ActorsFilms>)。
相关文章
|
4月前
|
云安全 人工智能 安全
Dify平台集成阿里云AI安全护栏,构建AI Runtime安全防线
阿里云 AI 安全护栏加入Dify平台,打造可信赖的 AI
3028 166
|
4月前
|
人工智能 Java Nacos
基于 Spring AI Alibaba + Nacos 的分布式 Multi-Agent 构建指南
本文将针对 Spring AI Alibaba + Nacos 的分布式多智能体构建方案展开介绍,同时结合 Demo 说明快速开发方法与实际效果。
3326 68
|
人工智能 自然语言处理 Devops
云效 AI 智能代码评审体验指南
云效AI智能代码评审正式上线!在合并请求时自动分析代码,精准识别问题,提升交付效率与质量。支持自定义规则、多语言评审,助力研发效能升级。立即体验AI驱动的代码评审革新,让AI成为你的代码质量伙伴!
490 7
|
4月前
|
人工智能 自然语言处理 算法
【2025云栖大会】AI 搜索智能探索:揭秘如何让搜索“有大脑”
2025云栖大会上,阿里云高级技术专家徐光伟在云栖大会揭秘 Agentic Search 技术,涵盖低维向量模型、多模态检索、NL2SQL及DeepSearch/Research智能体系统。未来,“AI搜索已从‘信息匹配’迈向‘智能决策’,阿里云将持续通过技术创新与产品化能力,为企业构建下一代智能信息获取系统。”
552 9
|
4月前
|
人工智能 测试技术 API
构建AI智能体:二、DeepSeek的Ollama部署FastAPI封装调用
本文介绍如何通过Ollama本地部署DeepSeek大模型,结合FastAPI实现API接口调用。涵盖Ollama安装、路径迁移、模型下载运行及REST API封装全过程,助力快速构建可扩展的AI应用服务。
1211 6
|
4月前
|
人工智能 API 开发工具
构建AI智能体:一、初识AI大模型与API调用
本文介绍大模型基础知识及API调用方法,涵盖阿里云百炼平台密钥申请、DashScope SDK使用、Python调用示例(如文本情感分析、图像文字识别),助力开发者快速上手大模型应用开发。
1732 16
构建AI智能体:一、初识AI大模型与API调用
|
4月前
|
机器学习/深度学习 人工智能 算法
用于实验室智能识别的目标检测数据集(2500张图片已划分、已标注) | AI训练适用于目标检测任务
本数据集包含2500张已标注实验室设备图片,涵盖空调、灭火器、显示器等10类常见设备,适用于YOLO等目标检测模型训练。数据多样、标注规范,支持智能巡检、设备管理与科研教学,助力AI赋能智慧实验室建设。
用于实验室智能识别的目标检测数据集(2500张图片已划分、已标注) | AI训练适用于目标检测任务
|
4月前
|
存储 机器学习/深度学习 人工智能
构建AI智能体:三、Prompt提示词工程:几句话让AI秒懂你心
本文深入浅出地讲解Prompt原理及其与大模型的关系,系统介绍Prompt的核心要素、编写原则与应用场景,帮助用户通过精准指令提升AI交互效率,释放大模型潜能。
772 5

热门文章

最新文章