StopCondition(停止条件)

vercel/ai SDK 中控制 Agent 循环何时停止的可替换策略函数,接收全量 steps 执行历史,支持同步和异步求值,多个条件组成数组时为 OR 语义(任意一个满足即停止)。

类型签名

typescript
// stop-condition.ts:L14-L19
type StopCondition<TOOLS, RUNTIME_CONTEXT> = (options: {
  steps: Array<StepResult<TOOLS, RUNTIME_CONTEXT>>;
}) => PromiseLike<boolean> | boolean;

入参是 { steps } 对象(预留扩展槽),接收全量历史步骤数组,返回 true 表示停止循环。支持异步求值(PromiseLike<boolean>),可以写"查数据库判断是否超出预算"等异步停止条件。

三个内置工厂函数

函数语义实现
isStepCount(n)完成 n 步后停止steps.length === n(精确匹配)
isLoopFinished()不干预,依赖自然终止() => false(Null Object 模式)
hasToolCall(...names)最后一步调用了指定工具时停止steps[-1].toolCalls.some(name)

Arrayable 与 OR 语义

stopWhen 参数类型为 Arrayable<StopCondition>,传数组时:

typescript
// generate-text.ts
(await Promise.all(conditions.map(c => c({ steps })))).some(r => r)

⚠️ 重要Promise.all 先并行求值所有条件,再 .some() 聚合——没有短路。即使第一个条件已返回 true,其余异步条件仍继续执行到完成。与 Array.some() 的短路语义不同。

设计原因:确保有副作用的异步条件(如记录日志、更新外部状态)在每轮检测时都被通知到,不被先完成的条件截断。

双终止机制

vercel/ai 的 do-while 循环有两条独立退出路径:

  1. 自然终止:LLM 返回 stop finish reason,无新 tool calls,循环直接退出,stop condition 不会被检测
  2. 显式终止:stop condition 返回 true,强制退出

isStepCount(1)isStepCount(20) 在 LLM 返回 stop 时行为完全相同——自然终止路径不经过 stop condition 检测。

两个默认值差异

调用方默认 stopWhen含义
generateText() 直接调用isStepCount(1)单步完成即停
ToolLoopAgent(默认配置)isStepCount(20)最多 20 步

容易踩坑:以为调用 generateText({ tools: ... }) 会自动多步循环,实际上默认只跑一步。

已知边界情况

isStepCount(0) 是静默的语义错误:do-while 保证至少执行一次,第一步完成后 steps.length === 1isStepCount(0)=== 0 条件永远不满足,循环无法通过 stop condition 层面终止。参数类型未做运行时校验,传 0 不报错但行为完全不符合预期。

与 LangChain 的对比

vercel/ai stopWhenLangChain maxIterations
类型策略函数(可替换)整数(硬截断)
可访问上下文全量 steps 历史无(框架内部计数)
语义感知hasToolCall 可按工具名停止只能按步数截断
组合数组 OR 语义不支持

引用本术语的文章