使用go语言、Python脚本搭建一个简单的chatgpt服务网站。

news2024/10/1 3:24:16

使用go语言、Python脚本搭建一个简单的GPT服务网站

前言

研0在暑假想提升一下自己,自学了go语言编程和机器学习相关学习,但是一味学习理论,终究是枯燥的,于是自己弄点小项目做。

在这之前,建议您需要掌握以下两个技巧,我在这里不赘述了

  1. 一个openAI账号,并申请了KEY(b站有教程)
  2. 魔法的method(自己摸索哈~网上应该也有教程嘿嘿~)

现在开始!!!

文章目录

  1. Python准备
  2. go服务器与html页面
  3. 总结与效果展示

一、准备一个Python脚本

第一步:利用pip下载OpenAi依赖包

pip install openai

第二步:将openai的操作封装成一个类,OpenAi.py

import openai


class OpenAi:
    def __init__(self, key):
        """
        传入一个key
        :param key: 你申请的key
        """
        openai.api_key = key # 这里设置key
        self.model_name = "gpt-3.5-turbo"  # 使用默认chatgpt3.5模型
        self.role = "user"  # 使用user的角色,此外还有system等角色,可以自己改着来玩玩

    def submit(self, question):
        """
        这个方法向gpt发送你想发给gpt的message, 返回一个response对象,是json格式的
        """
        response = openai.ChatCompletion.create(
            model=self.model_name,  # 使用ChatGPT引擎
            messages=[
                {"role": "user", "content": question},
            ],
            temperature=0,
            # stream=True  # this time, we set stream=True
        )
        return response

第三步:编写Python脚本 testGPT2.py,注意修改自己的key

import os, sys
# 这里我们导入刚才封装的类
import OpenAI as op


def main():
    # 接受命令行参数
    args = sys.argv[1:]
    message = " ".join(args)
    open_ai_object = op.OpenAi("把你申请的key复制到这里来")
    response = open_ai_object.submit(message)
    # 取出gpt的回答
    gpt_answer = response["choices"][0]["message"]["content"]
    # 以utf-8的形式输出到命令行,避免中文乱码,后续go语言将会读取
    print(gpt_answer.encode('utf-8'))


if __name__ == '__main__':
    main()


第四步:测试一下下,启动cmd,cd到testgpt2.py的目录下

python testgpt2.py 你是谁啊?
python testgpt2.py who are you?

在这里插入图片描述
可以发现当询问中文时,脚本会返回中文的utf-8编码,询问英文的时候,会返回英文内容。
运行成功!!!!!!

小结:截至目前,python脚本准备好了,我们接下来使用go搭建一个简单的服务器。

二、搭建一个go服务器和一个html页面,注意修改自己的路径

这里需要准备一个服务器代码和一个html页面,这里直接提供给大家。
初学go!可能有漏洞,欢迎大家指正!

server.go

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

var count int = 0

func main() {
	// 注册处理函数
	http.HandleFunc("/data", askForGpt)
	http.HandleFunc("/", helloHandler)
	// 启动服务器,监听在指定的端口
	port := 8080
	fmt.Printf("Server started at :%d\n", port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	if err != nil {
		fmt.Println("Error:0", err)
	}
}

const (
	// 这里大家改成自己html页面的路径即可
	HTML_PATH = "C:\\Users\\45191\\Desktop\\test8.html"
)

// 这个函数调用后会向前端返回一个html页面
func helloHandler(w http.ResponseWriter, r *http.Request) {
	countUsers()
	responseHtmlContent := readHtmlFileAll(HTML_PATH)
	// 向客户端发送响应
	fmt.Fprint(w, responseHtmlContent)
}

// 服务器读取本地html页面
func readHtmlFileAll(path string) string {
	// 打开文件
	file, err := os.Open(path)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return "ERROR"
	}
	defer file.Close()
	// 读取文件内容
	content, err := io.ReadAll(file)
	if err != nil {
		fmt.Println("Error:", err)
		return "Error"
	}
	return string(content)
}

// 简单地统计一下多少个用户访问
func countUsers() {
	count++
	fmt.Printf("第%d个用户访问服务器!\n", count)
}

// 这个函数负责从前端发来的请求中解析出用户要问gpt的问题,然后执行python脚本,
// 将答案返回
func askForGpt(w http.ResponseWriter, r *http.Request) {
	countUsers()
	// 读取 POST 请求的内容
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Error reading request body", http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()
	question := parseJson(body)
	answer := execPythonScript(question)
	answerJsonObject := ResponseData{
		Answer: answer,
	}
	data, err := json.Marshal(answerJsonObject)
	fmt.Fprint(w, string(data))
}

// 定义一个接受json数据的内容,用来接受前端发来的json格式数据
type RequestData struct {
	Content string `json:"content"`
}

// 定义一个reponse数据
type ResponseData struct {
	Answer string `json:"answer"`
}

// 解析json格式数据
func parseJson(body []byte) string {
	var requestData RequestData
	err := json.Unmarshal(body, &requestData)
	if err != nil {
		fmt.Printf("Error parseJson: %v", err)
	}
	fmt.Printf("requestData: %v\n", requestData)
	return requestData.Content
}

const (
	// 这里大家改成自己的python脚本路径即可
	SCRIPT_PATH = "D:\\pyprojects\\MachineLearning\\testGPT2.py"
)

// 运行python脚本的函数
func execPythonScript(question string) string {
	// 定义要运行的Python参数
	scriptArgs := []string{question}
	// 创建一个Command对象运行Python脚本
	cmd := exec.Command("python", append([]string{SCRIPT_PATH}, scriptArgs...)...)
	fmt.Println(cmd)
	var stdout, stderr bytes.Buffer
	// 设置命令的输出和错误输出
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	// 执行命令等待完成
	err := cmd.Run()
	if err != nil {
		fmt.Println("Error running Python script: ", err)
		return ""
	}
	fmt.Println("Python script completed")
	// 从python脚本的输出中获得答案
	rawString := strings.TrimSpace(stdout.String())[2:]
	rawString = rawString[:len(rawString)-1]
	s := strings.ReplaceAll(rawString, "\\n", "\n")
	return s
}

test8.html

偷偷告诉大家这个代码大部分是我叫gpt帮我写的,哈哈哈哈,毕竟搞不来前端,我只是改了少量内容,比如js部分代码。

<!DOCTYPE html>
<html>
<head>
    <title>精美左右布局页面</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            background-color: #f5f5f5;
        }
        .container {
            display: flex;
            justify-content: space-between;
            align-items: stretch;
            height: 100vh;
            background-color: #fff;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            border-radius: 5px;
            margin: 20px auto;
            overflow: hidden;
        }
        .left {
            width: 40%;
            padding: 20px;
            padding-right: 30px;
            background-color: #f8f8f8;
        }
        .right {
            flex: 1;
            padding: 20px;
            padding-left: 30px;
            background-color: #fff;
            overflow: auto;
            border-left: 1px solid #ddd;
        }
        h1, h2 {
            margin: 0 0 10px;
            color: #333;
        }
        input[type="text"] {
            width: 100%;
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 5px;
            font-size: 16px;
            box-sizing: border-box;
        }
        button {
            padding: 10px 20px;
            background-color: #007bff;
            color: #fff;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
        pre {
            white-space: pre-wrap;
            word-wrap: break-word;
        }
        .history-icon {
            font-size: 20px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="left">
            <h2>输入问题:</h2>
            <input type="text" id="question" name="question">
            <button onclick="getAnswer()">获取答案</button>

            <details>
                <summary>
                    <span class="history-icon">&#x1F4DD;</span> 查看历史问题
                </summary>
                <ul>
                    <li>历史问题 1</li>
                    <li>历史问题 2</li>
                    <li>历史问题 3</li>
                    <!-- 在这里添加更多历史问题 -->
                </ul>
            </details>
        </div>
        <div class="right">
            <h2>答案:</h2>
            <pre id="answer"></pre>
        </div>
    </div>

    <script>
        function getAnswer() {
            // 获取输入框内容
            var questionInput = document.getElementById("question");
            var answerPre = document.getElementById("answer");

            // 构造 POST 请求的数据
            var data = {
                content: questionInput.value
            };
            // 发送 POST 请求
            fetch("http://localhost:8080/data", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify(data)
            })
            .then(response => response.json())
            .then(data => {
                // 将 UTF-8 编码的字节序列转换为 Uint8Array
                var byteString = data.answer
                var decodedString = decodeUtf8(byteString)
                answerPre.textContent = decodedString
            })
            .catch(error => {
            });
        }
        
		// 自己写的utf-8格式转文本函数
        function decodeUtf8(byteString){
            var resStr = ""
            var subStr = ""
            var count = 0
            for (var i = 0;i<byteString.length-1;){
                if (byteString[i] == '\\' && byteString[i+1] == 'x'){
                    subStr += byteString.substr(i+2,2)
                    i = i + 4
                    count ++
                    if (count == 3){
                        resStr += hexToUtf8String(subStr)
                        subStr = ""
                        count = 0
                    }

                } else {
                    resStr += byteString[i]
                    i ++
                }   
            }
            return resStr
        }
        function hexToUtf8String(hex) {
            const bytes = [];
            for (let i = 0; i < hex.length; i += 2) {
                bytes.push(parseInt(hex.substr(i, 2), 16));
            }
            return new TextDecoder().decode(new Uint8Array(bytes));
        }
        
    </script>
</body>
</html>

三、总结与效果展示

通过以上步骤,就已经实现了本题目的简单的要求,下面进行效果展示。

前端页面输出,访问127.0.0.1:8080

在这里插入图片描述

后台服务器输出

在这里插入图片描述

后续,我将继续学习,继续完善加强这个小项目,添加更多功能,欢迎关注!!!如果有什么问题,欢迎在评论区发表,一起学习,一起纠错!!!

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

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

相关文章

纹波和噪声测试知识

随着开关频率和开关速度不断的提升&#xff0c;在使用开关型的DC/DC电源的时候&#xff0c;要特别关注输入输出电源的纹波。但是测量DC/DC电源的纹波和噪声没有一个行业标准。不同厂家的测试环境以及测试标准都不太一样&#xff0c;导致很多人很迷惑。这篇文章提供了一个简单可…

为什么网络互联地址设置为30位地址

对于点对点链路&#xff0c;为了节约IPv4地址&#xff0c;一般为其分配/30地址块&#xff0c;这样包含4个地址&#xff1a;最小地址作为网络地址&#xff0c;最大地址作为广播地址&#xff0c;剩余两个可分配地址&#xff0c;分配给链路两端的接口&#xff0c;这是最普遍的方法…

stm32之14.超声波测距代码

-------------------- 源码 void sr04_init(void) { GPIO_InitTypeDef GPIO_InitStructure; //打开端口B的硬件时钟&#xff0c;就是供电 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE); //打开端口E的硬件时钟&#xff0c;就是供电 RC…

Electron学习3 使用serialport操作串口

Electron学习3 使用serialport操作串口 一、准备工作二、 SerialPort 介绍1. 核心软件包(1) serialport(2) serialport/stream(3) serialport/bindings-cpp(4) serialport/binding-mock(5) serialport/bindings-interface 2. 解析器包3. 命令行工具 三、创建一个demo程序1. 创建…

自平衡性:保持数据结构稳定的关键

自平衡性是一种重要的数据结构属性&#xff0c;它确保在执行插入、删除等操作后&#xff0c;数据结构能够自动进行调整&#xff0c;以保持整体的平衡状态。平衡的数据结构可以提供更快的操作性能&#xff0c;避免极端情况下的低效操作&#xff0c;同时保持树或其他结构的整体稳…

【C语言】动态内存管理(malloc,free,calloc,realloc)-- 详解

一、动态内存分配 定义&#xff1a;动态内存分配 (Dynamic Memory Allocation) 就是指在程序执行的过程中&#xff0c;动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样&#xff0c;需要预先分配存储空间&#xff0c;而是由系统根据程…

JVM——类加载与字节码技术—字节码指令

2.字节码指令 2.1 入门 jvm的解释器可以识别平台无关的字节码指令&#xff0c;解释为机器码执行。 2a b7 00 01 b1 this . init&#xff08;&#xff09; return 准备了System.out对象&#xff0c;准备了参数“hello world”,准备了对象的方法println(String)V&#xff…

车载充电器日本PSE认证申请资料和流程

PSE认证是日本针对电气用品的一个强制性安全认证。 日本的采购商在购进商品后一个月内必须向日本经济产业省&#xff08;METI&#xff09;注册申报。在日本市场上销售的DENAN目录范围内的电气电子产品都必须通过PSE认证。日本DENAN将电气电子产品分为两类&#xff1a;特定电气…

CSS background 背景

background属性为元素添加背景效果。 它是以下属性的简写&#xff0c;按顺序为&#xff1a; background-colorbackground-imagebackground-repeatbackground-attachmentbackground-position 以下所有示例中的花花.jpg图片的大小是4848。 1 background-color background-col…

【面试专题】Spring篇①

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;Java面试专题 目录 1.你知道 Spring 框架中有哪些重要的模块吗&#xff1f; 2. 谈谈你对 IOC 的认识。 3. 谈谈你对 AOP 的认识。 4.在实际写代码时&#xff0c;有没有用到过 AOP&#xff1f;用…

数字化技术无限延伸,VR全景点亮智慧生活

随着互联网的发展&#xff0c;我们无时无刻不再享受着互联网给我们带来的便利&#xff0c;数字化生活正在无限延伸&#xff0c;各行各业也开始积极布局智能生活。要说智慧生活哪个方面应用的比较多&#xff0c;那应该就是VR全景了&#xff0c;目前VR全景已经被各个行业广泛应用…

PyPI 如何在本地配置访问不同的仓库地址

PyPI 是可以在本地计算机上进行配置来访问远程的仓库地址的。 检查配置文件 检查配置文件使用的命令为&#xff1a; pip config -v list 通过上面的配置文件&#xff0c;我们可以知道 Python 的 PyPI 的配置文件信息。 上面图片显示的是配置文件的扫描路径。 修改 pip.ini…

flink checkpoint时exact-one模式和atleastone模式的区别

背景&#xff1a; flink在开启checkpoint的时候有两种模式可以选择&#xff0c;exact-one和atleastone模式&#xff0c;那么这两种模式有什么区别呢&#xff1f; exact-one和atleastone模式的区别 先说结论&#xff1a;exact-one可以完全做到状态的一致性&#xff0c;而atle…

gorm中正确的使用json数据类型

一、说明 1、JSON 数据类型是 MySQL 5.7.8 开始支持的。在此之前&#xff0c;只能通过字符类型&#xff08;CHAR&#xff0c;VARCHAR 或 TEXT &#xff09;来保存 JSON 文档。现实中也很多人不会采用json的存储方式&#xff0c;直接定义一个字符类型,让前端转换传递进来,返回给…

Spring Boot多环境指定yml或者properties

Spring Boot多环境指定yml或者properties 文章目录 Spring Boot多环境指定yml或者properties加载顺序配置指定某个yml 加载顺序 ● application-local.properties ● application.properties ● application-local.yml ● application.yml application.propertes server.port…

2023前端面试笔记 —— CSS3

系列文章目录 内容链接2023前端面试笔记HTML52023前端面试笔记CSS3 文章目录 系列文章目录前言一、CSS选择器的优先级二、通过 CSS 的哪些方式可以实现隐藏页面上的元素三、px、em、rem之间有什么区别&#xff1f;四、让元素水平居中的方法有哪些五、在 CSS 中有哪些定位方式六…

什么是遗传算法(Genetic Algorithm,简称 GA)?

目录 一、遗传算法介绍二、遗传算法应用场景三、遗传算法具体案列1、求解旅行商问题&#xff08;TSP 问题&#xff09;2、求解一个矩阵中的最大值3、基于遗传算法的图像压缩方法 四、遗传算法重要意义五、生物进化与遗传算法之间的关系 一、遗传算法介绍 遗传算法&#xff08;…

Sketch 98 中文版-mac矢量绘图设计

Sketch是一款专为Mac操作系统设计的矢量图形编辑软件&#xff0c;被广泛应用于UI/UX设计、网页设计、移动应用设计等领域。Sketch提供了各种工具和功能&#xff0c;包括绘图、图形设计、排版等&#xff0c;可以帮助设计师轻松地创建高质量的矢量图形和模型。Sketch的主要特点包…

自动气象站丨自动观测和记录气象信息

所谓自动气象站&#xff0c;是指无需人工就能自动检测多种气象要素&#xff0c;并将数据实时发送给观测中心的气设备。它是填补小区域气象探测数据空白的重要手段。自动气象站主要由传感器、数据采集器、太阳能供电系统、立杆组成&#xff0c;可以监测风速、风向、降雨量、空气…

[MyBatis系列①]增删改查

目录 1、基础回顾 2、例子引入 3、映射文件 4、增删改查 4.1、add 4.2、delete 4.3、update 4.4、check 5、核心配置文件 5.1、properties 5.2、typeAliases 5.2.1、自定义 5.2.2、⭐MyBatis自带 5.3、environments 5.3.1、environment 5.3.2、transactionMana…