"""真实 InferenceEngine 工厂:读取环境变量/.env 构造 HttpLLMClient + InferenceEngine。 设计要点: - 不硬编码任何密钥;优先级 GENESIS_INFERENCE__* 环境变量 > 裸 DEEPSEEK_API_KEY/LLM_BASE_URL > 默认值。 - 自动加载仓库根目录 .env(被 .gitignore 忽略,密钥不入库)。 - engine=None 时由 orchestrator/qa_loop 调用,接通 P5-T10 人工质量门禁的真实 LLM 路径。 """ from __future__ import annotations import os from pathlib import Path from genesis.config import InferenceModels, ModelSpec from genesis.inference.client import HttpLLMClient from genesis.inference.engine import InferenceEngine from genesis.inference.exceptions import LLMNotConfiguredError _DEFAULT_BASE_URL = "https://api.deepseek.com" _DEFAULT_MODEL = "deepseek-chat" _DEFAULT_FALLBACK = "qwen-max" def _load_dotenv(path: Path = Path(".env")) -> None: """将 .env 中的 KEY=VALUE 注入 os.environ(仅当变量尚未设置时)。""" if not path.is_file(): return for raw in path.read_text(encoding="utf-8").splitlines(): line = raw.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, val = line.partition("=") key = key.strip() val = val.strip() if len(val) >= 2 and val[0] in "\"'": if val[-1] == val[0]: val = val[1:-1] os.environ.setdefault(key, val) def build_inference_engine() -> InferenceEngine: _load_dotenv() api_key = os.environ.get("GENESIS_INFERENCE__API_KEY") or os.environ.get("DEEPSEEK_API_KEY") base_url = ( os.environ.get("GENESIS_INFERENCE__BASE_URL") or os.environ.get("LLM_BASE_URL") or _DEFAULT_BASE_URL ) model = ( os.environ.get("GENESIS_INFERENCE__MODEL") or os.environ.get("LLM_MODEL") or _DEFAULT_MODEL ) fallback = ( os.environ.get("GENESIS_INFERENCE__FALLBACK_MODEL") or os.environ.get("LLM_FALLBACK_MODEL") or _DEFAULT_FALLBACK ) if not api_key: raise LLMNotConfiguredError( "缺少 LLM API Key:请设置 GENESIS_INFERENCE__API_KEY" "(或 .env / 环境变量 DEEPSEEK_API_KEY)" ) models = InferenceModels( primary=ModelSpec(name=model), fallback=ModelSpec(name=fallback), ) client = HttpLLMClient(base_url=base_url, api_key=api_key) return InferenceEngine(client=client, models=models)