使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库

熬夜冠军
• 阅读 1572

使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库

文章博客地址:套路猿-使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库

流程

使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库

  1. 将数据集 通过 openai embedding 得到向量+组装payload,存入 qdrant
  2. 用户进行问题搜索,通过 openai embedding 得到向量,从 qdrant 中搜索相似度大于0.8的数据
  3. 从 qdrant 中取出相似度高的数据
  4. 将获取到的QA,组装成 prompt 向chatgpt进行提问,得到回答

向量数据库 qdrant

  • qdrant 是一个开源的向量搜索引擎,支持多种向量距离计算方式
  • 官方文档:https://qdrant.tech/documentation/quick_start/
  • 本节 介绍 qdrant 都是基于官方文档的例子,如已熟悉可以直接阅读下一节 [数据导入k8s知识库]

    安装 qdrant

    docker 安装

    docker pull qdrant/qdrant && \
    docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant

    collection 说明

    collection 是 qdrant 中的一个概念,类似于 mysql 中的 database,用于区分不同的数据集合
    官方文档:https://qdrant.tech/documentation/collections/#collections
    collection 下面是 collection 字段说明,以创建 collection 为例

    PUT /collections/{collection_name}
    {
      "name": "example_collection",
      "vectors": {
        "size": 300,
        "distance": "Cosine"
      }
    }

    name: collection 名称
    vectors: 向量的配置
    size: 向量的维度
    distance: 向量的距离计算方式,Cosine(余弦距离), Euclidean(欧式距离),Dot product(点积)
    如果需要将 openai embedding 后 存入 qdrant,需要将 size 设置为 1536openai embedding

插入数据

这个是官网 http add point 的例子,可以看到 payload 是可以存储任意的 json 数据,这个数据可以用于后续的过滤

curl -L -X PUT 'http://localhost:6333/collections/test_collection/points?wait=true' \
    -H 'Content-Type: application/json' \
    --data-raw '{
        "points": [
          {"id": 1, "vector": [0.05, 0.61, 0.76, 0.74], "payload": {"city": "Berlin" }},
          {"id": 2, "vector": [0.19, 0.81, 0.75, 0.11], "payload": {"city": ["Berlin", "London"] }},
          {"id": 3, "vector": [0.36, 0.55, 0.47, 0.94], "payload": {"city": ["Berlin", "Moscow"] }},
          {"id": 4, "vector": [0.18, 0.01, 0.85, 0.80], "payload": {"city": ["London", "Moscow"] }},
          {"id": 5, "vector": [0.24, 0.18, 0.22, 0.44], "payload": {"count": [0] }},
          {"id": 6, "vector": [0.35, 0.08, 0.11, 0.44]}
        ]
    }'
  • id:唯一
  • vector:向量,可在HuggingFace 找相应的模型训练,获取,也可以 openai embedding 得到
  • payload:任意的自定义 json 数据

    搜索数据

    这是 qdrant 官方搜索数据的例子

    curl -L -X POST 'http://localhost:6333/collections/test_collection/points/search' \
      -H 'Content-Type: application/json' \
      --data-raw '{
          "vector": [0.2,0.1,0.9,0.7],
          "limit": 3
      }'

    vector:向量,通过 openai embedding 得到
    limit:返回的数据条数

    数据导入k8s知识库

    // 模拟数据集 question:answer
    var questions = []string{
      "什么是Kubernetes中的Deployment?",
      "Kubernetes中的Service有什么作用?",
    }
    
    var answers = []string{
      "Deployment是Kubernetes中用于管理应用程序副本的资源对象。它提供了副本的声明性定义,可以实现应用程序的部署、扩展和更新。",
      "Service用于定义一组Pod的访问方式和网络策略。它为Pod提供了一个稳定的网络地址,并可以实现负载均衡、服务发现和内部通信。",
    }
    
    func main() {
    // 第一步:自己创建 一个collection:  kubernetes
      var err error
      err = qdrant.Collection("kubernetes").Create(1536)
      if err != nil {
          log.Fatalln("创建collection出错:", err.Error())
      }
    
      points := []*pb.PointStruct{}
      // 批量 进行BuildQdrantPoint
      for index, question := range questions {
          if index < 9 {
              continue
          }
          p, err := BuildQdrantPoint(question, answers[index])
          if err != nil {
              log.Fatalln("创建point出错:", err.Error())
          }
          fmt.Println(p.Id)
          points = append(points, p)
    
      }
      err = qdrant.FastQdrantClient.CreatePoints("kubernetes", points)
      if err != nil {
          log.Fatalln("批量创建point出错:", err.Error())
      }
    }
  • 模拟数据集,将数据集导入到 k8s 知识数据库中
  • BuildQdrantPoint 函数是将问题和答案转换成 qdrant 的 point
  • 其中 vector 是通过 openai embedding 得到的,这里使用的是 openai embedding

搜索数据

代码实现

import (
    "fmt"

    myai "embedding-knowledge-base/ai"
    "embedding-knowledge-base/qdrant"
)

func main() {
    prompt := "什么是Kubernetes中的DaemonSet?"
    // prompt := "苹果不削皮能吃吗"
    p_vec, err := myai.SimpleGetVec(prompt)
    if err != nil {
        panic(err)
    }
    points, err := qdrant.FastQdrantClient.Search("kubernetes", p_vec)
    if err != nil {
        panic(err)
    }

    fmt.Printf("用户的问题是:%s\n", prompt)
    if points[0].Score < 0.8 {
        fmt.Println("违规问题或者超纲问题")
        return
    }
    answer := points[0].Payload["answers"].GetStringValue()
    fmt.Printf("知识库答案是:%s\n", answer)
    tmpl := "question: %s\n" + "reference answer: %s\n"
    finalPrompt := fmt.Sprintf(tmpl, prompt, points[0].Payload["question"].GetStringValue(), answer)
    fmt.Println("------------------------")
    fmt.Printf("结合知识库参考答案:chatgpt的回答是:%s\n", myai.K8sChat(finalPrompt))
    // 不结合知识库参考答案
    fmt.Printf("不依赖本地知识库, chatgpt的直接回答是:%s\n", myai.K8sChat(prompt))
}
  • 通过 prompt 搜索qdrant 知识库,如果相似度小于 0.8,有可能是用户乱提问,或问知识库无关的问题,直接返回
  • 取相似度度大于 0.8,则取第一条数据,组装成promot向gpt进行提问,得到回答
  • 具体的实现可以参考 main.go 的代码

    示例

  • 问无关的问题,比如:苹果不削皮能吃吗
    使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库
    可以看到 相似度太低,提示违规问题或者超纲问题
  • 问k8s 本地知识库的问题,比如:什么是Kubernetes中的Deployment?
    使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库
  • 问k8s本地知识库的问题,但单独向chatgpt提问,得到的答案 并不是已定与k8s相关,比如问 网关是什么
    使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库

    • 可以看到,红线部分,是直接将用户问题 向 chatgpt 请求 得到的答案,跟k8s无关
    • 红线前面的回答:是正确的,结合k8s本地知识库,可以让回答偏向 数据集设定的主题

    示例源码地址及使用

    源码地址:embedding-knowledge-base
    进入根目录,将目录 ai/common.go 的 以下 const改成自己的

      SocksProxy = "socks5://127.0.0.1:1080"
      AIKey      = "your api key"

    docker 安装 qdrant

    make install-qdrant

    数据集导入qdrant

  • 导入 adrant,我这边就是模拟 了十几条k8s相关的问题,在 prebuild/prebuild.go
  • 更多的数据集,需要自己用脚本抓取,然后导入qdrant

    make import-qdrant

    搜索

    make search
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这