refactor: 优化环境变量获取逻辑,确保默认值处理

- 在多个文件中更新环境变量的获取方式,使用 `or` 语法简化代码,确保在未设置环境变量时使用默认值。
- 移除 `.env.template` 中与 LightRAG 相关的环境变量配置,简化配置文件。
- 更新 `pyproject.toml` 中的项目描述,提供更清晰的项目定位。
- 在文档中增加对图片上传响应格式的详细说明,提升用户理解。
This commit is contained in:
Wenjie Zhang 2025-11-12 19:53:56 +08:00
parent 628b37c73c
commit d999613f41
18 changed files with 49 additions and 45 deletions

View File

@ -26,10 +26,6 @@ MYSQL_DATABASE=database_name
MYSQL_PORT=3306 MYSQL_PORT=3306
MYSQL_CHARSET=utf8mb4 MYSQL_CHARSET=utf8mb4
# region lightrag
LIGHTRAG_LLM_PROVIDER=
LIGHTRAG_LLM_NAME=
# endregion lightrag
# region neo4j # region neo4j

View File

@ -148,4 +148,20 @@ MYSQL_CHARSET=utf8mb4
智能体会自动识别多模态消息并将其传递给支持图片的模型。如果模型不支持图片,会自动忽略图片内容,只处理文本部分。系统会将图片转换为符合模型要求的格式(通常是 base64 编码的 JPEG 或 PNG确保与主流多模态模型兼容。 智能体会自动识别多模态消息并将其传递给支持图片的模型。如果模型不支持图片,会自动忽略图片内容,只处理文本部分。系统会将图片转换为符合模型要求的格式(通常是 base64 编码的 JPEG 或 PNG确保与主流多模态模型兼容。
目前仅支持上传单个图片,图片直接以 base64 存储在数据库 目前仅支持上传单个图片,图片以 base64 编码形式存储在数据库。系统会自动处理图片的格式转换和压缩,并生成缩略图以优化性能。
### 图片上传响应格式
```json
{
"success": true,
"image_content": "<base64编码的原始图片数据>",
"thumbnail_content": "<base64编码的缩略图数据>",
"width": 1024,
"height": 768,
"format": "JPEG",
"mime_type": "image/jpeg"
}
```
系统会将图片信息与用户查询一同传递给支持多模态的模型,并自动适配模型要求的格式。

View File

@ -26,10 +26,6 @@ LightRAG 知识库可在知识库详情中可视化,但不支持在侧边栏
在 Neo4j 的检索中可以看到,实际上 LightRAG 的节点和边依然是和知识图谱本身构建在了同一个 Neo4j 数据库中,但是使用了特殊的 tag 做区分。这点在后面介绍知识图谱的时候也会额外说明。 在 Neo4j 的检索中可以看到,实际上 LightRAG 的节点和边依然是和知识图谱本身构建在了同一个 Neo4j 数据库中,但是使用了特殊的 tag 做区分。这点在后面介绍知识图谱的时候也会额外说明。
系统默认使用 `siliconflow``Qwen/Qwen3-30B-A3B-Instruct-2507` 模型进行图谱构建。可通过环境变量自定义图谱构建模型:
<<< @/../.env.template#lightrag{bash}
## 文档管理 ## 文档管理

View File

@ -104,14 +104,14 @@ DEFAULT_CHAT_MODEL_PROVIDERS: dict[str, ChatModelProvider] = {
} }
``` ```
### 3. 配置环境变量 ### 2. 配置环境变量
`.env` 文件中添加对应的环境变量: `.env` 文件中添加对应的环境变量:
```env ```env
CUSTOM_API_KEY_ENV_NAME=your_api_key_here CUSTOM_API_KEY_ENV_NAME=your_api_key_here
``` ```
### 4. 重新部署 ### 3. 重新部署
```bash ```bash
docker compose restart api-dev docker compose restart api-dev

View File

@ -1,7 +1,7 @@
[project] [project]
name = "yuxi-know" name = "yuxi-know"
version = "0.4.0.dev" version = "0.4.0.dev"
description = "Add your description here" description = "基于大模型的智能知识库与知识图谱智能体开发平台,融合了 RAG 技术与知识图谱技术,基于 LangGraph v1 + Vue.js + FastAPI + LightRAG 架构构建"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [

View File

@ -19,8 +19,8 @@ def rename_and_resolve_duplicates():
Connects to Milvus, renames collections from 'kb_kb_' to 'kb_', Connects to Milvus, renames collections from 'kb_kb_' to 'kb_',
and resolves duplicates by keeping the collection with more rows. and resolves duplicates by keeping the collection with more rows.
""" """
milvus_uri = os.getenv("MILVUS_URI", "http://localhost:19530") milvus_uri = os.getenv("MILVUS_URI") or "http://localhost:19530"
milvus_token = os.getenv("MILVUS_TOKEN", "") milvus_token = os.getenv("MILVUS_TOKEN") or ""
connection_alias = "rename_script" connection_alias = "rename_script"
try: try:

View File

@ -22,7 +22,7 @@ def load_chat_model(fully_specified_name: str, **kwargs) -> BaseChatModel:
env_var = model_info.env env_var = model_info.env
api_key = os.getenv(env_var, env_var) api_key = os.getenv(env_var) or env_var
base_url = get_docker_safe_url(model_info.base_url) base_url = get_docker_safe_url(model_info.base_url)

View File

@ -30,9 +30,9 @@ def get_connection_manager() -> MySQLConnectionManager:
"user": os.getenv("MYSQL_USER"), "user": os.getenv("MYSQL_USER"),
"password": os.getenv("MYSQL_PASSWORD"), "password": os.getenv("MYSQL_PASSWORD"),
"database": os.getenv("MYSQL_DATABASE"), "database": os.getenv("MYSQL_DATABASE"),
"port": int(os.getenv("MYSQL_PORT", "3306")), "port": int(os.getenv("MYSQL_PORT") or "3306"),
"charset": "utf8mb4", "charset": "utf8mb4",
"description": os.getenv("MYSQL_DATABASE_DESCRIPTION", "默认 MySQL 数据库"), "description": os.getenv("MYSQL_DATABASE_DESCRIPTION") or "默认 MySQL 数据库",
} }
# 验证配置完整性 # 验证配置完整性
required_keys = ["host", "user", "password", "database"] required_keys = ["host", "user", "password", "database"]

View File

@ -120,7 +120,7 @@ class Config(BaseModel):
def _setup_paths(self): def _setup_paths(self):
"""设置配置文件路径""" """设置配置文件路径"""
self.save_dir = os.getenv("SAVE_DIR", self.save_dir) self.save_dir = os.getenv("SAVE_DIR") or self.save_dir
self._config_file = Path(self.save_dir) / "config" / "base.toml" self._config_file = Path(self.save_dir) / "config" / "base.toml"
self._config_file.parent.mkdir(parents=True, exist_ok=True) self._config_file.parent.mkdir(parents=True, exist_ok=True)
@ -169,7 +169,7 @@ class Config(BaseModel):
def _handle_environment(self): def _handle_environment(self):
"""处理环境变量和运行时状态""" """处理环境变量和运行时状态"""
# 处理模型目录 # 处理模型目录
self.model_dir = os.environ.get("MODEL_DIR", self.model_dir) self.model_dir = os.environ.get("MODEL_DIR") or self.model_dir
if self.model_dir: if self.model_dir:
if os.path.exists(self.model_dir): if os.path.exists(self.model_dir):
logger.debug(f"Model directory ({self.model_dir}) contains: {os.listdir(self.model_dir)}") logger.debug(f"Model directory ({self.model_dir}) contains: {os.listdir(self.model_dir)}")

View File

@ -8,15 +8,13 @@ from lightrag.utils import EmbeddingFunc, setup_logger
from neo4j import GraphDatabase from neo4j import GraphDatabase
from pymilvus import connections, utility from pymilvus import connections, utility
from src import config
from src.knowledge.base import KnowledgeBase from src.knowledge.base import KnowledgeBase
from src.knowledge.indexing import process_file_to_markdown, process_url_to_markdown from src.knowledge.indexing import process_file_to_markdown, process_url_to_markdown
from src.knowledge.utils.kb_utils import get_embedding_config, prepare_item_metadata from src.knowledge.utils.kb_utils import get_embedding_config, prepare_item_metadata
from src.utils import hashstr, logger from src.utils import hashstr, logger
from src.utils.datetime_utils import shanghai_now from src.utils.datetime_utils import shanghai_now
LIGHTRAG_LLM_PROVIDER = os.getenv("LIGHTRAG_LLM_PROVIDER", "siliconflow")
LIGHTRAG_LLM_NAME = os.getenv("LIGHTRAG_LLM_NAME", "zai-org/GLM-4.5-Air")
class LightRagKB(KnowledgeBase): class LightRagKB(KnowledgeBase):
"""基于 LightRAG 的知识库实现""" """基于 LightRAG 的知识库实现"""
@ -53,8 +51,8 @@ class LightRagKB(KnowledgeBase):
"""删除数据库同时清除Milvus和Neo4j中的数据""" """删除数据库同时清除Milvus和Neo4j中的数据"""
# Drop Milvus collection # Drop Milvus collection
try: try:
milvus_uri = os.getenv("MILVUS_URI", "http://localhost:19530") milvus_uri = os.getenv("MILVUS_URI") or "http://localhost:19530"
milvus_token = os.getenv("MILVUS_TOKEN", "") milvus_token = os.getenv("MILVUS_TOKEN") or ""
connection_alias = f"lightrag_{hashstr(db_id, 6)}" connection_alias = f"lightrag_{hashstr(db_id, 6)}"
connections.connect(alias=connection_alias, uri=milvus_uri, token=milvus_token) connections.connect(alias=connection_alias, uri=milvus_uri, token=milvus_token)
@ -73,9 +71,9 @@ class LightRagKB(KnowledgeBase):
logger.error(f"Failed to drop Milvus collection {db_id}: {e}") logger.error(f"Failed to drop Milvus collection {db_id}: {e}")
# Delete Neo4j data # Delete Neo4j data
neo4j_uri = os.getenv("NEO4J_URI", "bolt://localhost:7687") neo4j_uri = os.getenv("NEO4J_URI") or "bolt://localhost:7687"
neo4j_username = os.getenv("NEO4J_USERNAME", "neo4j") neo4j_username = os.getenv("NEO4J_USERNAME") or "neo4j"
neo4j_password = os.getenv("NEO4J_PASSWORD", "0123456789") neo4j_password = os.getenv("NEO4J_PASSWORD") or "0123456789"
try: try:
driver = GraphDatabase.driver(neo4j_uri, auth=(neo4j_username, neo4j_password)) driver = GraphDatabase.driver(neo4j_uri, auth=(neo4j_username, neo4j_password))
@ -118,7 +116,7 @@ class LightRagKB(KnowledgeBase):
if isinstance(metadata.get("language"), str) and metadata.get("language"): if isinstance(metadata.get("language"), str) and metadata.get("language"):
addon_params.setdefault("language", metadata.get("language")) addon_params.setdefault("language", metadata.get("language"))
# 默认语言从环境变量读取,默认 English # 默认语言从环境变量读取,默认 English
addon_params.setdefault("language", os.getenv("SUMMARY_LANGUAGE", "English")) addon_params.setdefault("language", os.getenv("SUMMARY_LANGUAGE") or "English")
# 创建工作目录 # 创建工作目录
working_dir = os.path.join(self.work_dir, db_id) working_dir = os.path.join(self.work_dir, db_id)
@ -181,10 +179,8 @@ class LightRagKB(KnowledgeBase):
model_spec = f"{llm_info['provider']}/{llm_info['model_name']}" model_spec = f"{llm_info['provider']}/{llm_info['model_name']}"
logger.info(f"Using user-selected LLM: {model_spec}") logger.info(f"Using user-selected LLM: {model_spec}")
else: else:
provider = LIGHTRAG_LLM_PROVIDER model_spec = config.default_model
model_name = LIGHTRAG_LLM_NAME logger.info(f"Using default LLM from environment: {model_spec}")
model_spec = f"{provider}/{model_name}"
logger.info(f"Using default LLM from environment: {provider}/{model_name}")
model = select_model(model_spec=model_spec) model = select_model(model_spec=model_spec)

View File

@ -40,9 +40,9 @@ class MilvusKB(KnowledgeBase):
# Milvus 配置 # Milvus 配置
# self.milvus_host = kwargs.get('milvus_host', os.getenv('MILVUS_HOST', 'localhost')) # self.milvus_host = kwargs.get('milvus_host', os.getenv('MILVUS_HOST', 'localhost'))
# self.milvus_port = kwargs.get('milvus_port', int(os.getenv('MILVUS_PORT', '19530'))) # self.milvus_port = kwargs.get('milvus_port', int(os.getenv('MILVUS_PORT', '19530')))
self.milvus_token = kwargs.get("milvus_token", os.getenv("MILVUS_TOKEN", "")) self.milvus_token = kwargs.get("milvus_token", os.getenv("MILVUS_TOKEN") or "")
self.milvus_uri = kwargs.get("milvus_uri", os.getenv("MILVUS_URI", "http://localhost:19530")) self.milvus_uri = kwargs.get("milvus_uri", os.getenv("MILVUS_URI") or "http://localhost:19530")
self.milvus_db = kwargs.get("milvus_db", "yuxi_know") self.milvus_db = kwargs.get("milvus_db") or "yuxi_know"
# 连接名称 # 连接名称
self.connection_alias = f"milvus_{hashstr(work_dir, 6)}" self.connection_alias = f"milvus_{hashstr(work_dir, 6)}"

View File

@ -214,13 +214,13 @@ def get_embedding_config(embed_info: dict) -> dict:
if hasattr(embed_info, "name"): if hasattr(embed_info, "name"):
# EmbedModelInfo 对象 # EmbedModelInfo 对象
config_dict["model"] = embed_info.name config_dict["model"] = embed_info.name
config_dict["api_key"] = os.getenv(embed_info.api_key, embed_info.api_key) config_dict["api_key"] = os.getenv(embed_info.api_key) or embed_info.api_key
config_dict["base_url"] = embed_info.base_url config_dict["base_url"] = embed_info.base_url
config_dict["dimension"] = embed_info.dimension config_dict["dimension"] = embed_info.dimension
else: else:
# 字典形式 # 字典形式
config_dict["model"] = embed_info["name"] config_dict["model"] = embed_info["name"]
config_dict["api_key"] = os.getenv(embed_info["api_key"], embed_info["api_key"]) config_dict["api_key"] = os.getenv(embed_info["api_key"]) or embed_info["api_key"]
config_dict["base_url"] = embed_info["base_url"] config_dict["base_url"] = embed_info["base_url"]
config_dict["dimension"] = embed_info.get("dimension", 1024) config_dict["dimension"] = embed_info.get("dimension", 1024)
else: else:

View File

@ -125,6 +125,6 @@ def get_reranker(model_id, **kwargs):
model_info = config.reranker_names[model_id] model_info = config.reranker_names[model_id]
base_url = model_info.base_url base_url = model_info.base_url
api_key = os.getenv(model_info.api_key, model_info.api_key) api_key = os.getenv(model_info.api_key) or model_info.api_key
assert api_key, f"{model_info.name} api_key is required" assert api_key, f"{model_info.name} api_key is required"
return OnlineReranker(model_name=model_info.name, api_key=api_key, base_url=base_url, **kwargs) return OnlineReranker(model_name=model_info.name, api_key=api_key, base_url=base_url, **kwargs)

View File

@ -18,7 +18,7 @@ class MinerUParser(BaseDocumentProcessor):
"""MinerU 文档解析器 - 使用 HTTP API 进行文档理解和解析""" """MinerU 文档解析器 - 使用 HTTP API 进行文档理解和解析"""
def __init__(self, server_url: str | None = None): def __init__(self, server_url: str | None = None):
self.server_url = server_url or os.getenv("MINERU_API_URI", "http://localhost:30001") self.server_url = server_url or os.getenv("MINERU_API_URI") or "http://localhost:30001"
self.parse_endpoint = f"{self.server_url}/file_parse" self.parse_endpoint = f"{self.server_url}/file_parse"
def get_service_name(self) -> str: def get_service_name(self) -> str:

View File

@ -20,7 +20,7 @@ class PaddleXDocumentParser(BaseDocumentProcessor):
"""PaddleX 文档解析器 - 使用 PP-StructureV3 进行版面解析""" """PaddleX 文档解析器 - 使用 PP-StructureV3 进行版面解析"""
def __init__(self, server_url: str | None = None): def __init__(self, server_url: str | None = None):
self.server_url = server_url or os.getenv("PADDLEX_URI", "http://localhost:8080") self.server_url = server_url or os.getenv("PADDLEX_URI") or "http://localhost:8080"
self.base_url = self.server_url.rstrip("/") self.base_url = self.server_url.rstrip("/")
self.endpoint = f"{self.base_url}/layout-parsing" self.endpoint = f"{self.base_url}/layout-parsing"

View File

@ -37,9 +37,9 @@ class MinIOClient:
def __init__(self): def __init__(self):
"""初始化 MinIO 客户端""" """初始化 MinIO 客户端"""
self.endpoint = os.getenv("MINIO_URI", "http://milvus-minio:9000") self.endpoint = os.getenv("MINIO_URI") or "http://milvus-minio:9000"
self.access_key = os.getenv("MINIO_ACCESS_KEY", "minioadmin") self.access_key = os.getenv("MINIO_ACCESS_KEY") or "minioadmin"
self.secret_key = os.getenv("MINIO_SECRET_KEY", "minioadmin") self.secret_key = os.getenv("MINIO_SECRET_KEY") or "minioadmin"
self._client = None self._client = None
# 设置公开访问端点 # 设置公开访问端点

View File

@ -4,7 +4,7 @@ from loguru import logger as loguru_logger
from src.utils.datetime_utils import shanghai_now from src.utils.datetime_utils import shanghai_now
SAVE_DIR = os.getenv("SAVE_DIR", "saves") SAVE_DIR = os.getenv("SAVE_DIR") or "saves"
DATETIME = shanghai_now().strftime("%Y-%m-%d") DATETIME = shanghai_now().strftime("%Y-%m-%d")
LOG_FILE = f"{SAVE_DIR}/logs/yuxi-{DATETIME}.log" LOG_FILE = f"{SAVE_DIR}/logs/yuxi-{DATETIME}.log"

View File

@ -1,6 +1,6 @@
{ {
"name": "yuxi-know-web", "name": "yuxi-know-web",
"version": "0.3.0.web", "version": "0.4.0.web",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",