
前言:本项目聚焦企业级AI助理系统能力升级,通过引入数据分析与MCP集成,完善前后端可视化体系,成功实现AI Copilot"数据分析(Text-to-SQL)"全链路能力,可支持多数据源结构自动发现、安全SQL执行及数据可视化呈现。同时,系统大幅扩展Agent工作流与插件系统,优化数据库结构,新增MCP Server动态接入及插件桥接机制,健全知识库/文档管理命令,升级基于Vue 3的前端聊天应用,实现流式对话、审批流、可视化组件协议与适配器等功能全覆盖。
系统采用分层架构设计,构建智能体交互、知识中枢(RAG)、数据分析(NL2SQL)和工具调用(MCP)三大核心功能模块,可高效支撑自然语言查询、跨系统操作与可视化报表生成。技术选型上,选用ASP.NET Core后端框架、Semantic Kernel AI框架及Qdrant向量数据库,支持私有化部署模式,通过严格权限控制保障数据安全,采用容器化云原生部署方案搭建覆盖企业现有系统的智能化交互层。整体而言,项目显著提升了系统智能数据分析、工具桥接及全栈一体化能力,为企业级场景落地与多智能体扩展筑牢坚实基础。
前面章节
.AI开发 1后端框架: ASP.NET Core2.AI框架: Semantic Kernerl (SK)、Agent Framework3.知识库:向量数据库(Qdrant)+关系型数据库(Post
https://blog.csdn.net/cao919/article/details/155895060
.net AI开发02 1后端框架: ASP.NET Core2.AI框架: Semantic Kernerl (SK)、Agent Framework3.知识库:向量数据库(Qdrant)+关系型数据库(Post
.net AI开发03 新增意图识别与工具选择工作流(IntentWorkflow),支持多智能体协作; 插件体系升级,支持多项目插件自动注册与工具发现; 对话历史与消息存储解耦,采用 Med
https://blog.csdn.net/cao919/article/details/156065076

第八章 引入RAG知识库与文档管理核心能力及事件总线
1.RAG 系统架构概览
2.知识库领域模型设计
3.嵌入模型选择:云端/本地
4.嵌入模型选型评估与量化版本
5.实现 RAG文件存储服务
6.消息队列集成
7.构建 RAG应用服务层
引入RAG知识库与文档管理核心能力及事件总线
新增RAG领域模型(嵌入模型、知识库、文档、切片)及其EF配置,完善数据库结构。集成RagService与EventBus,支持RabbitMQ事件驱动。实现知识库创建、文档上传API,支持本地文件存储与幂等校验。扩展通用服务接口,优化依赖注入与项目结构,修复部分注册与兼容性问题,为后续RAG检索增强功能奠定基础。

时效性缺失、私有领域空白RAG 检索增强生成技术在让LLM回答问题之前,先去外部知识库中检素相关的信息,然后将检素到的信息作为参考资料喂给LLM,让它基于资料生成答案
1.索引阶段(后台异步运行的数据处理流程 文本转换为向量==>>构建语义索引)
2.检索与生成阶段(能够在线实时响应用户请求的流程)
ETL(提取、转换、加载)流:
1.加载(格式解析、编码标准化、元数据提取)
2.分割(LLM的上下文窗口有限)递归字符分割、重叠窗口
3.嵌入(人类语言翻译成机器语言,嵌入模型==>>文本转换为高维向量==>>高维的语义空间,余弦相似度 -1~1)
4.存储(文本块内容、向量数据库、元数据==>>持久化存储、向量数据库)
1111
知识库管理状态
嵌入模型
1粉丝 南浔:为何对接各个厂家的模型请求参数基本一样,ur,appkey,他们是商量好的吗?
OpenAI事实标准
111
如何选嵌入模型
闭源厂商云端模型 API:优势:接入成本低、弹性扩展-劣势:数据隐私风险、长期成本不可控、网络延迟
开源模型本地私有化:优势:绝对的数据安全、零增量成本、高性能与低延迟劣势:硬件门槛高、维护复杂度
FP16(16位浮点数)INT8(Q8 0)==>>映射为8位整数,体积减半INT4(Q4) ==>映射为4为整数
cur http://127.0.0.1:1234/v1/embeddings\\-H"Content-Type:application/json"\\d'"model":"text-embedding-qwen3-embedding-4b","input":"Some text to embed"
111
RAG数据接入:
1.用户通过API创建知识库
2.用户上传文件(异步)
3.系统计算文件Hash(幂等性)
4.现在数据库生成文档记录
5.发送消息到RabbitMQ



聚合根
KnowledgeBase
cs
using Zilor.AICopilot.SharedKernel.Domain;
namespace Zilor.AICopilot.Core.Rag.Aggregates.KnowledgeBase;
public class KnowledgeBase : IAggregateRoot
{
private readonly List<Document> _documents = [];
protected KnowledgeBase()
{
}
public KnowledgeBase(string name, string description, Guid embeddingModelId)
{
Id = Guid.NewGuid();
Name = name;
Description = description;
EmbeddingModelId = embeddingModelId;
}
public Guid Id { get; set; }
public string Name { get; private set; } = string.Empty;
public string Description { get; private set; } = string.Empty;
/// <summary>
/// 嵌入模型ID。一个知识库内的所有文档必须使用相同的嵌入模型。
/// </summary>
public Guid EmbeddingModelId { get; private set; }
// 导航属性:对外只暴露只读集合
public IReadOnlyCollection<Document> Documents => _documents.AsReadOnly();
/// <summary>
/// 添加新文档到知识库
/// </summary>
public Document AddDocument(string name, string filePath, string extension, string fileHash)
{
var document = new Document(Id, name, filePath, extension, fileHash);
_documents.Add(document);
return document;
}
/// <summary>
/// 移除文档
/// </summary>
public void RemoveDocument(int documentId)
{
var doc = _documents.FirstOrDefault(d => d.Id == documentId);
if (doc != null)
{
_documents.Remove(doc);
}
}
public void UpdateInfo(string name, string description)
{
Name = name;
Description = description;
}
}
Document
cs
using Zilor.AICopilot.SharedKernel.Domain;
namespace Zilor.AICopilot.Core.Rag.Aggregates.KnowledgeBase;
public class Document : IEntity<int>
{
private readonly List<DocumentChunk> _chunks = [];
protected Document()
{
}
internal Document(Guid knowledgeBaseId, string name, string filePath, string extension, string fileHash)
{
KnowledgeBaseId = knowledgeBaseId;
Name = name;
FilePath = filePath;
Extension = extension;
FileHash = fileHash;
Status = DocumentStatus.Pending;
CreatedAt = DateTime.UtcNow;
}
public int Id { get; set; }
public Guid KnowledgeBaseId { get; private set; }
/// <summary>
/// 原始文件名
/// </summary>
public string Name { get; private set; } = string.Empty;
/// <summary>
/// 文件存储路径
/// </summary>
public string FilePath { get; private set; } = string.Empty;
/// <summary>
/// 文件扩展名
/// </summary>
public string Extension { get; private set; } = string.Empty;
/// <summary>
/// 文件哈希值
/// </summary>
public string FileHash { get; private set; } = string.Empty;
/// <summary>
/// 文档处理状态
/// </summary>
public DocumentStatus Status { get; private set; }
/// <summary>
/// 切片数量
/// </summary>
public int ChunkCount { get; private set; }
/// <summary>
/// 错误信息
/// </summary>
public string? ErrorMessage { get; private set; }
public DateTime CreatedAt { get; private set; }
public DateTime? ProcessedAt { get; private set; }
// 导航属性
public KnowledgeBase KnowledgeBase { get; private set; } = null!;
public IReadOnlyCollection<DocumentChunk> Chunks => _chunks.AsReadOnly();
#region 领域行为方法
/// <summary>
/// 开始解析文档
/// </summary>
public void StartParsing()
{
if (Status != DocumentStatus.Pending && Status != DocumentStatus.Failed)
throw new InvalidOperationException($"当前状态 {Status} 不允许开始解析");
Status = DocumentStatus.Parsing;
ErrorMessage = null;
}
/// <summary>
/// 完成解析,准备切片
/// </summary>
public void CompleteParsing()
{
if (Status != DocumentStatus.Parsing) return;
Status = DocumentStatus.Splitting;
}
/// <summary>
/// 添加文档切片
/// </summary>
public void AddChunk(int index, string content)
{
// 允许在 Splitting 或 Embedding 阶段添加/重新生成切片
if (Status != DocumentStatus.Splitting && Status != DocumentStatus.Embedding)
throw new InvalidOperationException($"当前状态 {Status} 不允许添加切片");
var chunk = new DocumentChunk(Id, index, content);
_chunks.Add(chunk);
ChunkCount = _chunks.Count;
}
/// <summary>
/// 清空所有切片(例如重新处理时)
/// </summary>
public void ClearChunks()
{
_chunks.Clear();
ChunkCount = 0;
}
/// <summary>
/// 开始向量化
/// </summary>
public void StartEmbedding()
{
Status = DocumentStatus.Embedding;
}
/// <summary>
/// 标记切片已向量化完成(更新向量ID)
/// </summary>
public void MarkChunkAsEmbedded(int chunkId, string vectorId)
{
var chunk = _chunks.FirstOrDefault(c => c.Id == chunkId);
chunk?.SetVectorId(vectorId);
}
/// <summary>
/// 文档处理全部完成
/// </summary>
public void MarkAsIndexed()
{
Status = DocumentStatus.Indexed;
ProcessedAt = DateTime.UtcNow;
}
/// <summary>
/// 标记处理失败
/// </summary>
public void MarkAsFailed(string errorMessage)
{
Status = DocumentStatus.Failed;
ErrorMessage = errorMessage;
}
#endregion
}
public enum DocumentStatus
{
Pending = 0, // 等待处理
Parsing = 1, // 正在读取/解析内容
Splitting = 2, // 正在进行文本切片
Embedding = 3, // 正在调用模型生成向量
Indexed = 4, // 索引完成,可用于检索
Failed = 5 // 处理失败
}
DocumentChunk 向量切片
cs
using Zilor.AICopilot.SharedKernel.Domain;
namespace Zilor.AICopilot.Core.Rag.Aggregates.KnowledgeBase;
public class DocumentChunk : IEntity<int>
{
protected DocumentChunk()
{
}
internal DocumentChunk(int documentId, int index, string content)
{
DocumentId = documentId;
Index = index;
Content = content;
CreatedAt = DateTime.UtcNow;
}
public int Id { get; set; }
public int DocumentId { get; private set; }
/// <summary>
/// 切片序号
/// </summary>
public int Index { get; private set; }
/// <summary>
/// 文本内容
/// </summary>
public string Content { get; private set; } = string.Empty;
/// <summary>
/// 向量数据库中的ID
/// </summary>
public string? VectorId { get; private set; }
public DateTime CreatedAt { get; private set; }
// 导航属性
public Document Document { get; private set; } = null!;
/// <summary>
/// 设置向量ID (当向量化完成后调用)
/// </summary>
public void SetVectorId(string vectorId)
{
VectorId = vectorId;
}
}
建模
cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Zilor.AICopilot.Core.Rag.Aggregates.KnowledgeBase;
namespace Zilor.AICopilot.EntityFrameworkCore.Configuration.Rag;
public class DocumentChunkConfiguration : IEntityTypeConfiguration<DocumentChunk>
{
public void Configure(EntityTypeBuilder<DocumentChunk> builder)
{
builder.ToTable("document_chunks");
builder.HasKey(c => c.Id);
builder.Property(c => c.Id).HasColumnName("id")
.ValueGeneratedOnAdd();
builder.Property(c => c.DocumentId)
.IsRequired()
.HasColumnName("document_id");
builder.Property(c => c.Index)
.IsRequired()
.HasColumnName("index");
// 内容字段,根据数据库类型可能需要配置为 TEXT
builder.Property(c => c.Content)
.IsRequired()
.HasColumnType("text")
.HasColumnName("content");
builder.Property(c => c.VectorId)
.HasMaxLength(100)
.HasColumnName("vector_id"); // 允许为空,因为刚切分完可能还没向量化
builder.Property(c => c.CreatedAt)
.IsRequired()
.HasColumnName("created_at");
// 索引配置:通常会根据文档ID查询切片,并按顺序排序
builder.HasIndex(c => new { c.DocumentId, c.Index })
.IsUnique(); // 保证同一文档内切片序号不重复
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Zilor.AICopilot.Core.Rag.Aggregates.KnowledgeBase;
namespace Zilor.AICopilot.EntityFrameworkCore.Configuration.Rag;
public class DocumentConfiguration : IEntityTypeConfiguration<Document>
{
public void Configure(EntityTypeBuilder<Document> builder)
{
builder.ToTable("documents");
builder.HasKey(d => d.Id);
builder.Property(d => d.Id).HasColumnName("id")
.ValueGeneratedOnAdd();
builder.Property(d => d.KnowledgeBaseId)
.IsRequired()
.HasColumnName("knowledge_base_id");
builder.Property(d => d.Name)
.IsRequired()
.HasMaxLength(256)
.HasColumnName("name");
builder.Property(d => d.FilePath)
.IsRequired()
.HasMaxLength(500)
.HasColumnName("file_path");
builder.Property(d => d.Extension)
.IsRequired()
.HasMaxLength(50)
.HasColumnName("extension");
builder.Property(d => d.FileHash)
.IsRequired()
.HasMaxLength(64) // 使用 SHA256,通常为 64 字符
.HasColumnName("file_hash");
// 状态枚举:建议存为字符串,方便数据库直观查看
builder.Property(d => d.Status)
.IsRequired()
.HasMaxLength(50)
.HasConversion<string>()
.HasColumnName("status");
builder.Property(d => d.ChunkCount)
.IsRequired()
.HasColumnName("chunk_count");
builder.Property(d => d.ErrorMessage)
.HasColumnName("error_message"); // 允许为空
builder.Property(d => d.CreatedAt)
.IsRequired()
.HasColumnName("created_at");
builder.Property(d => d.ProcessedAt)
.HasColumnName("processed_at"); // 允许为空
// 配置导航属性 Chunks
builder.HasMany(d => d.Chunks)
.WithOne(c => c.Document)
.HasForeignKey(c => c.DocumentId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade); // 删除文档时级联删除切片
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Zilor.AICopilot.Core.Rag.Aggregates.EmbeddingModel;
namespace Zilor.AICopilot.EntityFrameworkCore.Configuration.Rag;
public class EmbeddingModelConfiguration : IEntityTypeConfiguration<EmbeddingModel>
{
public void Configure(EntityTypeBuilder<EmbeddingModel> builder)
{
builder.ToTable("embedding_models");
builder.HasKey(e => e.Id);
builder.Property(e => e.Id).HasColumnName("id");
builder.Property(e => e.Name)
.IsRequired()
.HasMaxLength(100)
.HasColumnName("name");
// 建议添加唯一索引,防止同名模型
builder.HasIndex(e => e.Name).IsUnique();
builder.Property(e => e.Provider)
.IsRequired()
.HasMaxLength(50)
.HasColumnName("provider");
builder.Property(e => e.BaseUrl)
.IsRequired()
.HasMaxLength(500)
.HasColumnName("base_url");
builder.Property(e => e.ApiKey)
.HasMaxLength(256)
.HasColumnName("api_key");
builder.Property(e => e.ModelName)
.IsRequired()
.HasMaxLength(100)
.HasColumnName("model_name");
builder.Property(e => e.Dimensions)
.IsRequired()
.HasColumnName("dimensions");
builder.Property(e => e.MaxTokens)
.IsRequired()
.HasColumnName("max_tokens");
builder.Property(e => e.IsEnabled)
.IsRequired()
.HasColumnName("is_enabled");
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Zilor.AICopilot.Core.Rag.Aggregates.KnowledgeBase;
namespace Zilor.AICopilot.EntityFrameworkCore.Configuration.Rag;
public class KnowledgeBaseConfiguration : IEntityTypeConfiguration<KnowledgeBase>
{
public void Configure(EntityTypeBuilder<KnowledgeBase> builder)
{
builder.ToTable("knowledge_bases");
builder.HasKey(kb => kb.Id);
builder.Property(kb => kb.Id).HasColumnName("id");
builder.Property(kb => kb.Name)
.IsRequired()
.HasMaxLength(200)
.HasColumnName("name");
builder.Property(kb => kb.Description)
.HasMaxLength(1000)
.HasColumnName("description");
builder.Property(kb => kb.EmbeddingModelId)
.IsRequired()
.HasColumnName("embedding_model_id");
// 配置导航属性 Documents
builder.HasMany(kb => kb.Documents)
.WithOne(d => d.KnowledgeBase)
.HasForeignKey(d => d.KnowledgeBaseId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade); // 删除知识库时级联删除文档
}
}
实现

RAG数据接入:
1.用户通过API创建知识库

2.用户上传文件(异步)
cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Zilor.AICopilot.HttpApi.Infrastructure;
using Zilor.AICopilot.RagService.Commands.Documents;
using Zilor.AICopilot.Services.Common;
namespace Zilor.AICopilot.HttpApi.Controllers;
[Route("/api/rag")]
[Authorize]
public class RagController : ApiControllerBase
{
/// <summary>
/// 创建知识库
/// </summary>
[HttpPost("knowledge-base")]
public async Task<IActionResult> CreateKnowledgeBase(CreateKnowledgeBaseCommand command)
{
var result = await Sender.Send(command);
return ReturnResult(result);
}
/// <summary>
/// 上传文档
/// </summary>
[HttpPost("document")]
[DisableRequestSizeLimit] // 允许上传大文件
public async Task<IActionResult> UploadDocument(
[FromForm] Guid knowledgeBaseId,
IFormFile file)
{
if (file.Length == 0)
{
return BadRequest(new { error = "请选择文件" });
}
// 将 IFormFile 转换为流
await using var stream = file.OpenReadStream();
var command = new UploadDocumentCommand(
knowledgeBaseId,
new FileUploadStream(file.FileName, stream));
var result = await Sender.Send(command);
return ReturnResult(result);
}
}
3.系统计算文件Hash(幂等性)

cs
using System.Security.Cryptography;
using MassTransit;
using Zilor.AICopilot.Core.Rag.Aggregates.KnowledgeBase;
using Zilor.AICopilot.Services.Common.Attributes;
using Zilor.AICopilot.Services.Common.Contracts;
using Zilor.AICopilot.Services.Common.Events;
using Zilor.AICopilot.SharedKernel.Messaging;
using Zilor.AICopilot.SharedKernel.Repository;
using Zilor.AICopilot.SharedKernel.Result;
namespace Zilor.AICopilot.RagService.Commands.Documents;
public record UploadDocumentDto(int Id, string Status);
public record FileUploadStream(string FileName, Stream Stream);
[AuthorizeRequirement("Rag.UploadDocument")]
public record UploadDocumentCommand(
Guid KnowledgeBaseId,
FileUploadStream File) : ICommand<Result<UploadDocumentDto>>;
public class UploadDocumentCommandHandler(
IRepository<KnowledgeBase> kbRepo,
IFileStorageService fileStorage,
IPublishEndpoint publishEndpoint)
: ICommandHandler<UploadDocumentCommand, Result<UploadDocumentDto>>
{
public async Task<Result<UploadDocumentDto>> Handle(
UploadDocumentCommand request,
CancellationToken cancellationToken)
{
// 1. 获取知识库聚合根(并急切加载 Documents 集合)
// 使用我们刚扩展的 GetAsync 方法,通过 includes 参数加载子实体
var kb = await kbRepo.GetAsync(
kb => kb.Id == request.KnowledgeBaseId,
includes: [k => k.Documents],
cancellationToken);
if (kb == null) return Result.NotFound("知识库不存在");
// 2. 计算文件 Hash (SHA256)
string fileHash;
using (var sha256 = SHA256.Create())
{
// 确保流从头开始
if (request.File.Stream.CanSeek) request.File.Stream.Position = 0;
var hashBytes = await sha256.ComputeHashAsync(request.File.Stream, cancellationToken);
fileHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
// 计算完 Hash 后,必须重置流位置,否则后续保存文件时会读到空内容
if (request.File.Stream.CanSeek) request.File.Stream.Position = 0;
}
// 3. 检查文件是否已存在 (基于 Hash 实现幂等性)
// 因为 Documents 已经加载到内存中,我们可以直接使用 LINQ 查询
var existingDoc = kb.Documents.FirstOrDefault(d => d.FileHash == fileHash);
if (existingDoc != null)
{
// 如果文件已存在,直接返回成功,并返回现有的文档 ID
// 这实现了接口的幂等性:多次上传同一文件不会产生副作用
return Result.Success(new UploadDocumentDto(existingDoc.Id, existingDoc.Status.ToString()));
}
// 4. 保存物理文件 (只有当文件不存在时才执行 IO 操作)
var extension = Path.GetExtension(request.File.FileName).ToLower();
var savedPath = await fileStorage.SaveAsync(request.File.Stream, request.File.FileName, cancellationToken);
// 5. 领域模型行为:添加文档
// 这一步是纯内存操作,修改了聚合根的状态
var document = kb.AddDocument(request.File.FileName, savedPath, extension, fileHash);
// 6. 持久化到数据库
await kbRepo.SaveChangesAsync(cancellationToken);
// 7. 发送集成事件 (通知后台 Worker 开始索引)
await publishEndpoint.Publish(new DocumentUploadedEvent
{
DocumentId = document.Id,
KnowledgeBaseId = kb.Id,
FilePath = savedPath,
FileName = request.File.FileName
}, cancellationToken);
return Result.Success(new UploadDocumentDto(document.Id, document.Status.ToString()));
}
}
4.现在数据库生成文档记录

5.发送消息到RabbitMQ

配置

对象

基础设施

cs
using System.Reflection;
using MassTransit;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace Zilor.AICopilot.EventBus;
public static class DependencyInjection
{
public static void AddEventBus(this IHostApplicationBuilder builder, params Assembly[] assemblies)
{
builder.Services.AddMassTransit(x =>
{
if (assemblies.Length > 0)
{
x.AddConsumers(assemblies);
}
x.SetKebabCaseEndpointNameFormatter();
// 默认配置 RabbitMQ
x.UsingRabbitMq((context, cfg) =>
{
// 从 Aspire 注入的连接字符串中读取配置
// 连接字符串名必须与 AppHost 中 .AddRabbitMQ("eventbus") 的名称一致
var connectionString = builder.Configuration.GetConnectionString("eventbus");
cfg.Host(connectionString);
cfg.ConfigureEndpoints(context);
});
});
}
}
服务层

rag模型
cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Zilor.AICopilot.Core.Rag.Aggregates.EmbeddingModel;
using Zilor.AICopilot.Core.Rag.Aggregates.KnowledgeBase;
using Zilor.AICopilot.Services.Common.Attributes;
using Zilor.AICopilot.SharedKernel.Messaging;
using Zilor.AICopilot.SharedKernel.Repository;
using Zilor.AICopilot.SharedKernel.Result;
namespace Zilor.AICopilot.Services.Common;
public record CreatedKnowledgeBaseDto(Guid Id, string Name);
[AuthorizeRequirement("Rag.CreateKnowledgeBase")]
public record CreateKnowledgeBaseCommand(
string Name,
string Description,
Guid EmbeddingModelId) : ICommand<Result<CreatedKnowledgeBaseDto>>;
public class CreateKnowledgeBaseCommandHandler(
IRepository<KnowledgeBase> kbRepo,
IReadRepository<EmbeddingModel> modelRepo)
: ICommandHandler<CreateKnowledgeBaseCommand, Result<CreatedKnowledgeBaseDto>>
{
public async Task<Result<CreatedKnowledgeBaseDto>> Handle(
CreateKnowledgeBaseCommand request,
CancellationToken cancellationToken)
{
// 1. 校验嵌入模型是否存在
// 知识库必须绑定一个具体的 Embedding 模型,因为这决定了向量的维度
var embeddingModel = await modelRepo.GetByIdAsync(request.EmbeddingModelId, cancellationToken);
if (embeddingModel == null)
{
return Result.NotFound("指定的嵌入模型不存在");
}
// 2. 创建实体
var kb = new KnowledgeBase(request.Name, request.Description, request.EmbeddingModelId);
// 3. 持久化
kbRepo.Add(kb);
await kbRepo.SaveChangesAsync(cancellationToken);
return Result.Success(new CreatedKnowledgeBaseDto(kb.Id, kb.Name));
}
}


异步与并发处理
RAG后台服务(实现文档索引)
文件存储:变化点

cs
using Zilor.AICopilot.Services.Common.Contracts;
namespace Zilor.AICopilot.Infrastructure.Storage;
public class LocalFileStorageService : IFileStorageService
{
private const string RootPath = "D:\\";
private const string UploadRoot = "uploads";
public async Task<string> SaveAsync(Stream stream, string fileName, CancellationToken cancellationToken = default)
{
// 1. 构建存储路径:uploads/2025/12/01/guid_filename.pdf
var datePath = DateTime.Now.ToString("yyyy/MM/dd");
var uniqueFileName = $"{Guid.NewGuid()}_{fileName}";
var relativePath = Path.Combine(UploadRoot, datePath);
var fullDirectory = Path.Combine(RootPath, relativePath);
if (!Directory.Exists(fullDirectory))
{
Directory.CreateDirectory(fullDirectory);
}
var fullPath = Path.Combine(fullDirectory, uniqueFileName);
// 2. 写入文件
await using var fileStream = new FileStream(fullPath, FileMode.Create);
if (stream.CanSeek) stream.Position = 0;
await stream.CopyToAsync(fileStream, cancellationToken);
// 3. 返回相对路径(统一使用正斜杠,方便跨平台和URL访问)
return Path.Combine(relativePath, uniqueFileName).Replace("\\", "/");
}
public Task<Stream?> GetAsync(string path, CancellationToken cancellationToken = default)
{
var fullPath = Path.Combine(RootPath, path);
if (!File.Exists(fullPath)) return Task.FromResult<Stream?>(null);
var stream = new FileStream(fullPath, FileMode.Open, FileAccess.Read);
return Task.FromResult<Stream?>(stream);
}
public Task DeleteAsync(string path, CancellationToken cancellationToken = default)
{
var fullPath = Path.Combine(RootPath, path);
if (File.Exists(fullPath))
{
File.Delete(fullPath);
}
return Task.CompletedTask;
}
}

星:接下来redlis会用上吗?
阿伦:刚才很多服务的是什么工具
星:aspire,阿伦前边的课没有听
6666
RabbitMQ 密码
1

2
3
4默认账号密码

5 上面密码pass
6
本地AI工具 坑呀
如何安装https://cloud.tencent.com/developer/article/2475814
---入坑开始

不翻墙在 LM Studio 下载模型,核心是修改镜像源 或手动下载后导入,以下是两种高效可行方法,优先推荐镜像源修改,操作简单且适配软件内下载流程。
方法一:修改镜像源(推荐,软件内直接下载)
- 关闭 LM Studio:确保软件完全退出,避免文件占用。
- 定位核心文件
- Windows:安装目录(如
D:\Program Files\LM Studio)→resources\app\.webpack\main\index.js和resources\app\.webpack\renderer\main_window.js。 - macOS:右键
LM Studio.app→显示包内容→Contents\Resources\app\.webpack\main\index.js和Contents\Resources\app\.webpack\renderer\main_window.js。
- Windows:安装目录(如
- 替换镜像 :用 VS Code 等编辑器打开上述两个文件,批量将所有
huggingface.co替换为国内镜像hf-mirror.com,保存文件。 - 重启并下载:重新打开 LM Studio,在模型搜索页选择 GGUF 格式模型(如 Q4_K_M 量化版),点击下载即可。
- 快捷工具(可选) :执行命令
npx lmstudio-mirror-switcher,一键完成镜像切换与备份,适合不想手动改文件的用户。
方法二:手动下载导入(稳定可靠,适合大文件)
-
获取模型文件
- 镜像网站下载:访问hf-mirror.com或魔搭社区(modelscope.cn),搜索所需 GGUF 格式模型(如
lmstudio-community/DeepSeek-R1-Distill-Qwen-14B-GGUF),下载对应量化版本(如 Q4_K_M)。 - 命令行下载(推荐):安装 huggingface-cli,配置环境变量后下载,示例命令如下:
bash
运行
# Windows(cmd) set HF_ENDPOINT=https://hf-mirror.com huggingface-cli download 模型仓库名 模型文件名 --local-dir 本地保存路径 # macOS/Linux(终端) export HF_ENDPOINT=https://hf-mirror.com huggingface-cli download 模型仓库名 模型文件名 --local-dir 本地保存路径 - 镜像网站下载:访问hf-mirror.com或魔搭社区(modelscope.cn),搜索所需 GGUF 格式模型(如
-
放置模型到指定目录
- 默认目录:Windows 一般为
C:\Users\用户名\.lmstudio\models;macOS 为/Users/用户名/.lmstudio/models;Linux 为~/.lmstudio/models。 - 自定义目录:在 LM Studio 中点击设置→模型目录→Change,选择自定义路径,将下载的模型文件放入该目录下的对应模型文件夹。
- 默认目录:Windows 一般为
-
刷新加载:重启 LM Studio,模型会自动加载到库中,可直接使用。
注意事项
- 模型格式:LM Studio 优先支持 GGUF 格式,下载时注意选择对应版本,避免格式不兼容。
- 目录结构:手动导入时,需按
模型仓库名/模型文件名.gguf的结构存放,确保软件正常识别。 - 存储路径:建议将模型放在非系统盘,预留足够空间(7B 模型约 4GB,14B 模型约 8GB)。
选择
git clone --depth 1 https://huggingface.co/Qwen/Qwen3-Embedding-0.6B-GGUF /e/Users/Administrator/.lmstudio/models/Qwen3-Embedding-0.6B-GGUF
https://hf-mirror.com/Qwen/Qwen3-Embedding-0.6B-GGUF/tree/main
方案 1:下载支持 AVX 指令集的老旧 GGUF 模型(无需换硬件) 部分早期 GGUF 模型没有强制要求 AVX2,可针对性下载: 优先选择 7B 及以下参数量、早期量化版本(如 Q4_0、Q5_0) 的模型,例如: TheBloke/Llama-2-7B-Chat-GGUF(找标注 avx 兼容的版本) TheBloke/Mistral-7B-v0.1-GGUF(老旧量化版) 下载渠道:在 hf-mirror.com 搜索模型时,筛选 2023 年底前发布的 GGUF 文件,查看模型说明中的「CPU 要求」,确认支持 AVX。 有支持的数据量小的Qwen3吗
有适配纯 AVX 的小参数量 Qwen3 GGUF 模型,优先选Qwen3-0.6B (最小、最易跑),其次是 Qwen3-1.7B,核心要下早期低量化版 + 适配纯 AVX 的转换版,搭配手动导入 + 指令集强制兼容,可在你的 Xeon E5-2450(仅 AVX)上运行。
一、可直接用的小参数量 Qwen3 模型(适配 AVX)
| 模型名称 | 参数量 | 推荐量化版 | 适配理由 | 下载链接(hf-mirror) |
|---|---|---|---|---|
| Qwen3-0.6B-GGUF | 0.6B | Q4_0 / Q5_0(早期版) | 最小参数量,早期 GGUF 转换未强制 AVX2 | https://hf-mirror.com/Qwen/Qwen3-0.6B-GGUF |
| Qwen3-1.7B-GGUF | 1.7B | Q4_K_M(2024 年初转换版) | 轻量,部分社区转换版保留 AVX 兼容 | https://hf-mirror.com/Qwen/Qwen3-1.7B-GGUF |
二、AVX 兼容关键操作(必做,否则仍加载失败)
- 选对文件 + 目录合规
- 下载上述模型的 Q4_0 / Q5_0 低量化版(Q8_0 等新版可能隐含 AVX2 依赖)。
- 存放结构:
E:\LMStudio_Models\Qwen3-0.6B-GGUF\Qwen3-0.6B-Q4_0.gguf(目录必须在 LM Studio 安装目录外)。
- 强制指令集兼容(修改配置)
- 关闭 LM Studio,打开
%APPDATA%\LM Studio\config.json,添加"cpuInstructionSetOverride": "avx",保存后重启。
- 关闭 LM Studio,打开
- 手动导入 + 刷新识别
- 放入合规目录后,在 LM Studio 的「Local Models」页点击右上角「Refresh」,即可显示模型,点击「Load」加载。
三、下载与导入步骤(避免镜像 / 缓存问题)
- 从 hf-mirror 链接下载目标量化文件(用 IDM / 迅雷,避免文件损坏)。
- 按「模型目录 \ 仓库名 \ 模型.gguf」存放,给目录 Administrator 完全控制权限。
- 修改 config.json 启用 AVX 兼容,重启 LM Studio,刷新本地模型列表,加载验证。
四、常见问题与修复
| 问题 | 修复方案 |
|---|---|
| 加载报 illegal instruction | 确认用 Q4_0/Q5_0 低量化,再次核对 config.json 的 avx 强制配置 |
| 模型不显示 | 检查目录结构,重新刷新,确保无.tmp/.part 临时后缀 |
| 推理极慢 | 0.6B 模型在 AVX 下约 1--5 tokens/sec,属正常,可换 Q4_0 进一步提速 |
五、替代方案(模型仍无法加载时)
若上述步骤无效,可下载 Qwen2-0.5B/1.8B 的早期 GGUF 版(更老旧,AVX 兼容更稳),能力接近 Qwen3 小模型,且社区验证纯 AVX 可运行。
需要我帮你下载并打包Qwen3-0.6B-Q4_0.gguf(AVX 兼容版)+ 目录结构模板 + config.json 修改脚本,你解压后直接放入目录就能用吗?
---入坑结束
https://hf-mirror.com/models?num_parameters=min:0,max:6B&sort=trending&search=Qwen3-0.6B-GGUF+Q4_0
源码地址
代码https://gitcode.com/cao9prc/AINET10fstyle
其他专题
AI开发01 1后端框架: ASP.NET Core2.AI框架: Semantic Kernerl (SK)、Agent Framework3.知识库:向量数据库(Qdrant)+关系型数据库(Post
https://blog.csdn.net/cao919/article/details/155895060
.net AI开发02 1后端框架: ASP.NET Core2.AI框架: Semantic Kernerl (SK)、Agent Framework3.知识库:向量数据库(Qdrant)+关系型数据库(Post
.net AI开发03 新增意图识别与工具选择工作流(IntentWorkflow),支持多智能体协作; 插件体系升级,支持多项目插件自动注册与工具发现; 对话历史与消息存储解耦,采用 Med
https://blog.csdn.net/cao919/article/details/156065076
在C# .net中RabbitMQ的核心类型和属性,除了交换机,队列关键的类型 / 属性,影响其行为
https://blog.csdn.net/cao919/article/details/157254797
.net AI MCP 入门 适用于模型上下文协议的 C# SDK 简介(MCP)
https://blog.csdn.net/cao919/article/details/147915384
C# .net ai Agent AI视觉应用 写代码 改作业 识别屏幕 简历处理 标注等
https://blog.csdn.net/cao919/article/details/146504537
C# net deepseek RAG AI开发 全流程 介绍
https://blog.csdn.net/cao919/article/details/147915384
WPF halcon 机器视觉
https://blog.csdn.net/cao919/article/details/134790240
