Skip to content

Step 3:多轮对话

单轮对话中,Agent 每次调用都是独立的——第二次问"那物流到哪了?"时,Agent 已经忘了刚才说的是哪个订单。多轮对话通过 AgentSession 解决这个问题。


什么是 AgentSession?

AgentSession 是跨 Agent 运行的会话状态容器。它在多次 RunAsync 调用之间保存上下文:

csharp
// 创建会话
AgentSession session = await agent.CreateSessionAsync();

// 第一轮
var r1 = await agent.RunAsync("我叫张三,是一名 .NET 开发者。", session);

// 第二轮——Agent 记得用户叫张三
var r2 = await agent.RunAsync("我的职业是什么?", session);
// ✅ 回复:你是 .NET 开发者

// 第三轮——继续深入
var r3 = await agent.RunAsync("帮我推荐几本 C# 进阶的书。", session);

每一轮调用后,Agent 框架自动把本轮的用户消息和 AI 回复追加到 session 中。后续调用带着完整历史上下文,所以 Agent 知道"职业"指的是"刚说的 .NET 开发者"。

Session 的序列化与恢复

AgentSession 可以序列化保存,也可以从序列化数据恢复——这在 Web 应用中至关重要:

csharp
// 序列化会话(存到文件/数据库/Redis)
var serialized = agent.SerializeSession(session);
await File.WriteAllTextAsync($"session_{userId}.json", serialized);

// 从序列化恢复会话
var savedSessionData = await File.ReadAllTextAsync($"session_{userId}.json");
AgentSession restoredSession = await agent.DeserializeSessionAsync(savedSessionData);

// 用恢复的会话继续对话
var reply = await agent.RunAsync("我们刚才说到哪了?", restoredSession);

完整的对话管理

csharp
public class ConversationManager
{
    private readonly AIAgent _agent;
    private readonly string _storagePath;

    public ConversationManager(AIAgent agent, string storagePath)
    {
        _agent = agent;
        _storagePath = storagePath;
    }

    public async Task<AgentSession> GetOrCreateSessionAsync(string userId)
    {
        var sessionFile = Path.Combine(_storagePath, $"session_{userId}.json");
        if (File.Exists(sessionFile))
        {
            var data = await File.ReadAllTextAsync(sessionFile);
            return await _agent.DeserializeSessionAsync(data);
        }
        return await _agent.CreateSessionAsync();
    }

    public async Task SaveSessionAsync(string userId, AgentSession session)
    {
        var serialized = _agent.SerializeSession(session);
        var sessionFile = Path.Combine(_storagePath, $"session_{userId}.json");
        Directory.CreateDirectory(_storagePath);
        await File.WriteAllTextAsync(sessionFile, serialized);
    }

    public async Task<string> ChatAsync(string userId, string message)
    {
        var session = await GetOrCreateSessionAsync(userId);
        var response = await _agent.RunAsync(message, session);
        await SaveSessionAsync(userId, session);
        return response.ToString();
    }
}

生产环境注意事项

并发安全

多个请求同时操作同一个 Session 可能导致消息乱序。解决方案是为每个用户会话使用独占的 Session 实例,或者实现 Session 的锁机制。

存储持久化

AgentSession 默认存在于内存中,服务器重启后丢失。生产环境需要实现 Session 的序列化存储。上面的 ConversationManager 类展示了基于文件的持久化方案。分布式部署建议使用 Redis 或 Cosmos DB。

上下文窗口管理

LLM 的上下文窗口是有限的(GPT-4o-mini 支持 128K tokens)。随着对话轮次增加,历史消息越来越多。解决办法是定期检查 Session 中的 Token 总数,在接近上限时把早期对话压缩为一段摘要。SerializeSessionDeserializeSessionAsync 方法让这种操作成为可能。

Session 的实现原理

AgentSession 内部维护了一个消息列表。每次 RunAsync 调用时,Agent 框架会:

  1. 从 Session 中读取历史消息
  2. 拼接新的用户消息
  3. 发送给 LLM
  4. 把 LLM 的回复追加到 Session 中

下一步:Step 4:持久化内存 — 通过 Context Provider 实现跨会话记忆。

学而不思则罔,思而不学则殆