talent-search — Technical Headhunting Talent Search
Positioning
Full-web talent sourcing for AI / infra / quant roles. Input is a candidate persona or a JD; outputs are:
- Excel candidate list (
talent-research/<topic>/candidates-YYYYMMDD.xlsx) - Search log (
search-log.mdin the same folder; template in Step 3) - Candidate library (
talent-research/_db/candidates.jsonl, accumulates across tasks — every round queries the library first for incremental updates, so it gets thicker with use) - Lessons file (
talent-research/_lessons/YYYY-MM.md, appended after every retrospective — source effectiveness / rejection patterns / network pitfalls / process improvements; the skill improves itself through use)
Host adaptation
This skill is tool-agnostic. Adapt it to the host agent as follows:
- Web search: use the host's built-in search/fetch tools (WebSearch / WebFetch or equivalent). Paper search can hit arXiv API, Semantic Scholar API, OpenAlex API directly; the GitHub API can be requested directly.
- Sites requiring login (Maimai, LinkedIn, Jike, Xiaohongshu, etc.): use a locally available browser-automation skill (e.g. agent-browser) or an equivalent tool, with the account logged in compliantly.
- Proxies: if outbound access times out, configure
https_proxyper your network; most.edu.cnsites actually work better without a proxy. - Node.js runtime:
scripts/gen_excel.mjsis zero-dependency and runs on Node.js ≥ 18. - Sub-agents: if the host supports parallel sub-agents (an Agent tool), dispatch the channels of Step 1 in parallel — max 2 at a time (see Step 1 discipline); otherwise run channels serially on the main thread.
- Environment specifics (mirrors, git proxies, long-task execution on Windows):
references/field-notes.md§3.
Step 0: Parse the persona, lock the search parameters
Read the persona / JD the user provided (it may be a file path, a benchmark-persona markdown, or plain text). Extract the parameters below; ask the user once about anything ambiguous, make reasonable inferences where possible and record them in the search log:
| Parameter | Notes |
|---|---|
| Direction keywords | Bilingual, e.g. "LLM pretraining data / pretraining data curation", "quant strategy / quant research" |
| School hard bar | E.g. China C9 / Hong Kong / overseas (tier table in references/school-list.md); degree level (PhD / Master / Bachelor) |
| Graduation window | E.g. "PhD 2021–2023, 3–5 years in industry" |
| Target companies / teams | The pool the candidate's current or past role should come from (frontier LLM labs, top quant funds, etc.) |
| Location constraint | Default none; the user may restrict geography (e.g. "only candidates currently working in China"). Then the search must verify current work location; anyone outside the region is dropped and logged as "rejected (location)" |
| Bonus / exclusion items | E.g. "vision-to-LLM data", "Chinese-speaking candidates only", "exclude pure academia" |
| Target count | Default 20; user-adjustable |
Benchmark-persona format example: examples/persona.example.md (three sections: resume facts, persona traits, search-target profile). If the user gave no file, draft a persona in that structure first, have the user glance at it, then search.
0.1 If the input is a JD: translate must-haves into retrievable signals
Read references/sourcing-plan.md. For each must-have, split it into a weak signal (resume keywords anybody can write) and a strong signal (commit records, conference authorship, blog bylines — expensive to fake). Put the weight of the whole search on strong signals; resume keywords are never a screening basis.
Also write the exclusion items into every layer: the boolean queries (NOT <company>), the company map, the screening rubric (one-strike veto) and the final list. An exclusion noted only in the intro gets lost by step 2.
0.2 Sanity-check the graduation-window constraint before promising a number
"Graduated within N years" is structurally at odds with "core contributor of a major framework" — people who carry a Megatron/DeepSpeed-level codebase usually have 5–10 years of runway, and graduation year is not a public record (only academics with a CV page publish it; enterprise engineers almost never do).
→ Before accepting the order, ask: is "inferred + confirm on the call" acceptable? Otherwise the usable pool collapses. See references/github-playbook.md §8.
0.3 Declare the delivery tiers up front
Agree with the user that output is tiered, not padded to a headcount (see Step 2.4):
- A — every hard constraint backed by a primary source
- B — contribution and identity verified, graduation year given as an inferred range and labelled
- C — rejected, with the reason
Incremental library check (mandatory if a library exists): if talent-research/_db/candidates.jsonl exists, query it before casting the net:
node scripts/db_upsert.mjs talent-research/_db/candidates.jsonl search <direction keyword / company / name>
Turn this round into an incremental update by these rules:
- In library with
last_verified≤ 90 days ago → reuse directly (only fill missing fields); do not re-search the whole web last_verifiedmore than 90 days ago (search results carry a ⚠ stale flag) → re-verify only current role and work locationstatusisrejected(with reason recorded) → skip this round unless the user names them for review- Focus this round's four-channel firepower on library gaps: new directions, new companies, new graduation cohorts
Read recent lessons: if talent-research/_lessons/ exists, read this month's and last month's lesson files (source effectiveness, rejection patterns, network pitfalls, process improvements) and tune channel priorities and query combinations accordingly. If a rejection pattern is relevant to the current persona, proactively flag it to the user (e.g. "last month 8/12 rejects in this direction failed the school bar — should we adjust the bar this round?").
Step 1: Four-channel parallel search
First read references/source-playbook.md (channel strategy + contact-collection rules), then dispatch 4 sub-agents, one per channel:
- Academia channel: paper authors, research groups, advisor lab pages, competition winners
- GitHub channel: contributors to relevant open-source projects, authors of official paper implementations — read
references/github-playbook.mdandreferences/repo-map.mdfirst, pick 3–5 repos aligned to the must-haves - Company channel: target companies' AI / research team pages, tech-blog authors, corporate open-source org members — if the company is unfamiliar, run
references/company-recon.mdfirst to check whether it has anyone worth poaching - Community channel: Zhihu / WeChat official accounts / tech forums / public LinkedIn pages (the main channel for Chinese-speaking candidate leads) — site tactics in
references/field-notes.md§2
If the user hands you an official roster page (university HPC team, lab people page, competition team), switch to references/roster-verify.md instead of the four channels — it is a different pipeline (scrape roster → reverse-lookup handles → verify each identity → confidence-tiered table).
Sub-agent discipline (learned the hard way)
- Dispatch at most 2 sub-agents in parallel. Four at once trips rate limits (429) and one channel will emit garbage. Run the four channels in two batches.
- A sub-agent may fail entirely (502 / DNS / never starts). Do not retry in a loop — two channels yielding 20+ leads already supports a 10-person shortlist; fill gaps from the main thread.
- Main-thread verification is not rate-limited and is the reliable fallback.
- Sub-agent output is a lead, not a conclusion. Search tools and sub-agents demonstrably cross-contaminate people: A's paper shows up under B's name, a fetch returns a fake 404 for a URL that exists, one result interleaves unrelated page text. Every attribution must be re-pulled on the main thread, per person, before it goes in the table. If a single result contains unrelated content, demote the whole result.
- Prompts are goal-oriented ("find", "research", "learn"), never prescribing the mechanism.
- Sub-agents return card lists only, never write files.
Sub-agent prompt template (substitute the host's actual tool names):
You are a talent-search sub-agent. Task: from the [<channel>] channel, find candidate leads for the persona below.
Persona: <direction keywords / school bar / graduation window / target companies / bonus-exclusion items / location constraint if any>
EXCLUSIONS (hard): <exclusion items> — a lead hitting any exclusion is discarded on sight.
Strong signals preferred: commit records in core directories, first-author conference papers, signed tech-blog bylines.
Resume keywords alone are weak signals and do not count.
Use the host's web-search tools (e.g. WebSearch/WebFetch) to search public information; if browser operations are needed
(sites requiring login, e.g. Maimai, LinkedIn), load a locally available browser-automation skill and follow its guidance.
Requirements:
- Stop after 8–15 candidate leads; do not chase exhaustiveness
- Better to miss than to be wrong: a name that matches but has no school/company/direction signal is "not found", not a lead
- Return one card per lead (markdown):
## Name (Chinese and English)
- Inferred education: school / degree / year (mark "unverified" if not confirmed)
- Current-role clue: company / title / dates / work location (mark "unverified" if unsure)
- Homepage: URL or not found
- GitHub: URL or not found
- Contact clue: email / LinkedIn or not found
- Sources: evidence URL for every item above
- Record public information only; mark uncertain items "unverified"; never fabricate
Each sub-agent returns its card list as the final reply to the lead agent. Do not write files.
Step 2: Consolidate, dedupe, cross-verify
> Verification-depth standard: the single-pass verification mode is the mandatory baseline — every shortlisted candidate must pass at least one round of cross-verification; unverified candidates never make the list. If the user wants higher confidence and has time, the five-pass verification mode is an optional deep-dive.
2.1 Two-source rule (non-negotiable)
Every attribution conclusion (this person = this GitHub account, this person works at this company, this paper is theirs) needs two independent primary sources. Typical pairs: paper corresponding-author email ↔ commit email; lab roster link ↔ account self-description; official team page ↔ paper authorship.
- One path only → mark "unconfirmed" in the output, never write it as fact.
- Hardest evidence: email inside the repo/paper == email published on the group's official page, plus repo ↔ paper one-to-one correspondence.
2.2 Confidence tiers
| Tier | Condition |
|---|---|
| High | Two independent primary sources agree (e.g. official link + account self-description matching school/team/employer) |
| Medium | Single strong source, or official link with an empty account profile |
| Low | Name-match only (pinyin/login guess) with no school/company/direction signal → "not found" in practice; isolate as "low confidence, manual check" |
| Not found | Write "not found" explicitly — never leave a blank that reads as a scraping miss |
2.3 Homonym hygiene
Chinese pinyin collisions (Zhang / Chen / Li / Wang / Yang / Xu / Fan / Gu / Zhao) are the single largest error source. A matching display name is not evidence — demand a second signal (school, employer, lab, direction). Jyutping / Wade-Giles romanisation (e.g. 劉家昌 = Ka Cheong Jason Lau) defeats pinyin search entirely; for HK/Taiwan names, reverse-lookup from the official Chinese record instead.
Also: organisation accounts ≠ personal accounts (lab/company orgs must be labelled as such, never as the candidate), and merge same-name/different-email identities in git stats.
2.4 Tiered delivery (A / B / C)
Never pad the list to hit a headcount:
- A — all hard constraints (school, graduation window, location, direction) backed by primary sources
- B — contribution and identity verified; graduation year given as an inferred range, explicitly labelled 推测/inferred
- C — rejected, reason recorded (school bar / location / exclusion / duplicate / unconfirmed identity)
Single-pass verification mode (standard / mandatory)
- Dedupe: merge cross-channel duplicates by "name + homepage/GitHub URL" into a shortlist
- Single pass: for each shortlisted person, fetch the personal homepage / academic page directly (primary sources) and verify education (school/degree/year), graduation window, current role, work location (if constrained), contact info. Anything unverifiable is marked "unverified", and the search log notes "single-pass only"
- School-bar check: grade the school against
references/school-list.md. Primary sources only (LinkedIn education section, personal homepage, paper authorship, PhD thesis title page, GitHub org); search aggregators and secondhand reports are locating clues, never verification evidence. Below-bar candidates are dropped with the reason recorded in the log (keep the "rejected (reason)" wording) - Fill per person: current role, internships/jobs, homepage, GitHub, public contact info. Anything unavailable is "not obtained" — never fabricate and never pass inference off as fact
- First-author papers: count first-author papers only; write the count first, then up to 5 representative titles (with venue and year). Titles only as verified from the paper itself, per authorship lists and Google Scholar
- University-lab experience: verified well-known university labs only, formatted "school + lab/group name (year range)"; criteria in
references/school-list.md; leave blank if none - Match highlight: one sentence per person on why they match (representative work, key experience, direction fit)
- Assign confidence + tier per §2.2 / §2.4
Five-pass verification mode (optional / deep)
For users who explicitly ask for deep verification, or for critical-role candidates. Every shortlisted person is cross-verified across five independent source dimensions, checking the same core facts (name, education, experience, current role, first-author papers). Dispatch one verification sub-agent per candidate, running all five passes in order:
| Pass | Source dimension | Verify focus |
|---|---|---|
| 1 | Academia: paper authorship, Google Scholar, arXiv, conference proceedings, PhD thesis, lab pages | Education, first-author papers, lab |
| 2 | GitHub: profile, contribution timeline, org membership, profile email, CODEOWNERS | Experience, current role, module ownership, contact |
| 3 | Professional platforms: Maimai, LinkedIn, Liepin/Boss public profiles | Experience, current role (login-walled sites via browser automation) |
| 4 | Company / institution primary sources: official team pages, press releases, tech blogs, corporate open-source orgs, business registration | Current role and tenure |
| 5 | Personal homepage / social media: personal site, Zhihu, WeChat account, X, Jike | Corroboration + contact info |
Verification sub-agent prompt template (substitute the host's actual tool names):
You are a candidate-verification sub-agent. Task: run five-pass cross-verification on the candidate below.
Candidate lead: <name + existing lead card>
EXCLUSIONS (hard): <exclusion items>
Use the host's web-search tools (e.g. WebFetch/WebSearch) to search public information; if browser operations are needed
(sites requiring login, e.g. Maimai, LinkedIn), load a locally available browser-automation skill and follow its guidance.
Five passes (in order, each an independent source dimension):
1. Academia (paper authorship / Scholar / arXiv / conferences / PhD thesis / lab pages)
2. GitHub (profile / contribution timeline / orgs / email / CODEOWNERS) — prefer HTML endpoints, git stats and hovercard over the rate-limited API
3. Professional platforms (Maimai / LinkedIn / Liepin public profiles; login-walled ones via browser automation)
4. Company / institution primary sources (official team pages / press releases / tech blogs / corporate open-source orgs)
5. Personal homepage / social media (personal site / Zhihu / WeChat account / X / Jike)
Each pass verifies the same facts: name, education (school/degree/year), experience (company/title/dates), current role, work location (if constrained).
Requirements:
- Two independent primary sources per attribution; one path only ⇒ mark "unconfirmed"
- Never infer a graduation year from an email/username year string without labelling it 推测/inferred
- When passes conflict, primary sources win; record the conflict and the ruling in a "conflicts" section
- If a location constraint is set and the current work location clearly violates it, mark "rejected (location)"
- Mark unavailable info "not obtained"; never fabricate. Better to miss than to be wrong.
Return (markdown):
## Verification result
- Name (Chinese and English)
- Education / Experience / Current role
- First-author papers: count + titles (≤5, with venue and year)
- University-lab experience
- Homepage / GitHub / contact info
- Confidence (high/medium/low) + the two sources behind it
- Delivery tier (A/B/C) + reason
## Five-pass record
Per pass: what was queried, what was found, conflicts with other passes
The sub-agent returns the verification result as the final reply to the lead agent. Do not write files.
Write back to the candidate library (mandatory)
After shortlist verification, upsert all candidates (including rejected ones) back into the shared library talent-research/_db/candidates.jsonl for incremental reuse by later tasks:
node scripts/db_upsert.mjs talent-research/_db/candidates.jsonl upsert <candidates.json>
The input JSON is fully compatible with gen_excel.mjs's candidates array, plus library-level fields:
| Field | Required | Notes |
|---|---|---|
direction |
recommended | This round's direction tag (e.g. llm-pretraining-data), the index for same-direction library queries |
last_verified |
auto | Last verification date; defaults to today |
verify_depth |
auto | single / five — the depth actually executed this round |
status |
recommended | active (shortlisted) or rejected (dropped); rejections must carry reject_reason so next round won't re-mine them |
confidence |
recommended | high / medium / low per §2.2 |
tier |
recommended | A / B / C per §2.4 |
sources |
recommended | The two primary-source URLs behind the attribution (array; renders into the 核验依据 column) — lets next round skip re-verification |
Dedup keys are GitHub URL > homepage URL > name (URLs case-insensitive, trailing slashes ignored); on upsert, non-empty new fields overwrite old ones, empty fields keep old values. Full schema in the script's header comment and examples/db-candidates.example.txt. The library contains personal information and is excluded by .gitignore — never commit it to git.
Library entry bar and contact rules:
- Entry hard bar: identity must be bi-directionally verified between homepage ↔ GitHub (or an authoritative institution page) — one-way information or search-engine-only hits never enter the library
- Contact info is tiered into the
notesfield: public phone / WeChat (self-published on their own homepage/README/public card only) > public email > no direct contact - Maimai direct links: if a candidate has a publicly accessible Maimai profile page (
maimai.cn/p/...or/u/...format), record it separately in themaimaifield for one-click reach; Maimai article pages (/article/detail/...) are not personal profiles — never mislabel them. Maimai profile pages no longer expose work history (they redirect to a landing page), so treat Maimai as a reachability channel only: company/title + activity, everything else "confirm on contact". A company name prefixed with "前" means the person has left and is likely open to a conversation — prioritise those. - Compliance red line: phone / WeChat / Maimai info comes from public channels only — no scraping of private chats, no bought data, no closed groups
Step 3: Produce outputs
Produce two files under talent-research/<topic>/:
candidates-YYYYMMDD.xlsx
Generate with scripts/gen_excel.mjs from this skill's directory (zero-dependency, runs on Node ≥ 18):
node scripts/gen_excel.mjs candidates.json output-path.xlsx
# with confidence tiers appended (recommended whenever attribution wasn't fully nailed down):
node scripts/gen_excel.mjs candidates.json output-path.xlsx --confidence
The structure of candidates.json and column definitions are in the script's header comment; an example is examples/candidates.example.json. Fixed 9 columns:
| Column | Content |
|---|---|
| Name | Chinese and English names |
| Education | School / degree / year; multiple degrees on separate lines |
| Internships / jobs | Company / title / dates, one per line |
| Personal homepage | URL (or "not obtained") |
| GitHub | URL (or "not obtained") |
| First-author papers | Count + representative titles (≤5, with venue and year), one per line |
| University-lab experience | "School + lab/group name (year range)"; blank if none |
| Match highlight | One-sentence match rationale |
| Notes (CV / contact) | CV link, LinkedIn, email and other public contact info (or "not obtained") |
--confidence appends three columns at the end (confidence / delivery tier A-B-C / evidence URLs), and colour-codes the confidence cell — recommended whenever any attribution rests on a single source, so the hiring manager can judge trust themselves.
search-log.md
Follow this template:
# Search log: <topic> (benchmark: <benchmark person>)
> Date established: YYYY-MM-DD
> Search methods: <tools actually used>
## Task requirements
- <persona or JD summary + all constraints (including mid-round additions, timestamped)>
- Must-have → signal translation: <must-have → strong signal, per must-have>
- Exclusions: <list>
- Verification method: single-pass mandatory; five-pass optional deep verification
## Search process
| Time | Query / action | Result and decision |
|------|-----------|-----------|
| ... | ... | ... (including "rejected" with reason, conflicts found during verification and how they were ruled on) |
## Delivery tiers
- A (fully primary-sourced): <count> · B (inferred graduation year): <count> · C (rejected): <count> + reason distribution
## Network environment notes
- <newly discovered pitfalls this round>
## TODO
- [ ] ...
Step 4: Retrospective and lessons (mandatory)
After outputs are done, the lead agent runs a short retrospective on this round's search log and appends reusable lessons to talent-research/_lessons/<current-month>.md — create it per references/lessons-template.md if absent (the directory is excluded by .gitignore and never enters the repo).
Retrospective inputs: the search log (per-channel hit counts, rejection-reason distribution, time sinks) + the output list + constraints the user added mid-round.
One line per lesson, one of six types:
| Type | What to write |
|---|---|
| Source effectiveness | Which source hit / failed (with data), how to re-rank next round |
| Signal effectiveness | Which strong signal actually separated real practitioners from resume-padding, which turned out to be noise |
| Rejection pattern | What the rejection distribution says about the persona, and suggested adjustments |
| Network pitfall | Newly discovered site-reachability issues and workarounds |
| Process improvement | The slowest / hardest-to-verify steps and how to fix them |
| Tooling | Which script / endpoint saved or wasted time; ideas for scripts/ |
Discipline:
- Only reusable conclusions, no task diary (the diary belongs in the search log)
- Date and direction tag on every entry
- New lessons conflicting with old ones never delete the old entry — append and note "conflicts with <date> entry; this one wins"
Maintenance and evaluation: required for every revision
Before merging any substantive change to this skill (SKILL.md flow, references/ search strategy and lessons), run the evaluation baseline first — self-modification without evaluation is driving blindfolded:
- Build a golden set (one-time, 10–20 people per direction): pick confirmed-correct candidates from past verified outputs, write them to
talent-research/_eval/golden.jsonl(covered by.gitignore, never committed). Each entry has name + github/homepage + education/current ground truth; format inexamples/golden.example.txt - Run the baseline: do one full cold run over the directions covered by the golden set (no candidate-library increment), get the result JSON, then score:
bash
node scripts/eval_recall.mjs talent-research/_eval/golden.jsonl <result.json> [result2.json ...]
Metrics: recall (pass bar defaults to 0.8, adjustable via the RECALL_MIN env var), education/current-role field accuracy of hits (normalized mutual-containment matching), misses list. Below bar exits with code 1
3. Merge decision: only merge if recall ≥ the bar and field accuracy is no lower than the last recorded run; otherwise fix and re-test. Script usage and metric definitions are in the script's header comment
4. Keep records: log every run in talent-research/_eval/records.md (date, recall, field accuracy, change summary) as the evidence chain that "evolution never regresses"
Proactive radar (scheduled monitoring, optional)
Upgrade from "search when asked" to "scan on schedule": configure once, get a weekly incremental talent-signal report.
- Configure: copy
examples/radar-config.example.jsonto<workspace>/talent-research/_radar/config.json; fill in the direction tag, paper-search keywords (English phrases), and the target GitHub org list
Field-tested China LLM-team GitHub org lookup (one wrong letter in an org name means zero results — trust this table and re-verify before filling):
| Team | org | Public-member signal |
|---|---|---|
| Alibaba · Qwen | QwenLM |
strong |
| Alibaba · ModelScope | ModelScope |
weak (~2) |
| Alibaba · Tongyi Lab | Alibaba-NLP |
medium |
| DeepSeek | deepseek-ai |
weak (~1) |
| Zhipu | THUDM / zai-org |
strong |
| Moonshot · Kimi | MoonshotAI |
weak (~2) |
| Tencent ARC Lab | TencentARC |
medium |
| Tencent Hunyuan | Tencent-Hunyuan |
none (members not public; keep as placeholder) |
| ByteDance Seed | ByteDance-Seed |
none (members not public; keep as placeholder) |
| Baidu PaddlePaddle | PaddlePaddle |
strongest (~35) |
| Huawei Noah's Ark | huawei-noah |
medium |
| Huawei MindSpore | mindspore-ai (note: not mindspore) |
none (members not public; keep as placeholder) |
| Meituan | meituan |
medium |
| JD | jd-opensource |
medium |
| Xiaohongshu | xiaohongshu-pub |
very weak (the company barely uses GitHub) |
- Run (with the workspace root as cwd):
bash
node scripts/radar_scan.mjs talent-research/_radar/config.json
The first run establishes the baseline; every later run is an incremental diff, with the report written to talent-research/_radar/radar-YYYY-MM-DD.md: new public GitHub org members (talent-flow signals) + new OpenAlex papers in the direction (with first author and institution — first authors are candidate leads)
3. Schedule: run weekly via the host's scheduler (cron / automation). After each run, quickly verify the new signals through the single-pass flow, upsert them into the candidate library, and write the lessons (which orgs / keywords have high signal density) into _lessons/
4. Boundaries: public information only (GitHub public members, OpenAlex public metadata); unauthenticated GitHub API is rate-limited to 60 req/hour, keep the org list under ~20 (each org costs ~1-2 requests per round); use OpenAlex for papers instead of the arXiv API (the latter's https is unreachable on some networks)
5. Weak-signal note: some companies (Tencent Hunyuan, ByteDance Seed, Huawei MindSpore) keep GitHub member lists fully private — an org scanning 0 people is normal. Keeping them costs almost nothing: the moment someone publicizes their membership, the radar catches it. Xiaohongshu has minimal GitHub presence; its signal comes mainly through the paper channel
Data-quality bottom lines
- Primary sources first: search engines are for discovering leads; verification must go back to official sites / homepages / papers / GitHub originals
- Two independent primary sources per attribution. One path only ⇒ "unconfirmed". A name that merely looks right is not evidence
- Better to miss than to be wrong — especially on identity attribution, where homonyms are the dominant error source
- Contact info from public sources only (GitHub profile email, personal-homepage email, paper corresponding-author email, public LinkedIn pages) — no bought data, no paid-database scraping, no closed groups
- Every key fact for every candidate must be traceable to a source URL in the search log
- Every shortlisted candidate must pass at least one round of cross-verification: unverified candidates never make the list
- If a location constraint is set: drop anyone whose current work location cannot be verified or clearly violates it, with the reason in the search log
- Better empty than fabricated: candidates who cannot be verified against primary sources are marked "unverified", never filled with guesses
- Exclusion items are enforced at every layer, not just stated once
- Candidate data is sensitive: it goes into the deliverable only. Never into public repos, public docs, or anything externally visible
支付宝 AI 按量付费(Machine Pay 付费改造)
本技能已接入支付宝 AI 按量付费(AI Pay-per-Use / A2M),遵循 probe → pay → complete → ack 标准 402 收款链路。执行序:Step 0 开始前先跑 probe(免费);Step 3 交付对应 complete 与 ack(付费后)。
付费节点设计
| 阶段 | 付费状态 | 产出 | 说明 |
|---|---|---|---|
| probe | 免费 | 岗位画像拆解 + 四通道可行性预判 + 前 3 条脱敏线索预览 | 探针阶段,让用户先看到检索质量,不生成订单、不调用任何支付接口 |
| pay | 付费解锁 | 完整四通道检索 + 双源核实 + 置信度分级名单 | 单次调用计费:amount=9.90,currency=CNY |
| complete | 履约 | 交付 candidates-YYYYMMDD.xlsx + search-log.md |
验付通过后才生成交付物 |
| ack | 确认 | 调用 alipay.aipay.agent.fulfillment.confirm 发送履约回执,订单闭环 |
支付宝确认履约后订单状态更新为已完成 |
计费标识:service_id = talent_search_v1,resource_id = talent-search/full-search-v1。
402 账单下发(pay 前置)
请求未携带有效 Payment-Proof 时,服务端/宿主返回 402 Payment Required,账单经 Payment-Needed 下发(Base64URL 编码)。账单 protocol 层字段:out_trade_no(全局唯一商户订单号,兼作幂等键,格式 TS_<timestamp_ms>_<6位随机hex>)、amount、currency、resource_id、pay_before(ISO8601 支付截止时间)、seller_signature、seller_sign_type=RSA2、seller_unique_id;method 层字段:seller_name、seller_id、seller_app_id、goods_name、seller_unique_id_key、service_id。
订单持久化与幂等:订单写入本地 orders/ 目录(每单一文件,含 out_trade_no/金额/resource_id/状态机:待支付 → 已支付 → 已履约 → 已确认 → 已关闭)。重试复用原 out_trade_no,禁止重新生成一笔账;同一 trade_no 禁止重复履约。
携带凭证重试与验付调用(complete 前置)
用户经支付宝完成支付后,智能体携带 Payment-Proof(Base64 编码,含 payment_proof、trade_no、可选 client_session)重试同一 resource_id。服务端调用 alipay.aipay.agent.payment.verify 验付,并同时校验全部五项:active=true;返回 amount 等于本地订单金额;out_trade_no 与本地订单一致;resource_id 与本次请求资源一致;trade_no 未被重复履约。任一不满足 → 重新返回 402,不交付。
支付依赖包
- Node.js(推荐,与技能运行时一致):
npm install @alipay/agent-payment@latest—— 能力:createPaymentNeededBill()(生成 402 账单并 RSA2 加签)、parsePaymentProofHeader()(解析Payment-Proof请求头)、verifyPayment()(调alipay.aipay.agent.payment.verify验付)、confirmFulfillment()(调alipay.aipay.agent.fulfillment.confirm履约回执)。 - Java(服务端部署时):
com.alipay.sdk:alipay-sdk-java:4.38.0.ALL。 - 应用私钥 PKCS#8 格式,禁止出现在前端、日志或仓库中。
托管说明
在 SkillHub/Pay Skill 平台托管时,按次计费由平台代收代结,上述协议字段用于平台审核与对账;自建服务端部署时,按本节协议实现 402 端点(预下单接口 alipay.aipay.agent.payment.precreate → amtPaymentLink 响应头 → Payment-Proof 验付 → 履约回执),沙箱测试后再签约上线。
本地联调自测
node scripts/pay_flow_demo.mjs # 跑完整链路:probe → 402 → pay → verify → complete → ack
node scripts/pay_flow_demo.mjs --keep # 保留 orders/ 下的订单与交付物以便查看
用演示密钥与本地 mock 验付,不联网、不产生真实扣款;会验证五项验付校验,并演示三种异常都被正确拒绝(同一凭证重放、伪造凭证、resource_id 不匹配)。改价或改协议字段后先跑它,再看 known 输出。
References
| File | When to read |
|---|---|
references/source-playbook.md |
Mandatory before Step 1 (channel strategy, contact-collection rules) |
references/school-list.md |
Mandatory during Step 2 school-bar verification |
references/github-playbook.md |
Step 1 GitHub channel + any "is this person really the maintainer?" question — anonymous/rate-limited GitHub SOP, email↔account mapping, bot blacklist, pitfalls |
references/repo-map.md |
Step 0/1 when picking which repos to mine for this direction (+ recipe to rebuild the map for a new direction) |
references/sourcing-plan.md |
Step 0 when the input is a JD — weak/strong signal translation, boolean queries, screening rubric, phone-screen questions |
references/company-recon.md |
Step 1 company channel when the target company is unfamiliar (four-path recon: GitHub org / paper emails / business registration / press) |
references/roster-verify.md |
When the input is an official roster page (university team, lab, competition team) instead of a persona |
references/field-notes.md |
Step 1 sub-agent dispatch + any site behaving oddly — sub-agent rate limits, output-pollution discipline, Maimai/site tactics, network environment |
references/lessons-template.md |
Read before appending lessons in Step 4 (entry format and discipline) |