Architecture
This page describes the architecture of the curriculum-document processing pipeline in this repository. The pipeline transforms curriculum PDFs into source-grounded, Learning Commons-shaped knowledge graph artifacts while preserving provenance and separating document reconstruction from curriculum-semantic interpretation.
The implementation is document- and profile-driven. Example runtime configurations in this repository cover curricula from Ghana, Nigeria, Rwanda, and India; the same pipeline is intended to support additional curriculum documents through configuration rather than country-specific code paths.
Overview
The production pipeline has five conceptual stages implemented through four main CLI entry points:
flowchart TD
A[Curriculum PDF] --> B[1. Page IR extraction]
B -->|PageIR per page| C[2. Page IR continuity verification]
C -->|Verified PageIRs + pair verdicts| D[3. Document IR construction]
D -->|DocumentIR| E[4. Academic Standards KG construction]
E -->|Validated Academic Standards KG| F[5. Learning Components construction]
F --> G[Combined Academic Standards + Learning Components KG]
| Stage | Main CLI entry point | Primary input | Primary output |
|---|---|---|---|
| Page IR extraction | backend/src/kgfeg/entries/extract_page_ir.py |
Curriculum PDF | One PageIR JSON per page |
| Page IR continuity verification | backend/src/kgfeg/entries/verify_page_ir_continuity.py |
Page images + extracted PageIRs |
Verified PageIRs + boundary verdicts |
| Document IR construction | backend/src/kgfeg/entries/stitch_document_ir.py |
Verified PageIRs + verification evidence |
document_ir.json |
| Academic Standards KG construction | backend/src/kgfeg/entries/create_kgs.py |
DocumentIR + kgs.as configuration |
Academic Standards KG artifacts |
| Learning Components construction | backend/src/kgfeg/entries/create_kgs.py |
Validated Academic Standards KG + kgs.lc configuration |
Combined AS + LC KG artifacts |
The first three stages reconstruct the source document with progressively broader context. The final two stages perform curriculum-semantic interpretation and knowledge graph construction.
Core design principles
Preserve source fidelity before adding semantics
PageIR and DocumentIR are intentionally layout- and document-oriented. They preserve
what is visible in the source, how content is arranged, and how content continues across
pages without deciding that an item is a grade, strand, objective, competency, or other
curriculum-semantic entity.
Curriculum semantics are first asserted during Academic Standards KG construction. Learning Components are then derived from the validated Academic Standards graph rather than independently reinterpreting the PDF.
Bound LLM decisions
LLMs are used for judgments that require visual or semantic interpretation, but the pipeline constrains the evidence supplied to each task. Examples include:
- one rendered PDF page for Page IR extraction;
- a bounded pair of candidate items around an adjacent-page boundary for continuity verification;
- a bounded
DocumentIRextraction window for Standards Framework Item extraction; - a bounded duplicate-candidate set for SFI deduplication;
- a bounded candidate-parent set for
hasChildresolution; and - one or a small batch of eligible standards plus resolved hierarchy context for Learning Component generation.
The pipeline does not ask an LLM to infer an unconstrained whole-document graph in a single step.
Keep deterministic invariants in Python
Python owns the pieces that should not depend on model judgment. Depending on the stage, this includes:
- schema validation;
- page coordinates and bounding-box constraints;
- page-level boundary-state derivation;
- verification confidence gates;
- deterministic stitching and complete source-item consumption;
- source-window planning and candidate registries;
- graph endpoint, cardinality, reachability, and cycle checks;
- deterministic UUIDv5 identities for finalized graph entities and relationships;
- run-level coverage and reconciliation checks; and
- serialization of final graph artifacts.
A model judgment therefore must pass stage-specific validation and, where applicable, confidence and graph-integrity gates.
Use independent semantic checking where the assertion risk is highest
Several higher-risk semantic operations use a producer/checker pattern: one LLM
produces a structured semantic judgment and an independent checker validates or
corrects it. This pattern is used for source-grounded SFI extraction, SFI duplicate
resolution, hasChild parent selection, and Learning Component decomposition.
Learning Component semantic deduplication is intentionally different: deterministic blocking nominates bounded candidate pairs for one semantic judge, while Python owns request coverage, retries, conflict-aware clustering, deterministic canonicalization, and final graph reconciliation.
Treat provenance and intermediate artifacts as first-class outputs
The pipeline persists intermediate decisions rather than retaining only the final graph. Source page indexes, segment identities, extraction windows, candidate identities, merge decisions, hierarchy decisions, LC claims, unresolved items, and validation reports remain inspectable after a run.
These artifacts support:
- source tracing and audit;
- debugging individual decisions;
- resumable processing;
- deterministic reruns where applicable; and
- downstream review of unresolved or excluded material.
Runtime configuration and run layout
A pipeline run is configured through a single RunConfig. Its top-level namespaces
mirror the major stages:
page_ir_extraction
page_ir_verification
document_ir
kgs
as # Academic Standards
lc # Learning Components
The KG section is optional at the RunConfig schema level, allowing the extraction,
verification, and stitching stages to be used without constructing a KG.
For a source PDF, the pipeline computes a stable document key and stores stage outputs under that document-specific result directory. The main stage directories are:
Each stage cross-checks relevant upstream artifacts before proceeding so outputs from a different source document or incompatible run are not silently combined.
Stage 1: Page IR extraction
Purpose: Convert each rendered PDF page into a structured, layout-faithful
PageIR while keeping curriculum semantics light.
The extraction entry point renders each selected PDF page to PNG and invokes the Page IR extraction pipeline on the image. When enabled, text-layer and table-layer information extracted with PyMuPDF can be supplied as additional hints. Those hints assist the vision model but do not replace the rendered page as the visual source of truth.
A PageIR can contain ordered structures such as:
- text blocks and headings;
- lists;
- tables and table cells;
- figures and captions;
- bounding boxes in page-image coordinates;
- local codes where visibly present; and
- item-level page-boundary hints such as complete, truncated, resumed, or both.
The page-level boundary state is computed deterministically from the item-level states; it is not directly inferred by the LLM.
Extraction includes both deterministic quality checks and an LLM validation/correction flow. Validators check structural properties such as bounding boxes, reading order, table integrity, figure plausibility, artifacts, text constraints, and continuity-state consistency. Structured-output failures can be retried before a Page IR is accepted.
Typical persisted artifacts include:
Stage boundary: Page IR extraction records what is visible on an individual page. It does not construct the document-wide curriculum hierarchy or graph relationships.
Stage 2: Page IR continuity verification
Purpose: Determine whether selected items at the bottom of page N continue into
selected items at the top of page N+1, then conservatively patch Page IR continuity
metadata when the evidence is strong enough.
This is specifically a cross-page continuity verification stage. It is not a generic second extraction pass over each page.
For each adjacent page pair, deterministic logic builds a bounded candidate set around the page break. Candidate families are kept structurally compatible where appropriate, for example table-to-table or viable block-to-block comparisons. Pair-specific image crops and compact structured excerpts are then supplied to the continuity verifier.
Existing extraction continuity hints are stripped from the candidate evidence sent to the verifier so the model independently evaluates the boundary rather than simply repeating Stage 1.
The semantic flow is:
bounded candidate pair
|
v
continuity verifier
|
v
independent validator
|
v
selected pair verdict
|
v
confidence-gated compile + postprocess
Verification configuration distinguishes several confidence concepts. In particular, a positive verdict may be good enough to win candidate selection without being strong enough to modify canonical Page IR boundary state. Only verdicts meeting the configured patch threshold are applied automatically.
The compile and postprocessing steps can also reconcile item boundary states, propagate compatible local table codes, patch repeated-header information, and normalize verified tables while preserving conflicts for inspection rather than silently overwriting them.
Typical persisted artifacts include:
verification/
├── verification_run.json
├── page_irs_pair_crops/
├── page_irs_pair_reports/
├── continuity_compile_report.json
└── page_irs_verified/
Stage boundary: Verification decides cross-page continuity. It does not infer curriculum-semantic hierarchy.
Stage 3: Document IR construction
Purpose: Deterministically stitch verified page-local structures into one
source-faithful DocumentIR.
The stitcher first normalizes page items, then computes cross-page links from verified boundary evidence and bounded deterministic heuristics. Confirmed continuation chains are materialized as document-level segments.
For text and list content, stitching can merge continuation chains and repair page-break hyphenation. For tables, the pipeline can reconstruct multi-page table structure, normalize repeated headers, align row and column structure, and optionally fill down leading grouping columns when the configuration explicitly permits it.
The stage also maintains lightweight document context such as section paths derived from nearby headings and preserves provenance back to the contributing Page IR items and page indexes. Segment identities are deterministic.
A critical invariant is that every normalized source item must be consumed exactly once by the resulting document segments.
Typical persisted artifacts are:
DocumentIR is the downstream source representation for KG construction, but it is
not a canonical curriculum hierarchy. It remains intentionally document-oriented:
- it does not decide curriculum statement types such as Strand, Competency, or Performance Objective;
- it does not assign final Standards Framework Item identities;
- it does not create
hasChildorsupportsrelationships; and - it does not generate Learning Components.
Those responsibilities begin in the KG stage.
Stage 4: Academic Standards KG construction
Purpose: Convert source-grounded DocumentIR evidence into a validated Academic
Standards graph containing a StandardsFramework, finalized StandardsFrameworkItems,
and hasChild relationships.
This is the first stage that performs curriculum-specific semantic interpretation. Its
behavior is configured under kgs.as, including source-facing statement types, code
policies, identity scope, grade mapping, table inclusion rules, hierarchy policies, and
curriculum-specific producer/checker instructions.
Academic Standards construction is a multi-phase process:
flowchart TD
A[DocumentIR] --> B[Plan source units and extraction windows]
B --> C[Extract source-grounded SFI candidates]
C --> D[Build global candidate registry]
D --> E[Review and merge duplicate candidates]
E --> F[Mint deterministic final SFI IDs]
F --> G[Resolve direct hasChild parents]
G --> H[Compile and validate Academic Standards KG]
Source-window planning
The pipeline deterministically selects and bounds the DocumentIR evidence supplied to
SFI extraction. Text blocks and eligible table content are converted into LLM-ready
windows with bounded heading, section, table, and scope context. Table inclusion and
chunking are controlled by curriculum configuration rather than sending every table
blindly to the model.
SFI extraction and validation
An extraction producer identifies source-grounded SFI candidates from each window. An independent checker validates or corrects the extraction using the same bounded source evidence plus curriculum-specific validation instructions.
Candidates retain explicit source anchors rather than becoming detached semantic summaries.
Global identity and deduplication
Window-local candidates are assembled into a global candidate registry before final identities are minted. Deterministic keys and configured identity/code scopes nominate possible duplicates and conflicts. Bounded duplicate review sets are semantically adjudicated through a producer/checker flow.
The pipeline distinguishes accepted merges from conflicts and review-needed cases rather than silently forcing ambiguous candidates into one identity.
Finalization and hasChild resolution
Eligible merge groups are finalized with deterministic UUIDv5 SFI identities.
For each finalized SFI, Python constructs a bounded parent-candidate set from source provenance, codes, configured hierarchy policy, active source context, and other structured evidence. A producer chooses the supported direct parent and an independent checker validates the judgment.
The resulting graph is then checked for constraints such as:
- valid endpoints;
- allowed direct-parent statement types;
- configured parent cardinality;
- duplicate edges;
- self-loops;
- directed cycles; and
- framework-root reachability.
The stage exports both final graph artifacts and the intermediate evidence needed to audit extraction, deduplication, finalization, and hierarchy resolution.
Representative outputs include:
kgs/
├── kg_run.json
├── kg_run_manifest.json
├── sfi_extraction_window_plan.json
├── sfi_extraction_windows.jsonl
├── sfi_extraction_results.jsonl
├── sfi_candidate_registry.json
├── sfi_merge_report.json
├── sfi_final_records.json
├── has_child_candidate_parent_sets.jsonl
├── has_child_edges_final.json
├── as_validation_report.json
├── as_unresolved_items.json
├── as_entity_provenance.json
├── as_standards_framework.json
├── as_standards_framework_items.jsonl
├── as_relationships_has_child.jsonl
└── as_kg_bundle.json
Stage boundary: Learning Component generation begins only after the Academic Standards bundle has been compiled and checked. Unresolved Academic Standards material is surfaced explicitly and influences LC eligibility/context handling.
Stage 5: Learning Components construction
Purpose: Derive atomic, reusable Learning Components from eligible finalized
Standards Framework Items and connect them back to their source standards through
supports relationships.
LC behavior is configured under kgs.lc. The Academic Standards graph is authoritative
for seed text and hierarchy context; this stage does not independently re-extract skill
content from the original PDF.
The LC flow is:
flowchart TD
A[Validated Academic Standards KG] --> B[Gate + select eligible SFI seeds]
B --> C[Build hierarchy-aware generation requests]
C --> D[LC producer generates atomic skills]
D --> E[Independent LC validator accepts or corrects]
E --> F[Exact + optional semantic deduplication]
F --> G[Mint deterministic LearningComponents]
G --> H[Create deterministic supports edges]
H --> I[Validate LC reconciliation]
I --> J[Compile + validate combined AS + LC KG]
Eligibility and hierarchy context
The LC phase first requires a passed, error-free Academic Standards validation report. Recorded AS finalization gaps can coexist with a valid bundle, so the default behavior is to continue over the resolved subgraph while excluding seeds whose ancestry crosses an unresolved root-fallback edge.
If lc_source_statement_types is configured, those exact source-facing statement types
define eligibility. Otherwise the deterministic fallback selects leaf SFIs whose
normalized statement type is Standard. Every exclusion receives an explicit reason.
Eligible seeds are batched by lc_request_batch_size. Each request contains the
framework context and, per seed, the authoritative description, language, statement
type, complete direct-parent UUID set, and ancestor graph. The hierarchy is explicitly
multi-parent: all branches are walked to the framework root, and each ancestor preserves
its own parent_uuids instead of being flattened into a single inferred path.
Sibling context can optionally be included for disambiguation and overlap avoidance,
but it cannot license new skill content. Statement codes are omitted from decomposition
input. If a reviewed override admits a seed with unresolved ancestry, the request is
marked unresolved_ancestor_path and the seed text becomes the sole authority for
curriculum scope.
Atomic-skill producer/checker flow
The LC producer decomposes every requested SFI into one or more atomic teachable skills. An already atomic seed can correctly produce one skill. The generic policy prevents fragments, unstated prerequisites, activities/resources/assessment prompts, and combinatorial splitting. When a seed names several actions over several objects or cases, decomposition splits on one axis only rather than producing an N-by-M cross product.
Python validates universal response integrity and any configured skill-count or skill-text bounds. A separate LC generation validator then receives the original request and the complete producer draft. It independently re-decomposes the seed and checks semantic granularity, wording fidelity, split axis, scope, sibling leakage, language, and runtime curriculum policy.
A passing verdict accepts the producer draft. A failing verdict must return a complete corrected response, which Python validates again. The phase persists producer drafts, validator verdicts, and accepted/corrected final responses separately, allowing the semantic decision path to be audited and safely resumed.
Isolated request failures are recorded and processing continues. The run raises only
when the fraction of affected eligible SFIs exceeds lc_max_failure_rate.
Deduplication and canonicalization
Exact LC identity normalization is deliberately language-independent: lowercase, whitespace collapse, and trailing-period removal. Exact duplicates group within the configured scope even if semantic deduplication is disabled.
When semantic deduplication is enabled, deterministic blocking nominates plausible text pairs using token overlap, containment, character trigrams, generated tags, corpus-frequency stopword suppression, and small shared-parent neighborhoods. An optional profile-defined language pack adds stopwords and affix folding for nomination only; these transformations never enter canonical LC identity.
The supported scope modes are:
framework: one document-wide merge scope;top_ancestor: the complete set of resolved root-level SFI ancestors;parent: the complete set of direct parent UUIDs; andnone: one isolated scope per source SFI.
Under top_ancestor or parent, empty or unresolved ancestry falls back to the seed
UUID, preventing unreliable hierarchy from enabling a cross-seed merge. Multi-parent
seeds key on the complete relevant set rather than allowing one branch to determine
scope.
Nominated pairs are adjudicated by one bounded semantic judge. SAME links are clustered deterministically, while explicit DISTINCT verdicts prevent transitive chaining from silently joining contradictory pairs. Dropped links are recorded as conflicts. Canonical normalized text is elected deterministically by claim count, then text length, then lexical order.
Finalization, supports, and validation
Each canonical skill mints a content-addressed UUIDv5 LearningComponent using the
document key, dedup scope, and canonical normalized text. The displayed description is
a deterministic representative original surface form; identity remains tied to the
canonical normalized content.
Learning Components retain per-claim provenance and aggregate source/framework
provenance. Claiming SFIs must agree on inherited attribution metadata. Each LC is then
linked to every claiming SFI by one deterministic primary supports relationship. When
one SFI contributed several merged wordings to an LC, support_confidence is the
minimum of those claim confidences.
Run-level LC validation checks deterministic identifiers, real relationship endpoints, unique LC/SFI support pairs, LC edge coverage, and exact eligible-SFI reconciliation: every eligible SFI must be claimed by an LC or formally recorded as failed, but never both or neither.
The final merge validates the complete graph again for the Academic Standards gate,
supports endpoints and counts, identifier collisions, LC provenance presence, and
summary alignment.
Representative outputs include:
kgs/
├── lc_eligible_sfis.json
├── lc_eligibility_report.json
├── lc_generation_requests.jsonl
├── lc_generation_draft_responses.jsonl
├── lc_generation_validation_verdicts.jsonl
├── lc_generation_responses.jsonl
├── lc_generation_failures.json
├── lc_dedup_candidate_pairs.jsonl
├── lc_dedup_verdicts.jsonl
├── lc_dedup_groups.json
├── learning_components.jsonl
├── lc_supports_edges.json
├── lc_entity_provenance.json
├── lc_generation_summary.json
├── as_lc_kg_bundle.json
├── as_lc_nodes.jsonl
└── as_lc_relationships.jsonl
Knowledge graph model
The current build pipeline produces three primary entity types.
StandardsFramework
The root curriculum/framework entity for the processed document.
StandardsFrameworkItem
Source-grounded curriculum items. Depending on the configured source framework, these
can represent both organizational/grouping structures and normative learning statements.
The source-facing statement_type remains curriculum-specific, while normalized fields
provide cross-framework interoperability where appropriate.
Academic Standards hierarchy is represented as:
(:StandardsFramework)-[:hasChild]->(:StandardsFrameworkItem)
(:StandardsFrameworkItem)-[:hasChild]->(:StandardsFrameworkItem)
LearningComponent
An atomic skill or concept derived from an eligible finalized standards item and aligned back to standards through:
Relationship scope
The shared graph schema also recognizes buildsTowards and relatesTo relationship
types for Standards Framework Items. However, the current create_kgs.py orchestration
implemented in this repository constructs and exports hasChild and supports.
buildsTowards and relatesTo should therefore be treated as schema-supported or
future/downstream relationship types, not as outputs of the current production build
pipeline.
Separation of responsibilities
A useful way to reason about the architecture is by where each type of assertion is allowed to enter the system.
| Layer | Owns | Does not own |
|---|---|---|
| Page IR | Visible page structure, coordinates, page-local item content, local continuation hints | Document-wide curriculum semantics |
| Verification | Evidence-backed continuation across adjacent page boundaries | Curriculum hierarchy |
| Document IR | Deterministic document-level stitching, table reconstruction, section context, provenance | Standards identity or KG relationships |
| Academic Standards KG | Curriculum statement types, global SFI identity, direct hierarchy, normalized grades/metadata | Atomic skill decomposition |
| Learning Components | Atomic skills, LC identity/deduplication, supports alignment |
Reinterpretation of the source document hierarchy |
This separation reduces the amount of semantic inference required at any one stage, makes errors easier to localize, and preserves an auditable path from the final graph back to the source PDF.
Current architectural boundaries
The production architecture documented here has several deliberate boundaries:
- Page continuity verification is bounded to adjacent-page continuation evidence rather than whole-document semantic validation.
- Academic Standards construction must globally reconcile candidate identity before minting final SFI identifiers.
- Learning Components are downstream of the Academic Standards graph and do not bypass it to extract skills directly from PDF pages.
- Progression-style
buildsTowardsand associativerelatesTorelationships are not currently constructed by the main KG orchestration path.
These boundaries should be preserved when adding new extraction policies, curriculum profiles, or downstream graph capabilities so each stage retains a clear and testable contract.