diff --git a/.env.template b/.env.template index e70ed3d6..7e07ca3c 100644 --- a/.env.template +++ b/.env.template @@ -5,6 +5,8 @@ RUN_EVENTS_STREAM_TTL_SECONDS=7200 RUN_CANCEL_KEY_TTL_SECONDS=1800 LANGGRAPH_CHECKPOINTER_BACKEND=postgres VITE_USE_RUNS_API=false +YUXI_ENABLE_LIGHTRAG=true +YUXI_ENABLE_KNOWLEDGE_GRAPH=true # region model_provider SILICONFLOW_API_KEY= # 推荐使用硅基流动免费服务 https://cloud.siliconflow.cn/i/Eo5yTHGJ diff --git a/docs/latest/advanced/deployment.md b/docs/latest/advanced/deployment.md index 98343435..3c64d368 100644 --- a/docs/latest/advanced/deployment.md +++ b/docs/latest/advanced/deployment.md @@ -30,6 +30,11 @@ cp .env.template .env.prod - `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY`: 修改默认密钥 - `SILICONFLOW_API_KEY` 等模型密钥 +如果希望以最小资源模式运行(禁用 LightRAG 和知识图谱),可额外设置: + +- `YUXI_ENABLE_LIGHTRAG=false` +- `YUXI_ENABLE_KNOWLEDGE_GRAPH=false` + ### 2. 启动服务 使用 `docker-compose.prod.yml` 文件启动生产环境: @@ -42,6 +47,13 @@ docker compose -f docker-compose.prod.yml up -d --build docker compose -f docker-compose.prod.yml --profile all up -d --build ``` +最小资源模式(不启动 Neo4j 图数据库)可使用: + +```bash +docker compose -f docker-compose.prod.yml up -d --build \ + api worker web postgres redis etcd minio milvus +``` + ### 3. 验证部署 - **Web 访问**: `http://localhost` (直接通过 80 端口访问,无需 :5173) diff --git a/server/routers/graph_router.py b/server/routers/graph_router.py index ccbad5d8..63ea94e8 100644 --- a/server/routers/graph_router.py +++ b/server/routers/graph_router.py @@ -13,6 +13,14 @@ from src.utils.logging_config import logger graph = APIRouter(prefix="/graph", tags=["graph"]) +def _is_graph_feature_enabled() -> bool: + return bool(getattr(graph_base, "feature_enabled", True)) + + +def _graph_disabled_detail() -> str: + return str(getattr(graph_base, "reason", "知识图谱功能已禁用")) + + # ============================================================================= # === 统一图谱接口 (Unified Graph API) === # ============================================================================= @@ -28,6 +36,9 @@ async def _get_graph_adapter(db_id: str) -> GraphAdapter: Returns: GraphAdapter: 对应的图谱适配器实例 """ + if not _is_graph_feature_enabled(): + raise HTTPException(status_code=503, detail=_graph_disabled_detail()) + # 检查图数据库服务状态 (仅对 Upload 类型需要) if not graph_base.is_running(): # 先尝试检测图谱类型,如果是不需要 graph_base 的类型则允许 @@ -58,6 +69,9 @@ async def get_graphs(current_user: User = Depends(get_admin_user)): 包含所有图谱信息的列表 (包括 Neo4j 和 LightRAG),以及每个类型的 capability 信息 """ try: + if not _is_graph_feature_enabled(): + return {"success": True, "data": []} + graphs = [] # 1. 获取默认 Neo4j 图谱信息 (Upload 类型) @@ -235,6 +249,9 @@ async def get_neo4j_node( async def get_neo4j_info(current_user: User = Depends(get_admin_user)): """获取Neo4j图数据库信息""" try: + if not _is_graph_feature_enabled(): + raise HTTPException(status_code=503, detail=_graph_disabled_detail()) + graph_info = graph_base.get_graph_info() if graph_info is None: raise HTTPException(status_code=400, detail="图数据库获取出错") @@ -248,6 +265,9 @@ async def get_neo4j_info(current_user: User = Depends(get_admin_user)): async def index_neo4j_entities(data: dict = Body(default={}), current_user: User = Depends(get_admin_user)): """为Neo4j图谱节点添加嵌入向量索引""" try: + if not _is_graph_feature_enabled(): + raise HTTPException(status_code=503, detail=_graph_disabled_detail()) + if not graph_base.is_running(): raise HTTPException(status_code=400, detail="图数据库未启动") @@ -275,6 +295,9 @@ async def add_neo4j_entities( ): """通过JSONL文件添加图谱实体到Neo4j(只接受 MinIO URL)""" try: + if not _is_graph_feature_enabled(): + raise HTTPException(status_code=503, detail=_graph_disabled_detail()) + # 服务层会验证 URL 并从 MinIO 下载文件 await graph_base.jsonl_file_add_entity(file_path, kgdb_name, embed_model_name, batch_size) return {"success": True, "message": "实体添加成功", "status": "success"} diff --git a/server/utils/lifespan.py b/server/utils/lifespan.py index 657e71bd..42ff8e1d 100644 --- a/server/utils/lifespan.py +++ b/server/utils/lifespan.py @@ -4,7 +4,6 @@ from fastapi import FastAPI from src.services.task_service import tasker from src.services.mcp_service import init_mcp_servers -from src.services.skill_service import init_skills_cache from src.services.run_queue_service import close_queue_clients, get_redis_client from src.storage.postgres.manager import pg_manager from src.knowledge import knowledge_base diff --git a/src/agents/common/tools.py b/src/agents/common/tools.py index b891644a..ef07870e 100644 --- a/src/agents/common/tools.py +++ b/src/agents/common/tools.py @@ -342,12 +342,10 @@ def gen_tool_info(tools) -> list[dict[str, Any]]: def get_buildin_tools() -> list: """注册静态工具""" - static_tools = [ - query_knowledge_graph, - get_approved_user_goal, - calculator, - text_to_img_demo, - ] + static_tools = [get_approved_user_goal, calculator, text_to_img_demo] + + if bool(getattr(graph_base, "feature_enabled", True)): + static_tools.insert(0, query_knowledge_graph) # subagents 工具 from .subagents import calc_agent_tool diff --git a/src/knowledge/__init__.py b/src/knowledge/__init__.py index 8fec1777..7dee53ad 100644 --- a/src/knowledge/__init__.py +++ b/src/knowledge/__init__.py @@ -3,24 +3,49 @@ import os from ..config import config from .factory import KnowledgeBaseFactory from .implementations.dify import DifyKB -from .implementations.lightrag import LightRagKB from .implementations.milvus import MilvusKB from .manager import KnowledgeBaseManager +from .services.disabled_graph_service import DisabledGraphService from .services.upload_graph_service import UploadGraphService +from src.utils import logger + + +def _env_enabled(name: str, default: bool = True) -> bool: + value = os.getenv(name) + if value is None: + return default + return value.strip().lower() in {"1", "true", "yes", "on"} + + +LIGHTRAG_ENABLED = _env_enabled("YUXI_ENABLE_LIGHTRAG", default=True) +KNOWLEDGE_GRAPH_ENABLED = _env_enabled("YUXI_ENABLE_KNOWLEDGE_GRAPH", default=True) # 注册知识库类型 KnowledgeBaseFactory.register("milvus", MilvusKB, {"description": "基于 Milvus 的生产级向量知识库,适合高性能部署"}) -KnowledgeBaseFactory.register("lightrag", LightRagKB, {"description": "基于图检索的知识库,支持实体关系构建和复杂查询"}) KnowledgeBaseFactory.register("dify", DifyKB, {"description": "连接 Dify Dataset 的只读检索知识库"}) +if LIGHTRAG_ENABLED: + from .implementations.lightrag import LightRagKB + + KnowledgeBaseFactory.register("lightrag", LightRagKB, {"description": "基于图检索的知识库,支持实体关系构建和复杂查询"}) +else: + logger.info("LightRAG feature disabled by YUXI_ENABLE_LIGHTRAG=false") + # 创建知识库管理器 work_dir = os.path.join(config.save_dir, "knowledge_base_data") knowledge_base = KnowledgeBaseManager(work_dir) # 创建图数据库实例 -graph_base = UploadGraphService() +if KNOWLEDGE_GRAPH_ENABLED: + try: + graph_base = UploadGraphService() + except Exception as e: + logger.warning(f"Knowledge graph init failed, fallback to disabled mode: {e}") + graph_base = DisabledGraphService(reason=f"知识图谱服务不可用: {e}") +else: + graph_base = DisabledGraphService(reason="知识图谱功能已禁用(YUXI_ENABLE_KNOWLEDGE_GRAPH=false)") # 向后兼容:让 GraphDatabase 指向 UploadGraphService -GraphDatabase = UploadGraphService +GraphDatabase = UploadGraphService if KNOWLEDGE_GRAPH_ENABLED else DisabledGraphService __all__ = ["GraphDatabase", "UploadGraphService", "knowledge_base", "graph_base"] diff --git a/src/knowledge/manager.py b/src/knowledge/manager.py index a9dc4fee..02d26217 100644 --- a/src/knowledge/manager.py +++ b/src/knowledge/manager.py @@ -55,6 +55,9 @@ class KnowledgeBaseManager: kb_types_in_use = set() for row in rows: kb_type = row.kb_type or "lightrag" + if not KnowledgeBaseFactory.is_type_supported(kb_type): + logger.warning(f"[InitializeKB] 跳过未启用的知识库类型: {kb_type}") + continue kb_types_in_use.add(kb_type) logger.info(f"[InitializeKB] 发现 {len(kb_types_in_use)} 种知识库类型: {kb_types_in_use}") @@ -179,6 +182,9 @@ class KnowledgeBaseManager: metadata_reloaded_types: set[str] = set() for row in rows: kb_type = row.kb_type or "lightrag" + if not KnowledgeBaseFactory.is_type_supported(kb_type): + logger.warning(f"Skip database due to unsupported kb_type: db_id={row.db_id}, kb_type={kb_type}") + continue kb_instance = self._get_or_create_kb_instance(kb_type) db_info = kb_instance.get_database_info(row.db_id) if not db_info and kb_type not in metadata_reloaded_types: diff --git a/src/knowledge/services/disabled_graph_service.py b/src/knowledge/services/disabled_graph_service.py new file mode 100644 index 00000000..5f21bbf7 --- /dev/null +++ b/src/knowledge/services/disabled_graph_service.py @@ -0,0 +1,30 @@ +class DisabledGraphService: + """禁用态图谱服务,占位实现。""" + + feature_enabled = False + + def __init__(self, reason: str = "知识图谱功能已禁用"): + self.reason = reason + + def start(self): + return None + + def close(self): + return None + + def is_running(self): + return False + + def get_graph_info(self, graph_name="neo4j"): + return None + + def query_node(self, keyword, threshold=0.9, kgdb_name="neo4j", hops=2, max_entities=8, return_format="graph", **kwargs): + if return_format == "triples": + return {"triples": [], "message": self.reason} + return {"nodes": [], "edges": [], "message": self.reason} + + async def add_embedding_to_nodes(self, node_names=None, kgdb_name="neo4j", batch_size=None): + raise RuntimeError(self.reason) + + async def jsonl_file_add_entity(self, file_path, kgdb_name="neo4j", embed_model_name=None, batch_size=None): + raise RuntimeError(self.reason)