统一管理所有环境变量(130+),提供 Config 单例。包含 LLM 通道解析、字段注册表、配置校验。
| 文件 | 职责 | 行数 |
|---|---|---|
settings.py |
Config 单例 dataclass(160 属性 + 30 方法);_load_from_env 调用 StockLensSettings 解析 env,自身只做编排(调 loader.X(...)) |
~1160 |
loader.py |
2026-05 G8 新增 — _load_from_env 的 9 个步骤抽出的可组合纯函数(proxy / 自选股 / 多 key / 模型推断 / fallback / Router 列表 / SearXNG 白名单 / WeChat 字节预算) |
368 |
raw_settings.py |
★ pydantic-settings StockLensSettings — 158 个 env var 的类型化定义(types/defaults/范围/枚举),单一类型解析事实源 |
~430 |
env_validator.py |
轻量启动校验 CoreEnvSettings(64 个核心字段,仅警告不阻断启动)— 用于无需写入 Config 的纯启动期变量 |
~220 |
settings_helpers.py |
LLM/News/Agent 工具函数 — 唯一源(_get_litellm_provider、_uses_direct_env_provider、parse_env_bool、resolve_* 等 17 个函数 + 常量) |
317 |
registry.py |
字段注册表 API(get_field_definition / get_category_definitions / build_schema_response)+ 从 fields/ 导入 _FIELD_DEFINITIONS |
239 |
fields/ |
配置字段子包 — 按业务域拆分 107 个字段定义(详见下方子表) | — |
llm.py |
LLM 多通道解析器,将 LLM_CHANNELS 转为 LiteLLM Router 配置 |
217 |
manager.py |
.env 文件读写 |
214 |
validation.py |
启动校验(结构化 issues) | 244 |
types.py |
配置相关 TypedDict / pydantic.dataclass(10 个 sub-config,构造时类型校验) | 227 |
__init__.py |
导出 Config 和 get_config() |
6 |
按 category 维度拆分原 1554 行 registry_fields.py,由 fields/__init__.py 重新合并为 _FIELD_DEFINITIONS,对外完全兼容。
| 文件 | category | 字段数 | 说明 |
|---|---|---|---|
base_fields.py |
base |
1 | 自选股列表 |
ai_model_fields.py |
ai_model |
25 | LiteLLM / 多通道 / Legacy 单 Key / Agent 模型 |
data_source_fields.py |
data_source |
15 | Tushare / yfinance / akshare / Tavily / SerpAPI / Bocha … |
notification_fields.py |
notification |
32 | 10 大渠道:微信/飞书/Telegram/Email/Discord/钉钉/PushPlus/ServerChan/Astrbot/Custom Webhook |
system_fields.py |
system |
12 | 日志、调度、Web UI、HTTP、市场回顾、交易日检查 |
agent_fields.py |
agent |
17 | 架构、协调器、技能、模型 |
backtest_fields.py |
backtest |
5 | 回测窗口、止损、止盈、强制重算 |
单例模式,通过 Config() 获取全局唯一实例。
股票列表
stock_list: List[str]— 自选股代码列表stock_groups: Dict[int, List[str]]— 分组股票email_groups: Dict[int, List[str]]— 分组邮件
LLM 配置
litellm_model: str— 主模型(provider/model 格式)litellm_fallback_models: List[str]— 备用模型列表litellm_config: Optional[str]— YAML 配置路径llm_channels: List[str]— 通道名列表llm_temperature: float— 采样温度gemini_api_key: Optional[str]— Gemini Keygemini_api_keys: List[str]— Gemini 多 Keyanthropic_api_key: Optional[str]— Anthropic Keyopenai_api_key: Optional[str]— OpenAI Keyopenai_base_url: Optional[str]— OpenAI 兼容端点deepseek_api_keys: List[str]— DeepSeek 多 Keyagent_litellm_model: Optional[str]— Agent 专用模型
搜索引擎
tavily_api_keys: List[str]serpapi_api_keys: List[str]bocha_api_keys: List[str]minimax_api_keys: List[str]brave_api_keys: List[str]searxng_base_urls: List[str]
数据源
tushare_token: Optional[str]twelvedata_api_key: Optional[str]realtime_source_priority: List[str]— 实时行情源优先级
通知渠道(每个渠道对应一组属性,详见 .env.example)
Agent 模式
agent_mode: bool— 启用 Agentagent_arch: str— 架构: single/multiagent_orchestrator_mode: str— 流水线: quick/standard/full/strategyagent_max_steps: int— 最大步数agent_skills: List[str]— 激活策略agent_risk_override: bool— 风控一票否决
报告配置
report_type: str— simple/full/briefsingle_stock_notify: bool— 单股立即推送report_integrity_enabled: bool— 完整性校验
Config() -> Config— 获取单例get(key: str, default=None) -> Any— 读取配置项set(key: str, value: Any) -> None— 运行时修改配置reload() -> None— 重新加载 .env 文件to_dict() -> Dict— 导出所有配置(脱敏)validate() -> List[str]— 校验配置,返回警告列表
Config 的 161 个顶层字段按 domain 拆成 10 个 pydantic.dataclass sub-config(见 types.py),可通过 lazy property 访问。这些 sub-config 是顶层字段的只读快照,构造时做类型校验。新代码请优先用 sub-config 路径:
| Sub-config | Property | 覆盖字段 | 典型字段示例 |
|---|---|---|---|
LLMConfig |
config.llm |
LLM 通道 / Key / 模型 / 重试 | litellm_model, gemini_api_keys, openai_base_url |
SearchConfig |
config.search |
搜索源 + 新闻策略 | tavily_api_keys, news_max_age_days, news_strategy_profile |
AgentConfig |
config.agent |
Agent 模式 / Orchestrator / Tools / Memory | agent_mode, agent_arch, agent_max_steps, agent_risk_override |
NotificationConfig |
config.notification |
10 渠道 + Bot tokens | wechat_webhook_url, feishu_app_id, pushover_api_token |
ReportConfig |
config.report |
报告类型 / 完整性 / 单股推送 | report_type, single_stock_notify, report_integrity_enabled |
DataSourceConfig |
config.datasource |
数据源 token / 限流 / 实时优先级 / fundamental 超时 | tushare_token, tickflow_api_key, realtime_source_priority, akshare_sleep_min/max, fundamental_stage_timeout_seconds |
BacktestConfig |
config.backtest |
回测窗口 + 评估配置 | backtest_eval_window_days, backtest_min_age_days |
PortfolioConfig |
config.portfolio |
投资组合风险 / 货币 | portfolio_risk_lookback_days |
BotConfig |
config.bot |
Bot 平台 + 限流 | bot_rate_limit_requests, dingtalk_app_secret |
SystemConfig |
config.system |
日志 / 路径 / 端口 / Auth | webui_port, admin_auth_enabled, log_level |
用法对比:
# 旧写法(仍可用,向后兼容)
priority = config.realtime_source_priority
# 新写法(推荐 — 类型校验 + 后续重构无忧)
priority = config.datasource.realtime_source_priority为什么这是正确的方向:Config 161 字段平铺早晚需要拆。直接迁到 sub-config 让上帝对象的 transition 路径平滑:旧字段保留兼容性,新代码用 sub-config 后下一轮重构可以删除顶层 alias。
registry_fields.py 是纯数据 — 130+ 个字段的 _FIELD_DEFINITIONS 列表;registry.py 是 API 函数(get_field / get_fields_by_category / validate_value)。这种拆分让注册表逻辑和数据可以独立演化。
{
"key": "STOCK_LIST", # 环境变量名
"type": "str", # 类型: str/int/float/bool/list
"default": "AAPL,AMZN,GOOGL", # 默认值
"required": False, # 是否必填
"category": "基础配置", # 分组
"description": "自选股列表", # 中文描述
"validation": {"min": 1}, # 校验规则
"sensitive": False, # 是否敏感(脱敏显示)
}get_field(key: str) -> FieldMeta— 获取字段元数据get_fields_by_category(category: str) -> List[FieldMeta]— 按分组获取get_all_fields() -> List[FieldMeta]— 获取全部字段validate_value(key: str, value: Any) -> Tuple[bool, str]— 校验单个值
解析 LLM_CHANNELS 环境变量,生成 LiteLLM Router 配置。
LLM_CHANNELS=deepseek,gemini
↓
读取 LLM_DEEPSEEK_BASE_URL, LLM_DEEPSEEK_API_KEY, LLM_DEEPSEEK_MODELS, ...
读取 LLM_GEMINI_API_KEY, LLM_GEMINI_MODELS, ...
↓
生成 LiteLLM Router model_list:
[
{"model_name": "openai/deepseek-chat", "litellm_params": {...}},
{"model_name": "gemini/gemini-3-flash", "litellm_params": {...}},
]
parse_channels() -> List[Dict]— 解析所有通道,返回 LiteLLM deployment 列表_parse_single_channel(name: str) -> List[Dict]— 解析单个通道_detect_protocol(name: str, base_url: str) -> str— 自动检测协议
.env 文件的读写管理器。
load_env(path: str = None) -> None— 加载 .env 文件save_env(key: str, value: str) -> None— 写入单个配置到 .envsave_env_batch(updates: Dict[str, str]) -> None— 批量写入delete_env(key: str) -> None— 删除配置项
启动时配置校验。
validate_all() -> List[str]— 校验所有配置,返回警告/错误列表validate_llm_config() -> List[str]— 校验 LLM 配置完整性check_deprecated() -> List[str]— 检查已废弃的配置项
- 被依赖:几乎所有模块都依赖 Config
- 依赖:
python-dotenv(.env 加载)
本模块管理所有 130+ 配置项,完整列表见 .env.example。
Config是单例,首次调用时初始化,后续调用返回同一实例- LLM 通道解析优先级:LiteLLM YAML > 多通道环境变量 > Legacy 单 Key
GEMINI_API_KEYS(复数)优先于GEMINI_API_KEY(单数),其他 provider 同理sensitive=True的字段在to_dict()时会脱敏显示(***)OPENAI_VISION_MODEL已移除(2026-05),env 中仍可设置但值会自动合并到VISION_MODEL- 新增字段:直接编辑
fields/<category>_fields.py中的FIELDS字典,registry.py无需修改 - 兼容性 shim 清理(2026-05):
helpers.py和registry_fields.py已删除;llm.py/validation.py直接从settings_helpers.py导入;registry.py直接从fields/导入 - 配置分层:107 个字段已按
tier标签分为required(2) /tuning(80) /experimental(25):required—— 核心功能必备(STOCK_LIST+LITELLM_MODEL),不配则系统不可用tuning—— 日常调优(多通道 LLM key、数据源 priority、推送渠道、超时等)experimental—— 实验性 / 高级(Agent 模式、LiteLLM YAML、罕见渠道、回测高级参数) 调用get_field_tier(key)取分层、get_fields_by_tier("required")取所有必填字段、get_tier_summary()取统计。新用户配置时优先看required即可
_get_litellm_provider/_uses_direct_env_provider等 LLM 工具的唯一源是settings_helpers.py;helpers.py/settings.py仅 re-export,不要重复定义STOCKLENS_*_WORKERS系列变量由stocklens/utils/concurrency.py单独读取,不进入 Config 单例- 跨模块 LLM 通道校验已抽到
stocklens/services/system_config_validators.py,SystemConfigService通过模块级 import 调用 - 2026-05 修复
settings_helpers.py循环引用注解:get_effective_agent_primary_model(config: "Config")/get_effective_agent_models_to_try(config: "Config")的 forward-reference 字符串Config在静态检查阶段无法解析(ruff F821)。修法:在if TYPE_CHECKING: from stocklens.config.settings import Config—— 不引入运行时循环依赖。
历史:Config._load_from_env 是个 ~380 行的 classmethod,里面用「Step 1 / Step 2 / ... / Step 9」注释标段。新增 env var 时这个方法持续膨胀,单元测试只能 end-to-end(构造完整 StockLensSettings 再跑)。
修复:把 9 步业务逻辑各自抽成 stocklens/config/loader.py 的 9 个纯函数:
| Step | 函数 | 输入 | 输出 |
|---|---|---|---|
| 2 | apply_proxy_side_effects |
http_proxy, https_proxy |
None(修改 os.environ) |
| 3 | resolve_stock_list |
stock_list_raw, split_csv |
List[str] |
| 4 | resolve_provider_keys |
raw, split_csv |
{"gemini":[...], "anthropic":[...], "openai":[...], "deepseek":[...]} |
| 5 | infer_litellm_model |
raw, provider_keys |
str(model 全名) |
| 6 | resolve_fallback_models |
raw, litellm_model, split_csv |
List[str] |
| 7 | build_router_model_list |
raw, provider_keys, 4 个 callable hooks |
(model_list, channels, source) |
| 7' | auto_infer_from_channels |
litellm_model, fallback_list, channels |
(updated_model, updated_fallback) |
| 8 | filter_searxng_urls |
raw_urls, split_csv |
List[str](过滤无效 + 警告) |
| 9 | compute_wechat_max_bytes |
msg_type, override |
int |
Config._load_from_env 现在只剩 ~70 行编排:调 loader.X(...) → 把结果填进 cls(...) dataclass。
settings.py 1290 → 1161 行 (-10%);loader.py 是 368 行集中的可组合纯函数。
tests/unit/test_config_subconfig.py::TestSubConfigFieldRoundtrip 参数化覆盖全部 10 个子配置(llm / search / agent / notification / report / datasource / backtest / portfolio / bot / system),断言每个声明字段都能从扁平 Config round-trip 到子配置。任何未来的子配置 schema 漂移(漏写字段、改名)都会被这个测试抓住。
测试统计:G 阶段总共新增 test_config_loader.py(37) + 扩展 test_config_subconfig.py(+10),合计 47 个 config 相关测试。