[NOTICKET] docs(knowledge-parsing): module README + refresh the parsing/seam statuses
Browse filesREADME follows the per-module convention already used by src/catalog, src/query,
src/retrieval and src/security, and records the three things that are easy to get
wrong and expensive to discover: text stays verbatim, page_idx is 0-based, and
markup never goes in text (with the recall numbers behind that last one).
KNOWLEDGE_PIPELINE_TODO.md β parsing (B) and seam (S) rows only; P/X/D/E/F and
S7/S8 are untouched:
- B6, B8, S2, S3, S4 -> done. Contract landed, envelope carries parser identity
read back from MinerU's _middle.json, section-structure claim corrected against
MinerU's source, source_wording added.
- B4, B5, S1, S5, S6 -> in progress, deliberately NOT marked done:
B5's acceptance test needs the Open Pit textbook (S7) before the NSR
cross-page case can be claimed; S5 is satisfied on the parsing side but is
Rifqi's to close; B4's language question is answered but text quality is not
yet judged. Marking unproven work done is what makes a checklist useless.
- S6: (c) chunk size, (d) chapters-as-list and (e) pydantic are closed. (b) page
indexing stays open as a joint call. (a) is open with its premise corrected β
text_format is emitted by both backends and is not a formatting signal, and
MinerU carries no emphasis information anywhere, so that ranker signal has no
source short of a separate PDF pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- KNOWLEDGE_PIPELINE_TODO.md +16 -11
- src/knowledge_parsing/README.md +76 -0
|
@@ -75,7 +75,12 @@ either port scaffolding you didn't want or discard measurements you can't recrea
|
|
| 75 |
|
| 76 |
## 3. The seam β shared with Sofhia
|
| 77 |
|
| 78 |
-
**Status
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
derived from real MinerU output rather than designed on paper, and it is adopted as the base. Three
|
| 80 |
of its choices improve on the prototype: a `kind` discriminator (text/table/chart/equation),
|
| 81 |
`source_items` index back-references into MinerU's `content_list.json`, and an explicit
|
|
@@ -84,12 +89,12 @@ verbatim-text warning that independently identifies the same failure mode as the
|
|
| 84 |
|
| 85 |
| # | Task | Owner | Status | Description |
|
| 86 |
|---|---|---|---|---|
|
| 87 |
-
| **S1** | Agree the parsed-artifact schema | Sofhia + Rifqi | π |
|
| 88 |
-
| **S2** | Add a document-level envelope | Sofhia + Rifqi |
|
| 89 |
-
| **S3** | Correct the section-structure claim | Rifqi β Sofhia |
|
| 90 |
-
| **S4** | Add `source_wording` while the contract is open | Rifqi β Sofhia |
|
| 91 |
-
| **S5** | Keep the seam off the file path | Sofhia + Rifqi |
|
| 92 |
-
| **S6** | Minor contract items (deferred to review) | Rifqi β Sofhia |
|
| 93 |
| **S7** | Commit fixture artifacts | Rifqi | β¬ | Both source PDFs exist (BUMA standard + Open Pit textbook). Promote a parsed artifact of each into the target repo as committed fixtures, so the extraction half is testable **with MinerU not installed at all** β the seam doing its job |
|
| 94 |
| **S8** | Write the seam rule into the repo docs | Rifqi | β¬ | Extraction never takes a file path; neither half imports the other's internals; parsing stays swappable behind the artifact. The prototype drifted from this, so the rule needs to outlive our memory of it |
|
| 95 |
|
|
@@ -105,11 +110,11 @@ detection with type labels. Presented to the execs 13 Aug as the prerequisite fo
|
|
| 105 |
| **B1** | MinerU cost justification (written) | Sofhia | β¬ | Owed for the budget proposal, from the 13 Aug exec review |
|
| 106 |
| **B2** | MinerU install + run on the sample docs | Sofhia | β
| Complete 9-page parse of the BUMA standard delivered 2026-08-19: **118 items** (90 text, 14 table, 11 equation, 3 chart), pages 0β8. Includes the rule-of-thumb grounding content (Other Activity, Controllable/Uncontrollable, Standby), so the fixture exercises all four extraction branches. Supersedes an earlier 5-page run |
|
| 107 |
| **B3** | Evaluate MinerU quality vs. the current path | Sofhia | β¬ | Compare on tables, the p.2 legend block, formulas and figures β against the PyMuPDF baseline the prototype used, which has real numbers rather than a guess |
|
| 108 |
-
| **B4** | Indonesian check on **parsing** | Sofhia |
|
| 109 |
-
| **B5** | Section-aware chunking | Sofhia |
|
| 110 |
-
| **B6** | Emit the agreed artifact | Sofhia |
|
| 111 |
| **B7** | Fallback path behind the same artifact | Sofhia | β¬ | Tesseract / Azure Document Intelligence emits the *same* artifact, so extraction never knows which parser ran |
|
| 112 |
-
| **B8** | Keep the parsing subsystem additive + gated | Sofhia |
|
| 113 |
|
| 114 |
**Findings from the real MinerU output that affect B5/B6:**
|
| 115 |
|
|
|
|
| 75 |
|
| 76 |
## 3. The seam β shared with Sofhia
|
| 77 |
|
| 78 |
+
**Status update 2026-08-19 (Sofhia).** The contract has landed in
|
| 79 |
+
`src/knowledge_parsing/contracts.py` and every open item raised against the draft is closed except
|
| 80 |
+
the two that need a joint decision (S6a formatting, S6b page indexing). Rows below updated
|
| 81 |
+
accordingly; the extraction-side rows (S7, S8) are left as Rifqi has them.
|
| 82 |
+
|
| 83 |
+
**Original note β under active discussion.** Sofhia circulated a draft `contracts.py` on 2026-08-19,
|
| 84 |
derived from real MinerU output rather than designed on paper, and it is adopted as the base. Three
|
| 85 |
of its choices improve on the prototype: a `kind` discriminator (text/table/chart/equation),
|
| 86 |
`source_items` index back-references into MinerU's `content_list.json`, and an explicit
|
|
|
|
| 89 |
|
| 90 |
| # | Task | Owner | Status | Description |
|
| 91 |
|---|---|---|---|---|
|
| 92 |
+
| **S1** | Agree the parsed-artifact schema | Sofhia + Rifqi | π | **Adopted as the base and landed** in `src/knowledge_parsing/contracts.py` (pydantic, per S6e). Everything raised against the draft is closed except the two joint decisions in S6 (a: formatting signal, b: page indexing). |
|
| 93 |
+
| **S2** | Add a document-level envelope | Sofhia + Rifqi | β
| **Done 2026-08-19.** `ParsedDocument{doc_id, source_path, content_hash, version, n_pages, parser_name, parser_version, chunks}` as proposed, plus three fields worth having: **`parser_backend`** read back from MinerU's `_middle.json` so the artifact records what *actually* ran rather than what was configured; `parser_config` (fingerprint of the settings that affect output); and `raw_output_dir`, pointing at the untouched MinerU output kept in the parse cache. |
|
| 94 |
+
| **S3** | Correct the section-structure claim | Rifqi β Sofhia | β
| **Corrected 2026-08-19.** Checked against MinerU's source: `pipeline` and `vlm` run *identical* `text_level` logic and both emit `bbox`, so heading availability is document-dependent, not backend-dependent β the original claim came from sampling the handbook, which has no numbered headings. The normalizer now **reads** the hierarchy into a `heading_path` breadcrumb instead of deriving it, with numbering as fallback. Fields stay Optional. |
|
| 95 |
+
| **S4** | Add `source_wording` while the contract is open | Rifqi β Sofhia | β
| **Done 2026-08-19.** `source_wording` added to `TermRecord`, and `heading` is carried verbatim β so "Physical **of** Availability" survives instead of being silently normalised (R1). |
|
| 96 |
+
| **S5** | Keep the seam off the file path | Sofhia + Rifqi | π | **Satisfied on the parsing side.** `ParsedDocument` never carries a file handle; `source_path` is provenance only, and extraction receives the artifact. Left open for Rifqi to confirm from his side β the prototype's `Parser.parse(path)` boundary is the shape this must not reproduce. |
|
| 97 |
+
| **S6** | Minor contract items (deferred to review) | Rifqi β Sofhia | π | **Four of six closed 2026-08-19; two need a joint call.** **(b) page indexing β OPEN:** the artifact uses `page_idx`/`page_idxs`, **0-based**, no conversion anywhere, so it always matches the raw MinerU output kept beside it; converting to 1-based is the UI's job, done once at display. Say if you want a 1-based `page_no` added instead, since this number reaches Mas Beta. **(a) formatting signal β OPEN, but the premise was wrong:** `text_format` is emitted by *both* backends and is not a formatting signal at all β it is the constant `'latex'` on equation blocks. Checked further: MinerU carries **no emphasis information anywhere** (zero `**` in its markdown, no style/font/span keys in `middle.json`). So the signal has no source in MinerU; it would need a separate pass reading the PDF directly (PyMuPDF exposes per-span bold flags). Recommend dropping it from the ranker for now β one of six signals, and adding a PDF pass buys nothing measured yet. Your ranker, your call. **(c) chunk size β done:** ~1500-token cap, matching calibration Β§4; splits at item boundaries so text stays verbatim. **(d) `chapter` lossy β done:** now `chapters: list[str]`, one per page the chunk spans. **(e) pydantic β done.** **(f) `TermRecord`** β left to you; the declaration in `contracts.py` is a placeholder, not a claim. |
|
| 98 |
| **S7** | Commit fixture artifacts | Rifqi | β¬ | Both source PDFs exist (BUMA standard + Open Pit textbook). Promote a parsed artifact of each into the target repo as committed fixtures, so the extraction half is testable **with MinerU not installed at all** β the seam doing its job |
|
| 99 |
| **S8** | Write the seam rule into the repo docs | Rifqi | β¬ | Extraction never takes a file path; neither half imports the other's internals; parsing stays swappable behind the artifact. The prototype drifted from this, so the rule needs to outlive our memory of it |
|
| 100 |
|
|
|
|
| 110 |
| **B1** | MinerU cost justification (written) | Sofhia | β¬ | Owed for the budget proposal, from the 13 Aug exec review |
|
| 111 |
| **B2** | MinerU install + run on the sample docs | Sofhia | β
| Complete 9-page parse of the BUMA standard delivered 2026-08-19: **118 items** (90 text, 14 table, 11 equation, 3 chart), pages 0β8. Includes the rule-of-thumb grounding content (Other Activity, Controllable/Uncontrollable, Standby), so the fixture exercises all four extraction branches. Supersedes an earlier 5-page run |
|
| 112 |
| **B3** | Evaluate MinerU quality vs. the current path | Sofhia | β¬ | Compare on tables, the p.2 legend block, formulas and figures β against the PyMuPDF baseline the prototype used, which has real numbers rather than a guess |
|
| 113 |
+
| **B4** | Indonesian check on **parsing** | Sofhia | π | Distinct from E1, which measured GLiNER, not the parser. Partly answered 2026-08-19: MinerU has **no `id` language code** β Indonesian documents use `ch`, whose model covers Latin script (`config.periksa()` now rejects a wrong code before models load). `lang` only applies to the `pipeline` backend and only when OCR actually runs, so it is moot under `vlm`. Text/layout **quality** on Bahasa Indonesia still to be judged against B3 |
|
| 114 |
+
| **B5** | Section-aware chunking | Sofhia | π | **Built 2026-08-19** in `normalize.py`. Splits on MinerU's `text_level` hierarchy into a `heading_path` breadcrumb, with numbering as fallback. Three real-document traps handled, two of them from Β§4 of the calibration doc: repeated breadcrumbs no longer reopen a section, **only numbered headings** may open one (so `Keterangan:` keeps a legend with its figure), and a ~1500-token cap guards documents with no detected headings. **Acceptance test still open** β needs the Open Pit textbook (S7) to confirm the NSR definition survives pp. 92β94 intact |
|
| 115 |
+
| **B6** | Emit the agreed artifact | Sofhia | β
| **Done 2026-08-19** (`5840d201`). `src/knowledge_parsing/` writes `ParsedDocument`, pydantic, not MinerU's native shape. `parse.py` keeps a content-addressed cache of the untouched MinerU output beside it, and `parser_backend` is read back from `_middle.json` so the artifact records what actually ran |
|
| 116 |
| **B7** | Fallback path behind the same artifact | Sofhia | β¬ | Tesseract / Azure Document Intelligence emits the *same* artifact, so extraction never knows which parser ran |
|
| 117 |
+
| **B8** | Keep the parsing subsystem additive + gated | Sofhia | β
| **Done 2026-08-19.** `src/knowledge/` is untouched; `src/knowledge_parsing/` sits beside it. MinerU is an **optional extra** (`knowledge-parsing`), not a main dependency, and is imported inside `parse.py` at call time β verified that importing the package leaves `mineru` and `torch` out of `sys.modules`, so the deployed Space neither builds nor ships them |
|
| 118 |
|
| 119 |
**Findings from the real MinerU output that affect B5/B6:**
|
| 120 |
|
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# knowledge_parsing
|
| 2 |
+
|
| 3 |
+
Document parsing β the first half of the knowledge pipeline. PDF/DOCX in, a
|
| 4 |
+
versioned `ParsedDocument` out.
|
| 5 |
+
|
| 6 |
+
The second half (`src/knowledge_extraction/`) consumes that artifact and never
|
| 7 |
+
the parser itself, which is what keeps MinerU swappable: a Tesseract or Azure
|
| 8 |
+
Document Intelligence path emits the same artifact and extraction never learns
|
| 9 |
+
which one ran.
|
| 10 |
+
|
| 11 |
+
Additive and flag-gated. The existing unstructured path (`src/knowledge/`,
|
| 12 |
+
Tesseract OCR β chunk β pgvector) is untouched and keeps running as-is.
|
| 13 |
+
|
| 14 |
+
## Files
|
| 15 |
+
|
| 16 |
+
- `contracts.py` β **the seam.** `Chunk`, `ParsedDocument`, and the declared
|
| 17 |
+
`Mention` / `TermRecord` handoff shapes. The one file both halves must agree on
|
| 18 |
+
- `config.py` β all settings, and the single place the GPU backend flips
|
| 19 |
+
- `parse.py` β MinerU wrapper with a content-addressed parse cache
|
| 20 |
+
- `normalize.py` β MinerU's flat item list β meaningful chunks
|
| 21 |
+
- `render.py` β LaTeX/HTML β readable prose for `Chunk.text`
|
| 22 |
+
- `checks.py` β quality warnings, including silent-corruption detection
|
| 23 |
+
- `manifest.py` / `report.py` β per-run record and throughput extrapolation
|
| 24 |
+
- `run.py` β batch CLI: one folder in, artifacts + manifest out
|
| 25 |
+
|
| 26 |
+
## Use
|
| 27 |
+
|
| 28 |
+
```bash
|
| 29 |
+
python -m src.knowledge_parsing.run --input data/knowledge_docs/
|
| 30 |
+
python -m src.knowledge_parsing.report --scale 6800
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
MinerU is an optional extra, not a main dependency β the agent service never
|
| 34 |
+
parses documents at request time, so the deployed Space does not ship it:
|
| 35 |
+
|
| 36 |
+
```bash
|
| 37 |
+
uv sync --extra knowledge-parsing
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
Importing this package does not import MinerU or torch; only `parse.py` does, at
|
| 41 |
+
call time.
|
| 42 |
+
|
| 43 |
+
## Three things that are easy to get wrong
|
| 44 |
+
|
| 45 |
+
**`Chunk.text` must stay verbatim.** Extraction's guardrail locates quoted spans
|
| 46 |
+
literally inside it. Reflowing or normalising the text makes the lookup fail and
|
| 47 |
+
the field goes silently null β which presents as a bad model, not as a parser
|
| 48 |
+
bug.
|
| 49 |
+
|
| 50 |
+
**`page_idx` is 0-based**, exactly as MinerU reports it, with no conversion
|
| 51 |
+
anywhere in the pipeline. Converting to 1-based is the UI's job, done once at
|
| 52 |
+
display time, so the artifact always matches the raw MinerU output kept beside
|
| 53 |
+
it in the cache.
|
| 54 |
+
|
| 55 |
+
**Markup never goes in `text`.** The term filter is an NER model reading prose;
|
| 56 |
+
MinerU writes formulas character-spaced (`P u r c h a s i n g ~ c o s t s`) and
|
| 57 |
+
tables as HTML, and neither yields a single mention. Measured on one document and
|
| 58 |
+
gold set, changing only the parse: raw markup scored recall 0.7561 against 0.8537
|
| 59 |
+
for plain text; rendering it back recovered 0.8293. The markup is preserved in
|
| 60 |
+
`Chunk.latex` and `Chunk.table_html`, because the formula branch needs that form.
|
| 61 |
+
|
| 62 |
+
## Output layout
|
| 63 |
+
|
| 64 |
+
```
|
| 65 |
+
data/knowledge_cache/parse/<hash>-<config>-<version>/ untouched MinerU output
|
| 66 |
+
data/knowledge_runs/<run_id>/
|
| 67 |
+
βββ manifest.json backend, versions, timings, quality warnings
|
| 68 |
+
βββ failures.jsonl documents that failed, with traces
|
| 69 |
+
βββ <doc>/chunks.json the artifact
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
Both are gitignored β they are data, not code.
|
| 73 |
+
|
| 74 |
+
See `KNOWLEDGE_PIPELINE_TODO.md` (root) for the task breakdown and the seam
|
| 75 |
+
discussion, and `KNOWLEDGE_PIPELINE_CALIBRATION.md` Β§4 for the chunking
|
| 76 |
+
constants this module follows.
|