Files
LocalAgent/docs/P1-02_重试策略修复说明.md
Mimikko-zeus 8a538bb950 feat: refactor API key configuration and enhance application initialization
- Renamed `check_environment` to `check_api_key_configured` for clarity, simplifying the API key validation logic.
- Removed the blocking behavior of the API key check during application startup, allowing the app to run while providing a prompt for configuration.
- Updated `LocalAgentApp` to accept an `api_configured` parameter, enabling conditional messaging for API key setup.
- Enhanced the `SandboxRunner` to support backup management and improved execution result handling with detailed metrics.
- Integrated data governance strategies into the `HistoryManager`, ensuring compliance and improved data management.
- Added privacy settings and metrics tracking across various components to enhance user experience and application safety.
2026-02-27 14:32:30 +08:00

246 lines
6.6 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.
# P1-02 重试策略修复说明
## 问题描述
**问题标题**: 重试策略声明与实际行为不一致
**问题类型**: 技术/稳定性
**所在位置**: `llm/client.py:68, 149, 218`
### 核心问题
网络异常(`Timeout``ConnectionError`)先被包装为 `LLMClientError`,后续 `_should_retry` 方法只能通过字符串匹配判断是否重试,导致大部分网络异常无法被正确识别为可重试异常,弱网环境下稳定性下降。
### 影响范围
- 意图识别模块
- 生成计划模块
- 代码生成模块
- 所有 LLM 调用场景
在网络抖动环境下,这些模块的失败率显著升高。
---
## 修复方案
### 1. 异常分类系统
`LLMClientError` 添加了错误类型分类:
```python
class LLMClientError(Exception):
# 异常类型分类
TYPE_NETWORK = "network" # 网络错误(超时、连接失败等)
TYPE_SERVER = "server" # 服务器错误5xx
TYPE_CLIENT = "client" # 客户端错误4xx
TYPE_PARSE = "parse" # 解析错误
TYPE_CONFIG = "config" # 配置错误
def __init__(self, message: str, error_type: str = TYPE_CLIENT,
original_exception: Optional[Exception] = None):
super().__init__(message)
self.error_type = error_type
self.original_exception = original_exception
```
### 2. 统一重试判断逻辑
重构 `_should_retry` 方法,基于异常类型而非字符串匹配:
```python
def _should_retry(self, exception: Exception) -> bool:
"""
判断是否应该重试
可重试的异常类型:
- 网络错误(超时、连接失败)
- 服务器错误5xx
- 限流错误429
"""
# LLMClientError 根据错误类型判断
if isinstance(exception, LLMClientError):
# 网络错误和服务器错误可以重试
if exception.error_type in (LLMClientError.TYPE_NETWORK,
LLMClientError.TYPE_SERVER):
return True
# 检查原始异常
if exception.original_exception:
if isinstance(exception.original_exception,
(requests.exceptions.ConnectionError,
requests.exceptions.Timeout,
requests.exceptions.ChunkedEncodingError)):
return True
return False
```
### 3. 保留原始异常信息
在所有异常包装点保留原始异常:
**非流式请求 (chat)**:
```python
except requests.exceptions.Timeout as e:
raise LLMClientError(
f"请求超时({timeout}秒)",
error_type=LLMClientError.TYPE_NETWORK,
original_exception=e
)
```
**流式请求 (chat_stream)**:
```python
except requests.exceptions.ConnectionError as e:
raise LLMClientError(
"网络连接失败",
error_type=LLMClientError.TYPE_NETWORK,
original_exception=e
)
```
### 4. 状态码分类
根据 HTTP 状态码自动分类错误类型:
```python
if response.status_code >= 500:
error_type = LLMClientError.TYPE_SERVER # 可重试
elif response.status_code == 429:
error_type = LLMClientError.TYPE_SERVER # 限流,可重试
else:
error_type = LLMClientError.TYPE_CLIENT # 不重试
```
### 5. 增强重试度量
`_do_request_with_retry` 中增强度量记录:
- 记录重试次数
- 记录错误类型
- 记录重试后成功/失败
- 输出更详细的重试日志
---
## 测试验证
### 测试结果
**所有测试通过**
```
测试 1: 异常分类
✓ 网络错误类型: network
✓ 服务器错误类型: server
✓ 客户端错误类型: client
测试 2: 重试判断逻辑
✓ 网络错误应该重试: True
✓ 超时错误应该重试: True
✓ 服务器错误应该重试: True
✓ 客户端错误不应该重试: False
✓ 解析错误不应该重试: False
✓ 配置错误不应该重试: False
✓ 带原始异常的网络错误应该重试: True
测试 3: 错误类型保留
✓ 状态码 500-504 (服务器错误): server
✓ 状态码 429 (限流错误): server
✓ 状态码 400-404 (客户端错误): client
```
---
## 修复效果
### 可重试的异常类型
| 异常类型 | 修复前 | 修复后 |
|---------|--------|--------|
| 网络超时 (Timeout) | ❌ 不重试 | ✅ 重试 |
| 连接失败 (ConnectionError) | ❌ 不重试 | ✅ 重试 |
| 服务器错误 (5xx) | ⚠️ 部分重试 | ✅ 重试 |
| 限流错误 (429) | ❌ 不重试 | ✅ 重试 |
| 客户端错误 (4xx) | ❌ 不重试 | ❌ 不重试 |
| 解析错误 | ❌ 不重试 | ❌ 不重试 |
| 配置错误 | ❌ 不重试 | ❌ 不重试 |
### 预期改进
1. **稳定性提升**: 弱网环境下的请求成功率显著提高
2. **用户体验**: 网络抖动时自动恢复,无需手动重试
3. **可观测性**: 更详细的重试日志和度量指标
4. **准确性**: 只重试真正可恢复的错误,避免无效重试
---
## 度量指标
### 建议监控的指标
1. **LLM 请求成功率**: 总成功次数 / 总请求次数
2. **平均重试次数**: 总重试次数 / 总请求次数
3. **超时后恢复成功率**: 重试成功次数 / 超时次数
4. **网络错误分布**: 各类网络错误的占比
5. **重试延迟**: 重试导致的额外延迟时间
### 度量数据位置
- 配置度量: `workspace/.metrics/config_metrics.json`
- 重试日志: 控制台输出
---
## 向后兼容性
**完全向后兼容**
- `LLMClientError` 仍然是标准异常,可以正常捕获
- 新增的 `error_type``original_exception` 属性是可选的
- 现有代码无需修改即可受益于修复
---
## 使用示例
### 捕获特定类型的错误
```python
from llm.client import get_client, LLMClientError
try:
client = get_client()
response = client.chat(messages=[...], model="...")
except LLMClientError as e:
if e.error_type == LLMClientError.TYPE_NETWORK:
print("网络错误,已自动重试")
elif e.error_type == LLMClientError.TYPE_CONFIG:
print("配置错误,请检查 .env 文件")
else:
print(f"其他错误: {e}")
```
### 检查原始异常
```python
try:
response = client.chat(...)
except LLMClientError as e:
if e.original_exception:
print(f"原始异常: {type(e.original_exception).__name__}")
```
---
## 相关文件
- `llm/client.py`: 主要修复文件
- `llm/config_metrics.py`: 度量指标增强
- `test_retry_fix.py`: 验证测试脚本
---
## 总结
此次修复解决了重试策略声明与实际行为不一致的核心问题,通过引入异常分类系统和保留原始异常信息,确保网络异常能够被正确识别并重试。预期在弱网环境下,系统稳定性将显著提升。