graph TD
subgraph pageSchemas [Page Schemas — Stage 1 and 2]
page[page.dtd] --> page_translated[page_translated.dtd]
page --> page_annotated[page_annotated.dtd]
page --> page_letter[page_letter.dtd]
page --> page_section[page_section.dtd]
page --> page_normalized[page_normalized.dtd]
end
subgraph segmentSchemas [Segment Schemas — Assembled Units]
letter_dtd[letter.dtd]
section_dtd[section.dtd]
parno_dtd[parno.dtd]
end
page_letter -.->|assembly| letter_dtd
page_section -.->|assembly| section_dtd
page -.->|extraction| parno_dtd
PDAI XML Schema Reference
DTD definitions for the PDAI document processing pipeline
Overview
AI-Scholar uses DTD (Document Type Definition) schemas to define and validate XML at each stage of the processing pipeline. The schemas form a hierarchy: a base page.dtd for OCR output, extended page schemas for enriched structures, and segment-level schemas for assembled logical units.
All DTD files reside in ai-scholar/src/ai_scholar/schemas/.
Schema Hierarchy
The PDAI schema system has two layers: page schemas (one XML file per physical page) and segment schemas (one XML file per logical unit assembled from multiple pages).
Page Schemas
page.dtd — Base Page (Stage 1)
The foundation schema for all page XML. Produced by the vision-LLM OCR step: one page image yields one XML file.
Exemplar documents: E57, Swerdlow1973, Maestlin1973 (all in pdai-reference)
Root Structure
<page pidx="042">
<meta model="gemini-3.8-flash"
instruction_template="full_page_scholarly"
lang="la" processed_date="2026-02-15"
page_index="042" />
<header>CAPUT TERTIUM.</header>
<main>
<p><parno>21.</parno> Cum igitur ...</p>
<figure ref="page_042_fig_1">
<caption>Fig. 3</caption>
</figure>
<marginal side="left" type="line-number">210</marginal>
</main>
<footnotes>
<footnote>Reference citation.</footnote>
</footnotes>
<footer>42</footer>
</page>Elements
| Element | Content Model | Description |
|---|---|---|
page |
(meta, header?, main, footnotes?, footer?) |
Root: a single physical page |
meta |
EMPTY |
Processing metadata (self-closing) |
header |
#PCDATA |
Running header text |
main |
(p \| figure \| marginal)* |
Primary content container |
p |
(#PCDATA \| fn \| parno \| hi \| br \| figure)* |
Paragraph with mixed content |
fn |
#PCDATA |
Inline footnote marker (content = number) |
parno |
#PCDATA |
Paragraph number in scholarly editions |
hi |
#PCDATA |
Inline formatting; rend attribute |
br |
EMPTY |
Line break (verse/lists only, never in prose) |
figure |
(caption?, description?) |
Figure reference; ref links to image file |
caption |
(#PCDATA \| hi)* |
Figure caption |
description |
(#PCDATA \| hi)* |
Figure description |
marginal |
#PCDATA |
Marginal annotation |
footnotes |
(footnote)* |
Container for page footnotes |
footnote |
(#PCDATA \| fn \| p \| hi)* |
Single footnote entry |
footer |
#PCDATA |
Printed page number or running footer |
Key Attributes
| Element | Attribute | Values | Required |
|---|---|---|---|
page |
pidx |
Zero-padded page index | Yes |
meta |
model |
AI model name | Yes |
meta |
lang |
Language code | Yes |
meta |
output_schema |
Schema name | No |
hi |
rend |
italic \| bold \| spaced \| superscript \| subscript |
No |
figure |
ref |
Image filename | Yes |
marginal |
side |
left \| right |
No |
marginal |
type |
line-number \| folio \| date \| reference \| topic |
No |
Notes
LaTeX mathematics appears as plain text:
$...$(inline) and$$...$$(display). There is no<equation>element.<fn>contains only the footnote number as text content. There is nodata-fnattribute (removed in v3.0).<marginal>replaces the older<marginalia>element (renamed in v3.0).
page_letter.dtd — Letter Page (Stage 2)
Extends page.dtd for correspondence documents by adding <letter-head> and <letter-closing> to the page content model.
Exemplar documents: kgw-13, Goldbach, kirch-06 (all in pdai-reference)
Instruction template: ai-instructions/structure/letter_tagging.j2
Additional Elements
| Element | Content Model | Description |
|---|---|---|
letter-head |
(reference?, sender?, addressee?, place?, date?, archive?, incipit?) |
Letter metadata from the edition |
letter-closing |
(greeting?, place-date?, salutation?, signature?, postscriptum?, address?, marginal?, p)* |
Letter closing section |
reference |
#PCDATA |
Edition reference number |
sender |
#PCDATA |
Sender name |
addressee |
#PCDATA |
Recipient name |
incipit |
#PCDATA |
Opening words |
greeting |
#PCDATA |
Closing greeting |
signature |
#PCDATA |
Signature text |
postscriptum |
(#PCDATA \| p)* |
Postscript content |
address |
(#PCDATA \| p)* |
Address block |
Example (KGW-13, page 38)
<page pidx="038">
<meta model="gemini-3.8-flash"
instruction_template="letter_ocr"
lang="de" processed_date="2026-02-10"
page_index="038" output_schema="page_letter"
document_type="correspondence" />
<letter-head>
<reference>19. KONRAD VISCHSES AN KEPLER</reference>
<sender>Konrad Vischses</sender>
<addressee>Johannes Kepler</addressee>
<place>Tübingen</place>
<date>14./24. Mai 1595</date>
<archive>Pulkowo, Sternwarte</archive>
</letter-head>
<main>
<p>Letter body content...</p>
</main>
<letter-closing>
<greeting>Vale</greeting>
<signature>Tuus M C Vischses</signature>
</letter-closing>
</page>page_section.dtd — Section Page (Stage 2)
Extends page.dtd for monographs and articles by adding hierarchical <section> elements to <main>.
Exemplar documents: E57 (chapters), Swerdlow1973 (article sections), Maestlin1973 (secondary article)
Instruction template: ai-instructions/structure/section_tagging.j2
Additional Elements
| Element | Content Model | Description |
|---|---|---|
section |
(heading?, (p \| section \| figure \| marginal)*) |
Hierarchical section; may nest |
heading |
(#PCDATA \| hi)* |
Section heading text |
Section Attributes
| Attribute | Description |
|---|---|
level |
Numeric depth (1 = chapter, 2 = section, etc.) |
l |
Legacy level string |
id |
Section identifier (e.g. chapter_3) |
page_translated.dtd — Translated Page (Stage 3)
Extends page.dtd with translation-specific metadata attributes. The structural elements are identical to page.dtd; only <meta> gains additional attributes.
Exemplar documents: E57 (page_NNN_translation_english.xml), kgw-13 (page_NNN_translation_english.xml)
Instruction templates: ai-instructions/translation/scholarly.j2, ai-instructions/translation/correspondence.j2
Additional Meta Attributes
| Attribute | Description |
|---|---|
source_lang |
Original language of the source text |
target_lang |
Translation target language code |
translator_agent |
Agent that performed translation |
validation_status |
valid or invalid |
Example
<page pidx="001">
<meta model="gemini-3.8-flash"
instruction_template="scholarly_translation"
lang="en" source_lang="la" target_lang="en"
translator_agent="translation-english"
output_schema="page_translated" />
<main>
<p><parno>1.</parno> Every change that occurs in any body...</p>
</main>
</page>page_annotated.dtd — Annotated Page (Stage 6)
Extends page.dtd with semantic annotation elements for named entity recognition.
Exemplar documents: No pdai-reference exemplar yet (planned).
Instruction templates: ai-instructions/entities/ner.j2, ai-instructions/entities/person.j2
Additional Elements
| Element | Content Model | Description |
|---|---|---|
entity |
#PCDATA |
Generic named entity |
person |
#PCDATA |
Person name |
place |
#PCDATA |
Geographic location |
date-ref |
#PCDATA |
Date reference |
citation |
#PCDATA |
Scholarly citation or reference |
page_normalized.dtd — Normalised Page
Simplified schema for cleaned plain text output. This does not extend page.dtd; it has an independent structure optimised for downstream text processing.
Exemplar documents: No pdai-reference exemplar yet (planned).
Segment Schemas
Segment schemas define the structure of assembled logical units — letters, chapters, paragraphs — that are derived from multiple page XML files. Page boundaries are preserved within segment bodies as <page-break pidx="NNN"/> elements.
letter.dtd — Assembled Letter
Schema for a complete letter assembled from one or more page_letter.dtd pages.
Exemplar: kgw-13 segments/letters/letter_001/letter_001.xml
Instruction template: ai-instructions/structure/letter_assembly.j2
Root Structure
<letter id="letter_001" number="1"
pages="15">
<letter-meta sender="BÜRGERMEISTER UND RAT VON WEIL"
addressee="UNIVERSITÄT TÜBINGEN"
date_original="22. Mai 1590 (a. St.)"
place="Weil" language="de"
source_pages="15"
assembled_date="2026-02-10" />
<letter-head>
<reference>1. BÜRGERMEISTER UND RAT VON WEIL AN DIE UNIVERSITÄT TÜBINGEN</reference>
<sender>Bürgermeister und Rat von Weil</sender>
<addressee>Universität Tübingen</addressee>
</letter-head>
<body>
<page-break pidx="015"/>
<p>Letter body content spanning multiple pages...</p>
</body>
<letter-closing>
<greeting>Vale</greeting>
<signature>Bürgermeister und Rat</signature>
</letter-closing>
<editorial>
<footnotes>
<footnote source-page="015">Editorial note.</footnote>
</footnotes>
</editorial>
</letter>Key Design
<page-break pidx="NNN"/>preserves provenance: every passage in the segment body can be traced to its source page<editorial>collects footnotes from all pages, withsource-pageattribute for citationThe
pagesattribute on<letter>lists the page range for quick reference
section.dtd — Assembled Section/Chapter
Schema for a chapter or section assembled from page_section.dtd pages.
Exemplar: Planned from E57 chapters.
Instruction template: ai-instructions/structure/section_assembly.j2
Key Elements
| Element | Content | Description |
|---|---|---|
section |
(section-meta, heading, body) |
Root: assembled chapter |
section-meta |
EMPTY |
Metadata: title, page range |
body |
(page-break \| p \| section \| figure \| marginal \| footnotes)* |
Content with page provenance |
parno.dtd — Assembled Paragraph
Schema for a single numbered paragraph extracted from pages.
Exemplar: E57 segments/paragraphs/parno_001/parno_001.xml
Key Elements
| Element | Content | Description |
|---|---|---|
paragraph |
(paragraph-meta, body) |
Root: one numbered paragraph |
paragraph-meta |
EMPTY |
Page range and section cross-reference |
section-ref |
EMPTY |
Which chapter this paragraph belongs to |
famulus-reference/1 tables
Next to the DTDs, which define the XML of pages and segments, every collection has a relational form in schema famulus-reference/1 (pdai/famulus-reference/src/famulus_reference/schema/core.sql). It is stored in <coll>/<coll>_famulus.duckdb and in the collection schema of the DuckLake. The DTDs themselves are stored in fr_dtd.
| Table | Content | Relation to the XML |
|---|---|---|
fr_collection |
title, description, licence, rights holder, DTD id, contract, landing, languages, period | collection descriptor |
fr_source |
kind, holding institution, shelfmark, origin URL, IIIF manifest, licence, sha256, path | source files |
fr_document |
title, creator, date, language, pages, entry type, biblio sha | document folder, biblio.xml |
fr_page |
image path, sha256, size, IIIF canvas | pages/page_NNN/ |
fr_segment |
seg_type (figure, print, instrument, letter, record, inscription …), parent, page range, bbox, DTD element, attrs | DTD object elements, segments/ |
fr_text |
text layers ocr, diplomatic, normalised, translation with sha256 |
page_NNN.xml, translations, index.duckdb pages |
fr_entity, fr_mention |
persons and places (register _register, GND) with evidence grade |
annotated entities |
fr_citation |
BibTeX, CSL-JSON, text citation, locator, gaps | biblio.xml only |
fr_provenance |
step, tool, model, instruction, input/output sha256, file, run | pipeline logs |
fr_validation |
check, grade DOCUMENTED/INFERRED/HYPOTHESIS/GAP, certificate sha256 |
audit, famulus-proof certificates |
fr_identifier |
scheme, value, resolver (URN, ARK, DOI, GND, shelfmark, IIIF) | — |
fr_dtd |
DTD text, version, sha256, status draft/deployed/approved |
the DTD files of this page |
fr_harvest |
datestamp, set, deleted flag | OAI-PMH |
A collection DTD drafted by the intake skill (pdai/collections/<coll>/config/page.dtd, wrapping page.dtd v4.0) keeps fr_dtd.status = draft until the owner approves it at the review gate.
Schema Registry
The registry at src/ai_scholar/schemas/registry.yml defines all schemas with their inheritance relationships, enabling automated pipeline validation.
schemas:
page:
file: page.dtd
description: "Base page structure from OCR processing"
extends: []
page_letter:
file: page_letter.dtd
description: "Letter/correspondence page structure"
extends: [page]
page_section:
file: page_section.dtd
description: "Section/chapter enriched page structure"
extends: [page]
page_translated:
file: page_translated.dtd
description: "Translated page with language metadata"
extends: [page]
page_annotated:
file: page_annotated.dtd
description: "Page with semantic entity annotations"
extends: [page]
page_normalized:
file: page_normalized.dtd
description: "Normalised text without footnotes"
extends: []
letter:
file: letter.dtd
description: "Assembled letter from page_letter pages"
extends: []
section:
file: section.dtd
description: "Assembled chapter from page_section pages"
extends: []
parno:
file: parno.dtd
description: "Assembled numbered paragraph"
extends: []Schema-to-Exemplar Mapping
Every schema should be exercised by at least one document in pdai-reference:
| Schema | Exemplar Document | Subcollection | Notes |
|---|---|---|---|
page.dtd |
E57, Swerdlow1973, Maestlin1973 | enestroem, kepler-prim, kepler-sec, examensarbeiten | All documents have base OCR pages |
page_letter.dtd |
kgw-13, Goldbach |
kepler-gw, eulerOO | Correspondence documents |
page_section.dtd |
E57, Swerdlow1973, kgw-03 |
enestroem, kepler-prim, kepler-gw | Chapter/section structure |
page_translated.dtd |
E57, kgw-13 |
enestroem, kepler-gw | English translations |
page_annotated.dtd |
— | — | Planned |
page_normalized.dtd |
— | — | Planned |
letter.dtd |
kgw-13 |
kepler-gw | Materialised letters |
section.dtd |
— | — | Planned from E57 chapters |
parno.dtd |
E57 | enestroem | 130 numbered paragraphs |
Pipeline Validation
When chaining agents, the orchestrator validates schema compatibility between pipeline steps:
sequenceDiagram
participant O as Orchestrator
participant R as SchemaRegistry
participant A1 as OCR_Agent
participant A2 as Translation_Agent
O->>R: Get A1 output_schema
R-->>O: page
O->>R: Get A2 input_schema
R-->>O: page
O->>R: is_compatible?
R-->>O: true
O->>A1: Execute
A1-->>O: page XML
O->>R: Validate against page.dtd
R-->>O: valid
O->>A2: Execute
A2-->>O: page_translated XML
Compatibility Rules
Same schema:
page->page— compatibleExtended schema:
page_translated->page— compatible (superset)Incompatible:
page_normalized->page— incompatible (different structure)
CLI Commands
# List all registered schemas
ai-scholar schemas list
# Show schema details
ai-scholar schemas info --schema page_letter
# Validate an XML file against a schema
ai-scholar schemas validate --schema page_letter \
--file ../pdai-reference/kepler-gw/kgw-13/pages/page_038/page_038.xml
# Validate an entire pipeline
ai-scholar validate-pipeline letter-pipeline