Files
CareerNexus/app/config.py
T
RoyirinRosmontis 3329c55e06 竞赛演示模式:首屏降噪与预置导览
- demo_mode 配置项(默认开):不弹初始设置,LLM/语义警告横幅降级为顶栏状态点
- 顶栏新增'开始演示'导览(驾驶舱→新岗位发现→Java演化→匹配诊断四步预置路线)
- 图谱默认降噪:场景/行业图层默认关、关系标签默认关(悬停/缩放显示)
- 演化页默认锁定 Java 强样例;数据版本徽标标注'(模拟)';联系方式移入设置页
- start.bat/start.sh 启动脚本
2026-08-12 16:25:37 +08:00

113 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.
"""极简配置加载:解析平铺的 ``key: value`` 格式 config.yaml,避免额外依赖。
运行时设置(监听地址/端口/访问令牌/LLM 密钥)保存在 config.json,
由设置接口写入、启动时合并读取,优先级:config.json > config.yaml > DEFAULTS。
"""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
CONFIG_JSON = ROOT / "config.json" # 运行时设置文件(测试可 monkeypatch 此路径)
# 允许通过设置面板/config.json 覆盖的键白名单
RUNTIME_KEYS = {
"mode", "host", "port", "access_token",
"llm_api_key", "llm_base_url", "llm_model", "llm_timeout",
"embed_model_path", "embed_provider", "embed_api_key",
"embed_api_base_url", "embed_api_model",
"guided", "jd_extra_dirs", "passive_sources", "demo_mode",
}
DEFAULTS: dict = {
"mode": "demo", # demo | live
"demo_mode": True, # 竞赛演示模式:不弹初始设置、警告降级为状态点
"host": "127.0.0.1",
"port": 9008,
"data_dir": "data",
"jd_raw_dir": "data/jd_raw",
"jd_extra_dirs": "", # 附加 JD 数据源目录(逗号分隔,可切换到真实数据集)
"passive_sources": "autoweaks", # 被动语料来源(无日期真实数据,不进图谱/演化/发现)
"graph_path": "data/graph.json",
"lexicon_path": "data/skills_lexicon.json",
"pending_path": "data/review/pending.json", # 旧待审核文件(仅作 SQLite 一次性迁移种子)
"audit_path": "data/review/audit.jsonl", # 旧留痕文件(仅作 SQLite 一次性迁移种子)
"state_db_path": "data/careernexus.db", # SQLite 应用状态库(审核留痕/候选/任务)
"decay_lambda": 0.35, # 时间衰减系数 w = exp(-λ·age)
"match_weight_required": 0.50, # 人岗匹配四维权重
"match_weight_preferred": 0.15,
"match_weight_experience": 0.20,
"match_weight_scenario": 0.15,
"match_threshold": 60.0, # 总分 >= 阈值视为匹配
"llm_api_key_env": "SPARK_API_KEY", # 密钥只从环境变量读取
"llm_base_url": "https://spark-api-open.xf-yun.com/v1",
"llm_model": "4.0Ultra",
"llm_timeout": 20,
}
def _coerce(value: str):
v = value.strip()
# 行内注释:值中第一个 " #" 之后的内容丢弃(URL 等含 # 的值不受影响)
if " #" in v:
v = v.split(" #", 1)[0].strip()
if v.lower() in ("true", "false"):
return v.lower() == "true"
try:
return int(v)
except ValueError:
pass
try:
return float(v)
except ValueError:
pass
return v
def load_config(path: str | Path | None = None) -> dict:
"""加载配置:DEFAULTS ← config.yaml ← config.json(运行时设置,优先级最高)。"""
cfg = dict(DEFAULTS)
p = Path(path) if path else ROOT / "config.yaml"
if p.exists():
for line in p.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or ":" not in line:
continue
key, _, value = line.partition(":")
cfg[key.strip()] = _coerce(value)
cfg.update(load_runtime_settings())
return cfg
def load_runtime_settings() -> dict:
"""读取 config.json 中的白名单键;文件不存在或损坏时返回空 dict。"""
try:
if CONFIG_JSON.exists():
data = json.loads(CONFIG_JSON.read_text(encoding="utf-8"))
return {k: v for k, v in data.items() if k in RUNTIME_KEYS}
except (json.JSONDecodeError, OSError):
pass
return {}
def save_runtime_settings(updates: dict) -> dict:
"""把白名单内的设置合并写入 config.json,返回保存后的完整运行时设置。"""
current = load_runtime_settings()
for k, v in updates.items():
if k not in RUNTIME_KEYS:
continue
if v is None or (isinstance(v, str) and not v.strip()):
current.pop(k, None) # 空值视为清除该设置
else:
current[k] = v
CONFIG_JSON.write_text(json.dumps(current, ensure_ascii=False, indent=2),
encoding="utf-8")
return current
def resolve(cfg: dict, key: str) -> Path:
"""把配置中的相对路径解析为工程根目录下的绝对路径。"""
p = Path(str(cfg[key]))
return p if p.is_absolute() else ROOT / p