使用 C# 和 Semantic Kernel 构建 PDF 向量搜索系统:从文本提取到语义搜索

在现代信息检索中,传统的关键词搜索已经无法满足复杂语义查询的需求。通过 Semantic Kernel,我们可以将文本数据转化为向量(Embedding),并结合向量数据库实现高效的语义搜索。本文将详细讲解如何使用 C# 构建一个 PDF 向量搜索系统,实现从 PDF 文本提取、向量化存储,到语义搜索的完整流程。


技术栈与依赖

本文示例使用以下 NuGet 包:

功能
DocumentFormat.OpenXml Office 文档操作
Microsoft.Data.Sqlite SQLite 数据库操作
Microsoft.Extensions.AI AI SDK 接口,支持嵌入生成
Microsoft.Extensions.AI.Ollama Ollama 模型嵌入生成
Microsoft.Extensions.VectorData.Abstractions 向量存储抽象接口
Microsoft.SemanticKernel.Connectors.Sqlite Semantic Kernel 与 SQLite 的向量存储连接
PdfPig PDF 文本抽取

这些依赖允许我们完成从 PDF 文档读取、文本切块、生成向量、存储和检索的完整流程。


数据模型设计

首先,我们定义 PdfVector 类来描述向量数据库中的记录:

复制代码
public class PdfVector
{
    [VectorStoreRecordKey]
    public ulong Key { get; set; }

    [VectorStoreRecordData]
    public string FileName { get; set; }

    [VectorStoreRecordData]
    public string Text { get; set; }

    [VectorStoreRecordVector(384, DistanceFunction.EuclideanDistance)]
    public ReadOnlyMemory<float> Vector { get; set; }
}
  • Key:唯一 ID。

  • FileName:PDF 文件名。

  • Text:文本块。

  • Vector:文本向量表示,维度 384,使用欧氏距离计算相似度。

这些属性通过 Semantic Kernel 的特性标记,自动映射到向量存储。


初始化 SQLite 向量存储

首先创建 SQLite 数据库,并加载向量扩展 vec0.dll

复制代码
const string databasePath = "pdf_vectors.db";
if (!File.Exists(databasePath))
{
    File.Create(databasePath).Dispose();
}

using var connection = new SqliteConnection($"Data Source={databasePath};");
await connection.OpenAsync();

connection.EnableExtensions(true);
connection.LoadExtension("./extensions/vec0.dll"); // 确保 vec0.dll 在项目目录

创建向量集合:

复制代码
var vectorStore = new SqliteVectorStore(connection);
var pdfsCollection = vectorStore.GetCollection<ulong, PdfVector>("pdfs");
await pdfsCollection.CreateCollectionIfNotExistsAsync();
  • SqliteVectorStore 用于管理向量数据。

  • pdfs 集合用于存储 PDF 文本向量。


PDF 文本抽取与切块

为了向量化,我们需要先从 PDF 中提取文本,并将其切成适合的块:

复制代码
static List<string> ExtractPdfChunks(string filePath, int chunkSize = 500)
{
    var textBuilder = new StringBuilder();

    using var pdf = UglyToad.PdfPig.PdfDocument.Open(filePath);
    foreach (var page in pdf.GetPages())
        textBuilder.AppendLine(page.Text);

    string fullText = textBuilder.ToString();

    var chunks = new List<string>();
    for (int i = 0; i < fullText.Length; i += chunkSize)
    {
        int length = Math.Min(chunkSize, fullText.Length - i);
        chunks.Add(fullText.Substring(i, length));
    }

    return chunks;
}
  • 使用 PdfPig 打开 PDF。

  • 将每页文本拼接成完整文本。

  • 按固定长度(默认 500 字符)切分成块,便于向量化。


使用 Ollama 生成文本向量

我们使用 OllamaEmbeddingGenerator 将文本块转成向量:

复制代码
IEmbeddingGenerator<string, Embedding<float>> generator =
    new OllamaEmbeddingGenerator(new Uri("http://localhost:11434/"), "all-minilm:latest");
  • 连接到本地 Ollama 服务。

  • "all-minilm:latest" 是嵌入模型。

  • GenerateEmbeddingVectorAsync 将文本块生成浮点向量。


存储向量到数据库

遍历 PDF 文件并存储向量:

复制代码
ulong keyCounter = 0;

foreach (var file in pdfFiles)
{
    var chunks = ExtractPdfChunks(file);

    foreach (var chunk in chunks)
    {
        var vector = await generator.GenerateEmbeddingVectorAsync(chunk);
        var record = new PdfVector
        {
            Key = keyCounter++,
            FileName = Path.GetFileName(file),
            Text = chunk,
            Vector = vector
        };
        await pdfsCollection.UpsertAsync(record);
        Console.WriteLine($"Upserted chunk from {record.FileName}");
    }
}
  • 每个文本块生成向量。

  • 创建 PdfVector 对象并插入/更新数据库。


向量搜索示例

向量搜索可以直接返回语义相关的 PDF 文本块:

复制代码
Console.WriteLine("Enter your question:");
var query = Console.ReadLine();
var queryEmbedding = await generator.GenerateEmbeddingVectorAsync(query);

var searchOptions = new VectorSearchOptions
{
    Top = 3,
    VectorPropertyName = "Vector"
};

var results = await pdfsCollection.VectorizedSearchAsync(queryEmbedding, searchOptions);

await foreach (var result in results.Results)
{
    Console.WriteLine($"File: {result.Record.FileName}");
    Console.WriteLine($"Text: {result.Record.Text}");
    Console.WriteLine($"Score: {result.Score}");
    Console.WriteLine(new string('-', 50));
}
  • 将用户输入的查询文本转为向量。

  • 使用 VectorizedSearchAsync 查询最相似的文本块。

  • 输出文件名、文本和相似度评分。


总结

通过这篇文章,你学会了如何使用 C#Semantic Kernel

  1. 从 PDF 提取文本。

  2. 对文本进行切块。

  3. 使用 Ollama 模型生成文本向量。

  4. 使用 SQLite 向量存储管理向量数据。

  5. 基于向量实现语义搜索。

这个系统可扩展性强,例如:

  • 支持 DOCX、TXT 等多种文件。

  • 可以将向量存储迁移到 Postgres、FAISS 或 Milvus。

  • 可结合大语言模型回答问题,实现 PDF 问答机器人。

相关推荐
星云数灵1 天前
大模型高级工程师考试练习题1
人工智能·大模型·大模型工程师·大模型考试题·大模型工程师练习题·大模型高频考题
草莓熊Lotso1 天前
Python 进阶核心:字典 / 文件操作 + 上下文管理器实战指南
数据结构·c++·人工智能·经验分享·笔记·git·python
乐迪信息1 天前
乐迪信息:目标检测算法+AI摄像机:煤矿全场景识别方案
人工智能·物联网·算法·目标检测·目标跟踪·语音识别
学术小白人1 天前
【EI会议征稿通知】2026年智能感知与自主控制国际学术会议(IPAC 2026)
人工智能·物联网·数据分析·区块链·能源
HyperAI超神经1 天前
在线教程丨 David Baker 团队开源 RFdiffusion3,实现全原子蛋白质设计的生成式突破
人工智能·深度学习·学习·机器学习·ai·cpu·gpu
ASKED_20191 天前
End-To-End之于推荐: Meta GRs & HSTU 生成式推荐革命之作
人工智能
liulanba1 天前
AI Agent技术完整指南 第一部分:基础理论
数据库·人工智能·oracle
自动化代码美学1 天前
【AI白皮书】AI应用运行时
人工智能
小CC吃豆子1 天前
openGauss :核心定位 + 核心优势 + 适用场景
人工智能
一瞬祈望1 天前
⭐ 深度学习入门体系(第 7 篇): 什么是损失函数?
人工智能·深度学习·cnn·损失函数