Files
CareerNexus/app/config.py
T
RoyirinRosmontis 1722c00249 真实数据集扩产与被动语料接入
- scripts/expand_real_dataset.py:AutoWeakS 全量 2215 条 + 500 条分层评测集 + 简历扩量(seed=2026 可复现)
- pipeline 被动语料机制(passive_sources):真实无日期 JD 参与接入/清洗/抽取验证(2915 条命中率 99.8%),不进图谱/演化/发现
- 新岗位发现第四指标'差异度'与双年窗口增长率口径
- 评测新增 real_jd_500 维度(99.91%);测试会话隔离 jd_extra_dirs 防止本机配置拖慢套件
2026-08-03 15:07:54 +08:00

112 lines
4.3 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",
}
DEFAULTS: dict = {
"mode": "demo", # demo | live
"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