【实操】基于ChatGPT构建知识库

news2024/11/28 22:03:00

前言

最近有些实践,因为后面要去研究fine-tune了,想着记录一下chatgpt+向量数据库构建知识库的一些实操经验,不记我很快就忘了,哈哈。

首先,提一下为啥会出现向量数据库这个技术方案?

大家经过实践发现,如果通过简单的prompt与ChatGPT聊一下专业领域或实时性比较强的内容时,效果是很差的,比如公司有广告投放的业务,我们就需要一批同学去研究不同平台的广告投放文档,这些API文档实时性比较强,你让ChatGPT直接去生成代码,效果就很差。

为了让效果好一点,我们可以将页面的所有内容都复制出来,放到prompt中,类似于:

context:

{document content}

please base the context generate xxxx code use python.

我们将最新的内容放到prompt中,ChatGPT就会基于最新的内容去生成代码了,类似的方式,我们可以将公司内部的文档放到prompt中,让ChatGPT基于文档内容回答。

但,ChatGPT有Tokens数量限制,我常用的GPT-3.5-16k,就是有16k的tokens限制,即单次request+response的tokens数不能超过16k,超过则会报错,所以如果文档内容稍微多一点,就无法全丢到prompt中。

目前的折中的解决方案就是使用向量数据库,其原理就是对文本进行embed后,再进行相似度运算。

关于embeddings原理,网上内容很多,我就不费篇章了,推荐一篇比较系统讲embed的文章:https://www.featureform.com/post/the-definitive-guide-to-embeddings

向量数据库基本使用

比较火的例子就是让ChatGPT基于你的PDF文件来聊天,这里的PDF文件可以有多个,比如100个PDF,那变通一下,就可以基于这套来构建公司的知识库。

我找了一张图,来自视频:https://www.youtube.com/watch?v=TLf90ipMzfE

4b0b45bd14591bceaa214f8b1a4c305a.png

我简单描述一下:

首先要做预处理:

  • 1.你需要将所有的PDF中的内容都抽成纯文档,如果PDF中是不可编辑的图片,你就需要使用OCR来获得内容。

  • 2.获得纯文本后,你需要对文本进行切块,太多内容放在一起生成一个embed vector(嵌入向量)效果是不好的。

  • 3.存入向量数据库

当用户提问时,定义问题为A,首先会将问题A通过同样的embed model转成embed vector,然后再计算问题A的embed vector与之前所有PDF生成的一系列embed vector的相似度(你可以用余弦来算,也可以用其他计算向量距离的算法来算)。

计算后,会获得当前PDF中与问题A最相近的内容(向量在空间上距离最近的),然后将这部分PDF内容和问题A一同输入给ChatGPT,其实就是为ChatGPT提供了上下文,让ChatGPT基于上下文回答。

一些细节:

  • Q:为何太多内容一起生成一个embed vector效果不好?

  • A:理论上是,内容多时,一般意思就多(信息多),此时对embed vector做相似计算时,获得一堆意思很多的内容给ChatGPT,生成的效果也是比较差的,我们做向量相似度计算的目的就是将与用户提问最相关的内容找出来,如果你每次都找出一大段的内容,效果就不好,所以这里可以引入很明显的优化点:做好数据的合理切块。

  • Q:具体要怎么计算相似度?内容那么多,代码上有没有轮子可以用?

  • A:一般不自己计算,而是交给向量数据库,通过SQL或相应的方法直接查询结果,向量数据库底层会帮你做向量相似度的计算。

上面整个流程其实在langchain文档中都给了具体的代码,30行左右的代码:https://colab.research.google.com/drive/181BSOH6KF_1o2lFG8DQ6eJd2MZyiSBNt?usp=sharing

当然熟练了上面的代码后,你会发现langchain给我们提供了RetrieverQA这个类,它做的就是将上面的过程再简化,5行左右的代码搞定基于文档的简单问答系统,详情:https://python.langchain.com/docs/use_cases/question_answering/vector_db_qa

一些问题

我实操了一些项目,通过这20行代码已经可以实现基本的知识库了,但深入使用后,还是会感到有点不足:

  • 1.准确度有时比较低

  • 2.会发散回答一些无关的内容

2个问题都无法100%解决,因为这不是工程问题,而是因为GPT模型本身的基底是概率性质的,但实践后,确实有一些改进方法。

过发散问题

首先说一些GPT发散回答的问题,最基本的改进方法就是在prompt中强调不要发散回答,要严格基于提供的上下文回答,如果上下文没有相关的内容,则回答不知道,例如:

# Context
{content from vector database}

# Requirements:
- 1. Before responding, please assess whether you have sufficient Context knowledge to answer the question. If you cannot answer, please reply directly with: "I'm sorry, I cannot answer your question."
- 2. Respond to questions using the provided Context knowledge.。

# Question:
{question}

进一步,就需要自己再做一层工程过滤,比如收集相关的问题,训练一个二分类模型,参考分类垃圾邮件相关的算法就好了。

准确度问题

关于准确度的问题,简单思索会发现有2个方面会影响最终生成内容的准确度:

  • 1.数据预处理

  • 2.embed算法和相似度算法

数据预处理

经过实践,发现数据做好预处理,准确度确实有所改进。

我有个项目,是做代码文档知识库的,这是为我个人打造的,场景是:我需要使用新的技术解决方案时,比如我要学openai怎么用,我就希望自己简单知道概念后,直接让ChatGPT帮我写代码,我做一套简单的解决方案:

  • 1.爬虫递归爬取相关的文档

  • 2.利用类似上文提到的PDF方案,20~30行代码,解决了核心功能(基于文档的问答系统)

  • 3.用streamlit弄了一个UI

但深入用时,会发现生成的代码,有时问题比较多。

因为常见的文本切分都是按固定字数切分的,这种方法可能会将语义连贯的部分切分开,导致计算相似向量时,获得的内容不全,然后因为是技术文档,通常会有example code,按字数切分容易将code切开,就很尴尬。

langchain本身提供了一个方法来降低这种影响,就是文档切开后,可以留一部分重复的内容,比如切块后的最后50字,与第二块的前50字是一样的,从而维持关联性,方法代码如下:

text_splitter = CharacterTextSplitter(        
    separator = "\n",
    chunk_size = 1000,
    # 维持关联性
    chunk_overlap  = 200,
    length_function = len,
)

但效果没有太大改善,所以我做了一些数据预处理:

1.按title来分,通常一个title中的内容是强关联的,不再按字数分,如果一个title下的内容很多,就拆分一下,但title依旧会带上,例如:

原文:

title 1
content(1800 char)

预处理后(按1000char切分):

title 1
content(0~1000 char)

title 1(不同块都带上原本的title)
content(1000~1800 char)

2.特殊内容直接业务处理,比如我处理的是代码文档,其中的example code挺重要的,我会将其单独存起来,不做embed vector,而是直接存原内容,然后通过id与相关的embed vector关联上,例如:

原文:

title 1
content(300 char)
code(100 char)
content(200 char)

title 2:
content(1200 char)
code(100 char)
content(200 char)

预处理后(按1000char切分)

title 1 + all content(300+500) = embed vector , code(100 char)存表获得code id => 表里会记录 embed vector 与 code_id的关联关系

title 2 + 1000 char + code_id
title 2 + 400 char(200 + 200) + code_id

这样相似度搜索后,我会将相关的code_id的内容也直接从数据库搜出来,将code直接输入给chatgpt,从而提高生成代码的合理性(当然很多文档本身就是屎一样,example code的代码就是有问题的,这种就是硬伤,但还是可以省点时间)

3.在prmopt中加上一些背景知识,还是以代码文档为例,比如我希望生成Python代码,有些文档的example只给了curl的请求方式,此时就可以在system prompt中加上,如:遇到curl转成python requests的代码之类的。

替换模型和算法

我目前主要使用openai的text-embedding-ada-002,但其实还有很多embedding model,上huggingface翻阅一下,然后看看对比,比如m3e的对比数据就比openai的ada-002要好一点:

7fe2e9ac63d7701766e4b2d213f54082.png

然后就是相似度算法,可以尝试替换,这里,我目前主要使用supabase来做,抛开supabase提供的其他功能,可以单纯将supabase当成Postgres database,然后开始白嫖,supabase中怎么做向量相似度计算的方案如下:

你如果不太了解,可以阅读supabase的这篇文章:https://supabase.com/blog/openai-embeddings-postgres-vector

基于supabase + vercel + nextjs,你会发现,做一个知识库项目早期只需要花域名钱和OpenAI的API钱,向量数据库、服务器这些,都可以白嫖。

其他方案

Bybrid Search

我们可以参考其他系统,比如推荐系统,如果遇到推荐不准确时,一个常用的方案就是多路召回,直白说,就是除了从向量数据库通过向量相似度计算获得数据外,再通过其他方式获得相关数据,做多一路。

看langchain的人twitter分享,将关键词搜索加入其中,准确率提高了20%,他将这种方式称为:Bybrid Search。

5f7dc052fdea7794d0e06b8a84cf666d.jpeg

Bybrid Search的代码目前只存在langchain JS版本中,Python版本是没有的,用起来很简单:https://js.langchain.com/docs/modules/data_connection/retrievers/integrations/supabase-hybrid,替换一下参数就好了。

langchain这个库多数时候都这样,功能帮你封装好,你要用,很简单的,你要定制化的用,抱歉,你需要自己解决,所以很多人吐槽langchain的过度封装,我自己到感觉还好,我的很多需求,确实需要定制化,但也很简单,继承langchain的类,将需要重写的方法重写掉,通常只需要加一点点自己的业务代码就好了。

改写用户提问

这是一个有风险的操作,毕竟你的改写可能会改变用户的语义,你可以用一些工具尝试改写一下,或将是否改写的选择权交给用户,比如UI上弄一个按钮:【优化提问】,之类的。

你可以尝试一下直接套多一层ChatGPT,让ChatGPT帮你将用户的问题在不改变语义的情况下,将问题写的更详细,这是有效果的。

或者,你可以直接使用PromptPerfect这类工具(我还没用过)

c5228f2463db67d1de2fe9b8ccf3d49f.png

multi-Document Agents

multi-Document Agents的核心思路就是构建Agent和不同的文档索引方案,让Agent帮你选择,具体如下图:

3e685e07bc6575cbcadab49c55e2e2aa.jpeg

我第一次了解这个方式是在LlamaIndex框架创始人的twitter上看见的,他在twitter上说受到了递归文档代理(Recursive Document Agents)的启发,弄了Multi-Dcouemtn Agents,相关推文:https://twitter.com/jerryjliu0/status/1693421308674289822。

在我看来,langchain定义是更通用的框架,它的功能非常多,而LlamaIndex主要目的是构建一个索引、搜索的LLM框架,因为langchain也有这部分功能,所以挺多人会对比一下,社区间主流的观点是LlamaIndex会快一点,如果大量的数据处理,推荐用LlamaIndex,而Langchain功能更多更灵活一些,在文档检索+LLM这个领域(狭义点就是知识库领域),两者都强调自己的Retrieval-Augending Generation(RAG,检索增强生成)的功能可以帮助你更好的利用文档内容😄。

回到Multi-Dcouemtn Agents,代码基本的细节可以看LlamaIndex文档:https://gpt-index.readthedocs.io/en/latest/examples/query_engine/recursive_retriever_agents.html。

获得实时性强的回答

有时,我们希望ChatGPT除了可以基于知识库回答一些专业性比较强的问题外,还希望可以回答外部实时性比较强的问题,比如行业咨询等。

这里的核心依旧是,通过某种方式可以获得外部数据,然后将其放到Prompt中,这样ChatGPT就知道了。嗯,具体怎么做呢?

假设,你希望通过Google获得实时性的信息,你就需要写一个Google爬虫,Google爬虫要比较稳定的运行,需要购买住宅IP,不然很容易被Google风控限了,当然,我建议你直接用相关的SaaS,比如:https://serpapi.com/,直接付费,然后就可以通过api获取Google数据了。

然后你会发现Langchain提供了serpaper相关的工具:SerpAPIWrapper,直接使用,便可以将Google获取的信息直接加到你现有的知识库项目中了。

如果还是不够清晰,可以直接看完整的代码解决方案:https://colab.research.google.com/drive/1Q-lm-apSJRYwoPvUZGiwYMezeky0yQXD?usp=sharing#scrollTo=XbOXzzQh-ASf

作者利用Langchain的RetrievalQA + SerpAPIWrapper构建了一个可以查询实时信息的知识库。

结尾

以上就是我实操过或简单尝试过的一些方案,简单而言,要构建一个好用的知识库,数据的预处理比较重要,有些企业会让员工人为的整理好原始的QA数据集,然后再基于这个QA做知识库,优质的预处理数据可以比较明显的提升知识库的效果,此外,直接基于原始QA数据集去做问答知识库的方案也被人拓展了一下,假设原数据是单纯的文档数据,那么就让ChatGPT帮你将文档数据转成QA数据集嘛,写个Prompt,让他基于内容问题之类的,但这个我没实操过,所以我也不清楚效果。

有了比较好的预处理数据,再结合本文上面的各种技巧,就可以获得一个准确度比较高的知识库解决方案了。

文章有点长,你居然看到了最后?不打赏一下增加我们的革命友谊吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1096321.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10.2手动推导linux中file, cdev, inode之间的关系

是时候可以手动推导一下linux里面基类父类和子类的关系了 代码放最后把 简单说明版 详细流程 第一步注册驱动 cdev结构体能看做是一个基类,那么链表里面都是字符设备驱动的cdev连载一起,啥串口,lcd的,通过cdev->list_head连接 那cdev结构体里有主次设备号 第一步 使用r…

【机器学习】特征工程:从理论到实践的关键技巧

机器学习之特征工程实践技巧 1、引言2、了解特征工程2.1 机器学习2.2 特征工程 3、特征工程的思路4、特征工程的方法5、特征工程的实践6、总结 1、引言 小屌丝:鱼哥, 你不厚道~ 小鱼:啥情况? 小屌丝:你为啥不给我讲…

GMSH如何对STL模型再次划分网格

由于时间比较忙,一直未曾继续写博文,今天写一篇关于Gmsh对STL模型二次划分网格的测试。此项测试还不够完善,目前仅实现了STL模型的细分,效果还可以,在OSG下显示的效果如下: 一、原始STL显示效果&#xff0…

SVN服务端客户端安装配置

SVN服务端客户端安装配置 1、服务端下载安装1.1 软件下载1.2 软件安装 2、客户端下载安装2.1 软件下载2.2 软件安装2.2.1 安装2.2.2. 汉化 3、SVN配置3.1 SVN服务器端配置3. 2 SVN客户端配置 1、服务端下载安装 1.1 软件下载 百度网盘链接:VisualSVN-Server-5.3.0…

YApi和Swagger接口管理

这篇博客针对苍穹外卖而写 YApi 之前的官网:yapi.smart-xwork.cn 由于之前的网址访问不了,现在我用的是这个网址:YApi Pro-高效、易用、功能强大的可视化接口管理平台 登录之后如下 创建两个工作空间 用户端接口也是如法炮制 Swagger 使用…

SSM - Springboot - MyBatis-Plus 全栈体系(二十七)

第六章 SpringBoot 二、SpringBoot3 配置文件 1. 统一配置管理概述 SpringBoot 工程下,进行统一的配置管理,你想设置的任何参数(端口号、项目根路径、数据库连接信息等等)都集中到一个固定位置和命名的配置文件(application.pro…

九、K8S之Ingress

Ingress 一、Ingress 概念 Ingress提供从集群外部到集群内服务的Http路由,流量路由由 Ingress 资源所定义的规则来控制。通过配置,Ingress可为Service提供外部可访问的URL,对其流量作负载均衡,代理转发。Ingress 的使用可以大大…

基于EtherCAT的机器人多轴同步运动控制

随着工业自动化的发展,机器人在生产线上的应用越来越广泛。为了实现高效、精确的运动控制,机器人的多轴运动必须能够实现同步操作,它能够提高机器人的运动精度和稳定性,实现更高效的生产线操作。同时,它也为机器人的协…

力扣刷题 day45:10-15

1,整数拆分 给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k > 2 ),并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 方法一:动态规划 #方法一:动态规划 def integerBreak…

2022最新版-李宏毅机器学习深度学习课程-P16 Loss可能影响网络训练失败softmax与sigmoid

一、实例回顾 分类的例子在前几讲中已详细分析,此处略过。 二、softmax与sigmoid 此处要明白!softmax和sigmoid的区别和联系 Softmax函数和Sigmoid函数的区别与联系 - 知乎 (zhihu.com) Sigmoid 多标签分类问题多个正确答案非独占输出(例如…

四叉堆在GO中的应用-定时任务timer

堆作为必须掌握的数据结构之一,在众多场景中也得到了广泛的应用。 比较典型的,如java中的优先队列PriorityQueue、算法中的TOP-K问题、最短路径Dijkstra算法等,在这些经典应用中堆都担任着灵魂般的角色。 理论基础 binary heap 再一起回忆…

2022最新版-李宏毅机器学习深度学习课程-P13 局部最小值与鞍点

一、优化失败的原因 局部最小值?鞍点? 二、数学推导分析 用泰勒公式展开 一项与梯度(L的一阶导)有关,一项与海赛矩阵(L的二阶导)有关 海瑟矩阵 VTHV通过海瑟矩阵的性质可以转为判断H是否是正…

LangChain-Decorators 包学习

libraryIO的链接:https://libraries.io/pypi/langchain-decorators 来colab玩玩它的demo 感觉这确实是个挺好用的库 想到之前纯调prompt来控制输出格式的痛苦,这个可太有效了 cool~ 最下面这个react的多智能体例子很好玩,来看看&…

MySQL 函数 索引 事务 管理

目录 一. 字符串相关的函数 二.数学相关函数 ​编辑 三.时间日期相关函数 date.sql 四.流程控制函数 centrol.sql 分页查询 使用分组函数和分组字句 group by 数据分组的总结 多表查询 自连接 子查询 subquery.sql 五.表的复制 六.合并查询 七.表的外连接 …

【微服务 SpringCloud】实用篇 · 服务拆分和远程调用

微服务(2) 文章目录 微服务(2)1. 服务拆分原则2. 服务拆分示例1.2.1 导入demo工程1.2.2 导入Sql语句 3. 实现远程调用案例1.3.1 案例需求:1.3.2 注册RestTemplate1.3.3 实现远程调用1.3.4 查看效果 4. 提供者与消费者 …

windows安装npm教程

在安装和使用NPM之前,我们需要先了解一下,NPM 是什么,能干啥? 一、NPM介绍 NPM(Node Package Manager)是一个用于管理和共享JavaScript代码包的工具。它是Node.js生态系统的一部分,广泛用于构…

MyBatisPlus(十八)字段类型处理器:对象存为JSON字符串

说明 将一个复杂对象(集合或者普通对象),作为 JSON字符串 存储到数据库表中的某个字段中。 MyBatisPlus 提供优雅的方式,映射复杂对象类型字段和数据库表中的字符串类型字段。 核心注解 TableName(autoResultMap true)TableF…

【Spring Boot 源码学习】@Conditional 条件注解

Spring Boot 源码学习系列 Conditional 条件注解 引言往期内容主要内容1. 初识 Conditional2. Conditional 的衍生注解 总结 引言 前面的博文,Huazie 带大家从 Spring Boot 源码深入了解了自动配置类的读取和筛选的过程,然后又详解了OnClassCondition、…

nodejs+vue百鸟全科赏析网站

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…

Linux性能优化--性能工具:特定进程内存

5.0 概述 本章介绍的工具使你能诊断应用程序与内存子系统之间的交互,该子系统由Linux内核和CPU管理。由于内存子系统的不同层次在性能上有数量级的差异,因此,修复应用程序使其有效地使用内存子系统会对程序性能产生巨大的影响。 阅读本章后&…