refactor(json_parser): 统一 LLM 响应的 JSON 解析逻辑,简化代码并提高解析成功率

This commit is contained in:
Windpicker-owo
2025-11-02 12:18:53 +08:00
parent bd1624a018
commit d1c3d2196a
8 changed files with 511 additions and 179 deletions

View File

@@ -0,0 +1,216 @@
# JSON 解析统一化改进文档
## 改进目标
统一项目中所有 LLM 响应的 JSON 解析逻辑,使用 `json_repair` 库和统一的解析工具,简化代码并提高解析成功率。
## 创建的新工具模块
### `src/utils/json_parser.py`
提供统一的 JSON 解析功能:
#### 主要函数:
1. **`extract_and_parse_json(response, strict=False)`**
- 从 LLM 响应中提取并解析 JSON
- 自动处理 Markdown 代码块标记
- 使用 json_repair 修复格式问题
- 支持严格模式和容错模式
2. **`safe_parse_json(json_str, default=None)`**
- 安全解析 JSON失败时返回默认值
3. **`extract_json_field(response, field_name, default=None)`**
- 从 LLM 响应中提取特定字段的值
#### 处理策略:
1. 清理 Markdown 代码块标记(```json 和 ```
2. 提取 JSON 对象或数组(使用栈匹配算法)
3. 尝试直接解析
4. 如果失败,使用 json_repair 修复后解析
5. 容错模式下返回空字典或空列表
## 已修改的文件
### 1. `src/chat/memory_system/memory_query_planner.py` ✅
- 移除了自定义的 `_extract_json_payload` 方法
- 使用 `extract_and_parse_json` 替代原有的解析逻辑
- 简化了代码,提高了可维护性
**修改前:**
```python
payload = self._extract_json_payload(response)
if not payload:
return self._default_plan(query_text)
try:
data = orjson.loads(payload)
except orjson.JSONDecodeError as exc:
...
```
**修改后:**
```python
data = extract_and_parse_json(response, strict=False)
if not data or not isinstance(data, dict):
return self._default_plan(query_text)
```
### 2. `src/chat/memory_system/memory_system.py` ✅
- 移除了自定义的 `_extract_json_payload` 方法
-`_evaluate_information_value` 方法中使用统一解析工具
- 简化了错误处理逻辑
### 3. `src/chat/interest_system/bot_interest_manager.py` ✅
- 移除了自定义的 `_clean_llm_response` 方法
- 使用 `extract_and_parse_json` 解析兴趣标签数据
- 改进了错误处理和日志输出
### 4. `src/plugins/built_in/affinity_flow_chatter/chat_stream_impression_tool.py` ✅
-`_clean_llm_json_response` 标记为已废弃
- 使用 `extract_and_parse_json` 解析聊天流印象数据
- 添加了类型检查和错误处理
## 待修改的文件
### 需要类似修改的其他文件:
1. `src/plugins/built_in/affinity_flow_chatter/proactive_thinking_executor.py`
- 包含自定义的 JSON 清理逻辑
2. `src/plugins/built_in/affinity_flow_chatter/user_profile_tool.py`
- 包含自定义的 JSON 清理逻辑
3. 其他包含自定义 JSON 解析逻辑的文件
## 改进效果
### 1. 代码简化
- 消除了重复的 JSON 提取和清理代码
- 减少了代码行数和维护成本
- 统一了错误处理模式
### 2. 解析成功率提升
- 使用 json_repair 自动修复常见的 JSON 格式问题
- 支持多种 JSON 包装格式(代码块、纯文本等)
- 更好的容错处理
### 3. 可维护性提升
- 集中管理 JSON 解析逻辑
- 易于添加新的解析策略
- 便于调试和日志记录
### 4. 一致性提升
- 所有 LLM 响应使用相同的解析流程
- 统一的日志输出格式
- 一致的错误处理
## 使用示例
### 基本用法:
```python
from src.utils.json_parser import extract_and_parse_json
# LLM 响应可能包含 Markdown 代码块或其他文本
llm_response = '```json\\n{"key": "value"}\\n```'
# 自动提取和解析
data = extract_and_parse_json(llm_response, strict=False)
# 返回: {'key': 'value'}
# 如果解析失败,返回空字典(非严格模式)
# 严格模式下返回 None
```
### 提取特定字段:
```python
from src.utils.json_parser import extract_json_field
llm_response = '{"score": 0.85, "reason": "Good quality"}'
score = extract_json_field(llm_response, "score", default=0.0)
# 返回: 0.85
```
## 测试建议
1. **单元测试**
- 测试各种 JSON 格式(带/不带代码块标记)
- 测试格式错误的 JSON验证 json_repair 的修复能力)
- 测试嵌套 JSON 结构
- 测试空响应和无效响应
2. **集成测试**
- 在实际 LLM 调用场景中测试
- 验证不同模型的响应格式兼容性
- 测试错误处理和日志输出
3. **性能测试**
- 测试大型 JSON 的解析性能
- 验证缓存和优化策略
## 迁移指南
### 旧代码模式:
```python
# 旧的自定义解析逻辑
def _extract_json(response: str) -> str | None:
stripped = response.strip()
code_block_match = re.search(r"```(?:json)?\\s*(.*?)```", stripped, re.DOTALL)
if code_block_match:
return code_block_match.group(1)
# ... 更多自定义逻辑
# 使用
payload = self._extract_json(response)
if payload:
data = orjson.loads(payload)
```
### 新代码模式:
```python
# 使用统一工具
from src.utils.json_parser import extract_and_parse_json
# 直接解析
data = extract_and_parse_json(response, strict=False)
if data and isinstance(data, dict):
# 使用数据
pass
```
## 注意事项
1. **导入语句**:确保添加正确的导入
```python
from src.utils.json_parser import extract_and_parse_json
```
2. **错误处理**:统一工具已包含错误处理,无需额外 try-except
```python
# 不需要
try:
data = extract_and_parse_json(response)
except Exception:
...
# 应该
data = extract_and_parse_json(response, strict=False)
if not data:
# 处理失败情况
pass
```
3. **类型检查**:始终验证返回值类型
```python
data = extract_and_parse_json(response)
if isinstance(data, dict):
# 处理字典
elif isinstance(data, list):
# 处理列表
```
## 后续工作
1. 完成剩余文件的迁移
2. 添加完整的单元测试
3. 更新相关文档
4. 考虑添加性能监控和统计
## 日期
2025年11月2日