init: RAG 知识库服务初始提交
- 后端 API(Flask + Gunicorn) - RAG 引擎(混合检索 + 云端 Reranker + 引用溯源) - 文档解析(MinerU + 多格式支持) - Docker 生产部署配置 - 排除前端项目、敏感配置、模型文件
This commit is contained in:
260
api/__init__.py
Normal file
260
api/__init__.py
Normal 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")
|
||||
Reference in New Issue
Block a user