如何从Hello-World智能体发展到实际对话智能体?
摘要:第一个智能体:从Hello World到实际对话 前言 在上一篇文章中,我们成功搭建了Agent Framework的开发环境,并创建了第一个简单的智能体。如果你完成了上篇文章的所有步骤,现在应该已经有了一个能够回答问题的&
第一个智能体:从Hello World到实际对话
前言
在上一篇文章中,我们成功搭建了Agent Framework的开发环境,并创建了第一个简单的智能体。如果你完成了上篇文章的所有步骤,现在应该已经有了一个能够回答问题的"HelloBot"。
但是,真正的智能体不仅仅是回答一个问题那么简单。一个优秀的智能体应该能够:
理解上下文:记住对话历史,理解用户意图
处理复杂任务:分解复杂问题,分步解决
保持对话状态:在多轮对话中保持一致性
处理异常情况:优雅地处理错误和边界情况
今天,我们将从基础的Hello World升级到创建一个真正有用的智能体。通过本文的学习,你将能够创建一个可以处理实际业务场景的AI助手。
一、智能体的核心概念深入
1.1 智能体的组成要素
在深入编码之前,我们需要理解智能体的几个关键组成部分:
1. 系统指令(System Instructions)
这是智能体的"人格设定",决定了它如何思考和回答。好的系统指令应该:
明确智能体的角色和职责
定义回答风格和格式
设置行为边界和限制
2. 上下文管理(Context Management)
智能体如何记忆和利用对话历史。这包括:
短期记忆:当前对话的上下文
长期记忆:跨会话的重要信息
上下文窗口管理:处理token限制
3. 响应处理(Response Handling)
如何解析和处理智能体的输出:
流式响应:实时显示回答过程
结构化输出:获取JSON等结构化数据
错误处理:处理各种异常情况
1.2 Agent Framework的智能体模型
Microsoft Agent Framework采用了统一的智能体模型,无论使用哪种LLM提供商,都使用相同的接口:
// 基本智能体创建
var agent = client
.AsAIAgent(
name: "AgentName", // 智能体名称
instructions: "Instructions", // 系统指令
modelConfiguration: config // 模型配置
);
这种统一的设计让代码在不同提供商之间迁移变得非常容易。
二、创建业务导向的智能体
2.1 场景设计:技术支持助手
让我们以一个实际场景为例:创建一个技术支持助手。这个助手需要能够:
回答常见技术问题
收集故障信息
提供解决方案步骤
必要时创建工单
2.2 系统指令设计
好的系统指令是智能体成功的关键。对于技术支持助手,我们的系统指令应该:
const string supportInstructions = """
你是一个专业的技术支持助手,专门帮助用户解决软件技术问题。
你的职责包括:
1. 耐心倾听用户的问题描述
2. 询问必要的故障信息(错误代码、操作步骤、环境信息等)
3. 提供清晰、分步的解决方案
4. 如果问题复杂,建议创建正式的技术支持工单
5. 始终保持专业和友好的态度
回答规范:
- 使用中文回答,技术术语保持英文
- 复杂解决方案使用编号列表
- 重要的技术细节使用**粗体**强调
- 如果涉及敏感信息,提醒用户通过安全渠道提供
限制:
- 不要提供未经证实的安全建议
- 不要代替用户执行危险操作
- 涉及账号安全问题时,引导用户联系官方支持
""";
2.3 智能体实现代码
基于上面的设计,我们来创建完整的技术支持助手:
// TechSupportAgent.cs
using Microsoft.Agents.AI;
using OpenAI;
public class TechSupportAgent
{
private readonly IAIAgent _agent;
// 系统指令
private const string Instructions = """
你是一个专业的技术支持助手,专门帮助用户解决软件技术问题。
你的职责包括:
1. 耐心倾听用户的问题描述
2. 询问必要的故障信息(错误代码、操作步骤、环境信息等)
3. 提供清晰、分步的解决方案
4. 如果问题复杂,建议创建正式的技术支持工单
5. 始终保持专业和友好的态度
回答规范:
- 使用中文回答,技术术语保持英文
- 复杂解决方案使用编号列表
- 重要的技术细节使用**粗体**强调
- 如果涉及敏感信息,提醒用户通过安全渠道提供
限制:
- 不要提供未经证实的安全建议
- 不要代替用户执行危险操作
- 涉及账号安全问题时,引导用户联系官方支持
""";
public TechSupportAgent(string apiKey, string model = "gpt-4o-mini")
{
var openAIClient = new OpenAIClient(apiKey);
_agent = openAIClient
.GetResponsesClient(model)
.AsAIAgent(
name: "TechSupportAssistant",
instructions: Instructions
);
}
public async Task<string> GetSupportAsync(string userQuestion)
{
try
{
Console.WriteLine($"用户问题: {userQuestion}");
Console.WriteLine("正在分析问题...");
var response = await _agent.RunAsync(userQuestion);
return response;
}
catch (Exception ex)
{
return $"抱歉,处理您的请求时出现错误: {ex.Message}。请稍后重试或联系人工支持。";
}
}
}
2.4 添加对话历史管理
真实的对话需要记住历史。让我们改进智能体,让它能够记住对话内容:
// EnhancedTechSupportAgent.cs
using Microsoft.Agents.AI;
using OpenAI;
using System.Text.Json;
public class EnhancedTechSupportAgent
{
private readonly IAIAgent _agent;
private readonly List<ConversationTurn> _conversationHistory;
// 对话回合记录
private class ConversationTurn
{
public string Role { get; set; } = string.Empty; // "user" 或 "assistant"
public string Content { get; set; } = string.Empty;
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}
public EnhancedTechSupportAgent(string apiKey, string model = "gpt-4o-mini")
{
var openAIClient = new OpenAIClient(apiKey);
_agent = openAIClient
.GetResponsesClient(model)
.AsAIAgent(
name: "TechSupportAssistant",
instructions: Instructions
);
_conversationHistory = new List<ConversationTurn>();
}
public async Task<string> ProcessQueryAsync(string userQuery)
{
// 添加用户查询到历史
_conversationHistory.Add(new ConversationTurn
{
Role = "user",
Content = userQuery
});
try
{
// 构建包含历史的上下文
var context = BuildConversationContext();
// 这里简化处理,实际应该使用框架的对话管理功能
var response = await _agent.RunAsync(context + userQuery);
// 添加助手回答到历史
_conversationHistory.Add(new ConversationTurn
{
Role = "assistant",
Content = response
});
// 如果历史太长,清理早期记录
ManageHistorySize();
return response;
}
catch (Exception ex)
{
return $"处理错误: {ex.Message}";
}
}
private string BuildConversationContext()
{
if (_conversationHistory.Count == 0)
return string.Empty;
// 只保留最近5轮对话作为上下文
var recentHistory = _conversationHistory
.TakeLast(5)
.Select(turn => $"{turn.Role}: {turn.Content}")
.ToList();
return "对话历史:\n" + string.Join("\n", recentHistory) + "\n\n当前问题: ";
}
private void ManageHistorySize()
{
// 保持最多20轮对话历史
if (_conversationHistory.Count > 20)
{
_conversationHistory.RemoveRange(0, _conversationHistory.Count - 20);
}
}
public string GetConversationSummary()
{
return JsonSerializer.Serialize(_conversationHistory, new JsonSerializerOptions
{
WriteIndented = true
});
}
}
三、实际对话场景示例
3.1 场景1:简单技术问题
让我们看看智能体如何处理一个简单问题:
用户输入:
我的程序报错:NullReferenceException,怎么办?
智能体应该:
理解这是.NET的常见异常
询问更多信息(哪行代码、什么操作时发生)
提供排查步骤
预期回答示例:
NullReferenceException是.NET中常见的运行时错误,表示尝试访问null对象的成员。
为了帮您准确解决问题,请提供以下信息:
1. **错误发生的具体代码行**(如果有堆栈跟踪)
2. **相关的对象是什么**(哪个变量为null)
3. **操作步骤**:错误发生前您执行了什么操作
常见原因和解决方案:
1. **未初始化的对象**:确保对象在使用前已经实例化
2. **方法返回null**:检查方法的返回值处理
3. **异步操作问题**:检查async/await调用链
请提供更多细节,我可以给出更具体的建议。
3.2 场景2:复杂故障排查
用户输入:
我们的网站突然变慢,CPU使用率很高,但不知道原因。
智能体应该:
引导用户收集系统信息
提供诊断步骤
建议监控工具
预期回答:
网站性能问题通常需要系统性的排查。让我们分步骤进行:
**第一步:基本信息收集**
1. 服务器规格(CPU核心数、内存大小)
2. 当前并发用户数
3. 数据库负载情况
**第二步:初步诊断**
1. 使用任务管理器或top命令查看具体哪个进程占用CPU
2. 检查应用程序日志中的错误或警告
3. 监控网络流量和响应时间
**第三步:深度分析**
1. 如果是ASP.NET应用,启用性能计数器
2. 使用dotnet-counters或dotnet-trace收集运行时信息
3. 分析数据库查询性能
**建议工具:**
- **Application Insights**:Azure的APM工具
- **dotnet-counters**:.NET性能计数器
- **SQL Server Profiler**:数据库性能分析
请先执行第一步的信息收集,然后我们可以进一步分析。
3.3 场景3:创建工单
当问题需要人工介入时:
用户输入:
按照你的建议检查了,还是找不到问题。我们需要紧急支持。
智能体应该:
确认需要人工支持
收集必要信息创建工单
提供后续流程
预期回答:
理解您的情况,这个问题可能需要我们的技术专家介入。
为了创建有效的技术支持工单,请提供以下信息:
1. **问题描述**:详细的问题现象和发生时间
2. **已尝试的解决步骤**:您已经尝试过哪些方法
3. **系统环境**:
- 操作系统版本
- .NET运行时版本
- 应用程序版本
4. **错误日志**:相关的应用程序日志或事件查看器记录
5. **影响范围**:有多少用户受影响,业务影响程度
**紧急联系方式**:
- 电话支持:400-xxx-xxxx(24小时)
- 在线工单系统:https://support.example.com
请尽快提交以上信息,我们的技术支持团队会优先处理您的请求。
四、高级特性:流式响应和结构化输出
4.1 流式响应实现
对于长回答,流式响应能提供更好的用户体验:
public async Task StreamResponseAsync(string userQuery)
{
Console.WriteLine("技术支持助手:");
// 注意:实际API可能有所不同,这里展示概念
var responseStream = await _agent.RunStreamingAsync(userQuery);
await foreach (var chunk in responseStream)
{
Console.Write(chunk);
await Task.Delay(50); // 模拟实时显示效果
}
Console.WriteLine();
}
4.2 结构化输出
有时我们需要智能体返回结构化数据:
public async Task<TroubleTicket> CreateTroubleTicketAsync(string issueDescription)
{
const string ticketInstructions = """
请根据用户的问题描述创建一个标准的技术支持工单。
返回JSON格式,包含以下字段:
- title: 工单标题
- severity: 严重程度(Low/Medium/High/Critical)
- category: 问题分类
- description: 详细描述
- stepsToReproduce: 重现步骤
- expectedBehavior: 期望行为
- actualBehavior: 实际行为
""";
var jsonAgent = _client
.GetResponsesClient("gpt-4o")
.AsAIAgent(
name: "TicketCreator",
instructions: ticketInstructions
);
var response = await jsonAgent.RunAsync(issueDescription);
try
{
return JsonSerializer.Deserialize<TroubleTicket>(response)
?? throw new InvalidOperationException("无法解析工单信息");
}
catch
{
// 如果JSON解析失败,创建默认工单
return new TroubleTicket
{
Title = "技术支持请求",
Severity = "Medium",
Description = issueDescription,
CreatedAt = DateTime.UtcNow
};
}
}
public class TroubleTicket
{
public string Title { get; set; } = string.Empty;
public string Severity { get; set; } = "Medium"; // Low/Medium/High/Critical
public string Category { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public List<string> StepsToReproduce { get; set; } = new();
public string ExpectedBehavior { get; set; } = string.Empty;
public string ActualBehavior { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
}
五、错误处理与边缘情况
5.1 网络错误处理
public async Task<string> GetResponseWithRetryAsync(string query, int maxRetries = 3)
{
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
return await _agent.RunAsync(query);
}
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
// 429 Too Many Requests
var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt)); // 指数退避
Console.WriteLine($"速率限制,等待 {delay.TotalSeconds} 秒后重试...");
await Task.Delay(delay);
}
catch (HttpRequestException ex) when ((int)ex.StatusCode >= 500)
{
// 服务器错误
if (attempt == maxRetries)
throw;
await Task.Delay(TimeSpan.FromSeconds(attempt));
}
catch (Exception ex) when (ex.Message.Contains("timeout", StringComparison.OrdinalIgnoreCase))
{
// 超时错误
Console.WriteLine($"请求超时,尝试 {attempt}/{maxRetries}");
if (attempt == maxRetries)
throw;
}
}
throw new InvalidOperationException($"在{maxRetries}次尝试后仍失败");
}
5.2 输入验证和清理
public string SanitizeInput(string userInput)
{
if (string.IsNullOrWhiteSpace(userInput))
throw new ArgumentException("输入不能为空");
// 限制输入长度
if (userInput.Length > 2000)
{
userInput = userInput[..2000] + "... [内容过长已截断]";
}
// 移除可能的恶意内容(简单示例)
var maliciousPatterns = new[]
{
"<script>", "javascript:", "onload=", "onerror="
};
foreach (var pattern in maliciousPatterns)
{
if (userInput.Contains(pattern, StringComparison.OrdinalIgnoreCase))
{
userInput = userInput.Replace(pattern, "[已过滤]", StringComparison.OrdinalIgnoreCase);
}
}
return userInput;
}
六、性能优化技巧
6.1 响应缓存
public class CachedAgent
{
private readonly IAIAgent _agent;
private readonly MemoryCache _cache;
public CachedAgent(IAIAgent agent)
{
_agent = agent;
_cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 100 // 最多缓存100个回答
});
}
public async Task<string> GetCachedResponseAsync(string query)
{
var cacheKey = $"response_{query.GetHashCode()}";
if (_cache.TryGetValue(cacheKey, out string cachedResponse))
{
Console.WriteLine("使用缓存回答");
return cachedResponse;
}
var response = await _agent.RunAsync(query);
// 缓存回答(设置合适的过期时间)
var cacheOptions = new MemoryCacheEntryOptions
{
Size = 1,
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
};
_cache.Set(cacheKey, response, cacheOptions);
return response;
}
}
6.2 并发处理
public async Task<List<string>> ProcessBatchAsync(List<string> queries, int maxConcurrency = 5)
{
var semaphore = new SemaphoreSlim(maxConcurrency);
var tasks = new List<Task<string>>();
foreach (var query in queries)
{
await semaphore.WaitAsync();
tasks.Add(Task.Run(async () =>
{
try
{
return await _agent.RunAsync(query);
}
finally
{
semaphore.Release();
}
}));
}
return (await Task.WhenAll(tasks)).ToList();
}
七、完整示例程序
7.1 主程序
// Program.cs
using System.Text.Json;
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("=== 技术支持助手启动 ===");
// 配置
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
Console.WriteLine("请设置 OPENAI_API_KEY 环境变量");
return;
}
// 创建智能体
var agent = new EnhancedTechSupportAgent(apiKey, "gpt-4o-mini");
Console.WriteLine("助手已就绪!输入您的问题(输入 'exit' 退出)");
Console.WriteLine();
while (true)
{
Console.Write("> ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
continue;
if (input.Equals("exit", StringComparison.OrdinalIgnoreCase))
break;
if (input.Equals("history", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("对话历史:");
Console.WriteLine(agent.GetConversationSummary());
continue;
}
try
{
var response = await agent.ProcessQueryAsync(input);
Console.WriteLine();
Console.WriteLine($"助手: {response}");
Console.WriteLine();
}
catch (Exception ex)
{
Console.WriteLine($"错误: {ex.Message}");
}
}
Console.WriteLine("感谢使用技术支持助手!");
}
}
7.2 项目结构
TechSupportDemo/
├── TechSupportDemo.csproj
├── Program.cs
├── TechSupportAgent.cs
├── EnhancedTechSupportAgent.cs
├── CachedAgent.cs
├── Models/
│ ├── TroubleTicket.cs
│ └── ConversationTurn.cs
└── README.md
八、测试与验证
8.1 单元测试示例
// TechSupportAgentTests.cs
using Xunit;
public class TechSupportAgentTests
{
[Fact]
public void SanitizeInput_RemovesMaliciousContent()
{
var agent = new TechSupportAgent("test-key");
var input = "问题描述<script>alert('xss')</script>";
var sanitized = agent.SanitizeInput(input);
Assert.DoesNotContain("<script>", sanitized);
Assert.Contains("[已过滤]", sanitized);
}
[Theory]
[InlineData("", true)]
[InlineData(" ", true)]
[InlineData("正常输入", false)]
public void SanitizeInput_ValidatesEmptyInput(string input, bool shouldThrow)
{
var agent = new TechSupportAgent("test-key");
if (shouldThrow)
{
Assert.Throws<ArgumentException>(() => agent.SanitizeInput(input));
}
else
{
var result = agent.SanitizeInput(input);
Assert.NotNull(result);
}
}
}
8.2 集成测试
// IntegrationTests.cs
public class IntegrationTests : IAsyncLifetime
{
private TechSupportAgent _agent;
public async Task InitializeAsync()
{
// 使用测试API密钥或模拟
var apiKey = Environment.GetEnvironmentVariable("TEST_OPENAI_API_KEY");
_agent = new TechSupportAgent(apiKey ?? "test-key");
}
[Fact]
public async Task Agent_RespondsToCommonIssue()
{
var response = await _agent.GetSupportAsync("程序报错 NullReferenceException");
Assert.NotNull(response);
Assert.NotEmpty(response);
Assert.Contains("NullReferenceException", response);
}
public Task DisposeAsync()
{
_agent = null;
return Task.CompletedTask;
}
}
九、总结与下一步
通过本文的学习,我们已经创建了一个功能完整的技术支持智能体。我们实现了:
✅ 智能系统指令设计:定义了专业的技术支持角色
✅ 对话历史管理:让智能体记住上下文
✅ 实际业务场景处理:处理从简单到复杂的技术问题
✅ 高级特性实现:流式响应、结构化输出
✅ 健壮性保障:错误处理、输入验证、性能优化
✅ 完整测试覆盖:单元测试和集成测试
关键收获:
系统指令的质量直接影响智能体的表现
对话历史管理是实现自然对话的关键
错误处理是生产环境应用的必备功能
性能优化能显著提升用户体验
下一篇文章预告:
在第三篇文章中,我们将探索如何让智能体具备真正的"执行能力"。通过工具集成,智能体将能够:
调用外部API获取实时信息
操作数据库进行数据查询
执行系统命令和文件操作
集成现有的业务系统
这将让我们的智能体从一个"知识库"升级为真正的"智能助手"。
实践建议:
尝试修改系统指令,观察智能体行为的变化
为你的业务场景定制专门的智能体
在实际项目中逐步引入Agent技术
持续关注框架更新和最佳实践
相关资源:
Agent Framework对话管理文档
系统指令设计最佳实践
.NET性能优化指南
"好的智能体不是知道所有答案,而是知道如何找到答案并帮助用户解决问题。"
