Files
news/embedding/factory.py
T
simon ff911cf6f7 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 既有失败与本改动无关
2026-09-25 11:13:37 +08:00

69 lines
2.3 KiB
Python

"""Embedding provider 工厂:根据配置构造合适后端。
配置优先级: 显式参数 > configs/llm_models.yaml scenes.embedding > .env > 默认。
"""
from __future__ import annotations
from configs.loader import load_scene_config
from configs.runtime_env import env_get
from .base import AsyncEmbeddingProvider, EmbeddingProvider
from .models import EmbeddingError, EmbeddingProviderType
from .remote import (
DashScopeAsyncEmbeddingProvider,
DashScopeEmbeddingProvider,
)
def _read_env(key: str, default: str | None = None) -> str | None:
"""读取环境变量(先热加载 .env,改文件后无需重启进程)。"""
return env_get(key, default)
def resolve_provider_type(provider: str | None = None) -> EmbeddingProviderType:
"""根据 provider 参数 / YAML 场景 / env 解析出 EmbeddingProviderType。
映射:
dashscope / qwen / remote -> DASHSCOPE
local / local-bge / bge / bge-m3 -> LOCAL_BGE
默认 dashscope。
"""
scene_provider = load_scene_config("embedding").get("provider")
p = (
provider
or scene_provider
or _read_env("EMBEDDING_PROVIDER", "dashscope")
or "dashscope"
).lower()
if p in ("dashscope", "qwen", "remote"):
return EmbeddingProviderType.DASHSCOPE
if p in ("local", "local-bge", "bge", "bge-m3"):
return EmbeddingProviderType.LOCAL_BGE
raise EmbeddingError(f"未知 embedding provider: {provider!r}")
def make_sync_provider(
provider: str | None = None,
**kwargs: object,
) -> EmbeddingProvider:
"""构造同步 provider。"""
pt = resolve_provider_type(provider)
if pt == EmbeddingProviderType.DASHSCOPE:
return DashScopeEmbeddingProvider(**kwargs) # type: ignore[arg-type]
# 本地后端
from .local import LocalBGEEmbeddingProvider
return LocalBGEEmbeddingProvider(**kwargs) # type: ignore[arg-type]
def make_async_provider(
provider: str | None = None,
**kwargs: object,
) -> AsyncEmbeddingProvider:
"""构造异步 provider。"""
pt = resolve_provider_type(provider)
if pt == EmbeddingProviderType.DASHSCOPE:
return DashScopeAsyncEmbeddingProvider(**kwargs) # type: ignore[arg-type]
from .local import LocalBGEAsyncEmbeddingProvider
return LocalBGEAsyncEmbeddingProvider(**kwargs) # type: ignore[arg-type]