.NET原生AI Agent框架:用C#构建可扩展工具调用智能体
在Python生态中,LangChain几乎成了AI Agent的代名词。而在.NET阵营,很多开发者还停留在"直接调SDK发HTTP请求"的原始阶段,手动解析function_call、拼接对话历史、处理多轮工具调用,代码冗余且容易出错。
事实上,微软官方早已为.NET生态打造了完整的智能体开发框架——Semantic Kernel。它不是简单的SDK封装,而是一套完整的内核架构,原生支持插件化工具调用、多步推理编排、记忆检索与计划生成,与C#强类型体系深度融合,编译期就能发现大量问题。
本文从工程落地视角出发,拆解工具调用智能体的核心架构,给出可直接复用的插件化实现方案,并覆盖权限控制、异常容错、动态扩展等工业级特性。
一、核心架构:智能体的三层能力模型
一个完整的工具调用智能体,本质上由三层能力构成:模型推理层负责理解与决策,工具执行层负责对接外部系统,编排内核层负责调度两者的多轮交互。Semantic Kernel正是中间那层编排内核。
Kernel是整个框架的核心容器,它聚合了三类资源:
- AI服务:统一抽象聊天补全、文本嵌入等模型能力,支持OpenAI、Azure OpenAI、国产大模型等多种后端
- 插件集:以.NET类形式封装的工具函数,通过特性标注元数据,模型可自主发现并调用
- 记忆与规划器:提供向量检索、任务拆解等高级能力,支撑复杂Agent场景
工具调用的完整执行链路是:Kernel将所有插件函数序列化为JSON Schema → 随对话历史一同发送给LLM → LLM决策是否调用工具 → Kernel解析参数并执行本地函数 → 将执行结果回填上下文 → 再次送入LLM生成最终回答。整个多轮往返过程对上层透明,开发者只需专注写业务函数。
二、前期准备
创建控制台或Web项目,安装核心NuGet包:
Install-Package Microsoft.SemanticKernel
Install-Package Microsoft.SemanticKernel.ChatCompletion
如果使用OpenAI服务,还需安装对应提供商包:
Install-Package Microsoft.SemanticKernel.Connectors.OpenAI
建议使用.NET 8及以上版本,SK 1.1x系列API已趋于稳定,适合生产环境使用。
三、基础实现:三步搭建可调用工具的智能体
3.1 第一步:构建Kernel内核
Kernel采用建造者模式配置,支持依赖注入集成。在ASP.NET Core中可以直接注册为服务,控制台程序则手动构建。
using Microsoft.SemanticKernel;
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(
modelId: "gpt-4o-mini",
apiKey: "your-api-key");
// 注册插件(后面定义)
builder.Plugins.AddFromType<SystemToolPlugin>();
builder.Plugins.AddFromType<WeatherToolPlugin>();
Kernel kernel = builder.Build();
Kernel对象本身是轻量级的,但内部持有的模型连接、插件实例建议复用。Web场景下按作用域创建,单例场景注意线程安全。
3.2 第二步:定义工具插件
插件就是普通C#类,通过KernelFunction和Description特性标注元数据。特性描述越精准,模型调用的准确率越高。
public class SystemToolPlugin
{
[KernelFunction]
[Description("获取当前系统时间,用于回答与日期时间相关的问题")]
public string GetCurrentTime()
{
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
[KernelFunction]
[Description("执行数学计算,支持加减乘除和基本表达式")]
public double Calculate([Description("数学表达式字符串")] string expression)
{
try
{
var table = new System.Data.DataTable();
var result = table.Compute(expression, null);
return Convert.ToDouble(result);
}
catch
{
throw new ArgumentException("表达式格式无效");
}
}
}
插件方法支持同步、异步、泛型返回值等多种签名。参数支持基本类型、数组、自定义类,SK会自动完成JSON反序列化。
3.3 第三步:启用自动工具调用
配置FunctionChoiceBehavior为Auto模式,Kernel会自动处理工具调用的完整多轮循环,开发者只需拿到最终结果。
var settings = new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
var result = await kernel.InvokePromptAsync(
"现在北京时间几点?帮我算一下256乘以1024等于多少",
new(settings));
Console.WriteLine(result);
这一行调用背后,Kernel自动完成了:识别需要调用工具 → 选择合适的函数 → 解析参数 → 执行GetCurrentTime和Calculate → 将结果返回模型 → 生成自然语言回答。整个过程无需手动干预。
四、进阶控制:手动编排工具调用
生产环境往往不能完全放权给AI自动执行,需要人工介入审批、审计日志、权限校验。这时可以切换为手动调用模式,精确控制每一步。
var settings = new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(autoInvoke: false)
};
ChatHistory history = [];
history.AddUserMessage("查询北京明天的天气,并给出穿衣建议");
while (true)
{
var response = await kernel.GetRequiredService<IChatCompletionService>()
.GetChatMessageContentAsync(history, settings, kernel);
// 模型返回文本回答,结束循环
if (response.Content is not null)
{
Console.WriteLine(response.Content);
break;
}
// 模型请求调用工具,人工审批后执行
foreach (var functionCall in response.Items.OfType<FunctionCallContent>())
{
// 权限校验、审计日志、人工审批都可以加在这里
Console.WriteLine($"AI请求调用: {functionCall.PluginName}.{functionCall.FunctionName}");
var functionResult = await functionCall.InvokeAsync(kernel);
history.Add(response);
history.Add(functionResult.ToChatMessageContent());
}
}
手动模式的价值在于:高危工具调用前增加人工确认、调用前后插入审计日志、对参数做安全校验、限制单轮最大调用次数防止死循环。
五、可扩展架构设计
真正的工业级Agent不会把所有工具写死在代码里,需要支持动态加载、热插拔、沙箱隔离。
5.1 插件动态发现与加载
基于反射扫描程序集,自动发现标注了KernelFunction的类,无需逐个注册:
public static void AddAllPlugins(this IKernelBuilder builder, Assembly assembly)
{
var pluginTypes = assembly.GetTypes()
.Where(t => t.GetMethods()
.Any(m => m.GetCustomAttribute<KernelFunctionAttribute>() != null));
foreach (var type in pluginTypes)
{
builder.Plugins.AddFromType(type);
}
}
配合.NET的AssemblyLoadContext,可以实现插件热加载,不重启主程序就能新增工具能力。
5.2 工具分级与权限控制
不同用户、不同场景下可用的工具集不同。通过特性标记工具安全等级,调用时动态过滤:
[AttributeUsage(AttributeTargets.Method)]
public class ToolSecurityLevelAttribute : Attribute
{
public SecurityLevel Level { get; }
public ToolSecurityLevelAttribute(SecurityLevel level) => Level = level;
}
// 使用时按当前用户权限过滤插件
var availableFunctions = kernel.Plugins.GetFunctionsMetadata()
.Where(f => GetSecurityLevel(f) <= userPermissionLevel);
5.3 调用过滤器与审计
SK支持函数调用过滤器,类似ASP.NET的中间件,可以在工具执行前后插入统一逻辑:
public class AuditFilter : IFunctionInvocationFilter
{
public async Task OnFunctionInvocationAsync(
FunctionInvocationContext context, Func<Task> next)
{
var log = new
{
context.Function.PluginName,
context.Function.Name,
context.Arguments,
Timestamp = DateTime.Now
};
// 记录调用前审计
await WriteAuditLogAsync(log);
try
{
await next();
// 记录成功结果
}
catch (Exception ex)
{
// 记录失败异常
throw;
}
}
}
// 注册过滤器
builder.Services.AddSingleton<IFunctionInvocationFilter, AuditFilter>();
六、工业级容错与稳定性保障
工具调用涉及外部系统,网络超时、接口异常、参数错误都可能发生。健壮的Agent必须有完善的容错机制。
6.1 自动重试与熔断
结合Polly策略,为插件方法增加重试、熔断、超时保护:
private static readonly AsyncRetryPolicy RetryPolicy = Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(attempt));
[KernelFunction]
public async Task<string> QueryDatabaseAsync(string sql)
{
return await RetryPolicy.ExecuteAsync(async () =>
{
// 实际数据库查询逻辑
return await ExecuteQueryInternalAsync(sql);
});
}
6.2 调用深度限制
防止模型陷入工具调用死循环,设置最大调用轮次上限,超过则强制终止并返回结果:
int maxIterations = 10;
for (int i = 0; i < maxIterations; i++)
{
// 执行一轮推理
var response = await GetModelResponseAsync(history);
if (!response.HasFunctionCalls) break;
// 执行工具调用
await ExecuteToolCallsAsync(response, history);
}
6.3 参数校验
不要完全信任模型生成的参数。工具方法入口处必须做合法性校验,防止SQL注入、路径穿越、越权访问等安全风险。
七、常见踩坑与最佳实践
坑一:插件描述写得太简略。Description是模型理解工具用途的唯一依据,写得越模糊,调用准确率越低。建议包含:功能说明、适用场景、参数含义、返回值格式。
坑二:返回纯文本而非结构化数据。工具返回的结果会被送回模型继续推理。纯自然语言返回会消耗大量token且容易产生歧义,优先返回JSON格式的结构化数据。
坑三:插件粒度过大。一个函数做太多事,模型难以决策何时调用。建议遵循单一职责,每个工具只做一件事,由模型负责组合编排。
坑四:忽略异常信息的反馈。工具执行失败时,不要吞掉异常直接返回空。将错误信息如实返回给模型,它通常能根据错误调整参数或换用其他工具。
最佳实践清单:
- 工具方法保持纯函数特性,减少外部状态依赖
- 输入输出使用基本类型,避免复杂类导致序列化问题
- 长耗时工具设置超时,避免阻塞整个推理链路
- 敏感操作增加二次确认,不要让AI直接执行高危动作
- 保留完整的调用链路日志,便于排查问题
八、总结与选型建议
Semantic Kernel作为.NET原生的AI Agent框架,最大的优势在于与.NET生态的深度融合。强类型插件、依赖注入、过滤器管道、异步编程模型,都是C#开发者熟悉的范式,学习成本远低于移植Python方案。
对于简单场景,几行代码开启Auto模式就能获得完整的工具调用能力;对于复杂企业应用,其插件化架构、过滤器机制、手动编排能力足以支撑生产级需求。配合记忆、规划器等模块,还可以进一步升级为具备规划、记忆、行动能力的完整智能体。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)