feat: Token Plan 迁移与 .env 热加载,并修复日报 AI 摘要为空

Token Plan 迁移 / 配置热加载:
- configs/llm_models.yaml: 各场景切到 Token Plan(deepseek-v4.1-flash / qwen3.6-flash)
- 新增 configs/runtime_env.py: .env 按 (mtime_ns, size) 热加载并同步 os.environ,
  统一 env_get 取值;llm / embedding / vectorstore / mcp / pipeline 改用 env_get
- configs/loader.py / scripts/run_scheduler.py 等配套调整
- 新增 tests/test_hot_reload.py

日报 AI 摘要为空修复(2026-09-25):
- 根因: 推理模型的 reasoning token 与正文共用 max_tokens, 预算 1500 被"思考"
  占满 -> text_tokens=0 / finish_reason=length, 摘要静默为空且不重试
- daily_report 场景新增 max_tokens(默认 4000, YAML 保存即热生效);
  LLMConfig 支持可选 max_tokens; 分块预算 800 -> 2000
- _llm_call 拆出 _call_once, 正文为空时自动加倍预算重试(上限 16000),
  用尽才降级返回空串; 网络异常重试语义不变
- docs/user-guide.md 新增 FAQ; continuation.md 记录本次排查
- 已重跑 2026-09-25 日报(report_id=357)补回 466 字摘要

测试: 相关用例 56 passed(test_hot_reload 12 passed);
      ruff 无新增问题; 3 个 crawler 既有失败与本改动无关
This commit is contained in:
2026-09-25 11:13:37 +08:00
parent 2eaea2ee81
commit ff911cf6f7
19 changed files with 1024 additions and 200 deletions
+19 -15
View File
@@ -18,7 +18,8 @@
# LLM_MODEL);若全部缺失则直接报错,绝不静默使用内置默认模型。
# · api_key_env / base_url_env 为可选字段,填写存放 API Key / 服务地址的
# 环境变量名;API Key 一律放 .env,禁止写入本文件(安全规范)。
# · 修改后无需重启常驻服务即可生效(每次调用重新读取;如需热更新缓存可重启)。
# · 修改后无需重启常驻服务即可生效:configs/loader.py 以 (mtime, size) 失效缓存,
# 保存后下一次调用即读到新值;.env 的改动由 configs/runtime_env.py 在约 2s 内热更新。
# =============================================================================
# ---- 全局默认参数(各场景可覆盖;低于 .env,高于代码内置默认)----
@@ -49,9 +50,9 @@ scenes:
# 建议模型: deepseek-v4-flash(生产实测) / deepseek-chat / qwen-plus / qwen-max
event_extraction:
provider: qwen # 建议 deepseek | qwen;留空则回退 .env 的 LLM_PROVIDER
model: qwen3.7-flash # 留空则回退 .env(DEEPSEEK_MODEL → LLM_MODEL)
api_key_env: DASHSCOPE_API_KEY # 例如: DEEPSEEK_API_KEY / QWEN_API_KEY / DASHSCOPE_API_KEY
base_url_env: QWEN_BASE_URL # 例如: DEEPSEEK_BASE_URL / QWEN_BASE_URL
model: qwen3.6-flash # Token Plan 模型;注意 Token Plan 无 qwen3.7-flash
api_key_env: QWEN_API_KEY # Token Plan 计费账号(sk-sp-…);勿用 DASHSCOPE_API_KEY
base_url_env: QWEN_BASE_URL # .env 指向 token-plan.*.maas.aliyuncs.com
temperature: 0.1
timeout_sec: 60
max_attempts: 3 # 单篇解析失败的最大重试次数
@@ -66,20 +67,23 @@ scenes:
# 使用方式:无需手动触发,定时任务自动执行;失败自动降级(日报留空,不影响入库)。
# 对模型的要求:
# · OpenAI 兼容 chat 接口(不需要 JSON 输出);
# · 输出长度 ≥ 1500 tokens(max_tokens=1500,输出超长会被截断并记 WARNING);
# · 输出长度 ≥ max_tokens 配置值(见下,输出超长会被截断并记 WARNING);
# · 中文摘要能力强、要点化输出稳定(每条一行,以 "- " 开头);
# · 上下文窗口 ≥ 8K tokens(素材按 3000 字符/块分块,多块先分段再合并);
# · temperature 0.3 左右,兼顾稳定与表达;网络失败按指数退避重试 3 次。
# · 输出长度需求:分段摘要约 800 tokens、合并摘要约 1500 tokens(代码内置,
# 不在本文件配置),模型应能稳定输出 1500+ tokens 的中文要点。
# · max_tokens 说明:推理模型(deepseek-v4.1-flash 等)的 reasoning token 与
# 正文共用该预算;预算过小时"思考"会占满配额导致正文为空
# (finish_reason=length、0 字符,日报因此没有 AI 摘要)。代码兜底见
# scheduler/reporter.py: 正文为空时自动加倍预算重试(最多 2 次,上限 16000)。
# 建议模型: deepseek-v4-flash(生产实测) / deepseek-chat / qwen-plus
daily_report:
provider: # 建议 deepseek | qwen;留空则回退 .env 的 LLM_PROVIDER
model:
api_key_env:
base_url_env:
provider: qwen # Token Plan 计费账号
model: deepseek-v4.1-flash
api_key_env: QWEN_API_KEY
base_url_env: QWEN_BASE_URL
temperature: 0.3
timeout_sec: 60
max_tokens: 4000 # 单块/合并摘要输出预算(需为 reasoning token 预留余量)
# ------------------------------------------------------------------------- #
# 场景 3: 个股 AI 要点分析
@@ -97,10 +101,10 @@ scenes:
# · 输出长度需求:约 500 tokens(代码内置,不在本文件配置)。
# 建议模型: deepseek-v4-flash(生产实测) / deepseek-chat / qwen-plus
stock_report:
provider: # 建议 deepseek | qwen;留空则回退 .env 的 LLM_PROVIDER
model:
api_key_env:
base_url_env:
provider: qwen # Token Plan(个股日报当前禁用,配置好以防将来启用时漏计费)
model: qwen3.6-flash
api_key_env: QWEN_API_KEY
base_url_env: QWEN_BASE_URL
temperature: 0.3
timeout_sec: 60