通过 crewai-webmetadata-extractor 提取公司网站和联系方式,构建两人Agent配合的获客工作流,1次API调用完成全部分析。
B2B 潜在客户开发工作流在各行业通常遵循同样的模式:你有一批目标公司的域名列表,针对每个域名,你需要三样东西才能写出一封有用的外推邮件——联系方式、对他们正在运行的技术栈的判断(以便定制推销话术),以及快速决定他们是否值得联系。
手工处理超过少数几条线索就难以扩展。自己写爬虫意味着在写任何真正的 Agent 逻辑之前就要处理安全 SSRF 抓取、HTML 解析和接口限速问题。
本文介绍如何构建一个双 Agent 的 CrewAI Crew,输入是一批公司 URL,输出是一份经过排序、有推理依据的候选名单——使用 crewai-webmetadata-extractor 处理数据提取侧,让 Agent 专注于判断而非解析。
pip install crewai crewai-webmetadata-extractor
你需要两个密钥:
一个免费的 RapidAPI 密钥,用于 Web Metadata & Contact Extractor API(每月 1000 次请求,无需绑定信用卡)
一个用于运行 CrewAI Agent 的 LLM 提供商的 API 密钥(OpenAI、Anthropic 等——CrewAI 负责提供商抽象,不在本文讨论范围内)
export WEBMETADATA_API_KEY=your-rapidapi-key
crewai-webmetadata-extractor 提供了四个现成的 BaseTool 子类。对于潜在客户开发场景,最重要的是两个:
每个工具都返回 JSON 字符串,API 错误以 {"error": true, ...} 的形式返回而不是抛出异常——因此列表中某个 URL 有误不会导致整个 Crew 运行崩溃。
两个 Agent:一个负责收集每个公司的原始信号,另一个负责将这些信号转化为排序后的、有推理依据的候选名单。
from crewai import Agent, Task, Crew, Process
from crewai_webmetadata_extractor import WebContactsTool, WebMetadataExtractTool
researcher = Agent(
role="B2B Lead Researcher",
goal="Extract contact information and technical footprint for a list of company websites",
backstory=(
"You investigate company websites to surface contact points and technology "
"signals that a sales team can act on. You report facts, not conclusions."
),
tools=[WebContactsTool(), WebMetadataExtractTool()],
verbose=True,
)
qualifier = Agent(
role="Sales Development Rep",
goal="Turn raw research into a prioritized, reasoned outreach shortlist",
backstory=(
"You review research on prospective companies and decide who's worth "
"contacting first, based on how good a fit their tech stack and public "
"presence make them for our product."
),
verbose=True,
)
target_urls = [
"https://example-company-one.com",
"https://example-company-two.com",
"https://example-company-three.com",
]
research_task = Task(
description=(
f"For each of these URLs, extract public contact info and the detected tech "
f"stack: {', '.join(target_urls)}. List what you found per company, including "
f"any URL that returned no usable contact info."
),
expected_output="A per-company breakdown of contacts found and tech stack detected.",
agent=researcher,
)
qualify_task = Task(
description=(
"Using the research above, rank the companies from most to least worth "
"contacting. Justify each ranking with a specific signal from the research "
"(a detected technology, a missing security header, presence or absence of "
"a direct contact channel) — not a generic guess."
),
expected_output="A ranked list of companies with a one-line justification each.",
agent=qualifier,
context=[research_task],
)
crew = Crew(
agents=[researcher, qualifier],
tasks=[research_task, qualify_task],
process=Process.sequential,
)
result = crew.kickoff()
print(result)
Researcher 的工具调用返回的是结构化 JSON,而非自由文本——这使得 qualifier 能够针对具体字段进行推理,而不是凭感觉判断。一次 WebMetadataExtractTool 调用包含 tech_stack 数组(检测到的 CMS、分析工具、框架)和分级的 security_headers 对象,因此"按匹配度排序"可以变成类似"使用 WordPress 且未检测到 CMS 特定的缓存层,且缺少 CSP 头——适合做开发/安全托管业务的潜在客户",而不是一个凭空编造的理由。
WebContactsTool 的输出有意限定在页面上实际存在的内容范围内(邮箱、电话、社交链接)——它是供 qualifier 权衡的原始信号,而非对已验证公司或人物数据的声明。
同样的两个工具可以组合出其他形态:加上 WebSEOAuditTool 就能转化为以 SEO 为角度的销售切入点("你的首页缺少 H1 结构,这是一份报告");对同一批 URL 接入 WebMarkdownTool,则能把 researcher 变成针对潜在客户自有公开内容的 RAG 管道的摄入步骤,而不再是一个潜在客户开发 Crew。
将"提取"和"判断"分离为两个 Agent(而非一个 Agent 同时做两件事)的意义在于:researcher 的输出始终可追溯——你可以独立于 qualifier 的结论对原始 JSON 进行日志记录或缓存,也可以单独修改 qualifier 的提示词而无需重新运行提取步骤。
crewai-webmetadata-extractor on PyPI
Underlying Python SDK (webmetadata-extractor)
Also available for LangChain