跳到正文

CrawlForge Team工程团队

阅读时长 9 分钟

如何将 CrawlForge 与 Anthropic Claude API 结合使用

Anthropic 的 Claude API 支持原生工具使用—— 你用 JSON schema 定义工具,Claude 在对话过程中自行决定何时调用它们。CrawlForge 的 31 个 web scraping 工具天然契合:它们让 Claude 具备搜索网页、提取内容、scraping 结构化数据和进行深度研究的能力,全部通过标准的 tool_use API 实现。

本指南将带你完成为 Claude API 定义 CrawlForge 工具、处理工具使用响应,以及构建一个生产级研究助手。

目录

前置条件

Bash
npm install @anthropic-ai/sdk dotenv
Bash
# .env
ANTHROPIC_API_KEY=sk-ant-xxxxx
CRAWLFORGE_API_KEY=cf_live_xxxxx

在 crawlforge.dev/signup 获取你的 CrawlForge API key —— 含 1,000 个免费 credits。要获取 Claude API 访问权限,请访问 console.anthropic.com 并参照 API 入门指南。

Claude 工具使用如何与 CrawlForge 协作

Claude 的工具使用遵循一个请求-响应循环:

  1. 你发送一条带工具定义和用户提示的消息
  2. Claude 以文本或一个 tool_use 内容块作出响应
  3. 你执行该工具(调用 CrawlForge API)并返回结果
  4. Claude 整合结果并继续其响应
You -> Claude: "What's on the Hacker News front page?" Claude -> You: tool_use { name: "extract_content", input: { url: "https://news.ycombinator.com" } } You -> CrawlForge: POST /api/v1/tools/extract_content { url: "..." } CrawlForge -> You: { content: "..." } You -> Claude: tool_result { content: "..." } Claude -> You: "Here are the top stories on Hacker News right now: ..."

步骤 1:定义 CrawlForge 工具 schema

定义 Claude 可以使用的工具。每个工具都需要 name、description 和 input_schema(JSON Schema 格式):

Typescript
// lib/tool-definitions.ts
import Anthropic from '@anthropic-ai/sdk';

export const crawlforgeTools: Anthropic.Tool[] = [
  {
    name: 'search_web',
    description: 'Search Google and return top results. Use when you need to find web pages about a topic. Returns titles, URLs, and snippets. Costs 5 credits.',
    input_schema: {
      type: 'object' as const,
      properties: {
        query: {
          type: 'string',
          description: 'The search query',
        },
        limit: {
          type: 'number',
          description: 'Maximum number of results (default: 5)',
        },
      },
      required: ['query'],
    },
  },
  {
    name: 'extract_content',
    description: 'Extract the main readable content from a web page. Returns clean text without navigation, ads, or boilerplate. Use when you need to read a specific URL. Costs 2 credits.',
    input_schema: {
      type: 'object' as const,
      properties: {
        url: {
          type: 'string',
          description: 'The full URL to extract content from',
        },
      },
      required: ['url'],
    },
  },
  {
    name: 'scrape_structured',
    description: 'Extract specific data from a page using CSS selectors. Use when you need structured data like prices, names, or lists. Costs 2 credits.',
    input_schema: {
      type: 'object' as const,
      properties: {
        url: {
          type: 'string',
          description: 'The URL to scrape',
        },
        selectors: {
          type: 'object',
          description: 'Map of field names to CSS selectors',
          additionalProperties: { type: 'string' },
        },
      },
      required: ['url', 'selectors'],
    },
  },
  {
    name: 'fetch_url',
    description: 'Fetch raw HTML from a URL. Cheapest option at 1 credit. Use for APIs or when you need the raw HTML.',
    input_schema: {
      type: 'object' as const,
      properties: {
        url: {
          type: 'string',
          description: 'The URL to fetch',
        },
      },
      required: ['url'],
    },
  },
  {
    name: 'extract_metadata',
    description: 'Get page metadata including title, description, Open Graph tags, and other meta information. Costs 1 credit.',
    input_schema: {
      type: 'object' as const,
      properties: {
        url: {
          type: 'string',
          description: 'The URL to extract metadata from',
        },
      },
      required: ['url'],
    },
  },
];

步骤 2:处理工具使用循环

核心模式:向 Claude 发送消息,检查它是否想使用某个工具,通过 CrawlForge 执行该工具,然后返回结果。

Typescript
// lib/agent.ts
import Anthropic from '@anthropic-ai/sdk';
import { crawlforgeTools } from './tool-definitions';

const client = new Anthropic();
const CRAWLFORGE_BASE = 'https://crawlforge.dev/api/v1/tools';

// Execute a CrawlForge tool
async function executeTool(
  name: string,
  input: Record<string, unknown>
): Promise<string> {
  const response = await fetch(`${CRAWLFORGE_BASE}/${name}`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.CRAWLFORGE_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(input),
  });

  if (!response.ok) {
    return JSON.stringify({ error: `HTTP ${response.status}: ${response.statusText}` });
  }

  const data = await response.json();
  return JSON.stringify(data);
}

// Run Claude with CrawlForge tools
export async function askClaude(prompt: string): Promise<string> {
  const messages: Anthropic.MessageParam[] = [
    { role: 'user', content: prompt },
  ];

  // Tool use loop: keep going until Claude gives a final text response
  while (true) {
    const response = await client.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 4096,
      tools: crawlforgeTools,
      messages,
    });

    // Check if Claude wants to use a tool
    if (response.stop_reason === 'tool_use') {
      // Add Claude's response to message history
      messages.push({ role: 'assistant', content: response.content });

      // Execute each tool call and collect results
      const toolResults: Anthropic.ToolResultBlockParam[] = [];

      for (const block of response.content) {
        if (block.type === 'tool_use') {
          const result = await executeTool(
            block.name,
            block.input as Record<string, unknown>
          );
          toolResults.push({
            type: 'tool_result',
            tool_use_id: block.id,
            content: result,
          });
        }
      }

      // Return tool results to Claude
      messages.push({ role: 'user', content: toolResults });
    } else {
      // Claude gave a final text response
      const textBlock = response.content.find(b => b.type === 'text');
      return textBlock ? textBlock.text : '';
    }
  }
}

// Usage
const answer = await askClaude(
  'What are the top 3 trending repositories on GitHub right now?'
);
console.log(answer);

这个循环会自动处理多步工具使用。Claude 可能先搜索,再从某个结果中提取内容,然后再次搜索 —— 循环会持续,直到它产出最终的文本响应。

步骤 3:构建一个研究助手

将这个 agent 封装进一个更结构化的应用中:

Typescript
// research-assistant.ts
import { askClaude } from './lib/agent';

async function researchTopic(topic: string, depth: 'quick' | 'thorough' = 'quick') {
  const systemPrompt = depth === 'thorough'
    ? `Research this topic thoroughly. Search for at least 3 different sources,
       read each one, and provide a comprehensive summary with citations.
       Topic: ${topic}`
    : `Quickly answer this question using web search if needed.
       Be concise and cite your source. Topic: ${topic}`;

  console.log(`Researching: ${topic} (mode: ${depth})`);
  const result = await askClaude(systemPrompt);
  console.log(result);
  return result;
}

// Quick research: ~7 credits (search + 1 extract)
await researchTopic('What is the current version of Next.js?', 'quick');

// Thorough research: ~11-15 credits (search + 3 extracts)
await researchTopic(
  'Compare the performance of Bun vs Node.js for HTTP servers in 2026',
  'thorough'
);

进阶:流式输出与工具使用

为了获得更好的用户体验,使用流式输出来实时展示 Claude 的思考过程:

Typescript
// lib/streaming-agent.ts
import Anthropic from '@anthropic-ai/sdk';
import { crawlforgeTools } from './tool-definitions';

const client = new Anthropic();
const CRAWLFORGE_BASE = 'https://crawlforge.dev/api/v1/tools';

async function executeTool(
  name: string,
  input: Record<string, unknown>
): Promise<string> {
  const response = await fetch(`${CRAWLFORGE_BASE}/${name}`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.CRAWLFORGE_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(input),
  });
  return JSON.stringify(await response.json());
}

export async function streamWithTools(prompt: string) {
  const messages: Anthropic.MessageParam[] = [
    { role: 'user', content: prompt },
  ];

  while (true) {
    const stream = client.messages.stream({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 4096,
      tools: crawlforgeTools,
      messages,
    });

    // Collect streamed text
    let hasToolUse = false;

    stream.on('text', (text) => {
      process.stdout.write(text); // Stream to terminal in real time
    });

    const response = await stream.finalMessage();

    if (response.stop_reason === 'tool_use') {
      hasToolUse = true;
      messages.push({ role: 'assistant', content: response.content });

      const toolResults: Anthropic.ToolResultBlockParam[] = [];
      for (const block of response.content) {
        if (block.type === 'tool_use') {
          console.log(`\n[Calling ${block.name}...]\n`);
          const result = await executeTool(
            block.name,
            block.input as Record<string, unknown>
          );
          toolResults.push({
            type: 'tool_result',
            tool_use_id: block.id,
            content: result,
          });
        }
      }

      messages.push({ role: 'user', content: toolResults });
    }

    if (!hasToolUse) break;
  }
}

// Usage: streams text to stdout as Claude generates it
await streamWithTools('What is CrawlForge MCP and how does it compare to Firecrawl?');

credits 费用明细

工作流使用的工具Credits
快速回答(1 个页面)extract_content2
搜索 + 阅读首个结果search_web + extract_content7
深入研究(3 个来源)search_web + 3x extract_content11
结构化数据提取scrape_structured2
页面元数据检查extract_metadata1
原始 HTML 抓取fetch_url1
多来源深度报告deep_research10

Free 套餐(一次性 1,000 credits)起步大约支持 140 次单页面提取或 90 次「搜索并阅读」工作流。Hobby 套餐($19/月,5,000 credits)非常适合开发和轻量级生产使用。

最佳实践

编写描述性的工具说明。 Claude 通过 description 字段来决定调用哪个工具。请写明该工具做什么、何时使用以及它的 credits 费用。「Extract the main readable content from a web page」比「Get content」更好。

在描述中包含 credits 费用。 当 Claude 知道 fetch_url 花费 1 credit 而 deep_research 花费 10 时,它会自然地为简单任务选择更便宜的选项。

优雅地处理错误。 将错误消息作为工具结果返回,而不是抛出异常。当某个工具失败时,Claude 可以调整策略 —— 例如尝试一个不同的 URL 或重新措辞一次搜索。

恰当地设置 max_tokens。 网页内容可能很长。将 max_tokens 设为至少 4096,给 Claude 留出空间将工具结果整合进全面的响应中。

用 system prompt 引导工具使用。 告诉 Claude 何时该搜索、何时该直接访问一个已知 URL。这能避免在直接使用 extract_content(2 credits)就足够时,发起不必要的 search_web 调用(5 credits)。

常见问题

我可以用 Claude 3.5 Haiku 配合 CrawlForge 来降低成本吗?

可以。所有支持工具使用的 Claude 模型都能与 CrawlForge 工具配合。Haiku 按 token 计价更便宜,但可能需要更明确的指令才能选对工具。Claude Sonnet 在成本与工具使用准确度之间提供了最佳平衡。

我该如何处理速率限制?

CrawlForge 的 API 包含速率限制响应头(X-RateLimit-Remaining)。如果你遇到 429 响应,请添加带指数退避的重试。对于高流量使用,Professional 套餐提供更高的速率限制。

Claude 能在一个回合中调用多个 CrawlForge 工具吗?

可以。Claude 可以在单次响应中请求多次工具使用。步骤 2 中的工具使用循环会处理这一点 —— 它会遍历所有 tool_use 块并一次性返回所有结果。

当 CrawlForge credits 用完时会发生什么?

API 会返回一个 402 Payment Required 错误。将其作为工具结果返回,以便 Claude 能告知用户。你可以通过控制台或 credits API endpoint 查询剩余 credits。

下一步

你现在拥有了一个具备实时网页访问能力、由 Claude 驱动的应用。进一步探索:


让 Claude 拥有实时网页访问能力。 免费开始,赠送 1,000 credits —— 无需信用卡。

亲自试一试——无需注册

在 Playground 中探索全部 31 个 CrawlForge 抓取与提取工具,然后免费开始,获取 1,000 credits。

1,000 免费 credits • 一次性 • 无需信用卡

标签

  • anthropic
  • claude-api
  • tool-use
  • integration
  • web-scraping
  • tutorial
  • typescript

关于作者

CrawlForge Team

工程团队

我们正在打造功能最全面的 Web 抓取 MCP server。我们开发的工具帮助开发者为 AI 应用提取、分析和转换 Web 数据。

邮件订阅

及时获取最新洞察

将教程、产品更新与 Web 抓取技巧直接发送到你的收件箱。

拒绝垃圾邮件,随时可取消订阅。

继续阅读

相关文章

教程

8 分钟

如何在 LangGraph 智能体中使用 CrawlForge

使用 LangGraph 和 CrawlForge 构建有状态的网页爬取智能体。本篇 TypeScript 指南涵盖图节点、状态管理以及条件化的爬取流程,从安装依赖到构建完整研究智能体,含可运行的代码示例。