决定 AI Agent 能力上限的第二层:三种失控,各需一个独立上限
掘金 5913 分那篇把 Agent 的能力上限落在上下文工程,论证很扎实——但它写到的路由、预算、熔断都停在单步之内。任务级的三个数字不在:最多跑多少步、最多花多少钱、允不允许派子 agent。步数上限从 10 提到 20,最坏账单的输入那一半约翻四倍——三种失控各需一个独立上限,外加三个能设上限的前提。

Agent 的能力上限不是一个数,是两层串起来的:上下文层决定它每一步看到什么、
判断得准不准;调用层决定这个循环能不能跑完、跑多久、花多少钱。 两层是串联,
不是并联——上下文优化到满分的 Agent,跑在一条没有花费上限的 while True 里,
上限依然是零。
本周掘金那篇 5913 分的帖子讲的是第一层,讲得很扎实。这篇讲第二层, 它有三个名字:步数、花费、扇出。
热帖把上限落在上下文工程,这个判断在它的论域里是对的
掘金那篇《走进 AI Agent 第二篇:决定 AI Agent 能力上限的关键技术》 (本刊选题雷达 2026-08-30 采集时 5913 分、13 条评论,均为移动读数)给出的答案是一句话:
模型的能力是天花板,上下文的质量才是真正决定 Agent 能跳多高的地板。
这句话我完全同意,论证密度也高。举三个它讲到的地方:
- KV Cache 约束。它给了三条硬规则:系统提示词与工具定义一旦确定就不要改、 动态信息永远追加到末尾、用标准 API 格式不要自行拼接消息。这三条是缓存命中率的 物理前提,不是风格建议。
- 上下文腐化与压缩。它引了 Lost in the Middle 那一类长上下文检索退化现象, 并给出作者自己的复现读数:用 Kimi K3 把上下文预算压到 128K 触发压缩, 一次把 147,877 字符压到 1,963 字符仍保住了关键信息——这是它自己跑的, 不是我们的实测。
- 状态栏。这一条它引的是第三方基准(ContextDistill-Bench),弱模型准确率涨 40 到 54 个百分点,强模型思考 token 砍掉八九成以上。
有意思的是,这篇文章并非没碰调用层的概念,而是它碰到的每一个都停在单步之内:
它写了「路由」——指的是靠 description 字段选中哪个 Skill;写了「预算」——指的是
单个工具输出占多少上下文;甚至写了「熔断器」——指的是压缩连续失败后停手。
三个词都在,指向的都是这一步内部怎么安排。
任务级的那三个数字不在:整个任务最多跑多少步、最多花多少钱、允不允许派子 agent 派几个。 「重试」这个词全文没有出现过。
这不是漏,是论域——讲上下文工程的文章没义务讲任务级的界。 但搜「AI Agent 能力上限」的人不是在做文献分类,他是 Agent 跑不稳, 想知道自己卡在哪儿。这两层的症状长得很不一样。
Harness 这一层里,还有一半没人画:调用怎么发出去
同一位作者的《图解 AI Agent ①:大模型接上 API,为什么还不算 Agent?》 给了一个好用的坐标系:
AI Agent ≈ 大模型(大脑)+ Harness(控制系统)
大脑负责想,Harness 负责让它接触真实环境、拿到反馈、再想下一步。
下面这一步是我们加的:Harness 这半边还能再切两半。
一半是它给模型看什么——上下文怎么组织、缓存前缀怎么稳住、长了怎么压、 什么时候派子 agent 隔离掉大块中间数据。热帖和图解系列讲的都是这半边。
另一半是它替模型把调用发出去——整个任务最多跑多少步、最多花多少钱、 允不允许派子 agent 派几个。 这半边通常没人画,因为单步跑通时它完全不可见:让 Agent 读个文件改行代码, 这一层是零也正常,只在循环跑长了之后才显形。
不是个别遗漏。刚引的那篇讲工具权限,同系列 《模型到底是怎么读取文件的?》讲文件读取, 同日采到 702 与 648 分——热度都在前半边。
三种失控,每种都要一个自己的上限:步数、花费、扇出
multigrid.ai 那篇 The unbounded agent 把这件事拆得很干净,第一句话就指出了问题:
Every agent framework’s quickstart is an unbounded agent. It has a loop, a tool list and a while-true.
每个框架的快速上手都是一个无界 agent:一个循环、一张工具表、一个 while-true—— 而你的生产代码大概率就是从那个 quickstart 长出来的。
步数:模型说完成了,不等于它真的能停
第一种失控最直观。原文的描述是 agent「retries the same failing tool, or oscillates between two states」——反复重试同一个失败的工具,或者在两个状态之间来回摆。
多数人的处理方式是让模型自己判断该停了。这个做法的问题,原文一句话说透:
The model deciding is a heuristic; a bound is a guarantee.
模型来判断是启发式,设一个界才是保证。而启发式失灵的时候,正好是你最需要它生效的时候—— 循环卡死往往就是因为模型对当前状态的判断出了问题,这时候再问它「你完成了吗」, 是在读一块坏掉的仪表。
花费:步数上限翻倍,输入侧的账单翻四倍
第二种没那么直觉。原文是「Steps are bounded but each step grows」——步数有界, 但每一步在变长:transcript 一直累积,第二十步把第一到十九步整个重发一遍。
由此得到的推论最该被记住:
If turns add roughly constant length, total input tokens across an
n-step run grow withnsquared rather thann. Doubling the step limit from ten to twenty does not double the worst-case bill; it roughly quadruples the input half of it.
如果每轮长度大致恒定,一次 n 步运行的总输入 token 是随 n 的平方增长,不是随 n。
把步数上限从 10 提到 20,最坏情况账单的输入那一半不是翻倍,是大约翻四倍——
注意限定语是「输入那一半」,输出侧不按这个规律走,所以整张账单的涨幅取决于你的
输入输出比。这个数字有直接的操作意义:调 max_steps 的人脑子里默认的是线性。
同一个机制在账单侧有另一套叙述。MachineLearningMastery 的 Identifying Token Costs Hiding in Your Agentic Loop 把它列为第一个成本陷阱:
Passing the full conversation history to every model call means you pay for the same historical tokens repeatedly.
这里是两篇文章咬合的地方:上下文膨胀有两个后果。热帖治的是质量后果—— 上下文长了模型读不好,所以要压缩要隔离;这一层治的是成本与终止性后果—— 上下文长了钱变多,而且是平方地变多,所以要设界。同一个膨胀,两拨人看到两件事。
MLM 的第二个陷阱和这条相乘:无界重试会「drag the full bloated context of the failure along for every retry」——每次重试都拖着那份已膨胀的失败上下文。失败重试是在最贵的 上下文长度上重复付费。重试预算本身怎么设(按近期成功调用的比例,而非每请求固定几次), 本刊 Retry storms: 9K to 100K RPS 写过,这里不重复。
扇出:子 agent 各带一份预算,乘起来才是你的账单
第三种最容易漏。原文:
The agent spawns sub-agents or parallel tool calls, each of which has its own step budget.
Agent 派出子 agent 或并行工具调用,每一个都带着自己那份步数预算。
这一条和前一条是两件事,别合并着算。前一条讲的是一个循环内部步数怎么把账单撑起来;
这一条讲的是循环有几个。一个五步的主循环派出三个各二十步的子 agent,总步数不是 5,
也不是你给主循环设的那个 20——每个子 agent 都带着自己的一份额度出门。原文的说法是
Bounds that are per-agent rather than per-task multiply:按 agent 设的界会翻倍,
按任务设的界才不会。 至于每个子 agent 内部的增长曲线长什么样,取决于它继承了多少
上下文,这个我们没有可引的数据,不猜。
这里有个不太舒服的呼应。热帖推荐用子 Agent 隔离来控上下文——「用隔离代替压缩」, 让大体积中间数据不进主上下文。隔离确实解决了上下文膨胀,但它同时把预算问题从一份 变成了 N 份。两个都正确的做法,在不同维度上互为代价——不是谁写错了, 是这一层没被摆上台面时必然会发生的事。

三种放在一起:
| 失控类型 | 症状长什么样 | 该设的界 | 能靠模型自觉吗 |
|---|---|---|---|
| 步数 | 反复重试同一个失败工具、两个状态之间摆动、任务永远差最后一步 | 单次任务最大步数 | 判断力本身就是坏掉的那个零件 |
| 花费 | 账单与步数不成比例、后半程每步都比前半程贵 | 单次任务花费上限 | 模型看不到累计账单 |
| 扇出 | 主循环看着很短,账单对不上 | 子 agent 并发数与深度上限 | 每个子 agent 只知道自己那份 |
这三种失控还有个照镜子的版本——你自己手动切模型的时候。本刊 《手动在四个模型之间切换?这套规则有三个失效点》 写的是人脑里那套规则的三个失效点:前提数字会过期、额度耗尽后没写降级路径、失效是无声的。 对照着看:人的失效是「忘了」,循环的失控是「不会停」——人会累会分心, 所以规则失效;循环不累不分心,所以它一直转。处方正好相反:人要提醒,循环要界。
能设上限的三个前提:完成条件、外部进展、可回滚的工具
界不是随便设的。同一篇文章给了三个前提,缺一条你就设不下去——或者设下去了不敢用。
一、可判定的完成条件。 原文:「Not ‘the model says it is finished’ but a predicate you can evaluate.」不是「模型说它完成了」,而是一个你能求值的谓词。 落到代码里就是一句话:你写得出一个能跑的判定函数吗。
二、外部可见的进展。 原文:「a step that changes nothing is detectable」—— 如果每一步本该改变 transcript 之外的某样东西,那么什么都没改的那一步就是可检出的。 这是空转检测的全部依据:不看模型说什么,看世界变了没有。
三、幂等可回滚的工具。 原文要求每个工具「leave the world in a state you can resume from or roll back」——留下一个你能续跑或能回滚的世界状态。这条决定了前两条敢不敢用: 工具不可回滚,你就不敢在中途停,于是所有上限都成了摆设——触发上限的后果 是留下一个半完成的烂摊子。
三条里最难的是第一条。多数 Agent 任务根本写不出可判定的完成条件—— 「把这个 bug 修好」「整理一下这批数据」,这些任务的完成态是模糊的。 写不出来,默认就退回去问模型,而这正是三种失控的共同起点。
我的判断是:写不出判定函数的任务,就该老老实实用步数上限硬兜,不要假装它有个终点。 硬兜的代价是偶尔在一个本来能做完的任务上中断,可见、可调、一次几毛钱; 不兜的代价是某一次它不停了,而你在睡觉。
MLM 那篇还列了两条不涉及架构改动、翻一遍代码就能拿到的:工具返回未过滤(原始 API 响应直接塞进上下文,元数据和空字段一个都用不上)与系统提示词重复注入(每次调用都送一份 定义了 20 个工具的系统提示词,哪怕这步只用一个)。
先把这一层画出来,再决定自建还是买
回到标题。上限是两层的串联:上下文层满分、调用层是零,Agent 的表现会是 「有时候很惊艳,跑长了就翻车」——这个症状最误导人的地方在于,它看起来像模型不够强。
有一件成本为零的事现在就能做:去你的 agent 循环里找这三个数字——最大步数、 单次任务花费上限、子 agent 并发与深度上限。找不到就是没设——不是设得不好, 是这一层在你的代码里还不存在。
再往外看一步。单个循环之外,这一层的问题会跑到进程外面去:多个 agent 共享同一个上游配额、 一个供应商静默降级同时影响所有循环、账单账户本身的层级变动。故障域比 API 端点大得多—— 本刊 Tier 3 to Tier 1 mid-flight 写的就是账单层与目录层 怎么在状态页全绿时让服务停下来。到了那个尺度,无论自建还是买,要问的是同一批问题: 上限是配置项还是硬编码,触发之后的默认动作是什么,多个循环共享配额时谁先被限。
热帖那句「上下文的质量才是地板」是对的。这篇想补的只有一句:地板之上还得有个天花板, 而这个天花板没人替你钉。