Spring AI Structured-Output源码分析

Spring AI Structured-Output源码分析

  • 在 Spring AI 中,StructuredOutputConverter(常用的实现类是 BeanOutputConverter)的核心作用是将大模型的非结构化文本输出 转换为 Java 的结构化对象(POJO)
  • StructuredOutputConverter extends Converter<String, T>, FormatProvider
    • FormatProvider:负责将你 Java Bean 的 JSON Schema
    • Converter:负责将模型返回的字符串 通过 Jackson 反序列化转换为你的Java Bean
  • 其实结构化对象本质实现原理也是基于:JSON 解析器 + Prompt 工程 + 结果解析 机制

实现思考&源码分析

如何告知大模型需要转换格式?

  • 原理

StructuredOutputConverter 接口有一个方法 getFormat()。当你调用这个方法时,它会生成一段自然语言的指令 ,这段指令中包含了你 Java Bean 的 JSON Schema。你需要手动或自动将这段指令拼接到发给 AI 的 Prompt 中。

  • 源码
    • 将你 Java Bean 的格式为 JSON Schema,并放到请求上下文中
java 复制代码
@GetMapping("/chat-format")
public BeanEntity simpleChatFormat(@RequestParam(value = "query", defaultValue = "写诗歌") String query) {
  return chatClient.prompt(query).call()
    .format(converter.getFormat()) // 显式注入格式指令
    .entity(BeanEntity.class) // 这里内部会自动调用 format()
    ;
}

//org.springframework.ai.chat.client.DefaultChatClient.DefaultCallResponseSpec#entity(java.lang.Class<T>)
public <T> T entity(Class<T> type) {
  var outputConverter = new BeanOutputConverter<>(type);
  return doSingleWithBeanOutputConverter(outputConverter);
}

@Nullable
private <T> T doSingleWithBeanOutputConverter(StructuredOutputConverter<T> outputConverter) {
  //1. 根据泛型 T 的类型,生成标准的 JSON Schema:详见BeanOutputConverter.getFormat()
  //2. 将生成的生成标准的 JSON Schema放入到上下文中,key为:"spring.ai.chat.client.output.format"
  var chatResponse = doGetObservableChatClientResponse(this.request, outputConverter.getFormat())
    .chatResponse();
  var stringResponse = getContentFromChatResponse(chatResponse);
  //3.大模型返回的字符串反序列化为JavaBean,详见:BeanOutputConverter.convert()
  return outputConverter.convert(stringResponse);
}
  • JSON Schema添加到Prompt提示词中,在Advisor链中最后一个ChatModelCallAdvisor实现
java 复制代码
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
  //1.增强格式指令,即提示词增强
  ChatClientRequest formattedChatClientRequest = augmentWithFormatInstructions(chatClientRequest);
  ChatResponse chatResponse = this.chatModel.call(formattedChatClientRequest.prompt());
  return ChatClientResponse.builder()
    .chatResponse(chatResponse)
    .context(Map.copyOf(formattedChatClientRequest.context()))
    .build();
}

private static ChatClientRequest augmentWithFormatInstructions(ChatClientRequest chatClientRequest) {
  //2.从请求上下文获取JSON Schema格式指令
  String outputFormat = (String) chatClientRequest.context().get(ChatClientAttributes.OUTPUT_FORMAT.getKey());
	//3.其实逻辑很简单:用户文本 + 换行 + JSON Schema格式指令
  Prompt augmentedPrompt = chatClientRequest.prompt()
    .augmentUserMessage(userMessage -> userMessage.mutate()
                        .text(userMessage.getText() + System.lineSeparator() + outputFormat)
                        .build());
  return ChatClientRequest.builder()
    .prompt(augmentedPrompt)
    .context(Map.copyOf(chatClientRequest.context()))
    .build();
}

大模型返回结果后是如何转换为对应的实体?

  • 原理

转换发生在应用层(你的服务内部),而不是模型层。模型返回的仍然是 String(一段 JSON 文本),Spring AI 在接收到这段文本后,调用 convert() 方法将其反序列化,详见:BeanOutputConverter.convert()。

  • 源码
java 复制代码
public T convert(@NonNull String text) {
  try {
    // 1. 预处理:清洗文本
    // 大模型经常喜欢返回 ```json { ... } ```这种 Markdown 格式
    // 这里会去除首尾的 Markdown 标记,只保留纯 JSON 字符串
    text = this.textCleaner.clean(text);

    // 2. 反序列化
    // 使用 Jackson 的 ObjectMapper 将 JSON 字符串转为 Java 对象
    return (T) this.objectMapper.readValue(text, this.objectMapper.constructType(this.type));
  }
  catch (JsonProcessingException e) {
    throw new RuntimeException(e);
  }
}

注意事项

  • 如果你使用了 StructuredOutputConverter(或者 .entity() 方法),请不要在你的 User Prompt 中再手写任何关于格式(Format)的指令,避免提示词冲突,只关注业务内容即可。
相关推荐
lanyxp13 小时前
Sentinel 管不到 SQL 这一层——我写了个 MyBatis SQL 熔断器
java
谁似人间西林客13 小时前
工业AI原生企业是什么?制造业智能化升级的新路径
大数据·人工智能·ai-native
段一凡-华北理工大学13 小时前
LangChain框架在高炉炼铁智能化领域的应用~系列文章09:工具调用Tool — 让AI学会操作高炉仪表盘
网络·人工智能·架构·langchain·高炉炼铁·高炉智能化·高炉智能体
工业胶粘剂技术13 小时前
K-1306双组份丙烯酸结构胶技术白皮书:TDS全参数解析、核壳增韧机理与高端制造选型指南
大数据·人工智能·制造
FL162386312913 小时前
国内快递面单识别检测数据集VOC+YOLO格式422张6类别
人工智能·yolo·机器学习
meilindehuzi_a13 小时前
从 Canvas 到 Vibe Coding:HTML5 游戏开发入门与 AI 飞机大战实战
前端·人工智能·html5
慧一居士13 小时前
SpringCloud 微服务Feigin 用的完整调用端和被调用的示例
java·spring cloud
把你拉进白名单14 小时前
7.OpenClaw源码解析——可靠消息投递
人工智能·llm·agent
劈星斩月14 小时前
机器学习之 定义与三大范式
人工智能·机器学习·监督学习·强化学习·无监督学习
触底反弹14 小时前
🎨 通义万相实战:用 Qwen 多模态 API 实现 AI 换装换姿势,10 行代码搞定!
vue.js·人工智能