企业级AI生产Token优化系统指南

0 阅读11分钟

\n\n本文深入分析了企业级AI应用中Token消耗的本质,指出其并非简单的财务指标,而是分布式系统与资源利用问题。通过优化提示词、引入缓存、上下文压缩及模型级联等手段,作者成功解决了AI Agent在规模化运行中面临的成本与延迟瓶颈,为构建高可靠、高吞吐的AI系统提供了工程化思路。

译自:The systems guide to production token optimization

作者:Boris Chabeda

当企业AI应用实现规模化时,它们不可避免地会遇到瓶颈。对于许多工程团队而言,这一瓶颈最初被诊断为账单问题,即每个月的API费用失去了控制。然而,将Token消耗纯粹视为一项财务指标,是对LLM(大语言模型)如何在生产环境中运行的根本性误解。Token优化与其它的优化方式不同,它不是一种会计练习,而是一项分布式系统和硬件利用率的挑战。

“Token优化与其它的优化方式不同,它不是一种会计练习,而是一项分布式系统和硬件利用率的挑战。”

在本指南中,我们将通过Concierge(一个对延迟敏感、同步的客户支持代理)和Pathfinder(一个异步、多步骤的自主CI调试代理)的视角,探讨这些系统在成长过程中如何受到自回归瓶颈的困扰,以及我们是如何解决这些问题的。

您实际支付的是什么?

Token不等于单词:将其视为单词会破坏您的预算“模型”。每个主流的LLM提供商都使用字节对编码(BPE)对文本进行标记化处理,将单词分解为子词单元。虽然常见的单词保持完整,但较生僻的单词或标点符号会分解为片段。作为一个经验法则,1个Token约等于4个字符,或标准英语散文中0.75个单词。

在进行生产环境预算时,您必须考虑到结构性定价差异;提供商对输入Token和输出Token的收费标准不同。输出Token的价格通常是输入Token的4-5倍。

作为基准,假设一个中端前沿模型每百万个输入Token大约花费3美元,每百万个输出Token花费15美元。

二次方的历史记录税

LLM提供商的API是完全无状态的;为了让LLM表现得好像记得过去发生的事件一样,您必须在每一次API调用中重新发送会话和输入的整个历史记录。

这意味着模型之前输出的内容会在随后的步骤中作为输入持续不断地向您收费。这引发了一种复合成本,影响了ConciergePathfinder,尽管它们的扩展曲线不同。

S为静态系统上下文(指令和模式),u为每一步的输入数据,r为模型的响应载荷。那么在每一次给定的轮次k中,输入成本为:

定义每一次给定轮次输入成本的公式。

当您在N个步骤的完整执行过程中汇总此成本时,总输入Token量会呈二次方复合增长。

总输入成本公式,即执行运行中每一给定轮次输入成本的总和(共N步)。

这种O(N^2)的历史记录积累正是导致成本和延迟激增的确切机制。

变量Concierge (聊天系统)Pathfinder (自主代理)
静态上下文 (S)3,100 Token (完整的退货/运输政策和品牌准则)1,200 Token (工具定义、系统约束、CI环境数据)
输入数据 (u)80 Token (简短的客户聊天回复)900 Token (海量原始文本载荷:日志摘录、文件读取、shell输出)
响应载荷 (r)220 Token (礼貌的面向客户的回答)300 Token (内部独白 + JSON工具参数)
步骤乘数 (N)10 轮 (平均支持会话长度)15 步 (平均代理排障循环长度)

当我们使用二次方公式计算单个会话消耗的总输入Token时:

  • Concierge: 每个10轮工单消耗45,300 Token
  • Pathfinder: 每个15步的转弯消耗150,000 Token

由于Pathfinder的步骤增量比Concierge大4倍,其Token成本曲线更加陡峭。如果Pathfinder陷入无限工具使用循环并达到30步,单次运行可能消耗570,000 Token

解决方案

修复单次调用

提示词卫生(Prompt hygiene): 将静态参考文档硬编码到系统提示词中意味着您每次轮询都要为解析相同的文本付费。因此,我们将静态文本从提示词中剥离,并改用动态注入。

对于Concierge,我们实施了一个RAG步骤,仅提取与工单相关的2-3个政策片段。提示词从3,100 Token减少到380 Token。对于10轮的会话,减少了60%。

对于Pathfinder,我们使用了LLMLingua-2进行自动提示词压缩,以便在发送给模型之前压缩冗长的CI日志文件。通过过滤掉非必要的日志行,我们在不牺牲调试准确性的情况下,将传入工具观测数据的规模减少了3倍。

from llmlingua import PromptCompressor

compressor = PromptCompressor(
model_name="microsoft/llmlingua-2-xlm-roberta-large-meetingbank",
use_llmlingua2=True
)

try:
compressed_result = compressor.compress_prompt(
raw_ci_log_text,
rate=0.33,
force_tokens=["Error", "Exception", "Failed", "Traceback", "FATAL"]
)
# Pass high-density payload to the frontier model
compact_prompt = compressed_result["compressed_prompt"]
except Exception as e:
print(f"Compression failed, falling back to raw log text: {e}")
# Graceful degradation: pass the raw (or truncated) log if compression fails
compact_prompt = raw_ci_log_text

消除重试: 依赖开放式的“返回JSON”等散文指令会导致格式错误。当解析失败时,系统会启动同步重试,发送整个累积的上下文,就好像它是新的一次尝试一样。我们将整个自然语言格式化请求替换为通过强制模式验证的严格结构化契约。

在Concierge和Pathfinder中,我们将输出格式转换为用于工具调用模式和工具执行载荷的严格Pydantic模式。两个系统的格式错误输出均降至0.5%以下,消除了由级联队列导致的尾部延迟峰值。

# Unified Schema Enforcement for Concierge Responses & Pathfinder Tool Execution
from pydantic import BaseModel
from typing import Literal

class TicketResponse(BaseModel):
    reply: str
    category: Literal["shipping", "returns", "billing", "product", "other"]
    escalate: bool
    confidence: float

# The API is structurally locked into emitting validated JSON matching the schema
response = client.messages.create(
    model="claude-opus-4",
    system=SYSTEM_PROMPT,
    messages=messages,
    tools=[
    {
    "name": "respond_to_ticket",
    "description": "Formulate a response and classify the support ticket.",
    "input_schema": TicketResponse.model_json_schema()
    }
    ],
    tool_choice={"type": "tool", "name": "respond_to_ticket"},
)

输出Token限制: 模型自然会生成冗长的推理链和对话填充,从而推高昂贵的输出Token。如果LLM提供商公开了Logit偏置(Logit bias),您可以在解码时直接抑制有效集之外的每一个Token;如果不支持,约束解码库(Outlines, Guidance)或带有枚举类型模式的强制工具调用将为您提供相同的保证。

状态管理

模型的无状态特性意味着我们必须在每次轮询时解析静态提示词前缀和历史步骤。我们引入了显式的缓存断点,允许推理引擎复用静态块的状态。我们修改了Concierge和Pathfinder,将稳定的历史片段标记为可缓存。按照标准的供应商定价,缓存读取可享受90%的折扣。请务必与您的供应商确认是否启用了缓存。

# Caching the stable history prefix for a multi-turn session
response = client.messages.create(
    model="claude-sonnet-4",
    max_tokens=4096,
    system=[{
        "type": "text",
        "text": SYSTEM_PROMPT,
        "cache_control": {"type": "ephemeral"} # Cache hits drop prefix costs by 90%
    }],
    tools=TOOL_SCHEMAS,
    messages=session_history + [{"role": "user", "content": current_step_input}],
)

对于10轮的Concierge聊天,这使输入成本降低了约70%。对于15步的Pathfinder轨迹,成本降低了76%。

语义缓存

跨不同会话的重复查询正在触发冗余的前沿模型调用。我们在LLM上游使用Redis实现了一个向量相似度缓存层。我们的Concierge服务分析显示,34%的客户支持工单是常见常见问题解答(FAQ)的语义重复。

拦截这些请求将命中请求的延迟降低到50ms以下。由于CI流水线日志的特性,我们尚未为Pathfinder的输入找到合适的缓存。

import os
import json
import redis
from redis.commands.search.query import Query

# Configure connection via environment variable for environment portability
redis_url = os.environ.get("REDIS_URL", "redis://localhost:6379")
r = redis.Redis.from_url(redis_url)

def get_cached_response(tenant_id, query_text, threshold=0.92):
    try:
results = r.ft(f"cache_idx:{tenant_id}").search( # scoped by tenant -- see below
Query("*=>[KNN 1 @vector $vec AS score]").sort_by("score").dialect(2),
query_params={"vec": query_vec.tobytes()},
)
except redis.RedisError as e:
print(f"Redis cache error: {e}")
return None # Fail-open: gracefully fall back to a cache miss)
  query_vec = embed(query_text) # small, fast bi-encoder -- not the frontier model

try:
results = r.ft(f"cache_idx:{tenant_id}").search( # scoped by tenant -- see below
Query("*=>[KNN 1 @vector $vec AS score]").sort_by("score").dialect(2),
query_params={"vec": query_vec.tobytes()},
)
except redis.RedisError as e:
print(f"Redis cache error: {e}")
return None # Fail-open: gracefully fall back to a cache miss

语义缓存可能是一个安全问题,因为如果您选择全局缓存,客户A的账户特定答案可能会因为用词接近而被提供给客户B。为了缓解这种情况,我们将缓存分为两层:一个用于租户无关内容的全局缓存,以及一个按租户、按用户命名的命名空间,对于任何触及账户状态的内容,其键值中都包含了嵌入在前缀中的租户ID。

缓存投毒是另一个风险:我们只写入那些通过模式验证和注入模式分类器的响应,并在每个缓存条目上盖上其源TraceId,我们鼓励定期清除未知缓存。

上下文压缩

无上限的对话或代理轨迹导致N不断增长,扩大了成本曲线并导致延迟恶化。我们通过实施滑动窗口来限制N,利用小型、超廉价的模型总结历史上下文。对于Concierge,我们保留最近的3轮原文,同时将较旧的轮次浓缩成一个滚动的元数据块。

对于Pathfinder,当调试步骤超过4次运行时,我们将最旧的工具执行输出修剪并总结成一个紧凑的时间线,将开放式的二次方成本爆炸转变为可预测的、有界的窗口。

def compact_session_history(history_steps: List[Dict[str, Any]], keep_recent: int = 3) -> List[Dict[str, Any]];
    """Flattens older history into a cheap summary block, preserving recent context."""
    if len(history_steps) <= keep_recent:
        return history_steps
    old_steps = history_steps[:-keep_recent]
    recent_steps = history_steps[-keep_recent:]
    
# Compress the old history using a fast, low-cost utility model
try:
historical_summary = summarize_with_utility_model(old_steps)
# Note: Anthropic prohibits 'system' roles in the messages array.
# Using 'assistant' ensures cross-provider compatibility.
return [{"role": "assistant", "content": f"[System Context: Summary of prior steps: {historical_summary}]"}] + recent_steps
except Exception as e:
print(f"History compression failed: {e}")
# Fallback: Return the uncompressed history to gracefully degrade
return history_steps

模型级联(Model cascading)

将每一次操作都直接导向昂贵的前沿模型,对于平凡任务而言是巨大的过度配置。我们集成了LiteLLM作为内部路由网关来实现模型级联,将每个请求路由到有能力完成任务的最低成本模型。

“将每一次操作都直接导向昂贵的前沿模型,对于平凡任务而言是巨大的过度配置。”

简单、重复的任务被路由到更小的模型,这使得70%的Concierge聊天从前沿模型中分流出去。对于Pathfinder,我们将代理循环分解为独立的子任务:高级规划、工具选择和代码补丁合成保留在前沿模型中,而诸如日志解析、正则表达式提取和错误字符串格式化等机械的、文本密集的任务则卸载到较小的模型中。这种混合编排使Pathfinder的Token成本降低了50%以上。

接下来是什么?

Concierge和Pathfinder的转型证明了关于生产环境AI的一个基本事实。您不能仅仅依赖前沿模型的自然语言能力来实现规模化。您必须围绕它来设计系统。通过将重点从天真的Token减少转向最大化系统资源利用率,我们夺回了对基础设施的绝对控制权。

“生成式AI时代的效率,不是由您的运营成本有多低来定义的,而是由您压缩信息的密度来定义的。”

生成式AI时代的效率,不是由您的运营成本有多低来定义的,而是由您压缩信息的密度、提供服务的速度以及解析输出的可靠程度来定义的。这里详述的架构决策不仅仅是一种Token优化策略;它们是构建高吞吐量、经过实战检验且具有弹性的AI系统所需的坚实基础。全 工智能