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:
+22
-5
@@ -9,15 +9,22 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from loguru import logger
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from configs.loader import config_path
|
||||
from configs.runtime_env import (
|
||||
dotenv_path,
|
||||
ensure_env_loaded,
|
||||
file_signature,
|
||||
start_env_watcher,
|
||||
)
|
||||
from embedding import make_sync_provider
|
||||
from vectorstore import SearchFilter, VectorStore, make_qdrant_client
|
||||
|
||||
# 加载 .env(API key 等)
|
||||
load_dotenv()
|
||||
# 加载 .env 并开启热加载:MCP server 是常驻进程,改配置无需重启
|
||||
ensure_env_loaded()
|
||||
start_env_watcher()
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 单例(模块加载时初始化,所有工具共用)
|
||||
@@ -29,17 +36,27 @@ class _Backend:
|
||||
vector_store: VectorStore
|
||||
|
||||
_backend: _Backend | None = None
|
||||
_backend_sig: tuple[Any, ...] | None = None
|
||||
|
||||
|
||||
def _config_signature() -> tuple[Any, ...]:
|
||||
"""(.env, llm_models.yaml) 的 mtime 签名;变化即表示需要重建后端。"""
|
||||
return (file_signature(dotenv_path()), file_signature(config_path()))
|
||||
|
||||
|
||||
def _get_backend() -> _Backend:
|
||||
global _backend
|
||||
if _backend is None:
|
||||
global _backend, _backend_sig
|
||||
sig = _config_signature()
|
||||
if _backend is None or sig != _backend_sig:
|
||||
if _backend is not None:
|
||||
logger.info("检测到配置变化, 重建 MCP 后端")
|
||||
emb = make_sync_provider() # 读取 EMBEDDING_PROVIDER 环境变量
|
||||
logger.info("MCP embedder 就绪: dim={}", emb.dim)
|
||||
client = make_qdrant_client()
|
||||
store = VectorStore(client)
|
||||
logger.info("MCP vector_store 就绪: count={}", store.count())
|
||||
_backend = _Backend(embedder=emb, vector_store=store)
|
||||
_backend_sig = sig
|
||||
return _backend
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user