C# 文件内容语义搜索 C#如何结合向量数据库实现文件的语义搜索

6次阅读

应先清洗分块再向量化:pdf用pdfpig提取文本,recursivecharactertextsplitter切块(512/64),每块单独embed;选qdrant作向量库,建集时设size=1536、distance.cosine,插入带payload,查询时searchrequest需显式启用withpayload=true、scorethreshold=0.3。

C# 文件内容语义搜索 C#如何结合向量数据库实现文件的语义搜索

OpenAIembedding 生成文件内容向量,别直接喂原始文本

文件语义搜索的核心不是“搜关键词”,而是把文件内容转成能比较的向量。C# 里最直接的路是用 OpenAIEmbedding(比如 text-embedding-3-small),但很多人一上来就拿整篇 PDF 或 word 的原始字符串丢进去——结果要么超长报错 400 Bad Request: invalid_request_error,要么向量质量差、检索不准。

真正该做的,是先做内容清洗和分块:

  • PDF 文件用 PdfPigIronPdf 提取纯文本,跳过页眉页脚、表格结构、页码
  • 按语义切分:用 RecursiveCharacterTextSplitter(来自 langchain.Chains)按句号/换行切,chunkSize=512chunkOverlap=64 是较稳的起点
  • 每块文本送进 CreateEmbeddingAsync,别拼成大段再 embed —— OpenAI 对单次输入长度有限制,且语义粒度会糊掉

Qdrant 而不是 elasticsearch 做向量库,除非你已有 ES 且启用了 knn

C# 生态里对接向量数据库Qdrant 是目前最省心的选择。它原生支持点积/余弦距离、Filter + search 混合查询、http/gRPC 双协议,C# 客户端 Qdrant.Client封装干净。而 Elasticsearch 虽然熟悉,但它的 knn 搜索在 8.x 后才稳定,且需要单独开 vector 字段、设 index_options,稍不注意就查不到结果。

关键配置差异:

  • 建集合时,Qdrant 必须指定 VectorParamsSize(比如 1536)和 Distance(推荐 Distance.Cosine
  • 插入数据前,确保每条记录带 payload:至少含 source_file_pathchunk_indextext_preview,否则搜到结果根本不知道来自哪份文件哪一段
  • 查询时用 SearchRequest,别用 ScrollQuery——后者不走向量索引

SearchAsync 返回结果没排序?检查 WithPayloadWithVector 是否漏设

QdrantClient.SearchAsync 后发现结果 Score 全是 0,或者顺序乱、不按相似度降序排——大概率是忘了在请求里显式启用 payload 和 score 解析。

Qdrant.Client 默认不返回 payload,也不保证 Result 列表按 score 排。必须写全:

new SearchRequest {     Vector = embedding,     Limit = 5,     WithPayload = true, // 不加这句,payload 是 null     WithVector = false, // 一般不用返回向量本身,关掉省带宽     ScoreThreshold = 0.3f // 过滤掉明显不相关的(cosine 距离下 0.7 以上才较可信) }

另外,Score 在 cosine 场景下是 [−1, 1] 区间,值越接近 1 越相似;如果用的是 Distance.Euclidean,那 score 是距离值,越小越好——别看名字想当然。

本地调试时别用 localhost:6333 直连 Qdrant,先确认 docker 容器网络可通

windows 上用 Docker Desktop 起 qdrant/qdrant,C# 程序跑在宿主机,连 http://localhost:6333 却超时或返回 Connection refused,不是代码问题,是网络没通。

常见断点位置:

  • Docker Desktop 设置里关了 Use the WSL 2 based engine?开了反而容易 DNS 解析失败,建议关掉并重启 Docker
  • 容器启动命令漏了 -p 6333:6333,或者 Windows 防火墙拦了 6333 端口
  • curl http://localhost:6333/health 能通,但 C# 报错?检查 HttpClient 是否设了 Timeout 太短(默认 100 秒够用),以及是否用了 http://127.0.0.1:6333(某些 .NET 版本对 localhost 解析更稳)

向量搜索真正难的不是调通 API,而是让 chunk 有区分度、让 filter 条件能和向量 query 同时生效、还有 embedding 模型更新后旧向量不能混查——这些都得在第一次入库时就想好版本标记和元数据结构

text=ZqhQzanResources