构建智能 Agent 系统:从对话到工作流的完整实践
引言
当你的 AI 应用需要:
- 💬 维护长期对话记忆
- 🧠 具备固定的身份和专业知识
- 🔧 自动调用工具完成任务
- 🔄 协调多个 Agent 完成复杂流程
那么,你需要的不再是简单的 IChatClient,而是一个完整的 Agent 系统。
本文将基于 Microsoft Agent Framework (MAF) 和 MEAI,带你从零构建生产级的智能 Agent 系统,并进阶到多 Agent 工作流编排。
Agent vs ChatClient:为什么需要 Agent?
核心区别
graph TB
subgraph "IChatClient - 无状态"
C1[调用 1] --> C2[返回]
C3[调用 2] --> C4[返回]
C5[调用 3] --> C6[返回]
style C1 fill:#FF9800
style C3 fill:#FF9800
style C5 fill:#FF9800
end
subgraph "AIAgent - 有状态"
A1[调用 1] --> A2[对话线程]
A2 --> A3[调用 2]
A3 --> A2
A2 --> A5[调用 3]
A5 --> A2
style A2 fill:#4CAF50,stroke:#2E7D32,stroke-width:3px
end
特性
IChatClient
AIAgent
状态管理
❌ 无状态,每次独立
✅ 内置对话线程(AgentThread)
身份定义
❌ 需手动传入 System Message
✅ 固定的 Instructions 和 Name
工具管理
⚠️ 需在每次调用时配置
✅ Agent 级别统一管理
对话持久化
❌ 需手动实现
✅ 内置 IMessageStore 支持
多轮交互
❌ 需手动维护消息历史
✅ 自动管理上下文
使用场景
单次查询、无状态任务
多轮对话、企业级应用
MAF Agent 快速入门
架构概览
graph TB
subgraph "应用层"
App[你的应用]
end
subgraph "Agent 层"
Agent[AIAgent]
Thread[AgentThread<br/>对话线程]
Store[IMessageStore<br/>消息存储]
end
subgraph "AI 抽象层"
Client[IChatClient<br/>MEAI]
end
subgraph "AI 服务层"
Service[OpenAI / Azure OpenAI / etc.]
end
App --> Agent
Agent --> Thread
Thread --> Store
Agent --> Client
Client --> Service
style Agent fill:#4CAF50,stroke:#2E7D32,stroke-width:3px
style Thread fill:#2196F3
核心概念
1. AIAgent - 智能体
定义:具有固定身份、专业知识和工具能力的 AI 实体。
using Microsoft.Extensions.AI.Agents;
// 创建 Agent
var agent = new AIAgent(chatClient, new AIAgentOptions
{
Name = "CodeReviewBot",
Instructions = @"你是一个专业的代码审查专家。
你的职责:
1. 检查代码质量和最佳实践
2. 识别潜在的 bug 和性能问题
3. 提供改进建议
审查标准:
- 遵循 SOLID 原则
- 注重可读性和可维护性
- 关注安全性和性能",
// Agent 级别的工具
Tools = new ToolCollection
{
AIFunctionFactory.Create(GetCodeMetrics),
AIFunctionFactory.Create(CheckSecurityVulnerabilities)
}
});
2. AgentThread - 对话线程
定义:一段独立的对话上下文,自动管理消息历史。
// 创建新的对话线程
var thread = await agent.CreateThreadAsync();
// 添加消息
await thread.AddMessageAsync("请审查这段代码:public class User { ... }");
// 获取响应(自动维护上下文)
var response = await thread.InvokeAsync();
Console.WriteLine(response.Text);
// 继续对话(Agent 能记住之前的内容)
await thread.AddMessageAsync("这个类还需要实现什么接口?");
var response2 = await thread.InvokeAsync();
3. IMessageStore - 消息持久化
定义:存储对话历史,支持恢复和审计。
// 使用内存存储(开发环境)
var memoryStore = new InMemoryMessageStore();
// 使用持久化存储(生产环境)
var dbStore = new SqlMessageStore(connectionString);
var agent = new AIAgent(chatClient, new AIAgentOptions
{
Name = "Assistant",
MessageStore = dbStore // 自动持久化所有消息
});
实战 1:构建智能客服 Agent
需求分析
构建一个能够:
- 查询订单状态
- 处理退款申请
- 提供产品咨询
- 记住用户的对话历史
完整实现
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.DependencyInjection;
using System.ComponentModel;
// 1. 定义业务工具
public class CustomerServiceTools
{
private readonly IOrderRepository _orders;
private readonly IProductCatalog _catalog;
private readonly ILogger<CustomerServiceTools> _logger;
public CustomerServiceTools(
IOrderRepository orders,
IProductCatalog catalog,
ILogger<CustomerServiceTools> logger)
{
_orders = orders;
_catalog = catalog;
_logger = logger;
}
[Description("查询订单状态和详情")]
public async Task<OrderInfo> GetOrderStatus(
[Description("订单号")] string orderId)
{
_logger.LogInformation("查询订单: {OrderId}", orderId);
var order = await _orders.GetByIdAsync(orderId);
if (order == null)
{
return new OrderInfo
{
Found = false,
Message = "未找到该订单,请检查订单号是否正确"
};
}
return new OrderInfo
{
Found = true,
OrderId = order.Id,
ProductName = order.ProductName,
Status = order.Status.ToString(),
Amount = order.Amount,
OrderDate = order.CreatedAt,
EstimatedDelivery = order.EstimatedDeliveryDate
};
}
[Description("申请退款")]
public async Task<RefundResult> RequestRefund(
[Description("订单号")] string orderId,
[Description("退款原因")] string reason)
{
_logger.LogInformation("退款申请: {OrderId}, 原因: {Reason}", orderId, reason);
var order = await _orders.GetByIdAsync(orderId);
if (order == null)
{
return new RefundResult
{
Success = false,
Message = "订单不存在"
};
}
// 验证退款条件
if (order.Status == OrderStatus.Delivered)
{
var daysSinceDelivery = (DateTime.Now - order.DeliveredAt!.Value).Days;
if (daysSinceDelivery > 7)
{
return new RefundResult
{
Success = false,
Message = "已超过7天无理由退货期限"
};
}
}
// 创建退款申请
var refundId = await _orders.CreateRefundAsync(orderId, reason);
return new RefundResult
{
Success = true,
RefundId = refundId,
Message = "退款申请已提交,预计3-5个工作日处理完成"
};
}
[Description("搜索产品信息")]
public async Task<List<ProductInfo>> SearchProducts(
[Description("搜索关键词")] string query,
[Description("返回数量,默认5")] int limit = 5)
{
_logger.LogInformation("搜索产品: {Query}", query);
var products = await _catalog.SearchAsync(query, limit);
return products.Select(p => new ProductInfo
{
Id = p.Id,
Name = p.Name,
Price = p.Price,
Description = p.Description,
InStock = p.Stock > 0
}).ToList();
}
}
// 2. 定义数据模型
public record OrderInfo
{
public bool Found { get; set; }
public string? OrderId { get; set; }
public string? ProductName { get; set; }
public string? Status { get; set; }
public decimal Amount { get; set; }
public DateTime OrderDate { get; set; }
public DateTime? EstimatedDelivery { get; set; }
public string? Message { get; set; }
}
public record RefundResult
{
public bool Success { get; set; }
public string? RefundId { get; set; }
public string Message { get; set; } = "";
}
public record ProductInfo
{
public string Id { get; set; } = "";
public string Name { get; set; } = "";
public decimal Price { get; set; }
public string Description { get; set; } = "";
public bool InStock { get; set; }
}
// 3. 配置依赖注入
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
// 注册业务服务
services.AddSingleton<IOrderRepository, OrderRepository>();
services.AddSingleton<IProductCatalog, ProductCatalog>();
services.AddSingleton<CustomerServiceTools>();
// 注册消息存储
services.AddSingleton<IMessageStore>(sp =>
new SqlMessageStore(connectionString)
);
// 注册 IChatClient
services.AddSingleton<IChatClient>(sp =>
{
var logger = sp.GetRequiredService<ILoggerFactory>();
return new OpenAIClient(new ApiKeyCredential("your-key"))
.AsChatClient("gpt-4o")
.UseLogging(logger)
.UseFunctionInvocation(); // 自动执行工具调用
});
// 注册 Agent
services.AddSingleton<AIAgent>(sp =>
{
var chatClient = sp.GetRequiredService<IChatClient>();
var tools = sp.GetRequiredService<CustomerServiceTools>();
var messageStore = sp.GetRequiredService<IMessageStore>();
// 注册工具
var toolCollection = AIFunctionFactory.Create(
typeof(CustomerServiceTools),
tools
);
return new AIAgent(chatClient, new AIAgentOptions
{
Name = "CustomerServiceBot",
Instructions = @"你是一个专业、友好的客服助手。
职责:
1. 查询订单状态并解答物流问题
2. 处理退款申请(需验证退货政策)
3. 提供产品咨询和推荐
4. 解答常见问题
沟通风格:
- 友好、耐心、专业
- 使用简洁明了的语言
- 主动询问必要信息
- 对无法处理的问题,引导用户联系人工客服
业务规则:
- 退货政策:商品签收后7天内可申请无理由退货
- 订单修改:仅限未发货状态
- 产品推荐:根据用户需求和预算推荐合适的产品",
Tools = toolCollection,
MessageStore = messageStore,
// 配置 ChatOptions
ChatOptions = new ChatOptions
{
Temperature = 0.3f, // 客服需要准确性
MaxOutputTokens = 1000
}
});
});
var provider = services.BuildServiceProvider();
// 4. 使用 Agent
var agent = provider.GetRequiredService<AIAgent>();
// 模拟用户对话
async Task HandleUserQuery(string userId, string userMessage)
{
Console.WriteLine($"\n用户 ({userId}): {userMessage}");
// 为每个用户维护独立的对话线程
var threadId = $"user-{userId}";
// 尝试恢复现有对话
AgentThread thread;
try
{
thread = await agent.GetThreadAsync(threadId);
Console.WriteLine(" [恢复现有对话]");
}
catch
{
// 创建新对话
thread = await agent.CreateThreadAsync(threadId);
Console.WriteLine(" [创建新对话]");
}
// 添加用户消息
await thread.AddMessageAsync(userMessage, role: ChatRole.User);
// 获取 Agent 响应
var response = await thread.InvokeAsync();
Console.WriteLine($"客服: {response.Text}");
}
// 模拟多轮对话
await HandleUserQuery("user123", "我想查询订单 ORDER-12345 的状态");
await HandleUserQuery("user123", "我对这个产品不满意,能退货吗?");
await HandleUserQuery("user123", "好的,我要申请退款,原因是尺码不合适");
// 输出示例:
// 用户 (user123): 我想查询订单 ORDER-12345 的状态
// [创建新对话]
// [AI 调用 GetOrderStatus("ORDER-12345")]
// 客服: 您的订单已发货,预计明天送达。商品是 iPhone 15,金额 5999元。
// 用户 (user123): 我对这个产品不满意,能退货吗?
// [恢复现有对话]
// 客服: 您的订单是昨天发货的,在签收后7天内可以申请无理由退货。请问您想现在申请退款吗?
// 用户 (user123): 好的,我要申请退款,原因是尺码不合适
// [恢复现有对话]
// [AI 调用 RequestRefund("ORDER-12345", "尺码不合适")]
// 客服: 退款申请已提交成功,申请号为 REFUND-789。预计3-5个工作日处理完成...
实战 2:多 Agent 协作系统
场景:代码生成与审查流程
需要三个专业 Agent 协作:
- 需求分析 Agent:理解用户需求,生成技术规格
- 代码生成 Agent:根据规格编写代码
- 代码审查 Agent:审查代码质量并提出改进
实现方式 1:手动编排
// 1. 定义各个 Agent
var requirementAgent = new AIAgent(chatClient, new AIAgentOptions
{
Name = "RequirementAnalyst",
Instructions = @"你是一个需求分析专家。
分析用户需求,生成详细的技术规格,包括:
- 功能描述
- 输入输出定义
- 边界条件
- 性能要求",
ChatOptions = new ChatOptions { Temperature = 0.5f }
});
var codeGenAgent = new AIAgent(chatClient, new AIAgentOptions
{
Name = "CodeGenerator",
Instructions = @"你是一个资深的 C# 开发者。
根据技术规格编写高质量的代码:
- 遵循 SOLID 原则
- 添加必要的注释
- 包含错误处理
- 使用现代 C# 特性",
ChatOptions = new ChatOptions { Temperature = 0.3f }
});
var reviewAgent = new AIAgent(chatClient, new AIAgentOptions
{
Name = "CodeReviewer",
Instructions = @"你是一个代码审查专家。
审查代码并提供反馈:
1. 代码质量(可读性、可维护性)
2. 潜在的 bug
3. 性能优化建议
4. 最佳实践
如果发现严重问题,标记为 'REJECT',否则 'APPROVE'",
ChatOptions = new ChatOptions { Temperature = 0.2f }
});
// 2. 编排工作流
public async Task<CodeGenerationResult> GenerateCodeAsync(string userRequirement)
{
// 步骤 1: 需求分析
Console.WriteLine("=== 步骤 1: 需求分析 ===");
var reqThread = await requirementAgent.CreateThreadAsync();
await reqThread.AddMessageAsync($"用户需求:{userRequirement}");
var specResponse = await reqThread.InvokeAsync();
var specification = specResponse.Text;
Console.WriteLine($"技术规格:\n{specification}\n");
// 步骤 2: 代码生成
Console.WriteLine("=== 步骤 2: 代码生成 ===");
var codeThread = await codeGenAgent.CreateThreadAsync();
await codeThread.AddMessageAsync($"技术规格:\n{specification}\n\n请生成代码");
var codeResponse = await codeThread.InvokeAsync();
var generatedCode = codeResponse.Text;
Console.WriteLine($"生成的代码:\n{generatedCode}\n");
// 步骤 3: 代码审查(循环直到通过)
var reviewCount = 0;
var maxRetries = 3;
string reviewResult;
while (reviewCount < maxRetries)
{
Console.WriteLine($"=== 步骤 3: 代码审查 (第 {reviewCount + 1} 次) ===");
var reviewThread = await reviewAgent.CreateThreadAsync();
await reviewThread.AddMessageAsync($@"请审查以下代码:
规格:
{specification}
代码:
{generatedCode}
请给出审查意见,并在最后一行明确标注 APPROVE 或 REJECT");
var reviewResponse = await reviewThread.InvokeAsync();
reviewResult = reviewResponse.Text;
Console.WriteLine($"审查结果:\n{reviewResult}\n");
if (reviewResult.Contains("APPROVE"))
{
Console.WriteLine("✅ 代码审查通过");
return new CodeGenerationResult
{
Success = true,
Specification = specification,
Code = generatedCode,
ReviewComments = reviewResult
};
}
// 如果审查不通过,让代码生成 Agent 根据反馈改进
Console.WriteLine("❌ 代码需要改进");
await codeThread.AddMessageAsync($@"代码审查反馈:
{reviewResult}
请根据反馈改进代码");
var improvedResponse = await codeThread.InvokeAsync();
generatedCode = improvedResponse.Text;
Console.WriteLine($"改进后的代码:\n{generatedCode}\n");
reviewCount++;
}
return new CodeGenerationResult
{
Success = false,
Message = "代码审查未通过,已达最大重试次数"
};
}
public record CodeGenerationResult
{
public bool Success { get; set; }
public string? Specification { get; set; }
public string? Code { get; set; }
public string? ReviewComments { get; set; }
public string? Message { get; set; }
}
// 使用
var result = await GenerateCodeAsync(
"创建一个用户注册功能,包括邮箱验证和密码强度检查"
);
if (result.Success)
{
Console.WriteLine("🎉 代码生成成功!");
Console.WriteLine($"\n最终代码:\n{result.Code}");
}
实战 3:Workflow 编排(高级)
使用 MAF Workflow 声明式编排
using Microsoft.Extensions.AI.Agents.Workflows;
// 1. 定义 Workflow
var workflow = new WorkflowBuilder()
.AddStep("analyze_requirement", async (context) =>
{
// 步骤 1: 需求分析
var userRequirement = context.Input["requirement"].ToString();
var thread = await requirementAgent.CreateThreadAsync();
await thread.AddMessageAsync(userRequirement);
var response = await thread.InvokeAsync();
context.State["specification"] = response.Text;
return WorkflowStepResult.Success();
})
.AddStep("generate_code", async (context) =>
{
// 步骤 2: 生成代码
var specification = context.State["specification"].ToString();
var thread = await codeGenAgent.CreateThreadAsync();
await thread.AddMessageAsync($"根据以下规格生成代码:\n{specification}");
var response = await thread.InvokeAsync();
context.State["code"] = response.Text;
context.State["review_count"] = 0;
return WorkflowStepResult.Success();
})
.AddStep("review_code", async (context) =>
{
// 步骤 3: 审查代码
var specification = context.State["specification"].ToString();
var code = context.State["code"].ToString();
var thread = await reviewAgent.CreateThreadAsync();
await thread.AddMessageAsync($@"
规格:{specification}
代码:{code}
请审查并标注 APPROVE 或 REJECT");
var response = await thread.InvokeAsync();
context.State["review_result"] = response.Text;
if (response.Text.Contains("APPROVE"))
{
return WorkflowStepResult.Success();
}
else
{
var reviewCount = (int)context.State["review_count"];
if (reviewCount >= 3)
{
return WorkflowStepResult.Failure("审查失败次数过多");
}
context.State["review_count"] = reviewCount + 1;
return WorkflowStepResult.Retry("need_improvement");
}
})
.AddStep("improve_code", async (context) =>
{
// 步骤 4: 根据反馈改进
var reviewResult = context.State["review_result"].ToString();
var thread = await codeGenAgent.GetThreadAsync(
context.State["code_thread_id"].ToString()
);
await thread.AddMessageAsync($"审查反馈:\n{reviewResult}\n\n请改进代码");
var response = await thread.InvokeAsync();
context.State["code"] = response.Text;
// 返回到审查步骤
return WorkflowStepResult.GoTo("review_code");
})
.Build();
// 2. 执行 Workflow
var workflowContext = new WorkflowContext
{
Input = new Dictionary<string, object>
{
["requirement"] = "创建一个线程安全的缓存管理器"
}
};
await foreach (var update in workflow.RunStreamingAsync(workflowContext))
{
Console.WriteLine($"[{update.StepName}] {update.Status}: {update.Message}");
}
if (workflowContext.IsCompleted)
{
Console.WriteLine("✅ Workflow 完成");
Console.WriteLine($"最终代码:\n{workflowContext.State["code"]}");
}
Agent 高级特性
1. 上下文管理(AIContextProvider)
为 Agent 提供动态上下文信息:
public class UserContextProvider : IAIContextProvider
{
private readonly IHttpContextAccessor _httpContext;
public async Task<string> GetContextAsync(AgentThread thread)
{
var userId = _httpContext.HttpContext.User.FindFirst("sub")?.Value;
var userProfile = await _userService.GetProfileAsync(userId);
return $@"当前用户信息:
- 姓名:{userProfile.Name}
- VIP 等级:{userProfile.VipLevel}
- 历史订单数:{userProfile.OrderCount}
- 偏好类别:{string.Join(", ", userProfile.Preferences)}";
}
}
// 注册
var agent = new AIAgent(chatClient, new AIAgentOptions
{
Name = "PersonalAssistant",
Instructions = "你是一个个性化助手,根据用户信息提供定制服务",
ContextProviders = new[]
{
new UserContextProvider(httpContextAccessor, userService)
}
});
2. 中间件(Middleware)
在 Agent 执行前后添加自定义逻辑:
public class AuditMiddleware : IAgentMiddleware
{
private readonly IAuditLogger _auditLogger;
public async Task<AgentResponse> InvokeAsync(
AgentThread thread,
Func<Task<AgentResponse>> next)
{
var startTime = DateTime.UtcNow;
// 记录请求
await _auditLogger.LogAsync(new AuditEntry
{
ThreadId = thread.Id,
Action = "AgentInvoke",
Timestamp = startTime
});
try
{
// 执行 Agent
var response = await next();
// 记录响应
await _auditLogger.LogAsync(new AuditEntry
{
ThreadId = thread.Id,
Action = "AgentResponse",
Success = true,
Duration = DateTime.UtcNow - startTime
});
return response;
}
catch (Exception ex)
{
// 记录异常
await _auditLogger.LogAsync(new AuditEntry
{
ThreadId = thread.Id,
Action = "AgentError",
Success = false,
Error = ex.Message
});
throw;
}
}
}
// 使用
var agent = new AIAgent(chatClient, new AIAgentOptions
{
Name = "Assistant",
Middleware = new[]
{
new AuditMiddleware(auditLogger),
new RateLimitMiddleware(),
new CacheMiddleware()
}
});
3. 审批流程(Human-in-the-Loop)
敏感操作需要人工审批:
public class ApprovalAgent : AIAgent
{
private readonly IApprovalService _approvalService;
public ApprovalAgent(
IChatClient chatClient,
IApprovalService approvalService,
AIAgentOptions options)
: base(chatClient, options)
{
_approvalService = approvalService;
}
protected override async Task<ChatResponse> OnToolCallAsync(
AgentThread thread,
FunctionCallContent toolCall)
{
// 检查是否需要审批
if (RequiresApproval(toolCall.Name))
{
// 请求人工审批
var approved = await _approvalService.RequestApprovalAsync(new ApprovalRequest
{
ThreadId = thread.Id,
ToolName = toolCall.Name,
Arguments = toolCall.Arguments,
Reason = $"Agent 请求执行敏感操作: {toolCall.Name}"
});
if (!approved)
{
return new ChatResponse(new ChatMessage(
ChatRole.Tool,
$"操作被拒绝:{toolCall.Name} 未获得审批"
));
}
}
// 执行工具调用
return await base.OnToolCallAsync(thread, toolCall);
}
private bool RequiresApproval(string toolName)
{
var sensitiveTools = new[] { "delete_data", "transfer_money", "send_email_blast" };
return sensitiveTools.Contains(toolName);
}
}
生产环境最佳实践
1. 性能优化
// 使用缓存减少重复调用
var agent = new AIAgent(chatClient, new AIAgentOptions
{
Name = "Assistant",
ChatOptions = new ChatOptions
{
// 启用缓存
CacheOptions = new CacheOptions
{
Enabled = true,
Duration = TimeSpan.FromMinutes(10)
}
}
});
// 限制消息历史长度
var thread = await agent.CreateThreadAsync(new AgentThreadOptions
{
MaxMessages = 20, // 只保留最近 20 条消息
MessageReducer = new SlidingWindowReducer(tokenLimit: 4000)
});
2. 错误处理与降级
public async Task<AgentResponse> SafeInvokeAsync(AgentThread thread)
{
try
{
return await thread.InvokeAsync();
}
catch (ModelOverloadedException ex)
{
_logger.LogWarning("模型过载,切换到备用模型");
// 降级到更轻量的模型
var fallbackAgent = new AIAgent(fallbackChatClient, options);
var fallbackThread = await fallbackAgent.GetThreadAsync(thread.Id);
return await fallbackThread.InvokeAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Agent 执行失败");
return new AgentResponse
{
Text = "抱歉,系统暂时无法处理您的请求,请稍后重试。",
IsError = true
};
}
}
3. 可观测性
// 添加 OpenTelemetry
var agent = new AIAgent(
chatClient.UseOpenTelemetry(),
new AIAgentOptions
{
Name = "Assistant",
// 启用详细日志
LogLevel = LogLevel.Debug
}
);
// 自定义指标
var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter("MyApp.Agents")
.AddPrometheusExporter()
.Build();
// 记录 Agent 调用
var meter = new Meter("MyApp.Agents");
var invocationCounter = meter.CreateCounter<long>("agent.invocations");
var responseTimer = meter.CreateHistogram<double>("agent.response_time");
var sw = Stopwatch.StartNew();
var response = await thread.InvokeAsync();
sw.Stop();
invocationCounter.Add(1, new KeyValuePair<string, object>("agent_name", agent.Name));
responseTimer.Record(sw.Elapsed.TotalSeconds);
总结
智能 Agent 系统的核心要点:
✅ 状态管理:AgentThread 自动维护对话上下文
✅ 身份定义:Instructions 赋予 Agent 专业能力
✅ 工具集成:统一管理 Agent 可用的工具
✅ 持久化:IMessageStore 支持对话恢复和审计
✅ 协作编排:多 Agent 协同完成复杂任务
✅ 生产特性:审批流程、错误处理、可观测性
记住:好的 Agent 系统 = 清晰的职责 + 完善的工具 + 灵活的编排 + 可靠的基础设施