Langchain-Chatchat+Qwen实现本地知识库(二)

发布时间 2023-12-27 14:35:35作者: HaruSuzumiya

在上一篇文章当中,我们已经通过Langchain-Chatchat+Qwen-14b-int4完成了本地知识库的搭建,现在我们通过vue和api接口来做定制的聊天页面,达成下图类似gpt或者其他大模型的显示效果:

image.png

1.知识库启动:

见上一篇文章

2.api接口部署:

在/Langchain-Chatchat/configs的serve_config文件中可以修改api的相关端口

API_SERVER = {
    "host": DEFAULT_BIND_HOST,
    "port": 6006,
}

# fastchat openai_api server
FSCHAT_OPENAI_API = {
    "host": DEFAULT_BIND_HOST,
    "port": 20000,
}

访问端口会跳转到接口的文档页面 image.png 可以看到相关接口的路径和参数,还可以点击try在线调试,可谓非常的人性化了。我们要用的接口为/chat/knowledge_base_chat(知识库问答)

3.前端页面:

分析下类似的聊天页面,大致分为聊天消息列表和聊天输入框两部分,先完成消息列表:

<div class="chatBox" ref="chatBox">
  <template v-for="(item,index) in msglist">
    <div class="chatUser" v-if="item.role=='user'">
      <div class="chatContent">
        <!-- 用户头像 -->
        <svg>
        </svg>
        <div class="chatContent-content">
          {{item.content}}
        </div>
      </div>
    </div>
    <div class="chatAssistant" v-if="item.role=='assistant'">
      <div class="chatContent">
        <!-- 机器人头像 -->
        <svg>
        </svg>
        <div class="chatContent-content">
          {{item.content}}
        </div>
      </div>
    </div>
  </template>
</div>

聊天输入框:

<div class="chatArea">
  <el-input ref="inputDiv" class="chatpdfArea-textarea" type="textarea" autosize placeholder="请输入对话内容,按 Ctrl+Enter 换行"
    v-model="askContent" :rows="1" @keydown.enter.native="messageSendlisten($event)">
  </el-input>
  <svg>
  </svg>
</div>

Enter直接将输入框文字插入到消息列表并发送,Ctrl+Enter 换行:

messageSendlisten(event) {
  if (event.keyCode == 13) {
    if (!event.ctrlKey) {
      event.preventDefault();
      this.msglist.push({
        role: 'user',
        content: this.askContent
      })
      this.askContent=''
      this.sendMsg();
    } else {
      let dIndex = event.srcElement.selectionStart // 光标所在位置
      this.askContent = this.askContent.slice(0, dIndex) + '\n' + this.askContent.slice(dIndex)
      this.$nextTick(() => { // 设置光标位置
        event.srcElement.selectionStart = dIndex + 1
        event.srcElement.selectionEnd = dIndex + 1
      })
    }
  }
},

直接请求api,需要等待一段时间,然后一下子更新一大段对话,这样的体验并不好,可以通过stream的方式访问api,此时发现回复的数据格式如下:

image.png 实现逐字回答的效果:

 // 节流略
sendMsg() {
  // 构建请求参数
  let that = this
  let params = {
    knowledge_base_name: "", //知识库名称
    top_k: 3,
    score_threshold: 0.8,
    stream: true,
    local_doc_url: false,
    model_name: "Qwen-14B-Chat-Int4",
    prompt_name: "default",
    temperature: 0.7,
    query: this.msglist[this.msglist.length - 1].content,
    histort: [] //消息历史,取消息列表最后n轮
  };
  that.msglist.push({
    role: 'assistant',
    content: '思考中...'
  })
  axios({
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
      'responseType': 'stream'
    },
    data: params,
    url: apiUrl + '/chat/knowledge_base_chat',
    onDownloadProgress: function (event) {
      if (that.msglist[that.msglist.length - 1].content == '思考中...') {
        that.msglist[that.msglist.length - 1].content = '';
      }
      let chunk = event.target.responseText
      try {
        /* 序列化返回的内容 */
        let chunkStr = chunk.replace(/}{/g, "},####,{");
        let answerArr = chunkStr.split(',####,')
        let answerStr = '';
        answerArr.forEach(function (item) {
          if (item.indexOf("docs") < 0) {
            let itemObj = JSON.parse(item)
            answerstr = answerstr + itemObj.answer
          }
        });
        that.msglist[that.msglist.length - 1].content = answerstr
        that.goHeight();
      } catch (e) { console.log(e) }
    }
  })
}

消息多了,发现显示的还是消息最上面一行,根本看不到后面的内容,需要给消息列表设置滚动:

.chatBox{
  flex: 1;
  margin: 0 auto;
  overflow-y: auto; 
  height:800px;
 }

自动滚动到回答内容的底部:

// 滚动区域初始高度
init() {
  this.$nextTick(() => {
    this.scrollHeight = this.$refs.chatBox.scrollHeight;
  })
},
//消息列表滚动到底部
goHeight() {
  this.$nextTick(() => {
    if (this.$refs.chatContent.scrollHeight > this.scrollHeight) {
      this.scrollHeight = this.$refs.chatBox.scrollHeight
      this.$refs.chatBox.scrollTop = this.$refs.chatBox.scrollHeight;
    }
  })
}

优化下样式,收工~