Loading...
Loading...
Compare original and translation side by side
config/persona-profile.mdconfig/gtm-config.mdconfig/persona-profile.mdconfig/gtm-config.mdconfig/gtm-config.mdconfig/persona-profile.mdconfig/gtm-config.mdconfig/persona-profile.mdnonenonenonenonenonenoneexecuteparallelMap// user query: ...// model query: ...// user query: set up my GTM config — my LinkedIn is https://www.linkedin.com/in/example
const url = "https://www.linkedin.com/in/example";
// Stage 1: the person. Base cost 1 credit. `fields` is a response WHITELIST —
// the result carries ONLY the groups listed here; an omitted group reads as
// undefined later and looks like missing data. basic_profile + experience covers
// the persona; social_handles carries the canonical profile URL the posts pull
// is keyed on; contact groups only add cost.
const pr = await callTool("person_enrich", {
professional_network_profile_urls: [url],
fields: ["basic_profile", "experience", "social_handles"],
});
if (!pr.ok) return { error: pr.message };
const person = pr.data[0]?.matches?.[0]?.person_data;
if (!person) return { error: "no_match" }; // → confirm the URL, then no-data fallback
const canonicalUrl = profileUrl(person) ?? url; // preloaded accessor
const current = person.experience?.employment_details?.current?.[0] ?? {};
const companyId = currentCompanyIds(person)[0]; // preloaded accessor
// Stage 2: company + posts are independent of each other — fan them out.
const calls = [
{ name: "social_post_list_live",
params: { professional_network_profile_url: canonicalUrl, limit: 10 } }, // 1 cr/post — cap deliberately
];
if (companyId) {
calls.push({ name: "company_enrich",
params: { crustdata_company_ids: [companyId], exact_match: true,
fields: ["basic_info", "taxonomy"] } }); // 2 cr, exactly one match
}
const results = await parallelMap(calls, async (c) => ({ name: c.name, r: await callTool(c.name, c.params) }));
const postsR = results.find(x => x.name === "social_post_list_live")?.r;
const companyR = results.find(x => x.name === "company_enrich")?.r;
// Posts are optional: a failed or empty pull means neutral voice, not a failed run.
const posts = postsR && postsR.ok
? (postsR.data.posts ?? []).map(p => ({
text: p.text,
date: p.date_posted,
reactions: p.engagement?.total_reactions,
comments: p.engagement?.total_comments,
}))
: [];
const company = companyR && companyR.ok
? pick(companyR.data[0]?.matches?.[0]?.company_data ?? {}, ["basic_info", "taxonomy"])
: null;
// Return the smallest projection — only what the script returns reaches the model.
return {
identity: {
name: person.basic_profile?.name,
title: person.basic_profile?.current_title,
location: person.basic_profile?.location,
company: current.name,
company_domain: current.company_website_domain,
start_date: current.start_date, // tenure = today minus this
},
past_roles: (person.experience?.employment_details?.past ?? []).slice(0, 5)
.map(e => ({ company: e.name, title: e.title })),
company,
posts,
};preview: trueperson_enrichperson_enrichbasic_profileexperiencesocial_handlessocial_handlesprofileUrlundefinedcertificationshonorsupdated_atbasic_profile.current_titleexperience.employment_details.current[].namesocial_handles.professional_network_identifier.profile_urlprofileUrlexecuteparallelMap// user query: ...// model query: ...// user query: set up my GTM config — my LinkedIn is https://www.linkedin.com/in/example
const url = "https://www.linkedin.com/in/example";
// Stage 1: the person. Base cost 1 credit. `fields` is a response WHITELIST —
// the result carries ONLY the groups listed here; an omitted group reads as
// undefined later and looks like missing data. basic_profile + experience covers
// the persona; social_handles carries the canonical profile URL the posts pull
// is keyed on; contact groups only add cost.
const pr = await callTool("person_enrich", {
professional_network_profile_urls: [url],
fields: ["basic_profile", "experience", "social_handles"],
});
if (!pr.ok) return { error: pr.message };
const person = pr.data[0]?.matches?.[0]?.person_data;
if (!person) return { error: "no_match" }; // → confirm the URL, then no-data fallback
const canonicalUrl = profileUrl(person) ?? url; // preloaded accessor
const current = person.experience?.employment_details?.current?.[0] ?? {};
const companyId = currentCompanyIds(person)[0]; // preloaded accessor
// Stage 2: company + posts are independent of each other — fan them out.
const calls = [
{ name: "social_post_list_live",
params: { professional_network_profile_url: canonicalUrl, limit: 10 } }, // 1 cr/post — cap deliberately
];
if (companyId) {
calls.push({ name: "company_enrich",
params: { crustdata_company_ids: [companyId], exact_match: true,
fields: ["basic_info", "taxonomy"] } }); // 2 cr, exactly one match
}
const results = await parallelMap(calls, async (c) => ({ name: c.name, r: await callTool(c.name, c.params) }));
const postsR = results.find(x => x.name === "social_post_list_live")?.r;
const companyR = results.find(x => x.name === "company_enrich")?.r;
// Posts are optional: a failed or empty pull means neutral voice, not a failed run.
const posts = postsR && postsR.ok
? (postsR.data.posts ?? []).map(p => ({
text: p.text,
date: p.date_posted,
reactions: p.engagement?.total_reactions,
comments: p.engagement?.total_comments,
}))
: [];
const company = companyR && companyR.ok
? pick(companyR.data[0]?.matches?.[0]?.company_data ?? {}, ["basic_info", "taxonomy"])
: null;
// Return the smallest projection — only what the script returns reaches the model.
return {
identity: {
name: person.basic_profile?.name,
title: person.basic_profile?.current_title,
location: person.basic_profile?.location,
company: current.name,
company_domain: current.company_website_domain,
start_date: current.start_date, // tenure = today minus this
},
past_roles: (person.experience?.employment_details?.past ?? []).slice(0, 5)
.map(e => ({ company: e.name, title: e.title })),
company,
posts,
};person_enrichpreview: trueperson_enrichbasic_profileexperiencesocial_handlessocial_handlesprofileUrlundefinedcertificationshonorsupdated_atbasic_profile.current_titleexperience.employment_details.current[].namesocial_handles.professional_network_identifier.profile_urlprofileUrlcompany_identifyconfidence_scoreexact_match: true// model query: resolve and enrich the user's current company by domain
const idr = await callTool("company_identify", { domains: ["example.com"] }); // ONE identifier type per call
if (!idr.ok) return { error: idr.message };
const matches = idr.data[0]?.matches ?? [];
const top = matches.slice().sort((a, b) => (b.confidence_score ?? 0) - (a.confidence_score ?? 0))[0];
if (!top) return { error: "no_company_match" };
const id = top.company_data?.basic_info?.crustdata_company_id ?? top.company_data?.crustdata_company_id;
const er = await callTool("company_enrich", {
crustdata_company_ids: [id],
exact_match: true,
fields: ["basic_info", "taxonomy"],
});
if (!er.ok) return { error: er.message };
return pick(er.data[0]?.matches?.[0]?.company_data ?? {}, ["basic_info", "taxonomy"]);social_profilescompany_identifycompany_identifyconfidence_scorecompany_enrichexact_match: true// model query: resolve and enrich the user's current company by domain
const idr = await callTool("company_identify", { domains: ["example.com"] }); // ONE identifier type per call
if (!idr.ok) return { error: idr.message };
const matches = idr.data[0]?.matches ?? [];
const top = matches.slice().sort((a, b) => (b.confidence_score ?? 0) - (a.confidence_score ?? 0))[0];
if (!top) return { error: "no_company_match" };
const id = top.company_data?.basic_info?.crustdata_company_id ?? top.company_data?.crustdata_company_id;
const er = await callTool("company_enrich", {
crustdata_company_ids: [id],
exact_match: true,
fields: ["basic_info", "taxonomy"],
});
if (!er.ok) return { error: er.message };
return pick(er.data[0]?.matches?.[0]?.company_data ?? {}, ["basic_info", "taxonomy"]);company_identifysocial_profilesstart_datebasic_infotaxonomystart_datebasic_infotaxonomyinferred// model query: resolve filter-ready values for the inferred ICP
const probes = [
{ tool: "company_autocomplete", params: { field: "basic_info.industries", query: "software" } },
{ tool: "person_autocomplete", params: { field: "experience.employment_details.current.seniority_level", query: "vice" } },
];
return await parallelMap(probes, async (p) => {
const r = await callTool(p.tool, p.params);
// Returns shape is { suggestions: [{ value }] } — project to the value strings.
return { field: p.params.field, values: r.ok ? (r.data.suggestions ?? []).map(s => s.value) : [], error: r.ok ? null : r.message };
});experience.employment_details.current.seniority_levelEntry LevelEntry Level ManagerExperienced ManagerSeniorDirectorVice PresidentCXOOwner / PartnerIn TrainingStrategicperson_autocompleteinferred// model query: resolve filter-ready values for the inferred ICP
const probes = [
{ tool: "company_autocomplete", params: { field: "basic_info.industries", query: "software" } },
{ tool: "person_autocomplete", params: { field: "experience.employment_details.current.seniority_level", query: "vice" } },
];
return await parallelMap(probes, async (p) => {
const r = await callTool(p.tool, p.params);
// Returns shape is { suggestions: [{ value }] } — project to the value strings.
return { field: p.params.field, values: r.ok ? (r.data.suggestions ?? []).map(s => s.value) : [], error: r.ok ? null : r.message };
});experience.employment_details.current.seniority_levelEntry LevelEntry Level ManagerExperienced ManagerSeniorDirectorVice PresidentCXOOwner / PartnerIn TrainingStrategicperson_autocompleteinferredinferredconfig/persona-profile.mdconfig/gtm-config.mdconfig/persona-profile.mdconfig/gtm-config.mdconfig/persona-profile.mdconfig/persona-profile.mdundefinedundefinedundefinedundefinedconfig/gtm-config.mdconfig/gtm-config.mdundefinedundefinednonenoneundefinedundefinedYou're set up. Try sales-prospecting ("build me a list from my ICP") or account-research ("research <company>") — both read this config automatically.
配置已完成。您可以尝试销售探矿(“根据我的ICP构建客户列表”)或客户研究(“研究<公司>”)——这两项技能会自动读取此配置。
inferredinferredperson_enrichbasic_profileexperiencesocial_handlescompany_identifycompany_autocompleteperson_autocompletecompany_enrichexact_match: truesocial_post_list_livelimitexecutecreditscredits_remainingaccount_creditsperson_enrichbasic_profileexperiencesocial_handlescompany_identifycompany_autocompleteperson_autocompletecompany_enrichexact_match: truesocial_post_list_livelimitexecutecreditscredits_remainingaccount_creditsr.okperson_enrichr.okperson_enrichnonenonecurrentColorbasic_profile.profile_picture_permalinkbasic_profileperson_enrichbinary/octet-streambasic_info.logo_permalinkcompany_identifycompany_enrichdata:image/jpeg;base64,...binary/octet-stream<img src>assets/crustdata-logo-light.pngcrustdata-logo-dark.png#5547E2#8387FFnonenonecurrentColorbasic_profile.profile_picture_permalinkperson_enrichbasic_profilebinary/octet-streambasic_info.logo_permalinkcompany_identifycompany_enrichdata:image/jpeg;base64,...binary/octet-stream<img src>assets/crustdata-logo-light.pngcrustdata-logo-dark.png#5547E2#8387FFlist_toolsget_schemaexecuteexecute({ code })await callTool(name, params)get_schemaperson_enrichcompany_identifycompany_enrichsocial_post_list_livecompany_autocompleteperson_autocompleteaccount_creditsconfig/persona-profile.mdconfig/gtm-config.mdlist_toolsget_schemaexecuteexecute({ code })await callTool(name, params)get_schemaperson_enrichcompany_identifycompany_enrichsocial_post_list_livecompany_autocompleteperson_autocompleteaccount_creditsconfig/persona-profile.mdconfig/gtm-config.md