agentscope-ai / agentscope-ai/agentscope-java

[Bug]: MEMORY.md 创建为default

Aberta
#1,623 0 comentários 0 reações 0 responsáveis Ver no GitHub
area/core/agent area/core/memory bug
Linguagem predominante
Java
Estrelas
5.6k
Forks
1.3k
Merge médio
4d 12h
PRs com merge (30d)
77

Descrição

# Bug: MemoryFlushMiddleware / MemoryMaintenanceMiddleware 使用 RuntimeContext.empty() 写入文件系统,导致所有用户共享同一 namespace(_default)

## 问题表现

使用 `RemoteFilesystemSpec`(`IsolationScope.USER`)+ MySQL 后端时,数据库中出现两类 namespace:

- **sessions**(`SessionTree` 写入):namespace 正确,含真实 userId
```
agents\x1F{agentName}\x1Fusers\x1F{userId}\x1Fsessions\x1F
```
- **memory 文件**(`MemoryFlushMiddleware` / `MemoryMaintenanceMiddleware` 写入):namespace 错误,userId 退化为 `_default`
```
agents\x1F{agentName}\x1Fusers\x1F_default\x1Froot\x1F → MEMORY.md
agents\x1F{agentName}\x1Fusers\x1F_default\x1Fmemory\x1F → YYYY-MM-DD.md, .consolidation_state
```

结果:所有用户的长期记忆(`MEMORY.md`、每日账本)被写入同一 namespace,用户间记忆互相覆盖,且 agent 在自己的 userId namespace 下找不到这些文件。

## 问题原因

`MemoryFlushMiddleware` 和 `MemoryMaintenanceMiddleware` 在 `onAgent()` 入口处立即捕获 `agent.getRuntimeContext()`,此时该值必然为 `null`。

```java
// MemoryFlushMiddleware.onAgent()(MemoryMaintenanceMiddleware 同理)
public Flux onAgent(Agent agent, AgentInput input, Function> next) {
final RuntimeContext rc =
agent instanceof AgentBase ab && ab.getRuntimeContext() != null
? ab.getRuntimeContext()
: RuntimeContext.empty(); // ← 始终走这里,rc 为 empty
return next.apply(input).doOnComplete(() -> doFlush(agent, rc).subscribe());
}
```

**根本原因**:`ReActAgent` 的 RuntimeContext 绑定发生在 `beforeAgentExecution()` 内部(即 Flux 被订阅、`call()` 真正执行时),而 `onAgent()` 是在 middleware chain 组装阶段被调用的,早于 `beforeAgentExecution()`:

```
streamEvents(msgs, rc)
└─ pendingRuntimeContext = rc // 暂存,未绑定
└─ MiddlewareChain.build().apply()
└─ MemoryFlushMiddleware.onAgent()
└─ agent.getRuntimeContext() == null // ← 此处捕获,必为 null
└─ next.apply(input)
└─ core(input) → call(msgs)
└─ beforeAgentExecution()
└─ bindRuntimeContextToHooks(rc) // rc 在这里才真正绑定
```

`SessionTree` 不受影响,因为它实现了 `RuntimeContextAware` 接口,在 `bindRuntimeContextToHooks()` 时通过回调拿到正确的 rc。

## 修复建议

将 rc 的读取从 `onAgent()` 入口移入 `doOnComplete()` 回调内部,在 agent 执行完成后(此时 `currentRuntimeContext` 已绑定)再读取:

```java
// MemoryFlushMiddleware
@Override
public Flux onAgent(Agent agent, AgentInput input, Function> next) {
return next.apply(input).doOnComplete(() -> {
RuntimeContext rc =
agent instanceof AgentBase ab && ab.getRuntimeContext() != null
? ab.getRuntimeContext()
: RuntimeContext.empty();
doFlush(agent, rc).subscribe();
});
}
```

```java
// MemoryMaintenanceMiddleware
@Override
public Flux onAgent(Agent agent, AgentInput input, Function> next) {
return next.apply(input).doOnComplete(() -> {
RuntimeContext rc =
agent instanceof AgentBase ab && ab.getRuntimeContext() != null
? ab.getRuntimeContext()
: RuntimeContext.empty();
maybeRunMaintenance(rc);
});
}
```

`Mono.using` 的 cleanup(`afterAgentExecution` / `unbindRuntimeContextFromHooks`)在 Reactor 中晚于下游 `doOnComplete` 触发,因此在 `doOnComplete` 内读取 `getRuntimeContext()` 时 rc 仍有效。

## 临时绕过方案

调用 `HarnessAgent.Builder.disableMemoryHooks()` 禁用上述两个 middleware,避免脏数据写入。Sessions 和 Compaction 不受影响,代价是长期记忆(MEMORY.md / 每日账本)不再持久化。

```java
HarnessAgent.builder()
...
.disableMemoryHooks()
.build();
```

Guia de contribuição

Abrir o guia de contribuição

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.