乐趣区

关于openai:OpenAI私有框架代码生成实践

作者:京东批发 牛晓光

依据现有调研和实际,由 OpenAI 提供的 ChatGPT/GPT- 4 模型和 CodeX 模型可能很好的了解和生成业界大多数编程语言的逻辑和代码,其中尤其善于 Python、JavaScript、TypeScript、Ruby、Go、C# 和 C++ 等语言。

然而在理论利用中,咱们常常会在编码时应用到一些公有框架、包、协定和 DSL 等。因为相干模型没有学习最新网络数据,且这些公有数据通常也没有公布在公开网络上,OpenAI 无奈依据这些公有信息生成对应代码。

一、OpenAI 常识学习形式

OpenAI 提供了几种形式,让 OpenAI 模型学习公有常识:

1. 微调模型

OpenAI 反对基于现有的根底模型,通过提供“prompt – completion”训练数据生成公有的自定义模型。

应用办法

在执行微调工作时,须要执行下列步骤:

1. 筹备训练数据:数据需蕴含 prompt/completion,格局反对 CSV, TSV, XLSX, JSON 等。

  • 格式化训练集:openai tools fine_tunes.prepare_data -f <LOCAL_FILE>
  • LOCAL_FILE:上一步中筹备好的训练数据。

2. 训练模型微调:openai api fine_tunes.create -t <LOCAL_FILE> -m <BASE_MODULE> --suffix "<MODEL_SUFFIX>"

  • LOCAL_FILE:上一步中筹备好的训练集。
  • BASE_MODULE:根底模型的名称,可选的模型包含 adababbagecuriedavinci 等。
  • MODEL_SUFFIX:模型名称后缀。

3. 应用自定义模型

应用老本

在微调模型形式中,除了应用自定义模型进行推理时所需领取的费用外,训练模型时所耗费的 Tokens 也会对应收取费用。依据不同的根底模型,费用如下:

论断

应用微调模型进行公有常识学习,依赖于大量的训练数据,训练数据越多,微调成果越好。
此办法实用于领有大量数据积攒的场景。

2. 聊天补全

GPT 模型接管对话模式的输出,而对话依照角色进行整顿。对话数据的开始蕴含零碎角色,该音讯提供模型的初始阐明。能够在零碎角色中提供各种信息,如:

  • 助手的简要阐明
  • 助手的个性特征
  • 助手须要遵循的指令或规定
  • 模型所需的数据或信息

咱们能够在聊天中,通过自定义零碎角色为模型提供执行用户指令所必要的公有信息。

应用办法

能够在用户提交的数据前,追加对公有常识的阐明内容。

openai.createChatCompletion({
  model: "gpt-3.5-turbo",
  messages: [
    { role: "system", content: "你是一款智能聊天机器人,帮忙用户答复无关内容管理系统低代码引擎 CCMS 的技术问题。智能依据上面的上下文答复问题,如果不确定答案,能够说“我不晓得”。\n\n" +
      "上下文:\n" + 
      "- CCMS 通过可视化配置形式生成中后盾管理系统页面,其通过 JSON 数据格式形容页面信息,并在运行时渲染页面。\n" + 
      "- CCMS 反对一般列表、筛选列表、新增表单、编辑表单、详情展现等多种页面类型。\n" + 
      "- CCMS 能够配置页面信息、接口定义、逻辑判断、数据绑定和页面跳转等交互逻辑。"
    },
    {role: "user", content: "CCMS 是什么?"}
  ]
}).then((response) => response.data.choices[0].message.content);

应用老本

除了用户所提交的内容外,零碎角色所提交的对于公有常识的阐明内容,也会依照 Tokens 消耗量进行计费。

论断

应用聊天补全进行公有常识学习,依赖于零碎角色的信息输出,且此局部数据的 Tokens 耗费会随每次用户申请而反复计算。

此办法实用于公有常识清晰精确,且内容量较少的场景。

二、公有常识学习实际

对于公有框架、包、协定、DSL 等,通常具备比较完善的应用文档,而较少领有海量的用户应用数据,所以在以后场景下,偏向于应用聊天补全的形式让 GPT 学习公有常识。

而在此基础上,如何为零碎角色提供大量而准确的常识信息,则是在保障用户应用状况下,节俭应用老本的重要形式。

3. 检索 - 发问解决方案

咱们能够在调用 OpenAI 提供的 Chat 服务前,应用用户所提交的信息对公有常识进行检索,筛选出最相干的信息,再进行 Chat 申请,检索 Tokens 耗费。

而 OpenAI 所提供的嵌入(Embedding)服务则能够解决检索阶段的工作。

应用办法

1. 筹备搜寻数据(一次性)

  • 收集:筹备欠缺的应用文档。如:https://jd-orion.github.io/docs
  • 分块:将文档拆分为简短的、大部分是独立的局部,这通常是文档中的页面或章节。
  • 嵌入:为每一个分块别离调用 OpenAI API 生成 Embedding。
await openai.createEmbedding({
  model: "text-embedding-ada-002",
  input: fs.readFileSync('./document.md', 'utf-8').toString(),}).then((response) => response.data.data[0].embedding);

  • 存储:保留 Embedding 数据。(对于大型数据集,能够应用矢量数据库)

2. 检索(每次查问一次)

  • 为用户的发问,调用 OpenAI API 生成 Embedding。(同 1.3 步骤)
  • 应用发问 Embedding,依据与发问的相关性对公有常识的分块 Embedding 进行排名。
const fs = require('fs');
const {parse} = require('csv-parse/sync');
const distance = require('compute-cosine-distance');

function (input: string, topN: number) {const knowledge: { text: string, embedding: string, d?: number}[] = parse(fs.readFileSync('./knowledge.csv').toString());

  for (const row of knowledge) {row.d = distance(JSON.parse(row.embedding), input)
  }

  knowledge.sort((a, b) => a.d - b.d);

  return knowledge.slice(0, topN).map((row) => row.text));
}

3. 发问(每次查问一次)

  • 给申请的零碎角色插入与问题最相干的信息
async function (knowledge: string[], input: string) {
  const response = await openai.createChatCompletion({
    model: "gpt-3.5-turbo",
    messages: [
      {
        role: 'system',
        content: "你是一款智能聊天机器人,帮忙用户答复无关内容管理系        统低代码引擎 CCMS 的技术问题。\n\n" + knowledge.join("\n")
      },
      {
        role: 'user',
        content: input
      }
    ]
  }).then((response) => response.data.choices[0].message.content);
  return response
}

  • 返回 GPT 的答案

应用老本

应用此办法,须要一次性的领取用于执行 Embedding 的费用。

三、低代码自然语言搭建案例

解决了让 GPT 学习公有常识的问题后,就能够开始应用 GPT 进行公有框架、库、协定和 DSL 相干代码的生成了。

本文以低代码自然语言搭建为例,帮忙用户应用自然语言对所需搭建或批改的页面进行形容,进而应用 GPT 对形容页面的配置文件进行批改,并依据返回的内容为用户提供实时预览服务。

应用办法

OpenAI 调用组件

const {Configuration, OpenAIApi} = require("openai");
const openai = new OpenAIApi(new Configuration({ /** OpenAI 配置 */}));
const distance = require('compute-cosine-distance');
const knowledge: {text: string, embedding: string, d?: number}[] = require("./knowledge")

export default function OpenAI (input, schema) {return new Promise((resolve, reject) => {
    // 将用户发问信息转换为 Embedding
    const embedding = await openai.createEmbedding({
      model: "text-embedding-ada-002",
      input,
    }).then((response) => response.data.data[0].embedding);
    
    // 获取用户发问与常识的相关性并排序
    for (const row of knowledge) {row.d = distance(JSON.parse(row.embedding), input)
    }
    knowledge.sort((a, b) => a.d - b.d);
    
    // 将相关性常识、原始代码和用户发问发送给 GPT-3.5 模型
    const message = await openai.createChatCompletion({
      model: "gpt-3.5-turbo",
      messages: [
        {
          role: 'system',
          content: "你是编程助手,须要浏览协定常识,并依照用户的要求批改代码。\n\n" + 
                  "协定常识:\n\n" +
                  knowledge.slice(0, 10).map((row) => row.text).join("\n\n") + "\n\n" + 
                  "原始代码:\n\n" +
                  "```\n" + schema + "\n```"
        },
        {
          role: 'user',
          content: input
        }
      ]
    }).then((response) => response.data.choices[0].message.content);

      // 查看返回音讯中是否蕴含 Markdown 语法的代码块标识
    let startIndex = message.indexOf('```');
    if (message.substring(startIndex, startIndex + 4) === 'json') {startIndex += 4;}

    if (startIndex > -1) {
      // 返回音讯为 Markdown 语法
      let endIndex = message.indexOf('```', startIndex + 3);
      let messageConfig;

      // 须要遍历所有代码块
      while (endIndex > -1) {
        try {messageConfig = message.substring(startIndex + 3, endIndex);

          if (/** messageConfig 正确性校验 */) {resolve(messageConfig);
            break;
          }
        } catch (e) {/* 本次失败 */}

        startIndex = message.indexOf('```', endIndex + 3);

        if (message.substring(startIndex, startIndex + 4) === 'json') {startIndex += 4;}

        if (startIndex === -1) {reject(['OpenAI 返回的信息不可辨认:', message]);
          break;
        }

        endIndex = message.indexOf('```', startIndex + 3);
      }
    } else {
      // 返回音讯可能为代码自身
      try {
        const messageConfig = message;

        if (/** messageConfig 正确性校验 */) {resolve(messageConfig);
        } else {reject(['OpenAI 返回的信息不可辨认:', message]);
        }
      } catch (e) {reject(['OpenAI 返回的信息不可辨认:', message]);
      }
    }
  })
}

低代码渲染

import React, {useState, useEffect} from 'react'
import {CCMS} from 'ccms-antd'
import OpenAI from './OpenAI'

export default function App () {const [ ready, setReady] = useState(true)
  const [schema, setSchema] = useState({})

  const handleOpenAI = (input) => {OpenAI(input, schema).then((nextSchema) => {setReady(false)
      setSchema(nextSchema)
    })
  }

  useEffect(() => {setReady(true)
  }, [schema])

  return (<div style={{ width: '100vw', height: '100vh'}}>
      {ready && (
        <CCMS
          config={pageSchema}
          /** ... */
        />
      )}
      <div style={{position: 'fixed', right: 385, bottom: 20, zIndex: 9999}}>
        <Popover
          placement="topRight"
          trigger="click"
          content={<Form.Item label="应用 OpenAI 助力搭建页面:" labelCol={{ span: 24}}>
              <Input.TextArea
                placeholder="请在这里输出内容,按下 Shift+ 回车确认。"
                defaultValue={defaultPrompt}
                onPressEnter={(e) => {if (e.shiftKey) {handleOpenAI(e.currentTarget.value)
                  }
                }}
              />
            </Form.Item>
          }
        >
          <Button shape="circle" type="primary" icon={/** OpenAI icon */} />
        </Popover>
      </div>
    </div>
  )
}

四、信息安全

依据 OpenAI 隐衷政策阐明,应用 API 形式进行数据拜访时:

  1. 除非明确的受权,OpenAI 不会应用用户发送的数据进行学习和改良模型。
  2. 用户发送的数据会被 OpenAI 保留 30 天,以用于监管和审查。(无限数量的受权 OpenAI 员工,以及负有窃密和平安任务的业余第三方承包商,能够拜访这些数据)
  3. 用户上传的文件(包含微调模型是提交的训练数据),除非用户删除,否则会始终保留。

另外,OpenAI 不提供模型的私有化部署(包含上述微调模型形式所生成的自定义模型),但能够通过分割销售团队购买公有容器。

文中所应用的训练数据、公有框架常识以及低代码框架均源自本团队开发并已开源的内容。用户应用相干服务时也会进行数据安全提醒。

退出移动版