Langchain-Chatchat 结合 Qwen 实现本地知识库前端开发
在上一篇文章当中,我们已经通过 Langchain-Chatchat+Qwen-14b-int4 完成了本地知识库的搭建。本文重点介绍如何通过 Vue 和 API 接口来定制聊天页面,达成类似 GPT 或其他大模型的显示效果。

1. 知识库启动与配置
确保后端服务已正常启动。在 /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,
}
访问对应端口会跳转到接口的文档页面,可以看到相关接口的路径和参数,支持在线调试。我们要用的核心接口为 /chat/knowledge_base_chat(知识库问答)。
2. 前端页面实现
分析类似的聊天页面,大致分为聊天消息列表和聊天输入框两部分。
2.1 消息列表组件
先完成消息列表的渲染逻辑:
<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>
2.2 聊天输入框
实现输入框及回车发送逻辑:
<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;
});
}
}
},
2.3 流式响应处理
直接请求 API 需要等待一段时间,然后一下子更新一大段对话,体验不佳。可以通过 stream 方式访问 API,实现逐字回答的效果。
此时发现回复的数据格式如下:

实现流式请求的代码如下:
// 节流略
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,
history: [] // 消息历史,取消息列表最后 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); }
}
});
}
2.4 样式优化与滚动
消息多了,发现显示的还是消息最上面一行,根本看不到后面的内容,需要给消息列表设置滚动:
.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;
}
});
}
3. 项目运行与测试
完成代码编写后,建议进行以下检查以确保功能正常:
- 依赖安装:确保
node_modules已完整安装,特别是axios和element-ui。 - CORS 配置:如果前后端分离部署,需在后端配置允许跨域访问,避免浏览器拦截请求。
- 端口一致性:确认前端配置的
apiUrl与后端serve_config中的端口一致。 - 流式解析:观察控制台输出,确保
onDownloadProgress能正确接收数据块,且JSON.parse无报错。
优化样式后,即可启动前端服务进行测试。至此,一个基于 Langchain-Chatchat 和 Qwen 的本地知识库对话界面开发完成。
