Native Shell
Native Shell은 모든 플랫폼의 앱 안에서 만들어지는 AI UI입니다
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗
Validate that supporting text quotes in your data actually appear in their cited references.
This tool fetches scientific publications (currently PubMed/PMC) and verifies that quoted text (supporting_text) can be found in the referenced document using deterministic substring matching.
# Using uv (recommended)
uv pip install linkml-reference-validator
# Using pip
pip install linkml-reference-validator
# Validate a single quote against a reference
linkml-reference-validator validate text \
"protein functions in cell cycle regulation" \
PMID:12345678
# Validate a data file using LinkML validation
linkml-validate -s schema.yaml data.yaml \
--validate-plugins linkml_reference_validator.plugins.ReferenceValidationPlugin
Install the linkml-reference-validator workflow skill with the skills CLI (requires Node.js). Preview available skills first:
npx skills add linkml/linkml-reference-validator --list
npx skills add linkml/linkml-reference-validator --skill linkml-reference-validator
Installation defaults to the current project. Use -a codex or
-a claude-code to select an agent; add -g for your user-wide skills directory:
npx skills add linkml/linkml-reference-validator --skill linkml-reference-validator -a codex -g
The skill helps agents configure reference QC, interpret failures and coverage, and set up repository hooks and CI using bundled reference guides. Install the runtime separately as described above; installing the skill does not activate hooks or configure CI, credentials, or data sources.
Scientific data often includes claims supported by quotes from publications. But how do you know the quotes are accurate?
Before:
gene_function:
gene: TP53
function: "regulates cell cycle"
evidence:
reference: PMID:12345678
supporting_text: "TP53 is critical for cell cycle regulation" # Is this really in the paper?
After validation:
$ linkml-reference-validator validate text \
"TP53 is critical for cell cycle regulation" \
PMID:12345678
✓ Valid: True
✓ Supporting text validated successfully in PMID:12345678
Note: The CLI was restructured in v1.x to use nested commands (
validate text,validate data,cache reference). The old hyphenated commands (validate-text,validate-data,cache-reference) still work for backward compatibility but are deprecated.
validate text - Quick Single Quote ValidationValidate a single quote against a reference without needing a schema.
linkml-reference-validator validate text <TEXT> <REFERENCE_ID> [OPTIONS]
Example:
# Basic validation
linkml-reference-validator validate text \
"protein functions in cell cycle regulation" \
PMID:12345678
# With editorial notes (ignored in matching)
linkml-reference-validator validate text \
"protein [X] functions in cell cycle regulation" \
PMID:12345678
# Multi-part quote with omitted text
linkml-reference-validator validate text \
"protein functions ... cell cycle regulation" \
PMID:12345678
Options:
--cache-dir PATH - Directory for caching references (default: references_cache)--verbose - Show detailed validation information--help - Show help messageExit Codes:
0 - Validation successful1 - Validation failedvalidate data - Full Data File ValidationValidate an entire data file using a LinkML schema.
linkml-reference-validator validate data <DATA_FILE> --schema <SCHEMA> [OPTIONS]
Example:
Schema (gene_schema.yaml):
id: https://example.org/genes
name: gene-schema
classes:
GeneFunction:
attributes:
gene:
range: string
function:
range: string
evidence:
range: Evidence
Evidence:
attributes:
reference:
range: Reference
implements:
- linkml:authoritative_reference # Marks this as a reference field
supporting_text:
range: string
implements:
- linkml:excerpt # Marks this as text to validate
Reference:
attributes:
id:
identifier: true
range: string
title:
range: string
Data (gene_data.yaml):
gene: TP53
function: "regulates cell cycle"
evidence:
reference:
id: PMID:12345678
title: "TP53 in cell cycle control"
supporting_text: "TP53 protein functions in cell cycle regulation"
Validation:
linkml-reference-validator validate data \
gene_data.yaml \
--schema gene_schema.yaml
# Output:
Validating gene_data.yaml against schema gene_schema.yaml
Cache directory: references_cache
✓ All validations passed!
Options:
--schema PATH (required) - Path to LinkML schema--target-class CLASS - Specific class to validate--cache-dir PATH - Directory for caching references--verbose - Show detailed output--help - Show help messagerepair - Automated Repair of Validation ErrorsAutomatically fix or flag supporting text validation errors based on confidence thresholds.
# Repair a single quote
linkml-reference-validator repair text <TEXT> <REFERENCE_ID> [OPTIONS]
# Repair a data file (dry run by default)
linkml-reference-validator repair data <DATA_FILE> --schema <SCHEMA> [OPTIONS]
Example - Single Quote:
# Try to repair a quote with ASCII subscript
linkml-reference-validator repair text "CO2 levels were measured" PMID:12345678
# Output:
# ✓ Repaired successfully
# Original: CO2 levels were measured
# Repaired: CO₂ levels were measured
# Action: CHARACTER_NORMALIZATION
# Confidence: HIGH
Example - Data File:
# Dry run - show what would be changed
linkml-reference-validator repair data disease.yaml \
--schema schema.yaml \
--dry-run
# Apply auto-fixes (creates backup)
linkml-reference-validator repair data disease.yaml \
--schema schema.yaml \
--no-dry-run
# Custom output file
linkml-reference-validator repair data disease.yaml \
--schema schema.yaml \
--no-dry-run \
--output repaired.yaml
Repair Report Output:
============================================================
Repair Report
============================================================
HIGH CONFIDENCE FIXES (auto-applicable):
PMID:12345678 at evidence[0]:
Character normalization fix
'CO2 levels...' → 'CO₂ levels...'
SUGGESTED FIXES (review recommended):
PMID:23456789 at evidence[1]:
Inserted ellipsis between non-contiguous parts
RECOMMENDED REMOVALS (low confidence):
PMID:34567890 at evidence[2]:
Similarity: 8%
Snippet: 'Fabricated text that...'
------------------------------------------------------------
Summary:
Total items: 5
Already valid: 2
Auto-fixes: 1
Suggestions: 1
Removals: 1
Unverifiable: 0
Repair Strategies:
| Strategy | Confidence | Description |
|---|---|---|
| Character Normalization | HIGH | Fix Unicode/symbol differences (CO2→CO₂, +/-→±) |
| Ellipsis Insertion | MEDIUM | Insert ... between non-contiguous text parts |
| Fuzzy Correction | VARIES | Suggest closest matching text from reference |
| Removal | VERY_LOW | Flag fabricated/not-found text for manual removal |
Options:
--dry-run / --no-dry-run - Show changes without applying (default: dry-run)--auto-fix-threshold FLOAT - Minimum similarity for auto-fixes (default: 0.95)--output PATH - Output file path (default: overwrite with backup)--config PATH - Path to repair configuration file--cache-dir PATH - Directory for caching references--verbose - Show detailed outputcache reference - Pre-cache ReferencesDownload and cache references for offline use.
linkml-reference-validator cache reference <REFERENCE_ID> [OPTIONS]
Example:
# Cache a single reference
linkml-reference-validator cache reference PMID:12345678
# Output:
Fetching PMID:12345678...
✓ Successfully cached PMID:12345678
Title: TP53 in cell cycle control
Authors: Smith J, Doe A, Johnson K
Content type: full_text_xml
Content length: 45231 characters
Use Cases:
linkml-validateThe recommended way to use this tool is as a LinkML validation plugin with the standard linkml-validate command.
1. Install both packages:
uv pip install linkml linkml-reference-validator
2. Create your schema with interface markers:
# my_schema.yaml
id: https://example.org/my-schema
name: my-schema
prefixes:
linkml: https://w3id.org/linkml/
classes:
Evidence:
attributes:
reference:
range: Reference
implements:
- linkml:authoritative_reference # <-- This marks it as a reference
supporting_text:
range: string
implements:
- linkml:excerpt # <-- This marks it as text to validate
3. Validate using linkml-validate:
linkml-validate \
--schema my_schema.yaml \
--validate-plugins linkml_reference_validator.plugins.ReferenceValidationPlugin \
my_data.yaml
✅ Integrated validation - Combines schema validation + reference validation in one command ✅ Standard LinkML workflow - Uses familiar LinkML tools ✅ Flexible schema design - Works with any schema using the interface pattern ✅ Rich error reporting - Shows exactly where validation fails in your data
reference:
id: PMID:12345678
supporting_text: "protein functions in cells"
Fetches:
ID Formats:
PMID:1234567812345678 (assumes PMID)Preprints are supported as first-class references:
reference:
id: DOI:10.1101/2024.01.01.573333 # by DOI
supporting_text: "the kinase phosphorylates the substrate in vitro"
reference:
id: PPR:PPR123456 # by Europe PMC preprint id
supporting_text: "the kinase phosphorylates the substrate in vitro"
Fetches:
fulltextRepo PDF route (epmc_preprint
provider), for the Europe-PMC-ingested subsetis_preprint / peer_review_status flags, so downstream KBs can apply
"not sole support" policiesID Formats:
DOI:10.1101/... (Crossref posted-content is detected as a preprint)PPR:PPR123456 (Europe PMC SRC:PPR id)PMID: pathSee the full-text guide for details and coverage caveats.
reference:
id: WIKIPEDIA:Saanen_goat
supporting_text: "The Saanen originates in the historic region of Saanen"
Fetches:
See Validating Wikipedia Articles.
For the validator to work, your LinkML schema must:
Use implements: [linkml:authoritative_reference] on slots that contain references:
classes:
Evidence:
attributes:
reference: # Can be nested object
range: Reference
implements:
- linkml:authoritative_reference
OR use a flat structure:
classes:
Evidence:
attributes:
reference_id: # Can be flat string
range: string
implements:
- linkml:authoritative_reference
Use implements: [linkml:excerpt] on slots containing quoted text:
classes:
Evidence:
attributes:
supporting_text: # The quote to validate
range: string
implements:
- linkml:excerpt
If using nested references, define the Reference class:
classes:
Reference:
attributes:
id:
identifier: true
range: string
title: # Optional: validates if provided
range: string
evidence:
reference:
id: PMID:12345678
title: "Study of Protein X"
supporting_text: "protein functions in cell cycle regulation"
evidence:
reference_id: PMID:12345678
supporting_text: "protein functions in cell cycle regulation"
statement:
text: "Protein X has multiple functions"
evidence:
- reference:
id: PMID:11111111
supporting_text: "protein functions in cell cycle"
- reference:
id: PMID:22222222
supporting_text: "protein regulates DNA repair"
[...]Use square brackets for editorial insertions that should be ignored during matching:
supporting_text: "protein [X] functions in cell cycle regulation"
# Matches: "protein functions in cell cycle regulation"
# Ignores: "X"
Use cases:
[sic] - Original spelling[emphasis added] - Added emphasis[gene name] - Clarifications[...] - Omitted content markersIf your corpus uses square brackets as literal source text, configure patterns that should be preserved instead of stripped:
validation:
literal_bracket_patterns:
- "\\d" # keep [2Fe-2S], [+21], [30S], [Ca2+]
- "[()+]" # keep notation with parens/charges, e.g. [poly(A)+]
Patterns are matched against the content inside [...]. If any pattern matches,
that bracketed text is preserved (not stripped) before normalization and validation.
Keep patterns specific to your notation. A pattern broad enough to also match
ordinary editorial notes will preserve those too — for example ^\S matches any
note that doesn't start with a space, so it would keep [important] as well as
[poly(A)+].
...Use ellipsis for gaps in quoted text:
supporting_text: "protein functions ... in cell cycle regulation"
# Matches both parts independently:
# - "protein functions"
# - "in cell cycle regulation"
Requirements:
Before matching, text is normalized:
Examples:
"T-Cell Receptor" → "t cell receptor"
"TP53 (p53) protein" → "tp53 p53 protein"
"α-catenin" → "alpha catenin"
"β-actin" → "beta actin"
"γ-tubulin" → "gamma tubulin"
Greek Letter Support:
All Greek letters (both uppercase and lowercase) are converted to their spelled-out English equivalents. This ensures:
References are automatically cached to disk to:
references_cache/
├── PMID_12345678.md
├── PMID_98765432.md
└── PMC_7654321.md
Cache files are stored as Markdown with YAML frontmatter for easy readability and compatibility:
---
reference_id: PMID:12345678
title: TP53 in cell cycle control
authors:
- Smith J
- Doe A
- Johnson K
journal: Nature
year: '2024'
doi: 10.1038/nature12345
content_type: full_text_xml
---
# TP53 in cell cycle control
**Authors:** Smith J, Doe A, Johnson K
**Journal:** Nature (2024)
**DOI:** [10.1038/nature12345](https://doi.org/10.1038/nature12345)
## Content
[Full text content follows...]
Note: The validator still supports reading legacy .txt format cache files for backward compatibility.
# Use custom cache directory
linkml-reference-validator validate text \
"quote" PMID:123 \
--cache-dir /path/to/cache
# Pre-cache references
linkml-reference-validator cache reference PMID:12345678
# Force re-fetch (bypass cache)
linkml-reference-validator cache reference PMID:12345678 --force
Schema (gene.yaml):
id: https://example.org/genes
name: gene-schema
classes:
GeneFunctionStatement:
tree_root: true
attributes:
gene_symbol:
range: string
function_description:
range: string
evidence:
range: Evidence
Evidence:
attributes:
reference:
range: Reference
implements:
- linkml:authoritative_reference
supporting_text:
range: string
implements:
- linkml:excerpt
Reference:
attributes:
id:
identifier: true
Data (tp53.yaml):
gene_symbol: TP53
function_description: "tumor suppressor"
evidence:
reference:
id: PMID:12345678
supporting_text: "TP53 functions as a tumor suppressor"
Validation:
linkml-validate \
--schema gene.yaml \
--validate-plugins linkml_reference_validator.plugins.ReferenceValidationPlugin \
tp53.yaml
# Check if a quote is in a paper
linkml-reference-validator validate text \
"protein kinase activity regulates cell proliferation" \
PMID:12345678
# With editorial note
linkml-reference-validator validate text \
"protein kinase [PKA] activity regulates cell proliferation" \
PMID:12345678
# Multi-part quote
linkml-reference-validator validate text \
"protein kinase activity ... regulates cell proliferation" \
PMID:12345678
Data (gene_annotations.yaml):
- gene_symbol: BRCA1
annotations:
- function: "DNA repair"
evidence:
reference:
id: PMID:11111111
supporting_text: "BRCA1 plays a critical role in DNA repair"
- function: "tumor suppressor"
evidence:
reference:
id: PMID:22222222
supporting_text: "BRCA1 functions as a tumor suppressor"
- gene_symbol: TP53
annotations:
- function: "cell cycle regulation"
evidence:
reference:
id: PMID:33333333
supporting_text: "TP53 regulates cell cycle checkpoints"
Validation:
linkml-reference-validator validate data \
gene_annotations.yaml \
--schema gene_schema.yaml \
--verbose
# Output shows validation for each reference:
# ✓ PMID:11111111 - "BRCA1 plays a critical role in DNA repair"
# ✓ PMID:22222222 - "BRCA1 functions as a tumor suppressor"
# ✓ PMID:33333333 - "TP53 regulates cell cycle checkpoints"
✅ Exact substring match (after normalization)
supporting_text: "protein functions in cells"
reference_content: "The protein functions in cells during mitosis."
# ✓ PASS - exact substring found
✅ Multi-part match
supporting_text: "protein functions ... during mitosis"
reference_content: "The protein functions in cells during mitosis."
# ✓ PASS - both parts found
✅ Editorial notes ignored
supporting_text: "protein [X] functions"
reference_content: "The protein functions in cells."
# ✓ PASS - [X] ignored in matching
✅ Case and punctuation normalized
supporting_text: "T-Cell Receptor"
reference_content: "The t cell receptor binds antigens."
# ✓ PASS - normalized to "t cell receptor"
❌ Text not in reference
supporting_text: "protein inhibits apoptosis"
reference_content: "The protein functions in cells."
# ✗ FAIL - "inhibits apoptosis" not found
❌ Partial multi-part match
supporting_text: "protein functions ... inhibits apoptosis"
reference_content: "The protein functions in cells."
# ✗ FAIL - second part not found
❌ Reference not accessible
supporting_text: "any quote"
reference_id: PMID:99999999
# ✗ FAIL - reference doesn't exist or can't be fetched
❌ Title mismatch (when title provided)
reference:
id: PMID:12345678
title: "Wrong Title"
supporting_text: "correct quote"
# ✗ FAIL - title doesn't match fetched reference
You can create a .linkml-reference-validator.yaml file in your project root to configure validation behavior:
validation:
cache_dir: references_cache
rate_limit_delay: 0.5
# Skip validation for specific prefixes (useful for unsupported reference types)
skip_prefixes:
- SRA # Sequence Read Archive
- MGNIFY # MGnify database
- BIOPROJECT # NCBI BioProject (currently has API issues)
# Control severity for unfetchable references
unknown_prefix_severity: WARNING # Options: ERROR, WARNING, INFO
# Map alternate prefixes to canonical ones
reference_prefix_map:
geo: GEO
NCBIGeo: GEO
skip_prefixes (list of strings)List of reference prefixes to skip during validation. References with these prefixes will return is_valid=True with INFO severity, allowing validation to pass without blocking your workflow.
Use cases:
Example:
validation:
skip_prefixes:
- SRA
- MGNIFY
- BIOPROJECT
With this configuration:
# These will pass validation with INFO severity
linkml-reference-validator validate text "some text" SRA:PRJNA290729
# ✓ Valid: True (INFO) - Skipping validation for reference with prefix 'SRA'
linkml-reference-validator validate text "some text" MGNIFY:MGYS00000596
# ✓ Valid: True (INFO) - Skipping validation for reference with prefix 'MGNIFY'
unknown_prefix_severity (ERROR | WARNING | INFO)Control the severity level for references that cannot be fetched (unsupported prefix, network error, etc.). Default: ERROR
Options:
ERROR (default) - Validation fails, blocking workflowWARNING - Validation fails but with lower severityINFO - Validation fails but logged as informationalNote: skip_prefixes takes precedence over unknown_prefix_severity. If a prefix is in skip_prefixes, it will return is_valid=True with INFO severity regardless of this setting.
Example:
validation:
skip_prefixes:
- SRA # These will be skipped (is_valid=True, INFO)
unknown_prefix_severity: WARNING # Other unfetchable refs get WARNING
With this configuration:
# SRA is skipped (from skip_prefixes)
linkml-reference-validator validate text "text" SRA:PRJNA290729
# ✓ Valid: True (INFO) - Skipping validation
# UNKNOWN prefix gets WARNING severity
linkml-reference-validator validate text "text" UNKNOWN:12345
# ✗ Valid: False (WARNING) - Could not fetch reference
source_extra_fields (per-source JSONPath map)Capture additional fields from reference API responses and append them to cached content so they are included in validation. Keys are source prefixes (e.g. clinicaltrials, PMID, DOI, GEO); values map a field name to a JSONPath expression into the raw API response. Prefer paths to a single value (string/number). If the path selects a list, its elements are converted to strings and joined with spaces. If it selects an object or other type, its string representation is used.
Example:
Save as my-config.yaml:
validation:
source_extra_fields:
clinicaltrials:
eligibility: "$.protocolSection.eligibilityModule.eligibilityCriteria"
outcomes: "$.protocolSection.outcomesModule.primaryOutcomes"
Pass this config when fetching so the cache includes these sections: use --config my-config.yaml with cache reference or validate. Captured field names are stored in extra_fields_captured in the cache frontmatter.
# Fetch and cache a trial with extra fields (eligibility, outcomes)
linkml-reference-validator cache reference clinicaltrials:NCT00000001 --config my-config.yaml
# Validate text against the cached content (including extra sections)
linkml-reference-validator validate text "Inclusion: age >= 18" clinicaltrials:NCT00001372 --config my-config.yaml
Default: references_cache/ in current directory
# Custom cache location
export REFERENCE_CACHE_DIR=/path/to/cache
linkml-reference-validator validate text "quote" PMID:123
# Or use CLI option
linkml-reference-validator validate text "quote" PMID:123 \
--cache-dir /path/to/cache
The tool respects NCBI API rate limits (3 requests/second without API key).
Optional: Set email for NCBI Entrez (recommended):
export NCBI_EMAIL="[email protected]"
Optional: Use NCBI API key for higher rate limits:
export NCBI_API_KEY="your_api_key_here"
Causes:
Solutions:
# Verify PMID exists on PubMed
# Check network connection
# Try again later (NCBI may be down)
Causes:
Solutions:
# Check if article has abstract on PubMed
# Look for PMC full text availability
# Try a different reference
Causes:
Solutions:
# Verify exact quote from PDF/HTML
# Try shorter, more specific quote
# Check if text is in figure caption
# Use editorial notes for differences: "protein [X] functions"
Cause:
"[editorial note]"Solution:
# Include actual quote text
supporting_text: "protein functions [in cells]"
# Not just: "[editorial note]"
Pre-cache references:
# Cache all references before validation
for pmid in PMID:111 PMID:222 PMID:333; do
linkml-reference-validator cache reference $pmid
done
Reuse cache directory:
# Share cache across projects
export REFERENCE_CACHE_DIR=~/.reference_cache
Use verbose mode to see what's slow:
linkml-reference-validator validate data data.yaml \
--schema schema.yaml \
--verbose
# Clone repository
git clone https://github.com/linkml/linkml-reference-validator
cd linkml-reference-validator
# Install with dev dependencies
uv sync --group dev
# Run tests
just test
# Run specific test
uv run pytest tests/test_cli.py::test_validate_text_command_success
linkml-reference-validator/
├── src/linkml_reference_validator/
│ ├── cli.py # CLI commands
│ ├── models.py # Data models
│ ├── validation/
│ │ └── supporting_text_validator.py # Core validation logic
│ ├── etl/
│ │ └── reference_fetcher.py # Reference fetching
│ └── plugins/
│ └── reference_validation_plugin.py # LinkML plugin
├── tests/
│ ├── fixtures/ # Test reference files
│ ├── test_cli.py # CLI tests
│ ├── test_e2e_integration.py # End-to-end tests
│ └── ...
├── justfile # Development commands
└── pyproject.toml # Project configuration
# All tests
just test
# Just pytest
just pytest
# With coverage
uv run pytest --cov=src/linkml_reference_validator
# Specific test file
uv run pytest tests/test_cli.py
# Doctests
just doctest
While the CLI is recommended, you can also use the Python API:
from linkml_reference_validator.validation.supporting_text_validator import (
SupportingTextValidator
)
from linkml_reference_validator.models import ReferenceValidationConfig
# Create validator
config = ReferenceValidationConfig(cache_dir="my_cache")
validator = SupportingTextValidator(config)
# Validate text
result = validator.validate(
supporting_text="protein functions in cell cycle regulation",
reference_id="PMID:12345678",
)
print(result.is_valid) # True/False
print(result.message) # Validation message
| Manual | linkml-reference-validator |
|---|---|
| ❌ Time consuming | ✅ Automated |
| ❌ Error prone | ✅ Consistent |
| ❌ Not scalable | ✅ Validates 100s of quotes |
| ❌ Not reproducible | ✅ Cached, versioned |
linkml-reference-validator uses deterministic substring matching, not fuzzy matching:
✅ Predictable - Same input always gives same result ✅ Explainable - Easy to understand why validation passed/failed ✅ No false positives - Won't accept paraphrased text ✅ Fast - No complex similarity calculations
Contributions welcome! See CONTRIBUTING.md for guidelines.
If you use this tool in your research, please cite:
@software{linkml_reference_validator,
title = {linkml-reference-validator: Validation of supporting text from references},
author = {Mungall, Chris},
year = {2024},
url = {https://github.com/linkml/linkml-reference-validator}
}
Apache 2.0 - see LICENSE