131 lines
3.9 KiB
Markdown
131 lines
3.9 KiB
Markdown
# 句子分块改进文档
|
||
|
||
## 问题描述
|
||
|
||
在原始的NER提取过程中,我们发现了一些实体被截断的问题,比如:
|
||
- `"丰复久信公"` (应该是 `"丰复久信营销科技有限公司"`)
|
||
- `"康达律师事"` (应该是 `"北京市康达律师事务所"`)
|
||
|
||
这些截断问题是由于原始的基于字符数量的简单分块策略导致的,该策略没有考虑实体的完整性。
|
||
|
||
## 解决方案
|
||
|
||
### 1. 句子分块策略
|
||
|
||
我们实现了基于句子的智能分块策略,主要特点:
|
||
|
||
- **自然边界分割**:使用中文句子结束符(。!?;\n)和英文句子结束符(.!?;)进行分割
|
||
- **实体完整性保护**:避免在实体名称中间进行分割
|
||
- **智能长度控制**:基于token数量而非字符数量进行分块
|
||
|
||
### 2. 实体边界安全检查
|
||
|
||
实现了 `_is_entity_boundary_safe()` 方法来检查分割点是否安全:
|
||
|
||
```python
|
||
def _is_entity_boundary_safe(self, text: str, position: int) -> bool:
|
||
# 检查常见实体后缀
|
||
entity_suffixes = ['公', '司', '所', '院', '厅', '局', '部', '会', '团', '社', '处', '室', '楼', '号']
|
||
|
||
# 检查不完整的实体模式
|
||
if text[position-2:position+1] in ['公司', '事务所', '协会', '研究院']:
|
||
return False
|
||
|
||
# 检查地址模式
|
||
address_patterns = ['省', '市', '区', '县', '路', '街', '巷', '号', '室']
|
||
# ...
|
||
```
|
||
|
||
### 3. 长句子智能分割
|
||
|
||
对于超过token限制的长句子,实现了智能分割策略:
|
||
|
||
1. **标点符号分割**:优先在逗号、分号等标点符号处分割
|
||
2. **实体边界分割**:如果标点分割不可行,在安全的实体边界处分割
|
||
3. **强制分割**:最后才使用字符级别的强制分割
|
||
|
||
## 实现细节
|
||
|
||
### 核心方法
|
||
|
||
1. **`_split_text_by_sentences()`**: 将文本按句子分割
|
||
2. **`_create_sentence_chunks()`**: 基于句子创建分块
|
||
3. **`_split_long_sentence()`**: 智能分割长句子
|
||
4. **`_is_entity_boundary_safe()`**: 检查分割点安全性
|
||
|
||
### 分块流程
|
||
|
||
```
|
||
输入文本
|
||
↓
|
||
按句子分割
|
||
↓
|
||
估算token数量
|
||
↓
|
||
创建句子分块
|
||
↓
|
||
检查实体边界
|
||
↓
|
||
输出最终分块
|
||
```
|
||
|
||
## 测试结果
|
||
|
||
### 改进前 vs 改进后
|
||
|
||
| 指标 | 改进前 | 改进后 |
|
||
|------|--------|--------|
|
||
| 截断实体数量 | 较多 | 显著减少 |
|
||
| 实体完整性 | 经常被破坏 | 得到保护 |
|
||
| 分块质量 | 基于字符 | 基于语义 |
|
||
|
||
### 测试案例
|
||
|
||
1. **"丰复久信公" 问题**:
|
||
- 改进前:`"丰复久信公"` (截断)
|
||
- 改进后:`"北京丰复久信营销科技有限公司"` (完整)
|
||
|
||
2. **长句子处理**:
|
||
- 改进前:可能在实体中间截断
|
||
- 改进后:在句子边界或安全位置分割
|
||
|
||
## 配置参数
|
||
|
||
- `max_tokens`: 每个分块的最大token数量 (默认: 400)
|
||
- `confidence_threshold`: 实体置信度阈值 (默认: 0.95)
|
||
- `sentence_pattern`: 句子分割正则表达式
|
||
|
||
## 使用示例
|
||
|
||
```python
|
||
from app.core.document_handlers.extractors.ner_extractor import NERExtractor
|
||
|
||
extractor = NERExtractor()
|
||
result = extractor.extract(long_text)
|
||
|
||
# 结果中的实体将更加完整
|
||
entities = result.get("entities", [])
|
||
for entity in entities:
|
||
print(f"{entity['text']} ({entity['type']})")
|
||
```
|
||
|
||
## 性能影响
|
||
|
||
- **内存使用**:略有增加(需要存储句子分割结果)
|
||
- **处理速度**:基本无影响(句子分割很快)
|
||
- **准确性**:显著提升(减少截断实体)
|
||
|
||
## 未来改进方向
|
||
|
||
1. **更智能的实体识别**:使用预训练模型识别实体边界
|
||
2. **动态分块大小**:根据文本复杂度调整分块大小
|
||
3. **多语言支持**:扩展到其他语言的分块策略
|
||
4. **缓存优化**:缓存句子分割结果以提高性能
|
||
|
||
## 相关文件
|
||
|
||
- `backend/app/core/document_handlers/extractors/ner_extractor.py` - 主要实现
|
||
- `backend/test_improved_chunking.py` - 测试脚本
|
||
- `backend/test_truncation_fix.py` - 截断问题测试
|
||
- `backend/test_chunking_logic.py` - 分块逻辑测试
|