mcpField
S
sales-outreach

sales-outreach

github.com/crustdata营销与增长11309 次安装2 周前更新
代码仓库

关于 sales-outreach

sales-outreach 是帮助销售人员撰写个性化触达内容的技能,先查找近期且可核实的个人或公司信号,再据此起草文案。它涵盖邮件、电话、LinkedIn 消息和回复处理,并要求没有真实线索时明确说明。

主要功能

  1. 01检索近期个人与公司信号
  2. 02按邮件、电话及 LinkedIn 编写触达内容
  3. 03生成多种切入角度供测试
  4. 04编写异议应对与回复
  5. 05无可靠线索时避免杜撰

适用场景

  1. 01根据近期动态撰写冷邮件
  2. 02为目标客户准备电话脚本和异议分支
  3. 03撰写 LinkedIn 邀请、私信及跟进内容
  4. 04按来信类型起草相应回复

MCP 相关

技能通过 Crustdata MCP 的 Code Mode 获取个人帖子、公司动态、融资和招聘信息,并将其作为触达内容的事实依据。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/crustdata/skills --skill sales-outreach

安装后在 Claude Code 中输入/sales-outreach确认已加载。

附带文件evals/evals.jsonREADME.md

详细介绍

销售拓展

只做一件事:我该说什么——对方能看出我确实做过功课吗?

文案是容易的那一半。触达有效的原因,在于开头提到了关于读者的具体、近期且真实的信息:对方上周写的一篇帖子、上个月完成的一轮融资、刚发布的职位,或刚聘请的高管。本技能会先从 Crustdata 提取这些信息,再围绕它来撰写文案。

先找切入点,再写文案。始终如此。 一封写得再漂亮、内容却只是“我看到你们在金融科技领域”的邮件,也不过是模板。一封直白的三行邮件,如果提到“你周二那篇关于值班轮换的帖子”,就能收到回复。如果某个目标没有真实信号,就如实说明,并提供其他角度——绝不要捏造切入点。


Code Mode 基本规则(读一遍,处处适用)

所有 Crustdata 调用都作为纯 JavaScript 脚本,在 Crustdata MCP 服务的 execute 工具中运行 (install.crustdata.com/mcp)。以下每段代码都遵循这些规则:

  • 只能使用纯 JavaScript。 根据 get_schema 提供的类型化工具接口编写代码,但脚本主体不得包含任何类型注解——: Type、as 或泛型都会导致解析错误,使整个运行在调用任何工具前失败,且不产生任何费用。
  • 每个脚本都必须以带来源标记的注释开头:对于用户的原始请求,使用 // user query: ...;对于推导出的步骤,使用 // model query: ...。没有此类注释的脚本会在运行前被拒绝。
  • 唯一的 I/O 原语: const r = await callTool(name, params) → { ok: true, data } 或 { ok: false, status, errorType, message }。务必根据 r.ok 分支处理——调用失败不会中止脚本,因此未检查的失败会悄悄使用空数据继续执行,看起来像是“对方近期没有动态”。这正是导致切入点被捏造的情况。
  • fields 是响应白名单。 结果只包含你列出的分组;之后读取未列出的分组时会得到 undefined,看起来就像数据缺失。请列出你会读取的所有分组。
  • 只返回最精简的数据投影。 只有脚本返回的内容会传给模型。用于拓展时,这意味着只返回候选切入点——日期、一行文字和链接,绝不要返回原始帖子或完整个人资料。
  • 使用 await parallelMap(items, fn) 并行执行互不依赖的任务;有单次调用上限时,先使用 chunk 分块。绝不要并行处理游标分页或存在依赖关系的阶段。在耗费较大的阶段后调用 checkpoint(acc),以便即使超时,也能返回已经花费资源获取的切入点。

步骤 0 — 收集信息(始终在动笔前执行)

  1. 配置文件是基础,但绝不能阻塞。 如果工作目录中存在 config/persona-profile.md 或 config/gtm-config.md,请读取它们:销售代表的表达风格(语气、句长、结尾署名、禁忌清单)、销售的产品以及购买者。如果文件不存在,就在对话中提一个简短问题(“你卖什么?署名怎么写?”),或推荐 icp-builder 技能,该技能可根据一个 LinkedIn URL 生成这两个文件。配置文件缺失绝不能阻止起草——使用简洁直接的语气撰写,并说明这一点。
  2. 用一行确认请求:对象(个人、客户或名单)——渠道(邮件 / 电话 / LinkedIn)——时机(首次触达 / 跟进 / 回复 / 异议)——目标(约会议?获得答复?寻求引荐?)。
  3. 找到切入点(见下一节)。一个精准的切入点胜过五个泛泛的切入点。

切入点层(这就是本技能)

四个来源,按成本从低到高、按具体程度从高到低排列。只提取一个;找到合适的信息就停止。在同一个人身上跑完全部四项,通常是在浪费点数。

Hook 1 — 他们说了什么(他们的帖子)

这是最好的 Hook,因为内容来自他们本人,而且有日期。根据此人的职业社交平台个人资料 URL 查找。每篇帖子消耗 1 个积分,所以 limit 是控制花费的旋钮——5 就足以找到一个 Hook。

// user query: write a cold email to https://www.linkedin.com/in/example — find a hook first
const url = "https://www.linkedin.com/in/example";
const cutoffMs = Date.now() - 60 * 24 * 3600 * 1000; // hooks older than ~60 days read as stale

const r = await callTool("social_post_list_live", {
  professional_network_profile_url: url, // person key; company_domain / crustdata_company_id key the company feed
  limit: 5,                              // 1 CREDIT PER POST — set this deliberately
  fields: ["text", "date_posted", "post_type", "engagement", "hyperlinks", "share_url"], // whitelist
});
if (!r.ok) return { hooks: [], error: r.message };

const parse = (d) => { const t = Date.parse(d ?? ""); return Number.isNaN(t) ? null : t; };
const posts = (r.data.posts ?? []).map(p => ({
  date: p.date_posted,
  ms: parse(p.date_posted),
  type: p.post_type,
  url: p.share_url, // the link to the post itself — the rep needs it to verify in five seconds
  text: (p.text ?? "").slice(0, 400),
  reactions: p.engagement?.total_reactions,
  comments: p.engagement?.total_comments,
  links: p.hyperlinks?.other_urls ?? [],
}));
return {
  fresh: posts.filter(p => p.ms !== null && p.ms >= cutoffMs),
  stale: posts.filter(p => p.ms !== null && p.ms < cutoffMs).length,
  undated: posts.filter(p => p.ms === null), // never silently drop these — read them yourself
};

解读结果时:优先选择他们原创的帖子,而不是没有评论的转发;优先选择观点,而不是公告。有真实看法的帖子能让你找到一句可以赞同或反驳的话,而这正是促成回复的关键。

Hook 2 — 公司发生了什么(融资、新闻、增长)

company_identify 免费,但匹配不够精确——一个域名可能匹配多家公司。选取最高的 confidence_score,然后按 id 补充信息,这样你只需为一个匹配结果付费(2 个积分)。改用域名或名称补充信息,可能会模糊匹配多家公司,并按每个返回的匹配结果收取 2 个积分;exact_match: true 可以缩小范围,但不能保证最终只匹配一家公司。

// model query: recent company events at acme.com worth opening an email with
const idr = await callTool("company_identify", { domains: ["acme.com"] }); // free, one identifier type per call
if (!idr.ok) return { error: idr.message };
const matches = idr.data[0]?.matches ?? [];
const best = matches.slice().sort((a, b) => (b.confidence_score ?? 0) - (a.confidence_score ?? 0))[0];
if (!best) return { error: "no company match for acme.com" };
const companyId = best.company_data?.basic_info?.crustdata_company_id ?? best.company_data?.crustdata_company_id;

const er = await callTool("company_enrich", {
  crustdata_company_ids: [companyId],                       // one id = one match = exactly 2 credits
  exact_match: true,                                        // narrows the fuzzy match; by-name/by-domain can still return >1 -> N x 2 cr
  fields: ["basic_info", "funding", "headcount", "news"],    // whitelist: list EVERY group you read
});
if (!er.ok) return { error: er.message };
const cd = er.data[0]?.matches?.[0]?.company_data ?? {};
const news = (firstArray(cd.news) ?? []).slice(0, 10).map(n => ({
  title: n.article_title, url: n.article_url, date: n.article_publish_date,
}));
return {
  name: cd.basic_info?.name,
  lastRound: cd.funding?.last_round_type,
  lastRaise: cd.funding?.last_fundraise_date,
  lastRoundUsd: cd.funding?.last_round_amount_usd,
  investors: cd.funding?.investors,
  headcount: cd.headcount?.total,
  growth12m: cd.headcount?.growth_percent?.["12m"],
  news,
};

融资是有时效性的 Hook。3 周前完成的一轮融资,可以用“恭喜 + 接下来通常会遇到的问题”来切入;14 个月前完成的融资只是背景信息,不适合作为开场。新闻文章有真实的发布日期——把日期写进句子里,如果引用具体说法,也要链接到文章。

Hook 3 — 他们在招什么人

招聘信息是公司发布的内容中最真实的一类。他们正在招聘的岗位,代表他们已经决定投入资金解决的问题。

// model query: what acme.com is hiring for, and whether their posts name our category
const domain = "acme.com";
const term = "<the tool you displace>"; // a brand/product/tech name -> ALWAYS [.] exact token, never (.)
const [fresh, named] = await parallelMap([
  {
    filters: eq("company.basic_info.primary_domain", domain),
    sorts: [{ field: "metadata.date_added", order: "desc" }],
    fields: ["job_details", "location", "metadata"],
    limit: 15,
  },
  {
    filters: and_(
      eq("company.basic_info.primary_domain", domain),
      exactToken("content.description", term) // [.] guarantees the literal token; (.) is typo-tolerant and pulls lookalikes
    ),
    fields: ["job_details"],
    limit: 1, // count query — read total_count, don't download rows
  },
], async (params) => await callTool("job_search", params));
if (!fresh.ok) return { error: fresh.message };
return {
  newest: fresh.data.job_listings.map(j => ({
    title: j.job_details?.title,
    city: j.location?.city,
    added: j.metadata?.date_added,
  })),
  total_open: fresh.data.total_count,
  postings_naming_term: named.ok ? named.data.total_count : null,
};

按职位名称筛选岗位,而不要用 job_details.category——类别字段粒度较粗,大多数招聘信息都会归到“其他”类别。如果你想说“你们有 4 个数据工程岗位正在招聘”,就按职位名称计数。

根据岗位情况,Hook 往往不言自明:有 3 个 SDR 职位空缺,说明他们刚刚提高了销售管道目标。首次招聘安全岗位,说明他们即将购买安全工具。指出具体职位以及招聘信息的发布时间。

Hook 4 — 已经发生的信号

最理想的情况:所需工作已经完成。sales-prospecting 的联系人追踪器、扩张雷达和客户排名都会输出带日期的信号行。直接交接其中一行,完全跳过抓取。

交接行可能长这样:某人——曾在[你的客户公司]担任[职位]——现任[新公司][职位]——于[日期]入职。这就是 Hook,而且已经带有日期。“于 2026-04-12 完成 B 轮融资”或“6 周前聘用了数据副总裁”也是如此。

不要重新抓取交接给你的内容。只有在需要补充背景时才花费积分(比如获取对方在新岗位发布的第一篇帖子),而且只在草稿确实需要时才这样做。

信号 → 切入角度
信号切入角度开场方式
刚完成融资(不到 3 个月)扩张压力——员工人数增加后哪些环节会出问题用半句话祝贺,然后谈融资带来的压力
新上任的同职能高管新职责、90 天计划、新预算以“刚刚上任”的氛围切入:他们接手了什么
核心联系人跳槽到新公司他们已经知道你们合作得如何“恭喜——你知道接下来会是什么情况”(最温暖的触达方式)
相关团队集中招聘他们已经认定问题确实存在提到相关职位;询问从现在到新人入职这段时间会发生什么
发表了有真实观点的帖子赞同、补充观点,或礼貌地提出不同意见回应他们的观点,而不是他们发帖这件事
招聘信息中提到技术技术栈契合或替换可能点出工具,说明衔接点
公司新闻 / 产品发布时机把发布消息与产品能消除的问题联系起来
没有钩子,就不要编造

如果拉取结果为空——没有近期帖子、融资是两年前的事、没有开放职位—— 就直说,并提供其他选择,不要编造依据:

  • 从职位层级的洞察切入(“300 人规模的公司,每位工程副总裁都会遇到这个问题”),并明确标注 这是职位层级的洞察——好的泛化内容胜过虚假的具体信息。
  • 找同一客户公司的另一位活跃人士来挖掘钩子——如果你已经有对方的个人资料 URL(来自用户清单,或来自负责发掘联系人的 sales-prospecting / account-research),就对他们运行 Hook 1。
  • 使用客户公司自己的话:按 company_domain 调用 social_post_list_live,即可获取个人动态不活跃时的公司动态。
  • 建议等待信号出现,并在 sales-prospecting 的关键人物追踪器中设置监控。

钩子写入草稿前必须通过三项测试:是他们自己的内容吗(而非行业内容)、有时间信息吗(帖子不超过 60 天,融资/新闻不超过 90 天),以及他们能否一眼认出来。


触达类型

1. 冷邮件

少于 90 个单词。 主题为 3-5 个单词,尽量小写,不用标题党,也不写“快速问个问题”。

  • 第 1 行——钩子。 写他们自己的事,注明时间,用他们的语言。读者应该会想: “对,我确实做过这件事。”
  • 第 2 行——衔接。 用一句话把他们的事与您能解决的痛点联系起来。
  • 第 3 行——一个具体成果。 给出数字或明确的客户情境,而非价值主张。
  • 第 4 行——一个请求。 选择既能推动进展、又最不费力的说法(“周四聊 15 分钟怎么样?”或“要我把两行版本发给你吗?”)。
  • 如果角色配置中有他们的落款,就使用该落款。icp-builder 编写的配置会记录语气和风格,但不记录落款,因此第一次请直接询问,并在之后沿用。

一个钩子,一个想法,一个请求。提供 2-3 个角度变体供测试(使用不同钩子,或用同一钩子切入不同痛点),并说明你会发送哪一个。

2. 电话脚本
  • 开场白 + 征求许可的话术(“突然打给你——能占用 30 秒,之后你可以让我挂电话吗?”)。
  • 来电原因,与同一个钩子相关。冷电话一说“只是联系一下”就会失败。
  • 价值假设,用一句话表达,并说明这只是一个可以由对方纠正的猜测。
  • 2-3 个开放式探索问题——他们现在如何处理这件事,以及这给他们造成了多大成本。
  • 请求:约一次会,而不是卖东西。
  • 异议应对分支,逐项写出:不感兴趣/发邮件给我/我们已经在用其他方案/没有预算/守门人。每项都用一句话表示理解,再问一个能让通话继续下去的问题。
  • 15 秒语音留言和一行后续短信,两者都提到同一个钩子,让各次触达彼此衔接。
3. LinkedIn 触达
  • 建立联系备注——最多 300 个字符,不推销。写上钩子,以及建立联系的理由。
  • 第一条私信——提供价值,或提出一个聪明的问题。仍然不推销。
  • 后续跟进——只有在对方回复或出现信号之后,才提出一个轻量请求。
  • 结束跟进——一行即可,礼貌得体,并为以后留下空间。

在做任何这些之前,先推荐 1-2 条值得认真评论的近期帖子——Hook 1 已经提供了这些帖子,无需额外成本。在发送联系请求前三天认真评论一条帖子,效果胜过任何开场白。

4. 回复处理

先分类,再推进。匹配对方回复的长度和语气——对方回两行,你也回两行。

回复类型应对方式
感兴趣提出两个具体时间。别说其他内容。不要重新推销。
提问用一段话直接回答,然后提出请求。
异议参考话术(见下文)。
转介(“去找某某”)向对方致谢,请他们引荐,或征求许可说明是他们介绍的;然后以这位介绍人为钩子开启新的对话。
现在不行确定日期。“我第一季度再联系你,怎么样?”然后务必记录下来。
明确拒绝礼貌地回复一句,然后结束。不要最后再推销一次。
取消订阅立即照办。不要反向挽留,也不要说“只是确认一下”。将对方从所有序列中移除。
5. 异议应对话术

先判断真正的异议是什么。“太贵了”通常意味着“价值不明确”或“公司内部没有支持者”。“时机不对”通常意味着“不是优先事项”。回答之前,先说出你认为真正的异议是什么。

然后:**认可 → 重新阐释 → 给出一个真实的证明 → 委婉地再次提出请求。**只给出一个证明,而且必须真实:具体的情况、经得起推敲的数据。绝不要编造案例。

给出两个版本:直接版(适合直言不讳的买家)和委婉版(适合仍在建立关系的对象)。

Playbook 模式:将团队最常遇到的 8-10 个异议整理到一份文档中,每个异议都包括判断、两个版本,以及需要避免的陷阱。这份内容值得渲染成页面,参见 Rules 下的 artifact 规则。

6. 序列构建器
  1. 先制作节奏表:日期、渠道、切入角度、个性化程度。在不同触达中变换角度:问题 → 证明 → 另一种角色的痛点 → 社会认同 → 结束联系。不要换几种说法,把同一个观点发五遍。
  2. 然后用类型 1-3 编写每次触达的内容。
  3. 标记每次触达是 1:1(销售代表撰写一条定制内容),还是 merge-field(可用于整个名单),并准确标出销售代表必须补充只有自己知道的信息的位置。
  4. 保持开场内容新鲜:第 1 次触达使用挖掘到的开场信息。到了第 4 次触达,这条信息已经过时两周,要么重新挖掘,要么改用不依赖时效性的角度。在表格中说明选择了哪种方式。

典型形式(根据销售方式调整,不要原样照搬):

日期渠道切入角度个性化
1邮件开场信息 + 由此体现的痛点1:1
3LinkedIn联系邀请说明,使用相同的开场信息,不推销1:1
5电话 + 语音留言拨打原因 = 开场信息1:1
8邮件证明,在同一邮件线程中merge-field
12邮件另一种角色对该痛点的理解merge-field
18邮件结束联系,只写一句merge-field
7. 由信号触发的草稿

输入:刚刚出现的信号(新一轮融资、高管入职、支持者离职、招聘激增),通常由 sales-prospecting 提供。输出:根据上方的信号 → 角度表,撰写一份提及实际触发事件及其日期的草稿。

让这类草稿奏效的规则是:信号出现当周就必须能发出去。融资六周后才发“祝贺完成新一轮融资”的邮件,还不如什么都不发。如果信号已经过时,就说明这一点并更换角度。

要让信号自动持续流入,请引导用户使用 sales-prospecting 的支持者追踪器来设置定期监控。


禁止敷衍规则(适用于每份草稿,无一例外)

在展示任何文案之前,都要逐条检查。带有 AI 痕迹的触达内容比不触达更糟糕,它会让读者觉得自己只是名单上的一员。

发现以下内容,立即删除:

  • **长破折号。**改用句号或逗号。
  • “希望你一切都好”“我想联系你”“有个小问题”“再跟进一下”“只是跟进一下”。
  • “深入探讨”“利用”“简化”“解锁”“提升”“无缝”“强大”。
  • 三段式结构(“更快、更便宜、更可靠”)。
  • 否定式并列结构(“不只是 X,而是 Y”)。
  • 填充性副词:“真的”“其实”“确实”“极其”。
  • 假装熟络(“希望你最近进展神速!”)和虚假的紧迫感。
  • 任何替换公司名称后仍然成立的句子。如果这句话适用于任何公司,那就不适用于任何公司。

然后大声读出来。听起来应该像销售代表在给一位聪明的同事发消息。如果草稿读起来像模板,就重写,不要敷衍了事。


列表模式——多个账户,一次运行

当输入是一个列表(通常是 sales-prospecting 生成的评分列表)时,顺序始终是:抓取钩子 → 撰写文案 → 导出。不能反过来。

步骤 1——并行抓取钩子。 每个人互不相关,因此使用 parallelMap。运行前说明成本:每篇帖子消耗 1 个积分,因此对 40 个人设置 limit: 3,最多消耗 120 个积分。

// user query: draft outreach for these 40 accounts — pull a hook for each first
const people = inputs.people; // [{ name, profileUrl, company, domain }]
const cutoffMs = Date.now() - 60 * 24 * 3600 * 1000;

const hooks = await parallelMap(people, async (p) => {
  const r = await callTool("social_post_list_live", {
    professional_network_profile_url: p.profileUrl,
    limit: 3,                                        // 1 cr/post x 3 x N people — quote this before running
    fields: ["text", "date_posted", "post_type", "share_url"],
  });
  if (!r.ok) return { name: p.name, hook: null, reason: r.message };
  const fresh = (r.data.posts ?? [])
    .map(x => ({ date: x.date_posted, ms: Date.parse(x.date_posted ?? ""), url: x.share_url, text: (x.text ?? "").slice(0, 300) }))
    .filter(x => !Number.isNaN(x.ms) && x.ms >= cutoffMs);
  return { name: p.name, company: p.company, hook: fresh[0] ?? null, reason: fresh.length ? null : "no recent post" };
});
checkpoint(hooks);
return {
  withHook: hooks.filter(h => h.hook),
  needCompanyFallback: hooks.filter(h => !h.hook).map(h => ({ name: h.name, company: h.company, reason: h.reason })),
};

对于所有 needCompanyFallback 中的人,使用账户层级的钩子 2 或钩子 3,或者如实使用合并字段模板。绝不能让空结果变成编造的第一句话。

步骤 2——联系人信息补全为可选项,且须确认成本。 运行前先报出最高成本,然后再运行:

“为 40 个人查找商务邮箱:最多约 40 个积分(每个匹配的人 1 个)。添加个人邮箱和电话号码会将上限提高到每人 5 个积分,因此总计约 200 个。只查商务邮箱——可以吗?”

// user query: get business emails for the 40 people on the list (worst case ~40 credits, confirmed)
const urls = inputs.profileUrls;
const batches = chunk(urls, 25); // hard cap: 25 URLs per call
const results = await parallelMap(batches, async (batch) => {
  const r = await callTool("person_contact_enrich", {
    professional_network_profile_urls: batch,
    fields: ["contact.business_emails"], // narrow the request; see the cost note below
  });
  return r.ok ? r.data : batch.map(u => ({ matched_on: u, matches: [], error: r.message }));
});
const rows = results.flat();
return {
  contacts: rows.map(m => ({
    url: m.matched_on,
    email: m.matches?.[0]?.person_data?.contact?.business_emails?.[0]?.email ?? null,
    status: m.matches?.[0]?.person_data?.contact?.business_emails?.[0]?.status ?? null,
  })),
  unmatched: rows.filter(m => !m.matches?.length).map(m => m.matched_on),
};

省略 fields 会请求全部三个层级(商务邮箱、个人邮箱、电话号码),并将上限提高到每个匹配的人 5 个积分。将白名单缩小到你实际需要的层级,并查看响应中的 credits_remaining,以了解实际消耗。如实报告未匹配的行——绝不能用猜测的邮箱格式凑数。

步骤 3——交付。 提供两个文件,不包含凭据,也不自动发送:

  • 营销活动 CSV:email, first_name, last_name, company, title, linkedin_url, hook_text, hook_date, hook_url, personalization_line, tier。钩子、日期及其链接都随文件一并提供,以便销售代表在发送前逐行核对。
  • 营销活动规范,供用户粘贴到其使用的任意序列发送工具中:包含每一步的完整文案、使用的合并字段、发送时段和时区、每日发送量递增计划(新域名从每天 20-30 封开始)、邮件线程设置(后续邮件在同一线程中发送)以及启用回复后停止发送。
  • 规范中的送达率防护措施:已预热的域名,第 1 次触达最多包含一个链接,优先使用纯文本而非 HTML,立即处理退订。

规则

  • 切入点优先,而且必须真实,否则就不要写。 切勿捏造帖子、引言、融资轮次、共同联系人或案例研究。“我没找到此人的近期动态,下面改用一个基于其职位的切入角度”是有效且诚实的回答。
  • 为草稿和给销售代表的备注中的每条引用注明日期,让他们能在 5 秒内核实。
  • 每次触达只讲一个切入点、一个想法、提出一个请求。简洁胜过巧思。
  • 绝不自动发送任何内容。 邮件草稿必须保持为草稿。此技能绝不会写入 CRM;在写作之前,任何日志条目都由你自行记录。序列器配置应是一份规范和一个 CSV,由销售代表自行加载并启动。
  • 立即并完全尊重退订请求。 绝不能试图通过增加发送量来绕过合规限制;如果用户希望这么做,必须明确由其自行承担责任。
  • 每份交付内容都必须遵守“杜绝水文”规则(完整清单见上文):不使用长破折号,不用“delve”/“leverage”/“streamline”,不加废话,也不写任何替换公司名称后仍然适用的内容。
  • 成本都是真实的:在拉取帖子、扩展名单或丰富联系人信息之前,先说明预期费用。每条 execute 响应都包含 credits 和 credits_remaining;account_credits 可免费查询余额。
  • 每份交付内容的结尾都必须询问交付方式:直接贴在聊天中、提供 CSV 和供序列器使用的活动规范,还是生成排版好的文档。
  • 根据内容调整布局,绝不能让布局遮挡内容。 品牌系统固定,布局不固定。如果真实内容放不下,例如公司或人名较长、标题有 12 个词、包含 200 行,就调整布局,而不是内容:增大卡片、自动换行而非截断、改为单栏、加宽列、提高上限,或为宽内容添加独立的滚动容器。绝不要通过裁切卡片、省略姓名或悄悄删掉行来解决适配问题。如果确实无法避免设置上限,需在 UI 中注明(“显示 214 项中的前 50 项”)。交付前检查渲染结果,并修复所有被截断的内容。
  • 渲染输出中的图标:使用 Lucide,也就是仪表板的图标集,并以内联 SVG 形式呈现,描边设为 currentColor。制品 UI 中不得使用表情符号。
  • 照片和徽标均可免费使用,可用于渲染输出。 basic_profile.profile_picture_permalink(人物照片)位于提供给你的任何人物记录的 basic_profile 组中;basic_info.logo_permalink(公司徽标)由 company_identify 和 company_enrich 的 basic_info 免费提供。两者都不额外消耗点数。将两者以内联 Base64 的形式写入 data:image/jpeg;base64,... URI,因为媒体 CDN 会以 binary/octet-stream 提供文件,远程 <img src> 会显示为空白。图片缺失时使用姓名首字母徽标作为后备。
  • 制品品牌标识:默认在聊天中直接提供文案,CSV 保持为纯数据文件,不要为了渲染而渲染制品。但如果交付内容被渲染为网页或文档(异议处理手册、序列规范、活动单页),则必须在页眉或页脚中加入 Crustdata 品牌标识组合:小号大写字样“Powered by”,加上官方 Crustdata 字标,并链接到 crustdata.com。此技能的 assets/ 中提供了成对字标文件:crustdata-logo-light.png(深色文字,适用于浅色背景)和 crustdata-logo-dark.png(白色文字,适用于深色背景)。根据主题以内联 Base64 形式嵌入对应版本,字标高度约为 17px;绝不要使用远程图片链接,因为渲染后的制品无法获取远程图片。品牌强调色:#5547E2(深色背景上使用 #8387FF)。正文使用 Geist 字体;若无法嵌入,则使用系统字体栈。

错误处理

  • 危险的情况不是显而易见的情况,而是调用失败。 拉取帖子后出现 r.ok === false,看起来和“此人没有发帖”完全一样。请针对这种情况添加分支,将其报告为错误,并回退到 Hook 2 或 Hook 3——绝不要把它伪装成个性化内容,塞进一条通用开场白里。
  • 403 指明某个字段 = 该字段对应的投影受套餐限制。从 fields 中移除该字段并重新运行。403 指明某个端点(联系人信息丰富化是常见情况)= 套餐不包含该功能;请明确说明,并交付不含邮箱的名单,而不是猜测邮箱地址格式。
  • 帖子为空、新闻为空、没有开放职位都是正常结果,并非失败。请报告哪些来源返回了空结果,并采用上文的无 Hook 路径。
  • company_identify 为同一个域名返回多个匹配项是预期行为,因为它采用模糊匹配。请选取 confidence_score 最高的匹配项;如果两个分数接近,请同时列出并询问。
  • 名单处理过程中未匹配的行要按名称报告,绝不能丢弃,也不能悄悄换成模板行。

成本速查表

调用成本
company_identify、autocomplete、get_schema、account_credits免费
social_post_list_live每条帖子 1 cr — limit 是控制开销的旋钮
按 id 调用 company_enrich,并设置 exact_match: true一个匹配项需 2 cr
job_search每个结果约 0.03 cr;limit: 1 + total_count ≈ 0.03 cr
person_contact_enrich无基础费用;按每位匹配人员返回的联系人类型计费(工作邮箱 1、个人邮箱 2、电话 2),每人最多 5 cr。缩小 fields 范围可以限定最高预估成本,但不能保证封顶——曾观察到只请求一个层级,却按每人 5 cr 的上限计费并返回全部三个层级,因此请按每人 5 cr 作为最高成本估算,并读取 credits_remaining 确认实际开销
撰写、分类、编排序列免费——这些都由模型处理

一封 Hook 充分的冷邮件通常需要 3-5 个积分。包含 Hook 和工作邮箱的 40 人序列,成本约为 150-200 个积分。

配合使用

  • sales-prospecting — 建立名单并触发本技能撰写内容所需的信号。Champion-tracker 中的行是这里转化率最高的输入。
  • account-research — 在重要客户触达前深入研究客户;其客户计划会提供桥接句所需的痛点地图。
  • icp-builder — 根据一个 LinkedIn URL 撰写 config/persona-profile.md(语气)和 config/gtm-config.md(你的销售内容),让草稿从首次运行起就听起来像销售代表本人。
  • warm-path-workspace — 如果有熟人引荐的路径,就利用它:引荐开场白胜过你能写出的最佳冷邮件。

工具依赖项

此技能需要:

  • Crustdata MCP 服务(install.crustdata.com/mcp): 一个提供 list_tools、get_schema 和 execute 的 Code Mode MCP。所有 Crustdata 数据工具都通过 execute({ code }) 中的纯 JavaScript 脚本调用,使用 await callTool(name, params) ——请依据 get_schema 提供的类型化接口编写代码,但脚本主体不得包含类型注解(类型注解会导致解析错误,使整个运行失败)。此处使用的工具:social_post_list_live、company_identify、company_enrich、job_search、person_contact_enrich(选择启用并确认费用)、account_credits。
  • 除此之外不需要其他内容。 不需要序列工具凭据,不需要发送邮件权限,也不需要 CRM 写入权限。此技能的外发内容为文案、CSV 和书面营销活动规范——由销售代表加载并启动。

评论

支持 Markdown