agentscope-ai / agentscope-ai/agentscope-java

[Bug]: 当前 main 默认 ObjectStoreTranscriptStore 的 segment key 不匹配 RemoteFilesystemSpec 路由,session 历史无法写入远程/MySQL

未關閉
#2,918 3 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視
bug
主要語言
Java
星號
5.6k
分支
1.3k
平均合併
4 天 12 小時
30 天內合併 PR
77

描述

## 环境

| | |
|---|---|
| agentscope-harness | `v2.0.2-95-gb4361103`(当前 main) |
| store | `agentscope-extensions-mysql`(`JdbcStore` → MySQL 8.0) |
| filesystem | `RemoteFilesystemSpec` with `IsolationScope.USER` |
| 场景 | 非 sandbox,CompositeFilesystem + 远程 store |

## 问题

在 v2.0.2 中,session JSONL 会走 `MemoryFlushMiddleware → offloadMessages → SessionTree.scheduleMirror()`,最终通过 `RemoteFilesystem.uploadFiles()` 写入 MySQL 的 `agentscope_store` 表。

当前 `main` 分支引入了 `TranscriptStore` 体系,默认使用 `ObjectStoreTranscriptStore`:

```java
// HarnessAgent.java ~L2410
if (effectiveTranscriptStore == null) {
if (wsManager.getFilesystem() != null) {
effectiveTranscriptStore = new ObjectStoreTranscriptStore(wsManager.getFilesystem());
} else {
effectiveTranscriptStore = new FilesystemTranscriptStore(...);
}
}
```

`ObjectStoreTranscriptStore.appendSegment()` 生成的 key 为:

```text
default/{agentId}/{sessionId}/events/{seqStart}-{seqEnd}-{writerId}.jsonl
```

而 `RemoteFilesystemSpec` 注册的路由是:

```java
routes.put("agents/" + effectiveAgentId + "/sessions/", overlayRoute(...));
```

`default/{agentId}/{sessionId}/...` 与 `agents/{agentId}/sessions/...` 不匹配,因此 `CompositeFilesystem.routeForPath()` 会 fallback 到 `defaultBackend`(`LocalFilesystem`),session segment 只写本地 workspace,**不进 `RemoteFilesystem`,不进 MySQL**。

## 复现

```java
HarnessAgent.builder()
.agentId("agent-qa")
.name("agent-qa")
.distributedStore(new MysqlDistributedStore(dataSource))
.filesystem(new RemoteFilesystemSpec())
.build();
```

运行一次完整对话后:

```sql
SELECT * FROM agentscope_store WHERE namespace_path LIKE '%sessions%';
-- 预期:有 agents␟agent-qa␟users␟...␟sessions␟ 前缀的记录
-- 实际:没有 session 相关记录
```

本地 workspace 会出现:

```text
workspace//default/agent-qa//events/0-5-xxx.jsonl
```

## 根因

1. `ObjectStoreTranscriptStore` 的 segment key layout 是 `{tenant}/{agentId}/{sessionId}/events/...`,与 `RemoteFilesystemSpec` 的 `{agents}/{agentId}/sessions/` 路由前缀不一致。
2. 即使手动调整 key 前缀去匹配 `agents/{agentId}/sessions/`,`USER` scope 下还带有 `users/{userId}` 层级,而 `TranscriptRef` 中没有 `userId`,难以正确构造命中路由的 key。
3. 框架目前没有内置基于 JDBC/MySQL 的 `TranscriptStore` 实现,用户无法通过 `.transcriptStore(...)` 直接接入已有 DataSource。

## 影响

1. 升级到当前 main 后,原先 v2.0.2 能写入 MySQL 的 session 历史会"静默丢失"到本地盘。
2. 在 K8s / emptyDir / 多副本场景下,本地 workspace 随 Pod 销毁,session 无法跨副本恢复。
3. 用户配了 `RemoteFilesystemSpec + JdbcStore` 后,会误以为 session 已持久化到 MySQL,但实际上没有。

## 建议

任一即可:

1. **官方提供 `JdbcTranscriptStore` / `RdbmsTranscriptStore` 实现**
直接通过 JDBC 同步写入独立表(如 `agentscope_transcript_segments`),不依赖 `RemoteFilesystemSpec` 路由匹配。

2. **调整默认 `ObjectStoreTranscriptStore` 的 key layout 以匹配 `RemoteFilesystemSpec` 路由**
默认生成 `agents/{agentId}/sessions/{sessionId}/events/...` 或兼容 `IsolationScope` 的 key,并确保 `TranscriptMiddleware` 传入的 `RuntimeContext` 携带 `userId` 以支持 USER scope。

3. **至少提供配置开关**
例如 `.transcriptStore(TranscriptStore.legacyMirror())` 或 `.disableTranscriptStore()`,让用户能显式选择行为,避免静默降级到本地盘。

## 相关

- #2297 — `agentName` 与 `agentId` 不一致导致 v2.0.2 session 路由失败
- #2691 — v2.0.2 中 session 写本地副本 + 异步 mirror 的设计缺陷

貢獻指南

開啟貢獻指南

評估

這個 Issue 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。