Skip to content

19 动手实战 实现实战案例3

你好,我是叶伟民。

上一节课我们使用模糊检索知识最基本的概念改造了实战案例1。这节课我们趁热打铁,继续动手实战,使用模糊检索知识最基本的概念实现实战案例3。

在动手操作之前,我们先梳理一下整体的流程。实现实战案例3主要包括五个步骤:

  1. 搭建向量编码服务(复用实战案例1已经搭建好的服务)
  2. 设计数据库(可参考实战案例1)
  3. 知识入库(可参考实战案例2)
  4. 检索知识(可参考实战案例1和2)
  5. 显示相关链接

考虑到我们已经进入第四章,所以从这节课开始,前面讲过的基础(例如激活虚拟环境)将不再重复了,更具体的步骤会列在这节课配套代码的 如何运行本节课程序.md 文件里面,配套代码你可以从 这里 获取。

好,我们正式开始今天的学习吧。

设计数据库

第1步搭建向量编码服务完全复用实战案例1已经搭建好的服务,所以我们就不重复讲解了,如果不熟悉的话,你可以回顾第一章的内容。我们从第2步设计数据库开始讲起。

表结构设计

我们先来思考这样一个问题,纯知识类RAG应用的数据库结构与基于MIS系统的RAG应用(也就是实战案例1)有什么区别呢?

答案是纯知识类RAG应用的数据库至少要有两个表。 第一个表是主表,由元数据组成,这个表我们直接参考实战案例2,也就是 第9节课 的内容。于是我们得出了后面这个表。

class 知识主表(models.Model):
    id = models.IntegerField(
         primary_key = True,
         editable = False)

    模块 = models.TextField(null=True,blank=True)
    标题 = models.TextField(null=True,blank=True)
    url = models.TextField(null=True,blank=True)
    向量编码 = VectorField(dimensions=1024,null=True,blank=True)
    向量编码模型 = models.TextField(default="bge-large-zh-v1.5")

第二个表是详细表,由文本和对应的向量编码组成,这个表我们直接参考实战案例1,也就是第18节课的内容。于是我们得出了后面这个知识详细表。

class 知识详细表(models.Model):
    id = models.IntegerField(
         primary_key = True,
         editable = False)

    文本内容 = models.TextField()
    内容在分段中的顺序 = models.IntegerField(null=True,blank=True)
    向量编码 = VectorField(dimensions=1024,null=True,blank=True)
    向量编码模型 = models.TextField(default="bge-large-zh-v1.5")
    知识主表 = models.ForeignKey(知识主表, on_delete=models.CASCADE)

执行命令更新数据库

然后我们切换到 Anaconda Powershell Prompt,输入以下命令更新数据库。

python manage.py makemigrations
python manage.py migrate

这时候应该提示数据库更新成功。然后我们需要打开数据库确认操作无误。

打开数据库确认操作无误

我们使用第16节课讲过的 pgadmin,打开 知识主表 表。这时我们应该看到已经成功添加了所有元数据列。

我们再打开 知识详细表 表做个检查。这时我们应该看到已经成功添加了 向量编码向量编码模型 这两列。其中 向量编码 的数据类型为我们在第16节课讲过的 vector 类型。

知识入库

设计完数据库之后,我们可以知识入库了。

实战案例1的数据早就在数据库里面了,所以不需要这一步。实战案例2包含了知识入库步骤,于是我们可以参考实战案例2,也就是第11节课的内容来完成知识入库。

导入原始内容

这里我们重申第11节课的精神,也就是 尽量选择最容易的知识入库方法。

然后结合第14节课“选择合适自己的RAG应用立项”一节的精神,建议同学们在项目立项阶段尽量不考虑必须从PDF和Word导入知识的项目,这些项目所耗费的时间精力会远比你想象中的高,会是个大坑。

回到我们今天节课,这里我们直接通过Excel导入,我们运行 import_data_from_excel.py 文件即可,它会把 data.xlsx 的数据导入到数据库。

对知识批量编码并存进数据库

现在原始内容已经入库,我们还需要对知识进行编码。

我们继续在 api.py 文件尾部添加后面的代码。

def 对知识批量进行向量编码(request):
    未编码的知识list = 销售入账记录.objects.filter(客户向量编码__isnull=True)
    for current in 未编码的知识list:
        current.客户向量编码= 调用向量编码服务(current.客户)
        current.save()

    result = {'code':200}
    return JsonResponse(result)

这段代码与第18节课相关代码类似,同学们可以直接参考,我们就不浪费时间重复了。

然后我们在 postman 调用http://127.0.0.1:8000/api/knowledge-embedding-batch 即可批量编码并将其保存进数据库。

除此之外,我们还可以在数据入库的时候就调用以上函数进行向量编码。

检索知识

现在知识已经入库,我们可以检索知识了。检索知识由两部分组成,其中根据元数据检索部分可以参考实战案例1,其他部分可以参考实战案例2。

我们打开 search.py 文件,往 查询 函数里添加后面这段代码。

    else:
        查询字符串 = 查询参数.strip()
        查询字符串向量编码 = 调用向量编码服务(查询字符串)
        模糊搜索结果RawQuerySet = 知识详细表.objects.raw('SELECT *, 1 - (向量编码 <=> %s) AS 余弦相似度,文本内容 FROM public."home_知识详细表" order by 余弦相似度 desc;',[str(查询字符串向量编码),])

        选取的结果 = 模糊搜索结果RawQuerySet[0]
        模糊搜索结果 = {
                '知识内容': 选取的结果.文本内容,
                'url': 选取的结果.知识主表.url,
                '标题': 选取的结果.知识主表.标题
            }
        return 模糊搜索结果

这段代码与第18节课相关代码类似,相信结合注释你很容易就能理解。

显示相关链接

我们打开 index.html 文件,在底部添加以下代码。

{% if link_text is not None %}
          <div>
      参考链接:<a href="{{link_url}}" target="_blank">{{link_text}}</a> &nbsp;
          </div>

然后我们打开 http://localhost:8000, 我们将看到如下界面。

与实战案例1(也就是第18节课)所讲的类似,我们修改了很多代码文件,就是没有修改 rag.py 文件。因为我们只是增强了检索知识的部分,至于整个RAG流程,其实没做任何改变。这是本节课甚至这门专栏的重点,RAG只有一个核心,提升检索知识的质量,更通俗地说,就是保证检索到的第一条知识是最匹配用户提问的,然后喂给大模型。如果能够做到这点,根本就不需要重排了(rerank)。

小结

好了,今天这一讲到这里就结束了,最后我们来回顾一下。这一讲我们学会了四件事情。

第一件事情是设计纯知识类RAG应用的数据库结构。纯知识类RAG应用的数据库结构至少由两个表组成。 第一个表称为主表,由元数据组成。第二个表是详细表,由具体文本和向量编码组成。

第二件事情是知识入库。我们直接从excel表导入。需要提醒的是,在项目立项阶段尽量不考虑必须从PDF和Word导入知识的项目,这些项目所耗费的时间精力会远比你想象中的高,会是个大坑。

第三件事情是检索知识。与实战案例1相比,实战案例3基本靠模糊检索,所以严重依赖于前面所讲的基础概念。你也可以借机复习巩固一下之前所学。

第四件事情是显示相关链接。RAG本质上是搜索应用,只不过因为大模型的发展,大幅提升了摘要和检索的质量,从而提升了用户体验。 显示相关链接其实就是传统搜索应用所具备的功能。

下一节课开始,我们将学习如何评估和改进RAG应用的检索质量。

思考题

发挥你的想象力,想象一下自己企业有哪些RAG应用是不需要从PDF和Word导入知识的。请认真想想,根据我的经验,实际上这些RAG应用还是有的,甚至不少。

欢迎你在留言区和我交流互动,如果这节课对你有启发,也推荐分享给身边更多朋友。