• 微信小程序| 打造ChatGPT英语四六级背单词小程序


    请添加图片描述

    一、需求背景

    学英语,最大的痛苦莫过于背单词!

    不知道你平时都是用什么方式在背单词呢?硬啃单词书?字典?还是说各类的背单词APP来回跳转?

    不可否认的是,单词一两遍完全记不住,没有个三四五六七八遍,都很难在考场的卷子上认出他!

    所以,这次我们来做一个通关英语四六级的背单词神器,让他能够基于艾宾浩斯遗忘规律来辅助我们高效背单词。

    这个项目也是AIGC的一个方向性的探索,我们基于LLM的能力,进一部封装。相比于专栏中的前几个项目是直接基于OpenAI接口的直接生成,这次我们需要对模型进行数据的填喂。同时制定好相应的语料生成规则,让他能够根据我们的语料信息生成到我们的答案内容。

    所以在本项目中,我们使用到的技术包括:语料Embedding技术Prompt微调技术、``Fine Tune技术,正是通过这三种技术能力的加持,由此颠覆以往的机械式背单词!打造一款更加完美的背单词程序!


    二、项目原理及架构

    2.1 实现原理

    在这里插入图片描述

    (1)语料的准备

    想要有好的单词记忆效果,我们的单词语料必须准备得足够完整!这里我们选用了GitHUB中开源词典数据项目。其中包含了四六级、考研、雅思托福等。这里我们主要针对英语四六级准备,所以这里使用四六级的词汇书就足够了!将其下载,然后导入到数据库中。
    在这里插入图片描述

    (2)制定LLM模型背单词规则

    背过单词的都知道,人的记忆功能都是符合记忆曲线的,同样地我们冶可以将这种记忆规则迁移到我们程序中!其中最重要的就是构建好相应的Prompt来满足单词记忆的规则效果!

    • 先对LLM进行记忆规则的自定义!

    请添加图片描述


    • 再对LLM输入背单词进行词义混淆内容的设定!

    请添加图片描述

    (3)记忆已经学会的单词

    为了让程序记住我们已经背诵过了的单词,就要让LLM具备记忆功能。在传统的web应用开发过程中,说到数据存储,第一时间我们考虑得更多得除了关系型数据库以及中间件缓存数据库!这是由于业务得特性所决定得!我们在web应用中,数据是规整的!是结构化的。所以用传统的关系型数据库完全可以满足系统的检索需求!

    但是,这次我们面对的是大语言模型,而且我们的数据是完全基于文本语料的,可以说不是完全地结构化以及关系型的数据!这个时候我们结合时下大火的向量数据库Milvus来进行数据的存储和检索!也就是传说中的Embedding技术该技术的核心原理:将文本内容通过数学运算,将其转化为向量。然后基于向量间的相似度运算来进行内容的匹配!从而检索出当前内容的最高相似度数据

    Milvus 是一款开源的特征向量相似度搜索引擎,具有使用方便、实用可靠、易于扩展、稳定高效和搜索迅速等特点,在全球范围内被上百家组织和机构所采用。Milvus 已经被广泛应用于多个领域,其中包括图像处理、机器视觉、自然语言处理、语音识别、推荐系统以及新药发现等。

    Milvus学习教程大全:

    在这里插入图片描述


    2.2 技术架构

    在这里插入图片描述


    2.3 技术栈

    模块语言及框架涉及的技术要点
    小程序前端基于VUE 2.0语法+Uni-app跨平台开发框架Http接口通信、Flex布局方式、uView样式库的使用、JSON数据解析、定时器的使用
    小程序接口服务端Python + Flask WEB框架rest接口的开发、 ChatGPT API接口的数据对接 、 前后端websocket实时通信

    2.4 数据交互原理

    选择操作模式
    数据交互
    构造问题数据发送
    通过API接口返回回答数据
    用户
    小程序
    小程序后端服务
    ChatGPT
    Milvus向量数据库

    三、项目功能的实现

    3.1 ChatGPT API的接入

    要接入ChatGPT API,需要按照以下步骤进行操作:

    1. 注册一个账号并登录到OpenAI的官网:https://openai.com/
    2. 在Dashboard页面上,创建一个API密钥。在“API Keys”选项卡下,点击“Generate New Key”按钮。将生成的密钥保存好,以备后续使用。
    3. 选择所需的API服务,例如“Completion” API,以使用OpenAI的文本生成功能。

    在这里插入图片描述
    使用Python调用ChatGPT API实现代码如下:

    • 方法一:使用request
    import requests
    import json
    
    # 构建API请求
    url = "https://api.openai.com/v1/engines/davinci-codex/completions"
    headers = {"Content-Type": "application/json",
               "Authorization": "Bearer YOUR_API_KEY"}
    data = {
        "prompt": "Hello, my name is",
        "max_tokens": 5
    }
    
    # 发送API请求
    response = requests.post(url, headers=headers, data=json.dumps(data))
    
    # 解析API响应
    response_data = json.loads(response.text)
    generated_text = response_data["choices"][0]["text"]
    
    print(generated_text)
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 方式二:使用openAI库
    from flask import Flask, request
    import openai
    
    app = Flask(__name__)
    
    openai.api_key = "YOUR_API_KEY_HERE"
    
    @app.route("/")
    def home():
        return "Hello, World!"
    
    @app.route("/chat", methods=["POST"])
    def chat():
        data = request.json
        response = openai.Completion.create(
            engine="davinci",
            prompt=data["message"],
            max_tokens=60
        )
        return response.choices[0].text
    
    if __name__ == "__main__":
        app.run()
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.2 小程序端设计与实现

    首页学习页
    在这里插入图片描述在这里插入图片描述
    学习统计页
    在这里插入图片描述

    3.3 Milvus数据库的接入

    • JAVA SDK工具类接入
    • FLAT:FLAT适用于希望在小规模、百万级数据集上获得完全准确和精确搜索结果的场景。
    • IVF_FLAT:IVF_FLAT是一种基于量化的索引,适用于追求查询准确度和速度之间的理想平衡的场景。
    • IVF_SQ8:IVF_SQ8是一种基于量化的索引,适用于那些资源非常有限且需要显著减少磁盘、CPU和GPU内存消耗的场景。
    • IVF_PQ:IVF_PQ是一种基于量化的索引,适用于那些即使付出准确度代价也要追求高查询速度的场景。
    • HNSW:HNSW是一种基于图的索引,适用于对搜索效率有高要求的场景。
    • ANNOY:ANNOY是一种基于树的索引,适用于追求高召回率的场景。
    • RAFT: 基于GPU的一种索引

    在这里插入图片描述


    3.4 小程序单词学习功能实现

    
    
    <view class="bgWrapper bg-linear-{{colorType}}">view>
    
    <view class="topline" wx:if="{{!learnDone}}">
        <view class="progress">{{learnedNum}} / {{learnNum}}view>
    view>
    
    <view class="wordWrapper" bindtap="playVoice" wx:if="{{!learnDone}}">
        <view class="word word-color-{{colorType}}" wx:if="{{ wordMode == 0 }}">{{wordDetail.word}}view>
        <view class="repeatTime" wx:if="{{ wordMode == 0 }}">
            <view class="times first {{ thisWordRepeatTime>=1? 'bg-color-light-'+colorType : 'bg' }}"
                wx:if="{{ repeatTimes>1 }}">view>
            <view class="times {{ thisWordRepeatTime>=2? 'bg-color-light-'+colorType : 'bg' }}"
                wx:if="{{ repeatTimes>=2 }}">view>
            <view class="times {{ thisWordRepeatTime>=3? 'bg-color-light-'+colorType : 'bg' }}"
                wx:if="{{ repeatTimes>=3 }}">view>
            <view class="times {{ thisWordRepeatTime==4? 'bg-color-light-'+colorType : 'bg' }}"
                wx:if="{{ repeatTimes==4 }}">view>
        view>
        <view class="pron" wx:if="{{ wordMode == 0 && wordDetail.phonetic.length > 0 }}">/ {{wordDetail.phonetic}} / <text
                class="voice iconfont icon-sound">text>view>
        <view class="timing" wx:if="{{ wordMode == 1 }}" catchtap="{{ isBtnActive? 'showWord':'' }}">
            <mpProgress class="progress" config="{{wordTimingConfig}}" reset="{{wordTimingReset}}"
                isStop="{{wordTimingStop}}" percentage="{{100}}" bindtimingOut="timingOut" data-type="word">mpProgress>
            
        view>
        <view class="timing" wx:if="{{ wordMode == 2 }}">
            <view class="model">view>
            <view class="model phonetic">view>
        view>
    view>
    
    <view class="content" wx:if="{{!learnDone}}">
        <view class="chooseCorrect" wx:if="{{ contentMode == 0 }}">
            <view class="choice {{choiceBgList[index]}}" wx:for="{{choiceOrder}}" wx:key="index" hover-class="wasTaped"
                data-index="{{index}}" bindtap="{{ isBtnActive? 'checkChoice':'' }}">
                <view class="pos">{{wrongTransWordList[item].translation.pos}}view>
                <view class="meaning"> {{wrongTransWordList[item].translation.meaning}}view>
            view>
        view>
        <view class="translationWrapper" wx:if="{{ contentMode == 1 }}">
            <view class="transRow" wx:for="{{wordDetail.shortTrans}}" wx:key="index">
                <view class="pos" wx:if="{{ item.pos != '' }}">{{item.pos}}view>
                <view class="meaning {{ item.more? 'moreBtn':'' }}" bindtap="{{ item.more? 'toDetail':'' }}"
                    hover-class="{{ item.more? 'tapedText':'' }}">{{item.meaning}}
                view>
            view>
        view>
        <view class="timing" wx:if="{{ contentMode == 2 }}" bindtap="{{ isBtnActive? 'showTrans':'' }}">
            <mpProgress class="progress" config="{{contentTimingConfig}}" reset="{{contentTimingReset}}"
                isStop="{{contentTimingStop}}" percentage="{{100}}" bindtimingOut="timingOut" data-type="content">
            mpProgress>
            
        view>
        <view class="timing" wx:if="{{ contentMode == 3 }}" bindtap="{{ isBtnActive? 'showTrans':'' }}">
            <view class="model">view>
            <view class="model">view>
            <view class="model">view>
        view>
    view>
    
    <view class="control" wx:if="{{!learnDone}}">
        <view class="btn answerBtn" wx:if="{{ controlMode == 0 }}" hover-class="wasTaped"
            bindtap="{{ isBtnActive? 'showAnswer':'' }}">
            <view class="text">答案view>
            <view class="decorate">view>
        view>
        <view class="knowWrapper" wx:if="{{ controlMode == 1 }}">
            <view class="btn knowBtn left" hover-class="wasTaped" bindtap="{{ isBtnActive? 'setAsKnown':'' }}">
                <view class="text word-color-{{colorType}}">认识view>
                <view class="decorate bg-color-light-{{colorType}}">view>
            view>
            <view class="btn knowBtn" hover-class="wasTaped" bindtap="{{ isBtnActive? 'setAsUnknown':'' }}">
                <view class="text notknowtext">不认识view>
                <view class="decorate dforNotKnow">view>
            view>
        view>
        <view class="knowWrapper" wx:if="{{ controlMode == 2 }}">
            <view class="btn knowBtn left" hover-class="wasTaped" bindtap="{{ isBtnActive? 'toNextWord':'' }}">
                <view class="text word-color-{{colorType}}">下一个view>
                <view class="decorate bg-color-light-{{colorType}}">view>
            view>
            <view class="btn knowBtn" hover-class="wasTaped" bindtap="{{ isBtnActive? 'changeToUnknown':'' }}">
                <view class="text notknowtext">记错了view>
                <view class="decorate dforNotKnow">view>
            view>
        view>
        <view class="btn nextBtn" wx:if="{{ controlMode == 3 }}" hover-class="wasTaped"
            bindtap="{{ isBtnActive? 'toNextWord':'' }}">
            <view class="text word-color-{{colorType}}">下一个view>
            <view class="decorate bg-color-light-{{colorType}}">view>
        view>
        <view class="bottomMenu">
            
            <view class="bottomBtn iconfont icon-getDetail" hover-class="wasTaped-bottom" bindtap="toDetail">view>
            <view class="bottomBtn iconfont icon-skip" hover-class="wasTaped-bottom" data-type="skip"
                bindtap="{{ isBtnActive? 'skip':'' }}">view>
            <view class="bottomBtn iconfont icon-learned" hover-class="wasTaped-bottom" data-type="master"
                bindtap="{{ isBtnActive? 'skip':'' }}">view>
            <view class="bottomBtn iconfont icon-addToNB-{{isInNotebook?'yes':'no'}}"
                hover-class="wasTaped-bottom{{isInNotebook?'1':''}}" bindtap="toggleAddToNB">view>
        view>
    view>
    
    <view class="doneWrapper" wx:if="{{learnDone}}">
        <view class="text word-color-{{colorType}}">本组单词学习已完成view>
        <view class="btn back" hover-class="wasTaped" bindtap="goBack">完成学习view>
        <view class="btn continue bg-color-light-{{colorType}}" hover-class="wasTaped" bindtap="reInit">继续学习view>
    view>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    四、推荐阅读

    🥇入门和进阶小程序开发,不可错误的精彩内容🥇 :

  • 相关阅读:
    窗口-视口转换(详细)
    Entity Framework Core 7批量数据插入实现高速化
    直线模组怎么分类?
    Windows server 2012R2安全加固
    流媒体技术基础-流媒体编码与协议
    解决 MyBatis 一对多查询中,出现每组元素只有一个,总组数与元素数总数相等的问题
    谷歌JAX快速入门笔记详解和案例
    transformer系列5---transformer显存占用分析
    Mysql :创建数据库和管理数据库
    台积电的战略布局:“曲线”抢单 | 百能云芯
  • 原文地址:https://blog.csdn.net/weixin_37797592/article/details/131454176