init: RAG 知识库服务初始提交

- 后端 API(Flask + Gunicorn)
- RAG 引擎(混合检索 + 云端 Reranker + 引用溯源)
- 文档解析(MinerU + 多格式支持)
- Docker 生产部署配置
- 排除前端项目、敏感配置、模型文件
This commit is contained in:
lacerate551
2026-06-04 17:35:27 +08:00
commit 100d1a06eb
158 changed files with 64534 additions and 0 deletions

View File

@@ -0,0 +1,361 @@
"""
文档版本查询模块(简化版)
保留核心的版本查询功能,删除未使用的生命周期管理功能。
功能:
1. 查询文档版本历史
2. 获取当前生效版本
3. 记录版本变更日志
使用方式:
from knowledge.document_versions import DocumentVersionQuery
query = DocumentVersionQuery()
# 获取版本历史
history = query.get_document_history("public_kb", "报销制度.pdf")
# 获取生效版本
active = query.get_active_version("public_kb", "报销制度.pdf")
"""
import logging
from enum import Enum
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, List, Dict
from data.db import get_connection
logger = logging.getLogger(__name__)
# ==================== 枚举与数据类 ====================
class DocumentStatus(Enum):
"""文档状态"""
DRAFT = "draft" # 草稿
ACTIVE = "active" # 生效中
DEPRECATED = "deprecated" # 已废止
SUPERSEDED = "superseded" # 被替代
@dataclass
class DocumentVersionInfo:
"""文档版本信息"""
document_id: str
collection: str
version: str
status: DocumentStatus
effective_date: Optional[str] = None
deprecated_date: Optional[str] = None
deprecated_reason: Optional[str] = None
change_summary: Optional[str] = None
supersedes: Optional[str] = None
created_at: Optional[str] = None
created_by: Optional[str] = None
chunk_count: int = 0
def to_dict(self) -> Dict:
"""转换为字典"""
return {
"document_id": self.document_id,
"collection": self.collection,
"version": self.version,
"status": self.status.value if isinstance(self.status, DocumentStatus) else self.status,
"effective_date": self.effective_date,
"deprecated_date": self.deprecated_date,
"deprecated_reason": self.deprecated_reason,
"change_summary": self.change_summary,
"supersedes": self.supersedes,
"created_at": self.created_at,
"created_by": self.created_by,
"chunk_count": self.chunk_count
}
# ==================== 文档版本查询 ====================
class DocumentVersionQuery:
"""文档版本查询(简化版)"""
def __init__(self):
"""初始化"""
pass
def get_document_history(
self,
collection: str,
document_id: str,
limit: int = 10
) -> List[DocumentVersionInfo]:
"""
获取文档版本历史
Args:
collection: 向量库名称
document_id: 文档ID
limit: 返回数量限制
Returns:
版本信息列表(按时间倒序)
"""
try:
with get_connection("knowledge") as conn:
cursor = conn.execute(
"""
SELECT
document_id, collection, version, status,
effective_date, deprecated_date, deprecated_reason,
change_summary, supersedes, created_at, created_by,
chunk_count
FROM document_versions
WHERE collection = ? AND document_id = ?
ORDER BY created_at DESC
LIMIT ?
""",
(collection, document_id, limit)
)
versions = []
for row in cursor.fetchall():
versions.append(DocumentVersionInfo(
document_id=row[0],
collection=row[1],
version=row[2],
status=DocumentStatus(row[3]) if row[3] else DocumentStatus.ACTIVE,
effective_date=row[4],
deprecated_date=row[5],
deprecated_reason=row[6],
change_summary=row[7],
supersedes=row[8],
created_at=row[9],
created_by=row[10],
chunk_count=row[11] or 0
))
return versions
except Exception as e:
logger.error(f"获取文档历史失败: {e}")
return []
def get_active_version(
self,
collection: str,
document_id: str
) -> Optional[DocumentVersionInfo]:
"""
获取当前生效版本
Args:
collection: 向量库名称
document_id: 文档ID
Returns:
生效版本信息,不存在则返回 None
"""
try:
with get_connection("knowledge") as conn:
cursor = conn.execute(
"""
SELECT
document_id, collection, version, status,
effective_date, deprecated_date, deprecated_reason,
change_summary, supersedes, created_at, created_by,
chunk_count
FROM document_versions
WHERE collection = ? AND document_id = ? AND status = 'active'
ORDER BY created_at DESC
LIMIT 1
""",
(collection, document_id)
)
row = cursor.fetchone()
if row:
return DocumentVersionInfo(
document_id=row[0],
collection=row[1],
version=row[2],
status=DocumentStatus(row[3]) if row[3] else DocumentStatus.ACTIVE,
effective_date=row[4],
deprecated_date=row[5],
deprecated_reason=row[6],
change_summary=row[7],
supersedes=row[8],
created_at=row[9],
created_by=row[10],
chunk_count=row[11] or 0
)
return None
except Exception as e:
logger.error(f"获取生效版本失败: {e}")
return None
def get_next_version(self, collection: str, document_id: str) -> str:
"""
获取下一个版本号
Args:
collection: 向量库名称
document_id: 文档ID
Returns:
版本号字符串,如 'v1', 'v2', 'v3'
"""
try:
with get_connection("knowledge") as conn:
cursor = conn.execute(
"""
SELECT version FROM document_versions
WHERE collection = ? AND document_id = ?
ORDER BY created_at DESC
LIMIT 1
""",
(collection, document_id)
)
row = cursor.fetchone()
if row and row[0]:
# 解析现有版本号
current = row[0]
if current.startswith('v') and current[1:].isdigit():
next_num = int(current[1:]) + 1
else:
# 非标准版本号从1开始
next_num = 1
else:
next_num = 1
return f"v{next_num}"
except Exception as e:
logger.warning(f"获取版本号失败: {e},使用默认 v1")
return "v1"
def create_version_record(
self,
collection: str,
document_id: str,
version: str = None,
status: str = "active",
change_summary: str = "",
supersedes: str = None,
created_by: str = "",
chunk_count: int = 0
):
"""
创建版本记录
Args:
collection: 向量库名称
document_id: 文档ID
version: 版本号(可选,不传则自动生成 v1, v2, v3...
status: 状态
change_summary: 变更摘要
supersedes: 替代的旧版本
created_by: 创建者
chunk_count: chunk数量
"""
# 自动生成版本号
if version is None:
version = self.get_next_version(collection, document_id)
try:
with get_connection("knowledge") as conn:
conn.execute(
"""
INSERT OR REPLACE INTO document_versions
(document_id, collection, version, status, effective_date,
change_summary, supersedes, created_at, created_by, chunk_count)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
document_id,
collection,
version,
status,
datetime.now().isoformat(),
change_summary,
supersedes,
datetime.now().isoformat(),
created_by,
chunk_count
)
)
conn.commit()
logger.info(f"创建版本记录: {collection}/{document_id} {version}")
except Exception as e:
logger.error(f"创建版本记录失败: {e}")
raise
def log_version_change(
self,
collection: str,
document_id: str,
change_type: str,
old_version: str = None,
new_version: str = None,
old_status: str = None,
new_status: str = None,
reason: str = "",
changed_by: str = ""
):
"""
记录版本变更日志
Args:
collection: 向量库名称
document_id: 文档ID
change_type: 变更类型update/deprecate/restore
old_version: 旧版本号
new_version: 新版本号
old_status: 旧状态
new_status: 新状态
reason: 变更原因
changed_by: 操作者
"""
try:
with get_connection("knowledge") as conn:
conn.execute(
"""
INSERT INTO version_change_logs
(document_id, collection, old_version, new_version,
old_status, new_status, change_type, reason, changed_by, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
document_id,
collection,
old_version,
new_version,
old_status,
new_status,
change_type,
reason,
changed_by,
datetime.now().isoformat()
)
)
conn.commit()
logger.info(f"记录版本变更: {collection}/{document_id} {change_type}")
except Exception as e:
logger.error(f"记录版本变更失败: {e}")
# ==================== 工厂函数 ====================
_version_query_instance = None
def get_version_query() -> DocumentVersionQuery:
"""获取文档版本查询实例(单例)"""
global _version_query_instance
if _version_query_instance is None:
_version_query_instance = DocumentVersionQuery()
return _version_query_instance