AMiner Deep Search
Host-model-driven survey paper collection. You (the model reading this) are the controller: run the tool scripts, read their JSON output, judge relevance yourself, and iterate until the collection target is met.
Routing (read first)
| Task shape | Skill |
|---|---|
| Single lookup answerable by one free API (paper by title, scholar by name, venue/org normalization) | aminer-free-academic |
| Deep analysis of one entity, multi-condition search, or paper collection under ~50 papers | aminer-academic-search |
| Personalized paper recommendations | aminer-daily-paper |
| Large-scale candidate collection (50+ papers), survey bibliography construction, citation snowballing | this skill |
Do not trigger on the words "survey" / "literature review" alone; trigger on the scale of collection the user actually needs.
Pre-flight
- Check the key without printing it:
[ -z "${AMINER_API_KEY:-}" ] && echo "AMINER_API_KEY missing" || echo "AMINER_API_KEY exists"
If missing, stop and ask the user to set AMINER_API_KEY (console: https://open.aminer.cn/open/board?tab=control). Never print the key.
Confirm the
topicand thetarget-size(default 400).Extract the user's hard constraints — year range, venues, authors, institutions, language, exclusion terms, ranking goal (latest / impact / classic+latest). Record year range and required fields into the state file so
addenforces them mechanically:
python3 scripts/paper_set.py init --topic "..." --year-from 2020 --year-to 2025 --require-fields year
Note: AMiner has no document-type filter (journal / conference / preprint); if the user requires one, say so and fall back to post-hoc venue filtering.
- If your round plan is estimated to cost ¥5 or more, tell the user the estimate and get confirmation before starting.
Tools
Both scripts live in scripts/ under this skill directory. They print exactly one JSON document to stdout (the tool result); diagnostics and a [cost] line go to stderr. They never score relevance — that is your job.
scripts/aminer_api.py — AMiner API calls
| Subcommand | Endpoint | Price |
|---|---|---|
search [--query Q] [--title T] [--abstract A] [--author NAME] [--org ORG] [--venue V] [--size 20] [--year-from Y] [--year-to Y] [--order n_citation|year] [--max-pages 3] |
GET /api/paper/search/pro (100 results/page) + free paper/info enrichment |
¥0.01/page |
qa-search [--query "natural language question"] [--topic-high '[["termA","termB"],["termC"]]'] [--size 20] [--year-from Y] [--year-to Y] [--citation-sort] |
POST /api/paper/qa/search (always use_topic=true; the backend ignores query when use_topic=false) + free enrichment |
¥0.05/call |
qa-search-pro [--query Q] [--query-type auto|topic|keywords|title|identifier] [--authors ...] [--orgs ...] [--venues ...] [--year-from Y] [--year-to Y] [--languages en zh] [--all-terms ...] [--any-terms ...] [--exclude-terms ...] [--search-in all|title|title_keywords|abstract] [--min-citations N] [--max-citations N] [--sort relevance|balanced|recent|citation] [--size 10] |
POST /api/paper/qa/searchPro (10 results/page, cursor pagination) + free enrichment |
¥0.30/page |
info --ids id1 id2 ... |
POST /api/paper/info (batched ≤100 ids) |
Free |
references --ids id1 id2 ... [--per-seed 20] |
GET /api/paper/relation per seed + free enrichment |
¥0.10/seed |
Choosing a search subcommand: search is the cheap bulk workhorse (¥0.01 per 100 results; fielded literal matching, year filtered client-side). qa-search handles natural-language questions cheaply. qa-search-pro is 30× the price of search per call and returns only 10 per page — reserve it for queries whose hard constraints search cannot express (language filter, exclusion terms, citation ranges, multi-value author/org/venue filters). Note: qa-search-pro must be enabled for the account; if it returns an HTTP 400 permission error, tell the user to enable the API in the AMiner console and fall back to search. If a cursor continuation page fails (the backend sometimes invalidates cursors mid-pagination), the script returns the pages already collected and prints a [warning] to stderr instead of failing — fewer results than --size after such a warning is expected, not an error.
search field notes: multiple fields combine with AND; matching is literal, so --author wants the full name ("Ashish Vaswani", not "Vaswani") — if a combined query returns 0, drop fields one at a time before giving up.
Output shape: search subcommands and info print [{id, title, year?, venue?, authors?, doi?, n_citation_bucket?, abstract_slice?, url}]; references additionally includes source_paper_ids (which seeds cited the paper). Seeds themselves are excluded from references output. doi is only available from search (the other endpoints do not return it).
scripts/paper_set.py — cross-round state file (no network)
State file defaults to outputs/paper_set.json relative to the working directory. Dedup is three-way: AMiner ID, lowercased DOI, and normalized title — preprint and published versions merge into one record (published venue wins; alternate IDs kept in alt_ids).
# Record hard constraints once; `add` enforces them from then on
python3 scripts/paper_set.py init --topic "..." --year-from 2020 --year-to 2025 --require-fields year
# Merge kept results (pipe the filtered JSON array in); --source records provenance per paper
python3 scripts/aminer_api.py search --query "..." \
| python3 scripts/paper_set.py add --source "search:..."
# → {"added": N, "duplicates": M, "merged_versions": K, "rejected": R, "reject_reasons": {...}, "total": T}
python3 scripts/paper_set.py stats # totals, tiers, field completeness, by_year
python3 scripts/paper_set.py mark-expanded --ids id1 id2 # record snowballed seeds
python3 scripts/paper_set.py promote --ids id1 id2 # move papers to the curated tier
python3 scripts/paper_set.py log-round --queries "q1" "q2" --added N --rejected R # trace
python3 scripts/paper_set.py export -o outputs/final_papers.json [--tier curated|candidate|all]
add also accepts --ids id1 id2 ... for bare IDs (note: bare IDs carry no year/title, so they are rejected when init requires those fields — pipe enriched JSON instead). Items carrying source_paper_ids (from references) automatically mark those seeds as expanded and get references:<seed> provenance entries.
If you want to filter before adding, read the search output first, then pipe only the kept items:
printf '%s' '[{"id":"...","title":"..."}]' | python3 scripts/paper_set.py add --source "search:..."
Round Protocol (core)
Round 0 — plan
- Derive 4–8 seed queries from the topic: synonyms, subfields, method names, datasets/benchmarks, common English abbreviations.
- Run
paper_set.py initwith the extracted hard constraints (see Pre-flight step 3). - Pick the ranking strategy from the user's goal:
- impact / foundational papers →
search --order n_citation - latest work →
search --order year(orqa-search-pro --sort recent) - classic + latest → run each seed query twice, once with
--order n_citationand once with--order year, and merge (the set dedupes) — this stops new papers from being crowded out by highly cited ones - no stated preference → default composite ranking (omit
--order)
- impact / foundational papers →
- Estimate rounds and cost (search ≈ ¥0.01/page, qa-search ¥0.05, qa-search-pro ¥0.30/page, references ¥0.10/seed). If the estimate is ≥¥5, confirm with the user first.
Each round (default budget: 12 rounds), six fixed steps
- Search: run 1–4
search/qa-searchcalls from the pending query queue, passing the user's structured filters (--author/--org/--venue/--year-from/--year-to). Prefersearch(cheapest); useqa-searchfor natural-language questions; useqa-search-proonly when a hard constraint (language, exclusion, citation range) cannot be expressed otherwise. - Filter: read the stdout results and judge topical relevance yourself. Hard constraints (year, required fields) are enforced by the state file; your job is the semantic judgment.
- Add: pipe only the kept items into
paper_set.py add --source "search:<query>". Never add papers you consider off-topic. Checkrejected/reject_reasonsin the output — a high rejection rate means your queries are drifting out of the constraint window. - Check: run
statsto see the total, tier counts, and field completeness. - Snowball: from this round's relevant additions pick ≤5 strong seeds (highly relevant, ranked high under
--order n_citation, not inexpanded_seeds) and runreferences --ids .... Filter the output for relevance, then add it with--source. Runmark-expandedfor seeds that yielded nothing addable. - Log & decide: run
log-round --queries ... --added N --rejected Rto append the trace, then choose the next move —- a search returned <5 results or poor quality → replace it with a reformulated query (max 2 variants per direction, then switch to snowballing);
- references are yielding many relevant papers → keep snowballing from fresh seeds;
- reached
target-size, or results are exhausted, or 2 consecutive rounds added <5 papers → terminate.
Wrap-up
Optionally promote the strongest papers to the curated tier. Run export (add --tier curated for a curated-only file), then report: final paper count, rejected/merged counts, total cost (sum the [cost] stderr lines), and the output path. The export carries full fields per paper (title, authors, year, venue, DOI when available, AMiner ID, URL, citation bucket, provenance found_by, tier) plus the constraints and the round trace.
Error handling
The scripts print structured JSON errors and never mask them as empty results. An empty result set is a plain [] with exit code 0 — it is not an error.
error value |
Meaning | What to do |
|---|---|---|
missing_aminer_api_key |
env var not set | stop; ask the user to set it |
invalid_params (40001) |
bad request parameters | fix the call, don't retry as-is |
permission_denied (40301) |
key lacks permission / balance | stop; tell the user to check the console |
token_expired (40302), invalid_api_key (40307), invalid_token (40308) |
credential problem | stop; ask the user to renew the key |
rate_limited (40306) |
too many requests | slow down; the script already retried |
server_error (50001), http_error, network_error |
AMiner-side/transport failure | script retried 3×; report if persistent |
Rules
- Never fabricate paper IDs or titles; only cite data actually returned by the tools.
- Free first: metadata always comes from the free
paper/info(the scripts already do this); never call the paidpaper/detailfor bulk metadata. - Keep the raw tool output out of your final answer; report counts and the exported file path instead.
- Never print or log
AMINER_API_KEY. - If AMiner returns fewer papers than the target, report the real count instead of inventing papers.
- Papers violating the user's hard constraints must never enter the result set — record constraints with
initso this is enforced mechanically.