alterlab-pdf-extract — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited alterlab-pdf-extract (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The free analog of Elicit's "columns" feature. Point it at a stack of PDFs, declare the columns you care about, and get back a tidy evidence table — one row per paper, one column per question — as CSV or a Markdown table. Built for data extraction across a corpus: literature screening, methods comparison, PICO/sample-size pulls, and review evidence sheets.
Every input is normalized to clean Markdown via Microsoft MarkItDown (see the alterlab-markitdown skill) before any column is extracted, so PDF, DOCX, PPTX, HTML, and the other MarkItDown formats all work.
outcome, effect size, limitations).
Not for: searching databases to find papers, or synthesizing a narrative review. Discovering literature and writing the prose synthesis is alterlab-literature-review's job — this skill operates on PDFs you already have and produces a structured table, not prose. Use it after you have the papers.
SCRIPT=skills/research-tools/alterlab-pdf-extract/scripts/extract_to_table.py
# Heuristic backend: use 'label:question' so the question carries the words the
# papers actually use — overlap is lexical, so a bare label like "Sample size"
# rarely matches "we enrolled 240 participants" (see Backends below).
uv run python "$SCRIPT" papers/*.pdf \
--column "Sample size:how many participants were enrolled" \
--column "Main finding:primary result reported" \
--column "Limitations:study limitations weaknesses" --format md
# Columns with explicit extraction questions, CSV to a file:
uv run python "$SCRIPT" a.pdf b.pdf \
--column "n:how many participants were enrolled" \
--column "effect:what was the main reported effect size" \
--format csv -o evidence.csv
# Precise LLM-backed extraction (needs OPENROUTER_API_KEY) — label-only columns
# are fine here, since the model reads the paper rather than matching keywords:
uv run python "$SCRIPT" lit/*.pdf \
--columns "Population" "Intervention" "Outcome" --backend llmA column is a label plus the question that drives extraction. Two ways to declare:
| Spec form | Meaning |
|---|---|
"Sample size" | label == question; keywords come from the label |
"n:how many participants" | label n, question how many participants |
Use --columns for a space-separated list of simple labels, and repeat --column for specs that need an explicit question. Both combine; duplicate labels are rejected.
literal keyword overlap with the column spec and emits the best evidence snippet. Deterministic — good for a first pass, screening, and reproducible runs. Returns an empty cell when no sentence shares a keyword. Gotcha: matching is lexical, not semantic. A generic label like "Sample size" tokenizes to sample, size — which never appears in "we enrolled 240 participants", so the cell comes back empty. For the heuristic backend, phrase columns as Label:question where the question uses the words the papers actually use (participants, enrolled, cohort, n=). Quantitative questions also get a small ranking bonus for sentences containing digits when the question itself contains a digit or %. When you only have generic labels, use the llm backend instead.
OpenAI-compatible endpoint (OpenRouter by default) for a concise per-column answer (N/A when the paper is silent). The model ID follows the ALTERLAB_MODEL convention (skills/core/shared/model_env.md): $ALTERLAB_MODEL, else the dated default. Override per-run with --model.
--format md → GitHub-flavored Markdown table (pipes in cell text are escaped).--format csv → CSV via csv.DictWriter.-o FILE writes to disk (UTF-8); otherwise the table goes to stdout.The first column is always Source (the input filename); a file that fails conversion yields an ERROR row so the table stays aligned.
alterlab-pdf-extract sits downstream of paper discovery and upstream of synthesis:
alterlab-literature-review, search backends).alterlab-literature-review, alterlab-paper-writer.scripts/extract_to_table.py — the CLI. --help lists every flag; --columns /--column define the table; --backend, --format, --model, -o control extraction and output.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.