Gemini 調用方式

對於 Gemini 系列,我們提供原生調用和 Openai 相容這 2 種調用方式。
使用前請執行 pip install google-genaipip install -U google-genai,安裝(更新)原生依賴。

1️⃣ 原生調用時,需在內部傳入 AiHubMix 密鑰和請求連結。注意這個連結和常規 base_url 寫法不同,請參考範例:

client = genai.Client(
    api_key="sk-***", # 🔑 換成你在 AiHubMix 生成的密鑰
    http_options={"base_url": "https://aihubmix.com/gemini"},
)

2️⃣ Openai 相容格式則維持通用的 v1 端點:

client = OpenAI(
    api_key="sk-***", # 換成你在 AiHubMix 生成的密鑰
    base_url="https://aihubmix.com/v1",
)

3️⃣ 針對 2.5 系列,如需顯示推理過程,可用以下 2 種方式:

  1. 原生調用:傳入 include_thoughts=True
  2. OpenAI 相容方式:傳入 reasoning_effort

詳細調用請參考下文程式碼範例。

Gemini 2.5 系列「推理」說明

  1. 2.5 系列皆為推理模型。
  2. 2.5 flash 為混合模型,類似 claude sonnet 3.7,可用 thinking_budget 控制推理預算以達最佳效果。
  3. 2.5 pro 為純推理模型,無法關閉 thinking,也不顯式傳遞推理預算。

Python 調用參考如下:

from google import genai
from google.genai import types

def generate():
    client = genai.Client(
        api_key="sk-***", # 🔑 換成你在 AiHubMix 生成的密鑰
        http_options={"base_url": "https://aihubmix.com/gemini"},
    )

    model = "gemini-2.0-flash"
    contents = [
        types.Content(
            role="user",
            parts=[
                types.Part.from_text(text="""對於普通股票投資者:分析財報有用的话,还要运气做什么?"""),
            ],
        ),
    ]

    print(client.models.generate_content(
        model=model,
        contents=contents,
    ))

if __name__ == "__main__":
    generate()

Gemini 2.5 Flash 支持

Openai 相容方式調用參考如下:

from openai import OpenAI

client = OpenAI(
    api_key="sk-***", # 換成你在 AiHubMix 生成的密鑰
    base_url="https://aihubmix.com/v1",
)

completion = client.chat.completions.create(
    model="gemini-2.5-flash-preview-04-17-nothink",
    messages=[
        {
            "role": "user",
            "content": "Explain the Occam's Razor concept and provide everyday examples of it"
        }
    ]
)

print(completion.choices[0].message.content)
  1. 用於複雜任務時,只需要將模型 id 設置為默認開啟思考的 gemini-2.5-flash-preview-04-17 即可。
  2. Gemini 2.5 Flash 通過 budget(思考預算)來控制思考的深度,範圍 0-16K,目前轉發采用的是默認預算 1024,最佳邊際效果為 16K。

多媒體文件

Aihubmix 目前只支持小於 20MB 的多媒體文件(圖片、音頻、視頻),用 inline_data 上傳。
大於 20M 的多媒體需要用 File API(尚未支持),待完善狀態跟踪,返回 upload_url

from google import genai
from google.genai import types

# 讀取文件為二進制數據
file_path = "yourpath/file.jpeg"
with open(file_path, "rb") as f:
    file_bytes = f.read()

client = genai.Client(
    api_key="sk-***", # 🔑 換成你在 AiHubMix 生成的密鑰
    http_options={"base_url": "https://aihubmix.com/gemini"}
)

response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents=types.Content(
        parts=[
            types.Part(
                inline_data=types.Blob(
                    data=file_bytes,
                    mime_type="image/jpeg"
                )
            ),
            types.Part(
                text="Describe the image."
            )
        ]
    )
)

print(response.text)

Code Execution

自动代码解析器用例参考:

Python
from google import genai
from google.genai import types

# 讀取文件為二進制數據
file_path = "yourpath/file.csv"
with open(file_path, "rb") as f:
    file_bytes = f.read()

client = genai.Client(
    api_key="sk-***", # 🔑 換成你在 AiHubMix 生成的密鑰
    http_options={"base_url": "https://aihubmix.com/gemini"}
)

response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents=types.Content(
        parts=[
            types.Part(
                inline_data=types.Blob(
                    data=file_bytes,
                    mime_type="text/csv"
                )
            ),
            types.Part(
                text="Please analyze this CSV and summarize the key statistics. Use code execution if needed."
            )
        ]
    ),
    config=types.GenerateContentConfig(
        tools=[types.Tool(
            code_execution=types.ToolCodeExecution
        )]
    )
)

for part in response.candidates[0].content.parts:
    if part.text is not None:
        print(part.text)
    if getattr(part, "executable_code", None) is not None:
        print("Generated code:\n", part.executable_code.code)
    if getattr(part, "code_execution_result", None) is not None:
        print("Execution result:\n", part.code_execution_result.output)

上下文快取

Gemini 在原生 API 下預設啟用了隱式上下文快取,無需開發者手動操作。每一次 generate_content 請求,系統會自動為輸入內容建立快取。當後續請求與此前內容完全一致時,將直接命中快取,返回上一次的推理結果,大幅提升回應速度並有機會節省 token 消耗。

  • 快取自動生效,無需手動配置。
  • 快取僅在內容、模型、參數完全一致時生效;任何欄位不同都會視為新請求,不命中快取。
  • 快取有效期(TTL)由開發者設定,也可以不設定。如果未指定,預設為 1 小時。無最小或最大時長限制,費用取決於快取 token 數與快取時間。
    • 雖然 Google 官方對 TTL 不設上下限,但由於我們作為轉發平台,僅支援有限的 TTL 配置範圍,不保證永久有效

注意事項

  • 無成本節省保證:快取 token 的計費為輸入原價的 25%,理論上輸入部分可最多節省 75% 成本,但 Google 官方並未承諾必然節省,實際帳單還需結合快取命中率、token 類型與儲存時長共同評估。

  • 快取命中條件:建議將重複的上下文放在請求前部,將易變內容(如用戶輸入)置於後部,以提高快取命中率。

  • 快取命中回饋:如果回應結果命中快取,在 response.usage_metadata 中會包含 cache_tokens_details 欄位,並有 cached_content_token_count,開發者可以據此判斷本次請求是否命中快取。
    範例回應欄位(命中快取時):

    cache_tokens_details=[ModalityTokenCount(modality=<MediaModality.TEXT: 'TEXT'>, token_count=2003)]
    cached_content_token_count=2003
    

程式碼範例:

from google import genai

client = genai.Client(
    http_options={"base_url": "https://aihubmix.com/gemini"},
    api_key="sk-***", # 換成你在 AiHubMix 生成的密鑰
)

prompt = """
《三国演义》词曰:打开字典    滚滚长江东逝水,浪花淘尽英雄。是非成败转头空:青山依旧在,几度夕阳红。白发渔樵江渚上,惯看秋月春风。一壶浊酒喜相逢:古今多少事,都付笑谈中。2 打开字典  宴桃园豪... : 话说天下大势,分久必合,合久必分:周末七国分争,并入于秦。及秦灭之后,楚、汉分争,又并入于汉。汉朝自高祖斩白蛇而起义,一统天下。后来光武中兴,传至献帝,遂分为三国。推其致乱之由,殆始于桓、灵二帝。桓帝禁锢善类,崇 信宦官。及桓帝崩,灵帝即位,大将军窦武、太傅陈蕃,共相辅佐。时有宦官曹节等弄权,窦武、陈蕃谋诛之,作事不密,反为所害。中涓自此愈横。3 打开字典  宴桃园豪... : 建宁二年四月望日,帝御温德殿。方升座,殿角狂风骤起,只 见一条大青蛇,从梁上飞将下来,蟠于椅上。帝惊倒,左右急救入宫,百官俱奔避。须臾,蛇不见了。忽然大雷大雨,加以冰雹,落到半夜方止,坏却房屋无数。建宁四年二月,洛阳地震;又海水泛溢,沿海居民,尽被大浪卷入海中。光和元 年,雌鸡化雄。六月朔,黑气十馀丈,飞入温德殿中。秋七月,有虹见于玉堂;五原山岸,尽皆崩裂。种种不祥,非止一端。4 打开字典  宴桃园豪... : 帝下诏问群臣以灾异之由,议郎蔡邕上疏,以为霓堕鸡化,乃妇寺干政之所致,言颇切 直。帝览奏叹息,因起更衣。曹节在后窃视,悉宣告左右·遂以他事陷邕于罪,放归田里。后张让,赵忠,封諝,段圭,曹节,候览,蹇硕,程旷,夏恽,郭胜十人朋比为奸,号为"十常侍"。帝尊信张让,呼为"阿父",朝政日非,以致天下人心思乱,盗贼蜂起。5 打开字典  宴桃园豪... : 时钜鹿郡有兄弟三人:一名张角,一名张宝,一名张梁。那张角本是个不第秀才。因入山采药,遇一老人,碧眼童颜,手执藜杖,唤角至一洞中,以天书三卷授之,曰:"此名太平要术。汝得之,当代天宣化,普救世人;若萌异心,必获恶报。"角拜问姓名。老人曰:"吾乃南华老仙也。"言讫,化阵清风而去。6 打开字典  宴桃园豪... : 角得此书,晓夜攻习,能呼风唤雨,号为太平道人。中平元年正月内,疫气流行,张角散施符水,为人治病,自称大贤良师。角有徒弟五百馀人,云游四方,皆能书符念咒。次后徒众日多,角乃立三十六方,─大方万馀人,小方六七千─,各立渠帅,称为将军。讹言"苍天已死,黄天当立。"又云"岁在甲子,天下大吉。"令人各以白土,书"甲子"二字于家中大门上。青、幽、徐、冀、荆、扬、兖、豫八州之人,家家侍奉大贤良师张角名字。角遣其党马元义,暗赍金帛,结交中涓封諝,以为内应。角与二弟商议曰:"至难得者,民心也。今民心已顺,若不乘势取天下,诚为可惜。"遂一面私造黄旗,约期举事;一面使弟子唐州,驰书报封諝。唐州乃迳赴省中告变。帝召大将军何进调兵擒马元义,斩之;次收封諝等一干人下狱。7 打开字典  宴桃园豪... : 张角闻知事露,星夜举兵,自称天公将军,─张宝称地公将军,张梁称人公将军─。申言于众曰:"今汉运将终,大圣人出;汝等皆宜顺从天意,以桨太平。"四方百姓,裹黄巾从张角反者,四五十万。贼势浩大,官军望风而靡。何进奏帝火速降诏,令各处备御,讨贼立功;一面遣中郎将卢植,皇甫嵩,朱隽,各引精兵,分三路讨之。8 打开字典  宴桃园豪... : 且说张角一军,前犯幽州界分。幽州太守刘焉,乃江夏竟陵人氏,汉鲁恭王之后也;当时闻得贼兵将至,召校尉邹靖计议。靖曰:"贼兵众,我兵寡,明公宜作速招军应敌。"刘焉然其说,随 即出榜招募义兵。榜文行到涿县,乃引出涿县中一个英雄。9 打开字典  宴桃园豪... : 那人不甚好读书;性宽和,寡言语,喜怒不形于色;素有大志,专好结交天下豪杰;生得身长七尺五寸,两耳垂肩,双手过膝,目能自顾其耳,面如冠玉,唇若涂脂;中山靖王刘胜之后,汉景帝阁下玄孙;姓刘,名备,字玄德。昔刘胜之子刘贞,汉武时封涿鹿亭侯,后坐酬金失侯,因此遗这一枝在涿县。玄德祖刘雄,父刘弘。弘曾举孝廉,亦尝作吏,早丧。玄德幼孤,事母至孝;家贫,贩屦 织席为业。家住本县楼桑村。其家之东南,有一大桑树,高五丈馀,遥望之,童童如车盖。相者云:"此家必出贵人。"10 打开字典 宴桃园豪... : 玄德幼时,与乡中小儿戏于树下,曰:"我为天子,当乘此车盖。"叔父刘元起奇其言,曰:"此儿非常人也!"因见玄德家贫,常资给之。年十五岁,母使游学,尝师事郑玄、卢植;与公孙瓒等为友。及刘焉发榜招军时,玄德年己二十八岁矣。当日见了榜文,慨然长叹。随后一人厉声言曰:"大丈夫不与国家出力,何故长叹?"11 打开字 典 宴桃园豪... : 玄德回视其人:身长八尺,豹头环眼,燕颔虎须,声若巨雷,势如奔马。玄德见他形貌异常,问其姓名。其人曰:"某姓张,名飞,字翼德。世居涿郡,颇有庄田,卖酒屠猪,专好结交天下豪杰。适才见公看榜而叹,故此相 问。"玄德曰:"我本汉室宗亲,姓刘,名备。今闻黄巾倡乱,有志欲破贼安民;恨力不能,故长叹耳。"飞曰:"吾颇有资财,当招募乡勇,与公同举大事,如何?"玄德甚喜,遂与同入村店中饮酒。12 打开字典 宴桃园豪... : 正饮间,见一大汉,推著一辆车子,到店门首歇了;入店坐下,便唤酒保:"快斟酒来吃,我待赶入城去投军。"玄德看其人:身长九尺,髯长二尺:面如重枣,唇若涂脂;丹凤眼,卧蚕眉:相貌堂堂,威风凛凛。玄德就邀他同坐,叩其姓名。其人曰:"吾姓关,名羽,字寿长,后改云长,河东解良人也。因本处势豪,倚势凌人,被吾杀了;逃难江湖,五六年矣。今闻此处招军破贼,特来应募。"玄德遂以己志告之。云长大喜。同到张飞庄上,共议大事。13 打开字典相关讨论 宴桃园豪... : 飞曰:"吾庄后有一桃园,花开正盛;明日当于园中祭告天地,我三人结为兄弟,协力同心,然后可图大事。"玄德、云长、齐声应曰:"如此甚好。"次日,于桃园中,备下乌牛白马祭礼等项,三人焚香,再拜而说誓曰:"念刘备、关羽、张飞,虽然异姓,既结为兄弟,则同心协力,救困扶危;上报国家,下安黎庶;不求同年同月同日生,但愿同年同月同日死。皇天后土,实鉴此心。背义忘恩,天人共戮。"誓毕,拜玄德为兄,关羽次之,张飞为弟。祭罢天地,复宰牛设酒,聚乡中勇士,得三百馀人,就桃园中痛饮一醉。来日收拾军器,但恨无马匹可乘。14 打开字典 宴桃园豪... : 正思虑间,人报"有两个客人,引一夥伴当,赶一群马,投庄上来。"玄德曰:"此天佑我也!"三人出庄迎接。原来二客乃中山大商:一名张世平,一名苏双,每年往北贩马,近因寇发而回。玄德请二人到庄,置酒管待,诉说欲讨贼安民之意。二客大喜,愿将良马五十匹相送;又赠金银五百两,镔铁一千斤,以资器用。玄德谢别二客,便命良匠打造双股剑。云长造青龙偃月刀,又名冷艳 锯,重八十二斤。张飞造丈八点钢矛。各置全身铠甲。共聚乡勇五百馀人,来见邹靖。邹靖引见太守刘焉。三人参见毕,各通姓名。玄德说起宗派,刘焉大喜,遂认玄德为侄。
"""

def generate_content_sync():
    response = client.models.generate_content(
        model="gemini-2.5-flash-preview-05-20",
        contents=prompt + "三國演義這一回出現了幾個任務 ",
    )
    print(response.usage_metadata)  # 命中快取時會顯示 cache_tokens_details 和 cached_content_token_count 欄位
    return response

generate_content_sync()

命中快取時,response.usage_metadata 會包含如下結構:

cache_tokens_details=[ModalityTokenCount(modality=<MediaModality.TEXT: 'TEXT'>, token_count=2003)]
cached_content_token_count=2003

**核心結論:**隱式快取支援自動命中與命中回饋。開發者可以通過 usage_metadata 判斷命中情況。成本節省非保證,實際效果因請求結構和使用場景而異。

Function calling

使用 openai 相容方式調用 Gemini 的 function calling 功能時,需要在請求體內部傳入tool_choice="auto",否則會報錯。

from openai import OpenAI

# 定義模型的 function 宣告
def schedule_meeting_function = {
    "name": "schedule_meeting",
    "description": "Schedules a meeting with specified attendees at a given time and date.",
    "parameters": {
        "type": "object",
        "properties": {
            "attendees": {
                "type": "array",
                "items": {"type": "string"},
                "description": "List of people attending the meeting.",
            },
            "date": {
                "type": "string",
                "description": "Date of the meeting (e.g., '2024-07-29')",
            },
            "time": {
                "type": "string",
                "description": "Time of the meeting (e.g., '15:00')",
            },
            "topic": {
                "type": "string",
                "description": "The subject or topic of the meeting.",
            },
        },
        "required": ["attendees", "date", "time", "topic"],
    },
}

# 配置 client
client = OpenAI(
    api_key="sk-***", # 換成你在 AiHubMix 生成的密鑰
    base_url="https://aihubmix.com/v1",
)

# 用 OpenAI 相容格式發送帶有 function 宣告的請求
response = client.chat.completions.create(
    model="gemini-2.0-flash",
    messages=[
        {"role": "user", "content": "Schedule a meeting with Bob and Alice for 03/14/2025 at 10:00 AM about the Q3 planning."}
    ],
    tools=[{"type": "function", "function": schedule_meeting_function}],
    tool_choice="auto" ## 📍 此處追加了 Aihubmix 相容,更穩定的請求方式
)

# 檢查是否有 function call
if response.choices[0].message.tool_calls:
    tool_call = response.choices[0].message.tool_calls[0]
    function_call = tool_call.function
    print(f"Function to call: {function_call.name}")
    print(f"Arguments: {function_call.arguments}")
    print(response.usage)
    #  在實際應用中,這裡可以調用你的 function:
    #  result = schedule_meeting(**json.loads(function_call.arguments))
else:
    print("No function call found in the response.")
    print(response.choices[0].message.content)

輸出結果示例:

Function to call: schedule_meeting
Arguments: {"attendees":["Bob","Alice"],"date":"2025-03-14","time":"10:00","topic":"Q3 planning"}
CompletionUsage(completion_tokens=28, prompt_tokens=111, total_tokens=139, completion_tokens_details=None, prompt_tokens_details=None)

Tokens 用量追踪

  1. Gemini 原生采用 usage_metadata追踪使用的 token,其中的字段对应如下:
  • prompt_token_count: 輸入 token 數
  • candidates_token_count: 輸出 token 數
  • thoughts_token_count: 推理使用的 token 數,性質上也是輸出 token
  • total_token_count: 總 token 使用量(輸入+輸出)
  1. 對於 OpenAI 相容格式,則採用 .usage 來追蹤,欄位對應如下:
  • usage.completion_tokens: 輸入 token 數
  • usage.prompt_tokens: 輸出 token 數(包含推理使用的 token 數)
  • usage.total_tokens: 總 token 使用量

使用方法如下:

from google import genai
from google.genai import types
import time

def generate():
    client = genai.Client(
        api_key="sk-***", # 換成你在 AiHubMix 生成的密鑰
        http_options={"base_url": "https://aihubmix.com/gemini"},
    )

    model = "gemini-2.5-pro-preview-03-25"
    contents = [
        types.Content(
            role="user",
            parts=[
                types.Part.from_text(text="""金融領域的「72 法則」是如何推導的?"""),
            ],
        ),
    ]
    generate_content_config = types.GenerateContentConfig(
        response_mime_type="text/plain",
    )

    final_usage_metadata = None
    
    for chunk in client.models.generate_content_stream(
        model=model,
        contents=contents,
        config=generate_content_config,
    ):
        print(chunk.text, end="")
        if chunk.usage_metadata:
            final_usage_metadata = chunk.usage_metadata
    
    # 在所有 chunk 處理完後,打印完整的 token 使用情況
    if final_usage_metadata:
        print(f"\nUsage: {final_usage_metadata}")

if __name__ == "__main__":
    generate()