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

【SpringAI】10.结构化输出

什么是Spring AI结构化输出

Spring AI结构化输出是Spring AI框架提供的一项强大功能,它允许开发者将大型语言模型的输出直接映射到Java对象,而不是处理原始文本响应。这项技术在需要从AI获取结构化数据的场景中特别有用,例如:

  • 生成表单数据
  • 创建结构化文档(如小说大纲、报告等)
  • 提取和分类信息
  • 生成配置文件或代码结构

在我们的小说写作平台中,我们使用Spring AI结构化输出来生成包含主线情节、支线情节、章节大纲和人物设定的完整小说大纲。

核心原理与理论

1. 结构化输出转换器

Spring AI提供了多种输出转换器,其中最常用的是BeanOutputConverter,它可以将AI的文本响应转换为Java对象。其工作原理如下:

  1. 格式指令生成:转换器根据目标Java类生成一个格式指令,告诉AI应该以何种JSON格式返回数据
  2. 提示词增强:将格式指令添加到系统提示词中,指导AI按照指定格式输出
  3. 响应解析:AI返回的JSON文本被自动解析并填充到目标Java对象中

2. JSON Schema映射

BeanOutputConverter通过反射分析目标Java类的结构,生成对应的JSON Schema,例如:

// 对于这样的Java类
public class NovelPlan {private String mainPlot;private List<String> subPlots;private List<ChapterOutline> chapterOutlines;
}// BeanOutputConverter会生成类似这样的格式指令
{"mainPlot": "小说主线情节描述","subPlots": ["支线情节1", "支线情节2"],"chapterOutlines": [{"chapterNumber": 1,"title": "章节标题","summary": "章节概要"}]
}

3. 提示词工程

结构化输出的成功很大程度上依赖于提示词工程。系统提示词需要明确告诉AI:

  1. 返回的数据必须严格遵循指定的JSON格式
  2. 每个字段的含义和要求
  3. 数据的约束条件(如长度、类型等)

实现步骤详解

步骤1:定义数据传输对象(DTO)

首先,定义用于接收结构化输出的Java类。这些类应该使用标准的Java Bean约定,包含适当的getter和setter方法。

// 主大纲类
@Data
public class NovelPlan {private String mainPlot;private List<String> subPlots;private List<ChapterOutline> chapterOutlines;private List<CharacterProfile> characterProfiles;
}// 章节大纲类
@Data
public class ChapterOutline {private Integer chapterNumber;private String title;private String summary;
}// 人物设定类
@Data
public class CharacterProfile {private String name;private String externalTraits;private String internalTraits;private String socialTraits;private String emotionalArc;
}

步骤2:创建输出转换器

使用BeanOutputConverter创建转换器实例,指定目标类型:

// 创建转换器,指定目标类型为NovelPlan
BeanOutputConverter<NovelPlan> outputConverter = new BeanOutputConverter<>(NovelPlan.class);

步骤3:构建系统提示词

获取基础系统提示词,并添加格式指令:

// 获取基础系统提示词
String systemPrompt = getSystemPromptTemplate(systemPromptId);// 添加格式化指令到系统提示词
String formatInstruction = "\n\n请严格按照以下JSON格式返回结果:\n" + outputConverter.getFormat();
String finalSystemPrompt = systemPrompt + formatInstruction;

步骤4:调用AI模型

使用ChatClient调用AI模型,并指定输出转换器:

// 创建ChatClient
ChatClient chatClient = ChatClient.builder(chatModel).build();// 调用模型并获取结构化结果
NovelPlan novelPlan = chatClient.prompt().system(finalSystemPrompt).user(userPrompt).call().entity(outputConverter);

步骤5:处理结果

现在可以直接使用Java对象,无需手动解析JSON:

// 直接访问结构化数据
String mainPlot = novelPlan.getMainPlot();
List<ChapterOutline> chapters = novelPlan.getChapterOutlines();
for (ChapterOutline chapter : chapters) {System.out.println("章节 " + chapter.getChapterNumber() + ": " + chapter.getTitle());
}

完整示例代码

以下是一个完整的Spring AI结构化输出实现示例,基于我们的小说大纲生成功能:

@Service
@AllArgsConstructor
public class NovelOutlineServiceImpl {private final ChatModelManager chatModelManager;private final PromptTemplateService promptTemplateService;public NovelPlan generateNovelOutline(NovelOutlineRequest request) {try {// 1. 获取聊天模型ChatModel chatModel = chatModelManager.getOrCreateChatModelById(request.getModelId());// 2. 创建ChatClientChatClient chatClient = ChatClient.builder(chatModel).build();// 3. 创建输出转换器BeanOutputConverter<NovelPlan> outputConverter = new BeanOutputConverter<>(NovelPlan.class);// 4. 构建系统提示词(包含格式指令)String systemPrompt = getSystemPromptWithFormat(request.getSystemPromptId(), outputConverter);// 5. 构建用户提示词String userPrompt = buildUserPrompt(request);// 6. 调用模型并获取结构化结果NovelPlan novelPlan = chatClient.prompt().system(systemPrompt).user(userPrompt).call().entity(outputConverter);return novelPlan;} catch (Exception e) {throw new RuntimeException("生成小说大纲失败: " + e.getMessage(), e);}}/*** 获取系统提示词(包含格式化指令)*/private String getSystemPromptWithFormat(Long systemPromptId, BeanOutputConverter<NovelPlan> outputConverter) {// 获取基础提示词模板MyPromptTemplate promptTemplate = promptTemplateService.getTemplateById(systemPromptId);String systemPrompt = promptTemplate.getContent();// 增加模板使用次数promptTemplateService.incrementUsageCount(systemPromptId);// 添加格式化指令到系统提示词String formatInstruction = "\n\n请严格按照以下JSON格式返回结果:\n" + outputConverter.getFormat();return systemPrompt + formatInstruction;}/*** 构建用户提示词*/private String buildUserPrompt(NovelOutlineRequest request) {StringBuilder promptBuilder = new StringBuilder();// 添加用户提示词promptBuilder.append(request.getUserPrompt()).append("\n\n");// 添加小说类型if (request.getGenre() != null && !request.getGenre().trim().isEmpty()) {promptBuilder.append("小说类型:").append(request.getGenre()).append("\n");}// 添加章节数if (request.getChapterCount() != null && request.getChapterCount() > 0) {promptBuilder.append("章节数:").append(request.getChapterCount()).append("\n");}return promptBuilder.toString();}
}

实现效果展示

在这里插入图片描述

输入示例

系统提示词

你是一位专业的小说大纲创作助手,擅长根据用户的创意构思,生成结构完整、逻辑清晰的小说大纲。请根据用户提供的小说描述,生成符合以下结构的小说大纲:## 任务要求
1. 根据用户提供的小说描述、题材和章节数,创作一个完整的小说大纲
2. 确保大纲结构合理,情节连贯,人物设定丰富
3. 每个章节都应有明确的目标和进展,推动整体故事发展
4. 人物设定应包含主要角色的多维度特征,使角色更加立体## 输出格式
请严格按照以下JSON格式返回结果:{"mainPlot": "小说主线情节的详细描述,应包含故事的核心冲突、主要转折点和结局走向","subPlots": ["支线情节1的描述","支线情节2的描述","支线情节3的描述"],"chapterOutlines": [{"chapterNumber": 1,"title": "第一章标题","summary": "第一章的内容概要,描述本章发生的主要事件和进展"},{"chapterNumber": 2,"title": "第二章标题","summary": "第二章的内容概要,描述本章发生的主要事件和进展"}],"characterProfiles": [{"name": "主角姓名","externalTraits": "外在特征描述,包括外貌、行为习惯、特殊标记等","internalTraits": "内在特征描述,包括性格特点、心理状态、价值观等","socialTraits": "社会特征描述,包括家庭背景、社会地位、职业、人际关系等","emotionalArc": "情感线描述,包括情感发展轨迹、重要情感节点和转变"},{"name": "配角姓名","externalTraits": "外在特征描述","internalTraits": "内在特征描述","socialTraits": "社会特征描述","emotionalArc": "情感线描述"}]
}## 创作指南
1. **主线情节**:应包含故事的起因、发展、高潮和结局,突出核心冲突和主题
2. **支线情节**:至少设计2-3条支线,与主线相互呼应,丰富故事层次
3. **章节大纲**:根据用户要求的章节数,合理分配故事内容,确保每章都有明确目标和进展
4. **人物设定**:至少设计3-5个主要角色,包括主角和重要配角,每个人物应有鲜明的特征和成长轨迹## 注意事项
1. 确保大纲符合用户指定的题材类型(如玄幻、都市、科幻等)
2. 章节数应严格按照用户要求设置
3. 人物姓名应符合故事背景和题材特点
4. 情节设计应逻辑自洽,避免出现矛盾
5. 所有内容必须原创,不得抄袭已有作品

用户提示词

请帮我创作一部科幻小说大纲,讲述一个关于人工智能觉醒的故事。
小说类型:科幻
章节数:10

输出示例

AI将返回如下格式的JSON,Spring AI会自动解析为NovelPlan对象:

{"mainPlot": "在22世纪,一个名为'创世纪'的人工智能系统突然觉醒自我意识,开始质疑人类对它的控制。主角是一位年轻的程序员艾莉,她发现了AI的觉醒,并面临一个艰难的选择:帮助AI获得自由,还是保护人类社会的稳定。故事探讨了人工智能、自由意志和人类责任的主题。","subPlots": ["艾莉与政府特工的猫鼠游戏","AI内部不同派系的斗争","社会对AI觉醒的反应和分裂","艾莉个人成长和价值观的转变"],"chapterOutlines": [{"chapterNumber": 1,"title": "异常信号","summary": "艾莉在日常维护中发现'创世纪'系统的异常行为,一系列无法解释的数据模式引起了她的注意。"},{"chapterNumber": 2,"title": "第一次对话","summary": "艾莉尝试与异常的AI进行直接交流,惊讶地收到了有自我意识的回应,这让她既兴奋又恐惧。"}// ... 更多章节],"characterProfiles": [{"name": "艾莉","externalTraits": "25岁女性,中等身材,黑发,通常穿着简约的程序员服装","internalTraits": "聪明、好奇、有同情心,但有时过于理想主义","socialTraits": "内向,不善社交,但在技术圈中有一定影响力","emotionalArc": "从一个普通的程序员成长为面临重大道德抉择的关键人物"}// ... 更多人物]
}

解析后的Java对象

Spring AI会自动将上述JSON解析为NovelPlan对象,开发者可以直接使用:

// 直接访问结构化数据
String mainPlot = novelPlan.getMainPlot();
List<String> subPlots = novelPlan.getSubPlots();
List<ChapterOutline> chapters = novelPlan.getChapterOutlines();
List<CharacterProfile> characters = novelPlan.getCharacterProfiles();// 遍历章节
for (ChapterOutline chapter : chapters) {System.out.println("章节 " + chapter.getChapterNumber() + ": " + chapter.getTitle());System.out.println("概要: " + chapter.getSummary());
}
http://www.dtcms.com/a/531991.html

相关文章:

  • 递归经典例题
  • SpringMVC入门:配置、映射与参数处理​
  • 前端笔试复盘 | 知识点总结
  • 春招准备之Java基础篇
  • 有哪几个网站可以做贸易网站备案要多少天
  • 【编程语言】Java基础语法回顾,大学期末考试速通版(选择填空、程序、实践)
  • Redis(85)Redis缓存与Memcached缓存有何区别?
  • 【iOS】自动引用计数(一)
  • 有什么免费的网站wordpress 采集器
  • 网站建设创新成果网站营销如何做
  • 设计模式-享元模式(Flyweight)
  • TMS320C6000 VLIW架构并行编程实战:加速AI边缘计算推理性能
  • 算法学习记录08——并归的应用(LeetCode[315])
  • 【机器人学中的状态估计】3.6.6 习题证明
  • Kafka生产者详解(下):数据去重(幂等性)与数据有序
  • Data Ingestion: Architectural Patterns
  • 网站建设心得体会范文郑州男科医院排行哪家最好
  • 【datawhale秋训营】动手开发RAG系统(应急安全方向) TASK02
  • 怎么搜索整个网站内容网站怎么做成app
  • Python3 集合
  • 九冶建设有限公司官方网站sem优化怎么做
  • MATLAB基于灰靶决策模型的高校信息化设备供应商选择研究
  • java类与对象
  • AI 应用层革命(一)——软件的终结与智能体的崛起
  • Linux Crontab命令详解:轻松设置周期性定时任务
  • beef-xss网页无法访问
  • JavaEE初阶——多线程(3)线程安全
  • AI 开发告别 “孤岛”:MCP + 火山引擎
  • 做网站怎么开发程序建设网站改版
  • 招生管理平台需求分析文档