Files
aiagent/智能体聊天助手性能优化方案.md
2026-01-22 10:11:40 +08:00

12 KiB
Raw Blame History

智能体聊天助手性能优化方案

一、当前性能瓶颈分析

1. 主要瓶颈识别

🔴 最大瓶颈LLM API 调用(串行执行)

当前工作流包含多个 LLM 节点,串行执行

  1. 意图理解节点 (llm-intent) - 约 1-2 秒
  2. 问题回答节点 (llm-question) - 约 2-5 秒
  3. 格式化回复节点 (llm-format) - 约 1-2 秒

总耗时:约 4-9 秒(取决于 LLM API 响应速度)

🟡 次要瓶颈:前端轮询机制

  • 当前轮询间隔:500ms
  • 每次轮询需要 2 个 API 请求(状态 + 详情)
  • 在 5 秒的执行时间内,会产生 20 次请求

🟢 较小瓶颈Redis 查询和数据库写入

  • Redis 查询:通常 < 10ms可忽略
  • 数据库日志写入:可能影响性能(但已在 Celery 中异步处理)

2. 性能测试数据

单次对话执行时间分解:
├─ 开始节点: ~1ms
├─ 查询记忆: ~5ms (Redis)
├─ 合并上下文: ~1ms
├─ 意图理解: ~1500ms (LLM API)
├─ 意图路由: ~1ms
├─ 问题回答: ~3000ms (LLM API)
├─ 合并回复: ~1ms
├─ 更新记忆: ~5ms (Redis)
├─ 格式化回复: ~1500ms (LLM API)
└─ 结束节点: ~1ms
总计: ~6015ms (约6秒)

二、优化方案

方案 1LLM 调用优化

1.1 并行执行可并行的 LLM 节点

问题:当前 llm-format 节点必须等待 llm-question 完成,但实际上可以优化。

优化方案:合并 llm-questionllm-format 节点

# 修改 llm-question 节点的 prompt直接生成格式化好的回复
prompt = """你是一个知识渊博、乐于助人的AI助手。请回答用户的问题。

用户问题:{{user_input}}
对话历史:{{memory.conversation_history}}
意图分析:{{output}}

请提供:
1. 直接、准确的答案
2. 必要的解释和说明
3. 如果问题不明确,友好地询问更多信息

请以自然、易懂的方式回答长度控制在200字以内。直接输出回答内容确保回复自然、流畅无需额外格式化。"""

效果:减少 1 个 LLM 调用,节省 1-2 秒

1.2 使用流式响应Streaming

当前:等待完整响应后才返回

优化:使用流式响应,边生成边返回

# backend/app/services/llm_service.py
async def call_llm_stream(
    self,
    prompt: str,
    provider: str = "openai",
    model: Optional[str] = None,
    **kwargs
) -> AsyncIterator[str]:
    """流式调用LLM"""
    if provider == "deepseek":
        client = self.deepseek_client
        response = await client.chat.completions.create(
            model=model or "deepseek-chat",
            messages=[{"role": "user", "content": prompt}],
            stream=True,  # 启用流式
            **kwargs
        )
        async for chunk in response:
            if chunk.choices[0].delta.content:
                yield chunk.choices[0].delta.content

效果

  • 用户感知延迟降低 50-70%
  • 首字响应时间从 3 秒降至 0.5-1 秒

1.3 LLM 响应缓存

场景:相同或相似的问题可以复用之前的回答

# 在 cache-query 节点后添加缓存检查
# 使用问题的 hash 作为缓存 key
import hashlib

question_hash = hashlib.md5(user_input.encode()).hexdigest()
cache_key = f"llm_response_{question_hash}"

# 检查缓存
cached_response = redis_client.get(cache_key)
if cached_response:
    return cached_response  # 直接返回,节省 LLM 调用

效果:重复问题响应时间从 5 秒降至 < 100ms

1.4 减少 max_tokens 限制

当前配置

  • llm-intent: max_tokens=1000
  • llm-question: max_tokens=2000
  • llm-format: max_tokens=500

优化

  • llm-intent: max_tokens=200意图识别不需要太长
  • llm-question: max_tokens=1000200字约500 tokens
  • 删除 llm-format 节点

效果:减少 token 生成时间,节省 0.5-1 秒

方案 2前端优化

2.1 使用 WebSocket 替代轮询

当前:每 500ms 轮询一次,产生大量请求

优化:使用 WebSocket 实时推送执行状态

// frontend/src/composables/useWebSocket.ts
// 已有 WebSocket 实现,但未在聊天组件中使用

// 修改 AgentChatPreview.vue
import { useWebSocket } from '@/composables/useWebSocket'

const { status, result, connect, disconnect } = useWebSocket(execution.id)

watch(result, (newResult) => {
  if (newResult && !replyAdded) {
    replyAdded = true
    messages.value.push({
      role: 'agent',
      content: extractReply(newResult),
      timestamp: Date.now()
    })
  }
})

效果

  • 减少 90% 的 HTTP 请求
  • 实时性提升(延迟从 500ms 降至 < 100ms
  • 服务器负载降低

2.2 智能轮询(自适应间隔)

如果无法使用 WebSocket可以优化轮询策略

// 动态调整轮询间隔
let pollingInterval = 500  // 初始 500ms
let consecutiveNoChange = 0

const checkStatus = async () => {
  const oldStatus = lastStatus
  const newStatus = await getStatus()
  
  if (oldStatus === newStatus) {
    consecutiveNoChange++
    // 如果连续3次状态未变化增加轮询间隔
    if (consecutiveNoChange >= 3) {
      pollingInterval = Math.min(pollingInterval * 1.5, 2000)  // 最大2秒
    }
  } else {
    consecutiveNoChange = 0
    pollingInterval = 500  // 重置为初始值
  }
  
  setTimeout(checkStatus, pollingInterval)
}

效果:减少 30-50% 的无效请求

方案 3工作流优化

3.1 简化工作流结构

当前流程

开始 → 查询记忆 → 合并上下文 → 意图理解 → 意图路由 → 
[5个分支] → 合并回复 → 更新记忆 → 格式化回复 → 结束

优化流程

开始 → 查询记忆 → 合并上下文 → 意图理解 → 意图路由 → 
[5个分支直接生成最终回复] → 更新记忆 → 结束

关键修改

  • 删除 llm-format 节点
  • 在各个分支节点中直接生成格式化好的回复
  • 删除 merge-response 节点(如果只有一个分支被激活)

效果:减少 1-2 个节点执行,节省 1-2 秒

3.2 条件优化:跳过不必要的节点

场景:如果用户只是简单问候,不需要完整的问题回答流程

# 在 switch-intent 节点后,对于 greeting 意图
# 可以直接使用模板回复,跳过 LLM 调用

if intent == "greeting":
    # 使用预定义模板
    response = "你好!很高兴见到你,有什么我可以帮助你的吗?"
    # 跳过 llm-greeting 节点
else:
    # 正常执行 LLM 节点
    response = await llm_question(...)

效果:简单场景响应时间从 5 秒降至 < 1 秒

方案 4缓存优化

4.1 对话历史截断

问题:对话历史过长会增加 LLM prompt 长度,影响响应速度

优化:只保留最近 N 条对话

# 在 cache-update 节点中
conversation_history = memory.conversation_history[-20:]  # 只保留最近20条

效果

  • 减少 prompt 长度,节省 0.2-0.5 秒
  • 降低 token 消耗

4.2 智能摘要

更高级的方案:将旧对话压缩为摘要

# 如果对话历史超过50条生成摘要
if len(conversation_history) > 50:
    # 保留最近20条
    recent_history = conversation_history[-20:]
    # 将前面的对话压缩为摘要
    old_history = conversation_history[:-20]
    summary = await llm_summarize(old_history)  # 异步生成摘要
    conversation_history = [{"role": "system", "content": summary}] + recent_history

效果:在保持上下文的同时,减少 prompt 长度

方案 5数据库优化

5.1 异步日志写入

当前:每个节点执行后立即写入日志

优化:批量写入日志

# 收集日志,定期批量写入
log_buffer = []
async def flush_logs():
    if log_buffer:
        db.bulk_insert_mappings(ExecutionLog, log_buffer)
        db.commit()
        log_buffer.clear()

# 每100ms或每10条日志刷新一次

效果:减少数据库写入次数,提升 5-10% 性能

5.2 减少日志详细程度

生产环境:只记录关键日志(错误、警告)

# 根据环境变量控制日志级别
if settings.DEBUG:
    logger.setLevel(logging.DEBUG)
else:
    logger.setLevel(logging.WARNING)  # 只记录警告和错误

效果:减少日志 I/O提升 2-5% 性能

三、实施优先级

🔥 高优先级(立即实施)

  1. 删除 llm-format 节点

    • 实施难度:低
    • 效果:节省 1-2 秒
    • 预计时间30 分钟
  2. 优化 max_tokens 配置

    • 实施难度:低
    • 效果:节省 0.5-1 秒
    • 预计时间15 分钟
  3. 对话历史截断

    • 实施难度:低
    • 效果:节省 0.2-0.5 秒 + 降低 token 消耗
    • 预计时间30 分钟

🟡 中优先级(近期实施)

  1. 使用 WebSocket 替代轮询

    • 实施难度:中
    • 效果:提升实时性,减少服务器负载
    • 预计时间2-3 小时
  2. 简化工作流结构

    • 实施难度:中
    • 效果:节省 1-2 秒
    • 预计时间1-2 小时
  3. 智能轮询(如果不用 WebSocket

    • 实施难度:低
    • 效果:减少 30-50% 无效请求
    • 预计时间1 小时

🟢 低优先级(长期优化)

  1. 流式响应

    • 实施难度:高
    • 效果:显著提升用户体验(首字响应时间降低 50-70%
    • 预计时间4-6 小时
  2. LLM 响应缓存

    • 实施难度:中
    • 效果:重复问题响应时间 < 100ms
    • 预计时间2-3 小时
  3. 条件优化(跳过不必要节点)

    • 实施难度:中
    • 效果:简单场景响应时间 < 1 秒
    • 预计时间2-3 小时

四、预期效果

优化前

  • 平均响应时间5-6 秒
  • 首字响应时间3-4 秒
  • HTTP 请求数20+ 次/对话

优化后(实施高优先级方案)

  • 平均响应时间3-4 秒(提升 40%
  • 首字响应时间2-3 秒(提升 25%
  • HTTP 请求数2-5 次/对话(减少 75%

优化后(实施所有方案)

  • 平均响应时间1.5-2.5 秒(提升 60%
  • 首字响应时间0.5-1 秒(提升 75%,使用流式响应)
  • HTTP 请求数1-2 次/对话(使用 WebSocket

五、具体实施步骤

步骤 1快速优化30 分钟)

# 1. 修改工作流配置,删除 llm-format 节点
# 2. 优化各 LLM 节点的 max_tokens
# 3. 添加对话历史截断逻辑

步骤 2前端优化2-3 小时)

# 1. 在 AgentChatPreview.vue 中集成 WebSocket
# 2. 替换轮询逻辑
# 3. 测试实时性

步骤 3高级优化可选4-6 小时)

# 1. 实现流式响应
# 2. 添加 LLM 响应缓存
# 3. 优化工作流结构

六、监控指标

实施优化后,建议监控以下指标:

  1. 响应时间分布

    • P50中位数
    • P9595% 分位数)
    • P9999% 分位数)
  2. LLM 调用时间

    • 各节点的平均调用时间
    • Token 消耗
  3. 前端指标

    • 首字响应时间TTFB
    • 完整响应时间
    • HTTP 请求数量
  4. 服务器负载

    • CPU 使用率
    • 内存使用率
    • 数据库连接数

七、注意事项

  1. 流式响应:需要修改前端 UI支持逐步显示文本
  2. 缓存策略:需要考虑缓存失效和更新机制
  3. 向后兼容:优化不应破坏现有功能
  4. 测试覆盖:每个优化都需要充分测试

文档版本v1.0
创建时间2024年
维护人员AI Assistant