running-zeroshot-ner
GitHub用于在临床或生物医学文本中进行零样本命名实体识别,支持运行时自定义标签且无需微调。通过OpenMed封装GLiNER模型,提供CLI和Python API,适用于无标注数据或动态标签场景。
Trigger Scenarios
Install
npx skills add maziyarpanahi/openmed --skill running-zeroshot-ner -g -y
SKILL.md
Frontmatter
{
"name": "running-zeroshot-ner",
"license": "Apache-2.0",
"metadata": {
"pairs": "adjacent",
"project": "OpenMed",
"version": "1.0",
"category": "openmed-core"
},
"description": "Extract arbitrary, custom entity types from clinical or biomedical text with no fine-tuning using OpenMed's GLiNER \/ GLiNER2 zero-shot support. Use when the user wants to define their own labels on the fly (e.g. Drug, Symptom, Device, Procedure), has no labelled data or a label set not covered by a fine-tuned model, or asks about openmed zero deps\/index\/infer, the gliner extra, or GLiNER. Pairs adjacent to extracting-clinical-entities (use that for high-accuracy fixed-schema NER) and loading-openmed-models."
}
Running Zero-Shot NER
Zero-shot NER lets you extract entity types you name at inference time — no
training, no labelled data. OpenMed wraps GLiNER (v1) and GLiNER2 behind a small
index + inference layer, exposed via the openmed zero CLI and the openmed.ner
Python API. It runs on-device.
When to use
- Your label set is custom or evolving ("Device", "Implant", "Allergen") and no fine-tuned OpenMed model emits exactly those labels.
- You have no labelled data to fine-tune with.
- You need a quick prototype or a one-off extraction over an unusual schema.
When to prefer a fine-tuned model instead (extracting-clinical-entities):
for a fixed, well-supported schema (diseases, drugs, anatomy), a fine-tuned
OpenMed model is more accurate and faster than zero-shot. Zero-shot trades some
accuracy for total label flexibility — use it for coverage of new types, then
graduate to a fine-tuned model once the schema stabilises.
Install
pip install "openmed[gliner]" # pulls GLiNER (and GLiNER2 if a recent gliner is installed)
openmed zero deps # diagnostic: prints "GLiNER v1: ok" / "GLiNER v2: ok"
openmed zero deps only checks availability — it does not install anything.
The two-step workflow: index, then infer
GLiNER checkpoints live as local model directories. OpenMed resolves them by a
short model_id via an index.json, so you build the index once and run inference
many times.
openmed zero index <models_dir>— scan a directory of downloaded GLiNER / GLiNER2 checkpoints and writeindex.json(model ids, family, domains, paths).openmed zero infer "<text>" --model-id <id>— run extraction against a model from the index, with labels you supply.
# 1) Build the index over your local models (writes <models_dir>/index.json)
openmed zero index /models/gliner --output /models/gliner/index.json
# 2) Run zero-shot NER with your OWN labels (comma-separated)
openmed zero infer "Patient on insulin glargine via an insulin pump for type 1 diabetes." \
--model-id gliner-biomedical \
--labels "Drug,Device,Disease" \
--threshold 0.5 \
--index-path /models/gliner/index.json
Output is JSON: each entity has text, start, end, label, and score.
CLI flags:
zero infer: positionaltext;--model-id/-m(required, an id from the index),--labels/-l(comma-separated custom labels),--domain/-d(label preset hint),--threshold/-c(default0.5),--index-path/-i.zero index: positionalmodels_dir;--output/-o,--pretty/--compact.
If you omit --labels, OpenMed falls back to the --domain defaults (or generic
defaults). Passing explicit --labels is what makes it truly zero-shot.
Python API
The same flow in code via openmed.ner:
from openmed.ner import infer, NerRequest
request = NerRequest(
model_id="gliner-biomedical", # id from your index.json
text="Started on insulin glargine via an insulin pump for type 1 diabetes.",
labels=["Drug", "Device", "Disease"], # your custom labels — no fine-tuning
threshold=0.5,
)
response = infer(request, index_path="/models/gliner/index.json")
for ent in response.entities:
print(f"{ent.label:8} {ent.text!r:30} {ent.score:.2f} [{ent.start}:{ent.end}]")
NerRequest fields: model_id, text, labels (None ⇒ domain/default labels),
domain, threshold. infer(...) returns a NerResponse whose .entities are
Entity objects with .text, .start, .end, .label, .score.
Build / load the index from Python too:
from openmed.ner import build_index, write_index, load_index, is_gliner_available
if is_gliner_available():
index = build_index("/models/gliner")
write_index(index, "/models/gliner/index.json")
index = load_index("/models/gliner/index.json")
Helpful label utilities:
from openmed.ner import get_default_labels, available_domains
available_domains() # domains with built-in label presets
get_default_labels("clinical") # default labels for a domain hint
Writing good labels
Zero-shot quality hinges on label phrasing. Prefer natural, specific noun phrases:
- Good:
["Drug", "Medical Device", "Disease", "Symptom", "Procedure"] - Weak:
["X", "thing", "misc"]
Tune threshold to trade recall for precision. Start at 0.5 and raise it if you
see spurious spans.
Hand-off to / from OpenMed
- From
loading-openmed-models: zero-shot uses local GLiNER checkpoints rather than the OpenMed registry; download them once, then pointzero indexat the directory. - To
extracting-clinical-entities: once your label schema stabilises and a fine-tuned OpenMed model covers it, switch toopenmed.analyze_textfor higher accuracy and speed. The output shape (label + offsets + score) is parallel, so downstream code changes little. - To de-identification: run
openmed.deidentifybefore zero-shot NER in a PHI workflow, then extract entities from the redacted text.
Edge cases & gotchas
zero inferneeds an index. Runzero index <models_dir>first, or pass a valid--index-path; the--model-idmust exist in that index.zero depsdoesn't install. It reports status only — install withpip install "openmed[gliner]".- GLiNER2 needs a recent
gliner(≈0.3.0+) and a GLiNER2/Fastino checkpoint;openmed zero depsshows whether v2 is available. - Accuracy vs. flexibility. Zero-shot is for coverage of new/custom types, not for squeezing out maximum F1 on a standard schema.
- Permissive licensing & local-first. Use permissively licensed GLiNER checkpoints; keep everything on-device and out of PHI logs.
Standards & references
- GLiNER (zero-shot NER): https://github.com/urchade/GLiNER
- GLiNER paper: https://arxiv.org/abs/2311.08526
- OpenMed model org: https://huggingface.co/OpenMed
Version History
- f213557 Current 2026-07-23 00:46


