Files
projectAIpopular/router_system/fallback.py
T
tzt c072a1a237 feat(v1): 架构与算法优化二轮——共享 LLM 客户端去重、语义缓存 O(1) 提升/淘汰、分类器热路径清理
- 新增 router_system/llm_client.py:OpenAICompatClient 统一 experts/judge/fallback
  三处复制的懒建 AsyncClient + /chat/completions + choices/usage 解析(~60 行去重);
  密钥解析统一走 config.get_api_key(激活原死代码,顺带消除 experts 默认环境名不一致)
- 语义缓存 L2:条目容器 list→OrderedDict(提升/淘汰 O(n)→O(1)),按 query 天然去重;
  n-gram 向量 lru_cache 复用(同一次 miss 的 get/put 免重复分词);
  A/B:淘汰路径 0.040→0.034s,miss→put 往返 9.41→8.57s(-9%)
- RuleJudge 覆盖度:response.lower() 提出逐词循环(原 O(terms×len) 重复复制)
- extract_content_terms 纯函数 lru_cache 化(专家与 Judge 对同一查询免重复分词),返回 tuple
- RuleClassifier:_score 去掉败者领域白建的命中词 list(胜出后单独收集);
  修复 code 规则 ("api",0.7) 重复登记(原命中计 1.4 分)
- difficulty:正则模块级预编译
- tests:恢复上一轮引入的乱码中文 docstring;网关测试输入串恢复为可判 code 的中文查询
2026-09-18 23:27:26 +08:00

85 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""大模型回退层:Mock 与 OpenAI 兼容 API 两种后端。"""
from __future__ import annotations
import asyncio
from typing import Dict
from .config import get_api_key
from .llm_client import OpenAICompatClient
from .models import ExpertResponse
class FallbackProvider:
name: str = "fallback"
async def generate(self, query: str) -> ExpertResponse:
raise NotImplementedError
class MockFallback(FallbackProvider):
"""确定性 mock 大模型:标识为 fallback,便于测试升级路径。"""
def __init__(self, model: str = "mock-large"):
self.model = model
self.name = f"fallback-{model}"
async def generate(self, query: str) -> ExpertResponse:
await asyncio.sleep(0.002)
body = (
f"(大模型回退)「{query}\n\n"
"这是一条来自大模型回退路径的完整回答。\n"
"要点:\n"
"1. 对复杂/跨域任务给出综合推理\n"
"2. 补充领域专家未覆盖的上下文\n"
"3. 给出可执行的后续建议\n"
)
return ExpertResponse(
text=body,
model_used=self.model,
latency_ms=2.0,
tokens=120,
cost_est=2.0 * 120 / 1_000_000,
)
class APIFallback(FallbackProvider):
"""OpenAI 兼容大模型 API(如 DeepSeek / OpenAI / 本地 vLLM)。"""
def __init__(self, model: str, base_url: str, api_key: str):
self.model = model
self.name = f"fallback-{model}"
self._client = OpenAICompatClient(base_url, api_key, timeout=90.0)
async def generate(self, query: str) -> ExpertResponse:
data = await self._client.chat(
self.model,
[{"role": "user", "content": query}],
temperature=0.3,
max_tokens=2048,
)
body = self._client.completion_text(data)
tokens = self._client.completion_tokens(data, body)
return ExpertResponse(
text=body,
model_used=self.model,
latency_ms=0.0,
tokens=tokens,
cost_est=2.0 * tokens / 1_000_000,
)
def build_fallback(cfg: Dict) -> FallbackProvider:
"""cfg 为 fallback 段配置。"""
ftype = cfg.get("type", "mock")
model = cfg.get("model", "deepseek-chat")
if ftype == "mock":
return MockFallback(model=model)
if ftype == "api":
api_key = cfg.get("api_key") or get_api_key(cfg)
if not api_key:
raise RuntimeError(
f"APIFallback 缺少 API Key:请设置环境变量 {cfg.get('api_key_env')} 或配置 api_key"
)
return APIFallback(model, cfg.get("base_url", "https://api.deepseek.com/v1"), api_key)
raise ValueError(f"未知 fallback 类型: {ftype}(支持 mock | api")