使用 .NET 10 和 AWS Bedrock 构建高可用 RAG 系统
- 作者

- 姓名
- Nino
- 职业
- Senior Tech Editor
大型语言模型(LLM)如 Claude 3.5 Sonnet 虽然具有强大的推理能力,但在企业应用中面临两大瓶颈:知识截止日期限制以及无法访问私有数据。传统的微调(Fine-tuning)方式成本高昂且更新缓慢。目前,**检索增强生成(RAG)**已成为解决这一问题的行业标准模式。RAG 将 LLM 视为按需推理引擎而非静态数据库:系统首先从向量库中检索相关文档片段,将其作为上下文注入 Prompt,从而让模型基于事实进行回答。
在本指南中,我们将使用最新的 .NET 10、微软的 Microsoft.Extensions.AI 抽象库以及 AWS Bedrock 构建一个端到端的 RAG 服务。对于希望简化多模型集成、追求极速响应的企业,n1n.ai 提供了统一的 API 网关,是替代复杂云端架构的理想选择。
技术栈选型
- 运行时:.NET 10 Minimal APIs。
- AI 抽象层:使用
Microsoft.Extensions.AI实现供应商无关的接口调用。 - 向量嵌入:Amazon Titan Text Embeddings G1,生成 1536 维度的稠密向量。
- 推理模型:Anthropic Claude 3.5 Sonnet。
- 向量存储:OpenSearch(使用 HNSW 算法和余弦相似度)。
为什么选择 .NET 10 与 Microsoft.Extensions.AI?
.NET 10 进一步增强了 AI 生态系统的标准化。通过 IEmbeddingGenerator 和 IChatClient 接口,开发者可以轻松地在 AWS Bedrock、Azure OpenAI 或 n1n.ai 之间切换,而无需重写核心业务逻辑。这种解耦能力对于需要长期维护的企业级项目至关重要。
AWS 环境准备
在 AWS 控制台中(推荐 us-east-1 区域),确保已开通以下模型访问权限:
- Amazon Titan Text Embeddings G1 - Text
- Anthropic Claude 3.5 Sonnet
对于本地开发,我们可以通过 Docker 快速启动一个禁用了安全插件的 OpenSearch 实例:
docker run -d -p 9200:9200 -p 9600:9600 \
-e "discovery.type=single-node" \
-e "plugins.security.disabled=true" \
--name opensearch-rag \
opensearchproject/opensearch:2.11.0
第一步:项目初始化与依赖安装
创建一个新的 Web API 项目并添加必要的 NuGet 包:
dotnet new webapi -n DotnetRagAws
cd DotnetRagAws
dotnet add package Microsoft.Extensions.AI
dotnet add package AWSSDK.BedrockRuntime
dotnet add package OpenSearch.Client
第二步:实现向量生成器
我们需要将 AWS Bedrock 的 Titan 模型封装进 IEmbeddingGenerator。以下是核心实现:
public class BedrockEmbeddingGenerator : IEmbeddingGenerator<string, Embedding<float>>
{
private readonly IAmazonBedrockRuntime _bedrockClient;
public BedrockEmbeddingGenerator(IAmazonBedrockRuntime bedrockClient)
{
_bedrockClient = bedrockClient;
}
public async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
IEnumerable<string> values,
EmbeddingGenerationOptions? options = null,
CancellationToken ct = default)
{
var results = new List<Embedding<float>>();
foreach (var text in values)
{
var body = JsonSerializer.Serialize(new { inputText = text });
var request = new InvokeModelRequest
{
ModelId = "amazon.titan-embed-text-v1",
ContentType = "application/json",
Body = new MemoryStream(Encoding.UTF8.GetBytes(body))
};
var response = await _bedrockClient.InvokeModelAsync(request, ct);
using var reader = new StreamReader(response.Body);
var json = await reader.ReadToEndAsync();
var output = JsonSerializer.Deserialize<TitanOutput>(json);
results.Add(new Embedding<float>(output!.Embedding));
}
return new GeneratedEmbeddings<Embedding<float>>(results);
}
private record TitanOutput([property: JsonPropertyName("embedding")] float[] Embedding);
public void Dispose() { }
public object? GetService(Type t, object? k) => null;
public EmbeddingGeneratorMetadata Metadata => new("BedrockTitan");
}
第三步:配置向量索引
在 OpenSearch 中,我们需要创建一个支持 k-NN 搜索的索引。1536 维是 Titan 模型的标准输出维度。使用余弦相似度(Cosine Similarity)能更好地处理文本语义的相似性。
public async Task EnsureIndexExists(IOpenSearchClient client)
{
var settings = new IndexSettings();
settings.Add("index.knn", true);
await client.Indices.CreateAsync("knowledge-base", c => c
.Settings(s => s.Setting("index.knn", true))
.Map<DocumentChunk>(m => m
.Properties(p => p
.KnnVector(v => v
.Name(n => n.Embedding)
.Dimension(1536)
.Method(mi => mi
.Name("hnsw")
.Engine("nmslib")
.SpaceType("cosinesimil")))
)
)
);
}
第四步:构建 RAG 编排服务
编排服务负责连接检索和生成两个环节。我们通过系统提示词(System Prompt)建立“护栏”,防止模型产生幻觉。
public class RagService
{
private readonly IEmbeddingGenerator<string, Embedding<float>> _generator;
private readonly IChatClient _chatClient;
private readonly OpenSearchVectorStore _store;
public async Task<string> QueryAsync(string userQuestion)
{
// 1. 将用户问题向量化
var embedding = await _generator.GenerateAsync(new[] { userQuestion });
// 2. 检索最相关的 3个文档片段
var chunks = await _store.SearchAsync(embedding.First().Vector.ToArray(), k: 3);
// 3. 构建上下文增强的 Prompt
var contextText = string.Join("\n---\n", chunks.Select(c => c.Content));
var systemMessage = "你是一个严谨的技术助手。请仅根据提供的上下文回答问题。如果无法从上下文中找到答案,请诚实说明。";
var userMessage = $"上下文:\n{contextText}\n\n问题:{userQuestion}";
// 4. 调用 Claude 3.5 Sonnet 生成回答
var response = await _chatClient.GetResponseAsync(new ChatMessage[] {
new(ChatRole.System, systemMessage),
new(ChatRole.User, userMessage)
});
return response.Text;
}
}
进阶优化策略
- 混合搜索(Hybrid Search):纯向量搜索在处理特定术语(如特定的错误代码或 ID)时可能表现不佳。建议结合 OpenSearch 的 BM25 关键词搜索和 k-NN 搜索,通过倒排索引提高召回率。
- 弹性与重试机制:云端 API 可能会遇到频率限制(Throttling)。使用 n1n.ai 提供的 API 可以获得更稳定的并发支持,或者在 .NET 中使用 Polly 实现指数退避重试。
- 文档分块(Chunking)优化:简单的字符计数分块会破坏语义。建议使用滑动窗口(Sliding Window)模式,例如每块 500 字符,重叠 50 字符,以保持上下文连贯性。
总结
通过 .NET 10 的标准化接口,开发者可以构建出既灵活又强大的 AI 应用。AWS Bedrock 提供了顶级的模型能力,而 OpenSearch 则充当了可靠的长期记忆库。如果您希望在生产环境中进一步提升多模型调度的效率,n1n.ai 是一个非常值得尝试的高性能聚合平台。
立即在 n1n.ai 获取免费 API 密钥。