【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模型接收对话形式的输入,而对话按照角色进行整理。对话数据的开始包含系统角色,该消息提供模型的初始说明。可以在系统角色中提供各种信息,如:

  • 助手的简要说明

  • 助手的个性特征

  • 助手需要遵循的指令或规则

  • 模型所需的数据或信息

我们可以在聊天中,通过自定义系统角色为模型提供执行用户指令所必要的私有信息。

使用方法

可以在用户提交的数据前,追加对私有知识的说明内容。

1openai.createChatCompletion({ 2 model: "gpt-3.5-turbo", 3 messages: [ 4 { role: "system", content: "你是一款智能聊天机器人,帮助用户回答有关内容管理系统低代码引擎CCMS的技术问题。智能根据下面的上下文回答问题,如果不确定答案,可以说“我不知道”。\n\n" + 5 "上下文:\n" + 6 "- CCMS通过可视化配置方式生成中后台管理系统页面,其通过JSON数据格式描述页面信息,并在运行时渲染页面。\n" + 7 "- CCMS支持普通列表、筛选列表、新增表单、编辑表单、详情展示等多种页面类型。\n" + 8 "- CCMS可以配置页面信息、接口定义、逻辑判断、数据绑定和页面跳转等交互逻辑。" 9 }, 10 { role: "user", content: "CCMS是什么?" } 11 ] 12}).then((response) => response.data.choices[0].message.content); 13 14 15

使用成本

除了用户所提交的内容外,系统角色所提交的关于私有知识的说明内容,也会按照Tokens消耗量进行计费。

结论

使用聊天补全进行私有知识学习,依赖于系统角色的信息输入,且此部分数据的Tokens消耗会随每次用户请求而重复计算。

此方法适用于私有知识清晰准确,且内容量较少的场景。

二、私有知识学习实践

对于私有框架、包、协议、DSL等,通常具备比较完善的使用文档,而较少拥有海量的用户使用数据,所以在当前场景下,倾向于使用聊天补全的方式让GPT学习私有知识。

而在此基础上,如何为系统角色提供少量而精确的知识信息,则是在保障用户使用情况下,节省使用成本的重要方式。

3. 检索-提问解决方案

我们可以在调用OpenAI提供的Chat服务前,使用用户所提交的信息对私有知识进行检索,筛选出最相关的信息,再进行Chat请求,检索Tokens消耗。

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

使用方法

1. 准备搜索数据(一次性)

  • 收集:准备完善的使用文档。如:https://jd-orion.github.io/docs

  • 分块:将文档拆分为简短的、大部分是独立的部分,这通常是文档中的页面或章节。

  • 嵌入:为每一个分块分别调用OpenAI API生成Embedding。

1await openai.createEmbedding({ 2 model: "text-embedding-ada-002", 3 input: fs.readFileSync('./document.md', 'utf-8').toString(), 4}).then((response) => response.data.data[0].embedding); 5 6 7
  • 存储:保存Embedding数据。(对于大型数据集,可以使用矢量数据库)

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

  • 为用户的提问,调用OpenAI API生成Embedding。(同1.3步骤)

  • 使用提问Embedding,根据与提问的相关性对私有知识的分块Embedding进行排名。

1const fs = require('fs'); 2const { parse } = require('csv-parse/sync'); 3const distance = require( 'compute-cosine-distance' ); 4 5function (input: string, topN: number) { 6 const knowledge: { text: string, embedding: string, d?: number }[] = parse(fs.readFileSync('./knowledge.csv').toString()); 7 8 for (const row of knowledge) { 9 row.d = distance(JSON.parse(row.embedding), input) 10 } 11 12 knowledge.sort((a, b) => a.d - b.d); 13 14 return knowledge.slice(0, topN).map((row) => row.text)); 15} 16 17 18

3. 提问(每次查询一次)

  • 给请求的系统角色插入与问题最相关的信息
1async function (knowledge: string[], input: string) { 2 const response = await openai.createChatCompletion({ 3 model: "gpt-3.5-turbo", 4 messages: [ 5 { 6 role: 'system', 7 content: "你是一款智能聊天机器人,帮助用户回答有关内容管理系 统低代码引擎CCMS的技术问题。\n\n" + knowledge.join("\n") 8 }, 9 { 10 role: 'user', 11 content: input 12 } 13 ] 14 }).then((response) => response.data.choices[0].message.content); 15 return response 16} 17 18 19
  • 返回GPT的答案

使用成本

使用此方法,需要一次性的支付用于执行Embedding的费用。

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

解决了让GPT学习私有知识的问题后,就可以开始使用GPT进行私有框架、库、协议和DSL相关代码的生成了。

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

使用方法

OpenAI调用组件

1const { Configuration, OpenAIApi } = require("openai"); 2const openai = new OpenAIApi(new Configuration({ /** OpenAI 配置 */ })); 3const distance = require('compute-cosine-distance'); 4const knowledge: { text: string, embedding: string, d?: number }[] = require("./knowledge") 5 6export default function OpenAI (input, schema) { 7 return new Promise((resolve, reject) => { 8 // 将用户提问信息转换为Embedding 9 const embedding = await openai.createEmbedding({ 10 model: "text-embedding-ada-002", 11 input, 12 }).then((response) => response.data.data[0].embedding); 13 14 // 获取用户提问与知识的相关性并排序 15 for (const row of knowledge) { 16 row.d = distance(JSON.parse(row.embedding), input) 17 } 18 knowledge.sort((a, b) => a.d - b.d); 19 20 // 将相关性知识、原始代码和用户提问发送给GPT-3.5模型 21 const message = await openai.createChatCompletion({ 22 model: "gpt-3.5-turbo", 23 messages: [ 24 { 25 role: 'system', 26 content: "你是编程助手,需要阅读协议知识,并按照用户的要求修改代码。\n\n" + 27 "协议知识:\n\n" + 28 knowledge.slice(0, 10).map((row) => row.text).join("\n\n") + "\n\n" + 29 "原始代码:\n\n" + 30 "```\n" + schema + "\n```" 31 }, 32 { 33 role: 'user', 34 content: input 35 } 36 ] 37 }).then((response) => response.data.choices[0].message.content); 38 39 // 检查返回消息中是否包含Markdown语法的代码块标识 40 let startIndex = message.indexOf('```'); 41 if (message.substring(startIndex, startIndex + 4) === 'json') { 42 startIndex += 4; 43 } 44 45 if (startIndex > -1) { 46 // 返回消息为Markdown语法 47 let endIndex = message.indexOf('```', startIndex + 3); 48 let messageConfig; 49 50 // 需要遍历所有代码块 51 while (endIndex > -1) { 52 try { 53 messageConfig = message.substring(startIndex + 3, endIndex); 54 55 if ( 56 /** messageConfig正确性校验 */ 57 ) { 58 resolve(messageConfig); 59 break; 60 } 61 } catch (e) { 62 /* 本次失败 */ 63 } 64 65 startIndex = message.indexOf('```', endIndex + 3); 66 67 if (message.substring(startIndex, startIndex + 4) === 'json') { 68 startIndex += 4; 69 } 70 71 if (startIndex === -1) { 72 reject(['OpenAI返回的信息不可识别:', message]); 73 break; 74 } 75 76 endIndex = message.indexOf('```', startIndex + 3); 77 } 78 } else { 79 // 返回消息可能为代码本身 80 try { 81 const messageConfig = message; 82 83 if ( 84 /** messageConfig正确性校验 */ 85 ) { 86 resolve(messageConfig); 87 } else { 88 reject(['OpenAI返回的信息不可识别:', message]); 89 } 90 } catch (e) { 91 reject(['OpenAI返回的信息不可识别:', message]); 92 } 93 } 94 }) 95} 96 97 98

低代码渲染

1import React, { useState, useEffect } from 'react' 2import { CCMS } from 'ccms-antd' 3import OpenAI from './OpenAI' 4 5export default function App () { 6 const [ ready, setReady ] = useState(true) 7 const [ schema, setSchema ] = useState({}) 8 9 const handleOpenAI = (input) => { 10 OpenAI(input, schema).then((nextSchema) => { 11 setReady(false) 12 setSchema(nextSchema) 13 }) 14 } 15 16 useEffect(() => { 17 setReady(true) 18 }, [schema]) 19 20 return ( 21 <div style={{ width: '100vw', height: '100vh' }}> 22 {ready && ( 23 <CCMS 24 config={pageSchema} 25 /** ... */ 26 /> 27 )} 28 <div style={{ position: 'fixed', right: 385, bottom: 20, zIndex: 9999 }}> 29 <Popover 30 placement="topRight" 31 trigger="click" 32 content={ 33 <Form.Item label="使用OpenAI助力搭建页面:" labelCol={{ span: 24 }}> 34 <Input.TextArea 35 placeholder="请在这里输入内容,按下Shift+回车确认。" 36 defaultValue={defaultPrompt} 37 onPressEnter={(e) => { 38 if (e.shiftKey) { 39 handleOpenAI(e.currentTarget.value) 40 } 41 }} 42 /> 43 </Form.Item> 44 } 45 > 46 <Button shape="circle" type="primary" icon={ /** OpenAI icon */ } /> 47 </Popover> 48 </div> 49 </div> 50 ) 51} 52 53 54

四、信息安全

根据OpenAI隐私政策说明,使用API方式进行数据访问时:

  1. 除非明确的授权,OpenAI不会使用用户发送的数据进行学习和改进模型。
  2. 用户发送的数据会被OpenAI保留30天,以用于监管和审查。(有限数量的授权OpenAI员工,以及负有保密和安全义务的专业第三方承包商,可以访问这些数据)
  3. 用户上传的文件(包括微调模型是提交的训练数据),除非用户删除,否则会一直保留。

另外,OpenAI不提供模型的私有化部署(包括上述微调模型方式所生成的自定义模型),但可以通过联系销售团队购买私有容器。

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

点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

PPDB:今晚老齐直播

【今晚老齐直播】今晚(本周三晚)20:0021:00小白开始“用”飞桨(https://www.oschina.net/action/visit/ad?id1185)由PPDE(飞桨(https://www.oschina.net/action/visit/ad?id1185)开发者专家计划)成员老齐,为深度学习小白指点迷津。

FLV文件格式

1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  

mysql设置时区

mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0