[NOTICKET] docs(knowledge-parsing): output contract for the parsing half
Browse filesCompanion to KNOWLEDGE_OUTPUT_CONTRACT.md, which covers extraction. Values are
literal from run 20260824-100720 on the 9-page BUMA standard, not illustrative.
Worth noting for the transport discussion: the envelope the extraction contract
proposes as "not built, for discussion" already exists here. chunks.json is a
self-describing object carrying parser name, version, backend, config
fingerprint and source hash β reusing it beats inventing a second shape.
Three gaps recorded rather than smoothed over:
- chunk_id disagrees across the seam. This side emits "doc::0003"; the
extraction contract shows "doc#0007". Provenance back-references break if a
consumer splits on the wrong separator. Needs one joint call.
- kind="equation" is declared but never emitted; all 11 equations arrive as
latex on text chunks. Consumers must not branch on it today.
- heading_path carries page-number pollution β its root is frequently "1. 7",
page 7's number caught as a numbered heading, in 7 of 14 distinct paths.
heading_path feeds the evidence ranker, so this is not cosmetic. Open
against B5.
Also records that the chunk count is now 31, against the 13 quoted in the
extraction contract's reference run: that run predates the section-aware
chunker. Anything calibrated on 13 needs rechecking.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Knowledge Pipeline β Parsing Output Contract
|
| 2 |
+
|
| 3 |
+
**Source of truth:** `src/knowledge_parsing/contracts.py`
|
| 4 |
+
**Producer:** `python -m src.knowledge_parsing.run --input data/knowledge_docs/`
|
| 5 |
+
**Reference run:** `20260824-100720` Β· BUMA `STD_2026_006_MNO` Β· 9 pages Β· MinerU `3.4.4`, backend `pipeline`
|
| 6 |
+
**Status:** offline artifacts only β no endpoint, no table. Values below are **literal from that run**, not illustrative.
|
| 7 |
+
|
| 8 |
+
Companion to `KNOWLEDGE_OUTPUT_CONTRACT.md` (extraction). This is the half **upstream** of it:
|
| 9 |
+
parsing emits `ParsedDocument`, extraction consumes it and never touches the parser.
|
| 10 |
+
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
## Transport reality β read this first
|
| 14 |
+
|
| 15 |
+
There is **no endpoint and no table yet.** The CLI writes one directory per run:
|
| 16 |
+
|
| 17 |
+
```
|
| 18 |
+
data/knowledge_runs/<run_id>/
|
| 19 |
+
βββ manifest.json run record: config, timings, quality warnings
|
| 20 |
+
βββ failures.jsonl documents that failed, with tracebacks
|
| 21 |
+
βββ <doc_id>/
|
| 22 |
+
βββ chunks.json β the artifact: one ParsedDocument
|
| 23 |
+
βββ sumber-mineru.txt pointer to the untouched MinerU output
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
**Unlike the extraction payloads, this artifact is not a bare array.** `chunks.json` is a single
|
| 27 |
+
self-describing object that carries its own provenance β parser name, version, backend, config
|
| 28 |
+
fingerprint, source hash. The envelope proposed at the end of the extraction contract already exists
|
| 29 |
+
here, and is worth reusing rather than inventing a second shape.
|
| 30 |
+
|
| 31 |
+
Three conventions hold:
|
| 32 |
+
|
| 33 |
+
- **`page_idx` is 0-based**, exactly as MinerU reports it, with no conversion anywhere. Converting to
|
| 34 |
+
1-based is the UI's job, done once at display, so the artifact always matches the raw MinerU output
|
| 35 |
+
kept beside it in the cache.
|
| 36 |
+
- **`text` is VERBATIM.** Extraction's span check locates quoted spans literally inside it. Reflowing
|
| 37 |
+
or normalising it makes the lookup fail and the field goes silently null β which presents as a bad
|
| 38 |
+
model, not as a parser bug.
|
| 39 |
+
- **Markup never goes in `text`.** Formulas and tables are rendered to readable prose for `text`, and
|
| 40 |
+
preserved in their original form in `latex` / `table_html`.
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## 1 Β· `chunks.json` β `ParsedDocument` *(single object)*
|
| 45 |
+
|
| 46 |
+
```json
|
| 47 |
+
{
|
| 48 |
+
"doc_id": "STD_2026_006_MNO",
|
| 49 |
+
"chunks": [ "β¦ Chunk[] β¦" ],
|
| 50 |
+
"source_path": "data\\knowledge_docs\\STD_2026_006_MNO.pdf",
|
| 51 |
+
"content_hash": "f10807178b20531e86237a2640ff4125d259aa68e5e9d9f910c0c2365cd92833",
|
| 52 |
+
"n_pages": 9,
|
| 53 |
+
"parser_name": "mineru",
|
| 54 |
+
"parser_version": "3.4.4",
|
| 55 |
+
"parser_backend": "pipeline",
|
| 56 |
+
"parser_config": "b367868551b9",
|
| 57 |
+
"version": 1,
|
| 58 |
+
"schema_version": "0.2.0",
|
| 59 |
+
"created_at": "2026-08-24T10:07:21",
|
| 60 |
+
"raw_output_dir": "β¦/data/knowledge_cache/parse/f10807178b20531e-b367868551b9-3.4.4"
|
| 61 |
+
}
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
| Field | Type | Req | Notes |
|
| 65 |
+
|---|---|---|---|
|
| 66 |
+
| `doc_id` | string | β
| Source filename stem |
|
| 67 |
+
| `chunks` | Chunk[] | β
| 31 on the reference run |
|
| 68 |
+
| `source_path` | string | β
| |
|
| 69 |
+
| `content_hash` | string | β
| sha256 of the **source file** β a changed document forces a new artifact |
|
| 70 |
+
| `n_pages` | int | β
| |
|
| 71 |
+
| `parser_name` | string | β
| Default `"mineru"`. The field exists so a Tesseract / Azure DI path is distinguishable |
|
| 72 |
+
| `parser_version` | string \| null | β | e.g. `"3.4.4"` |
|
| 73 |
+
| `parser_backend` | string \| null | β | `pipeline` Β· `vlm` Β· `hybrid`. **Read back from MinerU's `_middle.json`** β what actually ran, not what was requested |
|
| 74 |
+
| `parser_config` | string \| null | β | 12-char fingerprint of the settings that affect output |
|
| 75 |
+
| `version` | int | β
| Version of **this artifact for this document**; bumped on re-parse |
|
| 76 |
+
| `schema_version` | string | β
| Versions **the contract**. Currently `0.2.0` |
|
| 77 |
+
| `created_at` | string \| null | β | ISO 8601, seconds precision |
|
| 78 |
+
| `raw_output_dir` | string \| null | β | Untouched MinerU output, kept content-addressed and never copied |
|
| 79 |
+
|
| 80 |
+
> **Why provenance is mandatory rather than nice-to-have:** without
|
| 81 |
+
> `parser_version` / `parser_backend`, a MinerU upgrade and a prompt change are
|
| 82 |
+
> indistinguishable when extraction results shift.
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
## 2 Β· `Chunk`
|
| 87 |
+
|
| 88 |
+
```json
|
| 89 |
+
{
|
| 90 |
+
"chunk_id": "STD_2026_006_MNO::0003",
|
| 91 |
+
"doc_id": "STD_2026_006_MNO",
|
| 92 |
+
"kind": "text",
|
| 93 |
+
"text": "Adalah serangkaian kegiatan yang dilakukan untuk menciptakan nilai tambah terhadap sumber daya β¦",
|
| 94 |
+
"page_idx": 1,
|
| 95 |
+
"page_idxs": [1],
|
| 96 |
+
"section_no": "2.1.1",
|
| 97 |
+
"heading": "Production",
|
| 98 |
+
"chapters": [],
|
| 99 |
+
"heading_path": ["2.1.1. Production"],
|
| 100 |
+
"has_formula": true,
|
| 101 |
+
"is_tabular": false,
|
| 102 |
+
"source_items": [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
| 103 |
+
"bbox": [122, 293, 275, 307],
|
| 104 |
+
"images": ["images/76503982720c45fc9d387accb74d5f5c5083f4f3109ed7fbf6a4da345bc20e0f.jpg"],
|
| 105 |
+
"latex": ["$$\nP r o d u c t i o n = M O H H x Q t y x P A x U A x P t y\n$$"],
|
| 106 |
+
"table_html": null
|
| 107 |
+
}
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
| Field | Type | Req | Notes |
|
| 111 |
+
|---|---|---|---|
|
| 112 |
+
| `chunk_id` | string | β
| **Format `<doc_id>::<seq>`**, `seq` zero-padded to 4. Note the `::` separator β see *Known gaps* |
|
| 113 |
+
| `doc_id` | string | β
| |
|
| 114 |
+
| `kind` | enum | β
| `text` Β· `table` Β· `chart` Β· `equation` |
|
| 115 |
+
| `text` | string | β
| **VERBATIM.** Excludes the section heading line β that lives in `heading`. Consumers needing both compose them |
|
| 116 |
+
| `page_idx` | int | β
| **0-based.** First page of the chunk |
|
| 117 |
+
| `page_idxs` | int[] | β
| Every page the chunk spans. Default `[]` |
|
| 118 |
+
| `section_no` | string \| null | β | e.g. `"2.1.3"`; null when the document is unnumbered |
|
| 119 |
+
| `heading` | string \| null | β | This chunk's own section title, **verbatim as written** β `"Physical of Availability (PA)"`, not the tidied `"Physical Availability"` |
|
| 120 |
+
| `chapters` | string[] | β
| Running header per page spanned. Default `[]` |
|
| 121 |
+
| `heading_path` | string[] | β
| Breadcrumb of enclosing headings, outermost first |
|
| 122 |
+
| `has_formula` | bool | β
| Cheap downstream filter |
|
| 123 |
+
| `is_tabular` | bool | β
| |
|
| 124 |
+
| `source_items` | int[] | β
| Indices into MinerU's `content_list.json` β the trace back |
|
| 125 |
+
| `bbox` | int[] \| null | β | `[x0, y0, x1, y1]` of the first source item. Carried for a curation UI; **nothing in the pipeline reasons about it** β ordering uses item sequence, never coordinates |
|
| 126 |
+
| `images` | string[] | β
| Paths relative to `raw_output_dir`. Formula images, table/chart crops |
|
| 127 |
+
| `latex` | string[] | β
| Source markup for formulas, kept beside the rendered prose |
|
| 128 |
+
| `table_html` | string \| null | β | Source markup for tables |
|
| 129 |
+
|
| 130 |
+
### Why `text` is rendered but markup is kept
|
| 131 |
+
|
| 132 |
+
The term filter downstream is an NER model reading prose. MinerU writes formulas character-spaced
|
| 133 |
+
(`P u r c h a s i n g ~ c o s t s`) and tables as HTML, and neither yields a single mention.
|
| 134 |
+
Measured on the same document and gold set, changing only the parse:
|
| 135 |
+
|
| 136 |
+
| `text` contains | Recall |
|
| 137 |
+
|---|---|
|
| 138 |
+
| raw markup | 0.7561 |
|
| 139 |
+
| plain text | 0.8537 |
|
| 140 |
+
| markup rendered back to prose | 0.8293 |
|
| 141 |
+
|
| 142 |
+
The markup is not discarded, because the formula branch needs exactly that form.
|
| 143 |
+
|
| 144 |
+
---
|
| 145 |
+
|
| 146 |
+
## 3 Β· Reference run β what 9 pages actually produced
|
| 147 |
+
|
| 148 |
+
| | |
|
| 149 |
+
|---|---|
|
| 150 |
+
| MinerU items | 118 β 90 text, 14 table, 11 equation, 3 chart |
|
| 151 |
+
| Chunks emitted | **31** β 15 `text`, 13 `table`, 3 `chart`, **0 `equation`** |
|
| 152 |
+
| Images | 27 unique (**3.0 per page**) |
|
| 153 |
+
| Quality warnings | 3 |
|
| 154 |
+
| Parse time | 385.5 s (**42.8 s/page**, CPU) |
|
| 155 |
+
|
| 156 |
+
> The chunk count differs from the `13` quoted in the extraction contract's reference run
|
| 157 |
+
> (`20260813-145132`). That run predates the section-aware chunker built 2026-08-19; **31 is
|
| 158 |
+
> current.** Anything calibrated against 13 chunks should be re-checked.
|
| 159 |
+
|
| 160 |
+
### Quality warnings β `manifest.json`
|
| 161 |
+
|
| 162 |
+
Warnings never fail a document; they are recorded so quality cannot drop silently across hundreds of
|
| 163 |
+
documents. The reference run's three:
|
| 164 |
+
|
| 165 |
+
| `jenis` | Page | Detail |
|
| 166 |
+
|---|---|---|
|
| 167 |
+
| `latex_tanpa_angka` | 1 | `P r o d u c t i o n = M O H H x Q t y x P A x U A x P t y` β symbolic, so no digits |
|
| 168 |
+
| `tabel_kosong` | 6 | 1 of 14 tables detected but empty |
|
| 169 |
+
| `chart_tanpa_konteks` | 7 | Chart with neither content nor caption |
|
| 170 |
+
|
| 171 |
+
The check that matters most β **every number in a formula cross-checked against the source PDF's own
|
| 172 |
+
text layer** β returned clean. It exists because a CPU-pipeline parse once wrote `5600` as `55600`
|
| 173 |
+
with no error and a perfectly valid-looking formula.
|
| 174 |
+
|
| 175 |
+
---
|
| 176 |
+
|
| 177 |
+
## Known gaps β do not design around these without asking
|
| 178 |
+
|
| 179 |
+
**`chunk_id` separator disagrees with the extraction contract.** This artifact emits
|
| 180 |
+
`STD_2026_006_MNO::0003`. `KNOWLEDGE_OUTPUT_CONTRACT.md` shows `STD_2026_006_MNO#0007`. Provenance
|
| 181 |
+
back-references break if a consumer parses on the wrong separator. **Needs one call, then both docs
|
| 182 |
+
updated.**
|
| 183 |
+
|
| 184 |
+
**`kind="equation"` is declared but never emitted.** All 11 equations arrive as `latex` entries on
|
| 185 |
+
`text` chunks with `has_formula=true`. Either the enum member goes, or the chunker should emit them β
|
| 186 |
+
but consumers must not branch on `kind == "equation"` today.
|
| 187 |
+
|
| 188 |
+
**`heading_path` carries page-number pollution.** On the reference run its root is frequently
|
| 189 |
+
`"1. 7"` β page 7's number caught as a numbered heading β appearing in 7 of 14 distinct paths. And
|
| 190 |
+
`"2. PENJELASAN PARAMETER"` never appears as a root although it should. `heading_path` feeds the
|
| 191 |
+
evidence ranker, so this is not cosmetic. Open against B5.
|
| 192 |
+
|
| 193 |
+
**`chapters` is empty on every chunk** of the reference run. The field is populated only when MinerU
|
| 194 |
+
reports running headers; this document has none detected.
|
| 195 |
+
|
| 196 |
+
---
|
| 197 |
+
|
| 198 |
+
## Caveat on the sample values
|
| 199 |
+
|
| 200 |
+
Field shapes, types and defaults are read from `src/knowledge_parsing/contracts.py`. Unlike the
|
| 201 |
+
extraction contract, **the values here are literal** β taken from
|
| 202 |
+
`data/knowledge_runs/20260824-100720/STD_2026_006_MNO/chunks.json`, with long strings truncated and
|
| 203 |
+
marked `β¦`.
|