首页 / 文章

Python中的Gemini API:具有模型角色与聊天对象功能的相同无状态循环结构。

使用google-genai SDK从Python调用Gemini:包括内容与部分、系统指令、令牌使用情况,以及用于在客户端记录对话历史的聊天对象。

发布者:REACTAPP.TOP阅读约 4 分钟892 词

来源: Part 4 — Call the Gemini API in Python (user / model, same loop)

继OpenAI和Claude之后,谷歌的Gemini是第三家采用相同架构的服务提供商:无状态接口、按角色标记的对话记录,以及每次调用时都会发送完整的对话历史。Gemini在命名上还有一个小巧的特别之处。

仅重命名,未重新设计

Gemini的对话结构与OpenAI和Claude基本一致,仅有两个名称上的变化。助手角色被称作model。这个称呼或许更为准确,因为“assistant”会让人联想到某种虚拟角色,而model则直接表明文本来自模型。它在列表中的位置也与之前相同。系统提示则变为system_instruction,通过配置对象传递而非包含在消息列表中。这其实是Claude“独立系统提示”理念的进一步发展。

消息列表通过 contents 来表示。列表中的每一轮对话都包含 parts,因为 Gemini 能够原生处理文本、图片和音频。在纯文本聊天中则不会显示这些内容:输入的是字符串,输出的是 resp.text。

在给出任何代码之前先提一个关于库的警告。多年来 Google 已发布了多个 Python 包,而较旧的教程中充斥着诸如 google-generativeai 以及 Bard 时代的封装类这样的已废弃导入方式。当前官方的 SDK 是 google-genai,通过 from google import genai 来导入,这里使用的也是它。

设置与单次对话

使用 pip install google-genai 进行安装,在 Google AI Studio 中创建一个密钥并将其导出为 GEMINI_API_KEY。客户端会从环境变量中获取该密钥,因此代码中不会出现密钥内容。

# current official SDK: pip install google-genai
from google import genai
from google.genai import types

client = genai.Client()  # key from env

resp = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Why does Gemini name the reply "
             "role 'model', not 'assistant'?",
    config=types.GenerateContentConfig(
        system_instruction="Be concise.",
        temperature=0,
    ),
)
print(resp.text)

与之前的两种提供方式相比,messages现已改为contents,且单次轮次可以仅是一个纯字符串。系统提示词则被放入GenerateContentConfig中。温度参数又重新成为常规配置项,而之前的Claude SDK曾将其移除。回答内容即为resp.text,令牌计数则存储在resp.usage_metadata中,分别以prompt_token_count和candidates_token_count的形式呈现,这些数值与谷歌系统的命名方式相同。务必始终记录这些数据。

托管式历史循环

可以直接自行实现ask()函数:将user和model的参数存储在列表中,每次添加一次对话内容,并再次传入contents=参数。这种方式是可行的。不过SDK还提供了OpenAI和Anthropic客户端默认不具备的便利功能:一个可自动处理内容追加的聊天对象。

chat = client.chats.create(
    model="gemini-2.5-flash",
    config=types.GenerateContentConfig(
        system_instruction="Be concise.",
    ),
)

r1 = chat.send_message(
    "Define a context window."
)
print(r1.text)

r2 = chat.send_message(
    "Now for a five-year-old."
)
print(r2.text)

for turn in chat.get_history():
    part = turn.parts[0].text[:40]
    print(turn.role, "->", part)

后续问题会在上下文中得到解答,因为聊天对象会自动重放之前的对话内容,这正是手动实现的函数会做的事。在最后遍历 get_history() 即可看到 user 和 model 轮流的发言记录,也就是上次请求时发送的那份列表。服务器端不会记住任何信息。聊天对象会在客户端保存输入内容,因此每次轮次都会继续累积输入标记,长对话会导致每次调用的成本上升。

有一点没有改变:SDK 后端的服务在多次请求之间也不会记住任何内容。

便利是有代价的,而在框架使用中这一点尤为重要。通过管理历史记录可以减少代码量并降低错误率,直到你需要删除旧的内容、对其进行总结,或将检索到的文档插入对话中时。到那时你就会希望恢复为简单的列表形式。LangChain处于这个尺度上的同一位置,但层次更高一些。既要熟悉手动管理的列表,也要了解受管理的对象,这样两者都不会限制你的灵活性。

运行示例

pip install -r requirements.txt
export GEMINI_API_KEY="AIza..."
python examples/part04_gemini.py

该脚本按顺序处理三件事:单次对话、用于确认等效性的手动列表循环,以及会显示历史记录的托管聊天功能。每次调用后都会记录令牌数量。若未设置密钥,它会立即停止并显示明确提示信息。由于这些调用是实时进行的且会产生费用,因此不要在持续集成环境中运行它。

三种提供商,相同架构

以下是对这三种服务的简要对比。OpenAI将system视为一条消息,历史记录由用户自行管理。Claude将system设为独立参数,同时要求指定max_tokens值,历史记录也由用户自行管理。Gemini则将system_instruction放入配置中,将助手命名为model,若用户允许的话会保留历史记录。这三种服务都基于相同的无状态循环机制:发送完整对话内容,接收回复,将其追加后再重复该过程。

正因如此,多提供商框架才能保持简洁,更换提供商也无需感到恐惧。迁移只需创建一个重命名映射表,而非重新编写代码。