查看: 40|回复: 0

第一个智能体:从Hello-World到实际对话

[复制链接]

5

主题

0

回帖

0

积分

热心网友

金币
0
阅读权限
220
精华
0
威望
0
贡献
0
在线时间
0 小时
注册时间
2009-2-21
发表于 2026-3-12 19:44:00 | 显示全部楼层 |阅读模式

第一个智能体:从Hello World到实际对话

前言

在上一篇文章中,我们成功搭建了Agent Framework的开发环境,并创建了第一个简单的智能体。如果你完成了上篇文章的所有步骤,现在应该已经有了一个能够回答问题的"HelloBot"。

但是,真正的智能体不仅仅是回答一个问题那么简单。一个优秀的智能体应该能够:

  1. 理解上下文:记住对话历史,理解用户意图
  2. 处理复杂任务:分解复杂问题,分步解决
  3. 保持对话状态:在多轮对话中保持一致性
  4. 处理异常情况:优雅地处理错误和边界情况

今天,我们将从基础的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 场景设计:技术支持助手

让我们以一个实际场景为例:创建一个技术支持助手。这个助手需要能够:

  1. 回答常见技术问题
  2. 收集故障信息
  3. 提供解决方案步骤
  4. 必要时创建工单

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,怎么办?

智能体应该:

  1. 理解这是.NET的常见异常
  2. 询问更多信息(哪行代码、什么操作时发生)
  3. 提供排查步骤

预期回答示例:

NullReferenceException是.NET中常见的运行时错误,表示尝试访问null对象的成员。

为了帮您准确解决问题,请提供以下信息:
1. **错误发生的具体代码行**(如果有堆栈跟踪)
2. **相关的对象是什么**(哪个变量为null)
3. **操作步骤**:错误发生前您执行了什么操作

常见原因和解决方案:
1. **未初始化的对象**:确保对象在使用前已经实例化
2. **方法返回null**:检查方法的返回值处理
3. **异步操作问题**:检查async/await调用链

请提供更多细节,我可以给出更具体的建议。

3.2 场景2:复杂故障排查

用户输入:

我们的网站突然变慢,CPU使用率很高,但不知道原因。

智能体应该:

  1. 引导用户收集系统信息
  2. 提供诊断步骤
  3. 建议监控工具

预期回答:

网站性能问题通常需要系统性的排查。让我们分步骤进行:

**第一步:基本信息收集**
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. 提供后续流程

预期回答:

理解您的情况,这个问题可能需要我们的技术专家介入。

为了创建有效的技术支持工单,请提供以下信息:
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;
    }
}

九、总结与下一步

通过本文的学习,我们已经创建了一个功能完整的技术支持智能体。我们实现了:

  1. 智能系统指令设计:定义了专业的技术支持角色
  2. 对话历史管理:让智能体记住上下文
  3. 实际业务场景处理:处理从简单到复杂的技术问题
  4. 高级特性实现:流式响应、结构化输出
  5. 健壮性保障:错误处理、输入验证、性能优化
  6. 完整测试覆盖:单元测试和集成测试

关键收获:

  • 系统指令的质量直接影响智能体的表现
  • 对话历史管理是实现自然对话的关键
  • 错误处理是生产环境应用的必备功能
  • 性能优化能显著提升用户体验

下一篇文章预告:
在第三篇文章中,我们将探索如何让智能体具备真正的"执行能力"。通过工具集成,智能体将能够:

  • 调用外部API获取实时信息
  • 操作数据库进行数据查询
  • 执行系统命令和文件操作
  • 集成现有的业务系统

这将让我们的智能体从一个"知识库"升级为真正的"智能助手"。


实践建议:

  1. 尝试修改系统指令,观察智能体行为的变化
  2. 为你的业务场景定制专门的智能体
  3. 在实际项目中逐步引入Agent技术
  4. 持续关注框架更新和最佳实践

相关资源:

  • Agent Framework对话管理文档
  • 系统指令设计最佳实践
  • .NET性能优化指南

"好的智能体不是知道所有答案,而是知道如何找到答案并帮助用户解决问题。"



来源:https://www.cnblogs.com/fanshaoO/p/19709742
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

相关侵权、举报、投诉及建议等,请发 E-mail:qiongdian@foxmail.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部