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

5.2 KiB
Raw Permalink Blame History

Bug 修复 v3 - 刷新后重复加载问题

修复的问题

刷新后加载两遍内容

原因:

  1. loadInitialData() 会调用 fetchAndDisplayHistory() 加载历史记录
  2. 1 秒后 restoreTaskState() 启动轮询
  3. loadRunningTask() 设置 lastEventIndex = 0,从头开始获取所有事件
  4. 导致已经在历史中的事件被重复处理

解决方案:

1. 等待历史加载完成

restoreTaskState() 中检查历史是否已加载:

async restoreTaskState() {
    // 检查历史是否已加载
    const hasMessages = Array.isArray(this.messages) && this.messages.length > 0;

    if (!hasMessages) {
        // 等待历史加载完成后再恢复
        setTimeout(() => {
            this.restoreTaskState();
        }, 500);
        return;
    }

    // 历史已加载,启动轮询
    taskStore.startPolling(...);
}

2. 计算正确的事件偏移量

loadRunningTask() 中获取任务详情,计算已处理的事件数量:

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_offsetevents.length
  • 设置 lastEventIndex 为当前事件数量
  • 轮询只获取新事件(from=lastEventIndex

3. 避免重复处理

  • 历史记录由 fetchAndDisplayHistory() 加载
  • 轮询只处理新事件
  • 不会重复显示已有内容

修改的文件

1. static/src/app/methods/taskPolling.ts

等待历史加载:

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

计算事件偏移量:

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. 多次刷新

  • 任务执行中多次刷新
  • 观察每次刷新是否正常
  • 观察是否有累积的重复内容

构建

cd static
npm run build

构建成功!

  • static/dist/assets/main.js (690.04 kB)
  • static/dist/assets/task.js (4.01 kB)

最终效果

  • 刷新后不重复显示内容
  • 历史记录正确加载
  • 新事件正常追加
  • 工具块正确显示
  • 流畅的输出效果150ms 轮询)

现在可以测试了!刷新页面应该只显示一次内容,新内容会正常追加。