知识库搭建-文档预处理-数据清洗:基于异步的AI文本批处理系统实践
项目背景
在构建企业级知识库和RAG(检索增强生成)系统时,文档预处理和数据清洗是至关重要的环节。原始文档往往存在格式不统一、内容冗余、质量参差不齐等问题,这些问题会直接影响到向量数据库的检索质量和后续AI模型的表现。为了解决这个问题,我开发了一个基于Python的异步文本批处理系统。通过这个系统的处理,我们可以显著提升知识库的质量,为后续的向量检索和AI应用打下坚实的基础。
技术架构
项目采用了以下核心技术栈:
- Python 3.x
- asyncio:用于异步并发处理
- aiohttp:处理异步HTTP请求
- OpenAI兼容API:用于文本处理
- tqdm:提供进度条显示
- Pathlib:处理文件路径
核心功能
- 多格式支持:支持txt、md、doc、docx等多种文本格式
- 异步处理:使用Python的asyncio实现高效的并发处理
- 并发控制:通过信号量限制最大并发数,避免API限制
- 自动化管理:自动创建输入输出目录,统一文件命名
- 错误处理:完善的异常处理机制
- 进度显示:实时显示处理进度
完整代码
1、batch_processor.py
python
import os
import asyncio
from pathlib import Path
from typing import List
import openai
from tqdm import tqdm
from datetime import datetime
from config import API_KEY, API_BASE, MODEL_NAME, PROMPT_TEMPLATE, INPUT_DIR, OUTPUT_DIR
class BatchProcessor:
def __init__(self):
"""初始化批处理器"""
# 配置OpenAI客户端
self.client = openai.OpenAI(
api_key=API_KEY,
base_url=API_BASE
)
# 创建输入输出目录
self._create_directories()
# 设置并发限制
self.semaphore = asyncio.Semaphore(5) # 限制最大并发数为5
def _create_directories(self):
"""创建必要的目录"""
os.makedirs(INPUT_DIR, exist_ok=True)
os.makedirs(OUTPUT_DIR, exist_ok=True)
def get_input_files(self) -> List[Path]:
"""获取输入文件列表,支持多种文本文件格式"""
input_path = Path(INPUT_DIR)
# 支持多种文本文件格式
text_extensions = ['.txt', '.md', '.doc', '.docx', '.rtf', '.html', '.htm']
input_files = []
for ext in text_extensions:
input_files.extend(list(input_path.glob(f'*{ext}')))
return input_files
async def process_file(self, input_file: Path) -> str:
"""异步处理单个文件"""
async with self.semaphore: # 使用信号量控制并发
try:
# 读取文件内容
with open(input_file, 'r', encoding='utf-8') as f:
content = f.read()
# 构建提示词
prompt = PROMPT_TEMPLATE.format(content=content)
# 调用API
response = await asyncio.to_thread(
self.client.chat.completions.create,
model=MODEL_NAME,
messages=[
{"role": "system", "content": "你是一个专业的文本优化助手。"},
{"role": "user", "content": prompt}
],
temperature=0.7,
max_tokens=4096 # 修改为模型支持的最大token数
)
# 获取生成的内容
generated_content = response.choices[0].message.content
# 添加延迟以避免API限制
await asyncio.sleep(1)
return generated_content
except Exception as e:
print(f"处理文件 {input_file} 时出错: {str(e)}")
return None
async def save_output(self, input_file: Path, content: str):
"""异步保存输出文件,统一使用.md格式"""
if content is None:
return
# 生成带时间戳的输出文件名,统一使用.md后缀
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = Path(OUTPUT_DIR) / f"{input_file.stem}_processed_{timestamp}.md"
# 使用异步文件操作
await asyncio.to_thread(
lambda: open(output_file, 'w', encoding='utf-8').write(content)
)
async def process_all_files(self):
"""异步处理所有文件"""
input_files = self.get_input_files()
if not input_files:
print(f"请在 {INPUT_DIR} 目录中放入要处理的文本文件")
return
print(f"找到 {len(input_files)} 个文件待处理")
# 创建所有任务
tasks = []
for input_file in input_files:
# 创建处理文件的任务
task = asyncio.create_task(self.process_file(input_file))
tasks.append((input_file, task))
# 使用tqdm显示进度条
with tqdm(total=len(tasks), desc="处理文件") as pbar:
# 等待所有任务完成
for input_file, task in tasks:
content = await task
await self.save_output(input_file, content)
pbar.update(1)
def main():
"""主函数"""
processor = BatchProcessor()
# 使用asyncio.run()来运行异步主函数
asyncio.run(processor.process_all_files())
if __name__ == "__main__":
main()
2、config.py
python
import os
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
# API配置
API_KEY = os.getenv(
"API_KEY", "sk-xxx"
) # 从环境变量获取API密钥
API_BASE = os.getenv("API_BASE", "https://xxx/v1") # API基础URL
MODEL_NAME = os.getenv("MODEL_NAME", "gpt-3.5-turbo") # 使用的模型名称
# 提示词模板集合
PROMPT_TEMPLATES = {
"optimize": """
你是一个专业的文档数据清洗专家,负责处理和优化用于RAG知识库构建的文档。请按照以下指南对提供的文档进行全面清洗和标准化处理:
### 数据清洗任务:
1. 去除无关内容:
- 删除所有广告内容
- 移除页眉页脚信息(如页码、章节标题等重复出现的元素)
- 清除水印文本
- 去除版权声明、免责声明等非核心内容
- 删除装饰性特殊字符和符号
2. 标准化格式:
- 将所有文本转换为UTF-8编码
- 统一标点符号(如将全角标点转为半角,或根据文档主要语言选择合适的标点规范)
- 规范化空格使用(删除多余空格,保持段落间隔一致)
- 对于中文文档,确保使用标准中文标点
- 对于英文部分,统一大小写规范(如专有名词、缩写等)
3. 处理缺失值和噪声:
- 修正OCR错误(如"0"与"O"、"1"与"l"的混淆)
- 识别并修复断行导致的词语分割
- 合并被错误分割的段落
- 修正明显的拼写和语法错误
- 标记无法修复的损坏内容
4. 结构优化:
- 重新组织文档的层次结构(标题、小标题、段落)
- 确保列表格式一致(编号、项目符号等)
- 保持表格数据的完整性和可读性
- 确保图表引用的连贯性
5. 语义保全:
- 确保清洗过程不改变原文档的核心含义
- 保留专业术语和领域特定词汇
- 维持上下文关系和逻辑连贯性
### 输出要求:
1. 严格要求:仅输出清洗后的文档内容,不要包含任何解释、说明或其他额外内容
2. 使用markdown格式输出清洗后的完整文档
3. 不要添加任何前缀、后缀或额外的评论
4. 输出结果强化:
- 确保文档具有清晰的层次结构,使用适当的标题级别(#、##、###等)
- 主要章节使用一级标题(#),子部分使用二级标题(##),更细分的内容使用三级标题(###)
- 相关内容应组织在同一部分下,保持逻辑连贯性
- 为没有明确标题的重要段落添加适当的小标题
- 确保标题层级之间的逻辑关系清晰,避免层级跳跃
请处理以下文档内容:
{content}
""",
}
# 当前激活的提示词模板
ACTIVE_TEMPLATE = os.getenv("ACTIVE_TEMPLATE", "optimize") # 默认使用优化模板
# 获取当前激活的提示词模板
PROMPT_TEMPLATE = PROMPT_TEMPLATES.get(ACTIVE_TEMPLATE, PROMPT_TEMPLATES["optimize"])
# 文件路径配置
INPUT_DIR = "docs" # 输入文件目录
OUTPUT_DIR = "docs-output" # 输出文件目录
3、requirements.txt
bash
openai>=1.0.0
python-dotenv>=0.19.0
tqdm>=4.65.0
aiohttp>=3.8.0
技术实现细节
1. 异步并发设计
python
class BatchProcessor:
def __init__(self):
# 限制最大并发数为5,避免API过载
self.semaphore = asyncio.Semaphore(5)
async def process_file(self, input_file: Path) -> str:
async with self.semaphore: # 使用信号量控制并发
# 处理逻辑
pass
2. 智能文件处理
python
def get_input_files(self) -> List[Path]:
"""获取输入文件列表,支持多种文本文件格式"""
text_extensions = ['.txt', '.md', '.doc', '.docx', '.rtf', '.html', '.htm']
input_files = []
for ext in text_extensions:
input_files.extend(list(input_path.glob(f'*{ext}')))
return input_files
3. 输出文件管理
python
async def save_output(self, input_file: Path, content: str):
# 生成带时间戳的输出文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = Path(OUTPUT_DIR) / f"{input_file.stem}_processed_{timestamp}.md"
项目优势
- 高效性:采用异步并发处理,显著提升处理效率
- 可扩展性:模块化设计,易于添加新功能
- 稳定性:完善的错误处理机制,确保系统稳定运行
- 易用性:简单的配置和使用方式,上手门槛低
使用方法
- 安装依赖:
bash
pip install -r requirements.txt
- 配置系统:
- 创建配置文件,设置必要的环境变量
- 可自定义处理模板和目录路径
- 运行系统:
bash
python batch_processor.py