之前尝试过和后端小伙伴一起写过openAI的网站,最近研究了一下openAI在react的包,发现真的非常适合入门,而且不需要再求着后端小伙伴给你写接口了,自己在前端就能用
+开箱即用,简单易上手
+可以实现绝大部分你可以想到的openAI使用场景
+也可以直接在node中使用,编写openAI的后端接口
- 需要申请key
- 在线上需要后端传递key密文,防止key在浏览器泄露
openAI和chatGpt的关系?
openAI是时下最火的人工智能研究机构,而chatGpt是旗下的一个大语言模型,本文的演示使用了openAI的包,而语言模型选用了chatGpt3.5。
一、前置准备
1.申请apiKey
奇迹和魔法不是免费的,想要使用openAI首先得在官网注册并购买key OpenAI,建议大家买最便宜的就可以,需要绑定海外银行卡支付。同时十分推荐在国内的购物网站看看有没有key售卖(大概一个key也就10块钱不到,网上偶尔又会有富哥放一些免费的key)
2.搭建工程
这里照例使用umi,大家也可以用自己喜欢的框架
yarn create umi
3.下载openAI的npm包
openai - npm
yarn add openai
二、尝试写一个简单的问答吧
我们首先新建一个openai的对象,这个实例对象可以直接用来请求外部接口,也是开箱即用的(甚至不需要考虑本地代理的问题,也不需要用复杂的axios,厉害吧)
import OpenAI from 'openai'; //这里写你的sdk const sdk = 'sk-XXX' //新建了一个OpenAI对象 const openai = new OpenAI({ apiKey: sdk, dangerouslyAllowBrowser: true// 这个属性可以让你讲sdk写在前端工程中(本地测试用可以,线上环境不要这样用) });
在线上环境,我们一般通过后端接口,密文传输key,也可以和用户信息结合传递不同的key
之后我们写一个输入框,一个发送按钮,和展示AI返回内容的块
return ( <> <Input type="text" style={{ width: '300px' }} onChange={(e) => setValue(e.target.value)} /> <Button type={'primary'} onClick={() => { callChatGPT(value) }}>发送</Button> <p>{chatHistory|| ''}</p> </> );
简单到甚至都不想写样式
之后我们对发送按钮写ai逻辑,具体可以看注释,注意使用该接口需要可以连接到api.openai.com
const callChatGPT = async (inputText: string) => { console.log('inputText', inputText) //通过await,通过请求拿到openai的返回 const chatCompletion: any = await openai.chat.completions.create({ messages: [{ role: 'user', content: inputText }],//传递我们的输入框内容 model: 'gpt-3.5-turbo',//根据你申请的key类型,选择合适的模型,一般都为3.5 }); console.log('chatCompletion', chatCompletion) setChatHistory(chatCompletion?.choices?.[0].message?.content || []) };
恭喜你迈出了第一步
完整代码如下
import { PageContainer } from '@ant-design/pro-components'; import { useState } from 'react'; import { Button, Input } from 'antd'; import OpenAI from 'openai'; const sdk = 'sk-XXX' const HomePage: React.FC = () => { const [chatHistory, setChatHistory] = useState(''); const [value, setValue] = useState(''); const openai = new OpenAI({ apiKey: sdk, dangerouslyAllowBrowser: true }); const callChatGPT = async (inputText: string) => { console.log('inputText', inputText) //通过await,通过请求拿到openai的返回 const chatCompletion: any = await openai.chat.completions.create({ messages: [{ role: 'user', content: inputText }], model: 'gpt-3.5-turbo',//根据你申请的key类型,选择合适的模型,一般都为3.5 }); console.log('chatCompletion', chatCompletion) setChatHistory(chatCompletion?.choices?.[0].message?.content || []) }; return ( <> <Input type="text" style={{ width: '300px' }} onChange={(e) => setValue(e.target.value)} /> <Button type={'primary'} onClick={() => { callChatGPT(value) }}>发送</Button> <p>{chatHistory|| ''}</p> </> ); export default HomePage;
三、太慢了!能不能逐字输出
chatGpt在回答一些问题的时候会耗时非常长,如果等回答完再完成接口的话我们的用户早就不耐烦了,可以考虑采用流式传输,逐字输出
一般现在主流的chatGpt网站都是采用的流式传输,具体实现原理可以参考我之前写的一篇react实现类GhatGPT逐字输出效果-CSDN博客
这里直接贴出代码,原理是通过监听去渲染页面
import { useState } from 'react'; import { Button, Input, message } from 'antd'; import OpenAI from 'openai'; const sdk = 'sk-XXX' const HomePage: React.FC = () => { const [value, setValue] = useState(''); const [output, setOutput] = useState(''); const openai = new OpenAI({ apiKey: sdk, dangerouslyAllowBrowser: true }); const callChatGPT = async (inputText: string) => { console.log('inputText', inputText) //这里定义接口为流式传输 const stream = await openai.beta.chat.completions.stream({ model: 'gpt-3.5-turbo', messages: [{ role: 'user', content: inputText }], stream: true, }); stream.on('content', (delta, snapshot) => { //在这里即可在每次接受到流式时就可以 console.log('delta', delta) //通过TextDecoder解析出字符串 //触发setState,触发渲染 setOutput(prevState => prevState + delta) }); }; return ( <> <Input type="text" style={{ width: '300px' }} onChange={(e) => setValue(e.target.value)} /> <Button type={'primary'} onClick={() => { callChatGPT(value) }}>发送</Button> <div><p>{output}</p></div> </> ); }; export default HomePage;
可以看到实现了非常流畅的流式传输效果
react的escape机制?
react的监听函数需要小心使用,stream.on是监听函数,挂载的作用域和页面的作用域是不一致的
四、对话
到这里已经可以实现绝大部分的gpt功能了,但是目前还是一问一答,没什么技术含量,要实现多语句的对话,需要把之前的所有对话传输进去
相当于每一次发送请求的时候,我们的所有之前对话也要装进message数组,传给chatGpt(到后面会越传越多,gpt会自己读取上下文,给出更合语境的解答)
import { PageContainer } from '@ant-design/pro-components'; import { useCallback, useEffect, useState } from 'react'; import { Button, Input, message } from 'antd'; import OpenAI from 'openai'; const sdk = 'sk-XXX' const HomePage: React.FC = () => { const [chatHistory, setChatHistory] = useState<any>([]);//对话的列表 const [value, setValue] = useState(''); const openai = new OpenAI({ apiKey: sdk, dangerouslyAllowBrowser: true }); const callChatGPT = async (inputText: string) => { console.log('inputText', inputText) const tempList = [...chatHistory] //将用户的语句装载进去,role使用为user tempList.push({ role: 'user', content: inputText }) console.log('tempList', tempList) setChatHistory(tempList) }; useEffect(() => { //当对话数组的最后一条为对象时,触发接口,传递给chatGpt if (chatHistory[chatHistory?.length - 1]?.role === 'user') { openai.chat.completions.create({ messages: chatHistory,//这里传递所有之前的对话 model: 'gpt-3.5-turbo', }).then((res: any) => { const tempList = [...chatHistory] tempList.push(res?.choices[0].message) console.log('tempList', tempList) setChatHistory(tempList) }); } }, [chatHistory]) return ( <> <Input type="text" style={{ width: '300px' }} onChange={(e) => setValue(e.target.value)} /> <Button type={'primary'} onClick={() => { callChatGPT(value) }}>发送</Button> <div> //一条一条的渲染,就像是对话一样 {chatHistory?.map((message: any, index: number) => ( <p style={{ margin: '10px', }} key={index}>{message?.content || ''}</p> ))} </div> </> ); }; export default HomePage;
你可以将代码中的实现改为流式传输,试试看!
我们可以看到gpt已经在和我们愉快的交流了
现在,你已经实现了绝大部分的语言模型功能,很神奇吧,你可以将它改造成最适合自己使用的状态,或者打磨一下发到线上让更多的人使用到
chatGpt的token
chatGpt会根据token来分隔我们的语句,一般而言对于英文语句,chatGpt会分隔每个词作为token,并对每个token进行逐个分析,而对于中文大部分都是逐个汉字进行分析的。
其中,prompt_tokens是我们的提示词,而completion_tokens即gpt的回答语句。
五、画一画自己喜欢的图
openAI不光是有chatGpt和我们聊天,我们也可以使用ai进行画画自己喜欢的图片
我们可以使用 openai.images接口,直接生成图片
const createImage = (value: string) => { openai.images.generate({ prompt: value,//用户的提示词 size: '512x512',//图片尺寸 response_format: 'url',//通过url的方式进行返回 n: 1//生成一张图片 }).then((res: any) => { console.log('res', res?.data?.[0]?.url) setImgUrl(res?.data?.[0]?.url) }) }
同时可以试试createVariation接口,生成相似图片
目前这个绘图的api支持更换模型(但是只能用线上的模型)相对于专业的绘图接口来说简陋一点,但是完成大部分场景还是可以的
当你完成了上面的教程后,你已经是入门openAI了,接下来你可以去看看源码文档查看其他调用方法,或者尝试在node中实现上面的功能(需要注意node不能直接import ES6模块)
参考文献
openAI的npm仓库openai - npm
openAI的gitlhub工程地址(里面有文档)https://github.com/openai/openai-node