ddbb79b9f6
单容器 Docker 架构的国土空间规划课程智能问答系统,集成 FastAPI 后端与 Next.js 前端。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
171 lines
6.0 KiB
Python
171 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
向量化服务 - 独立处理文档向量化
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import logging
|
|
from pathlib import Path
|
|
from datetime import datetime, timedelta
|
|
|
|
# 添加src目录到Python路径
|
|
sys.path.append(str(Path(__file__).parent.parent))
|
|
|
|
from src.core.database import SessionLocal, get_database_info
|
|
from src.models.document import Document
|
|
from src.services.document_service import DocumentService
|
|
from src.core.config import get_settings
|
|
|
|
# 配置日志
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
handlers=[
|
|
logging.FileHandler('vectorizer_service.log'),
|
|
logging.StreamHandler()
|
|
]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# 减少不必要的日志
|
|
logging.getLogger("pypdf").setLevel(logging.ERROR)
|
|
logging.getLogger("PIL").setLevel(logging.ERROR)
|
|
logging.getLogger("chromadb").setLevel(logging.WARNING)
|
|
|
|
class VectorizerService:
|
|
"""向量化服务"""
|
|
|
|
def __init__(self):
|
|
self.settings = get_settings()
|
|
self.batch_size = 5 # 每次处理的文档数量
|
|
self.check_interval = 60 # 检查间隔(秒)
|
|
self.max_retries = 3 # 最大重试次数
|
|
self.retry_delay = 30 # 重试延迟(秒)
|
|
|
|
def get_unprocessed_documents(self, session):
|
|
"""获取未处理的文档"""
|
|
try:
|
|
# 查找未处理的文档
|
|
documents = session.query(Document).filter(
|
|
Document.is_processed == False,
|
|
Document.source_type == "knowledge_base"
|
|
).order_by(Document.id).limit(self.batch_size).all()
|
|
|
|
# 过滤掉文件不存在的文档
|
|
valid_documents = []
|
|
for doc in documents:
|
|
if Path(doc.file_path).exists():
|
|
valid_documents.append(doc)
|
|
else:
|
|
logger.warning(f"文档文件不存在: {doc.file_path}")
|
|
# 标记为已处理以避免重复检查
|
|
doc.is_processed = True
|
|
session.commit()
|
|
|
|
return valid_documents
|
|
except Exception as e:
|
|
logger.error(f"获取未处理文档失败: {e}")
|
|
return []
|
|
|
|
def process_document(self, session, document_service, document):
|
|
"""处理单个文档"""
|
|
logger.info(f"开始处理文档: {document.file_path} (ID: {document.id})")
|
|
|
|
for retry in range(self.max_retries):
|
|
try:
|
|
# 使用异步处理
|
|
import asyncio
|
|
|
|
# 创建新的事件循环
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
|
|
# 处理文档
|
|
success = loop.run_until_complete(
|
|
document_service.process_document(document.id)
|
|
)
|
|
|
|
loop.close()
|
|
|
|
if success:
|
|
logger.info(f"✅ 文档处理成功: {document.file_path}")
|
|
return True
|
|
else:
|
|
logger.warning(f"❌ 文档处理失败: {document.file_path}")
|
|
if retry < self.max_retries - 1:
|
|
logger.info(f"等待 {self.retry_delay} 秒后重试...")
|
|
time.sleep(self.retry_delay)
|
|
|
|
except Exception as e:
|
|
logger.error(f"文档处理异常 (尝试 {retry + 1}/{self.max_retries}): {e}")
|
|
if retry < self.max_retries - 1:
|
|
logger.info(f"等待 {self.retry_delay} 秒后重试...")
|
|
time.sleep(self.retry_delay)
|
|
|
|
logger.error(f"文档处理最终失败: {document.file_path}")
|
|
return False
|
|
|
|
def run(self):
|
|
"""运行向量化服务"""
|
|
logger.info("=" * 60)
|
|
logger.info("向量化服务启动")
|
|
logger.info("=" * 60)
|
|
|
|
# 显示数据库信息
|
|
db_info = get_database_info()
|
|
logger.info(f"数据库: {db_info.get('type', 'Unknown')}")
|
|
logger.info(f"批量大小: {self.batch_size}")
|
|
logger.info(f"检查间隔: {self.check_interval}秒")
|
|
|
|
session = SessionLocal()
|
|
document_service = DocumentService(session)
|
|
|
|
try:
|
|
while True:
|
|
try:
|
|
# 获取未处理的文档
|
|
documents = self.get_unprocessed_documents(session)
|
|
|
|
if documents:
|
|
logger.info(f"找到 {len(documents)} 个待处理文档")
|
|
|
|
success_count = 0
|
|
for doc in documents:
|
|
if self.process_document(session, document_service, doc):
|
|
success_count += 1
|
|
|
|
logger.info(f"批量处理完成: {success_count}/{len(documents)} 成功")
|
|
else:
|
|
logger.info("没有待处理的文档,等待中...")
|
|
|
|
# 等待下一次检查
|
|
time.sleep(self.check_interval)
|
|
|
|
except KeyboardInterrupt:
|
|
logger.info("收到中断信号,停止服务")
|
|
break
|
|
except Exception as e:
|
|
logger.error(f"服务运行异常: {e}")
|
|
logger.info(f"等待 {self.check_interval} 秒后继续...")
|
|
time.sleep(self.check_interval)
|
|
|
|
finally:
|
|
session.close()
|
|
logger.info("向量化服务停止")
|
|
|
|
def main():
|
|
"""主函数"""
|
|
# 检查环境
|
|
if not os.path.exists("vectorizer_service.log"):
|
|
with open("vectorizer_service.log", "w") as f:
|
|
f.write(f"向量化服务日志 - 启动时间: {datetime.now()}\n")
|
|
|
|
# 运行服务
|
|
service = VectorizerService()
|
|
service.run()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|