如何搭建个人的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.rhkb.cn/news/83079.html

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

相关文章

机器视觉、图像处理和计算机视觉:概念和区别

机器视觉、图像处理和计算机视觉是相关但有区别的概念。 机器视觉主要应用于工业领域&#xff0c;涉及图像感知、图像处理、控制理论和软硬件的结合&#xff0c;旨在实现高效的运动控制或实时操作。 图像处理是指利用计算机对图像进行复原、校正、增强、统计分析、分类和识别…

Linux 1.2.13 -- IP分片重组源码分析

Linux 1.2.13 -- IP分片重组源码分析 引言为什么需要分片传输层是否存在分段操作IP分片重组源码分析ip_createip_findip_frag_createip_doneip_glueip_freeip_expireip_defragip_rcv 总结 本文源码解析参考: 深入理解TCP/IP协议的实现之ip分片重组 – 基于linux1.2.13 计网理论…

2023好用苹果电脑杀毒软件Cleanmymac X

苹果电脑怎么杀毒&#xff1f;这个问题自从苹果电脑变得越来越普及&#xff0c;苹果电脑的安全性问题也逐渐成为我们关注的焦点。虽然苹果电脑的安全性相对较高&#xff0c;但仍然存在着一些潜在的威胁&#xff0c;比如流氓软件窥探隐私和恶意软件等。那么&#xff0c;苹果电脑…

适配器模式来啦

网上的大多数的资料中适配器模式和代理模式都是紧挨着进行介绍的&#xff0c;为什么呢&#xff1f;&#xff1f;&#xff1f; 是因为适配器模式和代理模式有太多的相似之处&#xff0c;可以进行联动记忆但是也要做好区分。 在菜鸟教程中&#xff0c;适配器模式的定义是作为两…

PyQt学习笔记-Windows系统版本兼容问题踩坑记录

1 Pyinstaller打包的exe在Win10上可以使用&#xff0c;在Win7上缺提示找不到dll。 错误信息&#xff1a; Traceback (most recent call last): File "main.py", line 4, in <module> ImportError: DLL load failed while importing QtWidgets: 找不到指定的…

【大数据】Flink 从入门到实践(一):初步介绍

Flink 从入门到实践&#xff08;一&#xff09;&#xff1a;初步介绍 Apache Flink 是一个框架和分布式处理引擎&#xff0c;用于在 无边界 和 有边界 数据流上进行 有状态 的计算。Flink 能在所有常见集群环境中运行&#xff0c;并能以内存速度和任意规模进行计算。 1.架构 1…

docker中的jenkins之流水线构建

docker中的jenkins之流水线构建项目 1、用node这种方式&#xff08;因为我用pipeline方式一直不执行&#xff0c;不知道为什么&#xff09; 2、创建项目 创建两个参数&#xff0c;一个是宿主端口号&#xff0c;一个是docker中的端口号 3、使用git项目中的Jenkinsfile 4、编写…

区块链学习6-长安链部署:如何创建特定共识节点数和同步节点数的链

正常prepare的时候只支持4 7 13 16个节点个数&#xff0c;想要创建10个节点&#xff0c;其中5个是共识节点&#xff0c;如何实现&#xff1f; 1. 注释掉prepare.sh的这几行&#xff1a; 2. 修改 crytogen的模板文件&#xff1a; 如果是cert模式&#xff1a;chainmaker-crypt…

《使用 VMware 在 Windows 上搭建 Linux 系统的完整指南》

《使用 VMware 在 Windows 上搭建 Linux 系统的完整指南》 1、准备工作1.1 安装 VMware 软件1.2 下载 Linux 发行版镜像文件1.3 安装SSH工具 2、创建新的虚拟机2.1 VMware页面2.2 打开VMware页面并点击创建新的虚拟机&#xff0c;选择自定义2.3 选择系统兼容性&#xff0c;默认…

嵌入式面试刷题(day3)

文章目录 前言一、怎么判断两个float是否相同二、float数据可以移位吗三、数据接收和发送端大小端不一致怎么办四、怎么传输float类型数据1.使用联合进行传输2.使用字节流3.强制类型转换 总结 前言 本篇文章我们继续讲解嵌入式面试刷题&#xff0c;给大家继续分享嵌入式中的面…

compile_and_runtime_not_namespaced_r_class_jar\debug\R.jar: 另一个程序正在使用

问题情况&#xff1a; run App的时候&#xff0c;提示该文件被占用 想要clean Project&#xff0c;还是提示该文件被占用&#xff0c;这个文件和连带的文件夹都无法被删除。 方法1&#xff1a; AndroidStudio下方的terminal&#xff08;没有这个窗口的话&#xff0c;从上面的…

Android Glide MemorySizeCalculator计算值,Kotlin

Android Glide MemorySizeCalculator计算值,Kotlin for (i in 100..1000 step 50) {val calculator MemorySizeCalculator.Builder(this).setMemoryCacheScreens(i.toFloat()).setBitmapPoolScreens(i.toFloat()).setMaxSizeMultiplier(0.8f).setLowMemoryMaxSizeMultiplier(0…

Fastjson 使用指南

文章目录 Fastjson 使用指南0 简要说明为什么要用JSON&#xff1f;用JSON的好处是什么&#xff1f;为什么要用JSON&#xff1f;JSON好处 1 常用数据类型的JSON格式值的范围 2 快速上手2.1 依赖2.2 实体类2.3 测试类 3 常见用法3.1 序列化操作核心操作对象转换为JSON串list转换J…

Java课题笔记~ Spring 概述

Spring 框架 一、Spring 概述 1、Spring 框架是什么 Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架&#xff0c;它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转&#xff08;IoC&#xff09;和面向切面编程&#xff08;AOP&#xff09;。 Spring…

canal 嵌入式部署 监听binlog

canal 嵌入式部署 背景技术选型canal原理用途嵌入式代码实现引入pom引入工具pommain方法引入常量定义install方法buildCanal方法pull方法printSummaryprintEntry2 总结谢谢 背景 最近发现一个需求,需要监听mysql 数据库里的数据变动, 但由于架构方面的原因, 只能做成单体嵌入式…

如何优化测试用例?

在我们日常测试工作中&#xff0c;编写测试用例往往花费较多时间&#xff0c;而且设计的测试用例冗杂和不完整&#xff0c;从而造成用例执行检查不完整&#xff0c;效率低下&#xff0c;不能及时发现项目缺陷。 因此提高测试用例编写和执行效率迫在眉睫&#xff0c;一般来说&am…

python之prettytable库的使用

文章目录 一 什么是prettytable二 prettytable的简单使用1. 添加表头2. 添加行3. 添加列4. 设置对齐方式4. 设置输出表格样式5. 自定义边框样式6. 其它功能 三 prettytable在实际中的使用 一 什么是prettytable prettytable是Python的一个第三方工具库&#xff0c;用于创建漂亮…

Endnote 具体期刊格式检索和下载方法——以nature期刊参考文献格式检索和下载为例

Endnote 具体期刊格式检索和下载方法——以nature期刊参考文献格式检索和下载为例 在外文文章写作时候&#xff0c;有时为了提高写作效率&#xff0c;会用到Endnote文献引用功能。然而&#xff0c;有时可能没有现成的参考文献格式&#xff0c;此时&#xff0c;比较快捷的方式&…

【云原生】kubernetes控制器deployment的使用

目录 ​编辑 1 Controller 控制器 1.1 什么是 Controller 1.2 常见的 Controller 控制器 1.3 Controller 如何管理 Pod 2 Deployment 2.1 创建 deployment 2.2 查看 deployment 2.3 扩缩 deployment 2.4 回滚 deployment 2.5 删除 deployment 1 Controller 控制器 …

【设计模式】观察者模式

什么是观察者模式&#xff1f; 观察者模式&#xff08;又被称为发布-订阅&#xff08;Publish/Subscribe&#xff09;模式&#xff0c;属于行为型模式的一种&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态…