PAYATHON 2026

Azure OpenAI on your data 的 prompt_token 是否影响计费?

支付老李

结论

会影响计费。响应中的 prompt_tokens 会计入模型的输入 token。即使请求中的 system 内容为空,最终提交给模型的提示词也不一定为空。

Azure OpenAI On Your Data 的作用是利用企业数据为回答提供依据,提高相关性并减少幻觉,并非减少 token 用量。服务会在后台构造完整的上下文,通常包含系统级指令、检索到的数据片段、引用信息、对话历史和检索编排所需的内容。因此,即使用户只输入 hello there,最终也可能产生数千个 prompt_tokens。

假设当前模型在对应区域的输入价格确实是 $0.0025 / 1,000 tokens,那么 4,000 个计费输入 token 的费用为:

4,000 ÷ 1,000 × $0.0025 = $0.01

这只是输入 token 的费用。输出 token、Azure AI Search 和其他关联服务可能单独计费。模型、区域和计价时间不同,价格也可能变化,请以实际部署对应的最新价格页和账单为准。

这些 prompt_tokens 从哪里来

On Your Data 一般使用检索增强生成,也就是 RAG。一次请求并不是把用户输入直接交给模型,而是大致经过以下流程:

用户问题
  → 查询理解或改写
  → 从数据源检索相关内容
  → 将检索结果、系统指令和用户问题组合成上下文
  → 调用模型生成带依据的回答

模型最终看到的输入可能类似下面的结构:

[检索上下文示例]
模型会依据检索到的数据生成带引用的回答……

[检索到的数据片段]
文档片段 1……
文档片段 2……
文档片段 3……

[对话历史]
此前的用户问题与助手回答……

[当前问题]
hello there

用户输入本身可能只有几个 token,但以下内容也会占用输入 token:

  • On Your Data 注入的内部系统指令;
  • 从数据源检索出的文档片段;
  • 文档标题、路径、引用和其他元数据;
  • 保留的对话历史;
  • 角色标记、消息结构等协议开销;
  • 某些检索模式使用的查询改写或编排上下文。

因此:

{"role": "system", "content": ""}

只能说明调用方没有填写自定义 system message,不能证明服务最终提交给模型的有效提示词为空。

长度相近的两个用户问题也可能产生差异很大的 prompt_tokens。影响 token 数量的往往不是问题多了几个单词,而是检索命中了多少内容、片段有多长,以及请求是否带有历史消息。

如何降低 token 使用量

1. 减少返回给模型的检索文档数量

如果当前 API 版本支持,可以适当降低数据源配置中的 top_n_documents。下面是示意配置,具体字段位置请以所用 API 版本的定义为准:

{
  "data_sources": [
    {
      "type": "azure_search",
      "parameters": {
        "top_n_documents": 3,
        "strictness": 4
      }
    }
  ]
}

不要一开始就把数值调得太低。减少检索片段可以降低 token 用量,但也可能让回答遗漏必要信息。

2. 优化文档切分

chunk 太大会把大量无关文字送入模型,太小则可能丢失上下文,需要检索更多片段才能完整回答问题。

可以从以下几方面调整:

  • 按标题、段落或语义边界切分;
  • 删除导航栏、页脚和重复模板等噪声;
  • 避免同一内容在多个文档中重复出现;
  • 保留必要的片段重叠,但不要设置过大的 overlap;
  • 尽量让每个片段只覆盖一个相对完整的主题。

没有适用于所有数据集的固定切分大小,需要结合文档类型和实际问答效果进行测试。

3. 调整检索严格程度

提高 strictness 通常可以过滤相关性较低的结果,减少进入上下文的无关片段。但如果设置得太高,也可能检索不到足够的资料。

调整时可以同时观察:

  • prompt_tokens;
  • 检索结果数量;
  • 回答是否遗漏信息;
  • 引用是否确实支持回答。

4. 控制对话历史

多轮聊天如果每次都发送完整历史,旧消息同样会计入输入 token。可以只保留回答当前问题所需的消息:

messages = [
    {
        "role": "user",
        "content": "I worked overtime what should I do?"
    }
]

对于较长的会话,可以先把旧对话压缩成摘要,再发送摘要和最近几轮消息。不过,生成摘要本身也可能产生一次额外的模型调用费用。

5. 对简单问候绕过数据检索

hello there 通常不需要查询企业知识库。应用层可以识别问候、致谢或固定命令,直接返回预设内容,或者改用不带数据源的普通聊天请求。

例如:

def is_simple_greeting(text: str) -> bool:
    normalized = text.strip().lower()
    return normalized in {
        "hello",
        "hello there",
        "hi",
        "hey",
        "thanks",
        "thank you",
    }


def handle_message(text: str):
    if is_simple_greeting(text):
        return "Hello! How can I help?"

    return call_azure_openai_with_data(text)

这种处理比让 RAG 流程接收所有消息更直接,也能避免无意义的检索和上下文注入。

6. 限制输出长度

限制输出长度不会减少 prompt_tokens,但可以降低 completion_tokens。可以根据业务需要设置 max_tokens,或使用当前 API 版本对应的输出限制参数。

7. 使用真实请求做对照测试

可以分别测试以下场景:

  1. 普通模型请求,不配置数据源;
  2. 配置 On Your Data;
  3. 降低 top_n_documents;
  4. 优化文档切分;
  5. 去掉对话历史。

记录每次请求返回的用量:

usage = response.usage

print({
    "prompt_tokens": usage.prompt_tokens,
    "completion_tokens": usage.completion_tokens,
    "total_tokens": usage.total_tokens,
})

不同 SDK 版本可能以对象或字典形式返回 usage,请以实际响应结构为准。

费用应如何计算

模型的输入和输出通常需要分别计费:

prompt_tokens = 4000
completion_tokens = 200

input_price_per_1k = 0.0025
output_price_per_1k = 0.01  # 仅为演示值,不代表当前 Azure 价格

input_cost = prompt_tokens / 1000 * input_price_per_1k
output_cost = completion_tokens / 1000 * output_price_per_1k
total_model_cost = input_cost + output_cost

print(input_cost)
print(output_cost)
print(total_model_cost)

计算公式为:

模型费用 =
(prompt_tokens ÷ 1,000 × 输入单价)
+
(completion_tokens ÷ 1,000 × 输出单价)

当输入单价为 $0.0025 / 1,000 tokens 时:

4,000 个输入 token = $0.01

不能直接把 total_tokens 全部乘以输入单价,因为 completion_tokens 应按输出价格计算。此外,还要检查 Azure AI Search、向量化或 embedding 等相关资源是否产生独立费用。

注意事项

  • prompt_tokens 是模型实际接收的输入规模,不只包括用户手写的 prompt。
  • 空的 system message 不会关闭 On Your Data 的内部指令和数据注入。
  • On Your Data 改善的是回答的知识依据,并不以减少 token 数量为目标。
  • 不同模型、区域、部署类型和计费时期的单价可能不同。
  • 检索参数的名称和可用范围可能随 API 版本变化,修改前需要核对当前版本的接口定义。
  • 减少 token 可能影响检索召回率和回答完整性,应使用实际问答集评估效果,不能只追求最低 token。

备注:内容仅供参考。