知识目录 / Agent 开发

会话管理与持久化:保存和恢复对话

学习如何保存和恢复 Agent 会话,实现长时间运行的任务。

会话管理与持久化:保存和恢复对话

前言

在前面的文章中,我们学习了 Agent4J 的各种功能。但有一个问题:如果程序退出了,对话历史就丢失了。

对于简单的问答,这没问题。但对于长时间运行的任务,比如代码重构、数据分析,你可能需要:

  • 保存当前进度,下次继续
  • 在多个地方访问同一个会话
  • 实现会话的版本管理

Agent4J 的会话序列化功能可以帮你解决这些问题。

会话序列化

保存会话

java
AgentClientSession session = agent.createSession();

// 进行一些对话
session.command("请帮我分析这个项目")
    .then(handler)
    .error(errorHandler);

// 保存会话
String json = session.serialization();

// 保存到文件
Files.writeString(Paths.get("session.json"), json);

// 或者保存到数据库
database.saveSession(sessionId, json);

恢复会话

java
// 从文件读取
String json = Files.readString(Paths.get("session.json"));

// 或者从数据库读取
String json = database.getSession(sessionId);

// 恢复会话
AgentClientSession restored = agent.getSessionFromSerialization(json);

// 继续对话
restored.command("继续刚才的任务")
    .then(handler)
    .error(errorHandler);

序列化的内容

会话序列化会保存:

  • 对话历史(所有消息)
  • 当前上下文
  • 工具调用记录
  • Plan 执行状态
  • Sub-Agent 信息

但不会保存:

  • Agent 的配置(名称、描述、模型等)
  • 工具和技能的定义
  • 回调处理器

这意味着你需要用同一个 Agent 实例来恢复会话。

实际应用

应用 1:长时间运行的任务

假设你有一个需要几小时才能完成的任务:

java
public class LongRunningTask {
    
    private AgentClient agent;
    private String sessionId;
    
    public void startTask() {
        agent = createAgent();
        AgentClientSession session = agent.createSession();
        
        // 开始任务
        session.command("请帮我重构整个项目,包括代码审查、bug 修复、性能优化")
            .then(new AgentResultHandler() {
                public void onMessage(String msg) { 
                    System.out.print(msg); 
                }
                
                public void onPlanStepComplete(Plan plan, int current, int total, String step, String result) {
                    // 每完成一步,保存会话
                    saveSession(session);
                    System.out.println("步骤 " + current + " 完成,已保存进度");
                }
            })
            .error(e -> {
                e.printStackTrace();
                // 出错时也保存会话
                saveSession(session);
            });
    }
    
    private void saveSession(AgentClientSession session) {
        String json = session.serialization();
        Files.writeString(Paths.get("task-session.json"), json);
    }
    
    public void resumeTask() {
        // 恢复会话
        String json = Files.readString(Paths.get("task-session.json"));
        AgentClientSession session = agent.getSessionFromSerialization(json);
        
        // 继续任务
        session.command("继续刚才的重构任务")
            .then(handler)
            .error(errorHandler);
    }
}

应用 2:会话版本管理

你可以保存多个版本的会话:

java
public class SessionVersionManager {
    
    private String baseDir = "sessions";
    
    public void saveVersion(String taskId, String version, AgentClientSession session) {
        String dir = baseDir + "/" + taskId;
        Files.createDirectories(Paths.get(dir));
        
        String json = session.serialization();
        Files.writeString(Paths.get(dir + "/" + version + ".json"), json);
    }
    
    public AgentClientSession loadVersion(AgentClient agent, String taskId, String version) {
        String json = Files.readString(Paths.get(baseDir + "/" + taskId + "/" + version + ".json"));
        return agent.getSessionFromSerialization(json);
    }
    
    public List<String> listVersions(String taskId) {
        return Files.list(Paths.get(baseDir + "/" + taskId))
            .map(path -> path.getFileName().toString().replace(".json", ""))
            .collect(Collectors.toList());
    }
}

使用示例:

java
SessionVersionManager manager = new SessionVersionManager();

// 保存版本
manager.saveVersion("refactor-task", "v1", session);
manager.saveVersion("refactor-task", "v2", session);

// 加载版本
AgentClientSession v1 = manager.loadVersion(agent, "refactor-task", "v1");

应用 3:多端访问

如果你有一个 Web 应用,可以在多个地方访问同一个会话:

java
@RestController
public class AgentController {
    
    @Autowired
    private AgentService agentService;
    
    @PostMapping("/api/chat")
    public ResponseEntity<String> chat(@RequestBody ChatRequest request) {
        // 获取或创建会话
        AgentClientSession session = agentService.getSession(request.getSessionId());
        
        // 发送消息
        StringBuilder response = new StringBuilder();
        session.command(request.getMessage())
            .then(new AgentResultHandler() {
                public void onMessage(String msg) {
                    response.append(msg);
                }
            })
            .error(e -> {
                response.append("错误:").append(e.getMessage());
            });
        
        // 保存会话
        agentService.saveSession(request.getSessionId(), session);
        
        return ResponseEntity.ok(response.toString());
    }
}

@Service
public class AgentService {
    
    private Map<String, String> sessionStore = new ConcurrentHashMap<>();
    private AgentClient agent;
    
    public AgentClientSession getSession(String sessionId) {
        String json = sessionStore.get(sessionId);
        if (json != null) {
            return agent.getSessionFromSerialization(json);
        }
        return agent.createSession();
    }
    
    public void saveSession(String sessionId, AgentClientSession session) {
        String json = session.serialization();
        sessionStore.put(sessionId, json);
    }
}

应用 4:会话导出与分享

你可以把会话导出为文件,分享给别人:

java
public class SessionExporter {
    
    public void exportToFile(AgentClientSession session, String filename) {
        String json = session.serialization();
        
        // 格式化 JSON
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode node = mapper.readValue(json, ObjectNode.class);
        String prettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
        
        Files.writeString(Paths.get(filename), prettyJson);
    }
    
    public AgentClientSession importFromFile(AgentClient agent, String filename) {
        String json = Files.readString(Paths.get(filename));
        return agent.getSessionFromSerialization(json);
    }
}

会话清理

长时间运行的应用需要注意会话清理:

java
public class SessionManager {
    
    private Map<String, SessionInfo> sessions = new ConcurrentHashMap<>();
    
    public void cleanup() {
        long now = System.currentTimeMillis();
        long maxAge = 24 * 60 * 60 * 1000; // 24 小时
        
        sessions.entrySet().removeIf(entry -> {
            SessionInfo info = entry.getValue();
            return (now - info.getLastAccessTime()) > maxAge;
        });
    }
    
    private static class SessionInfo {
        private String sessionId;
        private String sessionData;
        private long lastAccessTime;
        
        // getter 和 setter
    }
}

性能考虑

序列化大小

会话序列化可能会产生较大的 JSON 字符串,特别是对话历史很长时。建议:

  • 定期清理不需要的对话历史
  • 对于非常长的对话,考虑拆分成多个会话
  • 使用压缩算法减小存储空间

存储选择

根据应用规模选择合适的存储:

  • 文件系统:适合单机应用
  • Redis:适合分布式应用
  • 数据库:适合需要持久化的应用
  • 对象存储:适合大规模应用

并发安全

如果多个线程可能同时访问同一个会话,需要考虑并发安全:

java
public class ThreadSafeSessionManager {
    
    private final ConcurrentHashMap<String, String> sessionStore = new ConcurrentHashMap<>();
    private final AgentClient agent;
    
    public AgentClientSession getSession(String sessionId) {
        String json = sessionStore.get(sessionId);
        if (json != null) {
            return agent.getSessionFromSerialization(json);
        }
        return agent.createSession();
    }
    
    public synchronized void saveSession(String sessionId, AgentClientSession session) {
        String json = session.serialization();
        sessionStore.put(sessionId, json);
    }
}

最佳实践

1. 定期保存

对于长时间运行的任务,建议定期保存会话:

java
session.command(task).then(new AgentResultHandler() {
    private int messageCount = 0;
    
    public void onMessage(String msg) {
        messageCount++;
        // 每 10 条消息保存一次
        if (messageCount % 10 == 0) {
            saveSession(session);
        }
    }
});

2. 保存时机

在关键节点保存会话:

  • 每个 Plan 步骤完成后
  • 每个 Sub-Agent 完成后
  • 发生错误时
  • 用户主动保存时

3. 版本管理

对于重要的任务,建议保留多个版本:

  • 每个重要步骤完成后保存一个版本
  • 保留最近 N 个版本
  • 定期清理旧版本

4. 错误恢复

实现错误恢复机制:

java
public class ResilientSession {
    
    public void executeWithRecovery(AgentClient agent, String task) {
        try {
            AgentClientSession session = loadOrCreateSession(agent);
            session.command(task)
                .then(handler)
                .error(e -> {
                    // 保存会话
                    saveSession(session);
                    // 记录错误
                    logError(e);
                    // 尝试恢复
                    retryOrNotify(task);
                });
        } catch (Exception e) {
            // 初始化失败,创建新会话
            AgentClientSession session = agent.createSession();
            session.command(task)
                .then(handler)
                .error(errorHandler);
        }
    }
}

总结

会话管理与持久化是 Agent4J 的重要特性,让你能:

  • 保存和恢复对话
  • 实现长时间运行的任务
  • 支持多端访问
  • 实现会话版本管理

合理使用这些特性,可以构建更强大、更可靠的 Agent 应用。


项目地址https://github.com/onlyGuo/agent4j

上一篇高级特性:Plan 和 Sub-Agent 的妙用

下一篇Agent4J 最佳实践:构建高质量的 Agent 应用