【实战】 Vue 3、Anything LLM + DeepSeek本地化项目(五)

本期实现目标

接上期《【实战】 Vue 3、Anything LLM + DeepSeek本地化项目(四)》预期目标本期完成了 知识库内容维护且达到模型的聊天信息可以从知识库中获取的最终效果

知识库内容的维护

知识库添加支持文件、链接和文字3种方式录入(知识库图1)

模型在知识库补充前后回答的内容对比

在没有给知识库提供最近很火的《长安的荔枝》电视剧信息前,模型的所有回答基本都是基于2023/06/19的信息进行的作答。

将豆瓣长安的荔枝 (2025)影评信息作为知识库内容添加到对应的聊天进程中后得到的结果将存在最新的信息,从思考过程中也可以看到模型有从知识库获取信息,最终模型也成功的把添加的豆瓣信息输出了出来。

Anything LLM 知识库

介绍

Anything LLM 知识库是一种基于大语言模型(LLM)构建的本地知识库

特点

  • 数据本地化:知识数据存储在本地,如文档、资料等上传后在本地进行存储、分切、向量嵌入等操作,数据安全可控,不会上传到第三方平台。
  • 模型运行本地化:通过工具如Ollama等可将模型部署在本地设备,依赖本地算力运行,保障了数据的私密性和安全性。
  • 应用程序本地化:本身是可安装在本地电脑上的全栈应用程序,有本地用户界面和交互环境,用户可直接在本地进行知识库的创建、管理、查询等操作,无需联网访问远程界面。
  • 多模态数据支持:能够将各种类型的文档、网页链接、音视频文件以及文字片段等转化为LLM可理解的上下文信息,为知识库提供丰富的内容来源。
  • 灵活的模型与数据库选择:用户可自由选择不同的LLM,如与llama.cpp兼容的开源模型、OpenAI、Google Gemini Pro等,以及多种向量数据库,如LanceDB、Astra DB、Pinecone等,还可根据需求进行替换和组合,打破了技术绑定。
  • 高效的工作区管理:将文档划分为称为"工作区"的对象,工作区类似线程且增加了文档的容器化,不同工作区内容相互独立,互不干扰,可保持每个工作区上下文清晰,同时支持多用户协作与权限分级,保障数据安全,适合企业内部文档管理等多用户场景。
  • 强大的Agent功能加持:内置Agent功能,可联网搜索、执行代码等,能够拓展对话能力边界,使知识库不仅能基于本地知识回答问题,还能结合实时信息提供更全面准确的回答。

构建方式

  • 基于RAG架构:采用Retrieval-Augmented Generation即检索增强型生成架构,工作流程分为预处理(文档清洗、分块)、向量化(嵌入模型处理)、检索(向量数据库查询)和生成(LLM模型回答)四个主要阶段,通过这种方式将文档等知识数据转化为LLM可理解和利用的知识,从而构建知识库。
  • 借助开源框架与工具:利用Anything LLM这一开源框架,结合Docker等工具进行部署,可快速搭建起知识库。其内置了向量数据库和embedding模型,只需接入合适的LLM,按照官方文档进行配置和操作,即可完成知识库的构建。

应用场景

  • 企业知识管理:企业可将内部的各种文档、资料等上传到知识库,员工在需要时通过与知识库的对话快速检索和获取所需信息,提高工作效率,同时保障企业知识的安全性和私密性。
  • 个人学习与研究:个人可将自己在学习、研究过程中积累的资料、笔记等整理到知识库中,方便随时查询和复习,还能利用知识库的智能对话功能进行知识的拓展和深化。
  • 创意开发与应用:开发者可基于Anything LLM知识库开发各种创意应用,如聊天机器人、虚拟助手等,为人们的生活和工作带来更多便利和乐趣。

功能在VUE3中的相关实现

知识库的维护

  1. 在项目中增加src/views/Library/documents.vue组件来作为知识库组件的主入口。
vue 复制代码
<template>
  <div class="document-main">
    <div class="document-content-group">
      <el-row class="document-content">
        <el-col class="document-content-btn">
          <uploadLibraryPlugin @reload="getDocumentList" />
          <el-button plain @click="deleteDocument">删除</el-button>
        </el-col>
        <el-col class="document-content-table">
          <el-table
            border
            :data="documentTableList"
            height="100%"
            @selection-change="selectionChange"
          >
            <el-table-column type="selection" align="center" width="55"></el-table-column>
            <el-table-column
              align="center"
              type="index"
              label="序号"
              width="80"
            ></el-table-column>
            <el-table-column
              align="center"
              prop="title"
              label="文件名称"
              show-overflow-tooltip
              min-width="200"
            ></el-table-column>
            <el-table-column
              align="center"
              prop="url"
              label="文件路径"
              min-width="200"
              show-overflow-tooltip
            ></el-table-column>
            <el-table-column
              align="center"
              prop="description"
              show-overflow-tooltip
              label="文件描述"
              min-width="200"
            ></el-table-column>
            <el-table-column
              align="center"
              prop="docAuthor"
              show-overflow-tooltip
              label="文件作者"
              min-width="100"
            ></el-table-column>
            <el-table-column
              align="center"
              prop="docSource"
              show-overflow-tooltip
              label="文件来源"
              min-width="120"
            ></el-table-column>
            <el-table-column
              align="center"
              prop="published"
              show-overflow-tooltip
              label="发布时间"
              min-width="120"
            ></el-table-column>
          </el-table>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script setup lang="ts">
import uploadLibraryPlugin from "@/views/Library/components/document/uploadLibraryPlugin.vue";
import { getAllDocuments, removeSystemDocuments } from "@/apis/anythionChatAPIs";
import { onMounted, ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
let documentTableList = ref<any>([]);
const selectionList = ref([]);
onMounted(() => {
  // 初始化获取所有工作空间信息
  // getWorkspaceList();
  getDocumentList();
});
// 删除文档内容
const deleteDocument = () => {
  if (selectionList.value.length < 1) {
    ElMessage.warning("请至少选择一条数据");
    return;
  }
  ElMessageBox.confirm("确定要删除选中项吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    let _deleteNames = selectionList.value.map((item: any) => {
      return `${item.docName}/${item.name}`;
    });
    removeSystemDocuments({ names: _deleteNames })
      .then(() => {
        ElMessage.success("删除成功");
        getDocumentList();
      })
      .catch(() => {
        ElMessage.error("删除失败");
      });
  });
};
// 列表复选框选中触发
const selectionChange = (selection: any) => {
  selectionList.value = selection;
};
const getDocumentList = async () => {
  const res: any = await getAllDocuments();
  documentTableList.value = [];
  res.localFiles.items.forEach((documentInfo: any) => {
    let _currentDocumentList = documentInfo.items.map((item: any) => {
      item.docName = documentInfo.name;
      item.type = documentInfo.type;
      return item;
    });
    documentTableList.value = documentTableList.value.concat(_currentDocumentList);
  });
};
</script>

<style scoped lang="scss">
.document-main {
  display: flex;
  overflow: hidden;
  height: 100%;

  .document-tab-group {
    width: 300px;
  }

  .document-content-group {
    flex: 1;
    padding: 8px;
    overflow: hidden;

    .document-content {
      display: flex;
      flex-direction: column;
      overflow: hidden;

      .document-content-btn {
        margin-bottom: 4px;
      }

      .document-content-table {
        flex: 1;
        overflow: hidden;
      }
    }
  }
}
</style>
  1. 增加src/views/Library/components/document/uploadLibraryPlugin.vue组件来实现维护知识库的弹窗效果
vue