如何让大模型返回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 方式。

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

相关推荐
Jaly_W几秒前
用于航空发动机故障诊断的深度分层排序网络
人工智能·深度学习·故障诊断·航空发动机
小嗷犬3 分钟前
【论文笔记】Cross-lingual few-shot sign language recognition
论文阅读·人工智能·多模态·少样本·手语翻译
夜幕龙10 分钟前
iDP3复现代码数据预处理全流程(二)——vis_dataset.py
人工智能·python·机器人
吃个糖糖27 分钟前
36 Opencv SURF 关键点检测
人工智能·opencv·计算机视觉
AI慧聚堂39 分钟前
自动化 + 人工智能:投标行业的未来是什么样的?
运维·人工智能·自动化
盛世隐者40 分钟前
【pytorch】循环神经网络
人工智能·pytorch
cdut_suye1 小时前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
开发者每周简报1 小时前
微软的AI转型故事
人工智能·microsoft
古希腊掌管学习的神1 小时前
[机器学习]sklearn入门指南(1)
人工智能·python·算法·机器学习·sklearn
普密斯科技2 小时前
手机外观边框缺陷视觉检测智慧方案
人工智能·计算机视觉·智能手机·自动化·视觉检测·集成测试