diff --git a/智能体聊天助手性能优化方案.md b/智能体聊天助手性能优化方案.md new file mode 100644 index 0000000..bdc02be --- /dev/null +++ b/智能体聊天助手性能优化方案.md @@ -0,0 +1,447 @@ +# 智能体聊天助手性能优化方案 + +## 一、当前性能瓶颈分析 + +### 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秒) +``` + +## 二、优化方案 + +### 方案 1:LLM 调用优化 ⭐⭐⭐⭐⭐ + +#### 1.1 并行执行可并行的 LLM 节点 + +**问题**:当前 `llm-format` 节点必须等待 `llm-question` 完成,但实际上可以优化。 + +**优化方案**:合并 `llm-question` 和 `llm-format` 节点 + +```python +# 修改 llm-question 节点的 prompt,直接生成格式化好的回复 +prompt = """你是一个知识渊博、乐于助人的AI助手。请回答用户的问题。 + +用户问题:{{user_input}} +对话历史:{{memory.conversation_history}} +意图分析:{{output}} + +请提供: +1. 直接、准确的答案 +2. 必要的解释和说明 +3. 如果问题不明确,友好地询问更多信息 + +请以自然、易懂的方式回答,长度控制在200字以内。直接输出回答内容,确保回复自然、流畅,无需额外格式化。""" +``` + +**效果**:减少 1 个 LLM 调用,节省 **1-2 秒** + +#### 1.2 使用流式响应(Streaming) + +**当前**:等待完整响应后才返回 + +**优化**:使用流式响应,边生成边返回 + +```python +# 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 响应缓存 + +**场景**:相同或相似的问题可以复用之前的回答 + +```python +# 在 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=1000(200字约500 tokens) +- 删除 `llm-format` 节点 + +**效果**:减少 token 生成时间,节省 **0.5-1 秒** + +### 方案 2:前端优化 ⭐⭐⭐⭐ + +#### 2.1 使用 WebSocket 替代轮询 + +**当前**:每 500ms 轮询一次,产生大量请求 + +**优化**:使用 WebSocket 实时推送执行状态 + +```typescript +// 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,可以优化轮询策略: + +```typescript +// 动态调整轮询间隔 +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 条件优化:跳过不必要的节点 + +**场景**:如果用户只是简单问候,不需要完整的问题回答流程 + +```python +# 在 switch-intent 节点后,对于 greeting 意图 +# 可以直接使用模板回复,跳过 LLM 调用 + +if intent == "greeting": + # 使用预定义模板 + response = "你好!很高兴见到你,有什么我可以帮助你的吗?" + # 跳过 llm-greeting 节点 +else: + # 正常执行 LLM 节点 + response = await llm_question(...) +``` + +**效果**:简单场景响应时间从 5 秒降至 **< 1 秒** + +### 方案 4:缓存优化 ⭐⭐⭐ + +#### 4.1 对话历史截断 + +**问题**:对话历史过长会增加 LLM prompt 长度,影响响应速度 + +**优化**:只保留最近 N 条对话 + +```python +# 在 cache-update 节点中 +conversation_history = memory.conversation_history[-20:] # 只保留最近20条 +``` + +**效果**: +- 减少 prompt 长度,节省 **0.2-0.5 秒** +- 降低 token 消耗 + +#### 4.2 智能摘要 + +**更高级的方案**:将旧对话压缩为摘要 + +```python +# 如果对话历史超过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 异步日志写入 + +**当前**:每个节点执行后立即写入日志 + +**优化**:批量写入日志 + +```python +# 收集日志,定期批量写入 +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 减少日志详细程度 + +**生产环境**:只记录关键日志(错误、警告) + +```python +# 根据环境变量控制日志级别 +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 分钟 + +### 🟡 **中优先级(近期实施)** + +4. **使用 WebSocket 替代轮询** ⭐⭐⭐⭐ + - 实施难度:中 + - 效果:提升实时性,减少服务器负载 + - 预计时间:2-3 小时 + +5. **简化工作流结构** ⭐⭐⭐ + - 实施难度:中 + - 效果:节省 1-2 秒 + - 预计时间:1-2 小时 + +6. **智能轮询(如果不用 WebSocket)** ⭐⭐⭐ + - 实施难度:低 + - 效果:减少 30-50% 无效请求 + - 预计时间:1 小时 + +### 🟢 **低优先级(长期优化)** + +7. **流式响应** ⭐⭐⭐⭐⭐ + - 实施难度:高 + - 效果:显著提升用户体验(首字响应时间降低 50-70%) + - 预计时间:4-6 小时 + +8. **LLM 响应缓存** ⭐⭐⭐ + - 实施难度:中 + - 效果:重复问题响应时间 < 100ms + - 预计时间:2-3 小时 + +9. **条件优化(跳过不必要节点)** ⭐⭐⭐ + - 实施难度:中 + - 效果:简单场景响应时间 < 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 分钟) + +```bash +# 1. 修改工作流配置,删除 llm-format 节点 +# 2. 优化各 LLM 节点的 max_tokens +# 3. 添加对话历史截断逻辑 +``` + +### 步骤 2:前端优化(2-3 小时) + +```bash +# 1. 在 AgentChatPreview.vue 中集成 WebSocket +# 2. 替换轮询逻辑 +# 3. 测试实时性 +``` + +### 步骤 3:高级优化(可选,4-6 小时) + +```bash +# 1. 实现流式响应 +# 2. 添加 LLM 响应缓存 +# 3. 优化工作流结构 +``` + +## 六、监控指标 + +实施优化后,建议监控以下指标: + +1. **响应时间分布** + - P50(中位数) + - P95(95% 分位数) + - P99(99% 分位数) + +2. **LLM 调用时间** + - 各节点的平均调用时间 + - Token 消耗 + +3. **前端指标** + - 首字响应时间(TTFB) + - 完整响应时间 + - HTTP 请求数量 + +4. **服务器负载** + - CPU 使用率 + - 内存使用率 + - 数据库连接数 + +## 七、注意事项 + +1. **流式响应**:需要修改前端 UI,支持逐步显示文本 +2. **缓存策略**:需要考虑缓存失效和更新机制 +3. **向后兼容**:优化不应破坏现有功能 +4. **测试覆盖**:每个优化都需要充分测试 + +--- + +**文档版本**:v1.0 +**创建时间**:2024年 +**维护人员**:AI Assistant