[FEATURE] Ontology-guided extraction and typing - #539
Open
halcoope wants to merge 1 commit into
Open
Conversation
|
Lexical Graph Coverage Report: The coverage is at unknown% (target: unknown%). Download the HTML report here. |
halcoope
force-pushed
the
ontology-guided-extraction
branch
from
September 11, 2026 18:14
a4e9720 to
3091051
Compare
|
Lexical Graph Coverage Report: The coverage is at unknown% (target: unknown%). Download the HTML report here. |
Lets a user supply an OWL/RDFS ontology as the extraction vocabulary, so that a graph names the same idea one way rather than several. Opt-in: with no ontology configured, the emitted graph queries and the extraction prompts are byte-identical to a build from before this change. Two mechanisms. The ontology's classes and properties, with their comments, are rendered into the extraction prompts, so the model is told the vocabulary instead of inventing one. After extraction, a deterministic transform component resolves each emitted name against the ontology and - depending on the authority the ontology is given - rewrites it to the authored spelling, or drops what does not conform. One setting spans that axis. `ontology_authority='off'` says nothing; `'align'`, the default, stores a matched concept under the ontology's name and keeps what the ontology does not declare; `'strict'` also excludes what does not conform. The level resolves to six independent dimensions, each individually overridable, so a single gate can be asked for by name. Optionally, values of declared datatype properties are coerced to their declared XSD type and written onto entity nodes as native graph properties, which is what makes a numeric or date comparison possible in a query. Coercion is lenient about lexical form - digit grouping, named-month dates - and strict about meaning: a literal that would need interpreting rather than parsing is refused rather than stored under a key whose name promises a number. The new code is one package, indexing/extract/ontology/: loading and validation, a plain-data read index that crosses the extraction process boundary, prompt rendering, XSD coercion, and the filter. Everything else is small edits at the call sites. Also included, and unrelated: pytest's default norecursedirs contains "build", so tests/unit/indexing/build/ had never been collected in a full-suite or CI run. Removing it surfaced six builder test files that no longer passed, plus five tests elsewhere that were already failing on main. Documented at docs-site/.../lexical-graph/ontology-guided-extraction.mdx.
halcoope
force-pushed
the
ontology-guided-extraction
branch
from
September 11, 2026 21:04
3091051 to
4e625b3
Compare
|
Lexical Graph Coverage Report: The coverage is at 73.7% (target: 80%). Download the HTML report here. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Title: [FEATURE] Ontology-guided extraction and typing
Description
Two related capabilities, both opt-in and both driven from an OWL/RDFS ontology you supply.
Extraction guided by a vocabulary. The ontology's entity types, permitted relationships
and attributes are rendered into the extraction prompts, and a deterministic filter then
resolves what the model emitted against the ontology — so a graph names the same idea one
way rather than several.
Companyrather thanCompanyandCORPORATION;worksForrather than
WORKS_FORandEMPLOYED BY.Typing of extracted values. Values of declared datatype properties are coerced to their
declared XSD type and written onto entity nodes as native graph properties, so
WHERE c.foundedYear < 2000becomes a query you can run against the graph rather thansomething only an LLM can answer from text.
With no ontology configured, the emitted graph queries and the extraction prompts are
byte-identical to a build from before this change.
Changes
Vocabulary
indexing/extract/ontology/(8 files, ~3,100 lines): ontology loading andstructural validation, a plain-data read index that crosses the extraction process
boundary, prompt rendering, XSD coercion, and the filter.
vocabulary instead of inventing one. Composed into the templates rather than substituted
into a placeholder, so a no-ontology prompt is unchanged and existing LLM caches stay
valid.
OntologyFilter, a deterministicTransformComponentrunning after the topicextractor: it resolves each emitted name, rewrites resolved names to the authored
spelling, drops what does not conform, and annotates survivors so the build stage needs no
ontology knowledge of its own.
ontology_authority='off'says nothing;'align'(thedefault) stores a matched concept under the ontology's name and keeps what the ontology
does not declare;
'strict'also excludes what does not conform. The level resolves to sixindependent dimensions, each individually overridable, so a single gate can be asked for by
name.
vocabulary_format:'prose'(default) renders three labelled sections, one peroutput channel;
'turtle'shows the ontology's own source.Typing
bounds, decimals, booleans, dates, times and URIs. Lenient about lexical form — digit
grouping, a zero fraction, named-month dates — and strict about meaning: a literal that
would need interpreting rather thahan stored under a key whose
name promises a number. Every value that coerces is JSON-serializable, because it travels
through node metadata into Cypher parameters.
typed_propertieschooses where:'off'(default) writesnothing,
'subject'keys it from the property's own local name on the subject entity,'complement'writestyped_value/datatypeon the value node,'both'does each toits own node.
enforce_datatypesmakes typing authoritative at'strict': a literal that does notparse as its declared type takes the fact with it, rather than being stored untyped.
entity insert is byte-identical at every placement and
value,search_strandclasscannot be disturbed. An ontology declaring a property name the graph model already owns is
refused at configuration time rath
Also
lexical_graph_index.py,build_pipeline.py,entity_graph_builder.py,local_entity_rewrites_graph_builder.py,config.py,constants.py,prompts.pyand tnorecursedirscontainsbuild, sotests/unit/indexing/build/had never been collected in a full-suite or CIrun. Removing it surfaced six buil passed, plus five tests
elsewhere already failing on main.es of test changes with
nothing to do with ontologies.
Problem
Extraction is by default only lightly guided: the LLM is seeded with preferred entity
classifications and asked to prefer are unguided altogether.
That keeps recall high, but the same concept can arrive under several names, which costs
retrieval precision and makes the graph hard to query directly.
Extracted attribute values have the a founding year or a
revenue figure is stored as the textng downstream can filter,
sort or compare on it. There was no way to say "these are the types and relationships I
want", and no way to have a declared datatype land as a native graph property.
Related issue (if any): none
Testing
Everything is driven from hand-built inputs and a small fixture ontology; no recorded
model output is checked in.
during development and deliberately removed: it required a specific Neptune Analytics
graph, so it would have been p
pytest)pre-existing live testsgated on
NEO4J_TEST_URI/Ss own invocation including--cov-fail-under=56`; total coverage 73.74%. The new package is at 100% of statementson six of seven modules and 99.4% on the seventh.
development, with typed properties confirmed present via
CALL neptune.graph.pg_schema(). Documentation site builds clean.Checklist
docs-site/src/content/docs/lextraction.mdx, plus theontologyparameter in the Indexing page'off'at every layer;typed_propertiesis delibera, so no ambientconfiguration can turn on graph writes. Enabling an ontology is a re-index boundary
(
normalize_namescan changeentity.class, which feeds the entity id), and th documented.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.