Firebase AI Logic(firebase-js-sdk)CountTokensRequest 接口详解:Token 计数请求的完整配置指南

原创2026-10-14 13:44:591,979 阅读
文章标签:开发工具后端

Firebase AI Logic(firebase-js-sdk)CountTokensRequest 接口详解:Token 计数请求的完整配置指南

CountTokensRequest 是 Firebase AI Logic(firebase-js-sdk 的 packages/ai 模块)中用于调用 GenerativeModel.countTokens() 的请求参数接口。本指南将围绕该接口的四个核心属性(contents、generationConfig、systemInstruction、tools)展开,结合仓库源码与测试用例,讲解如何构造一次准确的 token 计数请求、理解其底层请求管线,以及如何解读返回的 CountTokensResponse 结果。读完本文,你将能够基于该接口精确预估一次生成请求的 token 消耗,为成本控制、上下文窗口管理与提示词优化提供数据支撑。

接口概览:CountTokensRequest 的定义与位置

在 packages/ai 模块中,CountTokensRequest 定义于 packages/ai/src/types/requests.ts#L424-L438,其 JSDoc 明确标注为 “Params for calling GenerativeModel.countTokens()”,即它是 token 计数方法专用的请求参数类型。接口签名如下:

export interface CountTokensRequest {
  contents: Content[];
  systemInstruction?: string | Part | Content;
  tools?: Tool[];
  generationConfig?: GenerationConfig;
}

该接口是 @public API,与 GenerateContentRequest、StartChatParams 等同属于请求参数类型族(均定义在同一文件中)。从结构上看,它几乎就是 GenerateContentRequest 的精简版(后者额外包含 toolConfig 与 safetySettings),这是因为 token 计数只需要评估提示词本身的 token 消耗,无需完整推理配置。

接口的四个属性中,contents 是必填项,其余三个均为可选属性(带 ?)。下表汇总了各属性的完整类型签名与语义:

属性 类型 必填 说明
contents Content[] 是 待计数的对话内容(Prompt 内容)
generationConfig GenerationConfig 否 控制模型如何生成响应的配置选项
systemInstruction string | Part | Content 否 引导模型按特定方式行为的指令
tools Tool[] 否 Tool(工具)配置

属性详解:四个字段的语义与取值约束

1. contents(必填):待计数的对话内容

contents: Content<a href="https://link.gitcode.com/i/426cb74c279f5dadbf4c08a04941af49" target="_blank">] 是唯一必填属性,表示需要统计 token 数的对话内容。其元素类型 Content 定义于 [packages/ai/src/types/content.ts#L24-L27:

export interface Content {
  role: Role;
  parts: Part[];
}
  • role:消息角色,通常为 user 或 model;
  • parts:消息内容的分段列表,Part 是以下类型的联合(见 content.ts#L34-L41):
export type Part =
  | TextPart        // { type: 'text', text: string, ... }
  | InlineDataPart  // { type: 'inlineData', inlineData: GenerativeContentBlob, ... },用于图片/音频/视频
  | FunctionCallPart
  | FunctionResponsePart
  | FileDataPart    // { type: 'fileData', fileData: { fileUri, mimeType } },用于引用云存储文件
  | ExecutableCodePart
  | CodeExecutionResultPart;

从集成测试 packages/ai/integration/count-tokens.test.ts 可以看到,contents 支持文本、图片(inlineData)、音频(inlineData)以及云存储文件引用(fileData)等多种模态的组合。例如:

const request: CountTokensRequest = {
  contents: [{
    role: 'user',
    parts: [
      { type: 'text', text: 'Describe these:' },
      {
        type: 'inlineData',
        inlineData: { mimeType: 'image/png', data: base64EncodedImage }
      },
      {
        type: 'inlineData',
        inlineData: { mimeType: 'audio/mpeg', data: base64EncodedAudio }
      }
    ]
  }]
};
const response = await model.countTokens(request);

2. generationConfig(可选):影响 token 计数的生成配置

generationConfig?: GenerationConfig 的文档说明为 “Configuration options that control how the model generates a response”,即控制模型生成响应的配置选项。GenerationConfig 的完整定义同样位于 packages/ai/src/types/requests.ts#L162-L239,其常用字段及当前仓库标注的注意事项如下:

字段 类型 说明
stopSequences string[] 停止序列
maxOutputTokens number 响应最大输出 token 数
responseMimeType string 输出 MIME 类型,支持 text/plain(默认)、application/json、text/x.enum
responseSchema TypedSchema | SchemaRequest 输出响应 JSON Schema(仅当 responseMimeType 为 application/json 或 text/x.enum 时生效)
responseJsonSchema {[key: string]: unknown} 基于 JSON Schema 的替代写法,设置后必须省略 responseSchema 且 responseMimeType 须为 application/json
responseModalities ResponseModality[] 响应模态(当前仅支持图片生成,@beta)
thinkingConfig ThinkingConfig 兼容模型的 “思考” 行为配置(thinkingBudget / thinkingLevel / includeThoughts)
speechConfig SpeechConfig 语音合成配置(@beta)
imageConfig ImageConfig 图片生成配置

需要注意的废弃字段(源码 JSDoc 已明确标注):candidateCount、temperature、topP、topK 在 Gemini 3.x 及以后模型中不再受支持,模型会忽略这些参数;presencePenalty、frequencyPenalty 在包含这些参数的请求中会直接以 400 错误失败。因此构造 generationConfig 时应省略这些字段。

另外,generative-model.ts#L252-L287 中的 validateGenerationConfig() 会在客户端校验三类组合错误并抛出 AIError:thinkingBudget 与 thinkingLevel 不能同时设置;responseSchema 与 responseJsonSchema 不能同时设置;设置任一 schema 时 responseMimeType 必须为 application/json 或 text/x.enum。

3. systemInstruction(可选):系统指令

systemInstruction?: string | Part | Content 的文档说明为 “Instructions that direct the model to behave a certain way”,即引导模型按特定方式行为的指令。它支持三种形式:

  • 字符串:如 'You are a friendly and helpful assistant.';
  • 单个 Part:如 { type: 'text', text: '...' };
  • 完整 Content:如 { role: 'system', parts: [{ type: 'text', text: '...' }] }。

集成测试 count-tokens.test.ts#L74-L88 演示了以 Content 形式传入系统指令并调用 countTokens() 的完整用法。由于系统指令是提示词的一部分,其 token 消耗会计入 CountTokensResponse 的结果中。

4. tools(可选):工具配置

tools?: Tool<a href="https://link.gitcode.com/i/b2937e5f7ddd7b1d285a579920cd57d1" target="_blank">] 的文档说明为 “Tool configuration”。Tool 是联合类型(见 [requests.ts#L515-L520):

export type Tool =
  | FunctionDeclarationsTool  // 函数声明工具(最多 64 个函数声明)
  | GoogleMapsTool            // Google Maps 接地工具
  | GoogleSearchTool          // Google Search 接地工具
  | CodeExecutionTool         // 代码执行工具
  | URLContextTool;           // URL 上下文工具

当提示词中附带函数声明、接地或代码执行等工具时,工具定义本身也会消耗 token,因此 tools 被纳入计数请求,以便更真实地预估实际生成请求的成本。

调用方式:GenerativeModel.countTokens() 的三种入参形态

CountTokensRequest 是 countTokens() 的类型化入参,但方法本身还支持字符串与 Part 数组两种便捷形态(见 packages/ai/src/models/generative-model.ts#L229-L245):

async countTokens(
  request: CountTokensRequest | string | Array<string | Part>,
  singleRequestOptions?: SingleRequestOptions
): Promise<CountTokensResponse> {
  const formattedParams = formatGenerateContentInput(request);
  return countTokens(
    this._apiSettings,
    this.model,
    formattedParams,
    this.chromeAdapter,
    { ...this.requestOptions, ...singleRequestOptions }
  );
}

即以下三种写法均可:

// 写法一:完整 CountTokensRequest 对象
await model.countTokens({ contents: [{ role: 'user', parts: [{ type: 'text', text: 'Hello' }] }] });

// 写法二:直接传字符串
await model.countTokens('Why is the sky blue?');

// 写法三:直接传 Part 数组(如单张图片)
await model.countTokens([{ type: 'inlineData', inlineData: { mimeType: 'image/png', data } }]);

内部会调用 formatGenerateContentInput()(见 packages/ai/src/requests/request-helpers.ts#L120-L144)将字符串或数组归一化为 { contents: <a href="https://link.gitcode.com/i/747cc861a919a903e8dece1b5d06b25f" target="_blank">content] } 结构,并为每个 Part 补上 type 判别字段。singleRequestOptions 可覆盖模型级默认选项(如 timeout、baseUrl),并支持通过 AbortSignal 取消请求(见 [requests.ts#L477-L509)。

一个结合 generationConfig、systemInstruction 与 safetySettings 的完整调用示例(源自集成测试 count-tokens.test.ts#L46-L105):

const model = getGenerativeModel(ai, {
  model: 'gemini-2.0-flash',
  generationConfig: {
    temperature: 0,
    topP: 0,
    responseMimeType: 'text/plain'
  },
  systemInstruction: {
    role: 'system',
    parts: [{ type: 'text', text: 'You are a friendly and helpful assistant.' }]
  },
  safetySettings: [
    {
      category: HarmCategory.HARM_CATEGORY_HARASSMENT,
      threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE
    }
  ]
});

const response = await model.countTokens('Why is the sky blue?');
console.log(response.totalTokens);          // 总 token 数
console.log(response.promptTokensDetails);  // 按模态拆分的明细

底层实现:一次 countTokens 请求的完整调用链

GenerativeModel.countTokens() 最终委托给 packages/ai/src/methods/count-tokens.ts 中的 countTokens() / countTokensOnCloud()。核心流程如下:

  1. 设备端前置检查:countTokens() 会检查 chromeAdapter.mode,若为 InferenceMode.ONLY_ON_DEVICE(纯端侧推理),直接抛出 AIError(错误码 UNSUPPORTED),提示 “countTokens() is not supported for on-device models”。这说明 token 计数仅支持云端推理路径(见 count-tokens.ts#L61-L74 与测试 count-tokens.test.ts#L202-L210)。

  2. 发送前清洗:countTokensOnCloud() 首先调用 cleanCountTokensRequestForWire()(见 request-helpers.ts#L244-L257),递归地对 contents 与 systemInstruction 中的每个 Part 执行 stripPartType(),剥离开客户端用于类型判别的 type 字段(如 type: 'text'),生成符合后端 wire 协议的纯净请求体;同时创建新的 Part 对象,不会修改调用方持有的原始请求对象(测试 count-tokens.test.ts#L195-L199 专门验证了这一点)。

  3. 后端适配映射:若 apiSettings.backend.backendType 为 GOOGLE_AI(Gemini Developer API),则调用 mapCountTokensRequest()(见 packages/ai/src/googleai-mappers.ts#L121-L133),将请求包装为 Google AI 期望的格式:

const mappedCountTokensRequest: GoogleAICountTokensRequest = {
  generateContentRequest: {
    model,
    ...countTokensRequest
  }
};
  1. 发起请求:最终通过 makeRequest() 发送,任务类型为 Task.COUNT_TOKENS(即 'countTokens',见 packages/ai/src/requests/request.ts#L38),stream: false,并把 CountTokensResponse 作为 JSON 直接返回。单元测试 count-tokens.test.ts#L56-L109 通过 mock makeRequest 验证了请求参数中包含 contents,以及 Google AI 后端下请求被正确映射。

结果解读:CountTokensResponse 的字段含义

countTokens() 返回 CountTokensResponse,定义于 packages/ai/src/types/responses.ts#L569-L578:

export interface CountTokensResponse {
  /** The total number of tokens counted across all instances from the request. */
  totalTokens: number;
  /** The breakdown, by modality, of how many tokens are consumed by the prompt. */
  promptTokensDetails?: ModalityTokenCount[];
}
  • totalTokens:请求中所有内容合计的 token 总数,可直接用于成本估算与上下文窗口校验;
  • promptTokensDetails:按模态(文本 / 图片 / 音频等)拆分的 token 明细,ModalityTokenCount 包含 modality 与 tokenCount 字段。

集成测试 count-tokens.test.ts 展示了不同输入下的结果形态:

  • 纯文本:promptTokensDetails 长度为 1,modality 为 TEXT,例如 Google AI 后端下 totalTokens === 7、Agent Platform 下为 6;
  • 单张图片:modality 为 IMAGE,例如一张测试图片在 Gemini 2.x 上约计 258 token(gemini-3-pro-preview 约 1089–1120 token);
  • 音频:modality 为 AUDIO,且不同后端对 totalTokens 与 tokenCount 的返回略有差异(Google AI 返回具体数值,Agent Platform 下部分字段可能为 undefined);
  • 多模态混合:promptTokensDetails 会按模态分别列出各项的 token 数;
  • 云存储文件引用(fileData,仅 Agent Platform 支持):可直接对 gs:// 存储桶中的文件计数,无需在客户端加载文件内容。

使用边界与注意事项

  1. 仅云端支持:当推理模式为 ONLY_ON_DEVICE(纯端侧)时,countTokens() 会抛出 UNSUPPORTED 错误;PREFER_ON_DEVICE 模式则会走云端计数路径。
  2. 请勿混用已废弃的生成参数:Gemini 3.x 及以后模型会忽略 temperature/topP/topK/candidateCount,对含 presencePenalty/frequencyPenalty 的请求返回 400。
  3. 返回字段存在后端差异:Google AI 与 Agent Platform 两个后端在 totalTokens、promptTokensDetails 的具体数值与字段填充上存在细微差异(集成测试中均有注释说明),跨后端迁移时应以实际响应为准。
  4. 建议与真实生成请求保持参数一致:将 generationConfig、systemInstruction、tools 一并传入计数请求,得到的 token 数才更接近后续实际 generateContent() 调用的消耗。

延伸阅读

登录后查看全文
firebase-js-sdk