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

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

相关推荐
weixin_437497773 小时前
读书笔记:Context Engineering 2.0 (上)
人工智能·nlp
喝拿铁写前端3 小时前
前端开发者使用 AI 的能力层级——从表面使用到工程化能力的真正分水岭
前端·人工智能·程序员
goodfat3 小时前
Win11如何关闭自动更新 Win11暂停系统更新的设置方法【教程】
人工智能·禁止windows更新·win11优化工具
北京领雁科技4 小时前
领雁科技反洗钱案例白皮书暨人工智能在反洗钱系统中的深度应用
人工智能·科技·安全
落叶,听雪4 小时前
河南建站系统哪个好
大数据·人工智能·python
清月电子4 小时前
杰理AC109N系列AC1082 AC1074 AC1090 芯片停产替代及资料说明
人工智能·单片机·嵌入式硬件·物联网
Dev7z4 小时前
非线性MPC在自动驾驶路径跟踪与避障控制中的应用及Matlab实现
人工智能·matlab·自动驾驶
七月shi人4 小时前
AI浪潮下,前端路在何方
前端·人工智能·ai编程
橙汁味的风5 小时前
1隐马尔科夫模型HMM与条件随机场CRF
人工智能·深度学习·机器学习
itwangyang5205 小时前
AIDD-人工智能药物设计-AI 制药编码之战:预测癌症反应,选对方法是关键
人工智能