feat: 大模型使用场景化配置与去重多源记录
- 新增 configs/llm_models.yaml: 4 个场景(event_extraction/daily_report/stock_report/embedding)
可独立配置 provider/model/api_key_env/base_url_env/temperature 等,含用途与模型要求说明
- 新增 configs/loader.py: YAML 场景加载器(优先级: CLI 参数 > YAML > .env > 内置默认)
- llm/client.py: load_llm_config 支持 scene 参数,LLMConfig 增加 max_attempts
- embedding/factory+remote+local: provider/model/batch_limit 支持场景覆盖
- scheduler/reporter+stock_reporter: 日报/个股摘要接入场景配置
- dedup: Fingerprint.source_ids 多源记录 + 旧库自动迁移 + DedupResult 多源字段
- scripts/run_dedup: uniques JSON 的 sources 字段 + data/deduped/{day}/sources.json 汇总
- scripts/run_event_extraction: 接入 event_extraction 场景
- 补充测试: 场景优先级/零值、多源合并、旧库迁移、embedding 场景覆盖
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"""configs/ 目录下 YAML 配置加载器。
|
||||
|
||||
目前支持加载 configs/llm_models.yaml 的场景配置(scenes.<scene>)。
|
||||
|
||||
配置优先级(从高到低):
|
||||
1. 代码 / 命令行显式参数(如 --provider qwen --model qwen-plus)
|
||||
2. 本文件 YAML 场景配置(scenes.<scene>)
|
||||
3. 环境变量 / .env(LLM_PROVIDER、DEEPSEEK_MODEL 等,向后兼容)
|
||||
4. 代码内置默认值
|
||||
|
||||
说明:API Key 一律放 .env,本文件只保存环境变量名(api_key_env),禁止写密钥。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from loguru import logger
|
||||
|
||||
DEFAULT_CONFIG_PATH = Path("configs/llm_models.yaml")
|
||||
|
||||
|
||||
@lru_cache(maxsize=8)
|
||||
def _load_yaml(path: Path) -> dict:
|
||||
"""读取 YAML 文件为 dict;文件缺失或解析失败返回空 dict(走兜底配置)。"""
|
||||
if not path.is_file():
|
||||
logger.debug("配置文件不存在,使用内置/环境变量兜底: {}", path)
|
||||
return {}
|
||||
try:
|
||||
import yaml
|
||||
|
||||
data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
||||
except Exception as e: # noqa: BLE001 - YAML 语法错误等
|
||||
logger.error("解析 {} 失败: {}", path, e)
|
||||
return {}
|
||||
return data if isinstance(data, dict) else {}
|
||||
|
||||
|
||||
def load_scene_config(scene: str) -> dict:
|
||||
"""读取 llm_models.yaml 中 scenes.<scene> 的配置 dict。
|
||||
|
||||
场景不存在或未配置时返回空 dict(调用方回退 .env / 内置默认)。
|
||||
scene 为空字符串同样返回空 dict。
|
||||
"""
|
||||
if not scene:
|
||||
return {}
|
||||
data = _load_yaml(DEFAULT_CONFIG_PATH)
|
||||
scenes = data.get("scenes") or {}
|
||||
cfg = scenes.get(scene)
|
||||
if cfg is None:
|
||||
logger.debug("llm_models.yaml 未配置场景 {!r},使用 .env 兜底", scene)
|
||||
return {}
|
||||
if not isinstance(cfg, dict):
|
||||
logger.warning("llm_models.yaml 场景 {!r} 应为 map,已忽略", scene)
|
||||
return {}
|
||||
return cfg
|
||||
|
||||
|
||||
def load_defaults() -> dict:
|
||||
"""读取 llm_models.yaml 顶层 defaults(全局默认参数)。"""
|
||||
data = _load_yaml(DEFAULT_CONFIG_PATH)
|
||||
d = data.get("defaults") or {}
|
||||
return d if isinstance(d, dict) else {}
|
||||
|
||||
|
||||
def clear_cache() -> None:
|
||||
"""清空 YAML 缓存(测试或热更新配置时使用)。"""
|
||||
_load_yaml.cache_clear()
|
||||
Reference in New Issue
Block a user