首页 > 其他分享 >stream json output

stream json output

时间:2024-08-18 17:37:33浏览次数:10  
标签:province Beijing None 修复 stream json capital output

Summary of Stream JSON parser

通过分析 Promptulate 项目的 issue,对问题进行了深入探讨并给出了相应的处理方案。本文将详细描述该问题的处理过程。

issues目标

  1. 原文如下

    Currently, we cannot enable stream if setting output schema,
    so Need to build a json parser to parse stream type json data like this:
    img

  2. 解读

    对于 Promptulate 中的 Agent 输出,通常在指定输出格式(Output Formatter)后,只有在 Agent 输出完成后才能得到最终结果。
    然而,当输出格式中的某些字段(Field)难以快速生成时,用户可能需要等待较长时间。因此,我们希望在输出过程中,将结果以流的形式逐步输出。

预期结果

如上图所示,我们希望在流式输出过程中,已生成的字段按格式输出,而尚未生成的字段标记为 None。

最终目标:用户可以指定 Output Formatter 并流式输出结果,且可以对结果进行动态处理。

img

处理流程

1. 开始的时候,存在理解的错误。

误解

起初认为需要在输出多个 Output Formatter 对象时,将这些对象一个一个地流式输出。

在指定 Output Formatter 且 stream=True 的情况下,默认情况下会输出如下格式的流式数据:
img
img

目前,agent在指定Output Formatter以及steam=true的情况下,默认会输出以下格式的流式数据

class LLMBaseResponse(BaseModel):
    province: str = Field(description="province in China")
    capital: str = Field(description="Capital of the province")

class LLMResponse(BaseModel):
    provinces: List[LLMBaseResponse] = Field(description="List of provinces and capitals in China")

response = pne.chat(
    api_key=DeepKey,
    messages="Please tell me all provinces and Capital in China.",
    output_schema=LLMResponse,
    model="deepseek/deepseek-chat",
    stream=True,
    )

输入如下:

    ```json
        {
            {province='Sichuan', capital='Chengdu'},
            {province='Liaoning', capital='Shenyang'},
            ……
        }
    ```

这样的话,我们只需要对最外层的对象进行正则匹配。再逐一把内部对象匹配输出就行。

2. Review 反馈

我们需要在流式结果输出的过程中,展示单个json对象的输出流程。
并在没有匹配成功时通过None进行表示

期望结果如下:
img

3. 将目标进行分解

为了实现这个目标,我们主要应该考虑两点问题。

  1. JSON 不完整情况下的处理:

    如何将不完整的 JSON 数据补全为完整的 JSON 对象,并转换为指定的 Output Formatter。

  2. 处理 None 值:

    如何将 None 值添加到 Output Formatter 字段中,并解决 Pydantic 中不接受 None 的字段报错问题。

        # 产生如下报错
        pydantic_core._pydantic_core.ValidationError: 1 validation error for LLMBaseResponse
        capital
        Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
        For further information visit https://errors.pydantic.dev/2.7/v/string_type
    

4.解决方案:

  1. JSON 修复:

    参考开源项目 half-json,该项目对 JSONDecoderError(JSON 解析失败的报错)进行了封装和处理,可以有效修复大部分不完整的 JSON 数据。

    以此,我们对比一下修复前后的情况

        修复前: {
        修复后: {}
        修复前: {
    
        修复后: {
        }
        修复前: {
        "
        修复后: {
        "":null}
        修复前: {
        "province
        修复后: {
        "province":null}
        修复前: {
        "province":
        修复后: {
        "province":null}
        修复前: {
        "province": "
        修复后: {
        "province": ""}
        修复前: {
        "province": "Beijing",
    
        修复后: {
        "province": "Beijing",
        "}":null}
        修复前: {
        "province": "Beijing",
        "
        修复后: {
        "province": "Beijing",
        "":null}
        修复前: {
        "province": "Beijing",
        "capital
        修复后: {
        "province": "Beijing",
        "capital":null}
        修复前: {
        "province": "Beijing",
        "capital":
        修复后: {
        "province": "Beijing",
        "capital":null}
        修复前: {
        "province": "Beijing",
        "capital": "
        修复后: {
        "province": "Beijing",
        "capital": ""}
        修复前: {
        "province": "Beijing",
        "capital": "Be
        修复后: {
        "province": "Beijing",
        "capital": "Be"}
        修复前: {
        "province": "Beijing",
        "capital": "Beijing
        修复后: {
        "province": "Beijing",
        "capital": "Beijing"}
    

    可以看出,修复完成后的json格式数据,已经可以转化为Output Formatter的结果了。

  2. 使用 Optional 处理 None 值:

    使用 Optional 对 Pydantic 模型进行封装,允许字段接受 None 值。

    流程如下

        def change_model(model: BaseModel) -> BaseModel:
            """change pydantic model to BaseModel
    
            Args:
                model (BaseModel): pydantic model
    
            Returns:
                BaseModel: BaseModel
            """
            annotations = {
                field: Optional[type_] for field, type_ in model.__annotations__.items()
            }
            new_model = type(model.__name__, (BaseModel,), {"__annotations__": annotations})
            return new_model
    

    这样我们得到的值就可以进行转化了

最终结果

通过 pydantic BaseModel 随时获取流式输出的对象的数据。
img
结果如下:
img

具体可以参考promptlate官方文档:https://www.promptulate.cn/#/use_cases/chat_usage?id=stream-json-parser

展望如下

上方解释了整个issues的完成过程,可是,同样的也存在一些问题。

  1. json数据

    • 数字的支持比较弱 --> -02 / 0. / .0

    • 还不支持分支回溯 --> [{]

    • 突然想到, 应该反思一下, 这个是一个fixer, 而不是一个将任何字符串都转为 json 的东西 应该明确 JSONFixer 的能力范围, 对 runratio.sh 也应该比较前后两个的 json 相似程度。 (听起来像无能者的辩白?)

    • 也需要吧 parser 也做成 stack 这样可以解决 ["a] --> ["a"] 这样的 case

    • 考虑分支回溯的方式来试探

    • 解析缺失的 JSON 常量

  2. 由于在进行这样的做法时,返回的response与设置的Output Formatter不一致。

    这样会产生什么样的问题呢,如果我需要对流失产生的数据转换会原来的Output Formatter的话,会产生报错。

    举出具体的例子的话,就像这样:

    class LLMBaseResponse(BaseModel):
        province: str = Field(None,description="province in China")
        capital: str = Field(description="Capital of the province")
    
    response = pne.chat(
        api_key=DeepKey,
        messages="Please tell me a provinces and Capital in China.",
        output_schema=LLMBaseResponse,
        model="deepseek/deepseek-chat",
        stream=True,
    )
    
    for i in response:
        print(i.province,i.capital)
        # 条件转换
        di_t = {}
        if i.province == None:
            di_t["province"] = "Unknown"
        if i.capital == None:
            di_t["capital"] = "Unknown"
        response = LLMBaseResponse(province=i.province,capital=i.capital)
        print(response)
        # 直接转换
        response = LLMBaseResponse(province=i.province,capital=i.capital)
        print(response)
    

    报错如下
    img

标签:province,Beijing,None,修复,stream,json,capital,output
From: https://www.cnblogs.com/lmlsh/p/18344201

相关文章

  • 【Java学习】Stream流详解
     所属专栏:Java学习Stream流是JDK 8引入的一个概念,它提供了一种高效且表达力强的方式来处理数据集合(如List、Set等)或数组。StreamAPI可以以声明性方式(指定做什么)来处理数据序列。流操作可以被分为两大类:中间操作(IntermediateOperations)和终端操作(TerminalOperation......
  • Stream流的一些使用方法
    一、创建Stream数据流Stream流创建Stream<Integer>stream=Stream.of(0,1,2,3,4,5);Collection创建List<Integer>integerList=newArrayList<>();integerList.add(0);integerList.add(1);integerList.add(2);integerList.add(3);integerList.add......
  • Json
    Jsonstringjson=@"{'Result':{'ResponseStatus':{'IsSuccess':true,'Errors':[],'SuccessEntitys':[{'Id......
  • JAVA中的对象流ObjectInputStream
    ObjectInputStream是Java中用于序列化对象的一种输入流,它允许我们将对象的状态信息从输入流中读取出来,以便在后续程序中使用。本文将详细介绍ObjectInputStream的原理、使用方法以及相关代码例子。一、ObjectInputStream简介概述ObjectInputStream继承了InputStream类,主要......
  • JAVA中的ObjectOutputStream类
    ObjectOutputStream是Java中用于序列化对象的一种输出流,它可以将Java对象的状态信息转换为字节流,以便于存储或通过网络传输。序列化是将对象转换为字节流的过程,而反序列化则是将字节流恢复为对象的过程。本文将详细介绍ObjectOutputStream的原理、使用方法以及相关代码例子。......
  • jsonunit 比较jsondiff
    https://github.com/lukas-krecan/JsonUnitimportstaticnet.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;importstaticnet.javacrumbs.jsonunit.assertj.JsonAssertions.json;...//comparestwoJSONdocuments(notelenientparsingofexpected......
  • 字符输入流InputStreamReader day17
    packagecom.shujia.day17.ketang;importjava.io.FileInputStream;importjava.io.InputStreamReader;/*转换流(字符流)=字节流+编码表字符流:(当一个文件使用记事本打开能够看懂的时候,就可以用字符流)字符输入流:Reader-......
  • 字符输出流 OutputStreamWriter day17
    packagecom.shujia.day17.ketang;importjava.io.FileOutputStream;importjava.io.OutputStreamWriter;importjava.util.Arrays;/*转换流(字符流)=字节流+编码表字符流:(当一个文件使用记事本打开能够看懂的时候,就可以用字符流)字符输入流:......
  • 字符缓冲输入流BufferedInputStream day17
    packagecom.shujia.day17.ketang;importjava.io.BufferedInputStream;importjava.io.FileInputStream;/*字符缓冲输入流:BufferedInputStream构造方法:BufferedInputStream(InputStreamin)创建一个BufferedInputStream并保存其参数,输入流in......
  • 字节缓冲输出流BufferedOutputStream day17
    packagecom.shujia.day17.ketang;importjava.io.BufferedOutputStream;importjava.io.FileOutputStream;/*java针对字节输入流和字节输出流都提供了相应的缓冲流来提高读写的速度。字节流:输入流:InputStream-FileInput......