你的 Agent 到底需要多少内存:从上下文、工具输出到并发任务的预算方法

2026-08-19 35 预计阅读时间: 1 分钟
来源: huggingface.co 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.

预计阅读时间:8 分钟

当一个 Agent 从简单的问答脚本变成能够调用工具、保留历史、并行执行任务的服务时,“需要多少内存”就不再等于模型上下文窗口有多大。真正占用内存的,通常还包括会话历史、工具返回值、中间状态、缓存,以及同时运行的任务数量。

这类问题适合用预算来回答,而不是凭经验猜一个机器规格。可以先把 Agent 的运行时内存拆开,再根据单个请求和并发数计算总需求。

先拆分 Agent 的内存账单

一个实用的近似模型可以写成:

总内存 ≈ 基础进程内存
       + 模型或 SDK 运行时内存
       + 单个会话状态
       + 单个请求的工具与中间结果
       + 并发带来的额外内存
       + 安全余量

其中,单个会话状态可能包含:

  • 消息历史和序列化后的请求体
  • Agent 当前计划、工具调用参数和结果
  • 压缩前后的摘要
  • 用户身份、权限和业务上下文
  • 临时缓存,例如检索结果或网页内容

工具输出尤其容易被低估。一个“读取目录”工具可能只返回几 KB,但网页抓取、日志查询、数据库导出或代码搜索很快就会产生 MB 级字符串。如果这些结果同时保存在消息历史、日志对象和缓存中,实际占用可能是原始数据的数倍。

用峰值而不是平均值做估算

平均内存只能说明系统平稳时的表现。生产环境更应该估算峰值:一次请求会产生多少中间数据,多少请求可能同时卡在工具调用阶段,以及失败重试是否会保留旧状态。

可以使用下面的简化公式:

峰值内存 ≈ 基础内存
          + 并发数 × (会话状态 + 工具结果 + 中间状态)
          + 缓存上限
          + 安全余量

例如,假设一个服务的基础进程和 SDK 占用 350 MB,每个活跃会话平均保留 2 MB,工具和中间结果最多占 8 MB,缓存限制为 512 MB,目标并发为 40,并预留 30% 安全余量:

(350 + 40 × (2 + 8) + 512) × 1.3 ≈ 1,001 MB

这个数字不是容量承诺,而是一个可审查的起点。真实部署前仍应使用压测记录峰值 RSS、容器限制和请求分布。

一个可以直接改造的预算脚本

下面的 Python 脚本只使用标准库。它把每项内存都转换为 MB,并输出目标并发下的估算值。示例参数是人为假设,运行前应替换成压测或线上观测数据。

#!/usr/bin/env python3
from dataclasses import dataclass


@dataclass
class MemoryBudget:
    base_mb: float
    session_mb: float
    tool_result_mb: float
    intermediate_mb: float
    cache_mb: float
    concurrency: int
    headroom: float = 0.30

    def estimate_mb(self) -> float:
        per_request = (
            self.session_mb
            + self.tool_result_mb
            + self.intermediate_mb
        )
        raw = self.base_mb + self.concurrency * per_request + self.cache_mb
        return raw * (1 + self.headroom)


budget = MemoryBudget(
    base_mb=350,
    session_mb=2,
    tool_result_mb=6,
    intermediate_mb=2,
    cache_mb=512,
    concurrency=40,
    headroom=0.30,
)

estimated = budget.estimate_mb()
print(f"Estimated peak memory: {estimated:.0f} MB")
print(f"Recommended container limit: {estimated / 1024:.2f} GiB")

可以把 tool_result_mb 分别替换为网页抓取、数据库查询、代码搜索等工具的实测峰值。若不同工具差异很大,最好按工具类型分别建模,而不是使用一个过于乐观的平均值。

控制内存的几个关键边界

限制工具输出,而不是只扩大上下文窗口

工具应该有明确的结果上限。例如日志查询可以限制返回行数,网页工具可以只保留正文,代码搜索可以只返回匹配片段。超过限制时,返回分页标记或摘要,让 Agent 决定是否继续获取。

def cap_text(text: str, max_chars: int = 20_000) -> str:
    if len(text) <= max_chars:
        return text
    return text[:max_chars] + "\n...[truncated; request a narrower query]"

字符数不是精确的内存大小,但它是工具接口中容易执行和监控的第一道边界。生产实现还应限制嵌套 JSON、数组元素数量和单次下载大小。

让会话历史有生命周期

不要无限追加消息。可以按消息数量、字符数或估算 token 数触发摘要,并删除已经不再需要的原始工具输出。摘要本身也应该设置上限,否则只是把无限历史换成另一种无限增长。

区分并发限制和队列长度

把并发限制设为 40,不代表可以让队列无限增长。排队请求可能已经包含完整请求体、身份上下文和重试状态。应同时限制:

  • 正在执行的 Agent 数量
  • 等待队列长度
  • 单个请求的最大执行时间
  • 工具调用的最大次数
  • 重试次数和保留状态的时间

当预算不足时,优先拒绝或延迟新任务,而不是让进程持续增长直到被操作系统终止。

监控分项,而不只看容器总内存

至少记录基础进程内存、活跃会话数、每个会话的历史大小、工具结果大小、缓存大小和并发请求数。只有看到分项数据,才能判断问题来自上下文增长、某个工具失控,还是并发配置过高。

部署前的检查清单

  • 用真实工具输出测量一次请求的平均值和 P95/P99 峰值
  • 分别压测低、中、高上下文长度
  • 验证超大工具结果会被截断、分页或拒绝
  • 为会话历史、缓存、队列和重试状态设置上限
  • 用峰值而不是平均值计算容器内存限制
  • 为运行时抖动和发布期间的额外进程预留空间
  • 观察 OOM 前的指标,确认服务能够尽早返回可诊断的错误

Agent 的内存需求不是一个固定常数,而是请求形态和并发策略共同产生的结果。先定义每个状态的上限,再用压测数据填入预算模型,通常比直接选择更大的机器更容易解释,也更容易长期控制成本。


相关推荐