agent-Specialization/BUG_FIX_V3_CHANGELOG.md
JOJO 07be7a1061 feat: gracefully stop tool execution on user request
- Remove direct task.cancel() calls, use stop flag instead
- Monitor stop flag every 100ms during tool execution
- Cancel tool task immediately when stop flag is detected
- Return "命令执行被用户取消" as tool result with role=tool
- Save cancellation result to conversation history
- Prevent abrupt task termination, allow graceful shutdown

Changes:
- server/socket_handlers.py: Comment out pending_task.cancel()
- server/tasks.py: Comment out entry['task'].cancel()
- server/chat_flow_tool_loop.py: Add stop flag monitoring loop

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-08 03:50:34 +08:00

188 lines
5.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Bug 修复 v3 - 刷新后重复加载问题
## 修复的问题
### 刷新后加载两遍内容
**原因**:
1. `loadInitialData()` 会调用 `fetchAndDisplayHistory()` 加载历史记录
2. 1 秒后 `restoreTaskState()` 启动轮询
3. `loadRunningTask()` 设置 `lastEventIndex = 0`,从头开始获取所有事件
4. 导致已经在历史中的事件被重复处理
**解决方案**:
#### 1. 等待历史加载完成
`restoreTaskState()` 中检查历史是否已加载:
```javascript
async restoreTaskState() {
// 检查历史是否已加载
const hasMessages = Array.isArray(this.messages) && this.messages.length > 0;
if (!hasMessages) {
// 等待历史加载完成后再恢复
setTimeout(() => {
this.restoreTaskState();
}, 500);
return;
}
// 历史已加载,启动轮询
taskStore.startPolling(...);
}
```
#### 2. 计算正确的事件偏移量
`loadRunningTask()` 中获取任务详情,计算已处理的事件数量:
```javascript
async loadRunningTask(conversationId) {
// 查找运行中的任务
const runningTask = result.data.find(...);
if (runningTask) {
// 获取任务详情,计算已处理的事件数量
const detailResponse = await fetch(`/api/tasks/${runningTask.task_id}`);
const detailResult = await detailResponse.json();
// 设置为当前事件数量,只获取新事件
this.lastEventIndex = detailResult.data.next_offset || detailResult.data.events.length;
debugLog('[Task] 设置起始偏移量:', this.lastEventIndex);
}
}
```
## 工作流程
### 页面刷新恢复流程(修复后)
1. **页面加载** (`mounted()`)
- 调用 `loadInitialData()`
- 加载历史记录(`fetchAndDisplayHistory()`
- 1 秒后调用 `restoreTaskState()`
2. **任务恢复** (`restoreTaskState()`)
- 检查是否已在流式输出中 → 跳过
- 查找运行中的任务
- **检查历史是否已加载** → 如果未加载,等待 500ms 后重试
- 历史已加载 → 继续
3. **加载任务详情** (`loadRunningTask()`)
- 查找运行中的任务
- **获取任务详情**
- **计算已处理的事件数量**
- 设置 `lastEventIndex` 为当前事件数量
4. **启动轮询** (`startPolling()`)
-`lastEventIndex` 开始轮询
- **只获取新事件**
- 每 150ms 轮询一次
5. **处理新事件**
- 只处理新产生的事件
- 追加到已有的历史记录后面
- 不重复显示已有内容
## 关键改进
### 1. 等待历史加载
-`restoreTaskState()` 中检查 `messages` 是否已加载
- 如果未加载,延迟 500ms 后重试
- 确保历史加载完成后再启动轮询
### 2. 正确的事件偏移量
-`loadRunningTask()` 中获取任务详情
- 计算已处理的事件数量(`next_offset` 或 `events.length`
- 设置 `lastEventIndex` 为当前事件数量
- 轮询只获取新事件(`from=lastEventIndex`
### 3. 避免重复处理
- 历史记录由 `fetchAndDisplayHistory()` 加载
- 轮询只处理新事件
- 不会重复显示已有内容
## 修改的文件
### 1. static/src/app/methods/taskPolling.ts
**等待历史加载**:
```javascript
async restoreTaskState() {
// 检查历史是否已加载
const hasMessages = Array.isArray(this.messages) && this.messages.length > 0;
if (!hasMessages) {
debugLog('[TaskPolling] 历史未加载,等待历史加载完成');
setTimeout(() => {
this.restoreTaskState();
}, 500);
return;
}
debugLog('[TaskPolling] 历史已加载,启动轮询');
// ... 启动轮询
}
```
### 2. static/src/stores/task.ts
**计算事件偏移量**:
```javascript
async loadRunningTask(conversationId) {
const runningTask = result.data.find(...);
if (runningTask) {
// 获取任务详情
const detailResponse = await fetch(`/api/tasks/${runningTask.task_id}`);
const detailResult = await detailResponse.json();
// 设置为当前事件数量
this.lastEventIndex = detailResult.data.next_offset || detailResult.data.events.length;
debugLog('[Task] 设置起始偏移量:', this.lastEventIndex);
}
}
```
## 测试场景
### 1. 正常发送消息
- 发送消息
- 观察输出是否正常
- 观察是否只显示一次
### 2. 刷新页面(任务进行中)
- 任务执行中刷新页面
- 观察历史记录是否正确显示
- 观察是否只显示一次
- 观察新内容是否正常追加
- 观察工具块是否保留
### 3. 刷新页面(任务完成后)
- 任务完成后刷新页面
- 观察历史记录是否完整
- 观察是否没有重复内容
### 4. 多次刷新
- 任务执行中多次刷新
- 观察每次刷新是否正常
- 观察是否有累积的重复内容
## 构建
```bash
cd static
npm run build
```
构建成功!
- `static/dist/assets/main.js` (690.04 kB)
- `static/dist/assets/task.js` (4.01 kB)
## 最终效果
- ✅ 刷新后不重复显示内容
- ✅ 历史记录正确加载
- ✅ 新事件正常追加
- ✅ 工具块正确显示
- ✅ 流畅的输出效果150ms 轮询)
现在可以测试了!刷新页面应该只显示一次内容,新内容会正常追加。