Skip to content

Latest commit

 

History

History
244 lines (182 loc) · 6.98 KB

File metadata and controls

244 lines (182 loc) · 6.98 KB

Thinking 区域更新优化方案

📊 问题分析

当前问题

Thinking 区域的更新频率远高于 Conversation 区域,导致性能问题:

区域 触发频率 更新内容 性能影响
Conversation 低频(token 生成) 完整的 token 内容 较小
Thinking 高频(thinking delta) 逐字符/逐词增量 较大

性能影响

每次 Thinking 更新都会触发:

  1. 重新格式化formatThinkingAsLines() 处理所有 thinking 行
  2. 重新计算滚动:计算滚动位置和可见范围
  3. 重新渲染:React 重新渲染整个区域

数据对比

假设 AI 生成一个 1000 字符的 thinking 内容:

  • Conversation:约 250 次 token 更新(假设平均 4 字符/token)
  • Thinking:约 1000 次 delta 更新(逐字符)

Thinking 的更新频率是 Conversation 的 4 倍!

✅ 解决方案

方案 1:使用 Throttling Hook(推荐)

使用 useSmartThrottledState Hook 来优化 Thinking 区域的更新。

实施步骤

  1. 导入 Hook
import { useSmartThrottledState } from './hooks/use-throttled-state.js';
  1. 创建带节流的更新函数
const [thinking, setThinking] = useState([]);
const { updateSmart } = useSmartThrottledState(setThinking, {
  throttleDelay: 50,  // 节流延迟 50ms
  immediateTypes: ['tool_start', 'tool_complete', 'thinking_signature', 'thinking_redacted']
});
  1. 在 progress 回调中使用
// 原代码
if (progress.type === 'thinking') {
  setThinking(prev => [...prev, ...]);
}

// 优化后
if (progress.type === 'thinking') {
  updateSmart(newThinking, 'thinking');
} else if (progress.type === 'tool_start') {
  updateSmart(newThinking, 'tool_start');
}

优化效果

  • 减少渲染次数:从 1000 次降低到约 20 次(50ms 节流)
  • 保持响应性:重要事件(如工具调用)仍然立即更新
  • 用户体验:几乎无感知,因为 50ms 的延迟人眼无法察觉

方案 2:增加 Delta 阈值

只有当累积的 delta 达到一定长度时才更新。

实施步骤

  1. 添加累积缓冲区
const thinkingBufferRef = useRef('');
const lastThinkingUpdateRef = useRef(0);

if (progress.type === 'thinking') {
  const delta = progress.delta || '';
  thinkingBufferRef.current += delta;

  // 只有当累积超过 20 个字符或超过 100ms 时才更新
  const now = Date.now();
  if (thinkingBufferRef.current.length > 20 || 
      now - lastThinkingUpdateRef.current > 100) {
    setThinking(prev => {
      // 使用累积的 delta
      const newThinking = [...prev];
      // ... 更新逻辑
      return newThinking;
    });
    
    thinkingBufferRef.current = '';
    lastThinkingUpdateRef.current = now;
  }
}

优化效果

  • 减少更新次数:从 1000 次降低到约 50 次(20 字符阈值)
  • 保持完整性:最终内容完全一致
  • 实现简单:不需要额外的 Hook

方案 3:使用 requestAnimationFrame

利用浏览器的 requestAnimationFrame 来优化更新时机。

实施步骤

const rafRef = useRef(null);
const pendingThinkingRef = useRef(null);

if (progress.type === 'thinking') {
  pendingThinkingRef.current = newThinking;

  if (!rafRef.current) {
    rafRef.current = requestAnimationFrame(() => {
      if (pendingThinkingRef.current) {
        setThinking(pendingThinkingRef.current);
        pendingThinkingRef.current = null;
      }
      rafRef.current = null;
    });
  }
}

优化效果

  • 与屏幕刷新率同步:通常 60fps,即 16.67ms 更新一次
  • 平滑更新:避免卡顿
  • 浏览器优化:浏览器会自动优化渲染时机

📊 方案对比

方案 优点 缺点 推荐度
Throttling Hook 灵活、可控、易用 需要额外代码 ⭐⭐⭐⭐⭐
Delta 阈值 简单、无需 Hook 可能丢失部分中间状态 ⭐⭐⭐⭐
requestAnimationFrame 与屏幕同步 Node.js 环境不适用 ⭐⭐⭐

🚀 推荐实施方案

组合方案:Throttling + Delta 阈值

结合两种方案,获得最佳性能:

const [thinking, setThinking] = useState([]);
const { updateSmart } = useSmartThrottledState(setThinking, {
  throttleDelay: 50,
  immediateTypes: ['tool_start', 'tool_complete', 'thinking_signature', 'thinking_redacted']
});

const thinkingBufferRef = useRef('');
const lastThinkingUpdateRef = useRef(0);

if (progress.type === 'thinking') {
  const delta = progress.delta || '';
  thinkingBufferRef.current += delta;

  const now = Date.now();
  const accumulatedDelta = thinkingBufferRef.current;
  
  // 只有当累积超过 10 个字符或超过 50ms 时才更新
  if (accumulatedDelta.length > 10 || now - lastThinkingUpdateRef.current > 50) {
    setThinking(prev => {
      const newThinking = [...prev];
      // 使用累积的 delta
      const lastEntry = newThinking[newThinking.length - 1];
      if (lastEntry && lastEntry.startsWith('🤔') && !lastEntry.includes('✅')) {
        const timestamp = lastEntry.match(/\[.*?\]/)[0];
        const currentContent = lastEntry.substring(lastEntry.indexOf('] ') + 2);
        newThinking[newThinking.length - 1] = `🤔 ${timestamp} ${currentContent}${accumulatedDelta}`;
      } else {
        const timestamp = `[${new Date().toLocaleTimeString()}]`;
        newThinking.push(`🤔 ${timestamp} ${accumulatedDelta}`);
      }
      return newThinking.slice(-30);
    });
    
    thinkingBufferRef.current = '';
    lastThinkingUpdateRef.current = now;
  }
} else {
  // 其他类型的事件使用 updateSmart
  updateSmart(newThinking, progress.type);
}

优化效果预估

假设一个 1000 字符的 thinking 内容:

方案 更新次数 渲染次数 性能提升
原始 1000 1000 -
仅 Throttling 1000 ~20 98% ↓
仅 Delta 阈值 1000 ~50 95% ↓
组合方案 1000 ~10 99% ↓

📝 实施清单

  • 创建 src/hooks/use-throttled-state.js
  • src/closer-cli.jsx 中导入 Hook
  • 修改 thinking 更新逻辑
  • 测试验证
  • 性能对比

🎯 预期结果

性能提升

  • 渲染次数减少:99%
  • CPU 使用降低:约 80-90%
  • 内存使用降低:约 50%(减少临时对象创建)
  • 用户体验提升:更流畅,无明显卡顿

兼容性

  • ✅ 完全向后兼容
  • ✅ 不影响功能
  • ✅ 不影响最终显示内容
  • ✅ 可配置的延迟参数

📚 参考资料


文档日期: 2025年1月 版本: 1.0 状态: 📋 方案设计完成