TypeChat、JSONSchemaChat实战 - 让ChatGPT更听你的话

发布时间 2024-01-09 14:04:54作者: 若邪

TypeChat 用一句话概括,就是用了它你可以让大语言模型(比如 ChatGPT)将自然语言转换成特定类型的 JSON 数据。

我们在使用 ChatGPT 的时候,大致流程如下:

假如我们需要 ChatGPT 按照我们输入的 prompt,输出指定格式的 JSON 数据,我们在 prompt 里将我们的要求描述清楚就行,比如

好像没什么问题,很听话。

我们再看一个例子

{
    "filters": [
        {
            "component": "select",
            "key": "店铺名称",
            "label": "店铺名称",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "key": "店铺编码",
            "label": "店铺编码",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "key": "店铺门头编码",
            "label": "店铺门头编码",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "key": "所在区域",
            "label": "所在区域",
            "placeholder": "全部"
        }
    ],
    "columns": [
        {
            "slot": false,
            "title": "店铺编码",
            "dataIndex": "店铺编码",
            "key": "店铺编码"
        },
        {
            "slot": false,
            "title": "店铺名称",
            "dataIndex": "店铺名称",
            "key": "店铺名称"
        },
        {
            "slot": false,
            "title": "店铺业务范围",
            "dataIndex": "店铺业务范围",
            "key": "店铺业务范围"
        },
        {
            "slot": false,
            "title": "店铺类型",
            "dataIndex": "店铺类型",
            "key": "店铺类型"
        },
        {
            "slot": false,
            "title": "所在区域",
            "dataIndex": "所在区域",
            "key": "所在区域"
        },
        {
            "slot": false,
            "title": "详细地址",
            "dataIndex": "详细地址",
            "key": "详细地址"
        }
    ],
    "pagination": {
        "show": true,
        "page": "page",
        "size": "size",
        "total": "result.total"
    },
    "includeModifyModal": false,
    "fetchName": "fetchTableList",
    "result": "[\"result\"][\"records\"]",
    "serviceName": "getTableList"
}

我们需要 ChatGPT 把上面 JSON 数据中 filters 字段中的 key 字段的值翻译为英文,使用驼峰语法。columns 字段中的 key、dataIndex 字段的值翻译为英文,使用驼峰语法。

我们的 prompt 可以这么写:

{
    "filters": [
        {
            "component": "select",
            "key": "店铺名称",
            "label": "店铺名称",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "key": "店铺编码",
            "label": "店铺编码",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "key": "店铺门头编码",
            "label": "店铺门头编码",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "key": "所在区域",
            "label": "所在区域",
            "placeholder": "全部"
        }
    ],
    "columns": [
        {
            "slot": false,
            "title": "店铺编码",
            "dataIndex": "店铺编码",
            "key": "店铺编码"
        },
        {
            "slot": false,
            "title": "店铺名称",
            "dataIndex": "店铺名称",
            "key": "店铺名称"
        },
        {
            "slot": false,
            "title": "店铺业务范围",
            "dataIndex": "店铺业务范围",
            "key": "店铺业务范围"
        },
        {
            "slot": false,
            "title": "店铺类型",
            "dataIndex": "店铺类型",
            "key": "店铺类型"
        },
        {
            "slot": false,
            "title": "所在区域",
            "dataIndex": "所在区域",
            "key": "所在区域"
        },
        {
            "slot": false,
            "title": "详细地址",
            "dataIndex": "详细地址",
            "key": "详细地址"
        }
    ],
    "pagination": {
        "show": true,
        "page": "page",
        "size": "size",
        "total": "result.total"
    },
    "includeModifyModal": false,
    "fetchName": "fetchTableList",
    "result": "[\"result\"][\"records\"]",
    "serviceName": "getTableList"
}
将这段 json 中,filters 字段中的 key 字段的值翻译为英文,使用驼峰语法,label、placeholder 字段的值保留中文。
columns 字段中的 key、dataIndex 字段的值翻译为英文,使用驼峰语法,title 字段的值保留中文。 
返回翻译后的 markdown 语法的代码块

最后面的那段话已经经过了几次修改,我们看下 ChatGPT 返回的内容:

{
    "filters": [
        {
            "component": "select",
            "storeName": "店铺名称",
            "label": "店铺名称",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "storeCode": "店铺编码",
            "label": "店铺编码",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "storeDoorCode": "店铺门头编码",
            "label": "店铺门头编码",
            "placeholder": "请输入"
        },
        {
            "component": "select",
            "area": "所在区域",
            "label": "所在区域",
            "placeholder": "全部"
        }
    ],
    "columns": [
        {
            "slot": false,
            "title": "店铺编码",
            "storeCode": "店铺编码",
            "key": "storeCode"
        },
        {
            "slot": false,
            "title": "店铺名称",
            "storeName": "店铺名称",
            "key": "storeName"
        },
        {
            "slot": false,
            "title": "店铺业务范围",
            "storeBusinessScope": "店铺业务范围",
            "key": "storeBusinessScope"
        },
        {
            "slot": false,
            "title": "店铺类型",
            "storeType": "店铺类型",
            "key": "storeType"
        },
        {
            "slot": false,
            "title": "所在区域",
            "area": "所在区域",
            "key": "area"
        },
        {
            "slot": false,
            "title": "详细地址",
            "detailedAddress": "详细地址",
            "key": "detailedAddress"
        }
    ],
    "pagination": {
        "show": true,
        "page": "page",
        "size": "size",
        "total": "result.total"
    },
    "includeModifyModal": false,
    "fetchName": "fetchTableList",
    "result": "[\"result\"][\"records\"]",
    "serviceName": "getTableList"
}

filters 里的 key 字段哪去了?columns 里 dataIndex 字段哪去了?

这只是我遇到的各种情况里的一种,当然也有成功的,反正就是很玄学,可能是我半年 prompt 练习生的水平不够吧。

如果我们用 TypeChat 会怎么样呢?

直接看效果 :

typechat.gif

完美。

TypeChat 的大致原理如下:

其实就是在 prompt 拼上了一个 TS 类型,要求 ChatGPT 按照 TS 类型声明输出 JSON。背后还会构建一个 TS 程序去进行校验,如果校验失败就把错误信息也加到 prompt 里,让 ChatGPT 再处理一次。

上面生成的 prompt 跟 TypeChat 生成的有些区别,我并没有直接使用 TypeChat,为了方便在我的项目里使用就抄了一份进行修改,在生成 prompt 的里加上了 “按照字段的注释进行处理” 这一要求。修改后的代码: https://github.com/lowcode-scaffold/lowcode-materials/blob/master/share/TypeChatSlim/index.ts

在 TS 类型的字段注释里还可以加入更离谱的东西来要求 ChatGPT 去处理,比如之前弄的一个根据聊天记录生成日程安排的小 demo,在字段注释里加上当前时间,让 ChatGPT 根据会话内容推算出具体的日期。TS 类型定义如下:

const res = await translate({
    schema: `export type SchedulType = {
              /**
               * 时间,格式:YYYY-MM-DD HH:mm:ss,当前时间为 ${new Date().toLocaleString()},请推算出正确的时间
               */
              time: string;
              /**
               * 活动主题
               */
              todo: string;
              /**
               * 地点
               */
              addr: string;
              /**
               * 活动的全体参与人
               */
              participant: string[];
            };`,
    typeName: 'SchedulType',
    request: `根据下面的聊天记录,生成日程安排:${message.data}`,
    createChatCompletion: createChatCompletionForScript,
    showWebview: true,
  });

a9cf046b-05ce-405c-9ebc-f7b8f4c373f0.png

聊天记录中只说到了周六下午两点,ChatGPT 根据字段注释里的当前时间正确推算出了日程的具体日期。

受到 TypeChat 的启发,借助 JSON Schema 实现了一个 JSONSchemaChat,原理差不多,效果如下: