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

260
api/__init__.py Normal file
View File

@@ -0,0 +1,260 @@
"""
API 路由层 — Flask 应用工厂
本模块实现 Flask 应用工厂模式,负责:
- 创建和配置 Flask 应用实例
- 初始化核心服务AgenticRAG、同步服务
- 注册所有 API Blueprint
- 配置前端静态文件路由
核心路由模块:
- chat_routes.py : 问答接口 (/chat, /rag, /rag/stream, /search)
- kb_routes.py : 向量库管理 (/collections, /documents/sync)
- document_routes.py: 文档管理 (/documents/upload, /documents/list)
- sync_routes.py : 同步服务 (/sync, /sync/status)
- image_routes.py : 图片服务 (/images/*)
- exam_pkg/api.py : 出题系统 (/exam/generate, /exam/grade)
架构说明:
- 会话管理、审计日志、反馈系统由后端服务负责
- 权限验证由后端网关完成Header 注入)
- RAG 服务无状态,不存储用户数据
Example:
>>> from api import create_app
>>> app = create_app()
>>> app.run(host='0.0.0.0', port=5001)
"""
import sys
import os
import logging
from typing import Optional
# 确保项目根目录在路径中
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if PROJECT_ROOT not in sys.path:
sys.path.insert(0, PROJECT_ROOT)
logger = logging.getLogger(__name__)
def create_app() -> 'Flask':
"""
Flask 应用工厂函数
创建并配置 Flask 应用实例,完成以下初始化步骤:
1. 创建 Flask 应用,配置 CORS
2. 初始化 Repository会话存储
3. 初始化核心服务AgenticRAG、同步服务
4. 注册所有 API Blueprint
5. 配置前端静态文件路由
6. 执行生产环境配置校验
Returns:
Flask: 配置完成的 Flask 应用实例
Example:
>>> app = create_app()
>>> app.run(host='0.0.0.0', port=5001)
"""
from flask import Flask, send_from_directory
from flask_cors import CORS
from config import ENABLE_SESSION, ENABLE_FEEDBACK, IS_PROD
# 静态文件目录(前端)
static_folder = os.path.join(PROJECT_ROOT, 'chat-ui')
app = Flask(__name__, static_folder=static_folder, static_url_path='')
CORS(app)
# ==================== Repository 依赖注入 ====================
# 会话存储开发环境用SQLite生产环境无状态
if ENABLE_SESSION:
from repositories.sqlite_session_repo import SQLiteSessionRepo
app.session_repo = SQLiteSessionRepo()
logger.info("会话存储: SQLite (开发环境)")
else:
from repositories.stateless_session_repo import StatelessSessionRepo
app.session_repo = StatelessSessionRepo()
logger.info("会话存储: 无状态 (生产环境)")
# ==================== 核心服务初始化 ====================
# Agentic RAG 引擎
try:
from core.agentic import AgenticRAG
from config import ENABLE_WEB_SEARCH
agentic_rag = AgenticRAG(
enable_web_search=ENABLE_WEB_SEARCH,
)
app.config['AGENTIC_RAG'] = agentic_rag
logger.info(f"Agentic RAG 引擎已初始化(网络搜索={'启用' if ENABLE_WEB_SEARCH else '关闭'}")
except Exception as e:
app.config['AGENTIC_RAG'] = None
logger.warning(f"Agentic RAG 初始化失败: {e}")
# 同步服务
try:
from knowledge.sync import KnowledgeSyncService
from config import DOCUMENTS_PATH
sync_service = KnowledgeSyncService(documents_path=DOCUMENTS_PATH)
app.config['SYNC_SERVICE'] = sync_service
logger.info("知识库同步服务已初始化")
except Exception as e:
app.config['SYNC_SERVICE'] = None
logger.warning(f"知识库同步服务未启用: {e}")
# 会话管理器(仅开发环境)
if ENABLE_SESSION:
try:
from services.session import SessionManager
session_manager = SessionManager()
app.config['SESSION_MANAGER'] = session_manager
logger.info("会话管理器已初始化")
except Exception as e:
app.config['SESSION_MANAGER'] = None
logger.warning(f"会话管理器初始化失败: {e}")
# ==================== 注册 Blueprint ====================
# 核心 API
from api.chat_routes import chat_bp
from api.kb_routes import kb_bp
from api.document_routes import document_bp
from api.sync_routes import sync_bp
app.register_blueprint(chat_bp)
app.register_blueprint(kb_bp)
app.register_blueprint(document_bp)
app.register_blueprint(sync_bp)
# 图片服务
from api.image_routes import image_bp
app.register_blueprint(image_bp)
# 健康检查
from api.auth_routes import auth_bp
app.register_blueprint(auth_bp)
# 会话管理(仅开发环境)
if ENABLE_SESSION:
try:
from api.session_routes import session_bp
app.register_blueprint(session_bp)
logger.info("会话管理 API 已启用")
except ImportError as e:
logger.info(f"会话管理 API 未加载: {e}")
# 审计日志(仅开发环境,依赖 session.db
if ENABLE_SESSION:
try:
from api.audit_routes import audit_bp
app.register_blueprint(audit_bp)
logger.info("审计日志 API 已启用")
except ImportError as e:
logger.info(f"审计日志 API 未加载: {e}")
# 反馈系统(开发和生产环境都启用)
if ENABLE_FEEDBACK:
try:
from api.feedback_routes import feedback_bp
app.register_blueprint(feedback_bp)
logger.info("反馈系统 API 已启用")
except ImportError as e:
logger.info(f"反馈系统 API 未加载: {e}")
# 出题系统(可选)
try:
from exam_pkg.api import exam_bp
app.register_blueprint(exam_bp, url_prefix='/exam')
logger.info("出题系统 API 已启用: /exam")
except ImportError as e:
logger.info(f"出题系统 API 未加载: {e}")
# ==================== 生产环境启动校验 ====================
if IS_PROD:
_validate_production_config()
# ==================== 前端静态文件路由 ====================
# 首页
@app.route('/')
def serve_index():
"""首页"""
return send_from_directory(static_folder, 'index.html')
# 静态文件(需要明确指定,避免与 API 路由冲突)
@app.route('/app.js')
def serve_app_js():
return send_from_directory(static_folder, 'app.js')
@app.route('/style.css')
def serve_style_css():
return send_from_directory(static_folder, 'style.css')
@app.route('/exam.html')
def serve_exam_html():
return send_from_directory(static_folder, 'exam.html')
@app.route('/exam.js')
def serve_exam_js():
return send_from_directory(static_folder, 'exam.js')
@app.route('/api-test.html')
def serve_api_test_html():
return send_from_directory(static_folder, 'api-test.html')
@app.route('/api-test.js')
def serve_api_test_js():
return send_from_directory(static_folder, 'api-test.js')
# ==================== 启动信息 ====================
_print_startup_info(app)
return app
def _validate_production_config() -> None:
"""
生产环境启动前配置校验
检查必要的配置项是否存在:
- DASHSCOPE_API_KEY: 大模型调用必需
Raises:
AssertionError: 缺少必需的配置项
"""
import os
from config import DASHSCOPE_API_KEY
# 检查环境变量或配置文件中的 API Key
has_key = os.getenv("DASHSCOPE_API_KEY") or os.environ.get("DASHSCOPE_API_KEY") or DASHSCOPE_API_KEY
assert has_key and has_key != "", \
"Missing DASHSCOPE_API_KEY in production environment"
logger.info("Configuration validated")
def _print_startup_info(app: 'Flask') -> None:
"""
打印启动信息摘要
在应用启动时输出注册的路由数量和主要端点列表,
便于运维人员确认服务状态。
Args:
app: Flask 应用实例
"""
route_count = len([rule for rule in app.url_map.iter_rules() if rule.endpoint != 'static'])
logger.info(f"应用初始化完成,共注册 {route_count} 个路由")
logger.info(" 问答接口: /chat, /rag, /rag/stream, /search")
logger.info(" 向量库: /collections, /collections/<name>")
logger.info(" 文档管理: /documents/upload, /documents/list, /documents/*")
logger.info(" 切片管理: /chunks/*")
logger.info(" 同步服务: /sync, /sync/status")
logger.info(" 图片服务: /images/*")
logger.info(" 健康检查: /health")