Initial commit: 国土空间规划课程智能体 v1.0

单容器 Docker 架构的国土空间规划课程智能问答系统,集成 FastAPI 后端与 Next.js 前端。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 09:40:18 +08:00
commit ddbb79b9f6
167 changed files with 44147 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
"""
文档处理服务(LangChain 1.0
"""
import os
import hashlib
from pathlib import Path
from typing import List, Dict, Any, Optional
from sqlalchemy.orm import Session
from langchain_core.documents import Document as LangChainDocument
from ..models.document import Document, DocumentChunk
from ..rag.vector_store import get_vector_store
from ..rag.document_loaders import DocumentLoaderFactory
from ..rag.text_splitters import get_text_splitter
class DocumentService:
"""文档处理服务"""
def __init__(self, db: Session):
self.db = db
self.vector_store = get_vector_store()
async def process_document(self, document_id: int) -> bool:
"""处理文档(使用LangChain 1.0"""
try:
document = self.db.query(Document).filter(Document.id == document_id).first()
if not document:
return False
# 1. 使用LangChain加载文档
documents = DocumentLoaderFactory.load_document(
file_path=document.file_path,
file_type=document.file_type,
metadata={
"document_id": document.id,
"knowledge_base_id": document.knowledge_base_id,
"title": document.title,
"filename": document.filename
}
)
# 2. 使用中文优化的文本分割器
text_splitter = get_text_splitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(documents)
# 3. 添加到向量存储
success = self.vector_store.add_documents(splits)
if success:
document.is_processed = True
self.db.commit()
return True
return False
except Exception as e:
print(f"处理文档失败: {str(e)}")
self.db.rollback()
return False
def search_documents(self, query: str, knowledge_base_ids: Optional[List[int]] = None, limit: int = 5) -> List[Dict[str, Any]]:
"""搜索文档(保留原有接口兼容性)"""
try:
# 构建过滤条件
filter_dict = None
if knowledge_base_ids:
filter_dict = {"knowledge_base_id": {"$in": knowledge_base_ids}}
# 使用LangChain Chroma进行搜索
results = self.vector_store.similarity_search_with_score(
query=query,
k=limit,
filter=filter_dict
)
# 格式化结果
search_results = []
for doc, distance in results:
metadata = doc.metadata if hasattr(doc, 'metadata') else {}
score = self._convert_distance_to_score(distance)
search_results.append({
"content": doc.page_content,
"metadata": metadata,
"score": score,
"distance": distance
})
return search_results
except Exception as e:
print(f"搜索文档失败: {str(e)}")
return []
def _convert_distance_to_score(self, distance: float) -> float:
"""将ChromaDB的distance转换为0-1范围的相似度分数"""
import math
# 内积距离(负值)
if distance < 0:
return (1 + distance) / 2
# 大距离使用对数缩放
if distance > 100:
return 1 / (1 + math.log(distance))
# 标准距离转换
return 1 / (1 + distance)
def get_document_chunks(self, document_id: int) -> List[DocumentChunk]:
"""获取文档的所有块"""
return self.db.query(DocumentChunk).filter(
DocumentChunk.document_id == document_id
).order_by(DocumentChunk.chunk_index).all()
def delete_document_chunks(self, document_id: int) -> bool:
"""删除文档的所有块"""
try:
self.db.query(DocumentChunk).filter(
DocumentChunk.document_id == document_id
).delete()
self.db.commit()
return True
except Exception as e:
print(f"删除文档块失败: {str(e)}")
self.db.rollback()
return False