主题
Google AI Gemini
https://ai.google.dev/gemini-api/docs
目录
- Maven 依赖
- API 密钥
- 可用模型
- GoogleAiGeminiChatModel
- GoogleAiGeminiStreamingChatModel
- 安全设置与安全评级
- 工具
- 结构化输出
- Python 代码执行
- 多模态
- 思考
- Gemini 文件 API
- 上下文缓存
- 批处理
Maven 依赖
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-google-ai-gemini</artifactId>
<version>1.22.0</version>
</dependency>API 密钥
在此免费获取 API 密钥:https://ai.google.dev/gemini-api/docs/api-key 。
可用模型
请在文档中查看可用模型列表。
gemini-3-pro-previewgemini-2.5-progemini-2.5-flashgemini-2.5-flash-litegemini-2.0-flashgemini-2.0-flash-lite
GoogleAiGeminiChatModel
常用的 chat(...) 方法均可用:
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
...
.build();
String response = gemini.chat("Hello Gemini!");同样也有 ChatResponse chat(ChatRequest req) 方法:
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.build();
ChatResponse chatResponse = gemini.chat(ChatRequest.builder()
.messages(UserMessage.from(
"How many R's are there in the word 'strawberry'?"))
.build());
String response = chatResponse.aiMessage().text();配置
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.httpClientBuilder(...)
.defaultRequestParameters(...)
.apiKey(System.getenv("GEMINI_AI_KEY"))
.baseUrl(...)
.modelName("gemini-2.5-flash")
.maxRetries(...)
.temperature(1.0)
.topP(0.95)
.topK(64)
.seed(42)
.frequencyPenalty(...)
.presencePenalty(...)
.maxOutputTokens(8192)
.timeout(Duration.ofSeconds(60))
.responseFormat(ResponseFormat.JSON) // or .responseFormat(ResponseFormat.builder()...build())
.stopSequences(List.of(...))
.toolConfig(GeminiFunctionCallingConfig.builder()...build()) // or below
.toolConfig(GeminiMode.ANY, List.of("fnOne", "fnTwo"))
.allowCodeExecution(true)
.includeCodeExecution(true)
.logRequestsAndResponses(true)
.safetySettings(List<GeminiSafetySetting> or Map<GeminiHarmCategory, GeminiHarmBlockThreshold>)
.thinkingConfig(...)
.returnThinking(true)
.sendThinking(true)
.responseLogprobs(...)
.logprobs(...)
.enableEnhancedCivicAnswers(...)
.mediaResolution(GeminiMediaResolutionLevel.MEDIA_RESOLUTION_HIGH)
.mediaResolutionPerPartEnabled(true)
.listeners(...)
.supportedCapabilities(...)
.build();默认请求参数
除了(或连同)上面展示的独立 builder 方法,你可以通过 defaultRequestParameters(...) 提供单个 ChatRequestParameters 对象。这些参数会应用于该模型发出的每个请求,除非它们被某个单独 ChatRequest 的参数覆盖。
你可以传入通用的 ChatRequestParameters,或 Gemini 专有的 GoogleAiGeminiChatRequestParameters。后者额外暴露了像 aspectRatio 与 imageSize 这样的 Gemini 独有选项:
java
GoogleAiGeminiChatRequestParameters parameters = GoogleAiGeminiChatRequestParameters.builder()
.modelName("gemini-2.5-flash")
.temperature(1.0)
.maxOutputTokens(8192)
.aspectRatio("16:9") // Gemini-specific
.imageSize("2K") // Gemini-specific
.build();
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.defaultRequestParameters(parameters)
.build();当同一参数既通过 defaultRequestParameters(...) 设置、又通过某个独立 builder 方法(例如 modelName(String))设置时,由独立 builder 方法设置的值优先:
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.defaultRequestParameters(GoogleAiGeminiChatRequestParameters.builder()
.modelName("gemini-2.5-flash")
.temperature(1.0)
.build())
.temperature(0.0) // overrides temperature from defaultRequestParameters
.build();
// effective parameters: modelName=gemini-2.5-flash, temperature=0.0GoogleAiGeminiStreamingChatModel
GoogleAiGeminiStreamingChatModel 允许逐词元流式输出响应的文本。响应必须由一个 StreamingChatResponseHandler 处理。
java
StreamingChatModel gemini = GoogleAiGeminiStreamingChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.build();
CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
gemini.chat("Tell me a joke about Java", new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.print(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
futureResponse.complete(completeResponse);
}
@Override
public void onError(Throwable error) {
futureResponse.completeExceptionally(error);
}
});
futureResponse.join();安全设置与安全评级
Gemini 会把你要发送的提示词与它生成的内容,都对照一组危害类别(例如 HARM_CATEGORY_HARASSMENT 或 HARM_CATEGORY_DANGEROUS_CONTENT)来检查。
你通过在模型 builder 上的 safetySettings(...) 来控制这些检查的严格程度:
java
ChatModel model = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.safetySettings(Map.of(
GeminiHarmCategory.HARM_CATEGORY_HARASSMENT, GeminiHarmBlockThreshold.BLOCK_ONLY_HIGH,
GeminiHarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, GeminiHarmBlockThreshold.BLOCK_LOW_AND_ABOVE))
.build();Gemini 会在响应上上报这些检查的结果。要读取它,请把 ChatResponse.metadata() 转换为 GoogleAiGeminiChatResponseMetadata:
java
ChatResponse chatResponse = model.chat(ChatRequest.builder()
.messages(UserMessage.from("Hello!"))
.build());
var metadata = (GoogleAiGeminiChatResponseMetadata) chatResponse.metadata();
// how the generated content was rated
for (GeminiSafetyRating rating : metadata.safetyRatings()) {
System.out.println(rating.category() + " -> " + rating.probability());
}可获取三部分信息:
| 方法 | 含义 |
|---|---|
safetyRatings() | 生成内容如何被评级,每个危害类别一项。无则为空。 |
promptSafetyRatings() | 你的提示词如何被评级。无则为空。 |
blockReason() | Gemini 直接拒绝该提示词的原因,若未拒绝则为 null。 |
当 Gemini 拒绝一个提示词时,它完全不返回任何内容。那种情况下 blockReason() 会被设置(例如 "SAFETY"、"PROHIBITED_CONTENT" 或 "BLOCKLIST"),AiMessage 不带任何文本,且 finishReason() 为 CONTENT_FILTER:
java
var metadata = (GoogleAiGeminiChatResponseMetadata) chatResponse.metadata();
if (metadata.blockReason() != null) {
System.out.println("Prompt was rejected: " + metadata.blockReason());
metadata.promptSafetyRatings().forEach(rating ->
System.out.println(" " + rating.category() + ": " + rating.probability()));
}GeminiSafetyRating.category() 与 probability() 是持有原始 API 值的普通 String,因此 Google 日后引入的危害类别会被原样传递,而不是弄坏你的应用。
同样的数据在 GoogleAiGeminiStreamingChatModel(在传给 onCompleteResponse 的 ChatResponse 上)与 GoogleAiGeminiBatchChatModel 上都可获取。
工具
工具(又称函数调用)受支持,包括并行调用。你可以使用 chat(ChatRequest) 方法,它接受一个可被配置为一个或多个 ToolSpecification 的 ChatRequest,从而让 Gemini 知道它可以请求调用一个函数。或者你可以用 LangChain4j 的 AiServices 来定义它们。
这里是一个用 AiServices 的天气工具示例:
java
record WeatherForecast(
String location,
String forecast,
int temperature) {}
class WeatherForecastService {
@Tool("Get the weather forecast for a location")
WeatherForecast getForecast(
@P("Location to get the forecast for") String location) {
if (location.equals("Paris")) {
return new WeatherForecast("Paris", "sunny", 20);
} else if (location.equals("London")) {
return new WeatherForecast("London", "rainy", 15);
} else if (location.equals("Tokyo")) {
return new WeatherForecast("Tokyo", "warm", 32);
} else {
return new WeatherForecast("Unknown", "unknown", 0);
}
}
}
interface WeatherAssistant {
String chat(String userMessage);
}
WeatherForecastService weatherForecastService =
new WeatherForecastService();
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.temperature(0.0)
.build();
WeatherAssistant weatherAssistant =
AiServices.builder(WeatherAssistant.class)
.chatModel(gemini)
.tools(weatherForecastService)
.build();
String tokyoWeather = weatherAssistant.chat(
"What is the weather forecast for Tokyo?");
System.out.println("Gemini> " + tokyoWeather);
// Gemini> The weather forecast for Tokyo is warm
// with a temperature of 32 degrees.使用 $ref、$defs 或原始 JSON Schema 的工具参数
工具参数通常用 Gemini 的 parameters 字段来描述,它理解一小组固定的 schema 关键字。标准 JSON Schema 更进一步:它能用 $ref 与 $defs 把文档的一部分指向另一部分,且它有像 minimum 与 maximum 这样 parameters 无处安放的关键字。
当工具参数包含这类东西时,LangChain4j 改通过 parametersJsonSchema(Gemini 中接受纯 JSON Schema 的那个字段)发送它们,schema 原封不动地抵达 API。无需配置,也无需切换开关:
java
JsonObjectSchema priceRange = JsonObjectSchema.builder()
.addNumberProperty("min")
.addNumberProperty("max")
.build();
ToolSpecification searchProducts = ToolSpecification.builder()
.name("search_products")
.description("Search the catalog")
.parameters(JsonObjectSchema.builder()
.definitions(Map.of("PriceRange", priceRange))
.addStringProperty("query")
// a reference to the definition above, resolved by Gemini
.addProperty("retail_price", JsonReferenceSchema.builder()
.reference("PriceRange")
.build())
// a fragment of JSON Schema, sent exactly as written
.addProperty("max_results", JsonRawSchema.from(
"{\"type\":\"integer\",\"minimum\":1,\"maximum\":50}"))
.required("query")
.build())
.build();这并不局限于你手写的 schema。它也覆盖 LangChain4j 为你构建的工具:一个参数类型自引用的 @Tool 方法,以及 schema 使用 $ref 的 MCP 工具。
响应 schema 的处理方式相同,见原始响应 Schema。
NOTE
Gemini 会拒绝 $schema 关键字。来自 schema 生成器的文档通常以 "$schema": "https://json-schema.org/draft/2020-12/schema" 开头,因此在把文档传给 JsonRawSchema 之前请删掉那行,否则请求会以 400 失败。
结构化输出
关于结构化输出的更多信息,见这里。
从自由文本做类型安全的数据抽取
大语言模型非常擅长从非结构化文本中抽取结构化信息。在下面的示例中,我们借助 AiServices 从一个天气预报文本中抽取出一个类型安全的 WeatherForecast 对象:
java
// A type-safe / strongly-typed object
// representing the weather forecast
record WeatherForecast(
@Description("minimum temperature")
Integer minTemperature,
@Description("maximum temperature")
Integer maxTemperature,
@Description("chances of rain")
boolean rain
) { }
// An interface contract, to interact with Gemini
interface WeatherForecastAssistant {
WeatherForecast extract(String forecast);
}
// Let's extract the data:
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.supportedCapabilities(RESPONSE_FORMAT_JSON_SCHEMA) // this is required to enable structured outputs feature
.build();
WeatherForecastAssistant forecastAssistant =
AiServices.builder(WeatherForecastAssistant.class)
.chatModel(gemini)
.build();
WeatherForecast forecast = forecastAssistant.extract("""
Morning: The day dawns bright and clear in Osaka, with crisp
autumn air and sunny skies. Expect temperatures to hover
around 18°C (64°F) as you head out for your morning stroll
through Namba.
Afternoon: The sun continues to shine as the city buzzes with
activity. Temperatures climb to a comfortable 22°C (72°F).
Enjoy a leisurely lunch at one of Osaka's many outdoor cafes,
or take a boat ride on the Okawa River to soak in the beautiful
scenery.
Evening: As the day fades, expect clear skies and a slight chill
in the air. Temperatures drop to 15°C (59°F). A cozy dinner at a
traditional Izakaya will be the perfect way to end your day in
Osaka.
Overall: A beautiful autumn day in Osaka awaits, perfect for
exploring the city's vibrant streets, enjoying the local cuisine,
and soaking in the sights.
Don't forget: Pack a light jacket for the evening and wear
comfortable shoes for all the walking you'll be doing.
""");响应格式 / 响应 Schema
你可以在创建 GoogleAiGeminiChatModel 时,或在调用它时指定一个 ResponseFormat。
尤其是,在 JSON 格式的情形下,你可以选择通过创建相应 Java 对象、或提供原始 JSON schema,以编程方式定义 schema。
响应 Schema
来看一个在创建 GoogleAiGeminiChatModel 时为一个食谱定义 JSON schema 的示例。本例中我们用 JsonObjectSchema 类声明该 JSON schema。
java
ResponseFormat responseFormat = ResponseFormat.builder()
.type(ResponseFormatType.JSON)
.jsonSchema(JsonSchema.builder() // see [1] below
.rootElement(JsonObjectSchema.builder()
.addStringProperty("title")
.addIntegerProperty("preparationTimeMinutes")
.addProperty("ingredients", JsonArraySchema.builder()
.items(new JsonStringSchema())
.build())
.addProperty("steps", JsonArraySchema.builder()
.items(new JsonStringSchema())
.build())
.build())
.build())
.build();
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.responseFormat(responseFormat)
.build();
String recipeResponse = gemini.chat("Suggest a dessert recipe with strawberries");
System.out.println(recipeResponse);请注意:
- [1] -
JsonSchema可以用JsonSchemas.jsonSchemaFrom()辅助方法从你的类自动生成。
java
JsonSchema jsonSchema = JsonSchemas.jsonSchemaFrom(TripItinerary.class).get();来看一个在调用 GoogleAiGeminiChatModel 时为一个食谱定义 JSON schema 的示例:
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.build();
ResponseFormat responseFormat = ...;
ChatRequest chatRequest = ChatRequest.builder()
.messages(UserMessage.from("Suggest a dessert recipe with strawberries"))
.responseFormat(responseFormat)
.build();
ChatResponse chatResponse = gemini.chat(chatRequest);
System.out.println(chatResponse.aiMessage().text());原始响应 Schema
另一个示例展示了如何用 Gemini API 的 responseJsonSchema,通过 JsonRawSchema 类提供一个原始 JSON schema。请注意谨慎,只使用 Gemini API 受支持的类型。每当响应 schema 内部任何位置含有一个 JsonRawSchema 或 JsonReferenceSchema 时,都会使用该字段,因此 $ref 与 $defs 在这里同样有效。
String rawSchema = """
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"birthDate": {
"type": "string",
"format": "date"
},
"preferredContactTime": {
"type": "string",
"format": "time"
},
"height": {
"type": "number",
"minimum": 1.83,
"maximum": 1.88
},
"role": {
"type": "string",
"enum": ["developer", "maintainer", "researcher"]
},
"isAvailable": { "type": "boolean" },
"tags": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"maxItems": 5
},
"address": {
"type": "object",
"properties": {
"city": { "type": "string" },
"streetName": { "type": "string" },
"streetNumber": { "type": "string" }
},
"required": ["city", "streetName", "streetNumber"],
"additionalProperties": true
}
},
"required": ["name", "birthDate", "height", "role", "tags", "address"]
}
""";
JsonRawSchema jsonRawSchema = JsonRawSchema.builder().schema(rawSchema).build();
JsonSchema jsonSchema = JsonSchema.builder().rootElement(jsonRawSchema).build();
ResponseFormat responseFormat = ResponseFormat.builder()
.type(ResponseFormatType.JSON)
.jsonSchema(jsonSchema)
.build();
GoogleAiGeminiChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(GOOGLE_AI_GEMINI_API_KEY)
.modelName("gemini-2.5-flash-lite")
.logRequests(true)
.logResponses(true)
.responseFormat(responseFormat)
.build();
UserMessage userMessage = UserMessage.from(
"""
Tell me about a detective named Sherlock Holmes,
who was born on November 28 1852 and sees the world over six feet from the ground.
He is a trouble-seeker, an active volunteer and lives in London at 221B Baker Street.
He plays the violin and he likes to conduct various physics and chemistry experiments.
He accepts clients or prefers to be contacted at 09:00am.
""");
ChatResponse response = gemini.chat(ChatRequest.builder()
.messages(userMessage)
.build());JSON 模式
你可以强制 Gemini 以 JSON 回复:
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.responseFormat(ResponseFormat.JSON)
.build();
String roll = gemini.chat("Roll a 6-sided dice");
System.out.println(roll);
// {"roll": "3"}一段系统提示词可以进一步描述 JSON 输出应当长什么样。Gemini 通常会遵循建议的 schema,但并不保证。若你要保证 JSON schema 被应用,就应当定义一个响应格式,如上一节所述。
Python 代码执行
除函数调用外,Google AI Gemini 还能让你在一个沙箱环境中创建并执行 Python 代码。这在需要更高级计算或逻辑的情形下特别有意思。
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.allowCodeExecution(true)
.includeCodeExecutionOutput(true)
.build();有两个 builder 方法:
allowCodeExecution(true):让 Gemini 知道它可以做一些 Python 编码includeCodeExecutionOutput(true):若你想看到它想出的实际 Python 脚本,及其执行的结果
java
ChatResponse mathQuizz = gemini.chat(
SystemMessage.from("""
You are an expert mathematician.
When asked a math problem or logic problem,
you can solve it by creating a Python program,
and execute it to return the result.
"""),
UserMessage.from("""
Implement the Fibonacci and Ackermann functions.
What is the result of `fibonacci(22)` - ackermann(3, 4)?
""")
);Gemini 会撰写一个 Python 脚本,在其服务器上执行,并返回结果。由于我们要求看到执行的代码与输出,答案会如下所示:
Code executed:def fibonacci(n): if n <= 1: return n else: return fibonacci(n-1) + fibonacci(n-2)
def ackermann(m, n): if m == 0: return n + 1 elif n == 0: return ackermann(m - 1, 1) else: return ackermann(m - 1, ackermann(m, n - 1))
print(fibonacci(22) - ackermann(3, 4))
Output:17586
The result of `fibonacci(22) - ackermann(3, 4)` is **17586**.
I implemented the Fibonacci and Ackermann functions in Python.
Then I called `fibonacci(22) - ackermann(3, 4)` and printed the result.若我们没有要求代码/输出,我们只会收到如下文本:
The result of `fibonacci(22) - ackermann(3, 4)` is **17586**.
I implemented the Fibonacci and Ackermann functions in Python.
Then I called `fibonacci(22) - ackermann(3, 4)` and printed the result.多模态
Gemini 是一个多模态模型,意味着它既能接受也能生成文本之外的不同_模态_。
输入模态
在输入端,Gemini 接受:
- 图片(
ImageContent) - 视频(
VideoContent) - 音频文件(
AudioContent) - PDF 文件(
PdfFileContent)
下面的示例展示了如何把一段文本提示词与一张图片混合:
java
// PNG of the cute colorful parrot mascot of the LangChain4j project
String base64Img = b64encoder.encodeToString(readBytes(
"https://avatars.githubusercontent.com/u/132277850?v=4"));
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.build();
ChatResponse response = gemini.chat(
UserMessage.from(
ImageContent.from(base64Img, "image/png"),
TextContent.from("""
Do you think this logo fits well
with the project description?
""")
)
);图像生成输出
一些 Gemini 模型(如 gemini-2.5-flash-image)能在其响应的一部分中生成图像。当有图像生成时,它们会被存储在 AiMessage 属性中,并可通过 GeneratedImageHelper 工具类访问。
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey("Your API Key")
.modelName("gemini-2.5-flash-image")
.build();
ChatResponse response = gemini.chat(UserMessage.from("A high-resolution, studio-lit product photograph of a minimalist ceramic coffee mug in matte black"));
// Extract generated images from the response
AiMessage aiMessage = response.aiMessage();
List<Image> generatedImages = GeneratedImageHelper.getGeneratedImages(aiMessage);
if (GeneratedImageHelper.hasGeneratedImages(aiMessage)) {
System.out.println("Generated " + generatedImages.size() + " image(s)");
System.out.println("Text response: " + aiMessage.text());
for (Image image : generatedImages) {
String base64Data = image.base64Data();
String mimeType = image.mimeType();
// You can now save the image, display it, or process it further
// For example, save to file:
byte[] imageBytes = Base64.getDecoder().decode(base64Data);
Files.write(Paths.get("generated_image.png"), imageBytes);
}
} else {
System.out.println("Text response: " + aiMessage.text());
}媒体分辨率
你可以控制发送给模型的媒体(图像、视频、PDF)的分辨率。这可以全局设置,也可逐块(逐图)设置。
全局媒体分辨率
要为请求中的所有媒体块设置媒体分辨率,请使用 .mediaResolution() builder 方法:
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.mediaResolution(GeminiMediaResolutionLevel.MEDIA_RESOLUTION_LOW) // or MEDIUM, HIGH, ULTRA_HIGH, UNSPECIFIED
.build();逐块媒体分辨率(Gemini 3)
在 Gemini 3 上,你可以用 ImageContent 中的 DetailLevel 为单张图像指定分辨率。先在 builder 中启用此特性,然后在 ImageContent 上设置细节级别:
java
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-3-pro-preview")
.mediaResolutionPerPartEnabled(true)
.build();
ChatResponse response = gemini.chat(
UserMessage.from(
ImageContent.from(url1, ImageContent.DetailLevel.LOW),
ImageContent.from(url2, ImageContent.DetailLevel.HIGH),
TextContent.from("Compare these two images")
)
);受支持的 DetailLevel 值及其到 Gemini 分辨率级别的映射:
LOW->MEDIA_RESOLUTION_LOWMEDIUM->MEDIA_RESOLUTION_MEDIUMHIGH->MEDIA_RESOLUTION_HIGHULTRA_HIGH->MEDIA_RESOLUTION_ULTRA_HIGH(词元数最高,某些特定用例如计算机操作所必需)AUTO->MEDIA_RESOLUTION_UNSPECIFIED
思考
GoogleAiGeminiChatModel 与 GoogleAiGeminiStreamingChatModel 都支持思考。
以下参数也控制思考行为:
GeminiThinkingConfig.includeThoughts与thinkingBudget:启用思考,更多细节见这里。returnThinking:控制是否在AiMessage.thinking()中返回思考(若可用),以及在使用GoogleAiGeminiStreamingChatModel时是否调用StreamingChatResponseHandler.onPartialThinking()与TokenStream.onPartialThinking()回调。默认禁用。若启用,思考签名也会被存储并返回在AiMessage.attributes()中。sendThinking:控制是否在后续请求中把存储于AiMessage的思考与签名发送给 LLM。- 默认禁用。
NOTE
请注意,当 returnThinking 未设置(为 null)而 thinkingConfig 已设置时,思考文本会被前置到 AiMessage.text() 字段内实际响应之前,且会调用 StreamingChatResponseHandler.onPartialResponse() 而非 StreamingChatResponseHandler.onPartialThinking()。
下面是一个如何配置思考的示例:
java
GeminiThinkingConfig thinkingConfig = GeminiThinkingConfig.builder()
.includeThoughts(true)
.thinkingBudget(250)
.build();
ChatModel model = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
.modelName("gemini-2.5-flash")
.thinkingConfig(thinkingConfig)
.returnThinking(true)
.sendThinking(true)
.build();Gemini 3 Pro
在 Gemini 3 Pro 上,思考配置引入了一个_思考级别_,取值为 "low" 或 "high"(默认为 high)。可以在思考配置内设置该级别:
java
GoogleAiGeminiChatModel modelHigh = GoogleAiGeminiChatModel.builder()
.modelName("gemini-3-pro-preview")
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
.thinkingConfig(GeminiThinkingConfig.builder()
.thinkingLevel(LOW) // or HIGH
.build())
.sendThinking(true)
.returnThinking(true)
.build();你可以传入一个字符串 "high" / "low",或一个 GeminiThinkingConfig.GeminiThinkingLevel.HIGH / GeminiThinkingConfig.GeminiThinkingLevel.LOW 枚举值。
使用 Gemini 3 Pro 时,必须把 sendThinking() 与 returnThinking() 配置为 true,以确保思考签名被正确地传给模型。
Gemini 文件 API
Gemini 文件 API 让你能上传并管理供 Gemini 模型使用的媒体文件。当你的总请求大小超出 20 MB 时这尤其有用,因为文件可以单独上传,并在内容生成请求中被引用。
主要特性
- 多模态支持:上传图像、音频、视频与文档
- 存储:文件存储 48 小时
- 容量:每个项目最多 20 GB 文件,单个文件最大 2 GB
- 免费:文件 API 免费可用
上传文件
你可以用两种方式上传文件:
从文件路径:
java
GeminiFiles filesApi = GeminiFiles.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.build();
// Upload from a file path
Path filePath = Paths.get("path/to/your/file.pdf");
GeminiFile uploadedFile = filesApi.uploadFile(filePath, "My Document");
System.out.println("File uploaded: " + uploadedFile.name());
System.out.println("File URI: " + uploadedFile.uri());从字节数组:
java
byte[] fileBytes = Files.readAllBytes(Paths.get("path/to/file.jpg"));
GeminiFile uploadedFile = filesApi.uploadFile(
fileBytes,
"image/jpeg",
"My Image"
);管理文件
列出所有已上传的文件:
java
List<GeminiFile> files = filesApi.listFiles();
for (GeminiFile file : files) {
System.out.println("File: " + file.displayName() + " (" + file.name() + ")");
}获取文件元数据:
java
GeminiFile file = filesApi.getMetadata("files/abc123");
System.out.println("File size: " + file.sizeBytes() + " bytes");
System.out.println("MIME type: " + file.mimeType());
System.out.println("Created: " + file.createTime());
System.out.println("Expires: " + file.expirationTime());删除一个文件:
java
filesApi.deleteFile("files/abc123");
System.out.println("File deleted successfully");文件状态
文件在其生命周期中可处于不同状态:
java
GeminiFile file = filesApi.getMetadata("files/abc123");
if (file.isActive()) {
System.out.println("File is ready to use");
} else if (file.isProcessing()) {
System.out.println("File is still being processed");
} else if (file.isFailed()) {
System.out.println("File processing failed");
}上下文缓存
上下文缓存 API会把庞大的、频繁复用的上下文(一段系统指令、长文档)一次性存储在 Google 服务器上,从而后续请求按名称引用它而非重发,降低输入词元成本与延迟。
GeminiCaches 管理缓存生命周期(create / get / list / delete)。消息使用聊天模型所用的同一套消息映射来缓存,因此你始终停留在 LangChain4j 的 ChatMessage 领域内:
java
GeminiCaches caches = GeminiCaches.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.build();
// Cache a large, reusable context (a system instruction plus a long document)
GeminiCachedContent cache = caches.createCache(
"gemini-2.5-flash",
List.of(
SystemMessage.from("You are a precise assistant answering questions about the attached document."),
UserMessage.from(longDocumentText)),
Duration.ofHours(1));
// Reuse it across many requests via cachedContentName
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.cachedContentName(cache.name())
.build();
String answer = gemini.chat("Summarize the cached document in 3 bullet points.");
// Manage the cache lifecycle
caches.getCache(cache.name());
caches.listCaches();
caches.deleteCache(cache.name());createCache 有三种过期形式:不带过期参数时使用 API 默认值(当前为 1 小时);带一个 Duration 时设置相对的存活时间;带一个 Instant 时设置绝对的到期时间。
注:显式上下文缓存需要付费档;免费档不可用。
批处理
GoogleAiBatchChatModel
GoogleAiBatchChatModel 提供了一个接口,以降低成本(标准价格的 50%)异步处理大量聊天请求。它非常适合非紧急、大规模、具有 24 小时周转 SLO 的任务。
创建批任务
内联批量创建:
java
GoogleAiGeminiBatchChatModel batchModel = GoogleAiGeminiBatchChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.build();
// Create batch requests
List<ChatRequest> requests = List.of(
ChatRequest.builder()
.messages(UserMessage.from("What is the capital of France?"))
.build(),
ChatRequest.builder()
.messages(UserMessage.from("What is the capital of Germany?"))
.build(),
ChatRequest.builder()
.messages(UserMessage.from("What is the capital of Italy?"))
.build()
);
// Submit the batch (generic API, no Gemini-specific options)
BatchResponse<ChatResponse> response = batchModel.submit(new BatchRequest<>(requests));
// Or, to set a Gemini-specific display name and priority, use GeminiBatchRequest:
BatchResponse<ChatResponse> response = batchModel.submit(GeminiBatchRequest.from(
requests,
"Geography Questions Batch", // display name
0L // priority (optional, defaults to 0)
));基于文件的批量创建:
对于较大的批次,或当你需要对请求格式有更多控制时,你可以从一个上传的文件创建批任务:
java
// First, upload a file with batch requests
GeminiFiles filesApi = GeminiFiles.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.build();
GeminiFile uploadedFile = filesApi.uploadFile(
Paths.get("batch_chat_requests.jsonl"),
"Batch Chat Requests"
);
// Wait for file to be active
while (uploadedFile.isProcessing()) {
Thread.sleep(1000);
uploadedFile = filesApi.getMetadata(uploadedFile.name());
}
// Create batch from file
BatchResponse<ChatResponse> response = batchModel.submit("My Batch Job", uploadedFile);处理批量响应
一个 BatchResponse 会暴露当前的 state(),以及逐次请求的 results() 和 responses() / errors() 便捷视图。依据 state() 分支(用 state().isTerminal() 判断批量任务是否仍在进行中):
java
BatchResponse<ChatResponse> response = batchModel.submit(new BatchRequest<>(requests));
if (!response.state().isTerminal()) {
System.out.println("Batch is " + response.state());
System.out.println("Batch ID: " + response.batchId());
} else if (response.state() == BatchState.SUCCEEDED) {
System.out.println("Batch completed successfully!");
// Process successful responses
for (ChatResponse chatResponse : response.responses()) {
System.out.println(chatResponse.aiMessage().text());
}
// Check for individual request errors within the batch
if (!response.errors().isEmpty()) {
System.out.println("Some requests failed:");
for (BatchError error : response.errors()) {
System.err.println("Error code: " + error.code() + ", message: " + error.message());
}
}
} else {
System.err.println("Batch " + response.state() + ": " + response.errors());
}注: 一个 state() == SUCCEEDED 的批次表示批任务已完成,但批次内的个别请求可能失败了。errors() 列表包含任何个别请求的失败(例如超时、速率限制),而 responses() 包含成功的响应。二者都是便捷视图,且永不为 null(无可上报时为空),因此请用 !responses().isEmpty() / !errors().isEmpty() 来优雅地处理部分失败。
将结果与请求关联
responses() 与 errors() 是扁平视图,会丢失"哪个输入产生了哪个结果"的对应关系。当你需要把每个结果映射回其来源请求时,请改用 results():它每个请求返回一个 BatchItemResult,顺序与提交的请求一致,因此第 i 个结果对应第 i 个请求。每个结果要么是 BatchItemResult.Success(携带 response()),要么是 BatchItemResult.Failure(携带 error()):
java
BatchResponse<ChatResponse> result = batchModel.submit(new BatchRequest<>(requests));
// ... poll until terminal ...
List<BatchItemResult<ChatResponse>> results = result.results();
for (int i = 0; i < results.size(); i++) {
BatchItemResult<ChatResponse> item = results.get(i);
if (item.isSuccess()) {
System.out.println("Request #" + i + " -> " + item.response().aiMessage().text());
} else {
BatchError error = item.error();
System.err.println("Request #" + i + " failed: " + error.code() + " - " + error.message());
}
}轮询结果
由于批处理是异步的,你需要轮询结果(结果可能最多耗时 24 小时处理):
java
BatchResponse<ChatResponse> result = batchModel.submit(new BatchRequest<>(requests));
String batchId = result.batchId();
// Poll until the batch reaches a terminal state
while (!result.state().isTerminal()) {
Thread.sleep(5000); // Wait 5 seconds between polls
result = batchModel.retrieve(batchId);
}
// Process final result
if (result.state() == BatchState.SUCCEEDED) {
System.out.println("Successful responses: " + result.responses().size());
for (ChatResponse chatResponse : result.responses()) {
System.out.println(chatResponse.aiMessage().text());
}
// Handle any individual request failures
if (!result.errors().isEmpty()) {
System.out.println("Failed requests: " + result.errors().size());
for (BatchError error : result.errors()) {
System.err.println("Error: " + error.code() + " - " + error.message());
}
}
} else {
System.err.println("Batch did not succeed: " + result.state());
}管理批量任务
取消一个批量任务:
java
String batchId = // ... obtained from submit(...)
try {
batchModel.cancel(batchId);
System.out.println("Batch cancelled successfully");
} catch (HttpException e) {
System.err.println("Failed to cancel batch: " + e.getMessage());
}删除一个批量任务:
java
batchModel.deleteBatchJob(batchId);
System.out.println("Batch deleted successfully");列出批量任务:
java
// List first page of batch jobs
BatchPage<ChatResponse> page = batchModel.list(new BatchPagination(10, null));
for (BatchResponse<ChatResponse> batch : page.batches()) {
System.out.println("Batch: " + batch);
}
// Get next page if available
if (page.nextPageToken() != null) {
BatchPage<ChatResponse> nextPage = batchModel.list(new BatchPagination(10, page.nextPageToken()));
}基于文件的批处理
对于高级用例,你可以把批量请求写入一个 JSONL 文件并上传它:
java
// Create a JSONL file with batch requests
Path batchFile = Files.createTempFile("batch", ".jsonl");
try (JsonLinesWriter writer = new StreamingJsonLinesWriter(batchFile)) {
List<BatchFileRequest<ChatRequest>> fileRequests = List.of(
new BatchFileRequest<>("request-1", ChatRequest.builder()
.messages(UserMessage.from("Question 1"))
.build()),
new BatchFileRequest<>("request-2", ChatRequest.builder()
.messages(UserMessage.from("Question 2"))
.build())
);
batchModel.writeBatchToFile(writer, fileRequests);
}
// Upload the file
GeminiFiles filesApi = GeminiFiles.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.build();
GeminiFile uploadedFile = filesApi.uploadFile(batchFile, "Batch Chat Requests");
// Create batch from file
BatchResponse<ChatResponse> response = batchModel.submit("File-Based Chat Batch", uploadedFile);批任务状态
BatchState 枚举表示批任务的各种可能状态:
PENDING:批任务已排队,等待处理RUNNING:批任务正在处理SUCCEEDED:批任务成功完成(终态)FAILED:批任务处理失败(终态)CANCELLED:批任务被用户取消(终态)EXPIRED:批任务在完成前过期(终态)UNSPECIFIED:状态未知或未提供
在 BatchResponse.state() 上用 BatchState.isTerminal() 来判断何时可以停止轮询。
设置批优先级
高优先级的批次会在低优先级的之前被处理。通过 GeminiBatchRequest 设置优先级:
java
// High priority batch
BatchResponse<ChatResponse> highPriority = batchModel.submit(GeminiBatchRequest.from(
urgentRequests, "Urgent Batch", 100L));
// Low priority batch
BatchResponse<ChatResponse> lowPriority = batchModel.submit(GeminiBatchRequest.from(
backgroundRequests, "Background Batch", -50L));配置
GoogleAiGeminiBatchChatModel 支持与 GoogleAiGeminiChatModel 相同的配置选项:
java
GoogleAiGeminiBatchChatModel batchModel = GoogleAiGeminiBatchChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.temperature(0.7)
.topP(0.95)
.topK(40)
.maxOutputTokens(2048)
.maxRetries(3)
.timeout(Duration.ofMinutes(5))
.logRequestsAndResponses(true)
.build();重要约束
- 模型一致性:一个批次中的所有请求必须使用同一模型
- 大小上限:内联 API 支持的总请求大小为 20MB 或以下
- 成本:批处理相比实时请求可节省 50% 成本
- 周转:24 小时 SLO,尽管完成通常快得多
- 用例:最适合大规模、非紧急的任务,如数据预处理或评估
示例:完整工作流
java
GoogleAiGeminiBatchChatModel batchModel = GoogleAiGeminiBatchChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.build();
// Prepare batch requests
List<ChatRequest> requests = new ArrayList<>();
for (int i = 0; i < 50; i++) {
requests.add(ChatRequest.builder()
.messages(UserMessage.from("Generate a creative story idea #" + i))
.build());
}
// Submit batch
BatchResponse<ChatResponse> result = batchModel.submit(GeminiBatchRequest.from(
requests, "Story Ideas Batch", 0L));
String batchId = result.batchId();
// Poll for completion
int attempts = 0;
int maxAttempts = 720; // 1 hour with 5-second intervals
while (!result.state().isTerminal()) {
if (attempts++ >= maxAttempts) {
throw new RuntimeException("Batch processing timeout");
}
Thread.sleep(5000);
result = batchModel.retrieve(batchId);
System.out.println("Status: " + result.state());
}
// Process results
if (result.state() == BatchState.SUCCEEDED) {
System.out.println("Generated " + result.responses().size() + " stories");
for (int i = 0; i < result.responses().size(); i++) {
ChatResponse chatResponse = result.responses().get(i);
System.out.println("Story #" + i + ": " + chatResponse.aiMessage().text());
}
// Report any failures
if (!result.errors().isEmpty()) {
System.err.println(result.errors().size() + " requests failed:");
for (BatchError error : result.errors()) {
System.err.println(" - Code " + error.code() + ": " + error.message());
}
}
} else {
System.err.println("Batch did not succeed: " + result.state());
}了解更多
如果你想进一步了解 Google AI Gemini 模型,请查看它的文档。