首页 / AI工具实战 / Claude API Agent框架实战:构建多步骤自动化工作流 2 次阅读
Claude API Agent框架实战:构建多步骤自动化工作流
AI工具实战 中级难度

Claude API Agent 框架实战
构建多步骤自动化工作流

本教程带你从零开始,用 TypeScript 和 Claude Agent SDK 搭建一套多智能体协作系统:从单次 API 调用,到带工具的 Agent 循环,再到多个子 Agent 并行协作,每一步都配有完整可运行代码。

📅 2026年2月28日 ⏱ 预计阅读 20 分钟 🛠 TypeScript / Node.js 📦 @anthropic-ai/sdk · @anthropic-ai/claude-agent-sdk

一、多智能体系统架构概览

多智能体系统架构图

在传统的单次 LLM 调用模式中,一个请求只能返回一个回答。而 多智能体架构(Multi-Agent Architecture)打破了这一限制:通过一个「主 Agent(Lead Agent)」统筹规划,将复杂任务拆解为多个子任务,分发给各专精的「子 Agent(Subagent)」并行执行,最终汇总结果。

这套模式特别适合以下场景:

  • 需要同时搜索多个信息源的研究任务
  • 代码审查 + 测试 + 文档同步进行的 CI/CD 流水线
  • 涉及多步骤决策、中间结果会影响后续步骤的开放式自动化

Anthropic 提供了两个层次的工具来实现这套架构:

工具层次适用场景核心包
Claude API + Tool Use 自定义工具、精细控制循环逻辑、需要人工审批节点 @anthropic-ai/sdk
Claude Agent SDK 内置文件/网络/终端工具、快速搭建 Agent、MCP 集成 @anthropic-ai/claude-agent-sdk
本教程路线 先用 Claude API 手写完整的 Agent 循环(加深理解),再用 Claude Agent SDK 快速搭建多 Agent 编排系统(提升效率)。两套方式都会覆盖。

二、环境准备

环境配置流程图

我们需要安装两个包,并正确配置 API Key。

1

初始化项目并安装依赖

创建一个新的 TypeScript 项目,安装 Anthropic SDK(用于 API 直调)和 Claude Agent SDK(用于高层 Agent 编排):

mkdir claude-agent-demo && cd claude-agent-demo
npm init -y
npm install @anthropic-ai/sdk @anthropic-ai/claude-agent-sdk zod
npm install -D typescript @types/node tsx
npx tsc --init --target es2022 --module nodenext --moduleResolution nodenext
2

配置 API Key

前往 console.anthropic.com 获取 API Key,然后写入环境变量文件。永远不要把 Key 硬编码到代码里:

# .env 文件
ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxxxxxxxxxxxxx
// 在代码里这样加载
import 'dotenv/config'; // 或直接在 shell 里 export ANTHROPIC_API_KEY=...
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();
// SDK 自动读取 process.env.ANTHROPIC_API_KEY
3

验证安装

运行一个最简单的 Hello World 来确认一切正常:

// hello.ts
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

const response = await client.messages.create({
  model: 'claude-opus-4-6',
  max_tokens: 256,
  messages: [{ role: 'user', content: '用一句话介绍你自己' }],
});

console.log(response.content[0].text);
npx tsx hello.ts
# 输出类似:我是 Claude,Anthropic 开发的 AI 助手...
Node.js 版本要求 Claude Agent SDK 需要 Node.js 18+。运行 node --version 检查,如果低于 18 请升级。Windows 用户建议通过 WSL2 运行。

三、手写 Agent 循环:理解 Tool Use 核心机制

Tool Use 工作循环图

在使用高层 SDK 之前,先理解底层的 Agentic Loop(智能体循环)至关重要。这个循环的核心逻辑是:

  1. 向 Claude 发送消息 + 可用工具列表
  2. Claude 返回 tool_use 块,要求调用某个工具
  3. 你的代码执行该工具,获取结果
  4. 将工具结果作为 tool_result 消息发回给 Claude
  5. 重复步骤 1-4,直到 Claude 返回 stop_reason: "end_turn"

下面是一个完整的例子:给 Claude 配备「天气查询」和「城市搜索」两个工具,让它自主完成一个多步骤研究任务:

TypeScript
// agent-loop.ts
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

// ① 定义工具
const tools: Anthropic.Tool[] = [
  {
    name: 'get_weather',
    description: '获取指定城市的当前天气。返回温度(摄氏度)和天气状况。',
    input_schema: {
      type: 'object',
      properties: {
        city: { type: 'string', description: '城市名称,如"北京"或"上海"' },
      },
      required: ['city'],
    },
  },
  {
    name: 'search_attractions',
    description: '搜索指定城市的热门景点列表。',
    input_schema: {
      type: 'object',
      properties: {
        city: { type: 'string', description: '城市名称' },
        limit: { type: 'number', description: '返回结果数量,默认3' },
      },
      required: ['city'],
    },
  },
];

// ② 模拟工具执行函数
async function executeTool(
  name: string,
  input: Record
): Promise {
  if (name === 'get_weather') {
    const city = input.city as string;
    // 实际项目中调用真实天气 API
    return JSON.stringify({ city, temp: 18, condition: '晴天', humidity: '45%' });
  }
  if (name === 'search_attractions') {
    const city = input.city as string;
    const limit = (input.limit as number) || 3;
    const data: Record = {
      '北京': ['故宫', '长城', '颐和园', '天坛'],
      '上海': ['外滩', '豫园', '东方明珠', '南京路'],
    };
    return JSON.stringify((data[city] ?? ['景点A', '景点B']).slice(0, limit));
  }
  return '工具不存在';
}

// ③ 主 Agent 循环
async function runAgent(userQuery: string): Promise {
  let messages: Anthropic.MessageParam[] = [
    { role: 'user', content: userQuery },
  ];

  while (true) {
    const response = await client.messages.create({
      model: 'claude-opus-4-6',
      max_tokens: 4096,
      thinking: { type: 'adaptive' },
      tools,
      messages,
    });

    console.log(`[stop_reason: ${response.stop_reason}]`);

    // 任务完成,提取最终文本回答
    if (response.stop_reason === 'end_turn') {
      const textBlock = response.content.find((b) => b.type === 'text');
      return (textBlock as Anthropic.TextBlock)?.text ?? '';
    }

    // 需要调用工具
    if (response.stop_reason === 'tool_use') {
      // 将 Claude 的回复追加到对话历史
      messages.push({ role: 'assistant', content: response.content });

      // 收集所有工具调用结果
      const toolResults: Anthropic.ToolResultBlockParam[] = [];

      for (const block of response.content) {
        if (block.type === 'tool_use') {
          console.log(`  → 调用工具: ${block.name}`, block.input);
          const result = await executeTool(
            block.name,
            block.input as Record
          );
          console.log(`  ← 工具结果: ${result}`);

          toolResults.push({
            type: 'tool_result',
            tool_use_id: block.id,
            content: result,
          });
        }
      }

      // 将工具结果发回给 Claude
      messages.push({ role: 'user', content: toolResults });
    }
  }
}

// ④ 运行测试
const answer = await runAgent(
  '帮我对比北京和上海的今日天气,并各推荐3个必去景点'
);
console.log('\n=== 最终回答 ===\n', answer);
关键点:保留完整 content 每次把 response.content(整个数组,不只是文本)追加到 messages,是因为 tool_use 块必须保留在历史中,Claude 才能正确匹配后续的 tool_result

四、用 Tool Runner 简化工具循环

Tool Runner 与手动循环对比图

手写循环虽然灵活,但代码量较多。Anthropic SDK 提供了 Tool Runner(Beta) 来自动处理整个循环,只需定义工具和 run 函数即可:

TypeScript
// tool-runner.ts
import Anthropic from '@anthropic-ai/sdk';
import { betaZodTool } from '@anthropic-ai/sdk/helpers/beta/zod';
import { z } from 'zod';

const client = new Anthropic();

// 用 Zod 定义工具——类型安全,不需要手写 JSON Schema
const getWeather = betaZodTool({
  name: 'get_weather',
  description: '获取城市天气,返回温度和天气状况',
  inputSchema: z.object({
    city: z.string().describe('城市名称'),
    unit: z.enum(['celsius', 'fahrenheit']).optional().default('celsius'),
  }),
  run: async ({ city, unit }) => {
    // 这里接入真实天气 API
    return `${city}:18°C,晴天(${unit})`;
  },
});

const searchAttractions = betaZodTool({
  name: 'search_attractions',
  description: '搜索城市热门景点',
  inputSchema: z.object({
    city: z.string(),
    limit: z.number().min(1).max(10).default(3),
  }),
  run: async ({ city, limit }) => {
    const db: Record = {
      北京: ['故宫', '长城', '颐和园', '天坛', '798艺术区'],
      上海: ['外滩', '豫园', '东方明珠', '南京路', '新天地'],
    };
    const spots = (db[city] ?? ['景点A', '景点B']).slice(0, limit);
    return JSON.stringify(spots);
  },
});

// Tool Runner 自动处理循环,返回最终 Message
const finalMessage = await client.beta.messages.toolRunner({
  model: 'claude-opus-4-6',
  max_tokens: 4096,
  tools: [getWeather, searchAttractions],
  messages: [
    {
      role: 'user',
      content: '对比北京和上海的今日天气,各推荐3个必去景点,最后给出旅行建议',
    },
  ],
});

// 提取文本回答
const text = finalMessage.content
  .filter((b) => b.type === 'text')
  .map((b) => (b as Anthropic.TextBlock).text)
  .join('');

console.log(text);

Tool Runner 的优势对比:

特性手动循环Tool Runner
代码量约 50 行约 20 行
类型安全需手写 JSON SchemaZod 自动生成
循环控制完全自定义自动,不可干预
人工审批支持不支持
流式输出支持(手动 stream)支持(stream: true)

五、多 Agent 编排:主 Agent + 子 Agent 并行

多Agent并行编排架构图

真正的多步骤自动化工作流往往需要多个专精 Agent 协同工作。Claude Agent SDK 的 query() + agents 选项,让你能够轻松定义子 Agent 并让主 Agent 在需要时召唤它们。

场景:自动竞品分析报告

我们构建一个系统:主 Agent 负责规划和整合,派出「研究员 Agent」和「写作 Agent」分工完成任务:

TypeScript
// multi-agent.ts
import { query } from '@anthropic-ai/claude-agent-sdk';

const result: string[] = [];

for await (const message of query({
  prompt: `
    请完成以下竞品分析任务:
    1. 使用 researcher 子Agent,分别调研 Notion 和 Obsidian 的核心功能
    2. 使用 writer 子Agent,将调研结果整理成一份结构化的对比报告
    3. 给出最终的选型建议
  `,
  options: {
    model: 'claude-opus-4-6',
    maxTurns: 20,
    allowedTools: ['Read', 'Glob', 'WebSearch', 'Agent'],
    systemPrompt: `
      你是一位高级产品经理。在处理复杂任务时,
      善用专精子Agent来分工提升效率。
    `,

    // 定义可调用的子 Agent
    agents: {
      researcher: {
        description:
          '专业信息研究员,擅长收集和整理产品信息、技术文档和用户评价。',
        prompt: `
          你是一位细致的产品研究员。
          为每个产品收集:核心功能列表、定价策略、目标用户、优缺点。
          以结构化 JSON 格式返回结果。
        `,
        tools: ['WebSearch', 'Read'],
      },

      writer: {
        description:
          '技术写作专家,能将研究数据转化为清晰易读的分析报告。',
        prompt: `
          你是一位经验丰富的技术写作者。
          接收 JSON 格式的研究数据,输出完整的 Markdown 格式对比报告。
          报告需包含:执行摘要、功能对比表、适用场景、选型建议。
        `,
        tools: ['Read'],
      },
    },
  },
})) {
  if ('result' in message) {
    result.push(message.result);
  } else if (message.type === 'assistant') {
    // 实时打印 Agent 思考过程(可选)
    const text = message.message.content
      .filter((b) => b.type === 'text')
      .map((b) => b.text)
      .join('');
    if (text) process.stdout.write(text);
  }
}

console.log('\n=== 最终报告 ===\n', result.join('\n'));

从外部并行启动多个独立 Agent

如果任务之间完全独立,可以用 Promise.all 并行启动多个 query 调用,大幅缩短总耗时:

TypeScript
// parallel-agents.ts
import { query } from '@anthropic-ai/claude-agent-sdk';

async function runResearchAgent(topic: string): Promise {
  const results: string[] = [];
  for await (const msg of query({
    prompt: `深度调研:${topic}。输出结构化摘要(500字以内)`,
    options: {
      model: 'claude-opus-4-6',
      allowedTools: ['WebSearch'],
      maxTurns: 8,
    },
  })) {
    if ('result' in msg) results.push(msg.result);
  }
  return results.join('');
}

// 并行调研三个主题
const topics = [
  'Claude API 的最新功能和定价',
  'OpenAI API 的最新功能和定价',
  'Google Gemini API 的最新功能和定价',
];

console.log('并行启动 3 个研究 Agent...');
const [claudeReport, openaiReport, geminiReport] = await Promise.all(
  topics.map(runResearchAgent)
);

console.log('全部完成,开始整合...');
// 再调用一个整合 Agent 汇总结果
for await (const msg of query({
  prompt: `
    以下是三个 AI API 的调研报告,请整合成一份对比分析:

    === Claude API ===
    ${claudeReport}

    === OpenAI API ===
    ${openaiReport}

    === Google Gemini API ===
    ${geminiReport}

    请输出:功能对比表、价格对比、适用场景建议。
  `,
  options: { model: 'claude-opus-4-6', maxTurns: 5 },
})) {
  if ('result' in msg) console.log(msg.result);
}
并行 vs 串行的选择原则 如果子任务之间没有数据依赖(如独立调研不同主题),用 Promise.all 并行。如果后一个任务依赖前一个的结果(如先研究后写作),串行或用 subAgent 编排。

六、会话保持与长对话上下文管理

会话恢复与上下文管理信息图

多步骤工作流往往需要跨多轮保持状态。Claude Agent SDK 提供了 Session Resumption(会话恢复),让你能在 Agent 暂停后以完整上下文继续工作:

TypeScript
// session-resume.ts
import { query } from '@anthropic-ai/claude-agent-sdk';

let sessionId: string | undefined;

// 第一轮:让 Agent 读取并分析代码库
console.log('=== 第一轮:分析阶段 ===');
for await (const message of query({
  prompt: '扫描当前目录下所有 TypeScript 文件,找出潜在的类型错误和不规范写法',
  options: {
    cwd: process.cwd(),
    allowedTools: ['Read', 'Glob', 'Grep'],
    model: 'claude-opus-4-6',
  },
})) {
  if (message.type === 'system' && message.subtype === 'init') {
    sessionId = message.session_id; // 记录 session ID
    console.log('Session ID:', sessionId);
  }
  if ('result' in message) {
    console.log('分析结果:', message.result);
  }
}

// 第二轮:基于第一轮的发现,继续修复(携带完整上下文)
console.log('\n=== 第二轮:修复阶段 ===');
for await (const message of query({
  prompt: '现在逐个修复你找到的问题,每处修改前先说明原因',
  options: {
    resume: sessionId, // 恢复之前的 session,保留所有上下文
    allowedTools: ['Read', 'Edit', 'Write'],
    permissionMode: 'acceptEdits',
  },
})) {
  if ('result' in message) {
    console.log('修复完成:', message.result);
  }
}

长对话的 Compaction(压缩)

当对话历史接近 200K token 上限时,可以开启 Compaction 自动压缩(仅 Opus 4.6,Beta 功能):

TypeScript
// long-conversation.ts
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();
const messages: Anthropic.Beta.BetaMessageParam[] = [];

async function chat(userMessage: string): Promise {
  messages.push({ role: 'user', content: userMessage });

  const response = await client.beta.messages.create({
    betas: ['compact-2026-01-12'],
    model: 'claude-opus-4-6',
    max_tokens: 4096,
    messages,
    context_management: {
      edits: [{ type: 'compact_20260112' }],
    },
  });

  // 关键:必须把完整 content(包括压缩块)追加回 messages
  messages.push({ role: 'assistant', content: response.content });

  return response.content
    .filter((b) => b.type === 'text')
    .map((b) => (b as Anthropic.TextBlock).text)
    .join('');
}

// 可以持续对话而不担心超过上下文窗口
await chat('分析这个 50KB 的代码文件...');
await chat('针对刚才发现的 Bug,设计修复方案');
await chat('把修复方案转成具体的 PR 描述');

七、错误处理与生产最佳实践

错误处理与最佳实践对比图

将 Agent 系统从 Demo 推向生产环境,需要做好几个关键点:

1. 类型化异常处理

始终使用 SDK 提供的类型化异常类,不要用字符串匹配:

TypeScript
// error-handling.ts
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

async function safeAgentCall(prompt: string): Promise {
  try {
    const response = await client.messages.create({
      model: 'claude-opus-4-6',
      max_tokens: 2048,
      messages: [{ role: 'user', content: prompt }],
    });
    return (response.content[0] as Anthropic.TextBlock).text;
  } catch (error) {
    if (error instanceof Anthropic.RateLimitError) {
      // 429:等待后重试(SDK 会自动重试,但这里演示手动处理)
      const retryAfter = (error as any).headers?.['retry-after'] ?? 30;
      console.warn(`触发速率限制,${retryAfter}秒后重试`);
      await new Promise((r) => setTimeout(r, Number(retryAfter) * 1000));
      return safeAgentCall(prompt); // 递归重试
    } else if (error instanceof Anthropic.AuthenticationError) {
      console.error('API Key 无效,请检查 ANTHROPIC_API_KEY 环境变量');
      process.exit(1);
    } else if (error instanceof Anthropic.BadRequestError) {
      console.error('请求格式错误:', error.message);
      return null;
    } else if (error instanceof Anthropic.APIError) {
      console.error(`API 错误 ${error.status}:`, error.message);
      return null;
    }
    throw error; // 未知错误,向上抛出
  }
}

2. 设置 maxTurns 防止无限循环

TypeScript
for await (const msg of query({
  prompt: '...',
  options: {
    maxTurns: 15,        // 防止 Agent 陷入死循环
    maxBudgetUsd: 0.5,   // 最多消耗 $0.5(生产环境必备)
    model: 'claude-opus-4-6',
  },
})) { ... }

3. 使用 Hooks 做审计日志

TypeScript
import { query, HookCallback } from '@anthropic-ai/claude-agent-sdk';
import { appendFileSync } from 'fs';

// 每次工具调用后记录审计日志
const auditHook: HookCallback = async (input) => {
  const record = {
    ts: new Date().toISOString(),
    tool: (input as any).tool_name,
    file: (input as any).tool_input?.file_path,
  };
  appendFileSync('./audit.log', JSON.stringify(record) + '\n');
  return {};
};

for await (const msg of query({
  prompt: '重构 src/ 目录下的所有文件',
  options: {
    cwd: '/your/project',
    allowedTools: ['Read', 'Edit', 'Write'],
    permissionMode: 'acceptEdits',
    hooks: {
      PostToolUse: [{ matcher: 'Edit|Write', hooks: [auditHook] }],
    },
  },
})) { ... }

4. 模型选择策略

不同任务用不同模型,节省成本:

TypeScript
function selectModel(task: 'simple' | 'medium' | 'complex'): string {
  const map = {
    simple: 'claude-haiku-4-5',    // 分类、提取:速度快、成本低
    medium: 'claude-sonnet-4-6',   // 写作、代码:均衡
    complex: 'claude-opus-4-6',    // 推理、Agent编排:最强
  };
  return map[task];
}

// 主 Agent 用 Opus,子 Agent 可以用 Sonnet
const agents = {
  researcher: {
    // 研究子任务用 Sonnet 省成本
    prompt: '...',
    model: 'claude-sonnet-4-6',
    tools: ['WebSearch'],
  },
};

常见问题解答

问题解答
Claude API 和 Claude Agent SDK 应该选哪个? 如果需要内置的文件读写、网络搜索、终端执行等工具,选 Agent SDK;如果需要自定义工具、精细控制循环或人工审批,选 Claude API + Tool Use。
budget_tokens 参数还能用吗? 在 Claude Opus 4.6 和 Sonnet 4.6 上已废弃。请改用 thinking: {type: "adaptive"},配合 output_config: {effort: "high"} 来控制推理深度。
如何避免 Agent 陷入死循环? 设置 maxTurns 参数(建议 10-20),以及 maxBudgetUsd 限制费用上限。手写循环时也要设置最大迭代次数。
多个子 Agent 并行会不会触发速率限制? 可能会。建议监控 x-ratelimit-remaining-requests 响应头,或使用 Batch API 处理非实时任务(享受 50% 折扣)。
工具调用结果很长,会撑爆 context window 吗? 注意控制工具返回结果的大小。对于大文件,只返回关键摘要而不是全文。超长对话可以开启 Compaction(Opus 4.6 Beta)自动压缩。
如何在 Agent 循环中实现流式输出? client.messages.stream() 替代 client.messages.create(),订阅 .on("text", delta => ...) 事件,循环结束时调用 stream.finalMessage() 获取完整结果。

本教程核心要点

  • 多智能体架构的核心是「主 Agent 规划 + 子 Agent 分工 + 并行执行」
  • 手写 Agentic Loop:每轮把完整 response.content 追加到 messages,直到 stop_reason === "end_turn"
  • Tool Runner 用 Zod 定义工具,自动处理循环,代码量减少一半
  • Claude Agent SDK 的 agents 选项支持定义可被主 Agent 召唤的子 Agent
  • 独立任务用 Promise.all 并行启动多个 query(),有依赖时串行
  • 生产环境必须:设置 maxTurnsmaxBudgetUsd、类型化异常处理、审计 Hooks
  • Opus 4.6 用 thinking: {type: "adaptive"},不要用已废弃的 budget_tokens
选择栏目
今日简报 播客电台 AI 实战教程 关于我
栏目
全球AI日报国内AI日报全球金融日报国内金融日报全球大新闻日报国内大新闻日报Claude Code 玩法日报OpenClaw 动态日报GitHub 热门项目日报AI工具实战AI应用开发编程实战工作流自动化AI原理图解AI Agent开发
我的收藏
播客版
0:00
--:--