目录

方案流程

1. Embeddings 介绍

术语:微调 vs 嵌入

一句话理解便是:embedding model 可以将文本转换为固定长度的连续向量。

架构流程图

3、Qdrant云数据库的搭建

3.1、初识Qdrant

3.2、创建云数据库

3.3、通过curl 接口访问

4、写入测试数据

4.1、准备测试数据

4.2、调用embeddingAPI :将测试数据转换为向量数据

将上面准备好的向量数据数组全部写入向量数据库

通过运行接口来调试一下

5、开始查询数据

5.1、将查询字符串转换为向量数据

5.2、根据向量来查询匹配相关性高的前三条记录

5.3、通过chatGPT对查询的相关性数据进行优化

5.4、调试效果

6、总结

参考资料


方案流程

  • 将本地答案数据集,转为向量存储到向量数据。
  • 当用户输入查询的问题时,把问题转为向量然后从向量数据库中查询相近的答案topK 这个时候其实就是我们最普遍的问答查询方案,在没有GPT的时候就直接返回相关的答案整个流程就结束了。
  • 现在有GPT了可以优化回答内容的整体结构,在单纯的搜索场景下其实这个优化没什么意义。但如果在客服等的聊天场景下,引用相关领域内容回复时,这样就会显得不那么的突兀。

本项目的代码全部开源, GitHub地址为: 

GitHub - aehyok/go-openai: go 对接OpenAI ChatGPT接口,同时 azure openai接口已支持gpt-35-turbo和gpt-4模型go 对接OpenAI ChatGPT接口,同时 azure openai接口已支持gpt-35-turbo和gpt-4模型 - GitHub - aehyok/go-openai: go 对接OpenAI ChatGPT接口,同时 azure openai接口已支持gpt-35-turbo和gpt-4模型https://github.com/aehyok/go-openai

本文中我使用的是后端语言golang,来调用的所有外部接口,但它们均是restful api,所以如果你使用的是其他语言,那么是完全可以替换的,包括nodejs或者直接使用前端请求都是可以实现我的功能的。后面有机会会使用vue3来添加一个页面,现在主要通过postman或者apifox来调试接口,主要为了验证逻辑想法。

接下来首先来看看embeddings到底是什么吧。

1. Embeddings 介绍

术语:微调 vs 嵌入

微调就像你通过学习准备考试,是一种长期记忆,但过了一周后考试来临,模型可能会忘记,或者记错它从来没有读过的事实。

嵌入就像记笔记,是一种短期记忆,当考试的时候,你把笔记带上,随时翻看笔记,对于笔记上有的内容可以得到准确的答案。

另外嵌入的搜索提问方式相对于微调有一个缺点就是它每次附带的文本数量是有限制的,因为除了原始的问题,它还需要带上搜索出来的问题,GPT-3.5是4K(大约5页),GPT-4最大是32K(大约40页)。

就好比你有成书的教科书可以借鉴,但每次却只能翻看其中几页笔记。

如果你想构建一个对大量文本问答的系统,OpenAI建议“搜索-问”(Search-Ask)的方法。

也就是先在本地文档库中Search,拿到本地的数据结果,再去Ask,把搜索结果和问题一起交给GPT,这样GPT可以根据你提供的内容以及它模型中的数据,一起将结果返还给你。

至于如何搜索,并非一定要基于向量的搜索,可以有多重搜索方式:

  • 基于关键字搜索

  • 基于图形的搜索

  • 基于向量的搜索

可以被应用于以下几种情况

- 搜索(根据查询字符串的相关性对结果进行排名)

- 聚类(根据相似性对文本字符串进行分组)

- 推荐(具有相关文本字符串的项目被推荐)

- 异常检测(识别关联度小的异常值)

- 多样性测量(对相似性分布进行分析)

- 分类(文本串按其最相似的标签进行分类)

本文将主要学习第一种情况:搜索,根据相关性进行排名。

也可以理解成搜索完一定会有结果,但是相关性有可能很低,有可能需要用户加以甄别。出来的信息可能不是用户需要的答案。

顺便看看 chatgpt 怎么说的:

一句话理解便是:embedding model 可以将文本转换为固定长度的连续向量。

比如我下面使用的 text-embedding-ada-002模型输出的向量维度便是1536,这个在官网是有描述的,大家可以认真看看), 同时它是可以将任意的文本转换为向量。

那么接下来我会根据我的思路把我整个的搭建流程和调试思路都展现出来,方便自己后面进行复习查阅,也方便可能需要的你。

架构流程图

从上图可以比较清晰明了的知道大致要干什么了

  • 准备测试数据:测试数据可能很多一个很大的数组,慢慢通过调用ChatGPT接口进行转换数据,然后将转换后的向量数据存储到qdrant云数据库中,相当于本地数据了。
  • 根据查询返回结果:首先还是将要查询的字符串调用ChatGPT接口转换为向量数据,然后再将向量数据与向量数据库中的进行匹配相似度,匹配结束可以再通过GPT-3.5或者GPT-4的模型接口进行进一步的优化数据处理。

接下来就根据如下步骤一步步进行搭建

  • Qdrant云数据库的搭建
  • 准备测试数据并写入云数据库
  • 进行查询并返回结果

3、Qdrant云数据库的搭建

3.1、初识Qdrant

说白一点就是为了存储我自己的测试数据,不过它的重点是存储向量数据。

来到github上看了一下,有点牛逼 而且是Rust写的。那就来试试玩玩呗。

Qdrant - Vector Database for the next generation of AI applications. Also available in the cloud Vector Search Database | Qdrant Cloud

GitHub - qdrant/qdrant: Qdrant - Vector Database for the next generation of AI applications. Also available in the cloud https://cloud.qdrant.io/

3.2、创建云数据库

通过github可以直接到云官网: Vector Search Database | Qdrant Cloud

可以看到能免费创建一个免费套餐,拿来做个测试还是非常方便的。

针对图示的配置,可以永久免费使用,所以基本的测试是没问题了,可以好好的愉快玩耍。

找到左侧菜单Clusters然后右侧点击 Create,输入一个cluster名称(是不是可以翻译为集群名称??)。创建后等待一会儿在进行初始化。

点击上面的api-key 或者左侧 Access 都可以创建访问云数据库的链接和api-key。

记得复制好哟,这个跟ChatGPT生成的API-Key一样,只能看到一次,所以要保存好。

3.3、通过curl 接口访问

Swagger UI (qdrant.tech) 这个就是官方提供给我们的Swagger。可视化 RESTful Web Api

我是通过这个主要看接口以及接口参数,主要还是通过postman或者apifox等工具来测试接口,swagger这里好像没有配置api-key的地方?

ok可以看到我之前创建的 collect 还在,其实这个时候本来是要创建一个collect集合(在关系型数据库中可以叫做table表)。

4、写入测试数据

4.1、准备测试数据

注意:以上数据来源于ChatGPT,仅供参考和测试使用

然而我想要的数据结构是json数组的,那么继续使用ChatGPT进行装逼

可以发现准备这一组测试数据,有一点不费吹灰之力的感觉,真是太爽了。

这里就是准备的json数组,总共13条简单的记录而已,主要是为了看一下效果

[ { "title": "感冒", "text": "感冒是一种由病毒引起的呼吸道感染。典型症状包括喉咙痛、流鼻涕、咳嗽、打喷嚏、头痛和发热。" }, { "title": "流感", "text": "流感(Influenza)是一种由流感病毒引起的呼吸道感染。症状与感冒相似,但通常更严重,包括高热、寒战、喉咙痛、咳嗽、鼻塞、肌肉痛和乏力。" }, { "title": "肠胃炎", "text": "肠胃炎是胃和肠道的炎症,通常由病毒、细菌或寄生虫感染引起。症状包括腹泻、呕吐、腹痛、恶心、发热和脱水。" }, { "title": "常见皮肤病", "text": "如湿疹、皮炎、脓疱疮、疱疹等。症状可能包括红肿、瘙痒、干燥、脱皮和疼痛。" }, { "title": "头痛", "text": "头痛有许多原因,如压力、紧张、缺水、缺乏睡眠等。头痛可能表现为钝痛、搏动痛、集中在头的某个部位等。" }, { "title": "过敏", "text": "过敏是免疫系统对外来物质(过敏原)的异常反应。症状包括打喷嚏、流鼻涕、鼻塞、喉咙痛、眼睛痒、红肿和喘息。" }, { "title": "高血压", "text": "高血压是血压持续升高的病状。许多高血压患者没有明显症状,但可能会引发头痛、眩晕、心悸和呼吸困难。" }, { "title": "糖尿病", "text": "糖尿病是一种由于胰岛素分泌不足或细胞对胰岛素反应不良导致的血糖水平过高的疾病。症状包括频繁的小便、口渴、饥饿、疲劳、视力模糊、感染和伤口愈合缓慢。" }, { "title": "哮喘", "text": "哮喘是一种慢性呼吸道炎症疾病,表现为气道对刺激物的过度反应。症状包括喘息、呼吸困难、胸闷和咳嗽。" }, { "title": "背痛", "text": "背痛可能是由于肌肉拉伤、韧带损伤、关节炎、椎间盘问题等原因引起的。症状包括持续或间歇性的背部疼痛、僵硬和肌肉痉挛。" }, { "title": "关节炎", "text": "关节炎是关节炎症的一个通用术语,可能是由于多种原因引起的,如磨损性关节炎、类风湿性关节炎等。症状包括关节疼痛、肿胀、僵硬和活动受限。" }, { "title": "痔疮", "text": "痔疮是肛门或直肠血管的炎症或肿胀。症状包括肛门疼痛、瘙痒、肿胀、出血和可能的肛门突出物。" }, { "title": "眼疾", "text": "如干眼症、结膜炎和近视等。症状可能包括眼睛干燥、瘙痒、红肿、分泌物和视力模糊。" }]

4.2、调用embeddingAPI :将测试数据转换为向量数据

这里暂时就要用到ChatGPT的接口了

看官网接口请求主要就两个参数,一个就是model 选择模型,我这里使用的是text-embedding-ada-002,另外一个input 就是我们要转换的数据字符串了,好了直接上代码看看

func GetEmbeddings(ctx *gin.Context) dto.ResponseResult {// 配置日志data, _ := ctx.GetRawData()var parameters map[string]interface{}// 包装成json 数据_ = json.Unmarshal(data, &parameters)input := parameters["input"].(string)// n := m["n"].(int)// size := m["size"].(string)var response = GetEmbeddingApi(input)var obj map[string]interface{}if err := json.Unmarshal(response, &obj); err != nil {panic(err)}fmt.Println("Body:", obj)return dto.SetResponseData(obj)
}func GetEmbeddingApi(input string) []byte {// 定义请求参数embeddingModel := EmbeddingModel{Model: "text-embedding-ada-002",Input: input,}// 定义请求地址url := utils.OpenAIUrl + `/v1/embeddings`// 将请求参数转换为json格式bytes, err := json.Marshal(embeddingModel)if err != nil {fmt.Println("Error:", err)// return dto.SetResponseFailure("调用openai发生错误")}// 定义请求req := fasthttp.AcquireRequest()defer fasthttp.ReleaseRequest(req)req.SetRequestURI(url)req.Header.SetMethod("POST")req.Header.Set("Content-Type", "application/json")req.Header.Set("Authorization", "Bearer "+utils.OpenAIAuthToken)req.SetBody(bytes)// 定义响应resp := fasthttp.AcquireResponse()defer fasthttp.ReleaseResponse(resp)if err := fasthttp.Do(req, resp); err != nil {fmt.Println("Error:", err)// return dto.SetResponseFailure("调用openai发生错误")}fmt.Println("Status:", resp.StatusCode())return resp.Body()
}

我在代码里添加了详细的注释,对照代码看一下应该还是比较好理解的。

这里其实就是通过go语言调用 restful 接口:

https://api.openai.com/v1/embeddings

请求,因为下面查询的时候还需要将查询字符串转换为向量数据,所以我单独进行了封装可以在两个地方调用。

循环上述方法将预准备的json测试数据全部转换为向量数据

// 解析请求参数
var jsonData []map[string]string
if err := c.Bind(&jsonData); err != nil {return dto.SetResponseFailure("error")
}
if len(jsonData) == 0 {return dto.SetResponseFailure("json is empty")
}
// 数据向量化
points := make([]Point, 0)
for _, v := range jsonData {// 获取文本内容input := v["text"]// 获取文本内容的向量response := GetEmbeddingApi(input)fmt.Println(response, "response----response")var embeddingResponse EmbeddingResponsejson.Unmarshal(response, &embeddingResponse)points = append(points, Point{ID:      uuid.New().String(),Payload: v,Vector:  embeddingResponse.Data[0].Embedding,})
}

将上面准备好的向量数据数组全部写入向量数据库

现在向量数据通过ChatGPT接口转换好了,现在就需要将向量数据写入到Qdrant云数据库中。 下面主要是调用了CreatePoints方法。

func CreatePoints(collectionName string, pointRequest PointRequest) (err error) {response := &CommonResponse{}var reqBytes []bytereqBytes, err = json.Marshal(pointRequest)if err != nil {return}body, err := middleware.Send(http.MethodPut, collectionApi+"/"+collectionName+pointsApi+"?wait=true", reqBytes)if err != nil {return}err = json.Unmarshal(body, &response)if err != nil {return}if response.Result == nil {return errors.New(response.Status.(map[string]interface{})["error"].(string))}return
}

这里其实就是通过go语言调用restful 接口 :

https://ui.qdrant.tech/#/points/upsert_points

现在测试数据有了,向量数据库也有了,上一小节将测试数据转换为了向量数据,这里上面刚刚又写好了向量数据写入云数据库的接口。那么写入数据的基本完成了。

通过运行接口来调试一下

5、开始查询数据

准备好查询数据,先通过 ##3.2将字符串转换为向量数据(也就是为什么进行封装上面的方法的原因),然后通过向量数据去查询云数据库,去查询相似度了。

5.1、将查询字符串转换为向量数据

那么这里就先准备一下查询云数据库的接口

var message ChatMeMessage
if err := c.Bind(&message); err != nil {// c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})// return
}
response := GetEmbeddingApi(message.Text)json.Unmarshal(response, &response)
fmt.Println(response, "response----response")
var embeddingResponse EmbeddingResponse
json.Unmarshal(response, &embeddingResponse)

这里其实就是通过go语言调用restful 接口 https://api.openai.com/v1/embeddings 请求

这里注意一下,我理解的正常的话只要云数据库有数据,就会返回数据的,无非相似度低一些而已。

5.2、根据向量来查询匹配相关性高的前三条记录

准备查询参数数据,然后到Qdrant云数据库进行查询:

params := make(map[string]interface{})
params["exact"] = false
params["hnsw_ef"] = 128sr := PointSearchRequest{Params:      params,Vector:      embeddingResponse.Data[0].Embedding,Limit:       3,WithPayload: true,
}//查询相似的
res, err := SearchPoints(utils.QdrantCollectName, sr)
if err != nil {// common.Logger.Error(err.Error())// c.JSON(http.StatusOK, common.Error(err.Error()))// return
}

这里其实就是通过go语言调用restful接口:

https://ui.qdrant.tech/#/points/search_points

5.3、通过chatGPT对查询的相关性数据进行优化

其实上面查询出来数据列出来就完事了,但是我上面也说了相关性的问题,那么这里我们可以通过ChatGPT对于查询返回的数据加工一下。

//组装本地数据
localData := ""
for i, v := range res {re := v.Payload.(map[string]interface{})localData += "\n"localData += strconv.Itoa(i + 1)localData += "."localData += re["title"].(string)localData += ":"localData += re["text"].(string)
}
messages := make([]ChatCompletionMessage, 0)
q := "使用以下段落来回答问题,如果段落内容与\"" + message.Text + "\"不相关就通过查询返回信息。"
q += localDatasystem := ChatCompletionMessage{Role:    "system",Content: "你是一个医院问诊客服机器人",
}user := ChatCompletionMessage{Role:    "user",Content: q,
}messages = append(messages, system)
messages = append(messages, user)
var chatResponse = GetChatCompletionsApi(messages)
var obj map[string]interface{}
if err := json.Unmarshal(chatResponse, &obj); err != nil {panic(err)
}
fmt.Println("Body:", obj)// 最后我通过一个方法进行统一返回参数处理
return dto.SetResponseData(obj)

5.4、调试效果

这是我通过GPT-3.5模型的接口调试其返回结果并不是非常理想。但是如果通过GPT-4.0就完全可以达到我想要的结果了

当然了我这里演示的数据较少,仅用作演示效果,但是这种简单的问答模式加上最后GPT来润色优化有点好用了。

而且还可以进行优化,比如,问的问题是本地没有的,通过GPT回答后,可以进行操作,将当前问答回写到本地云数据库,这样下次再有类似的问答,就可以直接使用本地的数据了,这里仅仅提供一点点的我思考的逻辑,不一定是对的。

6、总结

这个对于我来说,理解起来还是蛮费劲的,主要是一开始没有抓到重点,其实现在把思路捋顺了,从应用的层面来看也就那么回事,当然了目前我的理解还是比较浅显的,有待机会进一步深入摸索,大数据训练模型。是不是可以考虑训练一个自己的AI虚拟人。当然还有另外一个Fine-Tunes 跟Embedding有没有关系,我得继续研究研究了,感觉上还是非常好玩的。

本文主要参考:https://github.com/coderabbit214/document-aihttps://github.com/coderabbit214/document-ai也感谢大佬的及时回复解答我的疑惑。

博客:vue.tuokecat.com/blog

github:github.com/aehyok

我的前端项目:pnpm + monorepo + qiankun + vue3 + vite3 + 工具库、组件库 + 工程化 + 自动化
不断完善中,整体框架都有了
在线预览:vue.tuokecat.comhttp://vue.tuokecat.com
github源码:https://github.com/aehyok/vue-qiankunhttps://github.com/aehyok/vue-qiankun

参考资料

https://juejin.cn/post/7212616585768370235

【ChatGPT】从零开始构建基于ChatGPT的嵌入式(Embedding) 本地(Local) 智能客服问答机器人模型相关推荐

  1. 我开发了一个温柔的智能客服聊天机器人ChatBot,并回答为什么不是ChatGPT(附思路和代码)

    前言 若问2023年科技领域什么最火,那当然是ChatGPT了,这么智能的对话机器人,给人带来无限的想象,围绕着ChatpGPT的各种热点和创意层出不穷.作为一个多年从事编程开发的程序员,我对于这么大 ...

  2. 从零开始构建基于textcnn的文本分类模型(上),word2vec向量训练,预训练词向量模型加载,pytorch Dataset、collete_fn、Dataloader转换数据集并行加载

    伴随着bert.transformer模型的提出,文本预训练模型应用于各项NLP任务.文本分类任务是最基础的NLP任务,本文回顾最先采用CNN用于文本分类之一的textcnn模型,意在巩固分词.词向量 ...

  3. ChatGPT在智能客服产品落地探讨

    AI语言模型中的ChatGPT近期在互联网平台上引起了广泛的讨论.那么,如果想将这个大型语言模型应用在智能客服产品中,或者将其在ToB SaaS应用软件领域落地,应该采用哪种构建策略? 现在ChatG ...

  4. 得ChatGPT者,得智能客服天下?

    ‍数据智能产业创新服务媒体 --聚焦数智 · 改变商业 在现代社会,高效.专业的客服服务已成为企业.组织机构竞争力的关键要素.智能客服系统应运而生,智能客服系统对客服的赋能作用和价值主要表现在提高效率 ...

  5. 智能客服 | 浅谈人工智能聊天机器人ChatGPT

    2022年底,OpenAI的预训练模型ChatGPT给人工智能领域的爱好者和研究人员留下了深刻的印象和启发,他展现的惊人能力将人工智能的研究和应用热度推向高潮,网上也充斥着和ChatGPT的各种聊天, ...

  6. 基于法律罪行知识图谱的智能预判与客服问答

    CrimeKgAssitant Crime assistant including crime type prediction and crime consult service based on n ...

  7. 从零开始搭建智能客服

    从零开始搭建智能客服 近年来,伴随着消费升级和企业服务意识强化,大量人工客服需求应运而生.第四范式不久前对外免费开放智能客服平台(链接:https://bot.4paradigm.com) , 帮助各 ...

  8. 报名啦!阿里云智能客服对话式AI算法大赛之知识图谱构建与问答

    看为推动知识图谱与语义计算技术发展及应用落地,阿里云智能客服 CATC(Conversational AI Technology Challenge)大赛系列一"CCKS 2021 知识图谱 ...

  9. 15年研发经验博士手把手教学:从零开始搭建智能客服

    转载自:http://www.sohu.com/a/228122295_355140 近年来,伴随着消费升级和企业服务意识强化,大量人工客服需求应运而生.第四范式不久前对外免费开放智能客服平台(链接: ...

最新文章

  1. hbuilderX安装git插件→拉取线上gitlab项目
  2. 视频+课件| PointDSC:基于特征匹配的点云配准方法(CVPR2021)
  3. Python学习笔记(二):标准流与重定向
  4. Java中的static
  5. android IntentService
  6. 趣谈设计模式 | 桥接模式(Bridge):将抽象与实现分离
  7. vins-mono后端优化
  8. Team Foundation Server 2010 安装、部署与配置(六):创建 Team Project .
  9. java开闭原则代码实现_如何提高你的代码设计能力?
  10. SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)
  11. 产品经理与数据的恩怨情仇
  12. 硬件电路设计基础知识
  13. poi excel 插入批注
  14. <Healing Psoriasis The Natural Alternative>笔记(持续进行中)
  15. WARNING: Waited 15 secs for write IO to PST disk 4 in group 3 in alert_asm.log
  16. 获取电脑ip并输入微信发送
  17. 使用python玩跳一跳超详细使用教程
  18. 分布式限流的解决方案
  19. 我的世界java版启动器怎么安装mod_我的世界MOD怎么安装 手机版MOD启动器下载
  20. 【精品】IntelliJ 文件模板 创建 通用Controller

热门文章

  1. Oracle如何快速、大量的插入数据
  2. 关于input在苹果和安卓手机上调用相机和相册的问题
  3. 微信公众号自定义菜单创建接口
  4. transact sql mysql_MySQL与Transact SQL(MS SQL Server)的SQL语句区别点滴(C++)
  5. mysql stuff函数_mysql   自定义 stuff
  6. Android:证书生成
  7. laravel评价详情及商家回复api
  8. java中length和length()的区别
  9. Hoeffding不等式的证明
  10. 【小学生打字练习软件】_在线网上打字比赛软件系统