我来帮你实现 OpenClaw 的本地保存功能。这里有几种常见的保存方案

openclaw openclaw中文博客 2

使用浏览器 localStorage(前端保存)

// openclaw-local-storage.js
class OpenClawLocalStorage {
  constructor(namespace = 'openclaw') {
    this.namespace = namespace;
  }
  // 保存会话
  saveSession(sessionId, data) {
    const sessions = this.getSessions();
    sessions[sessionId] = {
      ...data,
      updatedAt: new Date().toISOString(),
    };
    localStorage.setItem(`${this.namespace}_sessions`, JSON.stringify(sessions));
    return sessionId;
  }
  // 获取所有会话
  getSessions() {
    const sessions = localStorage.getItem(`${this.namespace}_sessions`);
    return sessions ? JSON.parse(sessions) : {};
  }
  // 获取单个会话
  getSession(sessionId) {
    const sessions = this.getSessions();
    return sessions[sessionId] || null;
  }
  // 删除会话
  deleteSession(sessionId) {
    const sessions = this.getSessions();
    delete sessions[sessionId];
    localStorage.setItem(`${this.namespace}_sessions`, JSON.stringify(sessions));
  }
  // 导出数据
  exportData() {
    const data = {
      sessions: this.getSessions(),
      version: '1.0',
      exportDate: new Date().toISOString(),
    };
    return JSON.stringify(data, null, 2);
  }
  // 导入数据
  importData(jsonString) {
    try {
      const data = JSON.parse(jsonString);
      if (data.sessions) {
        const existing = this.getSessions();
        const merged = { ...existing, ...data.sessions };
        localStorage.setItem(`${this.namespace}_sessions`, JSON.stringify(merged));
        return true;
      }
      return false;
    } catch (error) {
      console.error('导入数据失败:', error);
      return false;
    }
  }
}

使用 IndexedDB(支持更大数据量)

// openclaw-indexeddb.js
class OpenClawIndexedDB {
  constructor(dbName = 'OpenClawDB', version = 1) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
  }
  // 初始化数据库
  async init() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);
      request.onupgradeneeded = (event) => {
        const db = event.target.result;
        // 创建会话存储
        if (!db.objectStoreNames.contains('sessions')) {
          const sessionsStore = db.createObjectStore('sessions', { keyPath: 'id' });
          sessionsStore.createIndex('updatedAt', 'updatedAt', { unique: false });
        }
        // 创建消息存储
        if (!db.objectStoreNames.contains('messages')) {
          const messagesStore = db.createObjectStore('messages', { keyPath: 'id', autoIncrement: true });
          messagesStore.createIndex('sessionId', 'sessionId', { unique: false });
        }
      };
      request.onsuccess = (event) => {
        this.db = event.target.result;
        resolve(this);
      };
      request.onerror = (event) => {
        reject(event.target.error);
      };
    });
  }
  // 保存会话
  async saveSession(session) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['sessions'], 'readwrite');
      const store = transaction.objectStore('sessions');
      const sessionData = {
        ...session,
        id: session.id || Date.now().toString(),
        updatedAt: new Date().toISOString(),
        createdAt: session.createdAt || new Date().toISOString(),
      };
      const request = store.put(sessionData);
      request.onsuccess = () => resolve(sessionData.id);
      request.onerror = (event) => reject(event.target.error);
    });
  }
  // 获取所有会话
  async getAllSessions() {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['sessions'], 'readonly');
      const store = transaction.objectStore('sessions');
      const request = store.getAll();
      request.onsuccess = (event) => resolve(event.target.result);
      request.onerror = (event) => reject(event.target.error);
    });
  }
  // 保存消息
  async saveMessage(sessionId, message) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['messages'], 'readwrite');
      const store = transaction.objectStore('messages');
      const messageData = {
        ...message,
        sessionId,
        timestamp: new Date().toISOString(),
      };
      const request = store.add(messageData);
      request.onsuccess = () => resolve(request.result);
      request.onerror = (event) => reject(event.target.error);
    });
  }
  // 获取会话消息
  async getSessionMessages(sessionId) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['messages'], 'readonly');
      const store = transaction.objectStore('messages');
      const index = store.index('sessionId');
      const request = index.getAll(sessionId);
      request.onsuccess = (event) => resolve(event.target.result);
      request.onerror = (event) => reject(event.target.error);
    });
  }
}

保存到文件(Node.js 环境)

// openclaw-file-saver.js
const fs = require('fs');
const path = require('path');
class OpenClawFileSaver {
  constructor(baseDir = './saves') {
    this.baseDir = baseDir;
    this.ensureDirExists();
  }
  ensureDirExists() {
    if (!fs.existsSync(this.baseDir)) {
      fs.mkdirSync(this.baseDir, { recursive: true });
    }
  }
  // 保存会话到文件
  saveSession(sessionId, data) {
    const filename = path.join(this.baseDir, `${sessionId}.json`);
    const sessionData = {
      ...data,
      sessionId,
      savedAt: new Date().toISOString(),
    };
    fs.writeFileSync(filename, JSON.stringify(sessionData, null, 2));
    return filename;
  }
  // 加载会话
  loadSession(sessionId) {
    const filename = path.join(this.baseDir, `${sessionId}.json`);
    if (fs.existsSync(filename)) {
      const content = fs.readFileSync(filename, 'utf-8');
      return JSON.parse(content);
    }
    return null;
  }
  // 列出所有保存的会话
  listSessions() {
    const files = fs.readdirSync(this.baseDir);
    return files
      .filter(file => file.endsWith('.json'))
      .map(file => {
        const filepath = path.join(this.baseDir, file);
        const content = fs.readFileSync(filepath, 'utf-8');
        return JSON.parse(content);
      });
  }
  // 导出为单个文件
  exportAll(outputPath = './openclaw_backup.json') {
    const sessions = this.listSessions();
    const exportData = {
      sessions,
      exportDate: new Date().toISOString(),
      version: '1.0',
    };
    fs.writeFileSync(outputPath, JSON.stringify(exportData, null, 2));
    return outputPath;
  }
}

使用示例

// 示例:在前端使用 localStorage
const storage = new OpenClawLocalStorage();
// 保存会话
const sessionId = storage.saveSession('session-1', { 'OpenClaw 对话',
  messages: [
    { role: 'user', content: '你好' },
    { role: 'assistant', content: '你好!我是OpenClaw助手' }
  ],
  model: 'gpt-3.5-turbo',
  temperature: 0.7
});
// 获取所有会话
const sessions = storage.getSessions();
// 导出数据
const exportData = storage.exportData();
// 导入数据
storage.importData(exportData);

自动保存功能

// openclaw-autosave.js
class OpenClawAutoSave {
  constructor(storage, options = {}) {
    this.storage = storage; // 可以是 localStorage 或 IndexedDB 实例
    this.autoSaveInterval = options.autoSaveInterval || 30000; // 30秒
    this.lastSaveTime = null;
    this.isSaving = false;
    this.queue = [];
    if (options.autoSave) {
      this.startAutoSave();
    }
  }
  startAutoSave() {
    this.autoSaveTimer = setInterval(() => {
      this.saveQueuedData();
    }, this.autoSaveInterval);
  }
  stopAutoSave() {
    if (this.autoSaveTimer) {
      clearInterval(this.autoSaveTimer);
    }
  }
  queueSave(sessionId, data) {
    this.queue.push({ sessionId, data });
  }
  async saveQueuedData() {
    if (this.isSaving || this.queue.length === 0) return;
    this.isSaving = true;
    const items = [...this.queue];
    this.queue = [];
    try {
      for (const item of items) {
        await this.storage.saveSession(item.sessionId, item.data);
      }
      this.lastSaveTime = new Date();
      console.log('自动保存完成', items.length, '个项目');
    } catch (error) {
      console.error('自动保存失败:', error);
    } finally {
      this.isSaving = false;
    }
  }
}

选择哪种方案取决于你的具体需求:

我来帮你实现 OpenClaw 的本地保存功能。这里有几种常见的保存方案-第1张图片-OpenClaw 中文版 - 真正能做事的 AI

  1. localStorage:简单、快速,适合小数据量
  2. IndexedDB:适合大量数据,支持索引查询
  3. 文件保存:适合Node.js环境

需要我帮你集成到具体的项目中吗?

抱歉,评论功能暂时关闭!