Agent Scope Java 2.x 系列【17】Harness:工作区远程存储模式
文章目录1. 概述2. 架构原理2.1 核心抽象2.2 BaseStore 接口2.3 RemoteFilesystemSpec 路由策略2.4 命名空间与多租户隔离3. JDBC 模式3.1 适用场景3.2 表结构3.3 配置案例3.4 优势4. OSS 模式4.1 适用场景4.2 配置案例4.3 优势5. Redis 模式5.1 适用场景5.2 配置案例5.3 优势6. 混合模式7. 自定义 BaseStore 实现8. 三种模式对比1. 概述AgentScope Harness的工作区文件系统通过BaseStore抽象层实现了存储与逻辑解耦支持将工作区数据记忆、技能、会话日志、子Agent配置等从本地磁盘迁移到远程共享存储。官方提供了三个扩展模块模块存储后端适用场景agentscope-extensions-redisRedis高性能缓存型存储适合低延迟读写agentscope-extensions-mysqlMySQL / JDBC关系型数据库适合需要事务和 SQL 查询的场景agentscope-extensions-oss阿里云 OSS对象存储适合大文件、低成本海量存储2. 架构原理2.1 核心抽象┌──────────────────────────────────────────────┐ │ HarnessAgent │ │ .distributedStore(store) │ │ .filesystem(new RemoteFilesystemSpec(...)) │ └──────────────┬───────────────────────────────┘ │ ┌───────▼────────┐ │ DistributedStore │ ← 统一入口 └───┬─────────┬──┘ │ │ ┌──────▼──┐ ┌──▼───────────┐ │BaseStore │ │AgentStateStore│ │(工作区KV) │ │(会话状态持久化) │ └──────┬──┘ └──────────────┘ │ ┌──────┼──────────┬──────────┐ │ │ │ │ Redis MySQL/JDBC OSS 自定义实现2.2 BaseStore 接口工作区文件的读写最终落到BaseStore基于命名空间的KV存储核心源码publicinterfaceBaseStore{StoreItemget(ListStringnamespace,Stringkey);voidput(ListStringnamespace,Stringkey,MapString,Objectvalue);ListStoreItemsearch(ListStringnamespace,intlimit,intoffset);voiddelete(ListStringnamespace,Stringkey);// CAS 乐观锁写入可选实现defaultbooleanputIfVersion(ListStringnamespace,Stringkey,MapString,Objectvalue,longexpectedVersion){returnfalse;}}2.3 RemoteFilesystemSpec 路由策略RemoteFilesystemSpec将工作区目录按路径前缀分片路由到不同的存储命名空间workspace/ ├── AGENTS.md ──→ store namespace [agents, id, users, uid, root] ├── MEMORY.md ──→ store namespace [agents, id, users, uid, root] ├── tools.json ──→ store namespace [agents, id, users, uid, root] ├── memory/ ──→ store namespace [agents, id, users, uid, memory] ├── skills/ ──→ store namespace [agents, id, users, uid, skills] ├── subagents/ ──→ store namespace [agents, id, users, uid, subagents] ├── knowledge/ ──→ store namespace [agents, id, users, uid, knowledge] └── agents/id/ ├── sessions/ ──→ store namespace [agents, id, users, uid, sessions] └── tasks/ ──→ store namespace [agents, id, users, uid, tasks]Overlay模式每条路由采用RemoteFilesystem上层可读写LocalFilesystem下层只读模板的叠加结构——本地模板文件作为基线用户编辑后copy-on-write落入远程存储后续读取优先使用远程版本。2.4 命名空间与多租户隔离IsolationScope控制命名空间前缀实现无侵入的多租户隔离IsolationScope命名空间结构共享范围SESSIONagents/id/sessions/sid单会话完全隔离USER默认agents/id/users/uid同一用户跨会话共享AGENTagents/id/shared所有用户共享GLOBALglobal全局共享3. JDBC 模式3.1 适用场景已有MySQL/PostgreSQL基础设施的团队需要通过 SQL 对存储数据进行查询和管理的场景需要事务性保证如记忆更新 会话日志写入的原子性企业内部合规要求使用关系型数据库3.2 表结构JdbcStore使用一张通用 KV 表包含乐观锁版本控制CREATETABLEIFNOTEXISTSagentscope_store(namespace_pathVARCHAR(300)NOTNULL,-- 命名空间路径段间用 0x1F 分隔item_keyVARCHAR(200)NOTNULL,-- 条目 Keyvalue_jsonLONGTEXTNOTNULL,-- Jackson 序列化的 MapString, ObjectversionBIGINTNOTNULLDEFAULT0,-- 乐观锁版本号每次写入 1updated_atBIGINTNOTNULLDEFAULT0,-- 最后写入时间epoch millisPRIMARYKEY(namespace_path,item_key))ENGINEInnoDBDEFAULTCHARSETutf8mb4;utf8mb4 主键超长问题MySQL InnoDB 索引键最大 3072 字节。VARCHAR(300)VARCHAR(200) 500 字符 × 4 字节 2000 字节安全落在限制内。JdbcStore自带的 DDL 列宽偏大建议用schema.sql预建表并将JdbcStore设为initializeSchema(false)。3.3 配置案例importio.agentscope.extensions.mysql.state.MysqlAgentStateStore;importio.agentscope.extensions.mysql.store.JdbcStore;importio.agentscope.harness.agent.DistributedStore;importio.agentscope.harness.agent.HarnessAgent;importio.agentscope.harness.agent.IsolationScope;importio.agentscope.harness.agent.filesystem.spec.RemoteFilesystemSpec;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjavax.sql.DataSource;ConfigurationpublicclassJdbcWorkspaceConfig{BeanpublicDistributedStoredistributedStore(DataSourcedataSource){returnDistributedStore.builder()// Agent 会话状态走 MySQL自动建表.agentStateStore(newMysqlAgentStateStore(dataSource,true))// 工作区 KV 走 MySQL表由 schema.sql 预建避免 utf8mb4 主键超长.baseStore(JdbcStore.builder(dataSource).initializeSchema(false).build()).build();}BeanpublicHarnessAgentharnessAgent(Modelmodel,DistributedStorestore){returnHarnessAgent.builder().name(jdbc-demo).model(model).workspace(Paths.get(.agentscope/workspace)).distributedStore(store).filesystem(newRemoteFilesystemSpec().isolationScope(IsolationScope.USER)).build();}}关键约束RemoteFilesystemSpec要求AgentStateStore也是分布式的。直接用本地默认的JsonFileAgentStateStore会在build()时抛IllegalStateException。必须通过DistributedStore.builder()同时提供agentStateStorebaseStore再通过.distributedStore(store)注入。3.4 优势无需额外运维组件复用现有MySQL集群不需要引入Redis或其他中间件SQL 查询能力可以直接通过SQL对存储数据进行查询、统计、清理事务支持多个KV操作可在同一事务中原子提交保证一致性成熟生态连接池HikariCP、监控Druid、分库分表工具链完善合规友好政企环境通常已有MySQL运维规范接入成本低4. OSS 模式4.1 适用场景技能包附带大体积脚本/模型文件数十MB级别会话日志长期归档合规留存要求180天以上多Region部署需要跨地域共享工作区数据需要低存储成本的海量数据场景4.2 配置案例BeanpublicDistributedStoredistributedStore(){returnOssDistributedStore.create().endpoint(oss-cn-hangzhou.aliyuncs.com).accessKeyId(System.getenv(OSS_ACCESS_KEY_ID)).accessKeySecret(System.getenv(OSS_ACCESS_KEY_SECRET)).bucketName(agentscope-workspace).basePath(prod/)// 可选统一路径前缀.build();}BeanpublicHarnessAgentharnessAgent(Modelmodel,DistributedStorestore){returnHarnessAgent.builder().name(oss-demo).model(model).workspace(Paths.get(.agentscope/workspace)).distributedStore(store).filesystem(newRemoteFilesystemSpec().isolationScope(IsolationScope.USER)).build();}4.3 优势低成本海量存储对象存储的GB/月成本远低于数据库和Redis大文件友好技能包的脚本、模型文件直接走对象存储不受KV大小限制自动扩展无需关心容量规划OSS按需分配跨 Region 同步OSS的跨区域复制能力天然支持多Region部署生命周期管理OSS的自动归档策略可直接用于过期会话日志清理5. Redis 模式5.1 适用场景高并发读写场景数百QPS以上需要低延迟1ms的实时会话多副本共享工作区且对一致性要求不高作为MySQL/OSS的热数据缓存层5.2 配置案例BeanpublicJedisPooljedisPool(){JedisPoolConfigconfignewJedisPoolConfig();config.setMaxTotal(50);config.setMaxIdle(10);returnnewJedisPool(config,localhost,6379);}BeanpublicDistributedStoredistributedStore(JedisPooljedisPool){returnRedisDistributedStore.builder().jedisPool(jedisPool).keyPrefix(agentscope:)// 可选Redis Key 前缀.build();}BeanpublicHarnessAgentharnessAgent(Modelmodel,DistributedStorestore){returnHarnessAgent.builder().name(redis-demo).model(model).workspace(Paths.get(.agentscope/workspace)).distributedStore(store).filesystem(newRemoteFilesystemSpec().isolationScope(IsolationScope.USER)).build();}5.3 优势极低延迟内存操作单次读写 1ms高吞吐单机可支持数万QPS适合高并发Agent服务数据结构丰富Hash/List/Sorted Set可用于优化搜索和索引集群支持Redis Cluster天然支持水平扩展6. 混合模式可以通过DistributedStore.builder()组合不同存储后端各取所长BeanpublicDistributedStoremixedStore(DataSourcedataSource,JedisPooljedisPool){DistributedStoremysqlMysqlDistributedStore.create(dataSource);DistributedStoreredisRedisDistributedStore.builder().jedisPool(jedisPool).build();returnDistributedStore.builder().agentStateStore(mysql.agentStateStore())// AgentState 用 MySQL — 需要持久性.baseStore(redis.baseStore())// 工作区文件用 Redis — 需要低延迟.sandboxExecutionGuard(redis.sandboxExecutionGuard())// 沙箱锁用 Redis.build();}7. 自定义 BaseStore 实现如果以上三种后端都不满足需求可以实现BaseStore接口接入任意存储publicclassPostgresStoreimplementsBaseStore{privatefinalJdbcTemplatejdbc;publicPostgresStore(DataSourcedataSource){this.jdbcnewJdbcTemplate(dataSource);}OverridepublicStoreItemget(ListStringnamespace,Stringkey){StringnsString.join(/,namespace);returnjdbc.queryForObject(SELECT item_key, value_json, version FROM agentscope_store WHERE namespace_path ? AND item_key ?,(rs,i)-newStoreItem(rs.getString(item_key),parseJson(rs.getString(value_json)),rs.getLong(version)),ns,key);}Overridepublicvoidput(ListStringnamespace,Stringkey,MapString,Objectvalue){StringnsString.join(/,namespace);StringjsonnewObjectMapper().writeValueAsString(value);// UPDATE first, INSERT if not existintupdatedjdbc.update(UPDATE agentscope_store SET value_json ?, version version 1 WHERE namespace_path ? AND item_key ?,json,ns,key);if(updated0){jdbc.update(INSERT INTO agentscope_store (namespace_path, item_key, value_json, version) VALUES (?, ?, ?, 1),ns,key,json);}}OverridepublicListStoreItemsearch(ListStringnamespace,intlimit,intoffset){StringnsString.join(/,namespace);returnjdbc.query(SELECT item_key, value_json, version FROM agentscope_store WHERE namespace_path LIKE ? ORDER BY namespace_path, item_key LIMIT ? OFFSET ?,(rs,i)-newStoreItem(rs.getString(item_key),parseJson(rs.getString(value_json)),rs.getLong(version)),ns%,limit,offset);}Overridepublicvoiddelete(ListStringnamespace,Stringkey){StringnsString.join(/,namespace);jdbc.update(DELETE FROM agentscope_store WHERE namespace_path ? AND item_key ?,ns,key);}}8. 三种模式对比维度RedisJDBC (MySQL)OSS读写延迟 1ms1-5ms10-50ms单 Key 大小限制512MB受 JSON 列限制~1GB无限制查询能力仅 Key 前缀扫描完整 SQL仅 List 前缀事务有限Lua 脚本完整 ACID最终一致运维成本需独立 Redis 集群可复用现有 DB云服务免运维存储成本高内存中磁盘低对象存储适合的文件类型小文本、配置结构化数据、JSON大文件、归档多 Region需自行同步需自行主从复制原生跨区域同步