本页内容
Anthropic 的 Claude API 支持原生工具使用—— 你用 JSON schema 定义工具,Claude 在对话过程中自行决定何时调用它们。CrawlForge 的 20 个 web scraping 工具天然契合:它们让 Claude 具备搜索网页、提取内容、scraping 结构化数据和进行深度研究的能力,全部通过标准的 tool_use API 实现。
本指南将带你完成为 Claude API 定义 CrawlForge 工具、处理工具使用响应,以及构建一个生产级研究助手。
目录
- 前置条件
- Claude 工具使用如何与 CrawlForge 协作
- 步骤 1:定义 CrawlForge 工具 schema
- 步骤 2:处理工具使用循环
- 步骤 3:构建一个研究助手
- 进阶:流式输出与工具使用
- credits 费用明细
- 最佳实践
- 常见问题
- 下一步
前置条件
npm install @anthropic-ai/sdk dotenv# .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 的工具使用遵循一个请求-响应循环:
- 你发送一条带工具定义和用户提示的消息
- Claude 以文本或一个
tool_use内容块作出响应 - 你执行该工具(调用 CrawlForge API)并返回结果
- 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 格式):
// 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 执行该工具,然后返回结果。
// 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 封装进一个更结构化的应用中:
// 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 的思考过程:
// 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_content | 2 |
| 搜索 + 阅读首个结果 | search_web + extract_content | 7 |
| 深入研究(3 个来源) | search_web + 3x extract_content | 11 |
| 结构化数据提取 | scrape_structured | 2 |
| 页面元数据检查 | extract_metadata | 1 |
| 原始 HTML 抓取 | fetch_url | 1 |
| 多来源深度报告 | deep_research | 10 |
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 驱动的应用。进一步探索:
- CrawlForge 快速上手:与 Claude Code 的原生 MCP 集成
- 全部 26 个工具详解:含 credits 费用和使用示例
- 构建一个 AI 研究助手:使用 Claude 和 CrawlForge
- CrawlForge vs Firecrawl 对比:帮助你选择合适的工具
让 Claude 拥有实时网页访问能力。 免费开始,赠送 1,000 credits —— 无需信用卡。
亲自试一试——无需注册
在 Playground 中运行 CrawlForge 的 27 个抓取与提取工具中的任意一个,然后免费开始,获取 1,000 credits。
1,000 免费 credits • 每月补充 • 无需信用卡
标签
及时获取最新洞察
将教程、产品更新与 Web 抓取技巧直接发送到你的收件箱。
拒绝垃圾邮件,随时可取消订阅。