social-connections
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSocial Connections
Social Connections(社交关系网络)
Build a ranked, evidence-backed list of the people a target plausibly knows, from a
single LinkedIn URL. You never see their real connection list. You rebuild it from
overlap (jobs, schools, labs, teams) and from first-party engagement: who reacts to,
comments on, tags, or co-authors with them.
The output is a spreadsheet (): tiered A-D, one row per person, every row
carrying the verbatim reason it is there, sliceable by where that person works today.
.xlsx根据单个 LinkedIn URL,构建一份经过排名且有证据支撑的名单,推测目标人物认识的人。你无法看到其真实联系人列表。你需要通过共同经历(工作、学校、实验室、团队)以及第一方互动来重建名单:谁对其帖子作出反应、发表评论、@ 其,或与其共同创作。
输出是一份电子表格():按 A-D 分层,每行一个人,且每行都附有说明其入选原因的逐字原文,可按这个人目前所在公司进行筛选。
.xlsxCrustdata calls
Crustdata 调用
Every Crustdata call runs through the Code Mode connector
(): plain-JavaScript scripts
calling . Every script opens with a
or comment, branches on , fans independent calls out with
, and returns only the compact projection it needs. is a
response whitelist: list every group or leaf path you read.
https://install.crustdata.com/mcpexecute({ code })await callTool(name, params)// user query: ...// model query: ...r.okparallelMapfieldsCredit costs differ by plan. Read on the first page of anything you
will repeat, multiply it out, and quote the projected total to the user before any
fan-out. (free) gives the balance.
credits_usedaccount_credits每次 Crustdata 调用都通过 Code Mode 连接器()运行:使用纯 JavaScript 的 脚本调用 。每个脚本都以 或 注释开头,根据 进行分支处理,使用 并行展开相互独立的调用,并且只返回所需的紧凑投影。 是响应白名单:列出你要读取的每一个分组或叶节点路径。
https://install.crustdata.com/mcpexecute({ code })await callTool(name, params)// user query: ...// model query: ...r.okparallelMapfieldscredits 成本因套餐而异。读取准备重复请求内容的首页上的 ,据此推算总额,并在任何并行扩展之前向用户报出预计总成本。(免费)可查询余额。
credits_usedaccount_creditsthe shape of a run
运行流程
phase 0 pull the target profile and posts (1 execute, main loop)
phase 1 derive the angle set, write profile.md + BRIEF.md
phase 2 quote the cost, then fan out one agent per angle (6-10 angles)
phase 3 merge + score (scripts/merge.py)
phase 4 resolution pass (person_enrich, 25 urls per call)
phase 5 re-merge + build the workbook (scripts/build_sheet.py)
phase 6 report, and record what was NOT covered
Phases 2 and 4 are the only expensive ones. Everything else is a script.
Parallel agents. Where you can launch subagents (the Agent tool in Claude Code),
run each angle and each resolution shard as its own agent, all launched in a single
message so they run concurrently. Where you cannot, run them one after another in
this conversation, each as one script, and halve each angle's cap. The
files, contract and scripts are the same either way.
executephase 0 拉取目标档案和帖子 (1 次 execute,主循环)
phase 1 推导角度集合,写入 profile.md + BRIEF.md
phase 2 报出成本,然后为每个角度启动一个 Agent(6-10 个角度)
phase 3 合并 + 评分 (scripts/merge.py)
phase 4 解析处理 (person_enrich,每次调用 25 个 URL)
phase 5 重新合并 + 构建工作簿 (scripts/build_sheet.py)
phase 6 报告,并记录未覆盖的内容
第 2 和第 4 阶段是仅有的高成本阶段,其余环节均由脚本完成。
并行 Agent。 如果可以启动子 Agent(Claude Code 中的 Agent 工具),则将每个角度和每个解析分片都作为独立 Agent 运行,并在单条消息中全部启动,使其并发执行。如果无法这样做,则在本对话中依次运行,每个 Agent 对应一个 脚本,并将每个角度的上限减半。无论采用哪种方式,文件、契约和脚本都相同。
executephase 0: pull the target
phase 0:拉取目标
The input must be a person profile (). If it is a company page
or anything else, stop and ask for the person's profile URL.
linkedin.com/in/...js
// user query: who does <url> know?
const url = "https://www.linkedin.com/in/<slug>"; // no trailing slash, no query string
const [p, posts] = await parallelMap([
() => callTool("person_enrich", { professional_network_profile_urls: [url],
fields: ["crustdata_person_id", "basic_profile", "professional_network", "experience", "education", "skills", "honors"] }),
() => callTool("social_post_list_live", { professional_network_profile_url: url, limit: 25,
fields: ["share_url", "backend_urn", "date_posted", "post_type", "text", "actor", "reposter", "engagement"] }),
], (f) => f());
if (!p.ok) return { error: p.message };
const row = p.data[0];
if (row.match_status !== "matched") return { status: row.match_status };
const t = row.matches[0].person_data;
const jobs = [...(t.experience?.employment_details?.current || []), ...(t.experience?.employment_details?.past || [])];
return {
person_id: t.crustdata_person_id, name: t.basic_profile?.name, headline: t.basic_profile?.headline,
connections: t.professional_network?.connections,
jobs: jobs.map((j) => pick(j, ["name", "title", "crustdata_company_id", "start_date", "end_date", "function_category", "description"])),
schools: (t.education?.schools || []).map((s) => pick(s, ["school", "degree", "field_of_study", "start_year", "end_year"])),
skills: t.skills?.professional_network_skills,
posts: posts.ok ? posts.data.posts.map((x) => ({ url: x.share_url, urn: x.backend_urn, date: x.date_posted,
type: x.post_type, author: x.actor?.name, author_url: x.actor?.professional_network_url,
author_type: x.actor?.actor_type, reposter: x.reposter?.name,
reactions: x.engagement?.total_reactions, comments: x.engagement?.total_comments,
text: (x.text || "").slice(0, 280) })) : { error: posts.message },
};A of means the person opted out: stop and tell the user,
and do not look them up another way. means ask for a different URL.
match_statusredactednot_foundRead now. It tells you how to turn this profile into an
angle set. Do not use a fixed angle list: a recruiter's graph is built from req
load, hiring cohorts and vendor account teams; an engineer's from team/lab/paper
overlap. The profile decides.
references/angles.mdCreate the run directory outside any git repo,
with , and write :
<somewhere>/<slug>-connections/raw/run.json{"name": "...", "linkedin_url": "...", "headline": "...", "connections": 500, "slug": "...", "person_id": 12345}The scripts title the report and the workbook from this file. The run directory
holds names, employers and locations for thousands of people: keep it private.
输入必须是个人档案()。如果是公司主页或任何其他类型,请停止并要求用户提供个人档案 URL。
linkedin.com/in/...js
// user query: who does <url> know?
const url = "https://www.linkedin.com/in/<slug>"; // no trailing slash, no query string
const [p, posts] = await parallelMap([
() => callTool("person_enrich", { professional_network_profile_urls: [url],
fields: ["crustdata_person_id", "basic_profile", "professional_network", "experience", "education", "skills", "honors"] }),
() => callTool("social_post_list_live", { professional_network_profile_url: url, limit: 25,
fields: ["share_url", "backend_urn", "date_posted", "post_type", "text", "actor", "reposter", "engagement"] }),
], (f) => f());
if (!p.ok) return { error: p.message };
const row = p.data[0];
if (row.match_status !== "matched") return { status: row.match_status };
const t = row.matches[0].person_data;
const jobs = [...(t.experience?.employment_details?.current || []), ...(t.experience?.employment_details?.past || [])];
return {
person_id: t.crustdata_person_id, name: t.basic_profile?.name, headline: t.basic_profile?.headline,
connections: t.professional_network?.connections,
jobs: jobs.map((j) => pick(j, ["name", "title", "crustdata_company_id", "start_date", "end_date", "function_category", "description"])),
schools: (t.education?.schools || []).map((s) => pick(s, ["school", "degree", "field_of_study", "start_year", "end_year"])),
skills: t.skills?.professional_network_skills,
posts: posts.ok ? posts.data.posts.map((x) => ({ url: x.share_url, urn: x.backend_urn, date: x.date_posted,
type: x.post_type, author: x.actor?.name, author_url: x.actor?.professional_network_url,
author_type: x.actor?.actor_type, reposter: x.reposter?.name,
reactions: x.engagement?.total_reactions, comments: x.engagement?.total_comments,
text: (x.text || "").slice(0, 280) })) : { error: posts.message },
};match_statusredactednot_found现在读取 。该文件说明了如何根据此档案构建角度集合。不要使用固定的角度列表:招聘人员的关系图谱应基于职位需求负载、招聘批次和供应商客户团队;工程师的关系图谱则应基于团队、实验室和论文的重合关系。具体采用哪些角度由档案决定。
references/angles.md在任意 git 仓库之外创建运行目录 ,并在其中创建 ,然后写入 :
<somewhere>/<slug>-connections/raw/run.json{"name": "...", "linkedin_url": "...", "headline": "...", "connections": 500, "slug": "...", "person_id": 12345}脚本会根据此文件为报告和工作簿命名。该运行目录会保存数千人的姓名、雇主和地点,请确保其保密。
phase 1: profile.md and BRIEF.md
phase 1:profile.md 和 BRIEF.md
profile.mdcrustdata_company_idactor/in/post_type| post_type | whose engagement | use it |
|---|---|---|
| the target's | mine reactors and commenters |
| mixed: the target's comment on someone else's post | mine commenters, read who they address |
| the original author's audience | skip |
A person's feed can include their employer's company-page posts typed :
the actor is , and the reactors are the company's audience. Those are
contaminated whatever their . Tell agents to re-check your
classification and overrule you.
original/company/...post_typeResolve every employer to a now with
(free), and verify each id returns people you expect. An agent that has to
rediscover an id burns calls and sometimes gets it wrong.
crustdata_company_idcompany_identifyBRIEF.mdreferences/output-contract.mdreferences/crustdata-traps.mdcrustdata_person_idprofile.mdprofile.mdcrustdata_company_idactor/in/post_type| post_type | 互动主体 | 用途 |
|---|---|---|
| 目标本人 | 挖掘互动者和评论者 |
| 混合:目标在他人帖子下发表的评论 | 挖掘评论者,并查看评论所指向的人 |
| 原作者受众 | 跳过 |
某个人的信息流可能包含其雇主公司主页发布的帖子,且 可能为 :此时 是 ,互动者是该公司的受众。无论 是什么,这些数据都已被污染。告诉 Agent 重新检查你的分类,并在必要时推翻你的判断。
post_typeoriginalactor/company/...post_type现在使用 (免费)将每个雇主解析为 ,并验证每个 ID 都能返回符合预期的人员。让 Agent 重新查找 ID 会浪费调用次数,而且有时还会得到错误结果。
company_identifycrustdata_company_idBRIEF.mdreferences/output-contract.mdreferences/crustdata-traps.mdcrustdata_person_idprofile.mdphase 2: quote the cost, then fan out the angles
phase 2:报出成本,然后扩展各个角度
Before launching anything, size every angle's pool. rejects
; size it with and , which
returns and bills one result row. Sizing is not free on every plan,
so size only the angles you mean to run, and count its cost in the quote.
counts for free with . Then run the first page of
one representative angle and read . Tell the user the angle list,
each angle's cap, and the projected spend, and wait for a yes.
person_searchlimit: 0limit: 1fields: ["crustdata_person_id"]total_countsocial_post_searchlimit: 0credits_usedThen launch one agent per angle. 6-10 angles depending on career length. Where you
can pick the subagent model, a fast one is enough for gathering.
Each agent prompt states: the run dir, "read BRIEF.md and profile.md first", its
angle slug, the specific orgs/schools/windows/company_ids it owns, what counts as
strong vs medium vs weak for that angle, and its record cap.
Rules that go in every prompt:
- every row needs a named reason. Do not pad an angle with bare company matches
- capture where each person is NOW (current_company, current_title), including for people who have left the shared employer. The workbook is sliced on this.
- never assert a tie you cannot name; evidence is verbatim from the data
- page with , or use
next_cursorfor a right-sized pool that runs past a few pages; never re-run a search to get more of itbatch_person_search - write and
raw/<slug>.jsonraw/<slug>.log.md - a large result may be saved to a file instead of shown inline; parse that file with a script rather than reading it into context
execute
The first-party engagement angle starts with free counts from the post index. Posts
by, commenting on, and tagging the target are keyed on their ,
always paired with the actor type (see trap 2):
crustdata_person_idjs
// model query: size the first-party engagement angle for the target
const id = inputs.person_id;
const q = {
commented_on: and_(eq("comments.crustdata_entity_id", id), eq("comments.actor_type", "person")),
reacted_to: and_(eq("reactors.crustdata_entity_id", id), eq("reactors.actor_type", "person")),
tagged_in: and_(eq("mentions.person_id", id), eq("actor.actor_type", "person")),
};
const out = await parallelMap(Object.entries(q), async ([k, filters]) => {
const r = await callTool("social_post_search", { filters, limit: 0 });
return [k, r.ok ? r.data.total_count : { status: r.status, error: r.message }];
});
return Object.fromEntries(out);Expect to carry this. The index keeps commenter and reactor lists for
only a small share of posts, so and are often 0 even
for an active poster; a 0 there is a gap to record in coverage, not a finding.
tagged_incommented_onreacted_toFor posts the target commented on, pull and keep, per post, the author plus the target's own comment:
, quoting its
. For the target's own posts, take reactor and commenter lists from
by with / in
and / set (trap 12). Reactors carry
and a object; commenters a object and .
fields: ["share_url", "date_posted", "actor", "comments"]comments.filter((c) => c.commenter?.crustdata_entity_id === id)comment_textsocial_post_list_livesocial_post_urlreactorscommentsfieldsmax_reactorsmax_commentsreaction_typereactorcommentercomment_textWhile agents run, do not idle-poll. Write , check imports
(, else ), and merge partial
results to shake out bugs early.
run.jsonopenpyxlpython3 -c "import openpyxl"pip install openpyxlRelay corrections mid-flight. When one agent discovers a trap, message it to the
agents still running that are exposed to it. Re-running one bucket of a finished
agent is cheap and often finds rows the first pass missed.
启动任何任务之前,先估算每个角度对应的候选池规模。 拒绝 ;应使用 和 进行规模探测,这会返回 ,并按一条结果记录计费。并非所有套餐都能免费进行规模探测,因此只探测你准备实际运行的角度,并将探测成本计入报价。 可以使用 免费获取计数。然后运行一个代表性角度的第一页,并读取 。向用户说明角度列表、各角度的上限和预计消耗,然后等待用户确认。
person_searchlimit: 0limit: 1fields: ["crustdata_person_id"]total_countsocial_post_searchlimit: 0credits_used随后为每个角度启动一个 Agent。根据职业经历长度设置 6-10 个角度。如果可以选择子 Agent 模型,采集任务使用快速模型即可。
每个 Agent 的提示词应说明:运行目录、“先读取 BRIEF.md 和 profile.md”、其角度 slug、负责的特定组织、学校、时间段和 company IDs、该角度下何谓强、中等、弱证据,以及记录上限。
每个提示词都必须包含以下规则:
- 每一行都需要一个可明确命名的理由。不要用仅匹配公司的记录填充某个角度
- 记录每个人目前在哪里,包括其当前公司 和职位
current_company,即使此人已经离开共同雇主。工作簿将据此进行筛选current_title - 绝不要断言无法明确指认的关系;证据必须逐字取自数据
- 使用 翻页,或者对超过数页且规模合适的候选池使用
next_cursor;绝不要为了获得更多结果而重新运行同一次搜索batch_person_search - 写入 和
raw/<slug>.jsonraw/<slug>.log.md - 大型 结果可以保存到文件而不是直接显示;应通过脚本解析该文件,而不是将其读入上下文
execute
第一方互动角度从帖子索引中的免费计数开始。目标发布、评论以及 @ 目标的帖子均以其 为键,并且始终要与 actor type 配对(参见陷阱 2):
crustdata_person_idjs
// model query: size the first-party engagement angle for the target
const id = inputs.person_id;
const q = {
commented_on: and_(eq("comments.crustdata_entity_id", id), eq("comments.actor_type", "person")),
reacted_to: and_(eq("reactors.crustdata_entity_id", id), eq("reactors.actor_type", "person")),
tagged_in: and_(eq("mentions.person_id", id), eq("actor.actor_type", "person")),
};
const out = await parallelMap(Object.entries(q), async ([k, filters]) => {
const r = await callTool("social_post_search", { filters, limit: 0 });
return [k, r.ok ? r.data.total_count : { status: r.status, error: r.message }];
});
return Object.fromEntries(out);预计 的计数会最大。索引仅为一小部分帖子保留评论者和互动者列表,因此即使目标非常活跃, 和 也经常为 0。这里的 0 应作为覆盖缺口记录,而不是一项发现。
tagged_incommented_onreacted_to对于目标评论过的帖子,提取 ,并针对每篇帖子保留作者以及目标自己的评论:,同时引用其 。对于目标自己的帖子,通过 调用 ,并在 中加入 /,同时设置 /(陷阱 12),以获取互动者和评论者列表。互动者包含 和 对象;评论者则包含 对象和 。
fields: ["share_url", "date_posted", "actor", "comments"]comments.filter((c) => c.commenter?.crustdata_entity_id === id)comment_textsocial_post_urlsocial_post_list_livefieldsreactorscommentsmax_reactorsmax_commentsreaction_typereactorcommentercomment_textAgent 运行期间,不要进行空轮询。写入 ,检查 是否可导入(,否则执行 ),并合并部分结果,以便提前发现并解决问题。
run.jsonopenpyxlpython3 -c "import openpyxl"pip install openpyxl在运行过程中转发修正。 当一个 Agent 发现某个陷阱时,将修正信息发送给仍在运行且可能受其影响的 Agent。重新运行已完成 Agent 中的一个分组成本较低,而且通常能找出首轮遗漏的记录。
phase 3: merge + score
phase 3:合并与评分
python3 scripts/merge.py <run-dir>
Dedupes by LinkedIn identity (a bare name only dedupes within one angle), merges evidence, caps each angle's
contribution to its best two items so a chatty angle cannot inflate a row, and
tiers. See to tune the bands.
references/scoring.mdA person seen once by vanity url and once by urn url stays two rows until phase 4
resolves the urn, so expect some duplicates at this stage.
Sanity-check before continuing: top tier-A rows should be people hit by 3-4
independent angles. If tier A is dominated by single-angle rows, an angle is
over-claiming strength.
python3 scripts/merge.py <run-dir>
该脚本按 LinkedIn 身份去重(仅凭姓名只能在单个角度内去重)、合并证据、将每个角度对同一行的贡献限制为其最强的两项证据,以免互动频繁的角度夸大结果,然后进行分层。参见 调整分层区间。
references/scoring.md如果同一个人一次以 vanity URL 出现、另一次以 urn URL 出现,在 phase 4 解析该 urn 之前会保留为两行,因此在此阶段预期会出现一些重复项。
继续前进行合理性检查:顶层 tier A 记录应当是同时被 3-4 个独立角度命中的人员。如果 tier A 大部分都是仅由单一角度命中的记录,则说明某个角度过度声称了证据强度。
phase 4: the resolution pass
phase 4:解析处理
Required if the output must be sliceable by employer. Engagement rows arrive as urn
urls with no employer, and most rows need this step.
python3 scripts/shard_resolve.py <run-dir> --shards 4Each shard is one script: 25 urls per call, fanned out
with , joined on . Pass the shard's list as
, and write the returned array to .
executeperson_enrichparallelMapmatched_ontodo_N.jsoninputs.urlsraw/resolve/done_N.jsonjs
// model query: resolve one shard of urn-only rows to profile + current employer
const out = await parallelMap(chunk(inputs.urls, 25), async (batch) => {
const r = await callTool("person_enrich", { professional_network_profile_urls: batch,
fields: ["basic_profile.name", "basic_profile.headline", "basic_profile.location",
"social_handles.professional_network_identifier.profile_url", "experience.employment_details.current"] });
if (!r.ok) return batch.map((u) => ({ query_url: u, error: r.message }));
return r.data.map((row) => {
if (row.match_status === "redacted") return { query_url: row.matched_on, redacted: true };
const p = row.matches?.[0]?.person_data;
if (!p) return { query_url: row.matched_on, status: row.match_status };
const cur = p.experience?.employment_details?.current || [];
const job = cur.find((j) => j.is_default) || cur[0] || {};
return { query_url: row.matched_on, resolved_url: p.social_handles?.professional_network_identifier?.profile_url,
name: p.basic_profile?.name, headline: p.basic_profile?.headline, location: p.basic_profile?.location?.raw,
current_company: job.name || "", current_title: job.title || "" };
});
});
return out.flat();Check the result before writing it: one row per url in the shard, and every
present in . One malformed url fails its whole batch with
a 400 that names it: drop that url and re-run only the rows that came back with
. Expect most rows to resolve; the rest are real gaps. For a long queue, add
shards rather than making one shard bigger.
query_urltodo_N.jsonerror如果输出必须能够按雇主筛选,则必须执行此阶段。互动记录以没有雇主信息的 urn URL 到达,而且大多数记录都需要此步骤。
python3 scripts/shard_resolve.py <run-dir> --shards 4每个分片对应一个 脚本:每次 调用处理 25 个 URL,使用 并行展开,并通过 关联。将分片的 列表作为 传入,并将返回的数组写入 。
executeperson_enrichparallelMapmatched_ontodo_N.jsoninputs.urlsraw/resolve/done_N.jsonjs
// model query: resolve one shard of urn-only rows to profile + current employer
const out = await parallelMap(chunk(inputs.urls, 25), async (batch) => {
const r = await callTool("person_enrich", { professional_network_profile_urls: batch,
fields: ["basic_profile.name", "basic_profile.headline", "basic_profile.location",
"social_handles.professional_network_identifier.profile_url", "experience.employment_details.current"] });
if (!r.ok) return batch.map((u) => ({ query_url: u, error: r.message }));
return r.data.map((row) => {
if (row.match_status === "redacted") return { query_url: row.matched_on, redacted: true };
const p = row.matches?.[0]?.person_data;
if (!p) return { query_url: row.matched_on, status: row.match_status };
const cur = p.experience?.employment_details?.current || [];
const job = cur.find((j) => j.is_default) || cur[0] || {};
return { query_url: row.matched_on, resolved_url: p.social_handles?.professional_network_identifier?.profile_url,
name: p.basic_profile?.name, headline: p.basic_profile?.headline, location: p.basic_profile?.location?.raw,
current_company: job.name || "", current_title: job.title || "" };
});
});
return out.flat();写入前检查结果:分片中的每个 URL 都应对应一行,并且 中的每个 都必须出现在结果中。一个格式错误的 URL 会导致整个批次返回带有所错 URL 的 400 错误:删除该 URL,并且只重新运行返回 的那些记录。预期大多数记录都能成功解析;其余记录属于真实缺口。对于较长的队列,应增加分片数量,而不是将单个分片做得更大。
todo_N.jsonquery_urlerrorphase 5: re-merge and ship
phase 5:重新合并并交付
python3 scripts/merge.py <run-dir> # folds in raw/resolve/done_*.json
python3 scripts/build_sheet.py --run-dir <run-dir> --dry-run
python3 scripts/build_sheet.py --run-dir <run-dir>
Writes in the run dir. Tabs: summary, all people, by
employer, tier A/B/C/D, coverage. Every people tab has a filter and a frozen header;
is the precomputed employer x tier pivot. Re-running overwrites it.
<slug>-connections.xlsxby employerAsk the user whether tier D belongs in the workbook. On a recruiter-shaped target it
can be thousands of rows of staffing noise; pass to leave it out (it
stays in ). On an engineer-shaped target it is usually worth keeping.
--no-tier-dfinal.jsonTo use it in Google Sheets: upload the file to Google Drive and open it with Sheets;
the tabs and filters carry over.
python3 scripts/merge.py <run-dir> # folds in raw/resolve/done_*.json
python3 scripts/build_sheet.py --run-dir <run-dir> --dry-run
python3 scripts/build_sheet.py --run-dir <run-dir>
脚本会在运行目录中写入 。标签页包括:summary、all people、by employer、tier A/B/C/D、coverage。每个 people 标签页都有筛选功能和冻结标题行; 是预先计算的 employer × tier 数据透视表。重新运行会覆盖现有文件。
<slug>-connections.xlsxby employer询问用户 tier D 是否应包含在工作簿中。当目标的画像属于招聘人员类型时,该层可能包含数千行与人员配置噪声相关的数据;传入 可将其排除(它仍会保留在 中)。当目标的画像属于工程师类型时,通常值得保留。
--no-tier-dfinal.json如需在 Google Sheets 中使用:将文件上传到 Google Drive,然后使用 Sheets 打开;各标签页和筛选功能都会保留。
phase 6: report
phase 6:报告
Lead with the workbook path and the tier table. Then the employer pivot, since that
is usually what the request is really about. Then the top of tier A with the reason
each person is there. Then the credits spent. Close with what was NOT covered,
specifically: skipped posts, partially-paged sources, unresolved names, any angle
you capped, and any tool that returned 403.
Write the traps you hit to in the run dir.
README.md首先给出工作簿路径和分层表。然后给出雇主数据透视表,因为这通常才是用户请求的核心。接着列出 tier A 的顶部人员及每个人被收录的原因。然后报告已消耗的 credits。最后明确说明未覆盖的内容,包括:跳过的帖子、仅部分翻页的数据源、未解析的姓名、设置过上限的任何角度,以及任何返回 403 的工具。
将遇到的陷阱写入运行目录中的 。
README.mdwhen a call fails
调用失败时
- is false with 403: that tool or field is not on the account's plan. Skip that part, record it in coverage, and keep going. Do not reshape the request to get around it.
r.ok - 429: wait () and retry that call only.
await sleep(ms) - A run that times out returns a with every call already paid for. Pass it back as
partialand skip the finished stages instead of paying twice.inputs - Zero results: say so in the angle log. Do not pad the angle to make up for it.
- 为 false 且状态为 403:该工具或字段不在账户套餐中。跳过该部分,将其记录到 coverage 中并继续执行。不要改变请求结构来绕过限制
r.ok - 429:等待(),并且只重试该次调用
await sleep(ms) - 运行超时时,会返回带有所有已付费调用的 。将其作为
partial传回,并跳过已经完成的阶段,避免重复付费inputs - 结果为零:在角度日志中明确说明。不要为了凑数而填充该角度
references
参考资料
- - deriving the angle set from any profile shape
references/angles.md - - the record schema every agent must emit
references/output-contract.md - - strength, score, tier bands
references/scoring.md - - the flaws that cost real rows, paste into BRIEF.md
references/crustdata-traps.md - - structural checks for the scripts, no network
evals/test_merge.py
- ——根据任意档案形态推导角度集合
references/angles.md - ——每个 Agent 必须输出的记录架构
references/output-contract.md - ——证据强度、分数和分层区间
references/scoring.md - ——会实际造成数据缺失的缺陷,应粘贴到 BRIEF.md 中
references/crustdata-traps.md - ——针对脚本的结构检查,不使用网络
evals/test_merge.py