feat:修改nl2sql功能

This commit is contained in:
2026-09-14 10:57:48 +08:00
parent a7f9e182a4
commit 67d5cfc2b8
15 changed files with 206 additions and 54 deletions
+28 -7
View File
@@ -25,6 +25,7 @@ logger = logging.getLogger("tool.llm")
FALLBACK_REPLY = "抱歉,服务暂时不可用,请稍后再试,或拨打客服热线 400-XXX-XXXX。"
@dataclass(frozen=True)
class Backend:
"""一个 OpenAI 兼容端点及其模型;name 为 ollama / api。"""
@@ -34,6 +35,7 @@ class Backend:
chat_model: str
embed_model: str
api_key: str = ""
api_trust_env: bool = True
@property
def is_ollama(self) -> bool:
@@ -50,7 +52,8 @@ class Backend:
"""ollama 走本机端点,必须绕过系统代理:httpx 会读取 Windows 系统代理,
但不识别其 ProxyOverride 白名单,localhost 请求会被转发到代理并返回 502。
API 后端保留 trust_env,远端接口可能依赖代理。"""
return httpx.AsyncClient(timeout=timeout, trust_env=not self.is_ollama)
return httpx.AsyncClient(timeout=timeout, trust_env=self.api_trust_env if not self.is_ollama else False)
def resolve_backends(cfg: LLMCfg) -> list[Backend]:
@@ -61,6 +64,7 @@ def resolve_backends(cfg: LLMCfg) -> list[Backend]:
base_url=cfg.ollama_base.rstrip("/") + "/v1",
chat_model=cfg.ollama_chat_model,
embed_model=cfg.ollama_embed_model,
api_trust_env=False,
)
if cfg.ollama_base and cfg.ollama_chat_model
else None
@@ -72,6 +76,7 @@ def resolve_backends(cfg: LLMCfg) -> list[Backend]:
chat_model=cfg.api_chat_model,
embed_model=cfg.api_embed_model,
api_key=cfg.api_key,
api_trust_env=cfg.api_trust_env,
)
if cfg.api_key and cfg.api_base and cfg.api_chat_model
else None
@@ -224,8 +229,8 @@ class LLMClient:
) or backend.embed_model
if not embed_model:
raise LLMFailError(f"backend={backend.name} 未配置 embed 模型")
is_dashscope_compatible = "/compatible-mode/" in backend.base_url.lower()
if is_dashscope_compatible:
is_dashscope_multimodal = "vision" in embed_model.lower() or "multimodal" in embed_model.lower()
if is_dashscope_multimodal:
base_url = backend.base_url.rstrip("/")
marker = "/compatible-mode/v1"
if base_url.lower().endswith(marker):
@@ -242,11 +247,27 @@ class LLMClient:
"input": texts,
"dimensions": self.cfg.embed_dimensions,
}
response = None
max_retries = max(1, int(getattr(self.cfg, "max_retries", 1)))
retry_backoff_sec = float(getattr(self.cfg, "retry_backoff_sec", 0))
async with backend.client(self.cfg.timeout) as client:
r = await client.post(url, headers=backend.headers, json=payload)
r.raise_for_status()
data = r.json()
if is_dashscope_compatible:
for attempt in range(max_retries):
try:
response = await client.post(
url,
headers=backend.headers,
json=payload,
)
response.raise_for_status()
break
except Exception:
if attempt == max_retries - 1:
raise
await asyncio.sleep(retry_backoff_sec * (2**attempt))
if response is None: # pragma: no cover - defensive guard
raise LLMFailError("Embedding 请求未返回响应")
data = response.json()
if is_dashscope_multimodal:
embeddings = data["output"]["embeddings"]
return [
item["embedding"]