首页 > 其他分享 >pydantic+openai+json: 控制大模型输出的最佳范式

pydantic+openai+json: 控制大模型输出的最佳范式

时间:2024-06-18 12:56:23浏览次数:21  
标签:输出 float number Item json openai type pydantic

调用大模型已经是如今做 ai 项目习以为常的工作的,但是大模型的输出很多时候是不可控的,我们又需要使用大模型去做各种下游任务,实现可控可解析的输出。我们探索了一种和 python 开发可以紧密合作的开发方法。

所有的代码都开源在了GitHub

大模型输出是按照 token 逐个预测然后解码成文本,就跟说话一样,但是有的时候我们需要用大模型做一些垂直领域的工作,例如给定一段文本,我们想知道他属于正向的还是负向的?最简单的方法就是给大模型写一段 prompt 告诉大模型请你告诉我这段文本是正向的还是负向的,只输出正向的还是负向的不要输出多余的东西。这种方法其实有两个问题

  1. 大模型有的时候挺犟的,你告诉他不要输出多余的他会说好的我不会输出多余的,这段文本的正向的/负向的
  2. 如果我们希望同时有多个输出,例如正向的还是负向的,以及对应的分数,这样的输出会很麻烦

所以,我们需要一种格式,大模型很擅长写,我们解析起来很方便,我们使用 python 开发的话也很方便,有没有呢?还真有,python 有一个库叫 pydantic,可以实现类->json->类的转换。

这里补充一个知识叫做 json scheme 是一种基于 JSON 的格式,用来描述 JSON 数据的结构。它提供了一种声明性的方式来验证 JSON 数据是否符合某种结构,这对于数据交换、数据存储以及 API 的交互等方面都非常有用。一个 JSON Schema 本身也是一个 JSON 对象,它定义了一系列的规则,这些规则说明了 JSON 数据应该满足的条件。例如,它可以指定一个 JSON 对象中必须包含哪些属性,这些属性的数据类型是什么,是否有默认值,以及其他一些约束条件。下面是一个 json scheme 的例子。

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer",
      "minimum": 0
    },
    "email": {
      "type": "string",
      "format": "email"
    }
  },
  "required": ["name", "age"]
}

ok,那怎么得到一个 json scheme,我们可以给描述或者一段 json 让大模型写,但是不够优雅,每次需要打开一个网页写写写然后复制粘贴回来。一种更优雅的方式是用 pydantic 导出,下面是一个例子, 定义一个Item 类然后使用Item.model_json_scheme()可以导出这个类的 json scheme 描述

from pydantic import BaseModel
from typing import List

class Point(BaseModel):
    x: float
    y: float
    z: float


class Item(BaseModel):
    id: int
    name: str
    description: str
    number: int
    price: float
    position: List[Point]

print(Item.model_json_schema())

他的输出是

{
  "$defs": {
    "Point": {
      "properties": {
        "x": {
          "title": "X",
          "type": "number"
        },
        "y": {
          "title": "Y",
          "type": "number"
        },
        "z": {
          "title": "Z",
          "type": "number"
        }
      },
      "required": ["x", "y", "z"],
      "title": "Point",
      "type": "object"
    }
  },
  "properties": {
    "id": {
      "title": "Id",
      "type": "integer"
    },
    "name": {
      "title": "Name",
      "type": "string"
    },
    "description": {
      "title": "Description",
      "type": "string"
    },
    "number": {
      "title": "Number",
      "type": "integer"
    },
    "price": {
      "title": "Price",
      "type": "number"
    },
    "position": {
      "items": {
        "$ref": "#/$defs/Point"
      },
      "title": "Position",
      "type": "array"
    }
  },
  "required": ["id", "name", "description", "number", "price", "position"],
  "title": "Item",
  "type": "object"
}

通过这种方式我们可以解决前面提出的第二个问题,将我们需要的多个答案写成一个 pydantic 的类,然后将 json scheme 以及问题描述作为 prompt 给大模型例如下面的这个 prompt

user_prompt = f"""
请帮我把这个物品的描述转换成json格式的数据,
json scheme格式如下:
{Item.model_json_schema()}
物品描述如下:
{item_desc}
请你分析上面的描述,按照json schema,填写信息。请一定要按照json schema的格式填写,否则会导致数据无法解析,你会被狠狠地批评的。
只需要输出可以被解析的json就够了,不需要输出其他内容。
"""

那第一个问题怎么解决呢?首先是大模型不止输出 json 还会输出一堆废话,我们可以观察到 json 前后是大括号,这个符号是一般不会出现的,所有我们可以从输出的字符串前后开始遍历,分别找到一个前大括号和一个后大括号,然后舍弃掉无关的

def extract_json(text):
    try:
        json_start = text.find("{")
        json_end = text.rfind("}") + 1
        json_content = text[json_start:json_end].replace("\\_", "_")
        return json_content
    except Exception as e:
        return f"Error extracting JSON: {e}"

获取到 json 之后,使用Item.model_validate_json(json字符串) 来构造一个实体类

当然我们也可以定义一个对象然后将他转换成 json

from pydantic import BaseModel
from typing import List


class Point(BaseModel):
    x: float
    y: float
    z: float


class Item(BaseModel):
    id: int
    name: str
    description: str
    number: int
    price: float
    position: List[Point]


item = Item(
    id=1,
    name="example",
    description="example description",
    number=1,
    price=1.0,
    position=[Point(x=1.0, y=2.0, z=3.0)],
)
print(item.model_dump_json())

输出是

{"id":1,"name":"example","description":"example description","number":1,"price":1.0,"position":[{"x":1.0,"y":2.0,"z":3.0}]}

下面我给出了一个完整的例子,使用质谱的 glm-4-air 模型,解析一个物体的描述

from enum import Enum
from typing import List
from openai import OpenAI
from pydantic import BaseModel
from dotenv import load_dotenv
from pydantic_settings import BaseSettings

load_dotenv()


class EnvSettings(BaseSettings):
    OPENAI_API_KEY: str
    OPENAI_API_BASE: str


class Point(BaseModel):
    x: float
    y: float
    z: float


class ChatRole(str, Enum):
    SYSTEM = "system"
    USER = "user"
    ASSISTANT = "assistant"


class Item(BaseModel):
    id: int
    name: str
    description: str
    number: int
    price: float
    position: List[Point]


def extract_json(text):
    try:
        json_start = text.find("{")
        json_end = text.rfind("}") + 1
        json_content = text[json_start:json_end].replace("\\_", "_")
        return json_content
    except Exception as e:
        return f"Error extracting JSON: {e}"


env_settings = EnvSettings()
client = OpenAI(
    api_key=env_settings.OPENAI_API_KEY, base_url=env_settings.OPENAI_API_BASE
)

item_desc = """
这个物品是戒指,它非常受人欢迎,它的价格是1000.7美元,编号是123456,现在还有23个库存,他的位置在(1.0, 2.0, 3.0),非常值得购买。
"""

user_prompt = f"""
请帮我把这个物品的描述转换成json格式的数据,
json scheme格式如下:
{Item.model_json_schema()}
物品描述如下:
{item_desc}
请你分析上面的描述,按照json schema,填写信息。请一定要按照json schema的格式填写,否则会导致数据无法解析,你会被狠狠地批评的。
只需要输出可以被解析的json就够了,不需要输出其他内容。
"""

resp = client.chat.completions.create(
    model="glm-4-air",
    messages=[
        {
            "role": ChatRole.SYSTEM,
            "content": "你是一个结构化数据的处理器,你精通json格式的数据,并且可以输出结构化的json数据。你可以根据给定的文字和json scheme,输出符合scheme的json数据。请注意,你的输出会直接被解析,如果格式不正确,会导致解析失败,你会被狠狠地批评的。",
        },
        {"role": ChatRole.USER, "content": user_prompt},
    ],
)

item = Item.model_validate_json(extract_json(resp.choices[0].message.content))
print(f"解析的物品信息:{item}")

json_item = item.model_dump_json()
print(f"转换成json格式:{json_item}")

输出是

解析的物品信息:id=123456 name='戒指' description='这个物品是戒指,它非常受人欢迎,它的价格是1000.7美元,编号是123456,现在还有23个库存,他的位置在(1.0, 2.0, 3.0),非常值得购买。' number=23 price=1000.7 position=[Point(x=1.0, y=2.0, z=3.0)]
转换成json格式:{"id":123456,"name":"戒指","description":"这个物品是戒指,它非常受人欢迎,它的价格是1000.7美元,编号是123456,现在还有23个库存,他的位置在(1.0, 2.0, 3.0),非常值得购买。","number":23,"price":1000.7,"position":[{"x":1.0,"y":2.0,"z":3.0}]}

标签:输出,float,number,Item,json,openai,type,pydantic
From: https://www.cnblogs.com/studyinglover/p/18254113

相关文章

  • 一个中转代码,底层调用openai,上层模拟openai
    openai的调用api几乎成为了实质性的大模型社区的调用标准,你看不论是阿里的灵积,智谱,together,vllm,ollama,fastchat等都支持openai的调用方式,所以这也是大势所趋,有时候我们想做个中间层,底层调用大模型,上层提供业务服务,特别是许多公司的多节点的agent,如果我们都保持一致那么统一性就很......
  • JSON响应中提取特定的信息——6.14山大软院项目实训2
    在收到的JSON响应中提取特定的信息(如response字段中的文本)并进行输出,需要进行JSON解析。在Unity中,可以使用JsonUtility进行简单的解析,但由于JsonUtility对嵌套对象的支持有限,通常推荐使用第三方库如Newtonsoft.Json来处理复杂的JSON结构。首先,确保Unity项目中已经包含了Newton......
  • 由心知天气服务器响应的实时天气数据并进行JSON解析
    由心知天气服务器响应的实时天气数据并进行JSON解析#include<netinet/in.h>#include<arpa/inet.h>#include<stdio.h>#include<errno.h>#include<sys/socket.h>#include<netinet/in.h>#include<netinet/ip.h>#include<arpa/inet.h>......
  • (26-4-02)基于OpenAI和LangChain的上市公司估值系统:OpenAI API和Langchain探索(2)Langch
    10.5.2 Langchain和OpenAI处理编写文件summarizer.py,定义了使用Langchain和OpenAI处理文本和文档的功能处。其中,类UnstructuredStringLoader用于加载未结构化的字符串内容,将其分割成适当大小的块。方法split_text_in_chunks和方法split_doc_in_chunks分别用于将文本......
  • Java JSON组成和解析
    本框架JSON元素组成和分析,JsonElement分三大类型JsonArray,JsonObject,JsonString。JsonArray:数组和Collection子类,指定数组的话,使用ArrayList来add元素,遍历ArrayList再使用Array.newInstance生成数组并添加元素即可.JsonObject:带有泛型的封装类,给带有泛型的字段赋......
  • 告密者斯诺登:永远不要信任 OpenAI 或其 ChatGPT 等产品|TodayAI
    为什么 OpenAI 变得越来越难以信任OpenAI,一家以开发先进人工智能技术而闻名的公司,正面临越来越多的信任危机。近期,一些令人不安的迹象使人们对这家公司的透明度和安全性产生了质疑。首先,在OpenAI的旧金山办公室外,有一些身份不明的秘密保安人员,这些人的出现给当地社区带......
  • 要将URL参数转换为JSON对象,可以使用以下函数:
    要将URL参数转换为JSON对象,可以使用以下函数:javascriptfunctiongetQueryParams(url){//使用正则表达式提取URL参数constparamsString=url.split('?')[1];if(!paramsString){return{};}//将参数字符串分割成数组,并解析键值对constparams=......
  • (26-4-01)基于OpenAI和LangChain的上市公司估值系统:OpenAI API和Langchain探索(1)OpenAI
    10.5 OpenAIAPI和Langchain探索接下来使用OpenAIAPI与Langchain对解析后的文档进行总结,从中提取有价值的信息。这将帮助我们更好地理解文档中的内容,包括业务情况、风险因素、财务状况分析等,并提供更简洁的概括信息。10.5.1 OpenAI接口编写文件openai_interface.py,实......
  • 支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象
    支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象SPI三方服务接入指南https://opendocs.alipay.com/isv/spiforisv服务端实现Demo以下Demo是通过Java实现的SPI服务样例,包括验签支付宝请求报文、业务逻辑处理、商家加签以及响应报文构造的逻辑。......
  • 支付宝签名和验签使用JSONObject是最优解。json字符串顺序和==符号都一致演示代码
    支付宝签名和验签使用JSONObject是最优解。json字符串顺序和==符号都一致演示代码支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象https://www.cnblogs.com/oktokeep/p/18249346packagecom.example.core.mydemo;importcom.alibaba.fastjson.JSON;imp......