Authoring an OntoGPT template
A template is a LinkML YAML file. OntoGPT turns each attribute of the root class into one line of the prompt, sends the text, parses the reply line by line, and grounds every value whose range is a NamedEntity subclass using that class's annotators. The template is the whole contract. Everything the model is told, and everything the grounder is allowed to return, lives in it.
Read references/schema-reference.md for every field OntoGPT reads, and references/ontology-guide.md to pick ontologies and annotator strings. Start from assets/template_skeleton.yaml.
Procedure
- Write the target as a record. List the fields. For each: single or list, free text or entity, and if entity, what kind. Sketch the YAML you want out before writing the schema. Keep it small: five to eight fields on the root, one level of nesting at most, two at the very most.
- Find the closest bundled template (
ontogpt list-templates, or the catalog in the ontogpt-select-template skill) and copy it. Its prefixes, annotators, and phrasing are tested.
- Choose ontologies per entity class with references/ontology-guide.md. Write
id_prefixes (bare prefixes, e.g. MONDO, HP, CHEBI) and annotators (sqlite:obo:mondo, sqlite:obo:hp) so that the prefixes the annotators return are in id_prefixes. Mismatch here is the most common failure: sqlite:obo:hp returns HP:, not HPO:.
- Write the prompts. Each attribute's
description (or annotations: prompt:) is what the model sees, verbatim, after field_name: <. Say what to extract, from the text only, and how to separate values (semicolon-separated list of ...). Add annotations: prompt.examples: with two to four short examples for anything the model might format oddly. Never mention identifiers or ontologies in prompts. The model produces labels; OAK produces ids.
- Set one
tree_root: true. Nested records are classes whose attributes are themselves entity or string fields; they are extracted by a second SPIRES pass per item. Mark them is_a: CompoundExpression when they represent relations.
- Validate.
uv run python skills/ontogpt-author-template/scripts/validate_template.py my_template.yaml --codegen
Fix every ERROR. Read every WARNING. Add --check-annotators once to confirm each annotator opens (this downloads ontologies).
- Run on one short text that contains known answers:
ontogpt -v extract -t my_template.yaml -i sample.txt
Check raw_completion_output (did the model answer the fields?), then extracted_object (did parsing keep them?), then the AUTO: values (did grounding fail?). Adjust prompts for the first, separators for the second, annotators and prefixes for the third.
- Install. A YAML path passed to
-t is copied into the package's templates directory and compiled; afterwards its bare name works. To contribute it to OntoGPT, add the YAML under src/ontogpt/templates/, run make to regenerate the pydantic module, and add a docs entry in mkdocs.yml.
Design rules that matter
- One entity class per ontology target.
Disease grounds to MONDO; do not reuse it for phenotypes.
- Descriptions are prompts. A description written for a human ("the disease field") produces bad extractions. Write "the name of the disease or condition the patient was diagnosed with, as written in the text".
- Say "semicolon-separated" on every multivalued field. The parser splits on
;. Commas inside values are kept.
- Free text stays
range: string. It is never grounded and never nested. Use it for quantities, dates, quoted findings.
- Enums restrict, not suggest. A fixed enum (
permissible_values) is appended to the prompt as "Must be one of: ...". A dynamic enum (reachable_from an ontology node) filters grounded ids after the fact and is invisible to the model.
prompt.skip: "true" hides a field from the prompt. Use it for provenance fields you fill later.
- Two annotators are usually enough. More annotators mean more downloads and more false partial matches. Order them by trust; the first hit wins.
- Test the grounding independently when in doubt:
runoak -i sqlite:obo:mondo annotate "type 2 diabetes".
Do not
- Do not put identifiers, URIs, or "use MONDO terms" in prompt text.
- Do not nest deeper than two levels; SPIRES recursion multiplies calls and errors.
- Do not use capital letters or hyphens in the file name; it becomes a Python module name.
- Do not declare a class
tree_root and is_a: NamedEntity at once unless the whole document is one entity (see ontology_class).
1---2name: ontogpt-author-template3description: Write or modify an OntoGPT extraction template (a LinkML schema with SPIRES annotations), including choosing the ontologies and OAK annotators that ground each entity class, setting id_prefixes, writing field prompts, and validating the schema before use. Use when no bundled template fits, when a template must ground to a different ontology, when asked to add fields or entity classes to a template, or when asked which ontology to use for a kind of entity.4license: BSD-3-Clause5---67# Authoring an OntoGPT template89A template is a LinkML YAML file. OntoGPT turns each attribute of the root class into one line of the prompt, sends the text, parses the reply line by line, and grounds every value whose range is a `NamedEntity` subclass using that class's `annotators`. The template is the whole contract. Everything the model is told, and everything the grounder is allowed to return, lives in it.1011Read [references/schema-reference.md](references/schema-reference.md) for every field OntoGPT reads, and [references/ontology-guide.md](references/ontology-guide.md) to pick ontologies and annotator strings. Start from [assets/template_skeleton.yaml](assets/template_skeleton.yaml).1213## Procedure14151. **Write the target as a record.** List the fields. For each: single or list, free text or entity, and if entity, what kind. Sketch the YAML you want *out* before writing the schema. Keep it small: five to eight fields on the root, one level of nesting at most, two at the very most.162. **Find the closest bundled template** (`ontogpt list-templates`, or the catalog in the `ontogpt-select-template` skill) and copy it. Its prefixes, annotators, and phrasing are tested.173. **Choose ontologies per entity class** with [references/ontology-guide.md](references/ontology-guide.md). Write `id_prefixes` (bare prefixes, e.g. `MONDO`, `HP`, `CHEBI`) and `annotators` (`sqlite:obo:mondo, sqlite:obo:hp`) so that the prefixes the annotators return are in `id_prefixes`. Mismatch here is the most common failure: `sqlite:obo:hp` returns `HP:`, not `HPO:`.184. **Write the prompts.** Each attribute's `description` (or `annotations: prompt:`) is what the model sees, verbatim, after `field_name: <`. Say what to extract, from the text only, and how to separate values (`semicolon-separated list of ...`). Add `annotations: prompt.examples:` with two to four short examples for anything the model might format oddly. Never mention identifiers or ontologies in prompts. The model produces labels; OAK produces ids.195. **Set one `tree_root: true`.** Nested records are classes whose attributes are themselves entity or string fields; they are extracted by a second SPIRES pass per item. Mark them `is_a: CompoundExpression` when they represent relations.206. **Validate.**21 ```bash22 uv run python skills/ontogpt-author-template/scripts/validate_template.py my_template.yaml --codegen23 ```24 Fix every ERROR. Read every WARNING. Add `--check-annotators` once to confirm each annotator opens (this downloads ontologies).257. **Run on one short text** that contains known answers:26 ```bash27 ontogpt -v extract -t my_template.yaml -i sample.txt28 ```29 Check `raw_completion_output` (did the model answer the fields?), then `extracted_object` (did parsing keep them?), then the `AUTO:` values (did grounding fail?). Adjust prompts for the first, separators for the second, annotators and prefixes for the third.308. **Install.** A YAML path passed to `-t` is copied into the package's templates directory and compiled; afterwards its bare name works. To contribute it to OntoGPT, add the YAML under `src/ontogpt/templates/`, run `make` to regenerate the pydantic module, and add a docs entry in `mkdocs.yml`.3132## Design rules that matter3334- **One entity class per ontology target.** `Disease` grounds to MONDO; do not reuse it for phenotypes.35- **Descriptions are prompts.** A description written for a human ("the disease field") produces bad extractions. Write "the name of the disease or condition the patient was diagnosed with, as written in the text".36- **Say "semicolon-separated" on every multivalued field.** The parser splits on `;`. Commas inside values are kept.37- **Free text stays `range: string`.** It is never grounded and never nested. Use it for quantities, dates, quoted findings.38- **Enums restrict, not suggest.** A fixed enum (`permissible_values`) is appended to the prompt as "Must be one of: ...". A dynamic enum (`reachable_from` an ontology node) filters grounded ids after the fact and is invisible to the model.39- **`prompt.skip: "true"`** hides a field from the prompt. Use it for provenance fields you fill later.40- **Two annotators are usually enough.** More annotators mean more downloads and more false partial matches. Order them by trust; the first hit wins.41- **Test the grounding independently** when in doubt: `runoak -i sqlite:obo:mondo annotate "type 2 diabetes"`.4243## Do not4445- Do not put identifiers, URIs, or "use MONDO terms" in prompt text.46- Do not nest deeper than two levels; SPIRES recursion multiplies calls and errors.47- Do not use capital letters or hyphens in the file name; it becomes a Python module name.48- Do not declare a class `tree_root` and `is_a: NamedEntity` at once unless the whole document is one entity (see `ontology_class`).