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

448 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 智能体聊天助手性能优化方案
## 一、当前性能瓶颈分析
### 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-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=1000200字约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中位数
- P9595% 分位数)
- P9999% 分位数)
2. **LLM 调用时间**
- 各节点的平均调用时间
- Token 消耗
3. **前端指标**
- 首字响应时间TTFB
- 完整响应时间
- HTTP 请求数量
4. **服务器负载**
- CPU 使用率
- 内存使用率
- 数据库连接数
## 七、注意事项
1. **流式响应**:需要修改前端 UI支持逐步显示文本
2. **缓存策略**:需要考虑缓存失效和更新机制
3. **向后兼容**:优化不应破坏现有功能
4. **测试覆盖**:每个优化都需要充分测试
---
**文档版本**v1.0
**创建时间**2024年
**维护人员**AI Assistant