kniv v7 small (ettin-encoder-32m)
One English encoder that gives a full linguistic analysis and extracts knowledge against a schema you supply at inference time.
- Linguistic layer: part of speech, morphology, lemmas, named entities, dependency trees, semantic roles, dialogue act and sentiment per sentence, coreference.
- Knowledge layer: entities of the types you name, facts for the
relations you name (a name plus an optional description), and for every
fact its polarity, modality, holder and time. In a conversation each
speaker is an entity, so "I live in Berlin" yields
(user, lives in, Berlin).
A small distilled student of the two kniv v7 models, kniv-v7-xlarge-u-en and kniv-v7-xlarge-d-en, on a 32M-parameter encoder (76M parameters with the heads): the same outputs, graphs and runtime as the larger models. It is close to them on tagging and parsing (POS 0.972, LAS 0.911) and gives usable entities, roles and facts, but it is clearly weaker on NER, semantic roles, coreference, relations and entity types (see Results and Limitations). For better quality at moderate size use kniv-v7-large-en.
GUIDE.md shows the known failure modes of the kniv v7 models and how to work around them in your own code; they apply to this model too.
The kniv v7 models, smallest first:
| model | base encoder | parameters | role |
|---|---|---|---|
| kniv-v7-tiny-en | ettin-encoder-17m | 55M | distilled student |
| kniv-v7-small-en (this model) | ettin-encoder-32m | 76M | distilled student |
| kniv-v7-medium-en | ettin-encoder-68m | 122M | distilled student |
| kniv-v7-large-en | ModernBERT-base | 221M | distilled student |
| kniv-v7-xlarge-d-en | deberta-v3-large | 538M | teacher |
| kniv-v7-xlarge-u-en | UltraBERT-380M | 485M | teacher |
Model details
| developed by | Dragonscale (dragonscale-ai) |
| model type | multi-task transformer encoder |
| version | kniv v7 (ettin-32m-s30-coref), 2026-11-01 |
| base model | jhu-clsp/ettin-encoder-32m |
| parameters | 76M |
| input | up to 512 words per window (1,024 subwords) |
| language | English |
| licence | Apache-2.0 |
Uses
Intended. Analysing English prose and conversations; extracting entities and facts for your own schema without fine-tuning, for agent memory, knowledge graphs, search and document analytics.
Out of scope.
- Languages other than English.
- Acting on extracted facts without review where errors matter (medical, legal, financial or employment decisions about people).
- Profiling individuals without their consent.
Installation
Everything needed to run the model is in this repository: the ONNX graphs,
the tokenizer, the settings, and the kniv_v7 Python package that turns text
into results. There is nothing else to install from elsewhere: download the
repository and put it on your Python path.
pip install onnxruntime tokenizers numpy huggingface_hub
import sys
from huggingface_hub import snapshot_download
path = snapshot_download("dragonscale-ai/kniv-v7-small-en") # includes kniv_v7/
sys.path.insert(0, path)
from kniv_v7 import Kniv
Quick start
kniv = Kniv(path)
out = kniv.analyse(
turns=[("user", "I live in Berlin and work for Siemens."),
("assistant", "Nice, how long have you been there?")],
relations=[{"name": "lives in", "description": "the place where the head resides"},
{"name": "employer", "description": "the organization the head works for"}],
entity_types=[{"name": "location"}, {"name": "organization"}, {"name": "person"}])
w = out[0]
for f in w["facts"]:
print(w["entities"][f["head"]]["text"], f["relation"], w["entities"][f["tail"]]["text"],
f["polarity"], f["modality"])
# user lives in Berlin positive asserted
# user employer Siemens positive asserted
Plain text works the same way:
out = kniv.analyse(
"Henry Wager Halleck (January 16, 1815 โ January 9, 1872) was a United States Army "
"officer, scholar, and lawyer.",
relations=[{"name": "date of birth", "description": "date on which the subject was born"},
{"name": "date of death", "description": "date on which the subject died"},
{"name": "military branch", "description": "branch to which this person belongs"}],
entity_types=[{"name": "person"}, {"name": "organization"}, {"name": "date"}])
w = out[0]
for f in w["facts"]:
print(w["entities"][f["head"]]["text"], f["relation"], w["entities"][f["tail"]]["text"],
f["polarity"], f["modality"])
# Henry Wager Halleck date of birth January 16 , 1815 positive asserted
# Henry Wager Halleck date of death January 9 , 1872 positive asserted
# Henry Wager Halleck military branch United States Army positive asserted
Every kniv v7 model returns exactly these facts for both examples, in fp32 and INT8.
(The sentence is from the English Wikipedia article on Henry Halleck, CC BY-SA 4.0.)
Pass text as kniv.analyse(text, ...) (paragraphs separated by blank lines)
or a conversation as turns. analyse returns one record per window:
| key | content |
|---|---|
tokens, sentences |
words; sentence spans (and speakers) |
words |
per word: pos, morph, lemma, head (0 = root, else 1-based), deprel |
ner |
[type, start, end) named-entity spans |
srl |
frames: predicate and args as [role, start, end) |
sentence_layers |
per sentence: dialogue acts (cls), sentiment |
coref |
clusters of [start, end] word spans (inclusive) |
entities |
type, text, mentions, spans (speakers first in a conversation) |
facts |
head, relation, tail (indices into entities), margin, polarity, modality, holder, time |
kniv.extract(...) returns entities and facts only. Without entity_types,
entities get six coarse types (PER, ORG, LOC, TIME, NUM, MISC).
Variants
| variant | load | for |
|---|---|---|
| fp32 | Kniv(path) |
reference |
| FP16 | Kniv(path, variant="fp16") |
half the download; GPUs (providers=["CUDAExecutionProvider"]) |
| INT8 | Kniv(path, int8=True) |
CPU speed: about 1.2x faster than fp32 (at this size much of the time is in the heads and the host code) |
FP16 matches fp32 within 0.001 on every task. INT8 stays within 0.8 points. To download only the FP16 files:
path = snapshot_download("dragonscale-ai/kniv-v7-small-en", ignore_patterns=[
"model.pt", "onnx/plain.onnx", "onnx/label.onnx", "onnx/marker.onnx"])
kniv = Kniv(path, variant="fp16")
PyTorch
model.pt holds the PyTorch weights. They are used through the model code in
kniv-nlp-models:
from kniv_v7 import Kniv
from v6.export.torch_backend import TorchBackend
kniv = Kniv(path, backend=TorchBackend.from_release(path))
How it works
One encoder runs up to three times per window:
- Plain pass. Every linguistic head, coreference and the entity typer.
- Label pass. Your type and relation names become vectors, cached per schema.
- Marker pass. The window with every entity mention marked. Every entity pair is scored against every relation, then each kept fact's attributes.
Decoding thresholds are in calibration.json, and every one can be changed
at run time (see GUIDE.md).
Results
| benchmark | score |
|---|---|
| UD English EWT, POS | 0.972 |
| UD English EWT, lemma / morphology | 0.974 / 0.972 |
| UD English EWT, UAS / LAS | 0.932 / 0.911 |
| OntoNotes 5, NER | 0.827 |
| PropBank, SRL | 0.773 |
| SGD, dialogue acts | 0.756 |
| LitBank, coreference | 0.620 |
| Re-DocRED, relations (gold / predicted entities) | 0.672 / 0.567 |
| FewRel, zero-shot relations (5 / 10 / 15-way) | 0.787 / 0.667 / 0.577 |
| CrossNER, zero-shot entity types | 0.410 |
| Few-NERD, entity types | 0.461 |
| LongMemEval, answer in extracted entities / facts | 0.747 / 0.447 |
Bias, risks and limitations
- Coverage. People, places and organisations from well-covered regions are recognised and related more reliably than others.
- Privacy. The model extracts facts about people, speakers included. Store and use them with the consent and data-protection care that implies.
- Facts are candidates. Treat each fact as a candidate with a confidence
(
margin), not as verified truth. Keep a human in the loop where facts about people drive decisions. - Document-level relations. More facts are missed than with the larger models (Re-DocRED end to end 0.567 against 0.627 for the better teacher and 0.608 for the 150M student). Lower the thresholds for recall (GUIDE.md).
- Harder tasks. Against the better teacher: NER 0.827 against 0.870, semantic roles 0.773 against 0.810, relations with gold entities 0.672 against 0.733.
- Coreference is the largest gap: LitBank 0.620 against 0.707.
- New entity types. Typing entities with type names it has not seen: CrossNER 0.410 against 0.580, Few-NERD 0.461 against 0.523.
- More facts, lower precision. On conversations it returns more facts than the
larger models (178 per LongMemEval question, against 134 for the 150M student and
73 for the UltraBERT model), many with low margins. Raise the thresholds or
filter on
marginfor precision (GUIDE.md). - Entity grouping is rule-based. Partial names ("Obama" / "Barack Obama") are not merged.
- Per-window facts. Facts are per window; nothing is linked across windows or documents.
- Long windows. Text past 1,024 subwords in a window is not analysed.
- English only.
Licence
Apache-2.0. The base encoder (jhu-clsp/ettin-encoder-32m) is MIT.
Citation
@misc{kniv_v7_2026,
title = {kniv v7: one encoder for linguistic analysis and schema-conditioned knowledge extraction},
author = {{Dragonscale}},
year = {2026},
howpublished = {\url{https://huggingface.co/dragonscale-ai/kniv-v7-small-en}}
}
Contact
Questions and issues: https://github.com/rustic-ai/kniv-nlp-models/issues.
- Downloads last month
- 24
Model tree for dragonscale-ai/kniv-v7-small-en
Base model
jhu-clsp/ettin-encoder-32mCollection including dragonscale-ai/kniv-v7-small-en
Article mentioning dragonscale-ai/kniv-v7-small-en
Evaluation results
- UPOS accuracy on UD English EWTtest set self-reported0.972
- LAS on UD English EWTtest set self-reported0.911
- span F1 on OntoNotes 5.0test set self-reported0.827
- end-to-end argument F1 on PropBank EWTtest set self-reported0.773
- CoNLL F1 on LitBanktest set self-reported0.620
- F1 (gold entities) on Re-DocREDtest set self-reported0.672
- F1 (end to end) on Re-DocREDtest set self-reported0.567
- macro F1, m=5 on FewRel (15 unseen)self-reported0.787