如何搭建个人的GPT网页服务

写在前面

在创建个人的 GPT网页之前,我登录了 Git 并尝试了一些开源项目,但是没有找到满足我个性化需求的设计。虽然许多收费的 GPT网页提供了一些免费额度,足够我使用,但是公司的安全策略会屏蔽这些网页。因此,我决定自己创建一个既可以从公司访问又能满足个性化需求的 聊天机器人网页服务,以提高我的工作效率。

经过一段时间的摸索,我成功搭建了自己的 GPT网页服务,并且这个服务整体来说非常稳定。在使用了几个月的时间里,只出现了一次网页崩溃的情况,而且只需重启服务就可以继续使用。

接下来,我将介绍我是如何搭建我的 GPT网页服务的。

在这里插入图片描述
在这里插入图片描述

需求分析

我的GPT网页服务主要是为个人使用,目的是为了提高工作效率,所以整体设计比较简单,主要包含

  1. 登录页面,防止其他人访问网页,网页被攻击。
  2. 聊天机器人页面,类似于聊天机器人官网的页面,向聊天机器人提交问题。
    在这里插入图片描述
  3. English Helper个性化页面,因为工作中每天都要接触到英语,所以创建该页面加强自己对英文词汇的理解。
    在这里插入图片描述

技术实现

整体使用的是最基础的页面架构,前端用的是HTML+CSS+JS,以及Bootstrap,后端用的是Flask。这样的好处是代码编写快捷,简单,只需要一个Pycharm编辑器就可以了。网页服务是搭建在Gunicorn上的,整体配置简单,方便。

就代码而言,整体结构也比较简单。主要分为一个Template包,里面包含的是一些前端的页面。后端服务的代码都写在了app.py里面。另外因为这里的所有需求都需要和聊天机器人进行交互,所以将和聊天机器人进行交互的逻辑单独写在了gpt_untils里面,用户配额和用户权限管理也是相对独立的一部分,将用户的相关逻辑写在了userconfig.py里面,因为这个网页只提供给少数人使用,用户的信息保存在userconfig.json文件里(而不是数据库里面)。
在这里插入图片描述

服务器选择的是阿里云服务器,因为阿里云服务器价格较为优惠,而且遇到问题有较多的参考文档。因为服务器是国内的,所以调用ChatGpt接口的时候需要使用代理,我使用过的代理有https://api.openai-proxy.com/v1和https://api2d.com,使用openai-proxy还需要自己去open-ai官网申请gpt账号,申请账号所花的成本大概为15块钱,使用https://api2d.com可以自己在上面充值,价格如下,充值一次有6个月的有效期,根据自己的需要选择即可。
在这里插入图片描述

代码详细解释

登录页面和首页面

登录页面代码如下,采用常用的登录页面的结构,失败后会给消息提示,页面的结构设计继承自base.html.

{% extends "base.html" %}{% block content %}<h3>用户登录</h3>
<form action="/login" method="post"><div class="row"><div class="col-8">请输入用户名:<input type="text" class="form-control" name="username"><br />请输入密码:<input type="password" class="form-control" name="password"><br /><div id="result">{{message}}</div><br /><button type="submit" id="submitbtn" class="btn btn-primary">登录</button></div></div>
</form>
{% endblock %}

以下为base.html的页面,主要定义了整个网页的框架结构。需要注意页面中的JS部分show_quota_info和gpt_show_stream_info方法,一个是显示用户配额信息,一个是显示GPT返回的结果。

show_quota_info函数使用jQuery库中的ajax方法发送GET请求到"/show_quotas"路径。请求成功后,将返回的数据使用$(“#quota_info”).html(data)方法插入到id为quota_info的HTML元素中。

gpt_show_stream_info函数首先禁用id为submitbtn的HTML元素(按钮),然后将id为result的HTML元素的内容设置为"ChatGPT正在请求中,加载中…"。接下来,创建一个EventSource实例,其中的参数是传入的url。通过该实例,可以与服务器建立一个持久化的连接,以接收来自服务器的事件。

在EventSource的onmessage事件处理程序中,首先检查变量begin_output,如果为false,表示第一次收到消息,则将begin_output设置为true,并清空id为result的HTML元素的内容。

然后通过检查接收到的消息的内容,如果内容为"[DONE]",表示流式输出结束,关闭EventSource连接,启用id为submitbtn的HTML元素(按钮),并调用show_quota_info函数刷新配额信息。

如果接收到的消息不是"[DONE]",则将该消息追加到id为result的HTML元素的内容中。

<!doctype html>
<html lang="zh-CN">
<head><!-- 必须的 meta 标签 --><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><!-- Bootstrap 的 CSS 文件 --><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet"><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.css.map" rel="stylesheet"><title>DaPengGPT</title><style>#topnav {background-color:#000;color: #fff;padding-top: 10px;padding-bottom: 10px;margin-bottom: 20px;}#topnav2{background-color:#000;color: #fff;padding-top: 10px;padding-bottom: 10px;margin-bottom: 20px;}#topnav2 a{color: #fff;font-size: 18px;}</style>
</head>
<body><div id="topnav" class="container-fluid"><div class="row"><div class="col-7"><h2>DaPengChatGPT</h2></div><div class="col-5 d-flex justify-content-end align-items-center"><div class="ml-auto pr-3"><h4><span>{{session["username"]}}</span>{% if session["username"] is defined %}&nbsp;&nbsp;|&nbsp;&nbsp;<a href="/logout">logout</a>{% endif %}</h4></div></div></div></br><h5><a href="/">MyChatGPT</a>&nbsp;&nbsp;&nbsp;&nbsp;<a href="/english_helper">English Helper</a></h5></div><div class="container-fluid"><div id="content">{% block content %}{% endblock %}</div>
</div><!-- JavaScript 文件是可选的。从以下两种建议中选择一个即可! --><!-- 选项 1:jQuery 和 Bootstrap 集成包(集成了 Popper) -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap/5.5.0/js/bootstrap.bundle.min.js"></script><script>// 展示最新的配额信息function show_quota_info(){$.ajax({url:"/show_quotas",type:"GET",success:function(data){$("#quota_info").html(data)}})}// 使用流式的方法,展示gpt的结果function gpt_show_stream_info(url){$("#submitbtn").prop('disabled', true)$("#result").html("ChatGPT正在请求中,加载中...")var source = new EventSource(url)var begin_output = falsesource.onmessage = function(event){if (begin_output === false){begin_output = true$("#result").html("")}if(event.data == "[DONE]"){source.close()$("#submitbtn").prop('disabled', false)show_quota_info()} else {$("#result").html($("#result").html() + event.data)}}}
</script>{% block myjavascript %}{% endblock %}<!-- 选项 2:Popper 和 Bootstrap 的 JS 插件各自独立 -->
<!--
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
-->
</body>
</html>
```html
如上代码中的show_quota_info和gpt_show_stream_info被主页面使用到,主页面的代码如下:```html
{% extends "base.html" %}{% block content %}
<style>#result{padding: 10px;background-color: #eee;margin-top: 10px;margin-bottom: 100px;word-wrap: break-word;}#rendered-content {display: none;
}</style>
<form action="/chatgpt-clone" method="post"><div class="alert alert-success" id="quota_info"></div><div><textarea class="form-control" rows="2" cols="20" name="question" id="question"></textarea></div><div style="margin-top: 10px;" class="text-right"><button type="button" id="submitbtn" class="btn btn-primary">提交问题</button></div><div id="rendered-content"></div><div id="result">返回的结果</div>
</form>
{% endblock %}{% block myjavascript %}<script src="https://cdn.bootcdn.net/ajax/libs/marked/3.0.2/marked.min.js"></script><script>var questionTextarea = document.getElementById('question'); // 获取 textarea 元素var renderedContent = document.getElementById('rendered-content'); // 获取渲染后内容的目标元素questionTextarea.addEventListener('input', function() {var inputValue = questionTextarea.value; // 获取 textarea 的输入值var renderedHTML = marked(inputValue);  // 使用 marked 库解析输入的 HTML 代码renderedContent.innerHTML = renderedHTML; // 将解析后的 HTML 渲染到目标元素中});</script><script type="text/javascript">$(function(){show_quota_info()$("#submitbtn").click(function(){var questionText = $("#question").val(); // 获取 textarea 的值var formattedQuestion = questionText.replace(/\n/g, '<br>'); // 将换行符转换为 <br> 标签var url = "/chatgpt-clone?question=" + encodeURIComponent(formattedQuestion);gpt_show_stream_info(url)})})</script>{% endblock %}

代码中有两个JS,其中第一个JS处理提交问题的表单数据,防止前端页面的代码不被识别,第二个JS用于处理表单传过来的值,传给chatgpt-clone函数。

如上前端页面代码中对应的登录代码的逻辑如下,获取前端提交过来的用户名和密码,使用userconfig.check_user()方法,和配置文件中存取的用户名和密码进行对比。

@app.route("/login", methods=["GET", "POST"])
def login():message = ""if request.method == "POST":username = request.form.get("username")password = request.form.get("password")if userconfig.check_user(username, password):session["username"] = usernamereturn redirect("/")else:message = "用户名或者密码错误"return render_template("login.html", message=message)

对应页面MyChatGPT 后端代码处理逻辑如下:

@app.route("/chatgpt-clone", methods=["POST", "GET"])
def chatgpt_clone():if "username" not in session:return redirect("/login")username = session["username"]question = request.args.get("question", "")question = str(question).strip()if question:new_question = questionif 'J:' in question[:2]:new_question = f"我们店的名字叫做茶园路-重庆鸡公煲,现在我们店新出了菜品{question[2:]},请帮我生成不少于100字的广告语,包含我们店的名字,强调菜品好吃,实惠,量大,特别有满足感,吃了还想吃"elif 'C:' in question[:2]:new_question = f"请帮我指出以下代码的问题,并优化代码{question[2:]}"return flask.Response(gpt_utils.gpt_stream(username, new_question),mimetype="text/event-stream")return "没有内容"

代码中除了单纯将前端提交的问题转给聊天机器人,另外当提交的问题有J:,C:前缀的时候,代码中会对前端提交的问题进行二次处理,然后转给聊天机器人。

类似于MyChatGPT 页面的后端代码,english_helper的后端代码如下:

@app.route("/english_helper", methods=["POST", "GET"])
def english_helper():if "username" not in session:return redirect("/login")# 获取用户上传过的文件列表username = session["username"]if request.args.get("word", ""):word = request.args.get("word", "")my_understanding = request.args.get("my_understanding", "")if 'O:' in word:new_word = word.replace('O:', '')sentence = f"""I want to use the following expressions in my email and meetings, but I am not sure if they are appropriate. Please help me optimize the expressions using declarative sentences and techniques from the book "Nonviolent Communication”.{new_word}"""else:sentence = f"""I'm an IT guy works in english environment, i want to know the meaning of '{word}' used in meeting or email,and how to pronounce it"""if my_understanding != '':sentence = sentence + f"""per my understanding it means {my_understanding}, is that correct?"""print(sentence)return flask.Response(gpt_utils.gpt_stream(username, sentence),mimetype="text/event-stream")return render_template("english_helper.html")

这段代码是将MyChatGPT 的表单分成两个输入框,根据数据框中输入的内容进行不同的处理,然后传给GPT接口。
现在市面上有很多GPT应用其实都是换汤不换药,仅仅是改了前端页面和业务范围,后端处理的逻辑与上方后端代码处理类似。

整体项目中有一段核心代码如下所示:

def gpt_stream(username, question):try:userconfig.check_quota(username)except Exception as e:yield "data: %s\n\n" % str(e)yield "data: [DONE]\n\n"returnopenai.api_key = os.getenv("OPENAI_API_KEY")openai.api_base = "https://api.openai-proxy.com/v1"result = openai.ChatCompletion.create(model="gpt-3.5-turbo",messages=[{"role": "user", "content": question}],stream=True,user=username)for chunk in result:if chunk["choices"][0]["finish_reason"] is not None:data = "[DONE]"else:data = chunk["choices"][0]["delta"].get("content", "")yield "data: %s\n\n" % data.replace("\n", "<br/>").replace(" ", "&nbsp;")

这段代码从open-ai官网获取并调试的,是个人项目和open-ai接口交互的部分,向接口传入提问的问题并按照一定格式返回GPT的答案。

最后在terminal敲入便可以运行项目。
在这里插入图片描述
在这里插入图片描述

写在后面

尽管自己搭建的chatgpt相比其他网站还存在一些不足之处,但它能够满足个性化需求,并且可以在公司内网访问,显著提高了工作效率。在开发chatGpt个人网页的过程中,我进一步巩固了自己的前后端知识,并为将来快速开发增强了信心。在开发过程中遇到了一些问题,但通过chatGpt工具,我得到了很多解答,进而提高了开发效率。同时,我还能够推测出市面上其他AI工具背后的逻辑。以下是项目的代码,请自行获取:

链接:https://pan.baidu.com/s/1UREj4rPbZn18lqe1cGaSlg?pwd=8hzg
提取码:8hzg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/26595.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2.4G芯片XL2408开发板,SOP16封装,芯片集成1T 8051内核单片机

XL2408开发板可用于2.4G芯片XL2408开发板的开发调试。XL2408烧录仿真需要使用WS_LINK。XL2408开发板烧录仿真需要接4根线&#xff1a;PA13:DIO&#xff0c;PA14:CLK&#xff0c;VCC&#xff0c;GND。 XL2408芯片集成射频收发机、频率收生器、晶体振荡器、调制解调器等功能模块,…

链表——LinkedList类的概述和实现

LinkedList类 1.1LinkedList类概述 LinkedList类底层是基于双向链表结构实现的&#xff0c;不同于ArrayList类和Vector类是基于数组实现的&#xff1b;LinkedList类是非线程安全的&#xff1b;LinkedList类元素允许为null&#xff0c;允许重复元素&#xff1b;LinkedList类插…

2023下半年软考初级网络管理员报名入口-报名流程-备考方法

软考初级网络管理员2023下半年考试时间&#xff1a; 2023年下半年软考初级网络管理员的考试时间为11月4日、5日。考试时间在全国各地一致&#xff0c;建议考生提前备考。共分两科&#xff0c;第一科基础知识考试具体时间为9:00到11:30&#xff1b;第二科应用技术考试具体时间为…

AWS 中文入门开发教学 49- S3 - 区域间复制

知识点 S3 存储桶内容在全球区域间进行复制官网 https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/replication.html 实战演习 在东京区建立存储桶 Name: woyaofuzhi启用版本控制(完成区域间复制必须开启版本控制) 在新加坡区建立存储桶 Name: woyaofuzhibac…

Vue2源码分析-环境搭建

安装rollup 项目初始化 npm init -y安装pnpm npm i -g pnpm安装rollup以及相关插件 pnpm i rollup rollup/plugin-babel babel/core babel/preset-env --save-dev在根目录创建rollup.config.js文件&#xff0c;并且配置如下 import babel from "rollup/plugin-babel…

配置固定二级子域名远程访问内网群晖NAS 7.X版 【内网穿透】——“cpolar内网穿透”

配置固定二级子域名远程访问内网群晖NAS 7.X版 【内网穿透】 文章目录 配置固定二级子域名远程访问内网群晖NAS 7.X版 【内网穿透】前言1. 创建一条固定数据隧道2. 找到“保留二级子域名”栏位3. 重新编辑之前建立的临时数据隧道4. 进入“在线隧道列表”页面5. 在其他浏览器访问…

基于 Emscripten + WebAssembly 实现浏览器操作 Excel

一、为什么要造这个轮子 【C】使用WebAssembly在浏览器端操作Excel_wasm文件用什么打开_你的薄荷醇的博客-CSDN博客使用WebAssembly在浏览器端操作Excel_wasm文件用什么打开https://blog.csdn.net/weixin_44305576/article/details/125545900?ops_request_misc%257B%2522requ…

【Redis】——AOF持久化

什么是AOF日志 AOF日志是redis为数据的持久化提供了的一个技术,日志里面记录着执行redis写命令。每当redis执行一条写命令的时候&#xff0c;就会将该命令记录 到AOF日志当中。当redis启动的时候&#xff0c;可以加载AOF日志中的所有指令&#xff0c;并执行这些指令恢复所有的…

FFmpeg将编码后数据保存成mp4

以下测试代码实现的功能是&#xff1a;持续从内存块中获取原始数据&#xff0c;然后依次进行解码、编码、最后保存成mp4视频文件。 可保存成单个视频文件&#xff0c;也可指定每个视频文件的总帧数&#xff0c;保存多个视频文件。 为了便于查看和修改&#xff0c;这里将可独立的…

webpack基础知识十:与webpack类似的工具还有哪些?区别?

一、模块化工具 模块化是一种处理复杂系统分解为更好的可管理模块的方式 可以用来分割&#xff0c;组织和打包应用。每个模块完成一个特定的子功能&#xff0c;所有的模块按某种方法组装起来&#xff0c;成为一个整体(bundle) 在前端领域中&#xff0c;并非只有webpack这一款…

chaitin-Nginx+Docker

Nginx实战 任务一 1、源码包安装NGINX A&#xff0c;搭建Web Server&#xff0c;任意HTML页面&#xff0c;其8080端口提供Web访问服务&#xff0c;截图成功访问http(s)&#x1f615;/[Server1]:8080并且回显Web页面 官网地址&#xff1a;http://nginx.org/en/download.html 步骤…

MySQL中的视图

系列文章目录 MySQL常见的几种约束 MySQL中的函数 MySQL中的事务 文章目录 系列文章目录前言一、视图的概念二、视图的好处三、SQL展示总结 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&…

十二、ESP32控制步进电机

1. 运行效果 2. 步进电机 最大特点:能够控制旋转一定的角度 3. 步进电机原理

【vue】vue基础知识

1、插值表达式&属性绑定 <!--template展示给用户&#xff0c;相当于MVVM模式中的V--> <template><div class"first_div">//插值表达式<p>{{ message }}</p>//这里的参数是从父组件的template里传过来的<p>{{data_1}}</p…

超详情的开源知识库管理系统- mm-wiki的安装和使用

背景&#xff1a;最近公司需要一款可以记录公司内部文档信息&#xff0c;一些只是累计等&#xff0c;通过之前的经验积累&#xff0c;立马想到了 mm-wiki&#xff0c;然后就给公司搭建了一套&#xff0c;分享一下安装和使用说明&#xff1a; 当前市场上众多的优秀的文档系统百…

Demystifying Prompts in Language Models via Perplexity Estimation

Demystifying Prompts in Language Models via Perplexity Estimation 原文链接 Gonen H, Iyer S, Blevins T, et al. Demystifying prompts in language models via perplexity estimation[J]. arXiv preprint arXiv:2212.04037, 2022. 简单来说就是作者通过在不同LLM和不同…

VLT:Vision-Language Transformer用于引用的视觉语言转换和查询生成分割

摘要 在这项工作中&#xff0c;我们解决了引用分割的挑战性任务。引用分割中的查询表达式通常通过描述目标对象与其他对象的关系来表示目标对象。因此&#xff0c;为了在图像中的所有实例中找到目标实例&#xff0c;模型必须对整个图像有一个整体的理解。为了实现这一点&#…

Flutter 让软键盘不再自动弹起

1、问题说明&#xff1a; 在开发中&#xff0c;经常遇到这种事&#xff0c;一个页面有输入框&#xff0c;点击输入框后&#xff0c;会弹起软键盘&#xff0c;同时输入框会聚焦&#xff0c;手动收起软键盘后&#xff0c;点击另一个按钮前往下一个页面或者显示一个弹窗&#xff0…

一百四十一、Kettle——kettle8.2在Windows本地开启carte服务以及配置子服务器

一、目的 在kettle建好共享资源库后&#xff0c;为了给在服务器上部署kettle的carte服务躺雷&#xff0c;先在Windows本地测试一下怎么玩carte服务 二、Kettle版本以及在Windows本地安装路径 kettle版本是8.2 pdi-ce-8.2.0.0-342 kettle本地安装路径是D:\j…

Vue + Cesium快速搭建,全流程(最新总结)

方式一&#xff1a;直接引入&#xff08;最简单&#xff09; 1.安装Cesium&#xff08;Vue搭建可以看我上一期的文章&#xff09; npm i cesium -save2.将node_modules\cesium\Build\Cesium文件夹拷贝到项目的public文件中 3.在public\index.html引入Cesium <!DOCTYPE h…