From 5b892b3a63878c207c0fd1e733f0ed99b2c230d1 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 4 Apr 2026 07:24:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DGemini=202.5=E6=80=9D?= =?UTF-8?q?=E8=80=83=E6=A8=A1=E5=9E=8B=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E6=97=B6thought=5Fsignature=E7=BC=BA=E5=A4=B1=E5=AF=BC?= =?UTF-8?q?=E8=87=B4400=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Google provider统一使用ChatGoogleGenerativeAI原生接口,不再走OpenAI兼容端点 (OpenAI协议不支持thought_signature字段,导致思考模型工具调用必然失败) - 通过client_args传递代理配置,替代原来的OpenAI兼容端点+openai_proxy方案 - 修补langchain-google-genai的_is_gemini_3_or_later()以覆盖Gemini 2.5模型 - 自动适配httpx代理参数名(proxies/proxy),修复代理配置被静默丢弃的问题 --- app/helper/llm.py | 98 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 25 deletions(-) diff --git a/app/helper/llm.py b/app/helper/llm.py index 5526d020..2c570499 100644 --- a/app/helper/llm.py +++ b/app/helper/llm.py @@ -1,11 +1,61 @@ """LLM模型相关辅助功能""" +import inspect from typing import List from app.core.config import settings from app.log import logger +def _patch_gemini_thought_signature(): + """ + 修复 langchain-google-genai 中 Gemini 2.5 思考模型的 thought_signature 兼容问题。 + langchain-google-genai 的 _is_gemini_3_or_later() 仅检查 "gemini-3", + 导致 Gemini 2.5 思考模型(如 gemini-2.5-flash、gemini-2.5-pro)在工具调用时 + 缺少 thought_signature 而报错 400。 + 此补丁将检查范围扩展到 Gemini 2.5 模型。 + """ + try: + import langchain_google_genai.chat_models as _cm + + # 仅在未修补时执行 + if getattr(_cm, "_thought_signature_patched", False): + return + + def _patched_is_gemini_3_or_later(model_name: str) -> bool: + if not model_name: + return False + name = model_name.lower().replace("models/", "") + # Gemini 2.5 思考模型也需要 thought_signature 支持 + return "gemini-3" in name or "gemini-2.5" in name + + _cm._is_gemini_3_or_later = _patched_is_gemini_3_or_later + _cm._thought_signature_patched = True + logger.debug( + "已修补 langchain-google-genai thought_signature 兼容性(覆盖 Gemini 2.5 模型)" + ) + except Exception as e: + logger.warning(f"修补 langchain-google-genai thought_signature 失败: {e}") + + +def _get_httpx_proxy_key() -> str: + """ + 获取当前 httpx 版本支持的代理参数名。 + httpx < 0.28 使用 "proxies"(复数),>= 0.28 使用 "proxy"(单数)。 + google-genai SDK 会静默过滤掉不在 httpx.Client.__init__ 签名中的参数, + 因此必须使用与当前 httpx 版本匹配的参数名。 + """ + try: + import httpx + + params = inspect.signature(httpx.Client.__init__).parameters + if "proxy" in params: + return "proxy" + return "proxies" + except Exception: + return "proxies" + + class LLMHelper: """LLM模型相关辅助功能""" @@ -23,31 +73,27 @@ class LLMHelper: raise ValueError("未配置LLM API Key") if provider == "google": + # 修补 Gemini 2.5 思考模型的 thought_signature 兼容性 + _patch_gemini_thought_signature() + + # 统一使用 langchain-google-genai 原生接口 + # 不使用 OpenAI 兼容端点,因其不支持 Gemini 思考模型的 thought_signature, + # 会导致工具调用时报错 400 + from langchain_google_genai import ChatGoogleGenerativeAI + + client_args = None if settings.PROXY_HOST: - # 通过代理使用 Google 的 OpenAI 兼容接口 - from langchain_openai import ChatOpenAI + proxy_key = _get_httpx_proxy_key() + client_args = {proxy_key: settings.PROXY_HOST} - model = ChatOpenAI( - model=settings.LLM_MODEL, - api_key=api_key, - max_retries=3, - base_url="https://generativelanguage.googleapis.com/v1beta/openai", - temperature=settings.LLM_TEMPERATURE, - streaming=streaming, - stream_usage=True, - openai_proxy=settings.PROXY_HOST, - ) - else: - # 使用 langchain-google-genai 原生接口(v4 API 变更:google_api_key → api_key,max_retries → retries) - from langchain_google_genai import ChatGoogleGenerativeAI - - model = ChatGoogleGenerativeAI( - model=settings.LLM_MODEL, - api_key=api_key, - retries=3, - temperature=settings.LLM_TEMPERATURE, - streaming=streaming, - ) + model = ChatGoogleGenerativeAI( + model=settings.LLM_MODEL, + api_key=api_key, + retries=3, + temperature=settings.LLM_TEMPERATURE, + streaming=streaming, + client_args=client_args, + ) elif provider == "deepseek": from langchain_deepseek import ChatDeepSeek @@ -103,9 +149,11 @@ class LLMHelper: http_options = None if settings.PROXY_HOST: + proxy_key = _get_httpx_proxy_key() + proxy_args = {proxy_key: settings.PROXY_HOST} http_options = HttpOptions( - client_args={"proxy": settings.PROXY_HOST}, - async_client_args={"proxy": settings.PROXY_HOST}, + client_args=proxy_args, + async_client_args=proxy_args, ) client = genai.Client(api_key=api_key, http_options=http_options)