探索LLM推理全阶段的JSON格式输出限制方法

AITNT-国内领先的一站式人工智能新闻资讯网站
# 热门搜索 #
AITNT-国内领先的一站式人工智能新闻资讯网站 搜索
探索LLM推理全阶段的JSON格式输出限制方法
9572点击    2024-10-31 10:14

文章详细讨论了如何确保大型语言模型(LLMs)输出结构化的JSON格式,这对于提高数据处理的自动化程度和系统的互操作性至关重要。


一、引言


1.1 JSON结构化输出的意义


对于基于大型语言模型(LLMs)的应用而言,确保输出能直接以结构化的JSON格式呈现,对于提升数据处理的自动化程度、增强系统的互操作性具有重要意义。例如,客户需要对LLM的输出进行信息提取时,若输出是一个JSON格式则会大大方便工程链路上的后处理;例如,LLM在调用工具(或其它智能体)时,需要按照工具要求传入正确的参数,若能保证LLM的输出是结构化的JSON,则能保证传参正确,从而正确调用工具。

然而,在实践中,即使我们在提示词中反复告诉模型要输出JSON结构,LLM还是偶尔出错。虽然“偶尔”出错的概率很低,但对于工程链路的设计来说,是致命且麻烦的。


1.2 LLM为何不能严格输出JSON


LLM在推理时,基于已经输出的句子,从词汇表中预测下一个词。预测时,为词汇表中的每个词分配一个概率,通过采样得到预测输出,如图1所示。例如,模型在输出"My name is"后,仅有0.62的概率输出自己的名字"Tang",即使我们在提示词中告诉了模型自己的名字是"Tang",模型也有0.38的概率输出别的名字。


探索LLM推理全阶段的JSON格式输出限制方法

图1. LLM推理的预测概率示意


因此,这个依概率采样的推理过程决定了LLM不可能100%按要求输出JSON格式。错误的JSON输出导致了我们在工程链路上无法作后续的解析,因此,能100%严格限制JSON格式输出的方法非常重要。


1.3 友商方案


  • OpenAI JSON Mode


推出于2023年12月份,基于提示词优化,用户仍需要在提示词中给出JSON示例,不能保证严格100%输出JSON。


  • Kimi JSON Mode


近期推出。类似OpenAI的 JSON Mode,用户仍需要在提示词中给出JSON示例,不能保证严格100%输出JSON。


  • OpenAI Structured Outputs


推出于2024年8月份,根据用户给出的JSON示例,严格保证100%输出JSON格式。

https://openai.com/index/introducing-structured-outputs-in-the-api/


探索LLM推理全阶段的JSON格式输出限制方法

图2. OpenAI输出JSON格式的方法,橙、黄、绿分别代表提示词优化、微调、动态限制解码法的JSON输出准确率


(动态限制解码法准确率为100%)


二、前中后三阶段的优化策略 


Motivation: 在一个基于通义千问的AI教评项目场景中,JSON格式输出对客户十分重要。因此,我们在该项目实践中由浅入深,从LLM推理的前、中、后三个阶段探索了限制输出JSON格式的方法。其中,“推理前”和“推理后”这两个阶段的方法用在了项目实践中,大大提高了AI教评任务中JSON格式的输出概率。为了进一步研究如何100%输出JSON格式,我们借他山之石,研究了OpenAI的Structured Outputs方法,在“推理中”这一阶段探索并验证了基于动态限制解码的100%输出JSON格式方法。


在分析相关工作基础上,我们将深入讨论每阶段的方法、优劣及其实现方式,以期帮助读者掌握提升JSON输出概率的办法,并应用在实践中。


2.1 推理“前”:Prompt Engineering


(以下提示词来自大量项目实践验证)


在提示词中加入这句话“The JSON object:json”可提高JSON输出概率。(别问,问就是大量实践总结的经验~)


在提示词中给出"##输出格式规范",并给出JSON示例```json ... ```



## 输出格式规范:

```json

[{

 "name":"<评价维度>",

 "mentions":"< 提及次数 >",

 "references":[{

  "time":"<发言时间>",

  "text":"<原文内容>"}]}]

```


The JSON object:json


【实践】


在利用Qwen-long作AI教评的一个项目中,我们需要从教师的课堂录音文本中提取结构化的教学维度信息。采用本节中的prompt加上2.3中的JSON后处理方法后,输出样本基本是符合预期的结构化JSON。JSON正确概率从50%左右上升到了95%。可见仅靠prompt和后处理,已经能以很高的概率使得大模型按照JSON格式输出。然而,在一些需要严谨输出JSON格式的场景,100%严格输出JSON格式的方法仍值得研究。


【优势】


实施简便,无需模型架构调整,可以大幅提高输出JSON的概率。


【不足】


高度依赖于人工设计的prompt,灵活性受限。不能100%输出JSON


2.2 推理“中”:基于动态限制解码实现100%输出JSON


【原理】


LLM依据已输出的词,从词汇表中预测下一个词,可以在词汇表中将不符合JSON规范的词概率置零,从而防止输出不符合JSON规范。(原理偏复杂,可跳过本节直接看结论)。假设我们想让LLM的输出为一个城市的如下信息:



city_info_schema=[{

 "name":"城市名",

 "country":"城市所属国家",

 "latitude":"城市纬度",

 "population":"城市人口(千万)",

 "top 3 landmarks":["知名景点1","知名景点2","知名景点3"]

}]


如上代码块所示,在内存中定义JSON输出的模式city_info_schema。LLM每轮逐个单词输出"response",对于JSON的"key"值,如"name",我们直接从内存拼接到输出字符串"response_str"中;对于JSON的"value",则让LLM通过推理产生。当用户提出问题“请填写杭州的城市信息”后,动态限制解码流程如下:


探索LLM推理全阶段的JSON格式输出限制方法

图3. 动态限制解码法示意图。其中只有绿色词是LLM的推理产生。


上图展示了动态限制解码的工作流程,每一轮推理过程我们给定了JSON的“键”,仅让模型推理“值”。可以进一步用正则式(Python re库)限制我们想要的输出格式:


city_regex = (

  r"""\{\n"""

  + r""" "name": "[\w\d\s]{1,16}",\n"""

  + r""" "country": "[\w\d\s]{1,16}",\n"""

  + r""" "latitude": [-+]?[0-9]*\.?[0-9]{0,2},\n"""

  + r""" "population": [-+]?[0-9]{1,9},\n"""

  + r""" "top 3 landmarks": \["[\w\d\s]{1,16}", "[\w\d\s]{1,16}", "[\w\d\s]{1,16}"\]\n"""

  + r"""\}"""

)


在推理过程中,根据正则式限制输出格式的流程如下:


探索LLM推理全阶段的JSON格式输出限制方法

图4. 动态限制解码法的”推理-限制-采样-拼接”流程


如第一个键"key"对应的"name",我们用正则式限制其必须输出16个字以内的英文,则"杭"的概率由于不符合正则式要求,预测概率置零,模型一定会按照我们的要求输出。


由于动态限制解码技术需要我们有冻结模型解码过程、改变词汇表采样概率、改变模型输入的权限,目前在线的API接口。


不支持编写动态限制解码算法。但是可以在本地部署模型以实现动态限制解码。


【实践】


在PAI平台的免费体验DSW(NVIDIA A10)上本地部署Qwen2-7B-Instruct实现动态限制解码。基于开源的sglang库,可快速部署动态限制解码算法。


pip install --upgrade pip

pip install "sglang[all]"

# Install FlashInfer CUDA kernels

wget "https://modelscope.oss-cn-beijing.aliyuncs.com/resource/flashinfer-0.1.2%2Bcu121torch2.3-cp310-cp310-linux_x86_64.whl"

pip install flashinfer-0.1.2+cu121torch2.3-cp310-cp310-linux_x86_64.whl


modelscope download --model=qwen/Qwen2-7B-Instruct --local_dir ./Qwen2-7B-Instruct


python3 -m sglang.launch_server --model-path Qwen2-7B-Instruct --port 30000


探索LLM推理全阶段的JSON格式输出限制方法

图5. sglang框架下的千问模型本地部署成功示意图


显示上图即部署成功。


###导入库

import json

import time

from sglang import set_default_backend, RuntimeEndpoint

import sglang as sgl

from sglang.test.test_utils import (

  add_common_sglang_args_and_parse,

  select_sglang_backend,

)

from sglang.utils import dump_state_text, read_jsonl

##定义“限制模型输出的正则式”

city_regex = (

  r"""\{\n"""

  + r""" "name": "[\w\d\s]{1,16}",\n"""

  + r""" "country": "[\w\d\s]{1,16}",\n"""

  + r""" "latitude": [-+]?[0-9]*\.?[0-9]{0,2},\n"""

  + r""" "population": [-+]?[0-9]{1,9},\n"""

  + r""" "top 3 landmarks": \["[\w\d\s]{1,16}", "[\w\d\s]{1,16}", "[\w\d\s]{1,16}"\]\n"""

  + r"""\}"""

)

## 将正则式应用在输出范式中

@sgl.function

def chat_example(s,question):

  s += sgl.system("You are a helpful assistant.")

  # Same as: s += s.system("You are a helpful assistant.")


  with s.user():

    s += question


  s += sgl.assistant_begin()

  s += "Answer: " + sgl.gen("json_output", max_tokens=256, regex=city_regex)

  s += sgl.assistant_end()

## 设置Qwen2的本地通信端口,上图设置为port30000

set_default_backend(RuntimeEndpoint("http://localhost:30000"))

## 捕捉用户输入

state = chat_example.run(

  question=input("请输入城市名:"),

  # temperature=0.1,

  stream=True

)

## 打印必然的JSON输出结果

for out in state.text_iter():

  print(out, end="", flush=True)


运行效果:试输入“杭州”和“纽约”两个城市。输出严格按照了正则式的限制。


探索LLM推理全阶段的JSON格式输出限制方法


探索LLM推理全阶段的JSON格式输出限制方法

图6. 基于动态限制解码的JSON格式输出结果。


【优势】


  • 100%严格输出JSON格式,甚至是任意正则式可以定义的格式。


  • 在输出的JSON中,节省了输出"key"值的token:因为"key"值是内存中定义好的,不需要由LLM推理而得。因此,相对于prompt的方式让模型输出全JSON的方式,节省了输出的token数量。(这也是为什么OpenAI的JSON 模式每token价格有30%的折扣的原因)


【不足】


  • 必须本地部署LLM。


2.3 推理“后”:JSON数据后处理


在模型返回response后,也可以利用后处理的技术,校正JSON结构以提高JSON输出的概率。


  • JSON Repair库


Python 的json_repair库,可以解决一部分模型输出JSON格式不规范的问题。



from json_repair import loads #pip install json_repair

import json


if __name__ == '__main__':

   

  bad_string= '''

[

      {

        "foo": "Foo bar baz",

        "tag": "foo-bar-baz"

      },

      {

        "中文": "foo bar foobar foo bar baz.",

        "标签": "foo-bar-foobar"

      }

    ]

'''

   

  parsed_json = loads(bad_string)

  json_str = json.dumps(parsed_json,ensure_ascii=False)

  print(json_str)


经实践验证,json_repair可以解决输出的JSON中缺少"},],"的问题。


  • 随机种子控制:可改变LLM推理的seed, 在不同的seed下输出以减少出错概率。


三、总结与展望


以上介绍的三种类型的方法,可以同时使用,但需要注意不同的场景限制:


【前、中、后三阶段方法总结】


探索LLM推理全阶段的JSON格式输出限制方法


【彩蛋】


qwen-max-0919、qwen-max-latest、qwen-plus、qwen-plus-0919、qwen-plus-latest、qwen-turbo-0919、qwen-turbo-latest以及qwen2.5系列模型已支持结构化输出JSON。(设置response_format = { "type": "json_object" } )


文章来自于“AI小智”,作者“唐殊”。


探索LLM推理全阶段的JSON格式输出限制方法

关键词: AI , LLMs , 模型训练 , 人工智能
AITNT-国内领先的一站式人工智能新闻资讯网站
AITNT资源拓展
根据文章内容,系统为您匹配了更有价值的资源信息。内容由AI生成,仅供参考
1
AI代理

【开源免费】Browser-use 是一个用户AI代理直接可以控制浏览器的工具。它能够让AI 自动执行浏览器中的各种任务,如比较价格、添加购物车、回复各种社交媒体等。

项目地址:https://github.com/browser-use/browser-use


2
AI工作流

【开源免费】n8n是一个可以自定义工作流的AI项目,它提供了200个工作节点来帮助用户实现工作流的编排。

项目地址:https://github.com/n8n-io/n8n

在线使用:https://n8n.io/(付费)


【开源免费】DB-GPT是一个AI原生数据应用开发框架,它提供开发多模型管理(SMMF)、Text2SQL效果优化、RAG框架以及优化、Multi-Agents框架协作、AWEL(智能体工作流编排)等多种技术能力,让围绕数据库构建大模型应用更简单、更方便。

项目地址:https://github.com/eosphoros-ai/DB-GPT?tab=readme-ov-file



【开源免费】VectorVein是一个不需要任何编程基础,任何人都能用的AI工作流编辑工具。你可以将复杂的工作分解成多个步骤,并通过VectorVein固定并让AI依次完成。VectorVein是字节coze的平替产品。

项目地址:https://github.com/AndersonBY/vector-vein?tab=readme-ov-file

在线使用:https://vectorvein.ai/(付费)

3
智能体

【开源免费】AutoGPT是一个允许用户创建和运行智能体的(AI Agents)项目。用户创建的智能体能够自动执行各种任务,从而让AI有步骤的去解决实际问题。

项目地址:https://github.com/Significant-Gravitas/AutoGPT


【开源免费】MetaGPT是一个“软件开发公司”的智能体项目,只需要输入一句话的老板需求,MetaGPT即可输出用户故事 / 竞品分析 / 需求 / 数据结构 / APIs / 文件等软件开发的相关内容。MetaGPT内置了各种AI角色,包括产品经理 / 架构师 / 项目经理 / 工程师,MetaGPT提供了一个精心调配的软件公司研发全过程的SOP。

项目地址:https://github.com/geekan/MetaGPT/blob/main/docs/README_CN.md

4
微调

【开源免费】XTuner 是一个高效、灵活、全能的轻量化大模型微调工具库。它帮助开发者提供一个简单易用的平台,可以对大语言模型(LLM)和多模态图文模型(VLM)进行预训练和轻量级微调。XTuner 支持多种微调算法,如 QLoRA、LoRA 和全量参数微调。

项目地址:https://github.com/InternLM/xtuner

5
prompt

【开源免费】LangGPT 是一个通过结构化和模板化的方法,编写高质量的AI提示词的开源项目。它可以让任何非专业的用户轻松创建高水平的提示词,进而高质量的帮助用户通过AI解决问题。

项目地址:https://github.com/langgptai/LangGPT/blob/main/README_zh.md

在线使用:https://kimi.moonshot.cn/kimiplus/conpg00t7lagbbsfqkq0