Files
projectAIpopular/router_system/config.py
T

119 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""配置加载:优先 YAML(若安装了 pyyaml),否则回退 JSON。
设计原则:router_system 核心零依赖,因此 pyyaml 是"可选"的。
默认 config/config.yaml 存在;若 pyyaml 不可用,可提供同名 .json。
"""
from __future__ import annotations
import json
import os
from pathlib import Path
from typing import Any, Dict, Optional
DEFAULT_CONFIG_PATH = Path(__file__).resolve().parent.parent / "config" / "config.yaml"
_DEFAULTS: Dict[str, Any] = {
"system": {"name": "multi-expert-router", "version": "0.1.0"},
"router": {
"low_confidence_threshold": 0.60, # 分类置信度低于此值 -> 直接走最后处理者
"judge_fallback_threshold": 0.70, # Judge 质量分低于此值 -> 升级最后处理者
"default_temperature": 0.2,
},
"execution": {
"mode": "rule", # ruleL0 零参数)| hybrid
"planner": "rule", # rule | hf
"expert_backend": "rule", # rule(规则执行器)| hf | api
"model_level": "L0", # L0 | L1 | L2
"max_plan_depth": 3,
},
"classifier": {"type": "rule", "model": "Qwen/Qwen3-0.6B", "confidence_floor": 0.55},
"domains": ["code", "math", "legal", "medical", "finance", "life", "education", "general"],
# 两级路由:大领域分组(用户接口指定 group → 组内路由模型 → 组内专业小模型)
# 组内路由模型只识别本组领域,体积约为统一路由模型的 1/4
"domain_groups": {
"tech": ["code", "math"],
"professional": ["legal", "medical", "finance"],
"lifestyle": ["life", "education"],
"general": ["general"],
},
"experts": {
"code": {"type": "mock", "model": "Qwen/Qwen2.5-Coder-7B-Instruct"},
"math": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"},
"legal": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"},
"medical": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"},
"finance": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"},
"life": {"type": "mock", "model": "Qwen/Qwen3-1.7B-Instruct"},
"education": {"type": "mock", "model": "Qwen/Qwen3-1.7B-Instruct"},
"general": {"type": "mock", "model": "Qwen/Qwen3-1.7B-Instruct"},
},
"fallback": {
"type": "mock",
"model": "deepseek-v4-flash",
"base_url": "https://api.deepseek.com",
"api_key_env": "DEEPSEEK_API_KEY",
},
"judge": {"type": "rule", "model": "Qwen/Qwen3-1.7B-Instruct"},
"cache": {
"enabled": True,
"semantic_enabled": True,
"similarity_threshold": 0.88,
"promote_frequency": 5,
},
}
def load_defaults() -> Dict[str, Any]:
return _DEFAULTS
def _try_load_yaml(path: Path) -> Optional[Dict[str, Any]]:
try:
import yaml # type: ignore
except ImportError:
return None
try:
with open(path, "r", encoding="utf-8") as f:
data = yaml.safe_load(f)
return data if isinstance(data, dict) else None
except Exception:
return None
def _try_load_json(path: Path) -> Optional[Dict[str, Any]]:
json_path = path.with_suffix(".json")
if not json_path.exists():
return None
try:
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
return data if isinstance(data, dict) else None
except Exception:
return None
def _merge_defaults(data: Dict[str, Any]) -> Dict[str, Any]:
"""将用户配置与内置默认配置做一层合并(用户优先)。"""
merged = dict(_DEFAULTS)
for k, v in data.items():
if isinstance(v, dict) and isinstance(merged.get(k), dict):
merged[k] = {**merged[k], **v}
else:
merged[k] = v
return merged
def load_config(path: Optional[Path | str] = None) -> Dict[str, Any]:
"""加载配置,返回 dict。文件不存在或解析失败时返回内置默认配置。"""
cfg_path = Path(path) if path else DEFAULT_CONFIG_PATH
if cfg_path.exists():
data = _try_load_yaml(cfg_path) or _try_load_json(cfg_path)
if data is not None:
return _merge_defaults(data)
return dict(_DEFAULTS)
def get_api_key(cfg: Dict[str, Any]) -> Optional[str]:
"""从环境变量读取 API Key(用于 api 类型后端)。"""
env_name = cfg.get("api_key_env") or "API_KEY"
return os.environ.get(env_name) or None