agentscope-ai / agentscope-ai/QwenPaw

[Bug]: Console 会话/消息层受限于 @agentscope-ai/chat SDK 的“单会话 pull”模型,阻塞多 Agent / 多工作空间演进

Đang mở
#5,767 2 bình luận 1 reaction 1 người được giao Được @zhijianma nhận Xem trên GitHub
bug
Ngôn ngữ chính
Python
Star
34.9k
Fork
3.1k
Merge trung bình
1 ngày 15 giờ
Pull request đã merge (30 ngày)
225

Mô tả

# [Architecture] Console 会话/消息层受限于 @agentscope-ai/chat SDK 的“单会话 pull”模型,阻塞多 Agent / 多工作空间演进

## QwenPaw Version

- **QwenPaw:** `1.1.12`
- **Console SDK:** `@agentscope-ai/chat@^1.1.71`(见 `console/package.json`)
- **SDK 源:** `agentscope-ai/agentscope-runtime`(spark-design),文件
`AgentScopeRuntimeWebUI/core/Context/ChatAnywhereSessionsContext.tsx`

## Description

QwenPaw Console 的「会话切换 + 历史消息渲染」这一层完全由 vendored 的
`@agentscope-ai/chat` SDK 驱动。该 SDK 的会话模型在概念上仍停留在
**“单个活跃会话的聊天控件”**,而不是 **“多会话并发运行的 Agent IDE”**。

这带来两类问题:

1. **可复现的体验缺陷** —— 切换会话白屏 / 闪回;切走正在生成的会话再切回,历史与
流式状态易丢失或错乱。
2. **结构性演进阻塞** —— 会话对象没有 agent / workspace 维度、没有并发会话模型、没有
push/订阅与事件游标,导致 QwenPaw 想做的多 Agent、多工作空间、后台并发运行等能力
无法在数据模型层自然表达,只能在上层反复打补丁。

根因集中在 SDK 的会话加载器:

```tsx
// ChatAnywhereSessionsContext.tsx

// (1) 进入即硬编码自动选中列表第一个会话;无“不选中 / 恢复上次 / 深链”概念
useMount(async () => {
const sessionList = await options.api.getSessionList();
setSessions(sessionList);
setCurrentSessionId(sessionList?.[0]?.id); // ← sessions[0] 硬编码
});

// (2) 每次切会话都“同步清空 → 再整份异步拉取”
export const useChatAnywhereSessionLoader = () => {
useAsyncEffect(async () => {
ReactDOM.flushSync(() => { setMessages([]); }); // ← 同步清空 = 白屏空窗
const session = await options.api.getSession(currentSessionId); // ← pull 整份
setMessages((session?.messages || []).map(m => ({ ...m, history: true })));
if (session?.generating) emit({ type: 'handleReconnect', ... });
}, [currentSessionId]); // ← 每次切换整体重跑
};

// (3) 会话对象只有 { id, name, messages } —— 无 agent_id / workspace_path
// 见 types/ISessions 的 IAgentScopeRuntimeWebUISession
```

### 五个结构性缺陷(均可在上面源码中定位)

| # | 缺陷 | 代码依据 | 后果 |
|---|------|---------|------|
| L1 | **Pull-only,无 push/subscribe** | 对外仅 `options.api.getSession(id)` 返回整份会话 | 离屏/后台生成中的会话无法把增量推进视图 |
| L2 | **切换即清空再重取** | `flushSync(setMessages([]))` → `await getSession` | 清空与返回之间必有空窗 → 白屏 / 闪回上一会话或 sessions[0] |
| L3 | **单会话渲染** | 单一 messages 上下文 + 单值 `currentSessionId` | “多会话并发运行”在模型层不存在,切走的运行态无一等表示 |
| L4 | **无 agent/workspace 维度** | `IAgentScopeRuntimeWebUISession = {id,name,messages}` | 多 Agent / 多工作空间隔离只能在 SDK 之外硬拼 |
| L5 | **无 seq / 无游标 / 无事件模型** | `getSession` 一次性全量返回 | 流式与持久化两条路不对账;断线不能按序重放 |

## Related PR(s)

(无)

## Security considerations

无直接安全影响,属架构 / 体验缺陷。

## Component(s) Affected

- [x] Core / Backend(会话历史读取与持久化契约需配合演进)
- [x] Console (frontend web UI)(SDK 会话层,根因所在)
- [ ] Channels
- [ ] Skills
- [ ] CLI
- [ ] Documentation
- [ ] Tests
- [ ] CI/CD
- [ ] Scripts / Deploy

## Environment

- **QwenPaw version:**
- **Console SDK:** `@agentscope-ai/chat@^1.1.71`
- **OS:** 与平台无关(Windows / macOS / Linux 均可复现)
- **Install method:** from source(后端 `app` + 前端 `npm run dev`)
- **Browser:** 任意 Chromium / Firefox

## Steps to Reproduce

1. 创建 3 个会话,每个各发 ≥5 轮问答,使其中 ≥2 个处于“正在生成”状态。
2. 在这 3 个会话之间随机快速来回切换 10~20 次。
3. 观察每次切换瞬间的消息区,以及切回“正在生成”会话后的历史 + 流式状态。

## Actual vs Expected

**Actual**
- 每次切换先清空、再异步取数 → 白屏 / 一闪而过的上一个会话内容。
- 切走一个正在生成的会话再切回,历史/流式状态依赖单次 `getSession` 重取,
容易历史不全、流式中断态丢失。
- 会话之间没有 agent / workspace 维度,同一入口下不同 Agent、不同工作空间的会话
无法在模型层天然区分。

**Expected**
- 切换会话应“同步命中内存中该会话状态并立即渲染”,无空窗、无闪回。
- 多个会话可并发运行;切走的会话继续在后台推进且状态可见,切回即完整。
- 会话身份自带 agent / workspace 维度,隔离由数据结构保证,而非上层拼接。

## Logs / Screenshots

体验类问题,建议录屏展示切换白屏 / 闪回。无异常堆栈(非崩溃类 bug)。

## Additional Notes — 概念对照与建议演进方向

同类现代 Agent IDE(opencode)在协议/SDK 层已采用 **事件溯源 + 投影 + 会话所有权**
模型,正是 `@agentscope-ai/chat` 当前缺失、也是 QwenPaw 多 Agent/多工作空间最需要的:

**A. 消息是有类型的事件流,而非一坨 messages**
opencode `packages/schema/src/session-message.ts`:消息是 tagged union
(`User / Assistant / AgentSwitched / ModelSwitched / Compaction / Shell / System`),
`Assistant.content` 是 `text | reasoning | tool` parts,`tool` 带
`pending/running/completed/error` 状态机,消息 id 用单调可排序的 `msg_`。
→ `AgentSwitched` / `ModelSwitched` 是一等事件,**多 Agent、多模型天生可表达**;
反观 agentscope SDK 只有 `{id,name,messages}`。

**B. 持久事件带 (aggregateID, seq, version),可按序重放**
opencode `packages/schema/src/event.ts`:

```ts
durable?: { aggregateID: string; seq: number; version: number }
```

→ 事件按 `(会话, seq)` 有序 append-only、可版本化、可游标重放(另见 `session-cursor`、
`event-manifest`、`effect-drizzle-sqlite/.../session` 的 SQLite 投影)。
支持“断线不丢、重连按 seq 补拉、离屏会话直接收事件”,`getSession` 全量拉取做不到。

**C. 会话所有权围栏,并发切换安全“由构造保证”**
opencode `packages/app/.../session-ownership`(含 test)验证:
“导航到 B 后不再运行 A 捕获的续作”“A→B→A 不复活旧续作”。
→ 这是“切走正在生成的会话再切回不串、不错乱”的关键原语;
pull + `flushSync([])` 模型无法做到,只能靠上层补偿。

### 建议(按成本递增,可分阶段)

1. **短期**:为 SDK 增加 push/subscribe,提供“切换同步取数、不清空”的渲染路径;
去掉 `sessions[0]` 硬编码自动选中,支持“不选中 / 恢复上次 / 深链”。
2. **中期**:在会话对象上引入 `agent_id` / `workspace_path` 一等维度,让隔离进入数据模型。
3. **长期**:把会话历史真源从“整份 messages 快照 + pull”迁到“事件日志
(按 `(session_id, seq)` 有序、append-only)+ 投影”,对齐 opencode 的事件溯源模型,
从架构上消除切换白屏、并发串话与多 Agent 表达缺失。

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.