首页 / 文章

大规模运行AWS AI智能体:令牌、使用量、限制与自主性

针对AWS智能体的令牌计数、使用量监控、上下文限制恢复以及持久化自主工作流,均通过全栈JavaScript开发者熟悉的模式进行说明。

发布者:REACTAPP.TOP阅读约 8 分钟1929 词

来源: 🏗️ Building Production-Ready Enterprise AI Agents on AWS — Part 3: Tokenization, Monitoring, Context Limits & Autonomous Workflows

一个设计合理、经过验证且具备检索功能的智能体,仍需在正式上线后的第一个月里经受考验。决定其能否存活下来的因素大多并不光鲜:如何计算令牌数、如何监控资源消耗、上下文窗口满载时会发生什么,以及长周期的自主循环是否像真正的流程引擎那样构建。如果你有在 AWS 上使用全栈 JavaScript 的经验,这些内容都会与你已有的知识对应起来,本讲解也会始终利用这些类比。

令牌化即序列化层

Web 应用会在数据通过网络传输之前使用 JSON.stringify 或 protobuf 进行序列化。语言模型则对文本执行类似的操作:在处理任何内容之前,文本会被分割成令牌,这些令牌是词片段而非单个字符或完整单词。

"Enterprise agents scale."
   → ["Enterprise", " agents", " scale", "."]   (≈4 tokens, illustrative)

作为工程师,关注这一点的原因主要有三点。

  • 计费单位就是令牌,类似于 Lambda 的 GB-秒。输入和输出都会产生费用,而且由于 Bedrock 不同模型的价格不同,令牌数量直接决定了 AWS 的账单金额。
  • 令牌还决定了数据量的上限,就像 express.json({ limit: '5mb' }) 中的请求体大小限制一样。在此情况下,上限指的是上下文窗口大小,当前模型该数值可达数十万令牌且还在持续增长,因此请查阅对应模型的实际限制,切勿猜测。
  • 不同语言的令牌化处理方式并不一致。非英语文本通常每个单词需要更多令牌,这对多区域部署的产品尤为重要。

在 Node.js 中,在发送数据前进行计数,就如同在发起请求前测量负载大小一样。

// Node.js: counting tokens before sending a request
// (conceptually like calculating payload size before a fetch call)
import { encoding_for_model } from "tiktoken";

const enc = encoding_for_model("gpt-4o");
const tokens = enc.encode(userPrompt);
console.log(`Payload size: ${tokens.length} tokens`); // like Content-Length
enc.free();

在 Bedrock 上,你可以跳过第三方分词器。CountTokens API 会在服务器端为支持的 Claude 模型统计词数,其消息结构与 Converse API 相同,还能让你在无需支付推理费用的情况下预先检查请求的词数:

# Python (boto3): pre-flight token count via Bedrock, no separate tokenizer library needed
response = bedrock_runtime.count_tokens(
    modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
    input={"converse": {"messages": [{"role": "user", "content": [{"text": userPrompt}]}]}}
)
print(f"Input tokens: {response['inputTokens']}")

经验教训:应像测量 HTTP 请求载荷大小那样在发送数据之前就统计令牌数量,而非在产生费用或请求被拒绝之后。

像 CloudWatch 监控 LLM 费用一样监控令牌使用情况

上一节讨论的是单个请求的情况。而综合视图则能显示每个代理每租户、每天的令牌消耗情况,你需要在财务部门之前掌握这些信息。没人会在没有仪表盘和警报功能的情况下将 Express API 上线到 AWS,令牌监控也应得到同样的重视。

对于代理而言,关键的监测指标包括:

  • 每个请求的提示词令牌数,相当于传入载荷的大小。
  • 每个请求的完成令牌数,相当于响应大小。
  • 会话中的累计令牌数,即该会话所占用的内存空间。
  • 每个代理、用户或租户的成本,其计量方式与多租户 SaaS 中按租户统计数据库使用量相同。
  • 智能体调用的 p95 和 p99 延迟需符合您设定的常规服务水平指标。请明确说明这些服务水平指标,一旦出现风险则切换为缓存路径或尽力而为的传输方式。
  • 以每分钟令牌数表示的使用速率,可与按每秒请求数设定的速率限制相类比。
  • 在 AWS 上的具体操作步骤:

    • 发送自定义的 CloudWatch 指标,包括提示词令牌数、完成词令牌数以及成本(prompt_tokens、completion_tokens、cost_usd),并标注智能体名称和租户信息。
    • 启用提示词缓存。Bedrock 可以以极低的成本和更低的延迟重复使用那些长期稳定不变的内容,如系统提示词、工具定义以及少量示例。请先将不可变内容放入缓存,否则每次调用时都需为相同的令牌支付全额费用。
    • 创建 CloudWatch 警报,当每日支出超过阈值时通过 SNS 发送通知,功能与 AWS 账单警报类似。
    • 通过 Kinesis Firehose 或直接写入的方式将每次代理调用记录到 S3 中,以便保留审计轨迹。SOC 2 认证及企业客户都会要求这样做。
    • 估算方法与 Lambda 相同:加上提示词和完成词令牌的费用,再乘以模型价格,最后加上每次会话的检索及基础设施成本。

    如果无法说明上周二 Agent X 在不同客户身上的具体成本,那就只能用于演示,无法投入实际生产,而这两者之间存在巨大差距。

    上下文限制错误:断路器机制与分页处理

    每个上下文窗口都有容量上限。长时间的对话、庞大的工具输出以及过大的检索数据都可能超出这一限制。这就好比智能体世界中的413 Payload Too Large错误,只不过情况更糟,因为它可能出现在多步骤任务的关键环节。

    相应的解决策略直接源自后端工程技术:

    1. 总结与压缩。像日志轮转一样定期压缩旧对话内容。
    2. 滑动窗口或截断。如同浏览大量查询结果时不会全部加载,同样要丢弃最旧的对话内容。
    3. 转交给检索系统处理。将相关资料放在提示词之外,按需获取,体现懒加载的理念。
    4. 拆分成多个子智能体。将一个庞大的任务拆分成多个带有新上下文的独立任务,就像把一个庞大的批量作业拆分成一系列小型任务。
  • 优雅地降级。如果在任务执行过程中无法恢复溢出状态,应以结构化且可恢复的检查点形式失败,而绝不能出现无声的幻觉结果。
  • 以下是代码中的捕获并压缩模式,类似于处理数据库超时情况:

    # Conceptual: catching a context-limit error, like catching a DB timeout
    try:
        result = agent.run(task, conversation_history)
    except AgentError as err:
        if err.code == "context_length_exceeded":
            # Keep the system prompt intact — compact the *conversation*, never the identity.
            # Summarizing everything (including system instructions) is how agents quietly
            # "forget" their role mid-task, the same way you'd never let a cache-eviction
            # policy drop your app's config alongside its session data.
            compacted = summarize(conversation_history, preserve="system_prompt")
            result = agent.run(task, compacted)
        else:
            raise  # don't swallow unrelated errors
    

    上下文限制错误是正常现象,并非异常情况。应将其视为速率限制和超时问题来处理:通过退避策略进行重试,在DynamoDB中保存检查点,并实现优雅降级。那种直接吞掉错误的空catch块是绝不可接受的。

    作为状态机的自主工作流

    一个自主代理不会只响应一次,它会规划、执行操作、观察情况并循环往复,直到达成目标。任何曾经为多步骤结账流程构建过XState状态机、Redux saga或AWS Step Functions工作流的人,都熟悉这种结构。这就是常见的ReAct风格循环:规划、执行、观察和评估,同时具备完成时的退出路径以及需要人工介入时的升级路径,这类系统应当被设计成具有高稳定性的工作流引擎。

    • 可重试步骤。重新执行某个步骤时不得向客户收取两次费用,也不得发送重复邮件。
  • 明确的停止条件。限制迭代次数、成本和时间,或者避免创建带有无限循环的while(true)结构。
  • 人工介入机制。与补偿事务类似,当置信度较低或涉及重大风险时,应交由人工处理而非自动执行。
  • 持久化状态。在DynamoDB或Step Functions的状态中为每一步创建检查点,以便在工作流中断后能够继续执行且不会丢失事务。同时为会话项设置DynamoDB的TTL,使过期的对话自动失效,这与Redis会话键的过期机制相同。
  • 备用模型。如果主模型(如 Bedrock 平台上的 Claude)出现超时或流量限制,断路器应自动切换到备用模型(如 Amazon Nova),且不应向用户显示错误信息。这与 HTTP 客户端切换到备用区域的做法类似。
  • 模型管理。处理模型更换时应像处理任何生产环境部署一样:先在部分流量上测试,需经过审批,并准备好回滚方案。“我们只是更换了模型”这种操作不应被视为未经批准的变更。
  • 终止开关。需提供一种手动或基于阈值触发的方式,能够立即暂停所有智能体实例,类似于用于控制异常发布的特性标志终止开关。
  • 检查点不过是 DynamoDB 中的另一条可恢复任务记录,Step Functions 在重启时会读取这些记录,从上一次正常执行的步骤继续执行:

    {
      "pk": "session#8f2c1a",
      "sk": "checkpoint#2026-08-23T10:15:00Z",
      "step": "plan",
      "hop_count": 2,
      "cost_so_far_usd": 0.014,
      "state": { "goal": "resolve billing dispute", "last_action": "lookup_charge" },
      "ttl": 1788084900
    }
    

    不要将自主性构建为围绕 LLM 调用的 while 循环。应当构建一个持久、可观测且能恢复状态的机器状态,这才是其本质所在。如果需要现成的解决方案,Step Functions 非常适合这一需求。

    在这一切之上还有一层:评估机制。防护规则、检索质量以及事实依据等方面都和应用程序代码一样需要回归测试。评估套件会使用固定的一组输入对智能体进行测试,并根据预期行为为输出打分,这样即便调整提示词或更换模型,也不会悄无声息地降低系统性能。Ragas 用于评估检索相关的指标,如忠实度与上下文相关性,而 Promptfoo 和 Braintrust 则负责更广泛的提示词及输出测试。你可以像使用 pytest 一样将所选的评估工具集成到持续集成流程中,这样一旦评估失败就会阻止部署,无需事后人工检查输出结果。

    生产就绪检查清单

    • 每个智能体都拥有一个可测试的职责以及范围明确的IAM权限。
    • 任务交接通过持久消息队列(SQS或EventBridge)传递结构化上下文,并设有防止循环的机制。
    • 输入约束与输出约束相互独立、可组合,且各自有独立的版本控制。
    • 数据检索质量有专门的精确度与召回率指标,不会被简单归结为“AI出错”。
    • 所有高重要性任务都会经过幻觉检测:包括事实依据核查、置信度评分或人工审核。
    • 在发起调用之前就会统计令牌使用量,而不仅仅是在之后计费。
    • CloudWatch控制台可按租户和智能体分别显示成本明细,并提供SNS预算预警功能。
    • 针对上下文不足的错误,已有明确的恢复方案:压缩数据、截断内容或转交处理。
  • 自主循环在迭代次数、耗时及资源使用方面存在严格限制。
  • 诸如退款、发送邮件和删除操作等不可逆行为需要人工审批。
  • 智能体的决策会被完整记录在S3或CloudWatch中,以确保合规性。
  • 在任何提示词或模型变更发布之前,都会通过CI系统进行自动测试,这一环节对于应用程序代码来说是绝不会跳过的。
  • 在投入生产之前,已通过模拟模型超时、检索结果损坏以及交接循环等场景对测试环境进行了压力测试,从而验证了相应的恢复机制。
  • 整体优势

    在你所熟悉的完整技术栈体系中,每个智能体概念都有对应的实现方式。智能体是具备决策能力的微服务,而数据传递则通过可靠、可追踪且有限跳数的路由机制完成。约束机制由多层中间件构成,信息检索则依托更智能的查询层实现,至于虚假信息,则是通过事实依据与验证来识别,而非通过友善请求来解决。令牌既用于表示数据载荷的大小,也是支付费用的单位。消耗监控功能相当于模型使用情况的 CloudWatch 工具。过大的上下文窗口会导致可预见的故障,因此需要相应的恢复方案;而自主性则体现为一种稳定的状态机,与单纯的 while 循环截然不同。

    企业级代理在生产环境中很少因为模型不够智能而失败。它们失败的真正原因在于团队忽视了那些在处理普通 API 时绝不会忽略的分布式系统基本原则:验证、可观测性、重试机制、幂等性,以及故障处理与优雅降级策略。如果你曾在 AWS 上部署过具备高容错性的 JavaScript 应用,那么你已经具备了相关的直觉。接下来要做的就是将这些直觉应用到那些陌生且难以预测、需要以文本形式响应的依赖组件上。