1. 为什么需要使用搜索增强技术
点击睁开
- 机器图纸标题搜索的挑战:
- 机器图纸标题通常包罗专业术语、缩写和特定格式(如“土豆分拣机 DWG-001 不锈钢”)。
- 用户查询可能含糊或表述不同(如“马铃薯筛选机”),传统搜索难以匹配语义相似的标题。
- 标题信息有限,传统搜索轻易遗漏相干图纸或返回无关结果。
- 搜索增强的上风:
- 语义理解:通过大模型天生语义嵌入向量,理解标题和查询的深层含义,支持含糊匹配和语义相干性排序。
- 示例:用户搜索“土豆分拣机”,传统搜索只能匹配标题中包罗“土豆分拣机”的图纸;增强搜索可匹配语义相似的标题,如“马铃薯筛选机”,因为 AI 模型能够理解“土豆”和“马铃薯”是同义词,“分拣”和“筛选”是近义词。
- 多维度匹配:结合标题中的专业术语和元数据(如质料、尺寸),提升搜索的准确性和全面性。
- 高效索引:使用向量存储(如 Redis)支持快速的相似度搜索,满足实时性需求。
- 用户体验提升:返回更相干、更精准的图纸标题结果,减少用户反复调解查询的成本。
1.1 体验
微信小程序名称 极客共享 输入搜索内容 有没有土豆分拣机
点击睁开
1.2 与传统全文检索(Elasticsearch)的对比
点击睁开维度传统全文检索(Elasticsearch)搜索增强(基于语义向量)技术原理基于倒排索引和关键词匹配,依靠分词和词频统计(如 BM25)。基于大模型天生语义嵌入向量,使用向量相似度(如余弦相似度)匹配。语义理解仅匹配关键词,缺乏语义理解。理解标题和查询的语义,支持含糊匹配和同义词匹配。查询灵活性用户查询需与标题关键词高度一致,否则结果不准确。支持含糊查询和不同表述的匹配(如“土豆分拣机”匹配“马铃薯筛选机”)。专业术语处理依靠分词器,专业术语可能被错误切分(如“土豆分拣机”被切为“土豆”和“分拣机”)。通过预训练模型理解专业术语和同义词的语义,减少分词错误。结果相干性基于词频和位置排序,可能返回无关结果。基于语义相似度排序,结果更相干。实时性与性能倒排索引查询速度快,但语义匹配需额外插件(如 Elasticsearch KNN)。向量搜索需高效索引(如 RedisSearch),实时性稍逊但可优化。适用场景得当关键词明白、标题格式标准化的场景。得当标题复杂、查询含糊或需语义理解的场景。机器图纸标题搜索示例查询“土豆分拣机”,仅匹配标题中包罗“土豆分拣机”的图纸,遗漏“马铃薯筛选机”。查询“土豆分拣机”,可匹配语义相似的标题,如“马铃薯筛选机”,因为 AI 模型理解“土豆”和“马铃薯”、“分拣”和“筛选”是同义词。
- 总结:
- 传统全文检索(Elasticsearch)得当关键词明白、标题格式标准化的场景,但对机器图纸标题的语义理解能力有限,轻易遗漏相干结果(如“马铃薯筛选机”)。
- 搜索增强通过语义向量匹配,解决了含糊查询、专业术语处理和同义词匹配的问题,特殊得当机器图纸标题搜索的复杂场景。
1.3 搜索增强的含义
- 搜索增强的定义:
- 搜索增强是指通过引入语义理解、向量嵌入等技术,改进传统搜索的范围性,提升搜索结果的相干性和准确性。
- 在机器图纸标题搜索中,搜索增强通过大模型(如 Sentence-Transformers)将标题文本转化为语义向量,支持基于语义的相似度匹配,而不但仅依靠关键词匹配。
- 核心上风:
- 理解查询和标题的语义,支持含糊匹配、同义词匹配和跨语言匹配。
- 结合图纸标题的上下文,提供更相干的搜索结果。
- 提升用户体验,减少因查询表述差别导致的搜索失败。
2. 系统架构设计
2.1 团体架构
- 前端:微信小程序 极客共享 用户输入机器图纸标题相干的查询(如“有没有土豆分拣机”)。
- 后端:
- .NET Core 应用程序,负责处理用户请求、调用 Python API 存储和搜索向量。
- Python API 服务,提供机器图纸标题的语义嵌入功能。
- 向量存储:
- 使用 Redis 存储机器图纸标题的语义向量,支持快速索引和相似度匹配。
- 数据流:
- 机器图纸标题信息 -> .NET Core -> Python API -> 返回向量 -> 存储到 Redis。
- 用户查询 -> .NET Core -> Python API -> 天生查询向量 -> Redis 搜索 -> 返回结果。
2. 实现步骤
2.1 机器图纸标题向量天生与存储
2.1.1 准备机器图纸标题数据
点击睁开
2.1.2 .NET Core 调用 Python API 天生向量
- 目标: 将标题文本和元数据发送到 Python API,获取语义嵌入向量,大模型是bge-large-zh-noinstruct_embeddings):
Python向量天生范例- from FlagEmbedding import FlagModel
- import pandas as pd
- import numpy as np
- from datasets import Dataset
- from scipy.spatial import distance
- import datetime
- import configparser
- import pymysql
-
- model = None
- def getModel():
- global model
- if model is None:
- model = FlagModel("./model",
- query_instruction_for_retrieval="Represent this sentence for searching relevant passages:",
- use_fp16=True)
- return model
- #获取向量
- def getFlagEmbedding(title):
- global model
- model = getModel()
- embedding = model.encode(title)
- return embedding
-
复制代码 .NET Core调用天生接口(其实就是普通的api请求)- /// <summary>
- /// 获取向量
- /// </summary>
- /// <param name="keyword"></param>
- /// <returns></returns>
- public async Task<double[]> GetFlagEmbedding(string keyword)
- {
- var vector = new double[] { };
- vector = null;
- try
- {
- var req = new
- {
- action = "getFlagEmbedding",
- keyword
- };
- var content = new StringContent(
- JsonSerializer.Serialize(req)
- , Encoding.UTF8, "application/json");
- var response = await _client.PostAsync(ConfigHelp.FlagSerachUrl, content);
- if (response.IsSuccessStatusCode)
- {
- var result = await response.Content.ReadAsStringAsync();
- var data = JsonSerializer.Deserialize<GetFlagEmbeddingRoot>(result);
- if (data.op)
- {
- vector = data.msg.Split(',').Select(double.Parse).ToArray();
- }
- }
- }
- catch (Exception ex)
- {
- LogUtils.Error("GetFlagEmbedding ", ex);
- }
- return vector;
- }
复制代码 2.1.3 存储向量到 Redis
- 目标:将天生的向量存储到 Redis,支持后续的相似度搜索。
搜索数据- public class RedisVectorHelp
- {
- private readonly IDatabase _db;
- private string _freefix;
- private string _indexName;
- private SearchCommands ft;
- public RedisVectorHelp(string freefix,string redisConnectionString,int dbNum=0)
- {
- var redis = ConnectionMultiplexer.Connect(redisConnectionString);
- _db = redis.GetDatabase(dbNum);
- _freefix = freefix;
- _indexName = _freefix + "_index";
- ft = new SearchCommands(_db, null);
- }
- /// <summary>
- /// 创建索引
- /// </summary>
- public void CreateFt()
- {
- var list = ft._List();
- var indexList = list.Select(result => result.ToString()).ToArray();
- //判断是否存在索引
- if (indexList.Contains(_indexName))
- {
- Console.WriteLine("Index already exists.");
- return;
- }
- ft.Create(_indexName,
- new FTCreateParams()
- .On(IndexDataType.HASH)
- .Prefix(_freefix + ":"),
- new Schema()
- .AddTextField("id")
- .AddVectorField("vector",
- VectorField.VectorAlgo.FLAT,
- new Dictionary<string, object>
- {
- ["TYPE"] = "FLOAT32",
- ["DIM"] = 1024,
- ["DISTANCE_METRIC"] = "COSINE"
- })
- );
- }
- /// <summary>
- /// 存储向量
- /// </summary>
- /// <param name="id"></param>
- /// <param name="vector"></param>
- public void StoreVectorData(string id, float[] vector)
- {
- // 构造键名
- var key = $"{_freefix}:{id}";
- VectorDom dom = new VectorDom
- {
- id = id,
- vector = vector
- };
- byte[] vectorBinary = vector.SelectMany(f => BitConverter.GetBytes(f)).ToArray();
- _db.HashSet(key, "id", dom.id);
- _db.HashSet(key, "vector", vectorBinary);
- }
- /// <summary>
- /// 向量搜索
- /// </summary>
- /// <param name="queryVector"></param>
- /// <param name="topK"></param>
- public List<string> SearchSimilarVectors(float[] queryVector, int topK = 50)
- {
- byte[] vectorQueryBinary = queryVector.SelectMany(f => BitConverter.GetBytes(f)).ToArray();
- //十六进制字符串
- //string vectorQueryBinaryStr = BitConverter.ToString(vectorQueryBinary).Replace("-", "");
- Query q = new Query($"*=>[KNN {topK} @vector $vec as score]");
- q.SortBy = "score";
- q.AddParam("vec", vectorQueryBinary);
- q.ReturnFields("id", "vector");
- q.Limit(0, topK);
- q.Dialect(2);
- var obj = ft.Search(_indexName, q);
- var docList = obj.Documents;
- var list = new List<string>();
- foreach (var doc in docList)
- {
- list.Add(doc["id"]);
- }
- return list;
- }
- }
- public class VectorDom
- {
- public string id { get; set; }
- public float[] vector { get; set; }
- }
复制代码 2.2.1 使用查询向量在 Redis 中搜索
RedisVector核心操作类- var searchVector = await GetFlagEmbedding(keyword);
- if (searchVector != null)
- {
- var queryVector = Array.ConvertAll(searchVector, x => (float)x);
- var temp = bykcsjRVHelp.SearchSimilarVectors(queryVector, 30);
- foreach (var id in temp)
- {
- if (!ids.Contains(id))
- {
- ids.Add(id);
- }
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |