一、前言
为了更好的服务用户,提供个性化服务,帮助客户解决问题,结合当下火热的AI技术推出快捷导航功能。
其功能如下:
- 语意理解:能够理解用户的自然语言输入,而不是单纯的关键词匹配搜索。
- 个性化推荐:可以根据用户的偏好和历史行为来推荐相应的功能和信息,这使得用户可以更快地找到自己需要的功能,并更好地解决相应的问题。
什么是 RAG?
RAG(Retrieval-Augmented Generation,检索增强生成)是:
1、先“查资料”(从你的知识库中检索相关内容)
2、再“作答”(把检索结果交给大模型生成答案)
说白了:用户提问 -> 把问题转成向量去知识库检索 -> 找到最相关的若干片段
所以,RAG 的结构很像“搜索引擎 + 语言模型”的组合, 知识库构建是铺地基,检索模块就是“搜索引擎”部分。
1、知识库构建
背景:客服同学有维护一套面向客户的知识库,这块可以直接拿来使用。
-
主要问答为主
-
日常维护
在这个步骤可分为 3 个部分:
- 数据清洗:文档内容调整,其他不需要。
- 切片策略:无需再次切片,每个问答就是完整一个切片。
- Embedding选择:文档向量化。
名词解释:
| 名词 | 说明 |
|---|---|
| Embedding | 嵌入:把文本的意思嵌入向量里,用向量来表示文本的含义。音频、视频也行。 |
| Vector | 向量:- 在数学中,向量是一种表示有大小和方向的量的工具。 |
-
在这里,可以简单把向量理解为一种数据的编码方式,每个维度都可以被认为是一个特性或属性,这些特性或属性共同描述了一个数据点。 |
-
为什么转为向量:因为转向量后,这样我们就可以通过一些算法来计算文本的相似程度「比如余弦相似度(Cosine Similarity)、欧氏距离(Euclidean Distance)等等。
-
如何把文本转化成向量:转化的过程其实就是文本特征提取,要用专业名词就是词嵌入。这个过程需要特定的模型「比如词袋模型、TF-IDF等」。
2、检索模块(Retrieval)
目标:在知识库中,快速准确地找到相关的切片。
对应检索 pipeline:
-
Query 向量化:将用户的问题,变成可计算的向量。
-
召回阶段:快速找到Top K,采用 混合检索(Hybrid Search)
- 向量召回(Dense Retrieval):基于Embedding的语义相似度检索。
- 关键词召回(Sparse Retrieval):基于传统搜索技术,如BM25或倒排索引。
-
精排阶段:会把 Query 和候选文本一起输入,逐条计算相关度分数。取 Top K1,再交给 LLM 进行生成。
-
结果过滤:检查过滤,敏感词 / 相同片段去重。
混合检索(Hybrid Search)
随着数据量的增加,会存在语义相似度不可解释的问题,而且不一定会随着关键词做语义相似度的完全跟随。
只靠向量语义检索不够稳,需要“关键词检索 + 语义检索”组合起来用。
- 语义检索像“凭感觉找意思接近的内容”,很聪明,但有时说不清“为什么命中这条”。
- 当数据越来越多时,这种“感觉”会变得不稳定:你明明输入了某个关键词,它不一定严格跟着这个词去找。
- 所以要加上关键词检索这条“硬规则”链路,先把可能相关的结果尽量召回,再用语义相似度做二次排序(精排),把最像的放前面。
采用关键词召回方案:
-
1、关键词命中召回
- 用户问到的词,文档里直接出现了,就优先拉进候选集合。
- 例:搜“发票抬头”,包含这个词的文档先召回。
-
2、近义词逻辑
- 用户和文档用词不一致,但意思相近也要能找到。
- 例:“报销凭证”≈“发票”;“退款”≈“退费”。
-
3、pinyin逻辑
- 处理中文拼音输入、错别字、输入法习惯。
- 例:用户输“fapiao”,也能召回“发票”。
-
4、ngram召回
- 把词拆成连续片段匹配,提升对部分匹配、错拼、黏连词的鲁棒性。
- 例:“增值税专用发票”拆成若干片段,输入不完整时也能匹配到。
一句话总结:
- 召回阶段要“宁可多抓一些”(关键词+近义词+pinyin+ngram)
- 排序阶段再“优中选优”(语义相似度精排)
这样效果通常比“只用向量”更稳定、更可解释。
二、工程落地
组件模块划分:
- 向量数据库采用:Elasticsearch。
- 知识库构建:每日定时获取文档,文档向量化,存储。
- 检索服务:交互 chatgpt、ES。
查询交互时序图如下:
ES 索引配置如图:
PUT /vector_query
{
"settings":{
"number_of_shards":1,
"number_of_replicas":1
},
"properties": {
"category": {
"type": "keyword"
},
"main_path": {
"type": "keyword"
},
"sub_path": {
"type": "keyword"
},
"title": {
"type": "text"
},
"doc": {
"type": "text"
},
"title_vector": {
"type": "dense_vector",
"dims": 1536
},
"doc_vector": {
"type": "dense_vector",
"dims": 1536
}
}
}
检索 demo如图:
def get_sim_doc(inp, get_emb=get_emb, es_mpt_index=es_mpt_index, topk=10):
logger.info('get_sim_doc-' + inp)
inp_emb = get_emb(inp)
logger.info('get_sim_doc-' + 'emb success!')
data = {
"_source": {
"excludes": ["*_vector"]
},
"size": topk,
"query": {
"script_score": {
"query": {
"match_all": {}
},
"script": {
"source": "if (cosineSimilarity(params.queryVector, 'title_vector')<cosineSimilarity(params.queryVector, 'doc_vector')) {return cosineSimilarity(params.queryVector, 'doc_vector')+1}return cosineSimilarity(params.queryVector, 'title_vector')+1",
"params": {
"queryVector": inp_emb
}
}
}
}
}
url = f'{es_host}/{es_mpt_index}/_search'
response = requests.post(url, headers=headers, json=data, auth=auth)
# print(response)
hits = response.json()['hits']
# print(json.dumps(data, ensure_ascii=False))
return hits
1、向量模型接入
有两种不同的向量模型接入方案:
ada 方案调用:通常指调用 OpenAI 的 embedding(历史上常叫 ada,如text-embedding-ada-002,现在更多用text-embedding-3-*)。bge 方案调用:指调用 BGE 系列 embedding(BAAI 开源模型,如bge-large-zh、bge-m3),一般是你自部署或走第三方推理服务。
接入的3个模型对比:
| 维度 | BGE | 文心一言 | OpenAI |
|---|---|---|---|
| 私有化部署 | 强(可完全自建) | 中(看产品形态) | 弱(通常云API) |
| 中文检索 | 强 | 强 | 中-强 |
| 生成能力 | 中(看搭配的LLM) | 强 | 强 |
| 工程复杂度 | 高 | 低-中 | 低-中 |
| 成本可控性 | 强(规模化后) | 中 | 中-弱 |
其他因素:
- 法务要求:优先国内模型
- 稳定性:考虑openai服务的不确定性,时常异常,暂时未做压测;(2023年12月份)
三、应用
背景:客服侧反馈店铺托管咨询量上月已达到 2300+条,4月7号单天咨询已经达到260+条,已严重影响其他正常客户的进线咨询,部分客户需要等待30分钟-1个小时后才能真正进入到人工接待,非常影响客户体验。
客户搜索店铺托管相关的关键词,跳出如何开通。
客户选择如何开通之后 展示:您好,您可点击申请立即开通(蓝色字体)按钮进行开通。
流程如下:
- 重复开通:需要判断是否已经开通,已开通提示已经开通,无需重复开通
- KYC异常:客户点击【立即开通】校验KYC状态,未完成KYC引导完成KYC
- 开通成功:客户点击【立即开通】校验 1 和 2 通过后,提示开通成功