从万亿 Token 到生产落地:AT&T 如何构建可扩展的电信 AI 计算体系

2026-07-24 27 预计阅读时间: 1 分钟
来源: azure.microsoft.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

AT&T 在开发 OTel2.0 的过程中处理了约一万亿个 Token。支撑这一规模的并非单一模型或单一 GPU,而是 Microsoft Foundry Managed Compute、开放模型,以及 AMD 和 NVIDIA GPU 基础设施组成的弹性计算体系。

这组信息揭示了大规模 AI 工程中的一个关键变化:当工作负载进入千亿乃至万亿 Token 级别,团队需要管理的已经不只是模型效果,而是模型选择、异构硬件、任务调度、吞吐、失败恢复和成本之间的整体关系。

万亿 Token 改变了哪些工程问题

一万亿 Token 不是一次超长的模型调用,更可能来自大量数据处理、模型实验、批量推理和反复迭代的累积。即使单次请求只处理几千个 Token,总请求量也可能迅速达到数亿级。

在这种规模下,几个原本不显眼的问题会成为系统瓶颈:

  • 吞吐而非单次延迟:离线处理更关心每小时完成多少 Token,而不是某个请求快了几十毫秒。
  • 失败成本被放大:万分之一的失败率落到数亿次请求上,仍会产生大量重试任务。
  • 模型并非固定资产:不同阶段可能需要不同大小、不同许可方式或不同硬件适配能力的模型。
  • GPU 利用率决定成本:输入长度分散、批次过小或调度不均都会让昂贵算力处于空闲状态。
  • 可观测性必须细化到任务和模型版本:仅监控 CPU、GPU 和 HTTP 状态码,无法解释 Token 成本为何突然上升。

因此,生产级架构通常要把数据准备、任务队列、推理端点和结果存储拆开。计算资源可以扩缩,任务状态则保存在独立的持久化系统中。节点被替换或端点调整时,工作不应从头开始。

模型选择与异构 GPU 为什么要解耦

AT&T 的案例同时涉及开放模型,以及 AMD 和 NVIDIA GPU 基础设施。这意味着模型层与算力层之间需要建立明确边界。

一种模型可能在某类 GPU 上具有更好的吞吐,另一种模型则可能因为运行时、量化格式或算子支持,在不同硬件上表现更稳定。团队不应仅根据单次基准测试决定所有工作负载,而应建立包含以下指标的评估矩阵:

维度 需要记录的指标
质量 任务成功率、人工评估结果、领域数据集得分
性能 首 Token 延迟、每秒输出 Token、批处理吞吐
资源 显存占用、GPU 利用率、并发上限
成本 每百万输入和输出 Token 的综合成本
运维 冷启动时间、失败率、驱动和运行时兼容性

Microsoft Foundry Managed Compute 在这里承担的是托管计算和扩展基础设施的角色。对应用层而言,更重要的设计目标是让调用协议、任务格式和指标定义保持稳定。这样更换模型、调整 GPU 池或扩展端点时,不必重写整条数据流水线。

可以这样实践:构建可恢复的批量推理客户端

下面是一个可直接运行并改造的 Python 示例。它使用通用 HTTP 接口提交批量文本,记录 Token 用量,并对限流或临时服务错误进行指数退避。

这里明确做一个假设:你的托管推理端点接受 POST /v1/chat/completions 形式的请求,并返回 OpenAI 兼容的 usage 字段。实际接入 Microsoft Foundry 中部署的模型时,需要把 AI_ENDPOINT、鉴权方式、模型名和请求格式替换为对应部署提供的值。

安装依赖:

python -m pip install httpx
export AI_ENDPOINT="https://your-endpoint.example.com"
export AI_API_KEY="replace-me"
export AI_MODEL="your-deployed-model"

将以下代码保存为 batch_infer.py,然后运行 python batch_infer.py

import asyncio
import os
import random

import httpx

ENDPOINT = os.environ["AI_ENDPOINT"].rstrip("/")
API_KEY = os.environ["AI_API_KEY"]
MODEL = os.environ["AI_MODEL"]
MAX_CONCURRENCY = int(os.getenv("MAX_CONCURRENCY", "8"))

async def infer(client: httpx.AsyncClient, text: str) -> dict:
    payload = {
        "model": MODEL,
        "messages": [
            {"role": "system", "content": "Return a concise technical summary."},
            {"role": "user", "content": text},
        ],
        "temperature": 0,
        "max_tokens": 128,
    }

    for attempt in range(6):
        response = await client.post(
            f"{ENDPOINT}/v1/chat/completions",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json=payload,
        )
        if response.status_code < 400:
            return response.json()
        if response.status_code not in {408, 429, 500, 502, 503, 504}:
            response.raise_for_status()

        delay = min(30, 2 ** attempt) + random.random()
        await asyncio.sleep(delay)

    raise RuntimeError("Inference failed after all retry attempts")

async def main() -> None:
    documents = [
        "Network event A: packet loss increased after a routing change.",
        "Network event B: latency returned to baseline after capacity expansion.",
        "Network event C: an alarm was correlated with a maintenance window.",
    ]
    semaphore = asyncio.Semaphore(MAX_CONCURRENCY)

    async with httpx.AsyncClient(timeout=60) as client:
        async def bounded_infer(text: str) -> dict:
            async with semaphore:
                return await infer(client, text)

        results = await asyncio.gather(
            *(bounded_infer(document) for document in documents)
        )

    total_prompt = sum(item.get("usage", {}).get("prompt_tokens", 0) for item in results)
    total_completion = sum(
        item.get("usage", {}).get("completion_tokens", 0) for item in results
    )

    for item in results:
        print(item["choices"][0]["message"]["content"])
    print({
        "prompt_tokens": total_prompt,
        "completion_tokens": total_completion,
        "total_tokens": total_prompt + total_completion,
    })

if __name__ == "__main__":
    asyncio.run(main())

这段代码适合验证接口,但还不是万亿 Token 流水线。进入生产环境后,应把内存中的 documents 替换为消息队列或分区数据集,并为每个任务保存稳定的任务 ID。消费者完成推理后,以任务 ID 幂等写入结果;只有写入成功,才确认队列消息。这样即使实例被回收,也不会静默丢失任务。

扩展时应监控什么

总 Token 数只说明规模,无法单独说明系统是否高效。建议至少按模型版本、端点、GPU 池和任务类型拆分以下指标:

metrics:
  counters:
    - inference_requests_total
    - inference_failures_total
    - prompt_tokens_total
    - completion_tokens_total
    - retry_attempts_total
  histograms:
    - request_latency_seconds
    - queue_wait_seconds
    - tokens_per_request
  gauges:
    - queue_depth
    - active_workers
    - gpu_utilization_ratio
    - gpu_memory_utilization_ratio
labels:
  - model_version
  - endpoint
  - accelerator_vendor
  - workload_type

这份 YAML 是一个与监控产品无关的指标清单,可以据此配置 Prometheus、OpenTelemetry 或云监控平台。标签必须控制基数,不要直接使用用户 ID、请求 ID或完整提示词作为指标标签。

成本分析也不应只看 GPU 小时。更有决策价值的指标是“每个成功业务任务的成本”,因为低价模型如果失败率更高、输出更长或需要更多重试,最终未必便宜。

从试验走向生产的检查清单

AT&T 约一万亿 Token 的处理规模表明,电信 AI 已经可以进入大规模工程化阶段,但规模本身并不等于可靠性。采用类似架构时,可以按以下顺序推进:

  • 先用真实领域样本建立质量、吞吐和成本基线,再决定模型与 GPU 组合。
  • 将模型调用封装在稳定接口之后,避免业务代码绑定某个模型或加速器厂商。
  • 使用可恢复队列、幂等写入和检查点,确保扩缩容不会破坏任务状态。
  • 同时记录输入与输出 Token,按模型版本和工作负载分摊成本。
  • 为限流、端点故障和区域容量不足设计退避、熔断与降级策略。
  • 对提示词、输入数据、模型权重和输出结果实施访问控制与保留策略。

真正可持续的万亿 Token 系统,不是简单堆叠更多 GPU,而是让模型、计算资源和数据流水线能够独立演进。灵活的模型选择与可扩展的托管计算提供了基础,可靠的任务协议、指标体系和故障恢复机制则决定它能否长期运行。


相关推荐