ai聊天问答知识库机器人源码,基于gpt实现的本地知识库问答实现,聊天对话效果,发送回复以及流式输出

发布时间 2023-04-25 16:46:37作者: 唯一客服系统开发笔记

现在基于gpt做自己项目的问答机器人,效果非常的好。可以把自己的文档上传上去,让机器人根据文档来进行回答。

想要实现智能AI问答功能,现在大部分都是基于向量数据库的形式。

整体的流程就是:上传文档===>openai向量接口 ====> 存入向量数据库

访客咨询:  咨询问题 ====> openai向量接口  ====>搜索向量数据库  ====> 组织prompt 到 openai的chat接口

 

下面的源码是前端逻辑,实现的界面以及问答的聊天对话效果,发送回复以及流式输出

 效果图的前端源码

<template>
    <div class="chatpdf">
        <div class="pannel">
            <div class="fileList">
                <div class="fileTitle" v-bind:class="{'active': collect==item.name}" @click="selectCollect(item.name)" v-for="(item,index) in collects">
                <svg t="1682317088056" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1403" width="16" height="16"><path d="M512 64c259.2 0 469.333333 200.576 469.333333 448s-210.133333 448-469.333333 448a484.48 484.48 0 0 1-232.725333-58.88l-116.394667 50.645333a42.666667 42.666667 0 0 1-58.517333-49.002666l29.76-125.013334C76.629333 703.402667 42.666667 611.477333 42.666667 512 42.666667 264.576 252.8 64 512 64z m0 64C287.488 128 106.666667 300.586667 106.666667 512c0 79.573333 25.557333 155.434667 72.554666 219.285333l5.525334 7.317334 18.709333 24.192-26.965333 113.237333 105.984-46.08 27.477333 15.018667C370.858667 878.229333 439.978667 896 512 896c224.512 0 405.333333-172.586667 405.333333-384S736.512 128 512 128z m-157.696 341.333333a42.666667 42.666667 0 1 1 0 85.333334 42.666667 42.666667 0 0 1 0-85.333334z m159.018667 0a42.666667 42.666667 0 1 1 0 85.333334 42.666667 42.666667 0 0 1 0-85.333334z m158.997333 0a42.666667 42.666667 0 1 1 0 85.333334 42.666667 42.666667 0 0 1 0-85.333334z" fill="#ffffff" p-id="1404"></path></svg>
                {{item.name}}</div>
            </div>
        </div>
        <div class="chatpdfBox">
            <div class="chatpdfLine">
                <div class="chatpdfLineScroll">
                    <h1>欢迎使用知识库AI</h1>
                    <h2>由 AI 支持的网页版 Copilot</h2>
                    <div class="chatpdfRow " v-bind:class="{'chatpdfAsk': item.type=='ask'}" v-for="(item,index) in msgList">
                        <div class="chatpdfContent" v-html="html(item.content)"></div>
                    </div>
                </div>
            </div>
            <div class="chatpdfArea">
                <button @click="clearHistory">
                    <svg t="1682398861245" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1371" width="20" height="20"><path d="M883.2 403.2l-147.2-44.8 57.6-224c0-6.4 0-19.2-6.4-25.6-6.4-6.4-12.8-12.8-19.2-12.8L627.2 57.6c-6.4 0-19.2 0-25.6 0C595.2 70.4 588.8 76.8 588.8 83.2L524.8 300.8 358.4 256c-6.4 0-19.2 0-25.6 0S320 275.2 320 281.6l-89.6 320C211.2 684.8 128 768 128 768c-6.4 6.4-12.8 19.2-6.4 32 0 12.8 12.8 19.2 25.6 25.6l524.8 140.8c0 0 6.4 0 6.4 0 6.4 0 19.2-6.4 25.6-12.8 6.4-6.4 83.2-89.6 115.2-179.2 32-83.2 89.6-326.4 89.6-332.8C908.8 422.4 896 409.6 883.2 403.2zM755.2 748.8c-25.6 57.6-70.4 115.2-89.6 147.2l-70.4-19.2c32-38.4 70.4-96 89.6-160 6.4-19.2-6.4-32-25.6-38.4-19.2-6.4-32 6.4-38.4 25.6-19.2 70.4-76.8 134.4-96 153.6l-57.6-12.8c32-38.4 70.4-96 83.2-153.6 6.4-19.2-6.4-32-25.6-38.4-19.2-6.4-32 6.4-38.4 25.6-19.2 64-70.4 128-89.6 153.6l-64-19.2c32-38.4 70.4-96 89.6-153.6 6.4-19.2-6.4-32-25.6-38.4C384 608 364.8 620.8 364.8 633.6c-19.2 64-70.4 128-96 153.6l-57.6-19.2c32-38.4 70.4-96 83.2-153.6l76.8-294.4 166.4 44.8c6.4 0 19.2 0 25.6 0C569.6 364.8 576 358.4 582.4 352L640 128l83.2 19.2-57.6 224c-6.4 19.2 6.4 32 19.2 38.4L832 454.4C819.2 524.8 780.8 691.2 755.2 748.8z" p-id="1372" fill="#ffffff"></path><path d="M364.8 473.6C364.8 492.8 371.2 505.6 390.4 512l339.2 96c0 0 6.4 0 6.4 0 12.8 0 25.6-6.4 32-25.6 6.4-19.2-6.4-32-19.2-38.4L409.6 448C390.4 448 371.2 454.4 364.8 473.6z" p-id="1373" fill="#ffffff"></path>
                    </svg>
                </button>
                <textarea @keydown.prevent.enter="sendAsk"  v-model="askContent"></textarea>
            </div>
        </div>
    </div>
</template>

<script>
    import MarkdownIt from 'markdown-it';
    import hljs from 'highlight.js';
    import 'highlight.js/styles/monokai-sublime.css';
    export default {
      name: 'ChatPage',
        data() {
          return {
              apiPost:"http://127.0.0.1:8083",
              collects:[],
              collect:"test",
            askContent:"",
            msgList:[
                {type:"ask",content:"自建私有数据知识库 · 与知识库AI聊天"},
                {type:"answer",content:"我是知识库机器人,一个专门响应人类指令的大模型"},
            ],
          }
        },
        methods: {
            sendAsk(){
                if(this.askContent=="") return;
                let msg={
                    'type':'ask',
                    'content':this.askContent,
                }
                this.msgList.push(msg);
                let data=JSON.stringify(this.msgList);
                localStorage.setItem("data_"+this.collect,data);
                this.scrollBottom();
                this.getReplyFromApi();
            },
            selectCollect(name){
                this.collect=name;
                this.msgList=[];
                this.getHistory();
            },
            getCollects(){
                let _this=this;
                fetch(this.apiPost+'/collects', {
                    method: 'get',
                })
                .then((response) => response.json())
                .then((response) => {
                    console.log(response);
                    _this.collects=response.result.collections;
                })
            },
            getReplyFromApi(){
                let _this=this;
                let msg={
                    'type':'answer',
                    'content':"正在为你生成答案...",
                }
                _this.msgList.push(msg);
                
                
                let i=0;
                var xhr = new XMLHttpRequest();
                xhr.open("GET", this.apiPost+"/"+this.collect+"/searchStream?keywords="+_this.askContent);
                xhr.setRequestHeader("Content-Type", "text/html");
                xhr.onprogress = function(event) {
                    console.log(i,event.currentTarget.responseText);
                    _this.msgList[_this.msgList.length-1].content=event.currentTarget.responseText;
                    _this.scrollBottom();
                };
                xhr.onreadystatechange = () => {
                  if (xhr.readyState === XMLHttpRequest.DONE) {
                    let data=JSON.stringify(this.msgList);
                    localStorage.setItem("data_"+this.collect,data);
                  }
                };
                xhr.send();
                this.askContent="";
            },
            getHistory(name){
                let str=localStorage.getItem("data_"+this.collect);
                if(!str) return;
                let data=JSON.parse(str);
                this.msgList=data;
            },
            //滚动到底部
            scrollBottom:function(){
                var _this=this;
                this.$nextTick(function(){
                    var container = _this.$el.querySelector(".chatpdfLine");
                    container.scrollTop = 999999999;
                });
            },
             html(sourceStr) {
              const md = new MarkdownIt({
                highlight: function (str, lang) {
                  if (lang && hljs.getLanguage(lang)) {
                    try {
                      return '<pre class="hljs"><code>' +
                             hljs.highlight(lang, str, true).value +
                             '</code></pre>';
                    } catch (__) {}
                  }
        
                  return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
                }
              });
        
              return md.render(sourceStr);
            },
            clearHistory(){
                localStorage.removeItem("data_"+this.collect);
                this.msgList=[];
            },
            getQuery(key) {
                // 获取所有参数
                var query = window.location.search.substring(1);
                var hash = window.location.hash.substring(1);
                // 如果锚点后面有参数,把锚点后面的参数加入到search参数中
                if(hash.indexOf("?") > -1){
                    query += "&" + hash.split("?")[1];
                }
                var key_values = query.split("&");
                var params = {};
                // 遍历参数并存入params对象
                key_values.map(function (key_val){
                    var key_val_arr = key_val.split("=");
                    params[key_val_arr[0]] = key_val_arr[1];
                });
                // 如果找到了key对应的参数,返回对应值
                if(typeof params[key]!="undefined"){
                    return params[key];
                }
                // 如果没找到,返回空字符串
                return "";
            }
        },
        mounted: function () {
            let collect=this.getQuery("collect");
            if(collect){
                this.collect=collect;
            }
            this.getCollects();
            this.getHistory();
        }
    }
</script>

<style>
    .chatpdf{
        display: flex;
        height: 100vh;
        flex-direction: row;
    }
    .chatpdf .pannel{
        width: 255px;
        background-color: rgb(0, 21, 41);
        display: none;
    }
    .chatpdfBox{
        display: flex;
        flex-direction: column;
        flex: 1;
        background: linear-gradient(to bottom right,#dbe6fb, #f3f4f8);
        background-size: cover;
        background-attachment: fixed;
    }
    .chatpdfHeader{
        font-size: 18px;
        padding: 10px;
        text-align: center;
        width: 100%;
    }
    .chatpdfLine{
        flex: 1;
        width: 100%;
        overflow-y: auto;
    }
    .chatpdfLine h1{
        color: #111111;
        text-align: center;
        margin-top: 80px;
        margin-bottom: 20px;
        font-size: 36px;
    }
    .chatpdfLine h2{
        color: #1e1e1e;
        text-align: center;
        font-size: 20px;
        font-weight: 400;
    }
    .chatpdfLineScroll{
        max-width: 1000px;
        margin: 0 auto;
    }
    .chatpdfRow{
        margin: 20px 10px;
        display: flex;
    }
    .chatpdfAsk{
        justify-content: flex-end;
    }
    .chatpdfContent{
        line-height: 23px;
        display: inline-block;
        border-radius: 8px;
        padding: 12px 15px;
        max-width: 700px;
        background: rgba(255, 255, 255, 0.6);
        font-size: 14px;
        box-shadow:  0px 0.3px 0.9px rgba(0, 0, 0, 0.12), 0px 1.6px 3.6px rgba(0, 0, 0, 0.16);
    }
    .chatpdfAsk .chatpdfContent{
        background: linear-gradient(90deg, #2870EA 10.79%, #1B4AEF 87.08%);;
        color: #fff;
    }
    .chatpdfContent pre{
        padding: 10px;
    }
    .chatpdfArea{
        display: flex;
        margin-bottom: 10px;
        max-width: 1000px;
        margin: 0 auto;
        width: 98%;
        margin-bottom: 15px;
        transition: all 0.3s,height 0s;
        
    }
    .chatpdfArea textarea{
        flex: 1;
        border: none;
        resize: none;
        outline: none;
        padding: 0px 5px;
        height: 40px;
        line-height: 35px;
        color: #404040;
        border-radius: 10px;
        box-shadow: 0px 0.3px 0.9px rgba(0, 0, 0, 0.12), 0px 1.6px 3.6px rgba(0, 0, 0, 0.08);
    }
    .chatpdfArea:hover{
        border-color: #4096ff;
    }
    .chatpdfArea button{
        height: 40px;
        color: #fff;
        background: linear-gradient(90deg, #1B4AEF 10.79%, #2870EA 87.08%);
        box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1);
        border: none;
        padding: 0 20px;
        border-radius: 15px;
        cursor: pointer;
        box-shadow: 0px 0.3px 0.9px rgba(0, 0, 0, 0.12), 0px 1.6px 3.6px rgba(0, 0, 0, 0.08);
        margin-right: 10px;
    }
    .chatpdfArea button:hover{
        background-color: #388aff;
    }
    .chatpdf .fileTitle{
        background-color: #1677ff;
        color: #fff;
        border-radius: 8px;
        padding: 10px;
        margin: 10px;
        font-size: 14px;
        cursor: pointer;
        display: flex;
    }
    .chatpdf .fileTitle svg{
        margin-right: 5px;
    }
    .chatpdf .fileTitle.active{
        background-color: #66a6ff;
    }
    @media (max-width: 768px) {
        .pannel{
            display: none;
        }
    }
</style>