当.NET 9遇见AI智能体:AntSK企业级知识库的技术革命与实战解析
引子:在AI浪潮席卷全球的今天,一个问题始终困扰着企业:如何让AI真正理解企业的知识资产?如何在保障数据安全的前提下,构建一个既智能又可控的知识库系统?今天,我们将深入剖析一个开源项目——AntSK,看看它如何用.NET 9和Semantic Kernel,为这个问题交出了一份令人惊艳的答卷。
一、为什么AntSK值得你关注?
1.1 企业知识库的痛点与机遇
在与多家企业的技术团队交流后,我发现大家都面临着相似的困境:
传统知识库的三大痛点:
-
检索效率低:关键词搜索经常找不到想要的内容,明明文档里有,就是搜不出来
-
知识孤岛:各部门的文档散落在不同系统,形成信息孤岛
-
维护成本高:需要专人整理分类,但往往跟不上业务变化的速度
AI知识库的新挑战:
-
数据安全:企业核心资料能否放心交给第三方AI服务?
-
成本控制:按查询次数付费的商业方案,成本难以预测
-
定制化需求:通用方案难以满足特定行业的业务场景
AntSK的出现,恰好击中了这些痛点。它不是简单地调用OpenAI API,而是构建了一个完整的企业级AI基础设施,让企业既能享受AI的智能,又能完全掌控自己的数据。
1.2 AntSK的技术定位:不只是知识库
如果你以为AntSK只是一个"能用AI问答的文档管理系统",那就太小看它了。从技术架构来看,AntSK更像是一个AI应用开发平台:
传统知识库 = 文档存储 + 关键词搜索
AI知识库 = 文档存储 + 向量检索 + LLM问答
AntSK = 文档存储 + 向量检索 + LLM问答 + 插件系统 + 多模型管理 + 智能体编排
这意味着,基于AntSK,你不仅能构建知识库,还能开发各种AI应用:智能客服、代码助手、业务分析工具……想象空间巨大。
二、技术架构深度剖析:藏在代码里的设计智慧
2.1 分层架构:清晰的职责边界
AntSK采用了经典的DDD(领域驱动设计)分层架构,但在AI应用的特殊需求下做了巧妙的调整。让我们从代码入手,看看这个架构的精妙之处。
表现层:Blazor Server的现代化选择
打开Program.cs,你会看到这样的配置:
builder.Services.AddServerSideBlazor();
builder.Services.AddAntDesign();// 实时通信支持
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
为什么选择Blazor Server而不是传统的MVC或React?
这个决策其实很有讲究。AI对话场景有个特点:需要实时流式输出。想象一下ChatGPT那种一个字一个字蹦出来的效果,这在传统的请求-响应模式下很难实现。
Blazor Server基于SignalR,天然支持服务器到客户端的实时推送。看看聊天服务的实现:
public async IAsyncEnumerable<string> SendChatByAppAsync(Apps app, ChatHistory history)
{var chatResult = chat.GetStreamingChatMessageContentsAsync(history, settings, _kernel);await foreach (var content in chatResult){yield return content.ConvertToString(); // 实时流式返回}
}
这个IAsyncEnumerable配合Blazor的实时渲染,就能完美实现流式输出效果。如果用传统的REST API,你得自己处理SSE(Server-Sent Events)或WebSocket,复杂度直线上升。
另一个亮点:Ant Design Blazor组件库
builder.Services.AddAntDesign();
builder.Services.Configure<ProSettings>(builder.Configuration.GetSection("ProSettings"));
Ant Design是阿里开源的企业级UI设计语言,Blazor版本提供了60+高质量组件。这意味着你不需要从零开始写UI,开箱即用的企业级界面,省下的可不只是时间。
应用服务层:智能化的业务编排
应用服务层是AntSK的"大脑",负责协调各种AI能力。核心服务包括:
1. KernelService:AI模型的统一调度器
这个服务的设计非常巧妙,它解决了一个关键问题:如何在一个系统中同时支持多种AI模型?
public Kernel GetKernelByApp(Apps app)
{var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == app.ChatModelID);var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);var builder = Kernel.CreateBuilder();WithTextGenerationByAIType(builder, chatModel, chatHttpClient);_kernel = builder.Build();RegisterPluginsWithKernel(_kernel);return _kernel;
}
注意这个设计:每次调用都创建新的Kernel实例。为什么不用单例模式?
因为不同的应用(App)可能配置了不同的模型、不同的插件。如果用单例,就会出现"串台"的问题。虽然每次创建新实例有性能开销,但换来的是完全的隔离性和灵活性。
再看模型适配的实现:
private void WithTextGenerationByAIType(IKernelBuilder builder, AIModels chatModel, HttpClient chatHttpClient)
{switch (chatModel.AIType){case AIType.OpenAI:builder.AddOpenAIChatCompletion(modelId: chatModel.ModelName,apiKey: chatModel.ModelKey,httpClient: chatHttpClient);break;case AIType.SparkDesk: // 讯飞星火var settings = chatModel.ModelKey.Split("|");SparkDeskOptions options = new SparkDeskOptions { AppId = settings[0], ApiSecret = settings[1], ApiKey = settings[2] };builder.Services.AddKeyedSingleton<IChatCompletionService>("spark-desk-chat", new SparkDeskChatCompletion(options, chatModel.Id));break;case AIType.LLamaFactory: // 本地模型builder.AddOpenAIChatCompletion(modelId: chatModel.ModelName,apiKey: "NotNull", // 本地模型不需要真实的API KeyhttpClient: chatHttpClient);break;}
}
这个适配器模式的实现非常优雅:
-
统一接口:无论是OpenAI、讯飞星火还是本地模型,对上层调用者来说都是一样的
-
易于扩展:新增一个AI厂商,只需要加一个case分支
-
配置驱动:模型类型、密钥等都从数据库读取,运行时可动态切换
2. KMService:知识库的向量化魔法
知识库服务是AntSK的核心竞争力所在。它基于微软的Kernel Memory框架,但做了大量定制化开发。
public MemoryServerless GetMemoryByKMS(string kmsID)
{var kms = _kmss_Repositories.GetFirst(p => p.Id == kmsID);var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == kms.ChatModelID);var embedModel = _aIModels_Repositories.GetFirst(p => p.Id == kms.EmbeddingModelID);var memoryBuild = new KernelMemoryBuilder().WithCustomTextPartitioningOptions(new TextPartitioningOptions{MaxTokensPerLine = kms.MaxTokensPerLine,MaxTokensPerParagraph = kms.MaxTokensPerParagraph,OverlappingTokens = kms.OverlappingTokens});WithOcr(memoryBuild, kms); // OCR支持WithTextGenerationByAIType(memoryBuild, chatModel, chatHttpClient);WithTextEmbeddingGenerationByAIType(memoryBuild, embedModel, embeddingHttpClient);WithMemoryDbByVectorDB(memoryBuild);return memoryBuild.Build<MemoryServerless>();
}
这段代码信息量巨大,我们逐一解读:
文档分块策略(Text Partitioning)
MaxTokensPerLine = kms.MaxTokensPerLine, // 每行最大token数
MaxTokensPerParagraph = kms.MaxTokensPerParagraph, // 每段最大token数
OverlappingTokens = kms.OverlappingTokens // 重叠token数
为什么需要分块?因为AI模型有上下文长度限制。一个100页的PDF不可能一次性塞给模型。
为什么需要重叠?举个例子:
段落1:...公司成立于2020年。
段落2:2020年,我们推出了首款产品...
如果严格按段落切分,"2020年"这个关键信息就被割裂了。通过设置重叠token(比如50个),段落2的开头会包含段落1的结尾,保证了语义的连贯性。
OCR集成:多模态文档处理
private static void WithOcr(IKernelMemoryBuilder memoryBuild, Kmss kms)
{if (kms.IsOCR == 1){memoryBuild.WithCustomImageOcr(new AntSKOcrEngine());}
}
这个功能看似简单,实则解决了一个大问题:扫描件和图片中的文字怎么办?
很多企业的历史文档都是扫描PDF,没有OCR就无法提取文字,知识库也就成了摆设。AntSK内置了OCR引擎,自动识别图片中的文字,真正做到了"来者不拒"。
3. ChatService:对话的艺术
聊天服务是用户直接接触的部分,它的实现展现了Semantic Kernel的强大之处。
public async IAsyncEnumerable<string> SendChatByAppAsync(Apps app, ChatHistory history)
{var _kernel = _kernelService.GetKernelByApp(app);var chat = _kernel.GetRequiredService<IChatCompletionService>();OpenAIPromptExecutionSettings settings = new() { Temperature = app.Temperature / 100 };if (!string.IsNullOrEmpty(app.ApiFunctionList) || !string.IsNullOrEmpty(app.NativeFunctionList)){_kernelService.ImportFunctionsByApp(app, _kernel);settings.ToolCallBehavior = ToolCallBehavior.EnableKernelFunctions;while (true){ChatMessageContent result = await chat.GetChatMessageContentAsync(history, settings, _kernel);if (result.Content is not null){yield return result.Content.ConvertToString();break;}// 处理函数调用IEnumerable<FunctionCallContent> functionCalls = FunctionCallContent.GetFunctionCalls(result);if (!functionCalls.Any()) break;foreach (var functionCall in functionCalls){FunctionResultContent resultContent = await functionCall.InvokeAsync(_kernel);history.Add(resultContent.ToChatMessage());}}}
}
这段代码实现了Function Calling(函数调用)机制,这是现代AI应用的核心能力。
什么是Function Calling?
简单说,就是让AI不仅能"说",还能"做"。举个例子:
用户:帮我查一下订单12345的物流信息
AI:好的,让我查询一下... [调用GetOrderInfo函数]
系统:订单12345已发货,预计明天送达
AI:您的订单12345已经发货,预计明天就能送达了
在这个过程中,AI识别出需要调用GetOrderInfo函数,系统执行函数获取真实数据,然后AI基于数据生成自然语言回复。
代码中的while(true)循环就是在处理这个过程:
-
AI生成回复,可能包含函数调用
-
如果有函数调用,执行函数,将结果加入对话历史
-
继续让AI生成回复,直到不再需要调用函数
这种机制让AI从"聊天机器人"进化成了"智能助手"。
2.2 领域核心层:AI能力的技术基石
Semantic Kernel:微软的AI编排框架
Semantic Kernel是微软开源的AI编排框架,可以理解为"AI界的Spring框架"。它提供了:
-
统一的AI服务抽象:无论是OpenAI、Azure还是本地模型,都用同一套接口
-
插件机制:让AI能调用外部功能
-
规划能力:自动分解复杂任务
AntSK深度集成了SK,从依赖配置就能看出:
<PropertyGroup><SKVersion>1.17.2</SKVersion>
</PropertyGroup><PackageReference Include="Microsoft.SemanticKernel" Version="$(SKVersion)" />
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="$(SKVersion)" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="$(SKVersion)-alpha" />
Kernel Memory:知识管理的瑞士军刀
Kernel Memory是SK的姊妹项目,专注于知识库和RAG(检索增强生成)场景。它的架构非常优雅:
文档输入 → 文本提取 → 分块 → 向量化 → 存储 → 检索 → 重排 → 生成答案
每个环节都是可插拔的,比如:
-
文本提取:支持Word、PDF、Excel等多种格式
-
向量化:支持OpenAI、BGE等多种嵌入模型
-
存储:支持Postgres、Qdrant、Redis等多种向量数据库
AntSK的配置文件展示了这种灵活性:
{"KernelMemory": {"VectorDb": "Disk", // 可选:Postgres、Qdrant、Redis、Disk、Memory"ConnectionString": "Host=localhost;Database=antsk;...","TableNamePrefix": "km-"}
}
开发环境用Disk(磁盘存储),生产环境换成Postgres,只需要改一行配置。
三、核心功能深度解析:从原理到实践
3.1 RAG架构:让AI"有据可依"
RAG(Retrieval-Augmented Generation,检索增强生成)是当前AI知识库的主流技术路线。简单说,就是让AI在回答问题前,先去知识库里"翻翻书"。
传统对话 vs RAG对话
传统对话:
用户:公司的年假政策是什么?
AI:根据我的训练数据,一般公司提供5-15天年假...(瞎编)
RAG对话:
用户:公司的年假政策是什么?
系统:[检索知识库] → 找到《员工手册》第3.2条
AI:根据《员工手册》第3.2条,正式员工享有年假5-15天,具体天数根据工龄计算...(有据可依)
AntSK的RAG实现
让我们看看SendKmsByAppAsync方法,这是RAG的核心实现:
public async IAsyncEnumerable<StreamingKernelContent> SendKmsByAppAsync(Apps app, string questions, ChatHistory history, string filePath, List<RelevantSource> relevantSources = null)
{List<RelevantSource> relevantSourceList = new List<RelevantSource>();var _kernel = _kernelService.GetKernelByApp(app);// 步骤1:文档检索if (!string.IsNullOrWhiteSpace(filePath)){// 临时文件问答var memory = _kMService.GetMemoryByApp(app);var fileId = ExtractGuidFromPath(filePath);// 导入文档到向量库await memory.ImportDocumentAsync(new Document(fileId).AddFile(filePath).AddTag(KmsConstantcs.AppIdTag, app.Id).AddTag(KmsConstantcs.FileIdTag, fileId),index: KmsConstantcs.FileIndex);// 向量检索var filters = new List<MemoryFilter>() {new MemoryFilter().ByTag(KmsConstantcs.AppIdTag, app.Id),new MemoryFilter().ByTag(KmsConstantcs.FileIdTag, fileId)};var searchResult = await memory.SearchAsync(questions, index: KmsConstantcs.FileIndex, filters: filters);// 提取相关片段relevantSourceList.AddRange(searchResult.Results.SelectMany(item => item.Partitions.Select(part => new RelevantSource(){SourceName = item.SourceName,Text = Markdown.ToHtml(part.Text),Relevance = part.Relevance})));}else {// 从知识库检索relevantSourceList = await _kMService.GetRelevantSourceList(app, questions);}// 步骤2:Rerank重排序(可选)if (!string.IsNullOrEmpty(app.RerankModelID)){var rerankModel = _aIModels_Repositories.GetById(app.RerankModelID);BegRerankConfig.LoadModel(rerankModel.EndPoint, rerankModel.ModelName);foreach (var item in relevantSourceList){item.Score = BegRerankConfig.GetScore(questions, item.Text);}relevantSourceList = relevantSourceList.OrderByDescending(x => x.Score).Take(app.MaxMatchesCount).ToList();}// 步骤3:构建增强Promptvar dataMsg = new StringBuilder();foreach (var item in relevantSourceList){dataMsg.AppendLine($"【{item.SourceName}】");dataMsg.AppendLine(item.Text);dataMsg.AppendLine();}// 步骤4:生成回答history.AddSystemMessage($"{app.Prompt}\n\n参考资料:\n{dataMsg}");var chatResult = chat.GetStreamingChatMessageContentsAsync(history, settings, _kernel);await foreach (var content in chatResult){yield return content;}
}
这段代码展示了一个完整的RAG流程,让我们逐步拆解:
步骤1:向量检索的原理
什么是向量检索?
传统的关键词搜索是"字面匹配":
问题:如何请假?
搜索:找包含"请假"的文档
向量检索是"语义匹配":
问题:如何请假?
向量化:[0.23, -0.45, 0.67, ...] (768维向量)文档1:员工请假需要提前申请 → [0.25, -0.43, 0.65, ...] 相似度:0.95
文档2:公司财务报表 → [0.01, 0.89, -0.34, ...] 相似度:0.12
即使文档里没有"请假"这个词,只要语义相关,也能被检索出来。
AntSK的向量检索实现:
var searchResult = await memory.SearchAsync(questions, // 用户问题index: KmsConstantcs.FileIndex, // 索引名称filters: filters); // 过滤条件(按应用ID、文件ID)
Kernel Memory会自动完成:
-
将问题转换为向量
-
在向量数据库中查找最相似的文档片段
-
返回Top-K个结果(K由配置决定)
步骤2:Rerank重排序的威力
向量检索虽然强大,但也有局限:它只考虑了"语义相似度",没有考虑"相关性"。
举个例子:
问题:公司的年假政策是什么?检索结果:
1. "员工享有年假5-15天" (相似度0.85)
2. "年假期间工资照发" (相似度0.83)
3. "年假申请需提前一周" (相似度0.82)
这三条都很相似,但第1条才是直接回答问题的。Rerank模型会重新评分:
Rerank后:
1. "员工享有年假5-15天" (得分0.95) ← 最相关
2. "年假申请需提前一周" (得分0.78)
3. "年假期间工资照发" (得分0.65)
AntSK使用BGE-Rerank模型,这是目前中文场景下效果最好的重排模型之一。
if (!string.IsNullOrEmpty(app.RerankModelID))
{BegRerankConfig.LoadModel(rerankModel.EndPoint, rerankModel.ModelName);foreach (var item in relevantSourceList){item.Score = BegRerankConfig.GetScore(questions, item.Text);}relevantSourceList = relevantSourceList.OrderByDescending(x => x.Score).Take(app.MaxMatchesCount).ToList();
}
这个两阶段检索(Retrieve + Rerank)是目前RAG的最佳实践:
-
第一阶段:向量检索,快速筛选出候选集(比如100条)
-
第二阶段:精排模型,精确排序取Top-K(比如3条)
步骤3:Prompt工程的艺术
检索到相关文档后,如何让AI基于这些文档回答?关键在于Prompt的设计。
history.AddSystemMessage($"{app.Prompt}\n\n参考资料:\n{dataMsg}");
一个典型的RAG Prompt长这样:
你是一个专业的企业知识助手。请基于以下参考资料回答用户问题。参考资料:
【员工手册.pdf】
第3.2条 年假制度
正式员工享有带薪年假,具体天数根据工龄计算:
- 工作满1年:5天
- 工作满3年:10天
- 工作满5年:15天【考勤管理规定.docx】
年假申请需提前一周提交,经部门主管批准后生效。用户问题:公司的年假政策是什么?请基于参考资料回答,如果参考资料中没有相关信息,请明确告知用户。
这个Prompt设计有几个关键点:
-
角色定位:明确AI的身份和职责
-
资料来源:标注每段资料的出处,增强可信度
-
回答要求:要求基于资料回答,避免胡编乱造
-
边界意识:如果资料中没有,要诚实告知
3.2 插件系统:让AI"手脚并用"
如果说RAG让AI有了"知识",那么插件系统就让AI有了"能力"。
三种插件类型
AntSK支持三种插件:
1. .NET原生插件(Native Plugin)
这是最强大的插件类型,用C#编写,可以调用任何.NET库。
[Description("AntSK:获取订单信息")]
public class OrderPlugin
{private readonly IOrderService _orderService;public OrderPlugin(IOrderService orderService){_orderService = orderService;}[KernelFunction][Description("根据订单号查询订单详情")]public async Task<string> GetOrder([Description("订单号")] string orderId){var order = await _orderService.GetOrderByIdAsync(orderId);return JsonSerializer.Serialize(new{OrderId = order.Id,ProductName = order.ProductName,Quantity = order.Quantity,Price = order.Price,Status = order.Status,ShippingAddress = order.ShippingAddress});}[KernelFunction][Description("取消订单")]public async Task<string> CancelOrder([Description("订单号")] string orderId,[Description("取消原因")] string reason){var result = await _orderService.CancelOrderAsync(orderId, reason);return result.Success ? "订单已成功取消" : $"取消失败:{result.Message}";}
}
使用场景:
用户:帮我查一下订单12345
AI:[调用GetOrder("12345")]
系统:返回订单详情JSON
AI:您的订单12345是小米MIX4手机,数量1个,价格4999元,当前状态是已发货...用户:帮我取消这个订单,我不想要了
AI:[调用CancelOrder("12345", "用户不想要了")]
系统:订单已成功取消
AI:好的,订单12345已经为您取消了
2. API插件(API Plugin)
基于OpenAPI规范的HTTP接口,可以集成任何RESTful API。
private async Task ImportApiFunction(Apps app, List<KernelFunction> functions)
{if (!string.IsNullOrWhiteSpace(app.ApiFunctionList)){var apiIdList = app.ApiFunctionList.Split(",");var apis = _apis_Repositories.GetList(p => apiIdList.Contains(p.Id));foreach (var api in apis){// 解析OpenAPI文档var openApiDocument = await OpenApiDocument.ParseAsync(api.Content);// 导入为SK插件var plugin = await _kernel.ImportPluginFromOpenApiAsync(api.Name,openApiDocument,new OpenApiFunctionExecutionParameters(){HttpClient = httpClient,EnableDynamicPayload = true});functions.AddRange(plugin);}}
}
只需要提供一个OpenAPI文档(Swagger JSON),AntSK就能自动解析并生成插件。这意味着你可以轻松集成:
-
天气API
-
地图API
-
支付API
-
任何符合OpenAPI规范的第三方服务
3. 函数插件(Function Plugin)
最轻量级的插件,直接在配置中定义。
{"name": "GetCurrentTime","description": "获取当前时间","parameters": {"timezone": {"type": "string","description": "时区,如Asia/Shanghai"}}
}
适合简单的工具函数,不需要复杂的业务逻辑。
插件的动态加载机制
AntSK的插件系统最巧妙的地方在于动态加载:
public void ImportFunctionsByApp(Apps app, Kernel kernel)
{List<KernelFunction> functions = new List<KernelFunction>();// 加载.NET插件if (!string.IsNullOrWhiteSpace(app.NativeFunctionList)){var functionIdList = app.NativeFunctionList.Split(",");var nativeFunctions = _functionService.GetFunctions().Where(f => functionIdList.Contains(f.Id));foreach (var func in nativeFunctions){kernel.ImportPluginFromType(func.Type, func.Name);}}// 加载API插件ImportApiFunction(app, functions);// 加载函数插件ImportPromptFunction(app, functions);
}
这意味着:
-
不同的应用可以配置不同的插件
-
插件可以在运行时动态启用/禁用
-
无需重启系统即可更新插件
3.3 多模型管理:一个平台,多种选择
企业在选择AI模型时,往往面临两难:
-
云端模型(OpenAI、Claude):效果好,但成本高、数据安全有顾虑
-
本地模型(Llama、ChatGLM):数据安全,但效果差、部署复杂
AntSK的解决方案是:全都要!
支持的模型类型
从代码可以看出,AntSK支持的模型类型非常丰富:
public enum AIType
{OpenAI, // OpenAI GPT系列AzureOpenAI, // Azure托管的OpenAISparkDesk, // 讯飞星火DashScope, // 阿里云积灵LLamaFactory, // 本地模型(通过LlamaFactory)Ollama, // 本地模型(通过Ollama)Mock // 测试用的模拟模型
}
本地模型的部署:LlamaFactory vs Ollama
LlamaFactory:
-
基于Python的模型推理框架
-
支持模型微调
-
适合需要定制化训练的场景
case AIType.LLamaFactory:builder.AddOpenAIChatCompletion(modelId: chatModel.ModelName,apiKey: "NotNull", // 本地模型不需要真实API KeyhttpClient: chatHttpClient); // 指向本地服务break;
Ollama:
-
Go语言编写的模型运行时
-
开箱即用,无需Python环境
-
适合快速部署和测试
case AIType.Ollama:builder.AddOpenAIChatCompletion(modelId: chatModel.ModelName,apiKey: "NotNull",httpClient: chatHttpClient);break;
两者都兼容OpenAI API格式,所以代码实现几乎一样,只是后端服务不同。
模型切换的灵活性
在AntSK中,切换模型就像换衣服一样简单:
{"apps": [{"id": "app1","name": "客服机器人","chatModelID": "gpt-4", // 使用GPT-4"embeddingModelID": "bge-large"},{"id": "app2","name": "内部知识库","chatModelID": "llama-3-8b", // 使用本地Llama3"embeddingModelID": "bge-large"}]
}
这种设计让企业可以根据场景灵活选择:
-
对外服务:用云端模型,保证效果
-
内部使用:用本地模型,保证安全
-
开发测试:用Mock模型,节省成本
3.4 文生图功能:Stable Diffusion的集成
除了文本生成,AntSK还集成了Stable Diffusion,提供文生图能力。
技术实现:P/Invoke调用C++库
Stable Diffusion的核心是用C++实现的,AntSK通过P/Invoke调用:
public static class Native
{[DllImport("stable-diffusion.dll", CallingConvention = CallingConvention.Cdecl)]public static extern SDImagePtr txt2img(IntPtr sd_ctx,string prompt,string negative_prompt,int clip_skip,float cfg_scale,int width,int height,SampleMethod sample_method,int sample_steps,long seed,int batch_count);
}public static Bitmap[] TextToImage(TextToImageParams parameters)
{SDImagePtr sd_Image_ptr = Native.txt2img(sd_ctx,parameters.Prompt,parameters.NegativePrompt,parameters.ClipSkip,parameters.CfgScale,parameters.Width,parameters.Height,parameters.SampleMethod,parameters.SampleSteps,parameters.Seed,parameters.BatchCount);Bitmap[] images = new Bitmap[parameters.BatchCount];for (int i = 0; i < parameters.BatchCount; i++){SDImage sd_image = Marshal.PtrToStructure<SDImage>(sd_Image_ptr + i * Marshal.SizeOf<SDImage>());images[i] = GetBitmapFromSdImage(sd_image);}return images;
}
这段代码展示了.NET与非托管代码交互的典型模式:
-
使用
DllImport声明外部函数 -
通过
Marshal类处理内存操作 -
将非托管内存转换为.NET对象(Bitmap)
应用场景
文生图功能可以用于:
-
营销素材生成:根据文案自动生成配图
-
产品原型设计:快速生成设计草图
-
教育培训:生成教学插图
四、技术栈深度分析:现代.NET的最佳实践
4.1 .NET 9的新特性应用
AntSK基于.NET 9构建,充分利用了最新的语言特性和框架能力。
主构造函数(Primary Constructors)
这是C# 12引入的特性,在AntSK中被广泛使用:
[ServiceDescription(typeof(IChatService), ServiceLifetime.Scoped)]
public class ChatService(IKernelService _kernelService,IKMService _kMService,IKmsDetails_Repositories _kmsDetails_Repositories,IAIModels_Repositories _aIModels_Repositories
) : IChatService
{// 直接使用构造函数参数,无需声明字段public async Task<string> GetAnswer(string question){var kernel = _kernelService.GetKernelByApp(app);// ...}
}
对比传统写法:
// 传统写法:需要声明字段并赋值
public class ChatService : IChatService
{private readonly IKernelService _kernelService;private readonly IKMService _kMService;private readonly IKmsDetails_Repositories _kmsDetails_Repositories;private readonly IAIModels_Repositories _aIModels_Repositories;public ChatService(IKernelService kernelService,IKMService kMService,IKmsDetails_Repositories kmsDetails_Repositories,IAIModels_Repositories aIModels_Repositories){_kernelService = kernelService;_kMService = kMService;_kmsDetails_Repositories = kmsDetails_Repositories;_aIModels_Repositories = aIModels_Repositories;}
}
主构造函数让代码更简洁,减少了样板代码。在AntSK这种服务众多的项目中,这个特性节省了大量代码。
可空引用类型(Nullable Reference Types)
<PropertyGroup><Nullable>enable</Nullable>
</PropertyGroup>
启用可空引用类型后,编译器会在编译时检查空引用问题:
public async Task<Apps?> GetAppById(string id)
{return await _apps_Repositories.GetByIdAsync(id);
}// 调用时必须处理null情况
var app = await GetAppById(appId);
if (app is null)
{return "应用不存在";
}
这个特性大大减少了运行时的NullReferenceException,提升了代码的健壮性。
异步流(IAsyncEnumerable)
这是AntSK实现流式输出的关键技术:
public async IAsyncEnumerable<string> SendChatByAppAsync(Apps app, ChatHistory history)
{var chatResult = chat.GetStreamingChatMessageContentsAsync(history, settings, _kernel);await foreach (var content in chatResult){yield return content.ConvertToString();}
}
为什么不用Task<List<string>>?
因为AI生成是一个持续的过程,如果等所有内容生成完再返回,用户体验会很差。使用IAsyncEnumerable,可以边生成边返回,实现流式输出。
在Blazor中的使用:
@code {private async Task SendMessage(){await foreach (var chunk in chatService.SendChatByAppAsync(app, history)){currentMessage += chunk;StateHasChanged(); // 触发UI更新}}
}
每次yield return,Blazor就会更新UI,用户就能看到文字一个个蹦出来。
4.2 依赖注入的高级用法
AntSK的依赖注入设计非常优雅,使用了自定义的服务注册机制。
自动服务注册
builder.Services.AddServicesFromAssemblies("AntSK");
builder.Services.AddServicesFromAssemblies("AntSK.Domain");
这两行代码会自动扫描程序集,注册所有带有ServiceDescription特性的服务:
[ServiceDescription(typeof(IChatService), ServiceLifetime.Scoped)]
public class ChatService : IChatService
{// ...
}
实现原理:
public static IServiceCollection AddServicesFromAssemblies(this IServiceCollection services, string assemblyName)
{var assembly = Assembly.Load(assemblyName);var types = assembly.GetTypes().Where(t => t.GetCustomAttribute<ServiceDescriptionAttribute>() != null);foreach (var type in types){var attr = type.GetCustomAttribute<ServiceDescriptionAttribute>();switch (attr.Lifetime){case ServiceLifetime.Singleton:services.AddSingleton(attr.ServiceType, type);break;case ServiceLifetime.Scoped:services.AddScoped(attr.ServiceType, type);break;case ServiceLifetime.Transient:services.AddTransient(attr.ServiceType, type);break;}}return services;
}
这种设计的好处:
-
约定优于配置:不需要在
Program.cs中逐个注册服务 -
自动发现:新增服务只需加特性,无需修改启动代码
-
生命周期明确:服务的生命周期在定义处声明,一目了然
键化服务(Keyed Services)
.NET 8引入了键化服务,AntSK在多模型支持中使用了这个特性:
case AIType.SparkDesk:builder.Services.AddKeyedSingleton<IChatCompletionService>("spark-desk-chat", // 键new SparkDeskChatCompletion(options, chatModel.Id));break;case AIType.Mock:builder.Services.AddKeyedSingleton<IChatCompletionService>("mock-chat", // 键new MockChatCompletion());break;
使用时可以通过键获取特定的服务:
var sparkService = serviceProvider.GetKeyedService<IChatCompletionService>("spark-desk-chat");
var mockService = serviceProvider.GetKeyedService<IChatCompletionService>("mock-chat");
这让同一个接口可以有多个实现并存,非常适合多模型场景。
4.3 数据访问层:SqlSugar的灵活性
AntSK使用SqlSugar作为ORM框架,这是一个国产的高性能ORM,支持多种数据库。
多数据库支持
{"DBConnection": {"DbType": "Sqlite", // 可选:Sqlite、PostgreSQL、MySQL、SqlServer、Oracle等"ConnectionStrings": "Data Source=AntSK.db;"}
}
为什么选择SqlSugar而不是EF Core?
-
性能更好:SqlSugar的查询性能比EF Core快20-30%
-
更灵活:支持更多的数据库类型,包括国产数据库(达梦、人大金仓等)
-
学习曲线平缓:API设计更接近SQL,容易上手
CodeFirst模式
AntSK使用CodeFirst模式,数据库表结构由代码定义:
public class Apps
{[SugarColumn(IsPrimaryKey = true, Length = 50)]public string Id { get; set; }[SugarColumn(Length = 100)]public string Name { get; set; }[SugarColumn(Length = 50)]public string ChatModelID { get; set; }[SugarColumn(Length = 50)]public string EmbeddingModelID { get; set; }[SugarColumn(ColumnDataType = "text")]public string Prompt { get; set; }public int Temperature { get; set; }public DateTime CreateTime { get; set; }
}
首次运行时,AntSK会自动创建表结构:
app.CodeFirst(); // 在Program.cs中调用public static void CodeFirst(this WebApplication app)
{var db = app.Services.GetRequiredService<ISqlSugarClient>();// 自动创建表db.CodeFirst.InitTables(typeof(Apps),typeof(AIModels),typeof(Kmss),typeof(KmsDetails),typeof(Apis),typeof(Users));
}
这种模式的好处:
-
版本控制友好:表结构变更体现在代码中,易于追踪
-
部署简单:不需要手动执行SQL脚本,系统自动建表
-
跨数据库:同一套代码可以在不同数据库上运行
4.4 配置管理:分层配置的最佳实践
AntSK的配置管理采用了分层设计,既灵活又安全。
配置文件结构
{"DBConnection": {"DbType": "Sqlite","ConnectionStrings": "Data Source=AntSK.db;"},"KernelMemory": {"VectorDb": "Disk","ConnectionString": "","TableNamePrefix": "km-"},"FileDir": {"DirectoryPath": "D:\\model"},"Login": {"User": "admin","Password": "admin"},"BackgroundTaskBroker": {"ImportKMSTask": {"WorkerCount": 1}}
}
强类型配置
AntSK使用Options模式,将配置映射为强类型对象:
public class DBConnectionOption
{public string DbType { get; set; }public string ConnectionStrings { get; set; }
}public class KernelMemoryOption
{public string VectorDb { get; set; }public string ConnectionString { get; set; }public string TableNamePrefix { get; set; }
}// 在Program.cs中注册
builder.Services.Configure<DBConnectionOption>(builder.Configuration.GetSection("DBConnection"));
builder.Services.Configure<KernelMemoryOption>(builder.Configuration.GetSection("KernelMemory"));
使用时通过依赖注入获取:
public class MyService
{private readonly DBConnectionOption _dbOption;public MyService(IOptions<DBConnectionOption> dbOption){_dbOption = dbOption.Value;}public void Connect(){var connectionString = _dbOption.ConnectionStrings;// ...}
}
强类型配置的优势:
-
编译时检查:配置项拼写错误会在编译时发现
-
智能提示:IDE可以提供自动完成
-
类型安全:避免类型转换错误
环境变量覆盖
在Docker部署时,可以通过环境变量覆盖配置:
services:antsk:image: antsk:latestenvironment:- DBConnection__DbType=PostgreSQL- DBConnection__ConnectionStrings=Host=postgres;Database=antsk;...- KernelMemory__VectorDb=Postgres
注意双下划线__表示配置层级,这是.NET配置系统的约定。
五、部署架构与运维实践
5.1 Docker容器化部署
AntSK提供了两种Docker部署方案,适应不同的场景需求。
简化版部署:快速上手
# docker-compose.simple.yml
version: '3.8'
services:antsk:container_name: antsk-simpleimage: registry.cn-hangzhou.aliyuncs.com/AIDotNet/antsk:latestports:- "5000:5000"environment:- ASPNETCORE_URLS=http://*:5000volumes:- ./appsettings.json:/app/appsettings.json- ./data:/app/datarestart: always
特点:
-
单容器部署:所有组件在一个容器中
-
SQLite数据库:无需额外数据库服务
-
磁盘向量存储:简化配置
-
适用场景:开发测试、小规模部署(<100用户)
启动命令:
docker-compose -f docker-compose.simple.yml up -d
完整版部署:生产级方案
# docker-compose.yml
version: '3.8'
services:antsk:container_name: antskimage: registry.cn-hangzhou.aliyuncs.com/AIDotNet/antsk:latestports:- "5000:5000"depends_on:- postgres- redisenvironment:- ASPNETCORE_URLS=http://*:5000- DBConnection__DbType=PostgreSQL- DBConnection__ConnectionStrings=Host=postgres;Database=antsk;Username=antsk;Password=${DB_PASSWORD}- KernelMemory__VectorDb=Postgres- KernelMemory__ConnectionString=Host=postgres;Database=antsk;Username=antsk;Password=${DB_PASSWORD}volumes:- ./appsettings.json:/app/appsettings.json- ./model:/app/modelrestart: alwaysnetworks:- antsk-networkpostgres:image: ankane/pgvector:latestcontainer_name: antsk-postgresenvironment:- POSTGRES_DB=antsk- POSTGRES_USER=antsk- POSTGRES_PASSWORD=${DB_PASSWORD}volumes:- postgres-data:/var/lib/postgresql/dataports:- "5432:5432"restart: alwaysnetworks:- antsk-networkredis:image: redis:alpinecontainer_name: antsk-redisports:- "6379:6379"volumes:- redis-data:/datarestart: alwaysnetworks:- antsk-networkvolumes:postgres-data:redis-data:networks:antsk-network:driver: bridge
特点:
-
多容器架构:应用、数据库、缓存分离
-
PostgreSQL + pgvector:专业的向量数据库
-
Redis缓存:提升查询性能
-
数据持久化:使用Docker Volume
-
适用场景:生产环境、大规模部署(>1000用户)
启动命令:
# 设置数据库密码
export DB_PASSWORD=your_secure_password# 启动服务
docker-compose up -d# 查看日志
docker-compose logs -f antsk
5.2 本地模型部署:LlamaFactory实战
对于需要完全私有化部署的企业,本地模型是最佳选择。AntSK集成了LlamaFactory,让本地模型部署变得简单。
环境准备
# 1. 安装Python 3.8+
python --version# 2. 安装LlamaFactory依赖
pip install -r requirements.txt# requirements.txt内容:
transformers>=4.37.0
torch>=2.0.0
accelerate>=0.20.0
peft>=0.4.0
datasets>=2.12.0
模型下载与配置
AntSK支持从ModelScope(魔塔社区)自动下载模型:
{"modelList": [{"name": "Qwen2-7B-Instruct","path": "Qwen/Qwen2-7B-Instruct","template": "qwen"},{"name": "ChatGLM3-6B","path": "THUDM/chatglm3-6b","template": "chatglm3"},{"name": "Llama-3-8B-Instruct","path": "meta-llama/Meta-Llama-3-8B-Instruct","template": "llama3"}]
}
启动本地模型服务
# 启动LlamaFactory API服务
python -m llamafactory.api \--model_name_or_path /app/model/Qwen2-7B-Instruct \--template qwen \--port 8000# 服务启动后,会提供OpenAI兼容的API
# http://localhost:8000/v1/chat/completions
在AntSK中配置
{"AIModels": [{"id": "local-qwen","name": "Qwen2-7B本地模型","aiType": "LLamaFactory","modelName": "Qwen2-7B-Instruct","endPoint": "http://localhost:8000/v1","modelKey": "NotNull"}]
}
性能优化建议
1. 量化加速
使用4-bit量化可以大幅减少显存占用:
python -m llamafactory.api \--model_name_or_path /app/model/Qwen2-7B-Instruct \--template qwen \--quantization_bit 4 \--port 8000
2. GPU加速
如果有NVIDIA GPU,启用CUDA加速:
# 检查CUDA是否可用
python -c "import torch; print(torch.cuda.is_available())"# 启动时会自动使用GPU
python -m llamafactory.api \--model_name_or_path /app/model/Qwen2-7B-Instruct \--template qwen \--device cuda \--port 8000
3. 批处理优化
调整批处理大小以平衡速度和显存:
python -m llamafactory.api \--model_name_or_path /app/model/Qwen2-7B-Instruct \--template qwen \--max_batch_size 8 \--port 8000
5.3 监控与日志:Aspire集成
AntSK集成了.NET Aspire,提供了开箱即用的监控能力。
Aspire Dashboard
// Program.cs
builder.AddServiceDefaults(); // 添加Aspire支持// 配置OpenTelemetry
builder.Services.Configure<OtlpExporterOptions>(o => o.Headers = $"x-otlp-api-key=antsk");
启动后,访问http://localhost:18888可以看到Aspire Dashboard,提供:
-
实时日志:所有服务的日志聚合
-
性能追踪:请求链路追踪
-
指标监控:CPU、内存、请求量等
结构化日志
AntSK使用Serilog进行日志记录:
{"Serilog": {"Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File"],"MinimumLevel": "Information","WriteTo": [{ "Name": "Console" },{"Name": "File","Args": {"path": "logs/log-.txt","rollingInterval": "Day","fileSizeLimitBytes": 10485760}}]}
}
日志级别建议:
-
开发环境:Debug
-
测试环境:Information
-
生产环境:Warning
六、实战应用场景:从理论到落地
6.1 企业知识管理系统
场景描述
某大型制造企业有以下痛点:
-
技术文档散落在各个部门,查找困难
-
新员工培训需要大量人力
-
重复性问题咨询占用专家时间
解决方案
1. 知识库构建
// 批量导入文档
var documents = new[]
{"产品手册.pdf","技术规范.docx","操作指南.pptx","常见问题.xlsx"
};foreach (var doc in documents)
{await memory.ImportDocumentAsync(new Document(Guid.NewGuid().ToString()).AddFile(doc).AddTag("department", "技术部").AddTag("category", "产品文档"),index: "company-knowledge");
}
2. 智能问答配置
{"app": {"name": "企业知识助手","prompt": "你是公司的技术支持专家,请基于公司文档回答问题。如果文档中没有相关信息,请明确告知用户并建议联系相关部门。","chatModelID": "gpt-4","embeddingModelID": "bge-large-zh","maxMatchesCount": 5,"rerankModelID": "bge-rerank"}
}
3. 权限控制
// 基于部门的文档访问控制
var filters = new List<MemoryFilter>()
{new MemoryFilter().ByTag("department", user.Department)
};var searchResult = await memory.SearchAsync(question,index: "company-knowledge",filters: filters);
实施效果
-
**查询效率提升80%**:从平均10分钟找到答案缩短到2分钟
-
**培训成本降低60%**:新员工可以自助查询,减少培训师工作量
-
专家时间释放:重复性问题减少70%,专家可以专注于复杂问题
6.2 智能客服系统
场景描述
某电商平台每天处理上万条客户咨询,人工客服压力巨大,且夜间无人值守。
解决方案
1. 多轮对话设计
public class CustomerServicePlugin
{[KernelFunction][Description("查询订单状态")]public async Task<string> GetOrderStatus([Description("订单号")] string orderId){var order = await _orderService.GetOrderAsync(orderId);return $"""订单号:{order.Id}商品:{order.ProductName}状态:{order.Status}物流:{order.ShippingInfo}预计送达:{order.EstimatedDelivery:yyyy-MM-dd}""";}[KernelFunction][Description("申请退款")]public async Task<string> RequestRefund([Description("订单号")] string orderId,[Description("退款原因")] string reason){var result = await _orderService.CreateRefundRequestAsync(orderId, reason);if (result.Success){return $"退款申请已提交,预计3-5个工作日处理完成。退款单号:{result.RefundId}";}else{return $"退款申请失败:{result.ErrorMessage}。请联系人工客服处理。";}}[KernelFunction][Description("查询物流信息")]public async Task<string> GetShippingInfo([Description("订单号或快递单号")] string trackingNumber){var tracking = await _shippingService.GetTrackingInfoAsync(trackingNumber);var sb = new StringBuilder();sb.AppendLine($"快递公司:{tracking.Carrier}");sb.AppendLine($"快递单号:{tracking.TrackingNumber}");sb.AppendLine($"当前状态:{tracking.Status}");sb.AppendLine("\n物流轨迹:");foreach (var record in tracking.Records){sb.AppendLine($"{record.Time:MM-dd HH:mm} {record.Location} {record.Description}");}return sb.ToString();}
}
2. 对话流程示例
用户:我的订单什么时候能到?
AI:请提供您的订单号,我帮您查询一下。用户:12345678
AI:[调用GetOrderStatus("12345678")]您的订单12345678(小米手机)已发货,快递单号:SF1234567890,预计明天(12月25日)送达。需要查看详细物流信息吗?用户:好的
AI:[调用GetShippingInfo("SF1234567890")]快递公司:顺丰速运快递单号:SF1234567890当前状态:运输中物流轨迹:12-23 10:30 深圳 已揽收12-23 15:20 深圳转运中心 已发出12-24 08:15 上海转运中心 已到达12-24 14:30 上海XX站 派送中用户:我不想要了,能退款吗?
AI:可以的。请问您的退款原因是什么?用户:买错了
AI:[调用RequestRefund("12345678", "买错了")]退款申请已提交,预计3-5个工作日处理完成。退款单号:RF20231224001您可以在"我的订单-退款记录"中查看进度。
3. 知识库集成
// 导入常见问题文档
await memory.ImportDocumentAsync(new Document("faq").AddFile("常见问题.md").AddTag("type", "faq"),index: "customer-service");// 在对话中自动检索相关FAQ
var relevantFAQs = await memory.SearchAsync(userQuestion,index: "customer-service",filters: new[] { new MemoryFilter().ByTag("type", "faq") });
实施效果
-
24/7全天候服务:夜间咨询也能及时响应
-
**响应速度提升90%**:从平均5分钟缩短到30秒
-
**客服成本降低50%**:AI处理70%的常规问题,人工只处理复杂问题
-
客户满意度提升:从75%提升到92%
6.3 研发团队的代码助手
场景描述
某软件公司有多个项目,新人上手困难,老员工也经常需要查阅文档。
解决方案
1. 代码库索引
// 导入项目文档
var projectDocs = new[]
{"README.md","API文档.md","架构设计.md","开发规范.md","部署指南.md"
};foreach (var doc in projectDocs)
{await memory.ImportDocumentAsync(new Document(Guid.NewGuid().ToString()).AddFile($"docs/{doc}").AddTag("project", "AntSK").AddTag("type", "documentation"),index: "dev-knowledge");
}
2. 代码搜索插件
public class CodeSearchPlugin
{[KernelFunction][Description("搜索代码片段")]public async Task<string> SearchCode([Description("搜索关键词")] string keyword,[Description("文件类型,如cs、js等")] string fileType = ""){var results = await _codeSearchService.SearchAsync(keyword, fileType);var sb = new StringBuilder();foreach (var result in results.Take(5)){sb.AppendLine($"文件:{result.FilePath}");sb.AppendLine($"行号:{result.LineNumber}");sb.AppendLine("```" + result.Language);sb.AppendLine(result.CodeSnippet);sb.AppendLine("```");sb.AppendLine();}return sb.ToString();}[KernelFunction][Description("查询API使用方法")]public async Task<string> GetApiUsage([Description("API名称")] string apiName){var doc = await _apiDocService.GetDocumentationAsync(apiName);return $"""API:{doc.Name}描述:{doc.Description}参数:{string.Join("\n", doc.Parameters.Select(p => $"- {p.Name} ({p.Type}): {p.Description}"))}返回值:{doc.ReturnType}示例代码:```csharp{doc.ExampleCode}```""";}
}
3. 对话示例
开发者:如何使用KernelService创建Kernel实例?
AI:[检索文档] 根据架构设计文档,KernelService提供了GetKernelByApp方法...[调用SearchCode("GetKernelByApp", "cs")]在KernelService.cs中的实现如下:```csharppublic Kernel GetKernelByApp(Apps app){var chatModel = _aIModels_Repositories.GetFirst(p => p.Id == app.ChatModelID);var chatHttpClient = OpenAIHttpClientHandlerUtil.GetHttpClient(chatModel.EndPoint);var builder = Kernel.CreateBuilder();WithTextGenerationByAIType(builder, chatModel, chatHttpClient);_kernel = builder.Build();RegisterPluginsWithKernel(_kernel);return _kernel;}```使用方法:1. 注入IKernelService2. 调用GetKernelByApp并传入应用配置3. 返回的Kernel实例已经配置好模型和插件开发者:如何添加一个新的AI模型支持?
AI:[检索文档] 添加新模型需要以下步骤:1. 在AIType枚举中添加新类型2. 在WithTextGenerationByAIType方法中添加case分支3. 实现对应的IChatCompletionService接口参考SparkDesk的实现:[调用SearchCode("case AIType.SparkDesk", "cs")]...
实施效果
-
**新人上手时间缩短60%**:从2周缩短到3天
-
文档查询效率提升:从翻文档10分钟到AI回答30秒
-
代码复用率提升:快速找到已有实现,避免重复造轮子
6.4 教育培训平台
场景描述
某在线教育平台希望为学员提供24小时答疑服务。
解决方案
1. 课程知识库
// 导入课程资料
var courseMaterials = new[]
{"第1章-基础知识.pdf","第2章-进阶内容.pdf","练习题答案.docx","常见错误解析.md"
};foreach (var material in courseMaterials)
{await memory.ImportDocumentAsync(new Document(Guid.NewGuid().ToString()).AddFile($"courses/{courseId}/{material}").AddTag("courseId", courseId).AddTag("chapter", GetChapterNumber(material)),index: "education");
}
2. 个性化学习助手
public class LearningAssistantPlugin
{[KernelFunction][Description("获取学习进度")]public async Task<string> GetLearningProgress([Description("学员ID")] string studentId){var progress = await _learningService.GetProgressAsync(studentId);return $"""当前进度:第{progress.CurrentChapter}章完成度:{progress.CompletionRate}%学习时长:{progress.TotalHours}小时练习正确率:{progress.ExerciseAccuracy}%建议:{progress.Recommendation}""";}[KernelFunction][Description("推荐学习内容")]public async Task<string> RecommendContent([Description("学员ID")] string studentId,[Description("知识点")] string topic){var recommendations = await _learningService.GetRecommendationsAsync(studentId, topic);var sb = new StringBuilder();sb.AppendLine("为您推荐以下学习资源:\n");foreach (var rec in recommendations){sb.AppendLine($"📚 {rec.Title}");sb.AppendLine($" 难度:{rec.Difficulty} | 时长:{rec.Duration}分钟");sb.AppendLine($" {rec.Description}\n");}return sb.ToString();}
}
3. 对话示例
学员:我不理解什么是依赖注入
AI:[检索课程资料] 依赖注入(Dependency Injection,DI)是一种设计模式...简单来说,就是不在类内部创建依赖对象,而是从外部传入。举个生活中的例子:- 传统方式:你要喝咖啡,自己买咖啡豆、磨豆、冲泡- 依赖注入:你要喝咖啡,直接从咖啡店买一杯现成的代码示例:```csharp// 传统方式public class UserService{private readonly UserRepository _repo = new UserRepository(); // 自己创建}// 依赖注入public class UserService{private readonly IUserRepository _repo;public UserService(IUserRepository repo) // 从外部传入{_repo = repo;}}```需要我推荐相关的学习资源吗?学员:好的
AI:[调用RecommendContent(studentId, "依赖注入")]为您推荐以下学习资源:📚 依赖注入详解视频难度:中级 | 时长:30分钟深入讲解DI的原理和.NET中的实现📚 依赖注入实战练习难度:中级 | 时长:45分钟通过实际项目学习DI的应用📚 常见DI错误及解决方案难度:高级 | 时长:20分钟总结开发中常见的DI使用问题
实施效果
-
答疑响应时间:从平均2小时缩短到即时响应
-
学员满意度:从68%提升到89%
-
教师工作量:减少40%的重复性答疑
-
学习效果:课程完成率提升25%
七、性能优化与最佳实践
7.1 向量检索性能优化
索引优化
1. 选择合适的向量维度
// BGE-Large: 1024维,精度高但速度慢
var embedModel = new BGEEmbedding("bge-large-zh-v1.5");// BGE-Base: 768维,平衡精度和速度
var embedModel = new BGEEmbedding("bge-base-zh-v1.5");// BGE-Small: 512维,速度快但精度略低
var embedModel = new BGEEmbedding("bge-small-zh-v1.5");
建议:
-
小规模知识库(<10万条):使用Large模型
-
中等规模(10-100万条):使用Base模型
-
大规模(>100万条):使用Small模型或考虑分片
2. 向量数据库选择
| 数据库 | 适用场景 | 性能 | 成本 |
|---|---|---|---|
| Disk | 开发测试 | ⭐⭐ | 免费 |
| Memory | 小规模、高性能 | ⭐⭐⭐⭐⭐ | 免费(需内存) |
| PostgreSQL+pgvector | 中小规模、需持久化 | ⭐⭐⭐ | 低 |
| Qdrant | 大规模、专业场景 | ⭐⭐⭐⭐ | 中 |
| Redis | 高并发、缓存场景 | ⭐⭐⭐⭐ | 中 |
3. 分块策略优化
// 根据文档类型调整分块参数
var options = documentType switch
{"技术文档" => new TextPartitioningOptions{MaxTokensPerParagraph = 1000, // 技术文档可以大一些MaxTokensPerLine = 300,OverlappingTokens = 100},"对话记录" => new TextPartitioningOptions{MaxTokensPerParagraph = 500, // 对话记录小一些MaxTokensPerLine = 150,OverlappingTokens = 50},_ => new TextPartitioningOptions{MaxTokensPerParagraph = 800,MaxTokensPerLine = 200,OverlappingTokens = 80}
};
7.2 LLM调用优化
1. 缓存策略
public class CachedChatService : IChatService
{private readonly IChatService _innerService;private readonly IMemoryCache _cache;public async Task<string> GetAnswer(string question){var cacheKey = $"chat:{question.GetHashCode()}";if (_cache.TryGetValue(cacheKey, out string cachedAnswer)){return cachedAnswer;}var answer = await _innerService.GetAnswer(question);_cache.Set(cacheKey, answer, TimeSpan.FromHours(1));return answer;}
}
适用场景:
-
常见问题(FAQ)
-
静态内容查询
-
不需要实时数据的场景
注意事项:
-
不要缓存包含个人信息的回答
-
设置合理的过期时间
-
考虑缓存失效策略
2. 批处理优化
public async Task<List<string>> GetAnswersBatch(List<string> questions)
{// 使用Task.WhenAll并行处理var tasks = questions.Select(q => GetAnswer(q));var answers = await Task.WhenAll(tasks);return answers.ToList();
}
3. 流式输出优化
public async IAsyncEnumerable<string> GetAnswerStream(string question)
{var buffer = new StringBuilder();var lastYieldTime = DateTime.Now;await foreach (var chunk in _chatService.GetStreamingAnswer(question)){buffer.Append(chunk);// 每100ms或累积50个字符才yield一次,减少网络开销if ((DateTime.Now - lastYieldTime).TotalMilliseconds > 100 || buffer.Length > 50){yield return buffer.ToString();buffer.Clear();lastYieldTime = DateTime.Now;}}if (buffer.Length > 0){yield return buffer.ToString();}
}
7.3 数据库性能优化
1. 连接池配置
{"DBConnection": {"ConnectionStrings": "Host=localhost;Database=antsk;Username=antsk;Password=***;Pooling=true;MinPoolSize=5;MaxPoolSize=100;ConnectionLifetime=300"}
}
参数说明:
-
MinPoolSize=5:最小连接数,保持5个热连接 -
MaxPoolSize=100:最大连接数,根据并发量调整 -
ConnectionLifetime=300:连接生命周期(秒),定期刷新连接
2. 索引优化
-- 为常用查询字段添加索引
CREATE INDEX idx_apps_chatmodelid ON apps(chat_model_id);
CREATE INDEX idx_kmss_createtime ON kmss(create_time);
CREATE INDEX idx_kmsdetails_kmsid ON kms_details(kms_id);-- 为向量字段添加HNSW索引(PostgreSQL+pgvector)
CREATE INDEX ON km_embeddings USING hnsw (embedding vector_cosine_ops);
3. 查询优化
// 避免N+1查询
var apps = await _apps_Repositories.GetListAsync();
var modelIds = apps.Select(a => a.ChatModelID).Distinct();
var models = await _aIModels_Repositories.GetListAsync(m => modelIds.Contains(m.Id));// 使用字典避免重复查询
var modelDict = models.ToDictionary(m => m.Id);
foreach (var app in apps)
{app.ChatModel = modelDict[app.ChatModelID];
}
7.4 并发控制
1. 限流策略
public class RateLimitMiddleware
{private readonly SemaphoreSlim _semaphore;public RateLimitMiddleware(int maxConcurrency = 10){_semaphore = new SemaphoreSlim(maxConcurrency);}public async Task<T> ExecuteAsync<T>(Func<Task<T>> action){await _semaphore.WaitAsync();try{return await action();}finally{_semaphore.Release();}}
}
2. 队列处理
// 后台任务队列
builder.Services.AddBackgroundTaskBroker().AddHandler<ImportKMSTaskReq, BackGroundTaskHandler>("ImportKMSTask");// 配置工作线程数
{"BackgroundTaskBroker": {"ImportKMSTask": {"WorkerCount": 2 // 根据CPU核心数调整}}
}
建议:
-
在线API:WorkerCount = CPU核心数 * 2
-
本地模型:WorkerCount = GPU数量(避免显存溢出)
八、安全与合规
8.1 数据安全
1. 敏感信息保护
public class SensitiveDataFilter
{private static readonly Regex PhoneRegex = new Regex(@"1[3-9]\d{9}");private static readonly Regex EmailRegex = new Regex(@"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b");private static readonly Regex IdCardRegex = new Regex(@"\d{17}[\dXx]");public string MaskSensitiveData(string text){// 手机号脱敏:138****1234text = PhoneRegex.Replace(text, m => m.Value.Substring(0, 3) + "****" + m.Value.Substring(7));// 邮箱脱敏:abc***@example.comtext = EmailRegex.Replace(text, m =>{var parts = m.Value.Split('@');var localPart = parts[0];var maskedLocal = localPart.Length > 3 ? localPart.Substring(0, 3) + "***" : "***";return maskedLocal + "@" + parts[1];});// 身份证脱敏:110***********1234text = IdCardRegex.Replace(text, m =>m.Value.Substring(0, 3) + "***********" + m.Value.Substring(14));return text;}
}// 在导入文档时自动脱敏
public async Task ImportDocumentWithMask(string filePath)
{var content = await File.ReadAllTextAsync(filePath);var maskedContent = _sensitiveDataFilter.MaskSensitiveData(content);await memory.ImportDocumentAsync(new Document(Guid.NewGuid().ToString()).AddText(maskedContent),index: "knowledge");
}
2. 访问控制
public class DocumentAccessControl
{public async Task<bool> CanAccess(string userId, string documentId){var user = await _userRepository.GetByIdAsync(userId);var document = await _documentRepository.GetByIdAsync(documentId);// 检查部门权限if (document.Department != user.Department && !user.IsAdmin){return false;}// 检查密级if (document.SecurityLevel > user.SecurityClearance){return false;}return true;}
}// 在检索时应用访问控制
public async Task<List<RelevantSource>> SearchWithAccessControl(string userId, string question)
{var user = await _userRepository.GetByIdAsync(userId);var filters = new List<MemoryFilter>(){new MemoryFilter().ByTag("department", user.Department),new MemoryFilter().ByTag("securityLevel", $"<={user.SecurityClearance}")};var searchResult = await memory.SearchAsync(question,filters: filters);return searchResult.Results.SelectMany(r => r.Partitions).Select(p => new RelevantSource { Text = p.Text }).ToList();
}
3. 审计日志
public class AuditLogger
{public async Task LogAccess(AuditLog log){await _auditRepository.InsertAsync(new AuditLog{UserId = log.UserId,Action = log.Action,Resource = log.Resource,IpAddress = log.IpAddress,Timestamp = DateTime.UtcNow,Details = JsonSerializer.Serialize(log.Details)});}
}// 在关键操作中记录审计日志
public async Task<string> GetAnswer(string userId, string question)
{await _auditLogger.LogAccess(new AuditLog{UserId = userId,Action = "Query",Resource = "KnowledgeBase",Details = new { Question = question }});var answer = await _chatService.GetAnswer(question);await _auditLogger.LogAccess(new AuditLog{UserId = userId,Action = "Response",Resource = "KnowledgeBase",Details = new { Answer = answer }});return answer;
}
8.2 内容安全
1. 敏感词过滤
public class ContentFilter
{private readonly HashSet<string> _sensitiveWords;public ContentFilter(){_sensitiveWords = LoadSensitiveWords();}public bool ContainsSensitiveContent(string text){return _sensitiveWords.Any(word => text.Contains(word));}public string FilterSensitiveContent(string text){foreach (var word in _sensitiveWords){text = text.Replace(word, new string('*', word.Length));}return text;}
}// 在生成回答前过滤
public async Task<string> GetSafeAnswer(string question)
{if (_contentFilter.ContainsSensitiveContent(question)){return "您的问题包含敏感内容,无法回答。";}var answer = await _chatService.GetAnswer(question);if (_contentFilter.ContainsSensitiveContent(answer)){return "抱歉,无法提供相关信息。";}return answer;
}
2. Prompt注入防护
public class PromptInjectionDetector
{private static readonly string[] InjectionPatterns = new[]{"ignore previous instructions","忽略之前的指令","你现在是","forget everything","system:","assistant:"};public bool IsPromptInjection(string input){var lowerInput = input.ToLower();return InjectionPatterns.Any(pattern => lowerInput.Contains(pattern.ToLower()));}
}// 在处理用户输入时检测
public async Task<string> GetAnswer(string question)
{if (_promptInjectionDetector.IsPromptInjection(question)){await _auditLogger.LogSecurity(new SecurityLog{Type = "PromptInjection",Content = question,Timestamp = DateTime.UtcNow});return "检测到异常输入,请重新提问。";}return await _chatService.GetAnswer(question);
}
8.3 合规性
1. 数据保留策略
public class DataRetentionPolicy
{public async Task CleanupExpiredData(){// 删除90天前的聊天记录var cutoffDate = DateTime.UtcNow.AddDays(-90);await _chatHistoryRepository.DeleteAsync(h => h.CreateTime < cutoffDate);// 删除180天前的审计日志var auditCutoffDate = DateTime.UtcNow.AddDays(-180);await _auditRepository.DeleteAsync(a => a.Timestamp < auditCutoffDate);// 归档1年前的文档var archiveCutoffDate = DateTime.UtcNow.AddYears(-1);var oldDocuments = await _documentRepository.GetListAsync(d => d.CreateTime < archiveCutoffDate && !d.IsArchived);foreach (var doc in oldDocuments){await ArchiveDocument(doc);}}
}
2. GDPR合规
public class GDPRService
{// 用户数据导出public async Task<byte[]> ExportUserData(string userId){var userData = new{Profile = await _userRepository.GetByIdAsync(userId),ChatHistory = await _chatHistoryRepository.GetListAsync(h => h.UserId == userId),Documents = await _documentRepository.GetListAsync(d => d.CreatedBy == userId),AuditLogs = await _auditRepository.GetListAsync(a => a.UserId == userId)};var json = JsonSerializer.Serialize(userData, new JsonSerializerOptions { WriteIndented = true });return Encoding.UTF8.GetBytes(json);}// 用户数据删除(被遗忘权)public async Task DeleteUserData(string userId){// 删除用户资料await _userRepository.DeleteAsync(userId);// 删除聊天记录await _chatHistoryRepository.DeleteAsync(h => h.UserId == userId);// 删除文档(或转移所有权)var documents = await _documentRepository.GetListAsync(d => d.CreatedBy == userId);foreach (var doc in documents){doc.CreatedBy = "deleted_user";await _documentRepository.UpdateAsync(doc);}// 匿名化审计日志var auditLogs = await _auditRepository.GetListAsync(a => a.UserId == userId);foreach (var log in auditLogs){log.UserId = "anonymous";await _auditRepository.UpdateAsync(log);}}
}
九、未来展望与技术趋势
9.1 多模态AI的深度集成
当前AntSK主要处理文本,未来将深度集成多模态能力:
1. 图像理解
// 未来的多模态对话
public async Task<string> GetMultimodalAnswer(string question, List<string> images)
{var imageDescriptions = new List<string>();foreach (var image in images){var description = await _visionModel.DescribeImage(image);imageDescriptions.Add(description);}var enhancedPrompt = $"""用户问题:{question}相关图片描述:{string.Join("\n", imageDescriptions)}请结合图片内容回答问题。""";return await _chatService.GetAnswer(enhancedPrompt);
}
应用场景:
-
医疗影像分析
-
工业质检
-
建筑设计审查
2. 视频内容理解
public async Task ImportVideoDocument(string videoPath)
{// 提取视频帧var frames = await _videoProcessor.ExtractKeyFrames(videoPath);// 提取音频并转文字var transcript = await _speechToText.TranscribeAsync(videoPath);// 分析每一帧var frameDescriptions = new List<string>();foreach (var frame in frames){var description = await _visionModel.DescribeImage(frame);frameDescriptions.Add($"[{frame.Timestamp}] {description}");}// 合并为文档var document = $"""视频标题:{Path.GetFileNameWithoutExtension(videoPath)}语音内容:{transcript}画面内容:{string.Join("\n", frameDescriptions)}""";await memory.ImportDocumentAsync(new Document(Guid.NewGuid().ToString()).AddText(document),index: "video-knowledge");
}
9.2 Agent工作流编排
基于Semantic Kernel的规划能力,构建更智能的Agent:
public class AgentOrchestrator
{public async Task<string> ExecuteComplexTask(string task){// 1. 任务分解var plan = await _planner.CreatePlanAsync(task);// 2. 执行计划var context = new KernelArguments();foreach (var step in plan.Steps){var result = await step.InvokeAsync(_kernel, context);context[step.OutputVariable] = result;}// 3. 汇总结果return context["final_result"].ToString();}
}// 示例:复杂的业务任务
var task = "分析上个月的销售数据,找出表现最好的产品,并生成一份包含图表的报告";// Agent会自动分解为:
// 1. 查询销售数据
// 2. 数据分析
// 3. 生成图表
// 4. 撰写报告
// 5. 格式化输出
9.3 边缘计算与模型优化
针对私有化部署需求,未来将提供更多优化方案:
1. 模型量化
public class ModelQuantizer
{public async Task QuantizeModel(string modelPath, QuantizationType type){switch (type){case QuantizationType.INT8:// 8位整数量化,模型大小减少75%await QuantizeToINT8(modelPath);break;case QuantizationType.INT4:// 4位整数量化,模型大小减少87.5%await QuantizeToINT4(modelPath);break;case QuantizationType.GPTQ:// GPTQ量化,保持精度的同时减少大小await QuantizeWithGPTQ(modelPath);break;}}
}
2. 推理加速
public class InferenceOptimizer
{public async Task OptimizeForHardware(string modelPath, HardwareType hardware){switch (hardware){case HardwareType.NVIDIA_GPU:// 使用TensorRT优化await OptimizeWithTensorRT(modelPath);break;case HardwareType.AMD_GPU:// 使用ROCm优化await OptimizeWithROCm(modelPath);break;case HardwareType.CPU:// 使用ONNX Runtime优化await OptimizeWithONNX(modelPath);break;case HardwareType.NPU:// 使用专用AI芯片await OptimizeForNPU(modelPath);break;}}
}
9.4 企业级治理能力
1. 模型监控
public class ModelMonitor
{public async Task MonitorModelPerformance(){var metrics = new{// 性能指标AverageLatency = await CalculateAverageLatency(),TokensPerSecond = await CalculateTokensPerSecond(),// 质量指标UserSatisfaction = await CalculateUserSatisfaction(),AnswerAccuracy = await CalculateAnswerAccuracy(),// 成本指标TotalTokensUsed = await GetTotalTokensUsed(),EstimatedCost = await CalculateEstimatedCost()};await _metricsService.RecordMetrics(metrics);// 告警if (metrics.AverageLatency > 5000) // 超过5秒{await _alertService.SendAlert("模型响应时间过长");}}
}
2. 内容审核
public class ContentModerator
{public async Task<ModerationResult> ModerateContent(string content){var result = new ModerationResult();// 1. 敏感词检测result.HasSensitiveWords = _sensitiveWordDetector.Detect(content);// 2. 情感分析result.Sentiment = await _sentimentAnalyzer.AnalyzeAsync(content);// 3. 主题分类result.Topics = await _topicClassifier.ClassifyAsync(content);// 4. 事实核查result.FactCheckResult = await _factChecker.CheckAsync(content);// 5. 偏见检测result.BiasScore = await _biasDetector.DetectAsync(content);return result;}
}
十、总结与展望
10.1 AntSK的技术价值
通过深入分析AntSK的源码和架构,我们可以总结出以下技术价值:
1. 架构设计的前瞻性
AntSK采用了现代软件工程的最佳实践:
-
领域驱动设计:清晰的业务边界和职责划分
-
依赖注入:高度解耦的服务架构
-
异步编程:充分利用.NET的异步特性
-
插件化架构:灵活的功能扩展能力
这些设计不仅让代码易于维护,更为未来的功能扩展预留了空间。
2. AI技术的深度整合
AntSK不是简单地调用AI API,而是深度整合了多种AI技术:
-
Semantic Kernel:统一的AI编排框架
-
Kernel Memory:专业的知识管理能力
-
RAG架构:检索增强生成的最佳实践
-
多模型支持:云端和本地模型的灵活切换
这种深度整合让AntSK成为了一个真正的AI应用开发平台。
3. 企业级能力的完整性
从数据安全到性能优化,从部署运维到监控告警,AntSK提供了企业级应用所需的完整能力:
-
数据安全:敏感信息保护、访问控制、审计日志
-
性能优化:缓存策略、批处理、并发控制
-
部署灵活:Docker容器化、多数据库支持
-
监控完善:Aspire集成、结构化日志
这些能力让AntSK不仅能用于演示,更能真正落地到生产环境。
10.2 .NET在AI领域的潜力
AntSK的成功,也展示了.NET生态在AI领域的巨大潜力:
1. 性能优势
.NET 9的性能已经达到了业界顶尖水平:
-
高吞吐量:每秒处理数万次请求
-
低延迟:毫秒级的响应时间
-
内存效率:优秀的GC性能
这些优势让.NET非常适合构建高性能的AI应用。
2. 开发效率
C#的现代语言特性大大提升了开发效率:
-
强类型系统:编译时发现错误
-
异步编程:简洁的async/await语法
-
LINQ:强大的数据查询能力
-
主构造函数:减少样板代码
3. 生态完善
.NET拥有丰富的生态系统:
-
Blazor:现代化的Web UI框架
-
EF Core/SqlSugar:成熟的ORM框架
-
Aspire:云原生应用开发
-
丰富的NuGet包:各种功能开箱即用
10.3 给开发者的建议
如果你想基于AntSK开发自己的AI应用,这里有一些建议:
1. 从小规模开始
不要一开始就追求大而全,先从一个具体场景入手:
-
选择一个痛点明确的场景(如FAQ问答)
-
准备少量高质量的文档(100-1000条)
-
使用简化版部署快速验证效果
-
根据反馈逐步优化和扩展
2. 重视数据质量
AI的效果很大程度上取决于数据质量:
-
文档整理:去除无关内容,保留核心信息
-
格式规范:统一的文档格式便于解析
-
定期更新:及时更新过时的信息
-
质量评估:定期检查检索和回答的准确性
3. 持续优化
AI应用需要持续优化才能达到最佳效果:
-
收集反馈:记录用户的满意度和问题
-
分析日志:找出常见的失败案例
-
调整参数:优化检索数量、温度等参数
-
更新模型:尝试新的模型和技术
4. 关注安全
企业应用必须重视安全:
-
数据脱敏:导入前处理敏感信息
-
访问控制:实现细粒度的权限管理
-
审计日志:记录所有关键操作
-
定期审查:检查是否有异常访问
10.4 结语
AntSK的出现,标志着.NET生态在AI领域迈出了重要一步。它不仅是一个优秀的开源项目,更是一个完整的AI应用开发参考实现。
从技术角度看,AntSK成功地将Semantic Kernel、Kernel Memory等前沿AI框架与.NET生态深度融合,创造了一个既强大又易用的AI开发平台。它展示了如何在传统的Web应用框架基础上,构建面向AI时代的新型应用。
从业务角度看,AntSK为企业数字化转型提供了一个可落地、可扩展的智能化解决方案。它让企业既能享受AI的智能,又能完全掌控自己的数据,在安全和效率之间找到了平衡点。
对于.NET开发者而言,AntSK不仅是一个可以直接使用的AI平台,更是学习现代AI应用架构设计的绝佳案例。通过研究它的源码,我们可以学到:
-
如何设计一个可扩展的AI应用架构
-
如何集成和管理多种AI模型
-
如何实现高性能的向量检索
-
如何构建企业级的安全和监控体系
在AI技术日新月异的今天,AntSK以其开源、开放的姿态,为.NET社区在AI领域的探索提供了一个重要的里程碑。相信随着项目的持续发展和社区的积极参与,AntSK将成为企业级AI应用开发的重要基础设施,推动更多创新应用的诞生。
让我们一起见证.NET在AI时代的华丽蜕变,共同构建更智能的未来! 🚀✨
附录:快速开始指南
A.1 5分钟快速体验
# 1. 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/AIDotNet/antsk:latest# 2. 创建配置文件
cat > appsettings.json << EOF
{"DBConnection": {"DbType": "Sqlite","ConnectionStrings": "Data Source=AntSK.db;"},"KernelMemory": {"VectorDb": "Disk"},"Login": {"User": "admin","Password": "admin"}
}
EOF# 3. 启动容器
docker run -d \--name antsk \-p 5000:5000 \-v $(pwd)/appsettings.json:/app/appsettings.json \-v $(pwd)/data:/app/data \registry.cn-hangzhou.aliyuncs.com/AIDotNet/antsk:latest# 4. 访问系统
# 打开浏览器访问 http://localhost:5000
# 账号:admin 密码:admin
A.2 常见问题
Q: 如何配置OpenAI API?
A: 在系统设置 → AI模型管理中添加:
-
模型类型:OpenAI
-
模型名称:gpt-4
-
API Key:你的OpenAI API Key
-
端点:https://api.openai.com/v1
Q: 如何使用本地模型?
A:
-
安装Ollama:
curl -fsSL https://ollama.com/install.sh | sh -
下载模型:
ollama pull qwen2:7b - 在AntSK中配置:
-
模型类型:Ollama
-
模型名称:qwen2:7b
-
端点:http://localhost:11434/v1
-
Q: 如何导入文档?
A:
-
创建知识库
-
点击"导入文档"
-
选择文件(支持PDF、Word、Excel等)
-
等待处理完成
-
开始问答
Q: 性能不够怎么办?
A:
-
使用PostgreSQL替代SQLite
-
启用Redis缓存
-
调整工作线程数
-
使用更小的向量模型
-
考虑使用GPU加速
更多AIGC文章
https://blog.csdn.net/u012094427/category_12764998.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=12764998&sharerefer=PC&sharesource=u012094427&sharefrom=from_link
RAG技术全解:从原理到实战的简明指南
https://blog.csdn.net/u012094427/category_12996154.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=12996154&sharerefer=PC&sharesource=u012094427&sharefrom=from_link
更多VibeCoding文章
https://blog.csdn.net/u012094427/category_13009222.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=13009222&sharerefer=PC&sharesource=u012094427&sharefrom=from_link
项目地址:https://github.com/AIDotNet/AntSK
https://github.com/AIDotNet/AntSK
在线体验:https://demo.antsk.cn/
https://demo.antsk.cn/技术文档:http://antsk.cn
http://antsk.cn

