NetCore + 开源大模型,文章标题搜索的全新突破

机械图纸文章标题搜索增强实现过程

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 存储机械图纸标题的语义向量,支持快速索引和相似度匹配。
  • 数据流
    1. 机械图纸标题信息 -> .NET Core -> Python API -> 返回向量 -> 存储到 Redis。
    2. 用户查询 -> .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);
}
}
}
posted @   xiecb  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示