如何让大模型返回Json结构数据及技术方案分析

JSON格式的优势

在应用开发过程中,其实我们使用的最多的结构为json结构,其有非常明显的优势;

  • 轻量级:格式紧凑,比XML等其他数据交换格式更轻便,因此传输速度更快,占用带宽更少。
  • 易于解析:基于文本,在各种编程语言中非常流行,都可以轻松解析和生成JSON数据。
  • 平台无关性:语言无关性,可以在不同的系统和编程语言之间无缝交换数据。
  • 支持复杂数据结构:表示复杂的对象和数组结构,这使得它非常适合表示层次化数据。
  • 易于人类阅读:键值对格式,易于理解和阅读。
  • 自描述性:有意义的键,这有助于理解数据结构的目的。

所以在AI应用开发,希望AI大模型可以返回Josn结构的数据,方便与现有系统的集成,同时也方便结果的解析。

大模型如何返回Json

提示词方案

在早期大模型并不支持json结构数据返回,到目前为止也不是所有的大模型都支持的。前期只能通过prompt(提示词)来实现。

prompt:查询某个导演最受欢迎的电影,包括:电影名称、电影的描述、电影的发行时间、演员列表,其中演员信息包括:姓名、年龄、参演过的最受欢迎的电影。

通过prompt提示词,我们希望大模型能够返回如下的json结构的数据。

json 复制代码
{
    "name":xxxx,
    "description": xxxx,
    "publishDate": xxxx,
    "performers": [
        {
            "name":xxx,
            "age":xxx,
            "films": [xxx,xxx,xxx]
        }
    ]
}

通过提示词让大模型返回json结构数据不够稳定,可能会出现json结构错误、缺失一些符号的情况,所以这种情况基本上不推荐,除非选择的大模型不支持Json mode方式。如果支持该方式,那么毋庸置疑选择json mode方式。

Json mode 方案

Json mode的方式使用上也非常简单,直接在请求时开启即可,比如;

  • 对于OpenAI

    java 复制代码
    OpenAiChatModel.builder()  
            ...  
            .responseFormat("json_object")  
            .build();
  • 对于Azure OpenAI

    java 复制代码
    AzureOpenAiChatModel.builder()  
            ...  
            .responseFormat(new ChatCompletionsJsonResponseFormat())  
            .build();
  • 对于 Vertex AI Gemini

    java 复制代码
    VertexAiGeminiChatModel.builder()  
        ...  
        .responseMimeType("application/json")  
        .build();
  • 对于 Mistral AI

    java 复制代码
    MistralAiChatModel.builder()  
        ...  
        .responseFormat(MistralAiResponseFormatType.JSON_OBJECT)  
        .build();
  • 对于Ollama

    java 复制代码
    OllamaChatModel.builder()  
            ...  
            .format("json")  
            .build();

该方式依然需要写好提示词,然后开启json mode。在可靠性上该方式没有任何问题,但是在返回性能会比普通差一些,这个很好理解,因为要处理json相关内容。

Json Schema 方案

在Json mode的方式,依然需要我们使用自然语言进行描述json的返回。但是当json的结构或者校验逻辑足够复杂时,自然语言描述显得有些力不从心了。

Json Schema不仅能约定Json的结构,还能约定数据类型、文本规则等,也是大模型支持比较好的方式。对Spring AI 结构化输出源码分析时,其就是使用的Json Schema方案。 然后我们在分析getFormat()方法

java 复制代码
// 根据返回值的类型,转换为json schema,使用jackson和jsonschema-generator配合完成
private void generateSchema() {
    JacksonModule jacksonModule = new JacksonModule();
    SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(DRAFT_2020_12, PLAIN_JSON)
       .with(jacksonModule);
    SchemaGeneratorConfig config = configBuilder.build();
    SchemaGenerator generator = new SchemaGenerator(config);
    JsonNode jsonNode = generator.generateSchema(this.typeRef.getType());
    ObjectWriter objectWriter = new ObjectMapper().writer(new DefaultPrettyPrinter()
       .withObjectIndenter(new DefaultIndenter().withLinefeed(System.lineSeparator())));
    try {
       this.jsonSchema = objectWriter.writeValueAsString(jsonNode);
    }
    catch (JsonProcessingException e) {
       logger.error("Could not pretty print json schema for jsonNode: " + jsonNode);
       throw new RuntimeException("Could not pretty print json schema for " + this.typeRef, e);
    }
}
// 将提示词 + json schema拼接一起
public String getFormat() {
    String template = """
          Your response should be in JSON format.
          Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
          Do not include markdown code blocks in your response.
          Remove the ```json markdown from the output.
          Here is the JSON Schema instance your output must adhere to:
          ```%s```
          """;
    return String.format(template, this.jsonSchema);
}

将获取到Json Schema 拼装在提示后,让大模型按照Json Schema返回。这就是 Spring AI框架的实现方式。

一个Json Schema结构如下;

json 复制代码
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "description": {
      "type": "string"
    },
    "publishDate": {
      "type": "string"
    },
    "performers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "age": {
            "type": "string"
          },
          "films": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}

每个字段可以加上 description 加强大模型对Json的理解。使用起来好是好,但是内容不够紧凑,含有一些对大模型无意义的内容,容易导致token超窗口大小等。但是对于Java开发者来说算是比较好的选择了。

prompt:请返回xxx导演最受欢迎的电影,请严格按照 + Json Schema 返回

TS DSL 方案

可以使用TypeScript 语法来约束DSL,AI的理解能力和生成效果都非常稳定。TypeScript描述的DSL如下;

TypeScript 复制代码
export interface Film {
    name: string; // "电影名字"
    description: string; // "请用50字以内概括电影的内容"
    publisDate: date; // "电影发行时间"
    performers: {
        name: string;
        sex: 男 | 女 ;
    }[];
}

从ts定义的DSL 与 Json Schema对比,很明显 TypeScript 定义的体积更小。

prompt:请返回xxx导演最受欢迎的电影,请严格按照 + TypeScript DSL 返回

Json格式返回适应场景

我们可以分场景进行讨论。

  • 对于非Web交互式的,比如完成函数调用,属于同步调用,非常适合使用json格式。
  • 对于Wen交互式的,需要流式返回的,一部分一部分返回,导致json结构被破坏无法完成展示,无法使用json格式。
  • json 格式过大,比如{}, : 无用字符,需要较高的调用成本,对成本要求的 不适合用json格式。

那么我们选择什么格式的数据返回呢?

思考方向就是,既有结构,还的体积小。通过分析yml结构数据满足我们的需求,不仅拥有更小的体积,同时支持流式解析。

总结

一般推荐使用:json mode + prompt + typescript 方式。因为Json mode是大模型支持的,是最可靠的。 如果有特殊需求,比如流式输出:yml + prompt + typescript 方式。

在思考技术方案,思维要发散,多种技术结合,才能有创新!!!!

相关推荐
昨日之日20061 小时前
Moonshine - 新型开源ASR(语音识别)模型,体积小,速度快,比OpenAI Whisper快五倍 本地一键整合包下载
人工智能·whisper·语音识别
浮生如梦_1 小时前
Halcon基于laws纹理特征的SVM分类
图像处理·人工智能·算法·支持向量机·计算机视觉·分类·视觉检测
深度学习lover1 小时前
<项目代码>YOLOv8 苹果腐烂识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·苹果腐烂识别
热爱跑步的恒川2 小时前
【论文复现】基于图卷积网络的轻量化推荐模型
网络·人工智能·开源·aigc·ai编程
阡之尘埃4 小时前
Python数据分析案例61——信贷风控评分卡模型(A卡)(scorecardpy 全面解析)
人工智能·python·机器学习·数据分析·智能风控·信贷风控
孙同学要努力6 小时前
全连接神经网络案例——手写数字识别
人工智能·深度学习·神经网络
Eric.Lee20216 小时前
yolo v5 开源项目
人工智能·yolo·目标检测·计算机视觉
其实吧37 小时前
基于Matlab的图像融合研究设计
人工智能·计算机视觉·matlab
丕羽7 小时前
【Pytorch】基本语法
人工智能·pytorch·python
ctrey_7 小时前
2024-11-1 学习人工智能的Day20 openCV(2)
人工智能·opencv·学习