Skip to content

stopWhen 参数用于控制 OpenRouter callModel 的多轮 tool 执行循环何时停止,是防止 agent 失控、控制成本的关键机制。提供 stepCountIsmaxCosthasToolCallmaxTokensUsed 等内置条件,支持数组组合(任一满足即停),也支持自定义函数实现基于内容、质量、时间的复杂停止逻辑。

基本用法

typescript
import { OpenRouter, stepCountIs } from '@openrouter/agent';

const openrouter = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY,
});

const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Research this topic thoroughly',
  tools: [searchTool, analysisTool],
  stopWhen: stepCountIs(5), // 最多执行 5 步
});

内置停止条件

stepCountIs(n) — 限制步骤数

typescript
import { stepCountIs } from '@openrouter/agent';

const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Analyze this data',
  tools: [analysisTool],
  stopWhen: stepCountIs(10), // 执行 10 步后停止
});

hasToolCall(name) — 等待特定 tool 被调用

typescript
import { hasToolCall } from '@openrouter/agent';

const finishTool = tool({
  name: 'finish',
  description: 'Call this when the task is complete',
  inputSchema: z.object({ summary: z.string() }),
  execute: async (params) => ({ done: true }),
});

const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Research until you have enough information, then call finish',
  tools: [searchTool, finishTool],
  stopWhen: hasToolCall('finish'), // 调用 finish tool 时停止
});

maxTokensUsed(n) — 限制 token 消耗

typescript
import { maxTokensUsed } from '@openrouter/agent';

const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Generate content',
  tools: [writingTool],
  stopWhen: maxTokensUsed(5000), // 消耗 5000 token 后停止
});

maxCost(amount) — 限制费用

typescript
import { maxCost } from '@openrouter/agent';

const result = openrouter.callModel({
  model: 'openai/gpt-5.2',
  input: 'Perform extensive analysis',
  tools: [analysisTool],
  stopWhen: maxCost(1.00), // 花费 $1.00 后停止
});

finishReasonIs(reason) — 指定完成原因

typescript
import { finishReasonIs } from '@openrouter/agent';

const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Complete this task',
  tools: [taskTool],
  stopWhen: finishReasonIs('stop'), // 模型自然完成时停止
});

组合条件

传入数组,任一条件满足即停止:

typescript
import { stepCountIs, hasToolCall, maxCost } from '@openrouter/agent';

const result = openrouter.callModel({
  model: 'openai/gpt-5.2',
  input: 'Research thoroughly but stay within budget',
  tools: [searchTool, finishTool],
  stopWhen: [
    stepCountIs(10),       // 最多 10 步
    maxCost(0.50),         // 最多 $0.50
    hasToolCall('finish'), // 或者 finish 被调用
  ],
});

自定义停止条件

用函数实现任意停止逻辑:

typescript
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Process data',
  tools: [processTool],
  stopWhen: ({ steps }) => {
    if (steps.length >= 20) return true;

    // 最后一步没有 tool 调用时停止
    const lastStep = steps[steps.length - 1];
    if (lastStep && !lastStep.toolCalls?.length) return true;

    return false;
  },
});

StopConditionContext 参数

自定义函数接收:

属性类型说明
stepsStepResult[]已完成的所有步骤,含结果和用量

StepResult 结构

typescript
interface StepResult {
  response: Response;
  toolCalls?: ParsedToolCall[];
  toolResults?: ToolExecutionResult[];
  tokens: {
    input: number;
    output: number;
    cached: number;
  };
  cost: number;
}

高级模式

基于时间停止

typescript
const startTime = Date.now();
const maxDuration = 30000; // 30 秒

const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Work on this task',
  tools: [workTool],
  stopWhen: () => Date.now() - startTime > maxDuration,
});

基于内容停止

typescript
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Search until you find the answer',
  tools: [searchTool],
  stopWhen: ({ steps }) => {
    const lastStep = steps[steps.length - 1];
    if (!lastStep) return false;

    const content = JSON.stringify(lastStep.response);
    return content.includes('ANSWER FOUND') || content.includes('TASK COMPLETE');
  },
});

基于质量停止

typescript
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Improve this text until it scores above 0.9',
  tools: [improverTool, scorerTool],
  stopWhen: ({ steps }) => {
    for (const step of steps) {
      for (const result of step.toolResults ?? []) {
        if (result.toolName === 'scorer' && result.result?.score > 0.9) {
          return true;
        }
      }
    }
    return false;
  },
});

从 maxToolRounds 迁移

typescript
// 旧写法
const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Hello',
  tools: [myTool],
  maxToolRounds: 5,
});

// 新写法
import { stepCountIs } from '@openrouter/agent';

const result = openrouter.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Hello',
  tools: [myTool],
  stopWhen: stepCountIs(5),
});

不设置 stopWhen 时,默认行为等同于 stepCountIs(5)

最佳实践

始终设置硬限制,防止失控执行:

typescript
stopWhen: [
  stepCountIs(100), // 步骤上限
  maxCost(10.00),   // 费用上限
  customCondition,  // 业务逻辑
],

常见问题

Q: stopWhen 数组中的条件是 AND 还是 OR 关系?

A: OR 关系。任一条件满足即停止。如果需要 AND,请使用自定义函数:stopWhen: ({ steps }) => conditionA(steps) && conditionB(steps)

Q: 不设 stopWhen 会发生什么?

A: 默认等同于 stepCountIs(5),执行 5 步后停止。建议在生产环境显式设置,同时加上费用上限。

Q: 停止后还能获取到已完成步骤的结果吗?

A: 可以。通过 getResponse() 获取完整响应,其中包含所有已完成步骤的 tool 调用结果和 token 用量。