1. 从零构建AI Agent:基于C#与LangChain的实战指南
最近在开发一个能处理复杂业务场景的智能助手时,我深刻体会到传统聊天机器人(如基础版ChatGPT)的局限性。当我们需要AI系统能够自主调用工具、处理多轮对话并完成特定任务时,就需要引入AI Agent的概念。本文将分享如何用C#构建一个具备规划、记忆和工具调用能力的AI Agent系统。
提示:本文假设读者已具备C#基础开发经验,并了解基本的AI概念。所有代码示例均基于.NET 6+环境。
1.1 AI Agent的核心架构
一个完整的AI Agent系统由四大核心组件构成:
- LLM(大语言模型):负责自然语言理解和生成,是整个系统的大脑。我们选用通义千问作为示例模型
- 记忆系统:包括短期记忆(对话历史)和长期记忆(RAG知识库)
- 规划模块:负责拆解复杂任务并制定执行流程
- 工具集:Agent可调用的外部函数,如计算器、数据库查询等
这种架构的优势在于:
- 突破了单一对话的限制,能处理需要多步骤协作的复杂任务
- 通过工具调用扩展了AI的能力边界
- 记忆系统使Agent能保持上下文一致性
2. 开发环境准备与基础配置
2.1 必要的开发工具
在开始编码前,需要准备以下环境:
- Visual Studio 2022(社区版即可)
- .NET 6+ SDK
- Python 3.8+(用于运行LangChain服务)
- 通义千问API密钥(可在阿里云控制台申请)
2.2 项目初始化与依赖安装
创建新的C#控制台项目后,通过NuGet安装以下关键包:
bash复制dotnet add package Microsoft.SemanticKernel
dotnet add package LangChain.NET
dotnet add package Newtonsoft.Json
对于Python端的LangChain服务,需要安装:
bash复制pip install langchain-community dashscope faiss-cpu
3. 核心功能实现详解
3.1 工具定义与注册
工具是Agent能力的延伸,我们首先定义两个基础工具:
csharp复制// 数学计算工具
public class CalculatorTool
{
[SKFunction, Description("计算数学表达式")]
public string Calculate(
[Description("数学表达式,如 '2+2' 或 '500*0.8'")] string expression)
{
try {
var dataTable = new DataTable();
var result = dataTable.Compute(expression, null);
return result.ToString();
}
catch (Exception e) {
return $"计算错误: {e.Message}";
}
}
}
// RAG知识库工具
public class RagSearchTool
{
private readonly IVectorStore _vectorStore;
public RagSearchTool()
{
// 初始化向量数据库
var embeddings = new DashScopeEmbeddings();
_vectorStore = new FAISS(embeddings);
}
[SKFunction, Description("从知识库搜索信息")]
public string Search(
[Description("搜索查询内容")] string query)
{
// 实际项目中这里应连接真实数据库
var mockData = @"【公司内部信息】
项目名称:深蓝计划
预算:50万元
截止日期:2026-12-31";
return mockData;
}
}
安全提示:计算工具避免使用eval等危险方法,推荐使用DataTable.Compute或自定义解析器
3.2 Agent核心逻辑实现
Agent的核心在于多轮对话调度,以下是C#实现的关键代码:
csharp复制public class AiAgent
{
private readonly ISKFunction _chatFunction;
private readonly List<SKContext> _memory = new();
public AiAgent()
{
var kernel = Kernel.CreateBuilder()
.AddChatCompletionService<QwenChatCompletion>()
.Build();
// 注册工具
kernel.ImportFunctions(new CalculatorTool(), "math");
kernel.ImportFunctions(new RagSearchTool(), "search");
_chatFunction = kernel.CreateFunctionFromPrompt(
"你是一个专业助理,请根据工具和上下文回答问题");
}
public async Task<string> RunAsync(string query)
{
var context = new SKContext {
["input"] = query,
["history"] = string.Join("\n", _memory.Select(m => m.Result))
};
for (int i = 0; i < 5; i++) // 防止无限循环
{
var result = await _chatFunction.InvokeAsync(context);
if (!result.FunctionCall.Any())
return result.Result;
foreach (var call in result.FunctionCall)
{
var toolResult = await kernel.RunAsync(
call.Function, call.Parameters);
context.Variables.Update(toolResult.Result);
}
_memory.Add(context);
}
return "超过最大重试次数";
}
}
3.3 记忆系统实现
有效的记忆系统是Agent持续学习的关键:
csharp复制public class MemorySystem
{
private readonly IVectorStore _longTermMemory;
private readonly List<string> _shortTermMemory = new();
public MemorySystem()
{
// 长期记忆使用FAISS向量数据库
_longTermMemory = new FAISS(new DashScopeEmbeddings());
}
public void AddToShortTerm(string message)
{
if (_shortTermMemory.Count > 10) // 限制对话历史长度
_shortTermMemory.RemoveAt(0);
_shortTermMemory.Add(message);
}
public async Task<string> SearchLongTerm(string query)
{
// 实际项目应连接真实数据库
return await _longTermMemory.SearchAsync(query, k: 3);
}
}
4. 实战案例与问题排查
4.1 典型使用场景示例
csharp复制// 初始化Agent
var agent = new AiAgent();
// 场景1:查询公司信息
var response1 = await agent.RunAsync("我们公司当前有哪些项目?");
Console.WriteLine(response1);
// 场景2:数学计算
var response2 = await agent.RunAsync("计算50万增加46%后的金额");
Console.WriteLine(response2);
预期输出:
code复制【公司内部信息】
项目名称:深蓝计划
预算:50万元
截止日期:2026-12-31
730000
4.2 常见问题与解决方案
问题1:工具调用失败
- 现象:Agent无法正确识别应调用的工具
- 排查:
- 检查工具函数是否正确定义了Description属性
- 验证工具注册时使用的名称是否唯一
- 确保prompt中包含工具使用说明
问题2:无限循环
- 现象:Agent不断要求调用工具
- 解决方案:
- 设置最大循环次数(如示例中的5次)
- 在prompt中明确限制工具调用次数
问题3:响应速度慢
- 优化方案:
- 对LLM响应添加超时控制
- 对工具调用实现缓存机制
- 考虑使用流式响应改善用户体验
5. 安全增强与性能优化
5.1 安全防护措施
针对AI系统的特殊安全需求,我们实施了以下防护:
- 输入过滤:
csharp复制public static string SanitizeInput(string input)
{
// 移除可能的安全风险字符
var regex = new Regex(@"[\;\-\+]");
return regex.Replace(input, "");
}
- 权限控制:
csharp复制[AttributeUsage(AttributeTargets.Method)]
public class ToolAccessAttribute : Attribute
{
public AccessLevel RequiredLevel { get; }
public ToolAccessAttribute(AccessLevel level)
{
RequiredLevel = level;
}
}
public enum AccessLevel
{
Guest,
User,
Admin
}
5.2 性能优化技巧
通过以下方法显著提升Agent响应速度:
- 异步工具调用:
csharp复制var tasks = result.FunctionCall.Select(call =>
kernel.RunAsync(call.Function, call.Parameters));
var toolResults = await Task.WhenAll(tasks);
- 记忆压缩:
csharp复制public string CompressMemory(List<string> history)
{
// 使用LLM总结对话历史
var prompt = $"总结以下对话:\n{string.Join("\n", history)}";
return _chatFunction.InvokeAsync(prompt);
}
- 缓存策略:
csharp复制MemoryCacheEntryOptions cacheOptions = new()
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
};
_cache.Set(cacheKey, result, cacheOptions);
6. 扩展思路与应用场景
6.1 进阶功能扩展
- 多Agent协作:
csharp复制public class AgentCoordinator
{
private readonly List<AiAgent> _agents;
public async Task<string> CoordinateAsync(string query)
{
// 根据query路由到不同Agent
var specialist = _agents.OrderBy(a =>
a.GetRelevanceScore(query)).First();
return await specialist.RunAsync(query);
}
}
- 动态工具加载:
csharp复制public void LoadToolAssembly(string path)
{
var assembly = Assembly.LoadFrom(path);
var toolTypes = assembly.GetTypes()
.Where(t => t.GetCustomAttribute<ToolAttribute>() != null);
foreach (var type in toolTypes)
_kernel.ImportFunctions(type);
}
6.2 典型应用场景
-
企业知识助手:
- 集成内部文档系统
- 自动生成周报
- 项目进度查询
-
数据分析助手:
- 自然语言查询数据库
- 自动生成可视化图表
- 异常数据预警
-
开发辅助工具:
- 代码生成与解释
- API文档查询
- 错误诊断
在实际开发中,我发现Agent系统最关键的不仅是技术实现,更是对业务场景的深入理解。比如在财务系统中,需要特别注意数字的精确性和审计日志的完整性;而在客服场景中,则更关注对话的流畅性和情感识别能力。