Skip to content

🔄 循环控制(Loop Control)

停止条件

Agent 循环在以下情况下停止:

  • 返回的完成原因不是 tool-calls
  • 调用的工具没有 execute 函数
  • 工具调用需要审批
  • 满足停止条件

默认情况下,Agent 在 20 步后停止(stepCountIs(20))。

内置停止条件

typescript
import { ToolLoopAgent, stepCountIs } from 'ai';

const agent = new ToolLoopAgent({
  model: "anthropic/claude-sonnet-4.5",
  tools: { /* your tools */ },
  stopWhen: stepCountIs(20),
});

组合多个条件

typescript
import { ToolLoopAgent, stepCountIs, hasToolCall } from 'ai';

const agent = new ToolLoopAgent({
  model: "anthropic/claude-sonnet-4.5",
  tools: { /* your tools */ },
  stopWhen: [
    stepCountIs(20),           // 最多 20 步
    hasToolCall('someTool'),   // 调用特定工具后停止
  ],
});

自定义停止条件

typescript
import { ToolLoopAgent, StopCondition, ToolSet } from 'ai';

const tools = { /* your tools */ } satisfies ToolSet;

const hasAnswer: StopCondition<typeof tools> = ({ steps }) => {
  return steps.some(step => step.text?.includes('ANSWER:')) ?? false;
};

const agent = new ToolLoopAgent({
  model: "anthropic/claude-sonnet-4.5",
  tools,
  stopWhen: hasAnswer,
});

基于成本的停止条件

typescript
const budgetExceeded: StopCondition<typeof tools> = ({ steps }) => {
  const totalUsage = steps.reduce(
    (acc, step) => ({
      inputTokens: acc.inputTokens + (step.usage?.inputTokens ?? 0),
      outputTokens: acc.outputTokens + (step.usage?.outputTokens ?? 0),
    }),
    { inputTokens: 0, outputTokens: 0 },
  );

  const costEstimate =
    (totalUsage.inputTokens * 0.01 + totalUsage.outputTokens * 0.03) / 1000;
  return costEstimate > 0.5; // 超过 $0.50 时停止
};

prepareStep:每步执行前的钩子

prepareStep 回调在循环的每一步之前运行,可用于动态修改设置。

动态模型选择

typescript
const agent = new ToolLoopAgent({
  model: 'openai/gpt-4o-mini',
  tools: { /* your tools */ },
  prepareStep: async ({ stepNumber, messages }) => {
    if (stepNumber > 2 && messages.length > 10) {
      return {
        model: "anthropic/claude-sonnet-4.5",
      };
    }
    return {};
  },
});

上下文管理

typescript
const agent = new ToolLoopAgent({
  model: "anthropic/claude-sonnet-4.5",
  tools: { /* your tools */ },
  prepareStep: async ({ messages }) => {
    if (messages.length > 20) {
      return {
        messages: [
          messages[0],            // 保留系统指令
          ...messages.slice(-10), // 保留最后 10 条消息
        ],
      };
    }
    return {};
  },
});

工具阶段控制

typescript
const agent = new ToolLoopAgent({
  model: "anthropic/claude-sonnet-4.5",
  tools: {
    search: searchTool,
    analyze: analyzeTool,
    summarize: summarizeTool,
  },
  prepareStep: async ({ stepNumber }) => {
    // 搜索阶段 (步骤 0-2)
    if (stepNumber <= 2) {
      return {
        activeTools: ['search'],
        toolChoice: 'required',
      };
    }

    // 分析阶段 (步骤 3-5)
    if (stepNumber <= 5) {
      return {
        activeTools: ['analyze'],
      };
    }

    // 总结阶段 (步骤 6+)
    return {
      activeTools: ['summarize'],
      toolChoice: 'required',
    };
  },
});

强制工具调用模式

结合 toolChoice: 'required' 和无 execute 函数的 done 工具:

typescript
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';

const agent = new ToolLoopAgent({
  model: "anthropic/claude-sonnet-4.5",
  tools: {
    search: searchTool,
    analyze: analyzeTool,
    done: tool({
      description: 'Signal that you have finished your work',
      inputSchema: z.object({
        answer: z.string().describe('The final answer'),
      }),
      // 无 execute 函数 - 调用时停止 Agent
    }),
  },
  toolChoice: 'required',
});

const result = await agent.generate({
  prompt: 'Research and analyze this topic, then provide your answer.',
});

const toolCall = result.staticToolCalls[0];
if (toolCall?.toolName === 'done') {
  console.log(toolCall.input.answer);
}

手动循环控制

对于需要完全控制的场景:

typescript
import { generateText, ModelMessage } from 'ai';

const messages: ModelMessage[] = [{ role: 'user', content: '...' }];

let step = 0;
const maxSteps = 10;

while (step < maxSteps) {
  const result = await generateText({
    model: "anthropic/claude-sonnet-4.5",
    messages,
    tools: { /* your tools */ },
  });

  messages.push(...result.response.messages);

  if (result.text) {
    break; // 模型生成文本时停止
  }

  step++;
}

前端面试知识库