Gemini API 无状态,需手动管理 history 数组并完整传入每次请求;PHP 中应通过 $_SESSION 或 Redis 持久化结构化 contents,严格校验 JSON 格式、角色顺序及空值边界。

PHP 本身不维护 Gemini 的多轮对话状态,必须由你主动管理 history 数组并每次完整传入请求体 —— 否则 Gemini 会当作全新对话处理,丢掉上下文。
为什么 Gemini API 不自动记住对话?
Gemini 的 generateContent 接口是无状态的:它没有 session、不认 cookie、也不绑定 request ID。每次调用都是独立请求,模型只看到你本次传入的 contents(含历史消息)。
常见错误现象:
- 连续两次调用,第二次回复突然“忘了”前文,像第一次聊天
- 手动拼接
contents时漏掉某条role: "user"或role: "model" - 把
history存在 PHP session 里但没做序列化/反序列化校验,导致 JSON 结构损坏
如何正确构造带 history 的请求体?
必须把全部对话轮次(用户 + 模型回复)按时间顺序塞进 contents 数组,每条是 { "role": "...", "parts": [...] } 结构。
立即学习“PHP免费学习笔记(深入)”;
实操要点:
-
role只能是"user"或"model"(不是"assistant") -
parts是数组,即使只有一段文本也要包成[{ "text": "..." }] - 历史消息必须包含所有已发生的交互,不能只传最新一条
- PHP 中建议用
json_encode($contents, JSON_UNESCAPED_UNICODE)避免中文乱码
示例片段:
$contents = [ ["role" => "user", "parts" => [["text" => "你好"]]], ["role" => "model", "parts" => [["text" => "你好!有什么可以帮您?"]]], ["role" => "user", "parts" => [["text" => "上一句你说了什么?"]]], ];
PHP 里怎么安全持久化 history?
不能依赖全局变量或静态属性 —— PHP-FPM 每次请求是新进程,变量不跨请求存活。
推荐方案:
- 用
$_SESSION:开启 session 后,把$contents数组存进$_SESSION['gemini_history'],每次请求前读取、追加、再写回 - 用 Redis:适合多服务器部署,key 可设为
gemini:conv:{uuid},value 存 JSON 字符串(注意控制长度,避免超限) - 避免直接存 raw response body:只存结构化
contents,别存整个 API 响应,否则解析成本高且易出错
关键细节:从存储读取后,务必用 json_decode(..., true) 并检查是否为数组,防止因数据损坏导致 Invalid argument supplied for foreach()。
容易被忽略的边界情况
实际跑通一轮不难,但真实场景下这些点常导致中断:
- 用户发空消息或纯空白字符,
parts为空数组会触发 400 错误 —— 调用前需trim()并跳过空输入 - Gemini 对单次
contents总长度有限制(通常 ~32k tokens),history 累积太多会报413 Payload Too Large—— 需定期截断,比如只保留最近 10 轮 - 不同 Gemini 模型(如
gemini-1.5-flashvsgemini-1.0-pro)对role顺序敏感度不同,严格按 user→model→user→model 交替
history 不是“开了就自动好”,它是一段需要你亲手裁剪、校验、续传的 JSON 数组 —— 漏一个逗号,少一对引号,或者角色写错,API 就直接拒绝。



















