Как работают токены и как их считать

Gemini и другие модели генеративного ИИ обрабатывают входные и выходные данные с помощью токенов.

Для моделей Gemini токен эквивалентен примерно четырем символам. 100 токенов – это примерно 60–80 слов на английском языке.

О токенах

Токены могут представлять собой отдельные символы, например z, или целые слова, например cat. Длинные слова разбиваются на несколько токенов. Набор всех токенов, используемых моделью, называется словарем, а процесс разбиения текста на токены – токенизацией.

Если платежные функции включены, стоимость вызова Gemini API частично определяется количеством входных и выходных токенов, поэтому полезно знать, как их считать.

Вы можете попробовать подсчитать токены в нашем блокноте Colab.

Посмотреть на ai.google.dev Попробовать блокнот Colab Посмотреть блокнот на GitHub

Подсчет токенов

Все входные и выходные данные Gemini API, включая текст, графические файлы и другие нетекстовые модальности, токенизируются.

Вы можете подсчитать токены следующими способами:

  • Вызовите функцию count_tokens с входными данными запроса.
    Возвращает общее количество токенов только во входных данных. Вы можете сделать этот вызов перед отправкой входных данных в модель, чтобы проверить размер запросов.

  • Используйте атрибут usage_metadata объекта response после вызова generate_content.
    Возвращается общее количество токенов как во входных, так и в выходных данных: total_token_count.
    Также возвращается количество токенов ввода и вывода по отдельности: prompt_token_count (токены ввода) и candidates_token_count (токены вывода).

    Если вы используете думающую модель, токены, задействованные в процессе рассуждения, возвращаются в thoughts_token_count. Если вы используете кеширование контекста, количество кешированных токенов будет указано в cached_content_token_count.

Подсчет текстовых токенов

Если вы вызовете count_tokens с текстовыми входными данными, он вернет количество токенов только для входного текста (total_tokens). Вы можете сделать этот вызов перед вызовом generate_content, чтобы проверить размер запросов.

Другой вариант – вызвать generate_content, а затем использовать атрибут usage_metadata для объекта response, чтобы получить следующее:

  • Количество токенов во входных данных (prompt_token_count), кешированном контенте (cached_content_token_count) и выходных данных (candidates_token_count).
  • Количество токенов для процесса мышления (thoughts_token_count).
  • Общее количество токенов в запросе и ответе (total_token_count).

Python

from google import genai

client = genai.Client()
prompt = "The quick brown fox jumps over the lazy dog."

total_tokens = client.models.count_tokens(
    model="gemini-3.8-flash", contents=prompt
)
print("total_tokens: ", total_tokens)

response = client.models.generate_content(
    model="gemini-3.8-flash", contents=prompt
)

print(response.usage_metadata)

JavaScript

import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({});
const prompt = "The quick brown fox jumps over the lazy dog.";

async function main() {
  const countTokensResponse = await ai.models.countTokens({
    model: "gemini-3.8-flash",
    contents: prompt,
  });
  console.log(countTokensResponse.totalTokens);

  const generateResponse = await ai.models.generateContent({
    model: "gemini-3.8-flash",
    contents: prompt,
  });
  console.log(generateResponse.usageMetadata);
}

await main();

Проложить маршрут

ctx := context.Background()
client, err := genai.NewClient(ctx, nil)

// Convert prompt to a slice of *genai.Content using the helper.
contents := []*genai.Content{
  genai.NewContentFromText(prompt, genai.RoleUser),
}
countResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", contents, nil)
if err != nil {
  return err
}
fmt.Println("total_tokens:", countResp.TotalTokens)

response, err := client.Models.GenerateContent(ctx, "gemini-3.8-flash", contents, nil)
if err != nil {
  log.Fatal(err)
}
usageMetadata, err := json.MarshalIndent(response.UsageMetadata, "", "  ")
if err != nil {
  log.Fatal(err)
}
fmt.Println(string(usageMetadata))
    ```

Как подсчитывать токены в многоходовых диалогах (чатах)

Если вы вызовете функцию count_tokens с историей чата, она вернет общее количество токенов текста от каждой роли в чате (total_tokens).

Другой вариант – вызвать send_message, а затем использовать атрибут usage_metadata для объекта response, чтобы получить следующее:

  • Отдельные счетчики токенов для входных данных (prompt_token_count), кешированного контента (cached_content_token_count) и выходных данных (candidates_token_count).
  • Количество токенов для процесса мышления (thoughts_token_count).
  • Общее количество токенов в запросе и ответе (total_token_count).

Чтобы понять, насколько большим будет следующий ответ, добавьте его в историю при вызове count_tokens.

Python

from google import genai
from google.genai import types

client = genai.Client()

chat = client.chats.create(
    model="gemini-3.8-flash",
    history=[
        types.Content(
            role="user", parts=[types.Part(text="Hi my name is Bob")]
        ),
        types.Content(role="model", parts=[types.Part(text="Hi Bob!")]),
    ],
)

print(
    client.models.count_tokens(
        model="gemini-3.8-flash", contents=chat.get_history()
    )
)

response = chat.send_message(
    message="In one sentence, explain how a computer works to a young child."
)
print(response.usage_metadata)

extra = types.UserContent(
    parts=[
        types.Part(
            text="What is the meaning of life?",
        )
    ]
)
history = [*chat.get_history(), extra]
print(client.models.count_tokens(model="gemini-3.8-flash", contents=history))

JavaScript

import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({});

async function main() {
  const history = [
    { role: "user", parts: [{ text: "Hi my name is Bob" }] },
    { role: "model", parts: [{ text: "Hi Bob!" }] },
  ];
  const chat = ai.chats.create({
    model: "gemini-3.8-flash",
    history: history,
  });

  const countTokensResponse = await ai.models.countTokens({
    model: "gemini-3.8-flash",
    contents: chat.getHistory(),
  });
  console.log(countTokensResponse.totalTokens);

  const chatResponse = await chat.sendMessage({
    message: "In one sentence, explain how a computer works to a young child.",
  });
  console.log(chatResponse.usageMetadata);

  const extraMessage = {
    role: "user",
    parts: [{ text: "What is the meaning of life?" }],
  };
  const combinedHistory = [...chat.getHistory(), extraMessage];
  const combinedCountTokensResponse = await ai.models.countTokens({
    model: "gemini-3.8-flash",
    contents: combinedHistory,
  });
  console.log(
    "Combined history token count:",
    combinedCountTokensResponse.totalTokens,
  );
}

await main();

Проложить маршрут

ctx := context.Background()
client, err := genai.NewClient(ctx, nil)

history := []*genai.Content{
  {Role: genai.RoleUser, Parts: []*genai.Part({Text: "Hi my name is Bob"})},
  {Role: genai.RoleModel, Parts: []*genai.Part({Text: "Hi Bob!"})},
}
chat, err := client.Chats.Create(ctx, "gemini-3.8-flash", nil, history)
if err != nil {
  log.Fatal(err)
}

firstTokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", chat.History(false), nil)
if err != nil {
  log.Fatal(err)
}
fmt.Println(firstTokenResp.TotalTokens)

resp, err := chat.SendMessage(ctx, genai.NewPartFromText("In one sentence, explain how a computer works to a young child."))
if err != nil {
  log.Fatal(err)
}
fmt.Printf("%#v\n", resp.UsageMetadata)

extra := genai.NewContentFromText("What is the meaning of life?", genai.RoleUser)
hist := chat.History(false)
hist = append(hist, extra)

secondTokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", hist, nil)
if err != nil {
  log.Fatal(err)
}
fmt.Println(secondTokenResp.TotalTokens)

Как подсчитывать мультимодальные токены

Все входные данные для Gemini API токенизируются, включая текст, графические файлы и другие нетекстовые модальности. Ниже приведены основные сведения о токенизации мультимодальных входных данных при обработке Gemini API:

  • Изображения, у которых оба размера не превышают 384 пикселя, считаются как 258 токенов. Изображения, размер которых превышает 768 x 768 пикселей хотя бы по одной стороне, обрезаются и масштабируются до фрагментов 768 x 768 пикселей. Каждый такой фрагмент считается как 258 токенов.

  • Видео- и аудиофайлы преобразуются в токены с фиксированной скоростью: видео – 263 токена в секунду, аудио – 32 токена в секунду. Этот тариф применяется к статической обработке (по умолчанию). При обработке агентами количество токенов может быть разным. Подробнее об использовании токенов видео в зависимости от режима обработки…

Разрешение медиаконтента

Модели Gemini 3 позволяют более точно управлять обработкой многомодальных изображений с помощью параметра media_resolution. Параметр media_resolution определяет максимальное количество токенов, выделяемых на одно входное изображение или кадр видео. Чем выше разрешение, тем лучше модель распознает мелкий текст и детали, но при этом увеличивается использование токенов и задержка.

Подробную информацию о параметре и о том, как он может повлиять на расчет токенов, можно найти в руководстве по разрешению медиафайлов.

Графические файлы

Если вы вызовете count_tokens с текстовым и графическим вводом, он вернет объединенное количество токенов текста и изображения только во вводе (total_tokens). Вы можете сделать этот вызов перед вызовом generate_content, чтобы проверить размер ваших запросов. Вы также можете отдельно вызвать функцию count_tokens для текста и файла.

Другой вариант – вызвать generate_content, а затем использовать атрибут usage_metadata объекта response, чтобы получить следующее:

  • Количество токенов во входных данных (prompt_token_count), кешированном контенте (cached_content_token_count) и выходных данных (candidates_token_count).
  • Количество токенов для процесса мышления (thoughts_token_count).
  • Общее количество токенов как во входных, так и в выходных данных (total_token_count).

Пример использования загруженного изображения из File API:

Python

from google import genai

client = genai.Client()
prompt = "Tell me about this image"
your_image_file = client.files.upload(file=media / "organ.jpg")

print(
    client.models.count_tokens(
        model="gemini-3.8-flash", contents=[prompt, your_image_file]
    )
)

response = client.models.generate_content(
    model="gemini-3.8-flash", contents=[prompt, your_image_file]
)
print(response.usage_metadata)

JavaScript

import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({});
const prompt = "Tell me about this image";

async function main() {
  const organ = await ai.files.upload({
    file: path.join(media, "organ.jpg"),
    config: { mimeType: "image/jpeg" },
  });

  const countTokensResponse = await ai.models.countTokens({
    model: "gemini-3.8-flash",
    contents: createUserContent([
      prompt,
      createPartFromUri(organ.uri, organ.mimeType),
    ]),
  });
  console.log(countTokensResponse.totalTokens);

  const generateResponse = await ai.models.generateContent({
    model: "gemini-3.8-flash",
    contents: createUserContent([
      prompt,
      createPartFromUri(organ.uri, organ.mimeType),
    ]),
  });
  console.log(generateResponse.usageMetadata);
}

await main();

Проложить маршрут

ctx := context.Background()
client, err := genai.NewClient(ctx, nil)

file, err := client.Files.UploadFromPath(
  ctx, 
  filepath.Join(getMedia(), "organ.jpg"), 
  &genai.UploadFileConfig{
    MIMEType : "image/jpeg",
  },
)
if err != nil {
  log.Fatal(err)
}
parts := []*genai.Part{
  genai.NewPartFromText("Tell me about this image"),
  genai.NewPartFromURI(file.URI, file.MIMEType),
}
contents := []*genai.Content{
  genai.NewContentFromParts(parts, genai.RoleUser),
}

tokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", contents, nil)
if err != nil {
  log.Fatal(err)
}
fmt.Println("Multimodal image token count:", tokenResp.TotalTokens)

response, err := client.Models.GenerateContent(ctx, "gemini-3.8-flash", contents, nil)
if err != nil {
  log.Fatal(err)
}
usageMetadata, err := json.MarshalIndent(response.UsageMetadata, "", "  ")
if err != nil {
  log.Fatal(err)
}
fmt.Println(string(usageMetadata))

Пример, в котором изображение представлено в виде встроенных данных:

Python

from google import genai
import PIL.Image

client = genai.Client()
prompt = "Tell me about this image"
your_image_file = PIL.Image.open(media / "organ.jpg")

print(
    client.models.count_tokens(
        model="gemini-3.8-flash", contents=[prompt, your_image_file]
    )
)

response = client.models.generate_content(
    model="gemini-3.8-flash", contents=[prompt, your_image_file]
)
print(response.usage_metadata)

JavaScript

import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({});
const prompt = "Tell me about this image";
const imageBuffer = fs.readFileSync(path.join(media, "organ.jpg"));

const imageBase64 = imageBuffer.toString("base64");

const contents = createUserContent([
  prompt,
  createPartFromBase64(imageBase64, "image/jpeg"),
]);

async function main() {
  const countTokensResponse = await ai.models.countTokens({
    model: "gemini-3.8-flash",
    contents: contents,
  });
  console.log(countTokensResponse.totalTokens);

  const generateResponse = await ai.models.generateContent({
    model: "gemini-3.8-flash",
    contents: contents,
  });
  console.log(generateResponse.usageMetadata);
}

await main();

Проложить маршрут

ctx := context.Background()
client, err := genai.NewClient(ctx, nil)

imageBytes, err := os.ReadFile("organ.jpg")
if err != nil {
    log.Fatalf("Failed to read image file: %v", err)
}
parts := []*genai.Part{
  genai.NewPartFromText("Tell me about this image"),
  {
        InlineData: &genai.Blob{
              MIMEType: "image/jpeg",
              Data:     imageBytes,
        },
  },
}
contents := []*genai.Content{
  genai.NewContentFromParts(parts, genai.RoleUser),
}

tokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", contents, nil)
if err != nil {
  log.Fatal(err)
}
fmt.Println("Multimodal image token count:", tokenResp.TotalTokens)

response, err := client.Models.GenerateContent(ctx, "gemini-3.8-flash", contents, nil)
if err != nil {
  log.Fatal(err)
}
usageMetadata, err := json.MarshalIndent(response.UsageMetadata, "", "  ")
if err != nil {
  log.Fatal(err)
}
fmt.Println(string(usageMetadata))

видео- или аудиофайлы;

Аудио- и видеоконтент преобразуется в токены по следующим фиксированным тарифам:

  • Видео: 263 токена в секунду.
  • Аудио: 32 токена в секунду.

Если вы вызовете count_tokens с текстовыми и видео- или аудиоданными, то получите общее количество токенов только для входных данных (total_tokens). Вы можете сделать этот вызов перед вызовом generate_content, чтобы проверить размер запросов. Вы также можете вызвать функцию count_tokens для текста и файла по отдельности.

Другой вариант – вызвать generate_content, а затем использовать атрибут usage_metadata для объекта response, чтобы получить следующее:

  • Количество токенов во входных данных (prompt_token_count), кешированном контенте (cached_content_token_count) и выходных данных (candidates_token_count).
  • Количество токенов для процесса мышления (thoughts_token_count).
  • Общее количество токенов как во входных, так и в выходных данных (total_token_count).

Python

from google import genai
import time

client = genai.Client()
prompt = "Tell me about this video"
your_file = client.files.upload(file=media / "Big_Buck_Bunny.mp4")

while not your_file.state or your_file.state.name != "ACTIVE":
    print("Processing video...")
    print("File state:", your_file.state)
    time.sleep(5)
    your_file = client.files.get(name=your_file.name)

print(
    client.models.count_tokens(
        model="gemini-3.8-flash", contents=[prompt, your_file]
    )
)

response = client.models.generate_content(
    model="gemini-3.8-flash", contents=[prompt, your_file]
)
print(response.usage_metadata)

JavaScript

import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({});
const prompt = "Tell me about this video";

async function main() {
  let videoFile = await ai.files.upload({
    file: path.join(media, "Big_Buck_Bunny.mp4"),
    config: { mimeType: "video/mp4" },
  });

  while (!videoFile.state || videoFile.state.toString() !== "ACTIVE") {
    console.log("Processing video...");
    console.log("File state: ", videoFile.state);
    await sleep(5000);
    videoFile = await ai.files.get({ name: videoFile.name });
  }

  const countTokensResponse = await ai.models.countTokens({
    model: "gemini-3.8-flash",
    contents: createUserContent([
      prompt,
      createPartFromUri(videoFile.uri, videoFile.mimeType),
    ]),
  });
  console.log(countTokensResponse.totalTokens);

  const generateResponse = await ai.models.generateContent({
    model: "gemini-3.8-flash",
    contents: createUserContent([
      prompt,
      createPartFromUri(videoFile.uri, videoFile.mimeType),
    ]),
  });
  console.log(generateResponse.usageMetadata);
}

await main();

Проложить маршрут

ctx := context.Background()
client, err := genai.NewClient(ctx, nil)

file, err := client.Files.UploadFromPath(
  ctx,
  filepath.Join(getMedia(), "Big_Buck_Bunny.mp4"),
  &genai.UploadFileConfig{
    MIMEType : "video/mp4",
  },
)
if err != nil {
  log.Fatal(err)
}

for file.State == genai.FileStateUnspecified || file.State != genai.FileStateActive {
  fmt.Println("Processing video...")
  fmt.Println("File state:", file.State)
  time.Sleep(5 * time.Second)

  file, err = client.Files.Get(ctx, file.Name, nil)
  if err != nil {
    log.Fatal(err)
  }
}

parts := []*genai.Part{
  genai.NewPartFromText("Tell me about this video"),
  genai.NewPartFromURI(file.URI, file.MIMEType),
}
contents := []*genai.Content{
  genai.NewContentFromParts(parts, genai.RoleUser),
}

tokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", contents, nil)
if err != nil {
  log.Fatal(err)
}
fmt.Println("Multimodal video/audio token count:", tokenResp.TotalTokens)
response, err := client.Models.GenerateContent(ctx, "gemini-3.8-flash", contents, nil)
if err != nil {
  log.Fatal(err)
}
usageMetadata, err := json.MarshalIndent(response.UsageMetadata, "", "  ")
if err != nil {
  log.Fatal(err)
}
fmt.Println(string(usageMetadata))

Использование токенов для видео по режиму обработки

Использование токенов для видео зависит от режима обработки:

Режим обработки Расчет токенов Типичное использование
Статический (по умолчанию). ~100 токенов/сек. по умолчанию (низкое разрешение) или ~300 токенов/сек. (высокое разрешение). Все кадры, выбранные с частотой 1 кадр/с. Предсказуемый, пропорциональный длительности видео.
Агентные возможности Зависит от сложности контента. Модель загружает только расшифровку, кадры и/или аудио, необходимые для ответа на запрос. До 88% меньше токенов для длинного контента.

При использовании агентской обработки лекция продолжительностью один час, для которой в статическом режиме требуется около 1,08 млн токенов, может использовать около 108 тыс.токенов в зависимости от запроса и контента.

Чтобы проверить фактическое использование токенов для запроса, используйте поле usage_metadata в ответе. В UsageMetadata токены агентных видео представлены в следующих полях:

  • Первый запрос (видео + запрос пользователя): prompt_token_count
  • Навигация: thoughts_token_count
  • Расшифровка, кадры и аудио загружаются по запросу. tool_use_prompt_token_count
  • Окончательный ответ: candidates_token_count

Подсчет токенов мышления

Если вы включите режим рассуждения, цена ответа будет суммой цен токенов вывода и токенов мышления. Общее количество сгенерированных токенов мышления можно получить из поля thoughtsTokenCount (или его эквивалента в SDK).

Python

# ...
print("Thoughts tokens:", response.usage_metadata.thoughts_token_count)
print("Output tokens:", response.usage_metadata.candidates_token_count)

JavaScript

// ...
console.log(`Thoughts tokens: ${response.usageMetadata.thoughtsTokenCount}`);
console.log(`Output tokens: ${response.usageMetadata.candidatesTokenCount}`);

Проложить маршрут

// ...
fmt.Println("Thoughts tokens:", response.UsageMetadata.ThoughtsTokenCount)
fmt.Println("Output tokens:", response.UsageMetadata.CandidatesTokenCount)

Модели мышления генерируют полные мысли, чтобы повысить качество окончательного ответа, а затем выводят краткие изложения, чтобы показать процесс мышления. Таким образом, API рассчитывает цену на основе всех токенов, которые модель генерирует для создания краткого пересказа, даже если API выводит только сам пересказ.

Подробнее о том, как настроить размышления, можно узнать в руководстве по размышлениям Gemini.

Окна контекста

Окна контекста моделей, доступных через Gemini API, измеряются в токенах. Окно контекста определяет, сколько входных данных вы можете предоставить и сколько выходных данных может сгенерировать модель. Вы можете определить размер окна контекста, вызвав конечную точку models.get или посмотрев документацию по моделям.

  • Модели Gemini 4 (gemini-4-argon). Окно контекста на 1 миллион токенов (1,048,576 токенов) используется для входных, выходных и токенов мышления. Для одного запроса можно сгенерировать не более 65,536 токенов для потоковой передачи (streamGenerateContent) и 32,768 токенов для других целей (generateContent). С помощью длинного декодирования можно сгенерировать до 1,048,576 токенов. Если сумма входных токенов и max_output_tokens превышает 1 048 576 токенов, API автоматически ограничивает выходные токены оставшимся местом в окне контекста (1,048,576 минус входные токены).
  • Модели Gemini 3 и 2.5. Используйте отдельные бюджеты для входных (1,048,576) и выходных (65,536) токенов.

Python

from google import genai

client = genai.Client()
model_info = client.models.get(model="gemini-3.8-flash")
print(f"{model_info.input_token_limit=}")
print(f"{model_info.output_token_limit=}")

JavaScript

import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({});

async function main() {
  const modelInfo = await ai.models.get({model: 'gemini-3.8-flash'});
  console.log(modelInfo.inputTokenLimit);
  console.log(modelInfo.outputTokenLimit);
}

await main();

Проложить маршрут

ctx := context.Background()
client, err := genai.NewClient(ctx, nil)
if err != nil {
  log.Fatal(err)
}
modelInfo, err := client.ModelInfo(ctx, "models/gemini-3.8-flash")
if err != nil {
  log.Fatal(err)
}
fmt.Println("input token limit:", modelInfo.InputTokenLimit)
fmt.Println("output token limit:", modelInfo.OutputTokenLimit)