这是最隐蔽的一条,因为它不报错。
ts
const output = await agent.generate(prompt, {
maxSteps: 8,
stopWhen: ({ steps }) => { /* 你精心写的业务刹车 */ }
});
看着像"两道保险",实际上只有一道。Mastra 1.49.0 的实现里,传了 maxSteps 就会优先把它转换成 stepCountIs(maxSteps) 作为停止条件,你自定义的 stopWhen 不会作为主要停止条件生效。
所以要验证自己的 stopWhen 到底跑没跑,唯一可靠的办法是不传 maxSteps,并且在回调里留下痕迹:
ts
stopWhen: ({ steps }) => {
const toolNames = steps.flatMap((s) =>
(s.toolCalls ?? []).map((c) => String(c.toolName ?? c.name ?? 'unknown')));
const shouldStop = toolNames.includes('find_activities') || steps.length >= 3;
stopWhenDecisions.push({ stepCount: steps.length, toolNames, shouldStop });
return shouldStop;
}
stopWhenDecisions 最后是空数组,说明 stopWhen 一次都没被调用——要么被 maxSteps 顶掉了,要么第一轮 LLM 请求就失败、根本没走到判断。这两种情况在日志上长得一样,不留痕就分不出来。
顺带一个字段兼容坑:不同 provider / 版本给的工具名字段可能是 toolName 也可能是 name,写 toolName ?? name ?? 'unknown' 不是防御性冗余,是必需的。
:::card 定位
stopWhen 是循环刹车,它看的是已完成的 steps,不看答案内容。用它判"答案够不够好"是层次错配——那是 isTaskComplete 和 outputProcessor 的活。
:::