refactor(agent): rename WORKSPACE_FILE to LOCAL_FILE for memory storage mode

该模式的 .agent-session 目录实际位于 workspace.root 之下、与各 session
的 workspace 子目录平级而非嵌套,旧名 WORKSPACE_FILE 容易误导为"存储在
某个 workspace 内部"。LOCAL_FILE 与 REDIS、MYSQL 同层级地描述后端类型,
更准确地反映实际行为。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
everywhere.z
2026-05-07 00:11:21 +08:00
parent 7fe12ed624
commit c31e279560
9 changed files with 55 additions and 47 deletions

View File

@@ -467,7 +467,7 @@ Session ID 只允许直接使用 `[a-zA-Z0-9_-]+`。其他字符会被 URL 编
| --- | --- | --- |
| `JVM` | 默认值,使用 AgentScope 的 JVM 内 Session | 单进程内多轮对话,进程退出后可丢失 |
| `NONE` | 不加载也不保存持久化 Session | 不需要持久化;如需严格无状态,配合唯一 `sessionId` 使用 |
| `WORKSPACE_FILE` | 保存到 `workspace.root/.agent-session/<sessionId>/` | 本地文件持久化、开发测试、小规模部署 |
| `LOCAL_FILE` | 保存到 `workspace.root/.agent-session/<sessionId>/` | 本地文件持久化、开发测试、小规模部署 |
| `REDIS` | 使用用户提供的 Redis 客户端 Bean | 多实例共享短期会话 |
| `MYSQL` | 使用用户提供的 `DataSource` Bean | 需要数据库持久化和运维治理 |
@@ -479,10 +479,10 @@ Session ID 只允许直接使用 `[a-zA-Z0-9_-]+`。其他字符会被 URL 编
| `save-after-call` | `true` | 调用成功后保存记忆 |
| `save-on-error` | `true` | 调用抛错时也尝试保存记忆 |
`WORKSPACE_FILE` 示例:
`LOCAL_FILE` 示例:
```properties
liteflow.agent.session.memory.mode=WORKSPACE_FILE
liteflow.agent.session.memory.mode=LOCAL_FILE
liteflow.agent.session.memory.load-on-first-use=true
liteflow.agent.session.memory.save-after-call=true
liteflow.agent.session.memory.save-on-error=true
@@ -526,7 +526,7 @@ LiteFlow 不创建 JDBC 连接池,`DataSource` 也需要由业务应用提供
└── .agent-session/
```
`<sessionId>/` 是 Agent 工具读写文件的目录;`.agent-session/` 只在 `session.memory.mode=WORKSPACE_FILE` 时创建,用于保存 AgentScope 的记忆文件。
`<sessionId>/` 是 Agent 工具读写文件的目录;`.agent-session/` 只在 `session.memory.mode=LOCAL_FILE` 时创建,用于保存 AgentScope 的记忆文件。
### 6.2 Workspace 配置
@@ -769,7 +769,7 @@ liteflow.agent.anthropic-compatible.gateway.base-url=https://anthropic-gateway.e
| `workspace root does not exist` | `auto-create=false` 且目录不存在 | 提前创建目录,或开启 `auto-create` |
| `Missing API key: please configure liteflow.agent.openai.api-key` | 对应平台凭据未配置 | 配置对应平台的 `api-key` |
| 多轮对话没有记忆 | 默认 Session ID 由 `NanoIdSessionIdGenerator` 每次随机生成 | 覆写 `resolveSessionId`,返回稳定的业务会话 ID |
| 重启后没有历史记忆 | `session.memory.mode=JVM``NONE` | 改为 `WORKSPACE_FILE``REDIS``MYSQL` |
| 重启后没有历史记忆 | `session.memory.mode=JVM``NONE` | 改为 `LOCAL_FILE``REDIS``MYSQL` |
| Redis 模式启动失败 | 未配置 Bean 名,或 Bean 类型与 `client-type` 不匹配 | 检查 `bean-name``client-type` 和 classpath 依赖 |
| MySQL 模式启动失败 | 未配置 `DataSource` Bean或 Bean 类型错误 | 配置正确的 `data-source-bean-name` |
| Shell 返回 `command 'xxx' not allowed by whitelist` | 白名单模式下命令未放行 | 加入白名单,或继续保持禁用 |

View File

@@ -0,0 +1,23 @@
package com.yomahub.liteflow.property.agent;
/**
* 仅在 {@link MemoryStorageMode#LOCAL_FILE} 模式下生效的配置项,
* 对应配置段 {@code liteflow.agent.session.memory.local-file.*}。
*
* <p>该模式下记忆持久化会落盘到 {@code workspace.root} 之下的
* {@link #SUB_DIR} 子目录中,按 {@code sessionId} 再分一层;该目录与
* 各 session 自己的 workspace{@code workspace.root/<sessionId>/})平级而不嵌套,
* 这样可以避免内置 {@code WorkspaceFileTools}
* 读到或覆盖 agent 自己的记忆,也让 {@code cleanup-on-session-expire}
* 在清空 workspace 子目录时不会误删持久化的记忆。
*
* <p>该位置故意硬编码、不暴露为可配置项:保证不同会话之间结构一致,
* 且与 Redis、MySQL 等远程后端的"持久化与 workspace 生命周期解耦"语义对齐。
* 需要自定义存储位置的用户应通过 SPI 注入自己的
* {@code AgentSessionFactory} 实现。
*/
public class LocalFileMemoryConfig {
/** 会话 JSON 文件存放的固定子目录名,位于 {@code workspace.root} 之下。 */
public static final String SUB_DIR = ".agent-session";
}

View File

@@ -6,7 +6,7 @@ package com.yomahub.liteflow.property.agent;
*
* <p>本配置与 {@link SessionConfig} 是正交的关注点:
* {@link SessionConfig} 控制 JVM 内 agent 实例的缓存、空闲超时与 LRU 淘汰;
* 而本配置决定 agent 的对话历史 <em>持久化到哪里</em>,例如 JVM 堆内、工作区文件、
* 而本配置决定 agent 的对话历史 <em>持久化到哪里</em>,例如 JVM 堆内、本地文件、
* Redis 或 MySQL。
*/
public class MemoryStorageConfig {
@@ -19,8 +19,8 @@ public class MemoryStorageConfig {
*/
private MemoryStorageMode mode = MemoryStorageMode.JVM;
/** {@link MemoryStorageMode#WORKSPACE_FILE} 模式下生效的子配置。 */
private WorkspaceMemoryConfig workspace = new WorkspaceMemoryConfig();
/** {@link MemoryStorageMode#LOCAL_FILE} 模式下生效的子配置。 */
private LocalFileMemoryConfig localFile = new LocalFileMemoryConfig();
/** {@link MemoryStorageMode#REDIS} 模式下生效的子配置。 */
private RedisMemoryConfig redis = new RedisMemoryConfig();
@@ -60,12 +60,12 @@ public class MemoryStorageConfig {
this.mode = mode;
}
public WorkspaceMemoryConfig getWorkspace() {
return workspace;
public LocalFileMemoryConfig getLocalFile() {
return localFile;
}
public void setWorkspace(WorkspaceMemoryConfig workspace) {
this.workspace = workspace;
public void setLocalFile(LocalFileMemoryConfig localFile) {
this.localFile = localFile;
}
public RedisMemoryConfig getRedis() {

View File

@@ -6,8 +6,8 @@ package com.yomahub.liteflow.property.agent;
* <ul>
* <li>{@link #NONE} 不保留也不持久化任何记忆,等价于无状态 agent</li>
* <li>{@link #JVM} 仅保留在 JVM 堆内(默认行为,与 2.15.4 之前版本一致)</li>
* <li>{@link #WORKSPACE_FILE} 通过 AgentScope 的 JsonSession把记忆以 JSON 文件
* 形式持久化到每个会话工作区目录下</li>
* <li>{@link #LOCAL_FILE} 通过 AgentScope 的 JsonSession把记忆以 JSON 文件
* 形式持久化到本地磁盘(与各 session 的 workspace 子目录平级)</li>
* <li>{@link #REDIS} 通过 AgentScope 的 RedisSession 持久化,
* 需要用户提供 {@code RedissonClient} / {@code UnifiedJedis} / {@code RedisClient} bean</li>
* <li>{@link #MYSQL} 通过 AgentScope 的 MysqlSession 持久化,
@@ -22,8 +22,8 @@ public enum MemoryStorageMode {
/** 仅 JVM 堆内缓存,进程重启即丢失;为默认值。 */
JVM,
/** 工作区文件持久化,每个会话一 JSON 文件,便于排查与离线分析。 */
WORKSPACE_FILE,
/** 本地文件持久化,每个会话一 JSON 文件,便于排查与离线分析。 */
LOCAL_FILE,
/** Redis 持久化,适合多实例部署、高并发场景。 */
REDIS,

View File

@@ -1,16 +0,0 @@
package com.yomahub.liteflow.property.agent;
/**
* 仅在 {@link MemoryStorageMode#WORKSPACE_FILE} 模式下生效的配置项,
* 对应配置段 {@code liteflow.agent.session.memory.workspace.*}。
*
* <p>该模式下记忆持久化会落盘到每个会话工作区目录下的固定子目录({@link #SUB_DIR}
* 该位置故意硬编码、不暴露为可配置项:一方面避免与工具产生的业务文件混在一起,
* 另一方面保证不同会话之间结构一致。需要自定义存储位置的用户应通过 SPI
* 注入自己的 {@code AgentSessionFactory} 实现。
*/
public class WorkspaceMemoryConfig {
/** 会话 JSON 文件存放的固定子目录名(位于工作区根目录下)。 */
public static final String SUB_DIR = ".agent-session";
}

View File

@@ -7,7 +7,7 @@ import io.agentscope.core.session.Session;
/**
* 用于为 {@link io.agentscope.core.session.Session} 接入额外持久化后端的 SPI。
*
* <p>框架内置模式({@code JVM}、{@code WORKSPACE_FILE}、{@code REDIS}、
* <p>框架内置模式({@code JVM}、{@code LOCAL_FILE}、{@code REDIS}、
* {@code MYSQL}、{@code NONE})。需要其他后端(例如 PostgreSQL、OSS、
* 加密 JSON的用户可以在 {@code META-INF/services/}{@link AgentSessionFactory}
* 下注册自定义工厂。

View File

@@ -15,10 +15,10 @@ import java.util.ServiceLoader;
* <p>解析顺序:
* <ol>
* <li>通过 {@link ServiceLoader} 注册的外部工厂</li>
* <li>框架内置工厂JVM、workspace、Redis、MySQL、none</li>
* <li>框架内置工厂JVM、local-file、Redis、MySQL、none</li>
* </ol>
* 出现冲突时外部工厂优先,因此用户可以覆盖内置实现
* (例如用自定义加密 JSON 工厂替换默认 workspace 工厂)。
* (例如用自定义加密 JSON 工厂替换默认本地文件工厂)。
*/
public final class AgentSessionFactoryRegistry {
@@ -27,7 +27,7 @@ public final class AgentSessionFactoryRegistry {
static {
// 先注册内置实现;如果存在 SPI 实现,再由 SPI 覆盖。
register(new InMemoryAgentSessionFactory());
register(new WorkspaceAgentSessionFactory());
register(new LocalFileAgentSessionFactory());
register(new RedisAgentSessionFactory());
register(new MysqlAgentSessionFactory());
register(new NoneAgentSessionFactory());

View File

@@ -10,7 +10,7 @@ import io.agentscope.core.session.Session;
*
* <p>注意:状态仍会在同一个 JVM 内跨调用保留(适合希望在单进程内保留多轮记忆的场景),
* 但进程退出后会丢失。如果需要跨重启持久化,请选择
* {@code WORKSPACE_FILE}、{@code REDIS} 或 {@code MYSQL}。
* {@code LOCAL_FILE}、{@code REDIS} 或 {@code MYSQL}。
*/
public class InMemoryAgentSessionFactory implements AgentSessionFactory {

View File

@@ -2,8 +2,8 @@ package com.yomahub.liteflow.agent.session.factory;
import com.yomahub.liteflow.agent.exception.AgentConfigException;
import com.yomahub.liteflow.property.agent.AgentConfig;
import com.yomahub.liteflow.property.agent.LocalFileMemoryConfig;
import com.yomahub.liteflow.property.agent.MemoryStorageMode;
import com.yomahub.liteflow.property.agent.WorkspaceMemoryConfig;
import io.agentscope.core.session.JsonSession;
import io.agentscope.core.session.Session;
@@ -14,28 +14,29 @@ import java.nio.file.Paths;
/**
* 通过把 JSON 文件存储在 {@code workspace.root/.agent-session/<sessionId>/}
* 下来支持 {@link MemoryStorageMode#WORKSPACE_FILE}
* 下来支持 {@link MemoryStorageMode#LOCAL_FILE}
*
* <p>session 存储子目录特意与 {@code workspace.root/<sessionId>/}
* 形式的单 session 工具 workspace 分离避免
* {@link com.yomahub.liteflow.agent.tool.WorkspaceFileTools} 读取或覆盖
* agent 自身记忆
* <p>session 存储子目录与各 session workspace{@code workspace.root/<sessionId>/}
* 平级而非嵌套一方面避免 {@link com.yomahub.liteflow.agent.tool.WorkspaceFileTools}
* 读到或覆盖 agent 自己的记忆另一方面让 {@code cleanup-on-session-expire}
* 在递归清空 workspace 子目录时不会误删持久化的记忆
* RedisMySQL 后端的"持久化与 workspace 生命周期解耦"语义保持一致
*/
public class WorkspaceAgentSessionFactory implements AgentSessionFactory {
public class LocalFileAgentSessionFactory implements AgentSessionFactory {
@Override
public MemoryStorageMode mode() {
return MemoryStorageMode.WORKSPACE_FILE;
return MemoryStorageMode.LOCAL_FILE;
}
@Override
public Session create(AgentConfig cfg) {
if (cfg.getWorkspace() == null || cfg.getWorkspace().getRoot() == null) {
throw new AgentConfigException(
"liteflow.agent.workspace.root is required when session.memory.mode=WORKSPACE_FILE");
"liteflow.agent.workspace.root is required when session.memory.mode=LOCAL_FILE");
}
Path root = Paths.get(cfg.getWorkspace().getRoot()).toAbsolutePath().normalize()
.resolve(WorkspaceMemoryConfig.SUB_DIR);
.resolve(LocalFileMemoryConfig.SUB_DIR);
try {
Files.createDirectories(root);
} catch (IOException e) {