> 自媒体 > (AI)人工智能 > 肝了一周,我终于有自己的ChatGPT站点了!
肝了一周,我终于有自己的ChatGPT站点了!
来源:Anyin
2023-04-04 09:38:42
806
管理

Hello 大家好,这里是Anyin。

前言

我最近利用业余时间,肝了一周的时间,终于完成的一个类ChatGPT站点的小应用。

废话不多说,先上地址:chat . anyin . org . cn

整个项目基于chatgpt-web[1] 和 ruoyi-vue-pro[2] 实现的。

是的,你没看错,后端就是用Java实现的。因为我比较熟悉的是Java,然而网上少有Java实现的后端,所以就想着自己尝试搞下。

站点服务器上使用的是魔法网络,所以体验并不丝滑;并且因为手上只有一个key,Token有限,后续还需要有大量测试,所以限制了Token的使用。 有需要白嫖的同学可以找找其他站点哈,应该有很多。

接下来给大家分享下在整个开发过程中遇到的一些小问题。

在项目开始你需要有以下几个东西需要准备:

• 一个OpenAI的账号,并且配合key

• 魔法网,可以在本地或者服务器进行代理

• 一个认证过的服务号,这里主要用作扫码登录使用

用户登录认证

项目开始,遇到的第一个问题就是用户登录认证问题。因为考虑到需要有Token的限制,所以必须有登录这一环节,否则你都不知道哪个用户用了多少的Token。

站点初步的想法是只在微信客户端 和 PC端上打开。

如果是在微信客户端打开,那么很简单,用公众号的那套授权认证就OK;PC端的授权认证就会比较麻烦,正规的做法是把PC站点在微信开放平台上绑定,公众号也在开放平台上绑定,这样子就可以使用开放平台的授权认证流程。

但是PC站点在微信开放平台做绑定,还需要认证审核还挺麻烦的,最后还是决定使用微信公众号的那套认证流程,做一点改造即可。

基本流程如下:

图片上网上偷的,原文地址看这儿[3]

其实基本原理就是在PC站点上生成一个跳转公众号H5的二维码,并且启动一个轮询(这里也可以使用websocket)服务端用户是否在微信客户端授权认证了。

如果用户在微信客户端登录认证了,就把结果写入到服务端,接着PC站点的轮询任务就拿到认证成功的结果,这样子PC站点就完成用户认证过程。

接口参数

在OpenAI的官网上有2个接口都可以完成聊天的功能。

• /v1/completions

• /v1/chat/completions

从接口文档可以看出,/v1/completions更适合做一些单一的一问一答的的场景,而且它也不支持gpt-3.5-turbo模型,支持text-davinci-003模型,另外这个模型又很贵。

而 /v1/chat/completions 接口则可以通过messages字段,来实现具有上下文的聊天场景,最重要的是它还便宜。1000个Token才0.002$。

所以,最终对接使用的是/v1/chat/completions接口。

接下来,我们来看看接口的几个重要参数:

model 指定需要使用的模型ID,这个接口只支持以下几个模型:

• gpt-4

• gpt-4-0314

• gpt-4-32k

• gpt-4-32k-0314

• gpt-3.5-turbo

• gpt-3.5-turbo-0301

messages

messages是一个对象数组,每个对象包含2个字段:role和content。role有三种角色:

• system 系统用户,这个时候content里面的内容就代表我们初始给ChatGPT一个指令,告诉ChatGPT应该怎么回答用户的问题。这也就是为什么很多类似的站点第一轮对话都有对应的prompts,其实就是告诉ChatGPT用户想干什么。

• user C端的用户,这个时候content的内容就是用户发送给ChatGPT的。

• assistant 助理,这个时候content的内容就是ChatGPT返回回来的内容。

所以,基于这个设计,我们如果需要一个具有上下文的聊天,那么每次发送给ChatGPT的messages字段都需要以以下的顺序发送:

[system, user, assistant, user, assistant...]

temperature

这个参数的输入范围是在0-2之间的浮点数,表示输出结果的随机性或者多样性。 这个参数设置的越小,那么ChatGPT返回的结果随机性就越小,越大返回的结果的随机性就越大。

基本意思就是对于同一句话,设置越小,那么多次请求返回的文本内容基本一致;设置越大,那么多次请求返回的文本内容就越不一样。当然,返回的文本内容想表达的意思基本一致。

max_tokens

就是本次调用ChatGPT返回的最大Token数量。如果你想知道你一次输入的文字有多少Token,可以通过这个网站来测试:Tokenizer[4]

n

n 表示ChatGPT应该给你返回几条记录。因为我们是对话模式,所以这里都是设置为1。 如果是类似让ChatGPT 给你取几个名字的场景,那么可以设置成其他数值,它就会给你返回多条记录。

当然,这也可以设置为1,然后通过文本内容明确告诉ChatGPT需要给你几个结果,这样子也可以做到同样的效果。

stream

这个参数是用来设置流式响应,你可以看到当遇到大段文本的时候,ChatGPT官网那种逐字打印给人感觉会很丝滑;而不是等到所有文本都收集好了,之后再一次性显示,那就需要等,体验感直接下头。

stop

就是告诉ChatGPT当遇到stop参数的文本的时候,就会停止下来,避免消耗大量的Token。例如,stop=n,那么ChatGTP在返回的内容,如果有n的内容,那么就会停止输出。

presence_penalty

这个参数是在 -2.0 - 2.0 之间,它大概意思是如果一个Token 在前面的内容已经出现过了,那么在后面生成的时候会给它一个概率惩罚,降低它再出现的概率,让ChatGPT会更倾向输出新的话题和内。

frequency_penalty

这个参数一样在 -2.0 - 2.0 之间,它的意思是对于重复出现的Token进行概率惩罚。这样子ChatGPT就会尽量使用不同的表述。如果设置为-2,那么它会更容易胡说八道。

logit_bias

这个参数是一个Map的数据结构,它的使用场景可以用来过滤敏感词。

例如:国家 2个字,可以计算出它的Token值(这个站点可以计算:Tokenizer[5]),然后指定这个Token值为-100。那么ChatGPT在返回的词汇中就不会出现这2个字。

不过建议使用1到-1就行。-100可能会慢。

好了。充分了解了这个接口的几个参数之后,终于可以开始进行编码了!!!

流式响应

在完成OpenAI的接口请求 和 前端展示的编码之后,我发现一个头疼的事情:体验好差啊!!!

当遇到ChatGPT需要给我大段的是输出内容的时候,需要等好久,然后前端也没有像官网那样子可以逐字输出,一定需要等所有文字都返回了之后,然后一次性显示。当遇到大段的文本输出的时候,可能需要等个30秒才能出来,体验感直接下头。= = !

这个时候我才意识到stream这个参数的重要性。

请求OpenA的接口,使用的是Hutool的 HttpRequest工具类,对于OpenAI 返回的流式喜响应可以这么处理:

// 构建请求 CompletionRequest completionRequest = CompletionRequest.builder() .max_tokens(messageMaxTokens) .temperature(new BigDecimal("0.8")) .n(1) .model(model) .messages(messages) .stream(true) // 设置流式响应 .build(); // 请求系统 InputStream input = null; try { string json = JSONUtil.toJSONStr(completionRequest); // 返回输入流 input = openAiHttpProxy .body(json) .execute().bodyStream(); }catch (Exception ex){ log.error("请求OpenAI接口异常, key={}", openAiHttpProxy.header("Authorization") ,ex); throw ServiceExceptionUtil.exception(ErrorCodeConstants.OPEN_AI_API_ERROR); } Scanner scanner = new Scanner(input, StandardCharsets.UTF_8.name()); try { while (scanner.hasNextLine()) { String line = scanner.nextLine(); String data = line.replaceAll("data: ", "").trim(); if(!(data.startsWith("{" ) && data.endsWith("}"))){ continue; } CompletionChunk chunk = JSONUtil.toBean(data, CompletionChunk.class); if(CollectionUtil.isEmpty(chunk.getChoices()) || StrUtil.isEmpty(chunk.getChoices().get(0).getDelta().getContent())){ continue; } function.apply(chunk); } }catch (Exception e){ log.error("请求结果: {}", e.getMessage()); throw ServiceExceptionUtil.exception(ErrorCodeConstants.OPEN_AI_API_ERROR); }finally { scanner.close(); }

这里是从OpenAI 接口拿到InputStream输入流,接着使用Scanner进行封装,然后逐行获取之后再转为CompletionChunk对象,最后通过function.apply()方法把数据提交给上层函数,用于给前端进行流式输出。

在Controller的代码如下:

@PostMapping("/message")@Operation(summary = "ChatGPT 消息发送")public ResponseEntity message(@RequestBody AppGptMessageReqVO req){ // 因为OpenAI 返回的数据是逐个Token的,这里我们给前端需要是一段文本,所以需要拼接。 StringBuilder stringBuilder = new StringBuilder(); // service 处理业务逻辑 StreamingResponseBody responseBody = outputStream -> chatService.message(req, userId != null, (chunk) -> { try { String content = chunk.getChoices().get(0).getDelta().getContent(); stringBuilder.append(content); outputStream.write(stringBuilder.toString().getBytes()); outputStream.flush(); Thread.sleep(100); } catch (Exception e) { log.error("write error", e); throw ServiceExceptionUtil.exception(ErrorCodeConstants.OPEN_AI_API_ERROR); } return null; }); return ResponseEntity.ok() .contentType(new MediaType(MediaType.APPLICATION_OCTET_STREAM, StandardCharsets.UTF_8)) .body(responseBody);}

这里使用ResponseEntity进行数据返回,同时指定了返回的ContentType 为 APPLICATION_OCTET_STREAM, 所以在前端是无法解析为JSON对象的,前端接收到的只是一段JSON的字符串而已,最后还需要前端再手动转下对象才可以。

最后

好了,以上就是上周肝这个项目遇到的一些问题和知识,如果有什么地方不对的,请指正。

另外,当使用流式响应的时候,每次对话的Token使用情况API就不再返回了, 这里需要另外计算。

References

[1] chatgpt-web: https://github.com/Chanzhaoyu/chatgpt-web[2] ruoyi-vue-pro: https://gitee.com/zhijiantianya/ruoyi-vue-pro[3] 图片上网上偷的,原文地址看这儿: https://zhuanlan.zhihu.com/p/360891056[4] Tokenizer: https://platform.openai.com/tokenizer[5] 这个站点可以计算:Tokenizer: https://platform.openai.com/tokenizer

0
点赞
赏礼
赏钱
0
收藏
免责声明:本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本网证实,对本文以及其中全部或者 部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。 凡本网注明 “来源:XXX(非本站)”的作品,均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对 其真实性负责。 如因作品内容、版权和其它问题需要同本网联系的,请在一周内进行,以便我们及时处理。 QQ:617470285 邮箱:617470285@qq.com
相关文章
全网都没有说明白,ChatGPT为什么无法在国内注册?顺带科普RLHF..
前段时间,一直有人在问,为什么国内无法注册ChatGPT,网上的回答也五花..
ChatGPT将取代金融、媒体、法律和技术工种?三分之二的从业者说不..
智东西编译 | 崔馨戈编辑 | 云鹏智东西2月8日消息,根据MLIV Pulse最新调..
上海山丘联康健康管理有限公司创始人、董事长颜艳春:ChatGPT让人均GDP增加..
贵阳网·甲秀新闻讯 “ChatGPT会超越互联网,开启人类的智业文明时代”5..
GPT-3自己上网搜答案!OpenAI新成果,让AI回答开放式问题..
WebGPT通过检索回答“尼罗河和长江谁更长?”的问题当被问道:“为什么苏..
国内如何正确使用ChatGPT
在当今互联网时代,聊天机器人已经成为了越来越多企业和个人的必备工具。..
网友:ChatGPT取代老胡 激怒胡锡进三度炮轰:人工智能打不败我..
下面这张图片,是网络大V、央媒《环球时报》前总编辑胡锡进在不到24小时..
北大“韦神”出题,GPT-4挑战失败,初二学生却给出标准答案!网友:又是凑..
近日北京大学北京国际数学研究中心推出的一道数学题获得了数学爱好者的广..
美女店长出轨多次,不雅聊天记录流出,内容非常露骨..
亲,原创不易,麻烦动动你的小手、点赞,转发,评论一下,谢谢^-^亲,原..
GPT-4来了,别慌,看懂了再大受震撼
Big news!它来了,上个月全网刷屏的ChatGPT又来了。这次,它又完成了超..
关于作者
国务院环卫工..(普通会员)
文章
636
关注
0
粉丝
1
点击领取今天的签到奖励!
签到排行

成员 网址收录40369 企业收录2981 印章生成216706 电子证书945 电子名片57 自媒体34015

@2022 All Rights Reserved 浙ICP备19035174号-7
0
0
分享
请选择要切换的马甲:

个人中心

每日签到

我的消息

内容搜索