使用 OllamaSharp 在 .NET 中实现简单的 RAG
本文将带你一步步使用 OllamaSharp 和 CommunityToolkit.VectorData.InMemory 在 .NET 10 中构建一个简单的 RAG 系统。我们将从 NuGet 包安装开始,到向量存储、相似度搜索,最终实现一个基于本地 Ollama 模型的智能问答系统。
目录
- 环境准备与 NuGet 包安装
- 定义数据模型 CloudData
- Step 1:为知识库生成嵌入向量
- Step 2:将嵌入向量存储到向量库
- Step 3:对用户查询生成嵌入向量
- Step 4:向量相似度搜索
- 完整代码清单
1. 环境准备与 NuGet 包安装
1.1 前置条件
| 组件 | 版本要求 |
|---|---|
| .NET SDK | 10.0 或更高 |
| Ollama | 本地安装并运行(默认端口 11434) |
| 嵌入模型 | 如 qwen3-embedding:0.6b(通过 ollama pull 下载) |
1.2 安装 Ollama
前往 ollama.com 下载并安装 Ollama,然后拉取嵌入模型:
ollama pull qwen3-embedding:0.6b
1.3 创建项目并安装 NuGet 包
本项目需要安装以下 4 个 NuGet 包:
# OllamaSharp — Ollama 的 .NET 客户端库,支持聊天、嵌入、模型管理等
dotnet add package OllamaSharp --version 5.4.30
# CommunityToolkit.VectorData.InMemory — 内存向量数据库,用于存储和检索嵌入向量
dotnet add package CommunityToolkit.VectorData.InMemory --version 1.0.0
# Microsoft.Extensions.Configuration.Json — 读取 JSON 配置文件
dotnet add package Microsoft.Extensions.Configuration.Json --version 10.0.8
# Microsoft.Agents.AI.OpenAI — 提供 AI 抽象层(VectorStore 等基础接口)
dotnet add package Microsoft.Agents.AI.OpenAI --version 1.9.0
1.4 各包的作用说明
| NuGet 包 | 作用 |
|---|---|
OllamaSharp |
与本地 Ollama 服务通信,调用嵌入模型和聊天模型 |
CommunityToolkit.VectorData.InMemory |
提供内存中的向量存储,支持相似度搜索 |
Microsoft.Extensions.Configuration.Json |
从 llama.json 读取模型配置 |
Microsoft.Agents.AI.OpenAI |
提供 VectorStore、EmbeddingGenerator 等统一接口 |
安装后的 .csproj 文件如下:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.VectorData.InMemory" Version="1.0.0" />
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.9.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.8" />
<PackageReference Include="OllamaSharp" Version="5.4.30" />
</ItemGroup>
<ItemGroup>
<None Update="llama.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
2. 定义数据模型 CloudData
向量存储中的每条记录需要是一个带有特定特性的类。创建 Models/CloudData.cs:
using Microsoft.Extensions.VectorData;
namespace study_RAG.Models
{
internal class CloudData
{
// 主键 — 向量存储中每条记录的唯一标识
[VectorStoreKey]
public int Key { get; set; }
// 数据字段 — 会被存储但不参与向量计算
[VectorStoreData]
public string Name { get; set; }
[VectorStoreData]
public string Description { get; set; }
// 向量字段 — 存储嵌入向量
// dimensions: 384 表示向量维度,必须与嵌入模型输出维度一致
// DistanceFunction.CosineSimilarity 表示使用余弦相似度计算距离
[VectorStoreVector(
dimensions: 384,
DistanceFunction = DistanceFunction.CosineSimilarity)]
public ReadOnlyMemory<float> Vector { get; set; }
}
}
关键特性说明
| 特性 | 作用 |
|---|---|
[VectorStoreKey] |
标记主键字段,用于唯一标识记录 |
[VectorStoreData] |
标记数据字段,存储在向量库中但不参与向量计算 |
[VectorStoreVector] |
标记向量字段,dimensions 必须与嵌入模型维度匹配 |
DistanceFunction.CosineSimilarity |
使用余弦相似度(值越接近 1 越相似) |
3. Step 1:为知识库生成嵌入向量
嵌入(Embedding) 是将文本转换为高维浮点数向量的过程。语义相近的文本在向量空间中距离更近。
// 从配置读取模型 ID
var modelId = configuration["ollama_model_id"]!.ToString();
// 创建 Ollama 客户端
var ollamaClient = new OllamaApiClient(
new Uri("http://localhost:11434"), modelId);
// 准备知识库数据
List<CloudData> cloudDatas = new()
{
new() {
Key = 0,
Name = "Azure App Service",
Description = "Host .NET, Java, Node.js, and Python web applications..."
},
new() {
Key = 1,
Name = "Azure Service Bus",
Description = "A fully managed enterprise message broker..."
},
new() {
Key = 2,
Name = "Azure Blob Storage",
Description = "Azure Blob Storage allows your applications to store and retrieve files in the cloud..."
},
// ... 更多服务
};
// 提取所有描述文本
var descriptions = cloudDatas.ConvertAll(cs => cs.Description);
// 调用 Ollama 嵌入 API 生成向量
var embResponse = await ollamaClient.EmbedAsync(new EmbedRequest
{
Dimensions = 384, // 向量维度
Input = descriptions, // 要嵌入的文本列表
Model = modelId // 使用的嵌入模型
});
关键参数
Dimensions = 384:指定输出向量的维度,必须与CloudData中定义的向量维度一致。Input:可以一次传入多个文本,Ollama 会批量处理,返回对应的嵌入向量数组。
4. Step 2:将嵌入向量存储到向量库
使用 InMemoryVectorStore 将生成的向量持久化到内存集合中:
// 创建内存向量存储
var vectorStore = new InMemoryVectorStore();
// 获取/创建名为 "cloudDatas" 的集合,键类型为 int,记录类型为 CloudData
var collection = vectorStore.GetCollection<int, CloudData>("cloudDatas");
// 确保集合存在(首次调用时创建)
await collection.EnsureCollectionExistsAsync();
// 将原始数据与嵌入向量组合,构建完整的记录列表
var records = new List<CloudData>();
for (int i = 0; i < cloudDatas.Count; i++)
{
records.Add(new CloudData
{
Key = cloudDatas[i].Key,
Name = cloudDatas[i].Name,
Description = cloudDatas[i].Description,
Vector = embResponse.Embeddings[i] // 对应第 i 条描述的嵌入向量
});
}
// 批量插入/更新到向量库
await collection.UpsertAsync(records);
核心 API 说明
| API | 作用 |
|---|---|
InMemoryVectorStore() |
创建内存中的向量数据库(进程退出后数据丢失) |
GetCollection<TKey, TRecord>("name") |
按名称和类型获取集合 |
EnsureCollectionExistsAsync() |
确保集合存在,不存在则创建 |
UpsertAsync(records) |
批量插入或更新记录 |
5. Step 3:对用户查询生成嵌入向量
将用户的问题也转换为向量,以便在向量空间中进行相似度比较:
// 用户查询
string query = "我应该使用哪个服务来存储word文档?";
// 对查询文本生成嵌入向量
var queryEmbResponse = await ollamaClient.EmbedAsync(new EmbedRequest
{
Dimensions = 384,
Input = new List<string> { query },
Model = modelId
});
// queryEmbResponse.Embeddings[0] 就是查询的向量表示
重要:查询和知识库必须使用同一个嵌入模型,否则向量空间不一致,搜索结果无意义。
6. Step 4:向量相似度搜索
在向量库中搜索与查询向量最相似的记录:
// 搜索最相似的 top 2 条记录
var searchResults = collection.SearchAsync(
queryEmbResponse.Embeddings[0], // 查询向量
top: 3 // 返回前 3 个最相似的结果
);
// 遍历搜索结果
await foreach (var result in searchResults)
{
Console.WriteLine($"Name: {result.Record.Name}");
Console.WriteLine($"Description: {result.Record.Description}");
Console.WriteLine($"Vector match score: {result.Score}");
Console.WriteLine();
}
搜索结果说明
| 属性 | 含义 |
|---|---|
result.Record |
匹配到的完整记录(包含 Name、Description 等) |
result.Score |
相似度分数(余弦相似度,范围 -1 到 1,越接近 1 越相似) |
7. 完整代码清单
以下是 Program.cs 的完整实现:
using CommunityToolkit.VectorData.InMemory;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.VectorData;
using OllamaSharp;
using OllamaSharp.Models;
using OllamaSharp.Models.Chat;
using System.Text;
// ========== 配置初始化 ==========
var configuration = new ConfigurationBuilder()
.AddJsonFile("llama.json", false, false)
.Build();
// ========== 知识库数据 ==========
List<CloudData> cloudDatas =
[
new() {
Key = 0,
Name = "Azure App Service",
Description = "Host .NET, Java, Node.js, and Python web applications and APIs in a fully managed Azure service."
},
new() {
Key = 1,
Name = "Azure Service Bus",
Description = "A fully managed enterprise message broker supporting both point to point and publish-subscribe integrations."
},
new() {
Key = 2,
Name = "Azure Blob Storage",
Description = "Azure Blob Storage allows your applications to store and retrieve files in the cloud."
},
new() {
Key = 3,
Name = "Microsoft Entra ID",
Description = "Manage user identities and control access to your apps, data, and resources."
},
new() {
Key = 4,
Name = "Azure Key Vault",
Description = "Store and access application secrets like connection strings and API keys in an encrypted vault."
},
new() {
Key = 5,
Name = "Azure AI Search",
Description = "Information retrieval at scale for traditional and conversational search applications."
}
];
// ========== Ollama 客户端初始化 ==========
var modelId = configuration["ollama_model_id"]!.ToString();
var ollamaClient = new OllamaApiClient(new Uri("http://localhost:11434"), modelId);
// ========== Step 1: 为知识库生成嵌入向量 ==========
var descriptions = cloudDatas.ConvertAll(cs => cs.Description);
var embResponse = await ollamaClient.EmbedAsync(new EmbedRequest
{
Dimensions = 384,
Input = descriptions,
Model = modelId
});
// ========== Step 2: 存储到向量库 ==========
var vectorStore = new InMemoryVectorStore();
var collection = vectorStore.GetCollection<int, CloudData>("cloudDatas");
await collection.EnsureCollectionExistsAsync(); //在向量库中创建集合
var records = new List<CloudData>();
for (int i = 0; i < cloudDatas.Count; i++)
{
records.Add(new CloudData
{
Key = cloudDatas[i].Key,
Name = cloudDatas[i].Name,
Description = cloudDatas[i].Description,
Vector = embResponse.Embeddings[i]
});
}
await collection.UpsertAsync(records);
// ========== Step 3: 对查询生成嵌入向量 ==========
string query = "我应该使用哪个服务来存储word文档?";
var queryEmbResponse = await ollamaClient.EmbedAsync(new EmbedRequest
{
Dimensions = 384,
Input = new List<string> { query },
Model = modelId
});
// ========== Step 4: 相似度搜索 ==========
var context = new StringBuilder();
context.AppendLine("匹配的 Azure services:");
Console.WriteLine($"=============匹配知识库 START==============");
await foreach (var result in searchResults)
{
context.AppendLine($"- {result.Record.Name}: {result.Record.Description}");
Console.WriteLine($"Name: {result.Record.Name}----Score:{result.Score}");
}
Console.WriteLine($"=============匹配知识库 END==============\n\n");
Console.WriteLine($"根据匹配到的信息构建查询\n\n");
// 构建带上下文的 Prompt
var prompt = $"""
请根据以下参考资料回答用户问题:
{context}
用户问题:{query}
回答:
""";
// 调用聊天模型生成回答
var chatResponse = ollamaClient.ChatAsync(new ChatRequest
{
Model = "qwen3.5:2b", // 聊天模型
Messages = new List<Message>
{
new(OllamaSharp.Models.Chat.ChatRole.User, prompt)
}
});
await foreach (var res in chatResponse)
{
if (!string.IsNullOrWhiteSpace(res.Message.Content))
{
Console.Write(res.Message.Content);
}
else if(!string.IsNullOrWhiteSpace(res.Message.Thinking))
{
Console.Write(res.Message.Thinking);
}
}
注:本示例使用内存向量存储,适合学习和原型验证。生产环境请使用支持持久化的向量数据库,并注意嵌入模型维度与
VectorStoreVector特性中dimensions参数的一致性。

浙公网安备 33010602011771号