# Bug 修复 v2 - 工具块和轮询优化 ## 修复的问题 ### 1. 工具块无法显示 **原因**: 工具事件处理逻辑不完整,缺少关键的工具创建和更新逻辑 **解决方案**: - 完整实现 `handleToolPreparing()` - 创建工具准备块 - 完整实现 `handleToolStart()` - 工具开始执行 - 完整实现 `handleToolUpdateAction()` - 工具状态更新和完成 - 添加 `update_action` 事件处理(工具完成的关键事件) - 在 WebSocket 中跳过 `update_action` 事件 ### 2. 轮询频率太慢(1秒) **原因**: 1秒的轮询间隔无法提供流式输出的体验 **解决方案**: - 将轮询间隔从 1000ms 改为 **150ms** - 接近流式输出的效果(每秒约 6-7 次更新) - 在 `stores/task.ts` 中修改 `startPolling()` 方法 ### 3. 刷新后加载两遍内容 **原因**: - `restoreTaskState()` 没有检查是否已在流式输出中 - `handleTaskComplete()` 会重新加载历史记录 **解决方案**: - 在 `restoreTaskState()` 中添加状态检查,避免重复恢复 - 检查最后一条消息是否是 assistant 消息 - 在 `handleTaskComplete()` 中移除 `fetchAndDisplayHistory()`,只更新统计 ## 修改的文件 ### 1. static/src/app/methods/taskPolling.ts **工具处理逻辑**: ```javascript handleToolPreparing(data) { // 创建工具准备块 const action = { id: data.id, type: 'tool', tool: { status: 'preparing', name: data.name, // ... 完整的工具属性 } }; msg.actions.push(action); this.preparingTools.set(data.id, action); } handleToolStart(data) { // 从 preparing 转为 running let action = this.preparingTools.get(data.preparing_id); action.tool.status = 'running'; action.tool.arguments = data.arguments; // ... 更新工具状态 } handleToolUpdateAction(data) { // 更新工具状态(包括完成) let targetAction = this.toolFindAction(data.id, ...); targetAction.tool.status = data.status; targetAction.tool.result = data.result; // ... 更新工具结果 } ``` **恢复逻辑优化**: ```javascript async restoreTaskState() { // 检查是否已在流式输出中 if (this.streamingMessage || this.taskInProgress) { return; } // 检查是否已有 assistant 消息 const lastMessage = this.messages[this.messages.length - 1]; const hasAssistantMessage = lastMessage && lastMessage.role === 'assistant'; // 只标记状态,不清空消息 this.streamingMessage = true; this.taskInProgress = true; } ``` **任务完成优化**: ```javascript handleTaskComplete(data) { // 只更新统计,不重新加载历史 this.fetchConversationTokenStatistics(); this.updateCurrentContextTokens(); // 移除了 fetchAndDisplayHistory() } ``` ### 2. static/src/stores/task.ts **轮询频率优化**: ```javascript startPolling(eventHandler) { // 150ms 间隔,接近流式输出效果 this.pollingInterval = window.setInterval(() => { this.pollTaskEvents(handler); }, 150); } ``` ### 3. static/src/composables/useLegacySocket.ts **跳过 update_action 事件**: ```javascript ctx.socket.on('update_action', (data) => { if (ctx.usePollingMode) { return; // 跳过 WebSocket 事件 } // ... 原有逻辑 }); ``` ## 事件处理流程 ### 工具执行流程 1. `tool_preparing` → 创建工具准备块 2. `tool_start` → 工具开始执行,更新状态为 running 3. `update_action` → 工具完成,更新状态为 completed,显示结果 ### 轮询流程 1. 每 150ms 轮询一次 2. 获取新事件(通过 `from` 参数) 3. 处理事件,更新界面 4. 任务完成后停止轮询 ### 刷新恢复流程 1. 页面加载完成 2. 延迟 1 秒后调用 `restoreTaskState()` 3. 检查是否已在流式输出中(避免重复) 4. 检查是否已有 assistant 消息(历史已加载) 5. 只标记状态,启动轮询 6. 轮询只处理新事件,不重建历史 ## 性能优化 - **轮询频率**: 150ms(每秒约 6-7 次) - **事件增量获取**: 通过 `from` 参数只获取新事件 - **避免重复加载**: 检查状态和消息,避免重复恢复 - **避免重复显示**: 任务完成后不重新加载历史 ## 测试建议 1. **工具块显示** - 发送需要调用工具的消息 - 观察工具块是否正常显示 - 观察工具状态变化(preparing → running → completed) - 观察工具结果是否正确显示 2. **流式输出效果** - 发送消息,观察输出是否流畅 - 观察思考块、文本块的更新频率 - 对比之前 1 秒轮询的卡顿感 3. **刷新恢复** - 任务执行中刷新页面 - 观察是否只显示一遍内容 - 观察新内容是否正常追加 - 观察工具块是否保留 4. **任务完成** - 等待任务完成 - 观察是否有重复内容 - 观察统计是否正确更新 ## 构建 ```bash cd static npm run build ``` 构建成功! - `static/dist/assets/main.js` (689.89 kB) - `static/dist/assets/task.js` (3.68 kB) ## 已知改进 - ✅ 工具块正常显示 - ✅ 轮询频率提升到 150ms(接近流式效果) - ✅ 刷新后不重复显示内容 - ✅ 任务完成后不重复加载历史 - ✅ 状态检查避免重复恢复 现在可以启动服务器测试了!