首页 / 文章 / 为AI代理选择推理服务提供商:延迟、工具调用和每任务成本
← 返回
IT技术

为AI代理选择推理服务提供商:延迟、工具调用和每任务成本

✍️ zhirenhun 📅 2026/8/24 👁 128 阅读 ⏱ 64 分钟
为AI代理选择推理服务提供商:延迟、工具调用和每任务成本

AI 智能体放大每个推理决策的影响。虽然与机器人聊天可能意味着每轮对话只需一次模型调用,但代理可能需要进行 5–30 次调用来搜索计划、选择工具、解析结果、重试失败并生成答案。因此,推理提供商之间的微小差异在任务层面会放大为显著差异。

如果您正在为 AI 智能体选择推理提供商,首 token 延迟(TTFT)、工具调用可靠性和结构化输出保证远比原始 token 价格或吞吐基准更重要。评估的适当单位不是每 token 价格,而是每完成任务的价格。

本教程定义了该指标,并提供了一个基准,用于在四个维度上比较 DigitalOcean AI Platform、Together AI、Fireworks AI 和 OpenAI。

为什么代理工作负载会破坏聊天机器人时代的提供商比较

大多数传统的 LLM 基准测试和提供商比较基于每秒输出 token 数、每百万 token 价格或通用推理基准得分,将提供商相互对比。这些指标仍然适用,但它们无法捕捉代理的完整行为。考虑一个具有以下工作流程的研究代理:

  1. 解释请求。
  2. 制定研究计划。
  3. 选择搜索工具。
  4. 生成搜索参数。
  5. 解释搜索结果。
  6. 决定是否需要更多证据。
  7. 执行另一次搜索。
  8. 提取相关事实。
  9. 调用计算器。
  10. 验证计算。
  11. 撰写答案。
  12. 检查最终响应。

这可能需要对模型进行 12 次顺序调用。

只有在收到前一次模型的响应或工具输出之后,后续调用才能开始。因此,延迟会在关键路径上累积。

假设提供商 A 的首 token 中位延迟为 400 毫秒,提供商 B 为 900 毫秒。如果每次调用产生约 100 毫秒的应用/网络开销,低延迟任务需要:12×(0.4+0.1)=6 秒。对于提供商 B,仅 TTFT 就会累计:12×0.9=10.8 秒。为这 12 个步骤每个再增加 100 毫秒的编排和网络开销,总时间变为:12×(0.9+0.1)=12 秒。

图像

小的延迟差异在智能体工作过程中会累积。例如,额外的 500 毫秒对单个聊天机器人响应可能不可察觉。然而,将 500 毫秒加到 12 步智能体循环的每一步,会使任务的总完成时间增加六秒。

工具失败可能在智能体的轨迹中累积:格式错误的参数必须被验证、修复或重试。这些操作会消耗额外的 token、延迟以及潜在的工具成本,并增加失败的机会。因此,评估智能体经济的正确单位不是 token 或单个模型调用,而是成功完成的任务。

实际上重要的四个标准

评估推理服务提供商时,不应仅仅比较 token 价格或宣传的吞吐量。AI 智能体会发出许多顺序的模型调用,调用外部工具,并且通常需要生成符合精确模式的输出。因此,应根据四个适用的指标来评估提供商:交互延迟、工具调用可靠性、结构化输出保证以及每完成任务的成本。

首 token 时间和 token 间延迟(非吞吐量)

首 token 时间 是指从发出请求到第一个生成的 token 到达之间经过的时间。token 间延迟测量连续输出 token 之间的时间。吞吐量通常以 tokens/sec 衡量,反映服务系统完成的总工作量。吞吐量对高流量系统很重要,但可能会掩盖交互式智能体的延迟问题。

想象两个提供商:

  • 提供商 A 在 300 毫秒后开始响应,并以每秒 70 个 token 的速度生成。
  • 提供商 B 在 900 毫秒后开始响应,并以每秒 130 个 token 的速度生成。

下面的信息图将 TTFT 与 token 生成时间分开,并说明为什么对于 20-token 的响应,提供商 A 更胜一筹:

图像

这很重要,因为代理工具调用通常很短。模型可能只需要输出一个函数名和若干 JSON 参数。首个 token 的等待时间可能超过生成完整响应所花费的时间。因此,如果提供商 A 的 TTFT 比提供商 B 低,尽管其生成吞吐量较低,也可能实现更快的代理执行。

工具/函数调用可靠性

工具调用允许模型选择一个函数并生成其参数。例如,天气代理可能会返回:

{
 "name": "get_weather",
 "arguments": {
   "city": "Douala",
   "unit": "celsius"
 }
}

响应是否有用完全取决于所调用的工具是否存在以及参数是否通过 schema 验证。典型的失败包括格式错误的 JSON、缺少必需字段、无效的枚举值、错误的数据类型、不存在的工具、不必要的工具调用,或参数中嵌入的自然语言文本。

每次无效的工具调用都可能导致一次付费重试。可靠性应当通过量化来评估,而不是仅凭提供商的“支持函数调用”标签来假设。下面给出一个基本的工具调用有效性度量:

图像

为每个模型和场景至少运行 50 次。测试 100–500 次可获得更稳定的结果。使用简单、含糊和对抗性提示进行测试。请使用与生产环境中相同的 JSON Schema 验证工具调用,而不仅仅是验证输出是否为有效的 JSON。

DigitalOcean 当前的推理模型列表在模型级别突出显示了工具调用支持。单个代理的文档描述了函数如何查询 API、数据库和其他外部系统。工具调用支持可能因模型和端点而异,因此请确认您打算使用的模型 ID 是否支持工具使用。您可以参考 DigitalOcean 支持的模型目录函数路由文档

Together 和 Fireworks 均提供使用 OpenAI 兼容请求格式的工具调用。Anthropic 提供原生的工具使用功能,包括 严格的工具 schema。OpenAI 通过其结构化输出功能允许 严格的函数 定义。

您选择的模型对工具选择有很大影响。模式设计、提示、采样设置以及可用工具的数量也会影响工具选择。

结构化输出保证

让 LLM 生成 JSON 有三种主要方式。它们提供不同级别的可靠性。

1. 提示 JSON

您告诉模型输出 JSON:

Return the answer as valid JSON only.

模型很可能会遵循此指令,但在技术上没有任何强制手段。它可能会添加额外的解释性文本、省略必需的字段、输出错误的数据类型,甚至输出无效的 JSON。

2. JSON 模式

JSON 模式可以强制响应为有效的 JSON,以便您的应用程序可以解析。例如:

{
 "amount": "50",
 "currency": "USD"
}

然而,有效的 JSON 可能并不符合您应用程序所期望的结构。在此示例中,amount 被提供为字符串,尽管应用程序期望的是数字。

3. 受模式约束的解码

受模式约束的解码会强制模型的输出符合提供的 JSON Schema 或语法。在此示例中,该模式可能要求:

  • amount 应为数字
  • currency 应为字符串
  • 两个字段都必须存在
  • 不得包含额外字段

一个符合要求的响应示例如下:

{
 "amount": 50,
 "currency": "USD"
}

基于模式约束的解码是防止代理工作流中许多格式和数据类型错误的最可靠方法之一。然而,模式无法保证响应包含正确的数据。它可以验证 amount 是一个数字,但不能验证 $50 是正确的退款金额。

Together 在其 结构化输出文档 中同时提供 json_object 和首选的 json_schema 格式。Fireworks 在其 结构化输出文档 中声明支持 JSON Schema 和用户定义的语法。Anthropic 的文档表明模式合规性得到保证,并使用 strict 工具。

请注意,Anthropic 与 OpenAI SDK 的兼容层 在调用函数时会忽略 strict 参数。如果需要严格遵循模式,请直接使用 Anthropic 的原生 API。在结构化输出的测试中,应包含深层对象、可选字段、枚举、列表、Unicode、转义字符以及无效的用户输入。

将未能遵循 schema 的失败与拒绝或截断分开记录;输出可能符合 schema 但仍未完成预期任务。下表比较了主要推理服务提供商在 JSON 生成、函数调用和 schema 强制方面的支持情况。

提供商 工具调用 JSON 模式 受 schema 约束的输出 重要说明
DigitalOcean 取决于模型 取决于模型/端点 已列出的支持模型 请在当前模型目录中核对确切的模型和 API 能力列。
Together AI 取决于模型 JSON Schema 函数调用和结构化输出的支持分别针对每种模型进行报告。
Fireworks AI JSON Schema + 语法 可用性和行为可能会根据所选模型和部署类型而有所不同。
OpenAI 严格 schema 当需要精确遵循 schema 时,请在受支持的模型和 API 功能中使用 strict: true
Anthropic 结构化输出 + 严格工具 当需要保证 schema 完全符合时,请使用原生 Claude API。

每完成任务的成本(决定因素的指标)

下图展示了 token 使用量、模型调用次数以及任务成功率如何共同决定完成代理任务的真实成本:

图像

例如,假设模型 A 的收费为每百万输入 token 0.20 美元,每百万输出 token 0.80 美元。此外,假设 A 每次任务尝试会进行八次调用。每次调用使用 1,200 个输入 token,并生成 180 个输出 token。那么每次尝试的成本为 $0.0030 72。最后,假设模型 A 能够在 96% 的情况下成功完成任务。其每成功完成一次任务的预期成本为 $0.00320。

图像

假设另一个模型 B 的 token 价格更低(每百万输入 token 0.12 美元,每百万输出 token 0.50 美元)。然而,模型 B 平均需要十次调用。此外,它只有 82% 的成功率。每次尝试的成本为 $0.00 23 40,我们应预期为每完成一次任务支付约 $0.00 285。模型 B 更便宜,但差距不大——它每成功完成一次任务仅能节省约 $0.00035。这大约相当于 10.8% 的折扣。如果我们仅看 token 价格,这种折扣将远小于我们的预期。

在生产环境中构建和运行模型时,总成本还将包括使用模型的费用、调用外部工具、检索、重试、基础设施、人工升级以及许多其他因素。核心教训很简单:应基于成功的端到端任务的成本来比较推理服务提供商,而不是孤立地看 token 价格。

基准测试:单个智能体循环,四个提供者

为了公平比较提供商,应在相同条件下对每个提供商进行基准测试。使用相同的任务、工具定义、温度、最大输出长度和成功标准。比较提供商时,始终使用能力相似的模型。请记住,目标不是在所有模型中寻找“最佳提供商”。目标是为您的智能体找到最佳的提供商 + 模型组合。同一提供商在不同模型上可能表现出色或较弱。对于此基准测试,使用需要两个工具的研究和摘要任务:

  • web_search(query, max_results) 用于搜索相关证据。
  • calculator(expression) 执行所需的计算。

只有在以下情况下,任务才被视为成功:

  1. 选择两个所需的工具。
  2. 智能体为每个工具提供有效的参数;
  3. 正确使用返回的证据;
  4. 计算出正确的结果并
  • 生成符合所需 JSON 模式的最终响应。
  • 此任务的成功将阻止流畅但不完整的答案被视为成功。

    步骤 1:配置每个推理提供者

    公平的基准测试将把特定于提供商的配置数据与基准测试逻辑本身分离。每个提供商的配置应仅包括该提供商的 API 端点、身份验证凭据以及精确的模型标识符。随后,基准测试逻辑可以在各提供商之间复用提示、工具、模式、重试规则和测量代码。

    假设每项服务都提供与 OpenAI 兼容的 API,实际操作如下:

    import os
    from dataclasses import dataclass
    from openai import OpenAI
    @dataclass(frozen=True)
    class ProviderConfig:
       """Connection settings for one provider-model combination."""
    
       name: str
       base_url: str
       api_key: str
       model: str
    
    def require_env(variable_name: str) -> str:
       """
       Return an environment variable or raise a clear configuration error.
    
       Failing early is preferable to discovering a missing API key after
       hundreds of benchmark tasks have already been scheduled.
       """
       value = os.getenv(variable_name)
    
       if not value:
           raise RuntimeError(
               f"Missing required environment variable: {variable_name}"
           )
    
       return value
    PROVIDERS = {
       "digitalocean": ProviderConfig(
           name="digitalocean",
           base_url="https://inference.do-ai.run/v1",
           api_key=require_env("DIGITALOCEAN_INFERENCE_KEY"),
           model=require_env("DO_MODEL"),
       ),
       "together": ProviderConfig(
           name="together",
           base_url="https://api.together.xyz/v1",
           api_key=require_env("TOGETHER_API_KEY"),
           model=require_env("TOGETHER_MODEL"),
       ),
       "fireworks": ProviderConfig(
           name="fireworks",
           base_url="https://api.fireworks.ai/inference/v1",
           api_key=require_env("FIREWORKS_API_KEY"),
           model=require_env("FIREWORKS_MODEL"),
       ),
       "openai": ProviderConfig(
           name="openai",
           base_url="https://api.openai.com/v1",
           api_key=require_env("OPENAI_API_KEY"),
           model=require_env("OPENAI_MODEL"),
       ),
    }
    
    def get_client(config: ProviderConfig) -> OpenAI:
       """Create an OpenAI-compatible client for one provider."""
    
       return OpenAI(
           api_key=config.api_key,
           base_url=config.base_url,
           timeout=60.0,
           max_retries=0,
       )
    

    max_retries=0 设置为有意的。我们不希望 SDK 的重试掩盖来自提供者的故障,并扭曲延迟结果。如果在您的生产应用中启用了重试,请实现并明确记录它们,以便基准能够区分首次尝试的性能和重试辅助的性能。

    将凭据存储在源代码之外

    PI 密钥和模型标识符应从环境变量加载,而不应硬编码到基准中。这可以保护凭据,并使用户能够在不更改基准底层评估逻辑的情况下评估不同的模型。

    在 Linux 或 macOS 上,按以下方式配置 DigitalOcean AI Platform:

    export DIGITALOCEAN_INFERENCE_KEY="your-api-key"
    export DO_MODEL="your-exact-model-id"
    

    以同样的方式配置其他提供商:

    export TOGETHER_API_KEY="your-api-key"
    export TOGETHER_MODEL="your-exact-model-id"
    export FIREWORKS_API_KEY="your-api-key"
    export FIREWORKS_MODEL="your-exact-model-id"
    export OPENAI_API_KEY="your-api-key"
    export OPENAI_MODEL="your-exact-model-id"
    

    确保您使用的模型标识符完全符合每个提供者返回或文档中所示的完整标识。不要仅基于诸如 “Llama 70B” 这样的系列名称来比较模型。不同提供者可能在相似名称下提供不同的修订版、量化、上下文长度或解码配置。

    为了在可能的情况下且提供者支持的前提下,保持以下设置不变,以确保跨提供者的可重复比较:

    • 系统提示和用户提示
    • 工具定义和 JSON Schema
    • 温度和采样参数
    • 最大输出 token 限制
    • 超时策略
    • 重试策略
    • 成功评估器
    • 智能体步骤的最大数量

    如果提供者不支持其中某个控制,请记录该差异,而不是悄悄替换为另一种配置。

    步骤 2:记录每次基准测试尝试

    聚合平均值不足以审计基准测试。应为每次尝试的任务生成一条记录,包含其身份、结果、令牌消耗、延迟和成本。至少,该记录应符合以下模式:

    result = {
       # Experiment identity
       "run_id": run_id,
       "timestamp_utc": timestamp_utc,
       "provider": provider_name,
       "model": model_name,
       "task_id": task_id,
       "scenario": scenario_name,
       # Quality and reliability
       "success": success,
       "tool_calls_expected": expected_calls,
       "tool_calls_attempted": attempted_calls,
       "tool_calls_valid": valid_calls,
       "error_type": error_type,
       # Retry and transport behavior
       "retry_count": retry_count,
       "http_status": http_status,
       # Token consumption
       "input_tokens": input_tokens,
       "output_tokens": output_tokens,
       # Performance
       "ttft_ms": ttft_ms,
       "model_latency_ms": model_latency_ms,
       "tool_latency_ms": tool_latency_ms,
       "task_latency_ms": task_latency_ms,
       # Cost
       "model_cost_usd": model_cost,
       "tool_cost_usd": tool_cost,
    }
    

    上述字段回答不同的问题。您必须以 JSON Lines 格式存储结果,每行一个 JSON 对象。这样可以在不重写整个文件的情况下追加新结果,对于大型实验来说非常方便。

    类别 字段 回答的问题
    ID 识别 provider 哪个推理提供方执行了该任务?
    model 哪个确切的模型处理了该请求?
    task_id 哪个评估任务产生了此结果?
    ✓ 质量和可靠性 success 代理是否正确完成了任务?
    tool_calls_expected 轨迹应包含多少次工具调用?
    tool_calls_valid 有多少次工具调用使用了正确的工具和有效的参数?
    error_type 任务或工具调用失败的原因是什么?
    T 令牌消耗 input_tokens 发送给模型的令牌数量是多少?
    output_tokens 模型生成了多少令牌?
    ⚡ 性能 ttft_ms 提供方生成第一个令牌用了多长时间?
    task_latency_ms 完整的代理任务耗时多久?
    $ 成本 model_cost_usd 模型推理的成本是多少?
    tool_cost_usd 外部工具执行的成本是多少?

    在运行基准测试之前定义成功

    评估成功标准应来自可执行的评估器,而不是主观检查。考虑基于研究和计算的任务:我们可能只有在代理满足以下条件时才判断预测成功:

    1. 调用适当的搜索工具。
    2. 提供符合模式且语义正确的参数。
    3. 使用正确的表达式调用计算器。
    4. 将检索到的证据纳入其答案中。
    5. 返回通过最终响应模式的输出。
    6. 计算出预期答案(在一定容忍范围内)。

    仅仅拥有有效的 JSON 不足以判断工具调用是否正确。调用可能通过验证,但选择了错误的工具获取信息,搜索了错误的信息,或提供了错误的计算。

    使用显式错误类别

    拥有受控的故障分类法也使得不同类型的故障更易于比较:

    ERROR_TYPES = {
       "timeout",
       "rate_limit",
       "authentication",
       "provider_error",
       "malformed_tool_call",
       "schema_validation",
       "wrong_tool",
       "incorrect_arguments",
       "tool_execution",
       "incorrect_answer",
       "max_steps_exceeded",
       "unknown",
    }
    

    记录失败的尝试而不是删除它们。删除失败会人为地提升成功率、延迟和成本指标。

    以 JSON Lines 格式保存结果。

    JSON Lines 每行存储一个 JSON 对象。它支持增量写入,适用于大规模实验,并且在基准测试被中断时仍能保留已完成的运行。

    import json
    from pathlib import Path
    from typing import Any
    
    def append_jsonl(
       file_path: str,
       record: dict[str, Any],
    ) -> None:
       """Append one benchmark record to a JSON Lines file."""
    
       path = Path(file_path)
    
       with path.open("a", encoding="utf-8") as file:
           file.write(
               json.dumps(
                   record,
                   ensure_ascii=False,
                   allow_nan=False,
               )
               + "\n"
           )
    import json
    from pathlib import Path
    from typing import Any
    
    def append_jsonl(
       file_path: str,
       record: dict[str, Any],
    ) -> None:
       """Append one benchmark record to a JSON Lines file."""
    
       path = Path(file_path)
    
       with path.open("a", encoding="utf-8") as file:
           file.write(
               json.dumps(
                   record,
                   ensure_ascii=False,
                   allow_nan=False,
               )
               + "\n"
           )
    

    每次尝试后使用它:

    append_jsonl("raw_results.jsonl", result)
    

    每次运行后写入比在内存中保留所有观察结果并在整个实验结束时才保存更安全。

    步骤 3:在聚合之前验证数据集

    在生成排名之前,请确保数据集包含所需的列,并且数值列包含合理的值。

    import pandas as pd
    df = pd.read_json("raw_results.jsonl", lines=True)
    required_columns = {
       "provider",
       "model",
       "task_id",
       "success",
       "tool_calls_expected",
       "tool_calls_valid",
       "input_tokens",
       "output_tokens",
       "ttft_ms",
       "task_latency_ms",
       "model_cost_usd",
       "tool_cost_usd",
    }
    
    missing_columns = required_columns.difference(df.columns)
    
    if missing_columns:
       raise ValueError(
           f"Missing required columns: {sorted(missing_columns)}"
       )
    
    numeric_columns = [
       "tool_calls_expected",
       "tool_calls_valid",
       "input_tokens",
       "output_tokens",
       "ttft_ms",
       "task_latency_ms",
       "model_cost_usd",
       "tool_cost_usd",
    ]
    
    if df[numeric_columns].isna().any().any():
       raise ValueError("One or more required numeric values are missing.")
    non_negative_columns = [
       "tool_calls_expected",
       "tool_calls_valid",
       "input_tokens",
       "output_tokens",
       "ttft_ms",
       "task_latency_ms",
       "model_cost_usd",
       "tool_cost_usd",
    ]
    
    if (df[non_negative_columns] < 0).any().any():
       raise ValueError("Negative token, latency, call, or cost value detected.")
    if (df["tool_calls_valid"] > df["tool_calls_expected"]).any():
       raise ValueError(
           "Valid tool calls cannot exceed expected tool calls."
       )
    df["success"] = df["success"].astype(bool)
    

    验证可以防止格式错误的记录悄悄破坏最终的比较。

    步骤 4:汇总提供方级别的结果

    按提供方和模型两个维度对结果进行分组。仅使用提供方本身作为比较单元是不合适的,因为在同一平台下,不同模型的结果可能会有很大差异。

    summary = (
       df.groupby(["provider", "model"], as_index=False)
         .agg(
             runs=("task_id", "size"),
             completed_tasks=("success", "sum"),
             success_rate=("success", "mean"),
             valid_tool_calls=("tool_calls_valid", "sum"),
             expected_tool_calls=("tool_calls_expected", "sum"),
             input_tokens=("input_tokens", "sum"),
             output_tokens=("output_tokens", "sum"),
             model_cost_usd=("model_cost_usd", "sum"),
             tool_cost_usd=("tool_cost_usd", "sum"),
         )
    )
    

    计算任务成功率

    例如,如果100次尝试中有94次通过了完整的评估器:

    Sp​=100/94​=0.94=94%。只有当完整轨迹满足预定义要求时,任务才算成功。

    计算工具调用有效性

    summary["tool_validity_rate"] = (
       summary["valid_tool_calls"]
       .div(summary["expected_tool_calls"])
       .where(summary["expected_tool_calls"] > 0)
    )
    

    假设代理在预期的 200 次中产生了 194 次有效的工具调用:

    Vp​=200/194​=0.97=97%

    这与任务成功不同。代理可以发出两次有效的工具调用,但仍然得到错误的最终答案。即使在一次无效调用后需要重试,它也能完成某些任务。请同时报告这两项指标。

    计算总令牌消耗

    summary["total_tokens"] = (
       summary["input_tokens"]
       + summary["output_tokens"]
    )
    

    应为输入/输出令牌保持单独的列,因为许多提供商对每种类型收取不同的费用。如果适用于您的计费,缓存的输入/推理令牌或批量价格也应分开。

    步骤 5:计算每个已完成任务的成本

    首先,将推理费用和外部工具费用合并:

    summary["total_cost_usd"] = (
       summary["model_cost_usd"]
       + summary["tool_cost_usd"]
    )
    

    然后,将每次尝试的成本(包括失败)除以已完成任务的数量:

    summary["cost_per_completed_task_usd"] = (
       summary["total_cost_usd"]
       .div(summary["completed_tasks"])
       .where(summary["completed_tasks"] > 0)
    )
    

    让我们看一个示例提供者,它完成了价值 0.40 美元的 100 次尝试,但只有 80 次成功完成:

    Cattempt​=100/$0.40​=$0.004

    该数字反映了尝试的成本,但我们关注的是有用结果的成本。正确的计算是:80/$0.40​=$0.005

    尽管有 20 次失败的尝试仍然消耗了令牌、工具和基础设施。将所有 100 次尝试相除会使完成工作的有效成本被低估 20%。

    步骤 6:报告中位数和尾部延迟

    少数极端观察值可能会扭曲平均延迟。中位延迟描述了典型任务,而 P95 和 P99 显示尾部变得有多慢。计算首次令牌时间和端到端任务延迟的百分位数:

    def calculate_percentiles(
       dataframe: pd.DataFrame,
       metric: str,
       prefix: str,
    ) -> pd.DataFrame:
       """Calculate P50, P95, and P99 for a latency metric."""
    
       return (
           dataframe
           .groupby(["provider", "model"])[metric]
           .quantile([0.50, 0.95, 0.99])
           .unstack()
           .reset_index()
           .rename(
               columns={
                   0.50: f"p50_{prefix}_ms",
                   0.95: f"p95_{prefix}_ms",
                   0.99: f"p99_{prefix}_ms",
               }
           )
       )
    
    
    ttft_percentiles = calculate_percentiles(
       df,
       metric="ttft_ms",
       prefix="ttft",
    )
    
    task_latency_percentiles = calculate_percentiles(
       df,
       metric="task_latency_ms",
       prefix="task_latency",
    )
    
    summary = (
       summary
       .merge(
           ttft_percentiles,
           on=["provider", "model"],
           how="left",
       )
       .merge(
           task_latency_percentiles,
           on=["provider", "model"],
           how="left",
       )
    )
    

    某服务商的 P50 任务延迟可能为 6 秒,而 P99 达到 25 秒。中位数会让人误以为典型请求能够很快完成;但实际上,P99 表明约有 1% 的请求需要 25 秒或更久才能结束。尾部延迟对代理尤为重要,因为单次缓慢的推理就可能阻塞整个多步骤执行。

    分别报告成功与失败的延迟

    单一延迟分布可能掩盖超时行为。例如,失败的请求可能很快结束,导致不可靠的提供商看起来更快。

    successful_runs = df[df["success"]]
    failed_runs = df[~df["success"]]
    
    successful_latency = calculate_percentiles(
       successful_runs,
       metric="task_latency_ms",
       prefix="successful_task_latency",
    )
    
    failed_latency = calculate_percentiles(
       failed_runs,
       metric="task_latency_ms",
       prefix="failed_task_latency",
    )
    

    使用成功任务延迟作为主要性能比较,但单独披露所有尝试的延迟和错误分布。

    步骤 7:生成最终报告

    仅在准备展示层时将比例转换为百分比,将毫秒转换为秒。在数据集中保留原始单位。

    summary["success_rate_pct"] = (
       summary["success_rate"] * 100
    )
    summary["tool_validity_rate_pct"] = (
       summary["tool_validity_rate"] * 100
    )
    summary["p50_task_latency_s"] = (
       summary["p50_task_latency_ms"] / 1000
    )
    summary["p95_task_latency_s"] = (
       summary["p95_task_latency_ms"] / 1000
    )
    summary["p99_task_latency_s"] = (
       summary["p99_task_latency_ms"] / 1000
    )
    
    report_columns = [
       "provider",
       "model",
       "runs",
       "completed_tasks",
       "success_rate_pct",
       "tool_validity_rate_pct",
       "total_tokens",
       "p50_task_latency_s",
       "p95_task_latency_s",
       "p99_task_latency_s",
       "total_cost_usd",
       "cost_per_completed_task_usd",
    ]
    
    report = summary[report_columns].sort_values(
       by="cost_per_completed_task_usd"
    )
    print(report.to_string(index=False))
    

    除非单一指标能真正反映部署目标,否则不要给出普遍的“赢家”。某个提供商可能在延迟方面领先,而另一个则在可靠性或有效成本方面领先。

    表格中的合成值仅是占位符/报告格式模板,并非实际测得的提供商排名。发布前请用每个提供商‑模型组合至少 50 次真实运行替换合成结果。进行 100 次或更多运行可得到更准确的成功率和尾部延迟估计。

    提供商 运行次数 中位任务延迟 有效工具调用 总令牌数 任务成功率 每完成任务成本
    DigitalOcean AI Platform 100 6.8 s 97.0% 612,000 94% $0.0041
    Together AI 100 5.9 s 95.5% 628,000 92% $0.0044
    Fireworks AI Best value 100 5.2 s 最低延迟 96.5% 604,000 93% $0.0039 最低成本
    OpenAI 最可靠 100 6.1 s 99.0% 571,000 最少令牌 98% $0.0068

    这些合成分数有助于解释为什么胜者会根据目标而变化。相对于上面的示例数字,Fireworks 具有最低的成本和延迟;OpenAI 具有最高的工具有效性和任务成功率。为了让发布的基准具有可信度:

    • 发布提示、模式、原始 JSONL 结果及聚合脚本。
    • 固定模型标识和 SDK 版本。
    • 记录时间戳并披露基准所在地区和日期。
    • 随机顺序执行提供商。
    • 测量前预热每个端点。
    • 在多个时间窗口内执行。
    • 区分传输、模型和工具延迟。
    • 发布失败情况,而非静默删除。
    • 每季度重新运行基准。

    使用批量推理降低代理成本

    批量推理不适用于急着等待人类用户响应的代理;但许多代理工作负载可以是异步的:

    • 夜间回归测试;
    • 工具调用评估;
  • 合成轨迹生成;
  • 文档丰富;
  • 离线研究作业;
  • 质量审核;
  • 长时间运行的后台自动化。
  • DigitalOcean 记录了一个 异步批处理工作流 使用上传输入 -> 创建作业 -> 轮询 -> 下载结果。Fireworks 声称相比无服务器按 token 定价可节省 50%,针对受支持的 批处理工作负载,他们将其描述为包括大规模评估。Anthropic 记录了 50% 的批处理折扣。

    设想每晚有一批 1,000 次评估,每次评估完成 12 次调用,每次调用使用 800 个输入 token,并产生 120 个输出 token。以我们的示例费率为基准:输入 token 每百万 $0.15,输出 token 每百万 $0.60:

    • 输入 token = 1,000 × 12 × 800 = 960 万。
    • 输出 token = 1,000 × 12 × 120 = 144 万。
    • 实时推理的费用为:(9.6 × $0.15) + (1.44 × $0.60) = $2.304

    图像

    使用 50% 的批处理折扣后,相同的 token 工作负载成本约为 $1.152。在这 1,000 次评估中,如果有 900 次成功,则每次完成评估的成本大约为 $0.00256 和 $0.00128(分别)。

    这些数字仅为示例,但该方法具有普遍性。批处理模式可以降低推理成本;它本身并不会减少搜索成本、工具费用、重试次数或编排开销。具有多个批处理步骤的代理也需要显式状态管理,因为每个阶段可能依赖前一批处理的输出。

    RAG 驱动的代理:检索如何融入循环

    检索增强生成 和代理解决相关但不同的问题。RAG 提供对外部知识的访问。代理决定何时、为什么以及如何获取和利用该知识。典型的 RAG 驱动代理遵循以下路径:

    图像

    检索主要通过以下四种方式影响每任务成本:

    • 嵌入、向量数据库、重排序和外部搜索操作可能会产生额外成本。
    • 检索到的段落会增加后续调用的输入令牌数量。
    • 相关证据可以提高成功的可能性并减少幻觉。
    • 错误的检索可能导致重复搜索和/或不必要的更长轨迹。

    您可以将所有代理管道费用分解为每次尝试任务的成本,然后将其转化为更有用的每次成功完成任务的成本。

    仅凭生成定价不足以确定 RAG 驱动代理的成本。如果提供商提供廉价的模型推理,但在检索上下文耗时较长(慢预填充)、提示缓存无效、检索质量差或失败的搜索触发额外代理循环时,每完成任务的成本可能会更高。因此,应使用完整的 RAG 代理管道和一组真实的文档来评估提供商,–– 而不是仅使用独立的模型端点。

    选择推理提供者的决策清单

    在将代理工作负载提交给提供商之前,请使用此检查清单:最佳提供商是指在满足您的质量和可靠性阈值的前提下,完成任务成本最低且尾部延迟可接受的提供商——而不一定是宣传最低 token 价格的提供商。

    评估标准 建议操作
    01 — 成功完成 使用 可执行的评估器 定义成功,而非仅依赖主观印象。
    02 — 完整轨迹 对整个代理任务进行基准测试,包括模型调用、工具、重试以及最终响应。
    03 — Token 分布 使用具有代表性的输入和输出 token 量;不要假设人为的 50:50 分布
    04 — TTFT 和尾部延迟 在预期并发水平下测量中位数、p95 和 p99 延迟——而不仅仅是总体平均值。
    05 — 工具语义 确认代理选择了正确的工具并提供语义正确的参数;仅有有效的 JSON 不足够。
    06 — 模式强制 确认每个模型都支持结构化输出,因为提供商层面的特性标签可能掩盖模型特定的差异。
    07 — 每完成任务的成本 计入失败尝试、重试、检索、缓存以及付费工具使用——而不仅仅是成功调用的 token 费用。
    08 — 速率限制和恢复 测试超时、HTTP 429 响应、幂等性、重试策略以及提供商的回退行为。
    09 — 实时 vs. 离线工作负载 对于交互式代理循环使用实时推理,对于符合条件的评估和后台作业使用批量推理。

    结论

    选择代理的推理提供商不仅仅是聊天机器人时代的比较。代理会发出多个串行调用,将参数格式化为结构化工具,摄取检索到的上下文,并重试失败的步骤。错误和工具使用会在循环中放大 TTFT、模式接受度、令牌价格和可靠性的细微差异。

    决定性的指标是 每完成任务的成本。应从每次调用、输入令牌、输出令牌、重试、检索获取、工具使用费和失败等方面进行评估——而不仅仅依赖公布的令牌价格。为您的工作流添加适当的可靠性和延迟过滤器。

    DigitalOcean AI Platform、Together AI、Fireworks AI、OpenAI 和 Anthropic 均提供具备代理能力的功能。然而,功能的可用性和行为会因模型和端点而异。围绕实际任务构建的可重复基准是做出可靠选择的唯一方法。

    发布测试套件、模式、模型 ID、原始结果、基准日期以及失败列表。每季度重新运行。如果模型更新、价格变动、模式被编辑或工作负载发生变化,今天的赢家可能明天就不再胜出。长期的赢家不是那些选择热门 API 的人,而是那些构建评估流程的人,该流程能够持续确定完成任务的最可靠且最经济的路径。

    参考文献与资源

    ——

    🧑‍💻

    zhirenhun

    一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

    ← 上一篇
    如何在 Hermes Agent 中使用 DigitalOcean 知识库添加 RAG
    下一篇 →
    量化的真实权衡:FP16、INT8 与 GGUF 在生产中按模型大小的实际分歧

    📌 相关推荐

    GraphRAG 是推理问题,而非数据库问题
    2026/8/30
    构建市场时光机:使用 Python 和 WebSocket 重放交易会话
    2026/8/30
    如何自行基准测试LLM推理:值得信赖的数字设计标准
    2026/8/30
    ← 返回文章列表