bogdanraduta commited on
Commit
433c92d
·
verified ·
1 Parent(s): 1aca094

Add README.md

Browse files
Files changed (1) hide show
  1. README.md +362 -4
README.md CHANGED
@@ -1,9 +1,367 @@
1
  ---
2
- library_name: mlx
3
  license: apache-2.0
4
- license_link: https://huggingface.co/Qwen/Qwen3-4B/blob/main/LICENSE
 
 
 
 
 
5
  pipeline_tag: text-generation
6
- base_model: mlx-community/Qwen3-4B-4bit
7
  tags:
8
- - mlx
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
 
2
  license: apache-2.0
3
+ language:
4
+ - en
5
+ - fr
6
+ - de
7
+ - ro
8
+ library_name: transformers
9
  pipeline_tag: text-generation
10
+ base_model: Qwen/Qwen3-4B
11
  tags:
12
+ - lora
13
+ - regulatory
14
+ - compliance
15
+ - ontology-extraction
16
+ - information-extraction
17
+ - mlx
18
+ - gguf
19
+ - flowx
20
+ model-index:
21
+ - name: semantic-mapper
22
+ results:
23
+ - task:
24
+ type: text-generation
25
+ name: Regulatory ontology extraction (held-out, document-disjoint, EN/FR/DE/RO)
26
+ metrics:
27
+ - type: f1
28
+ name: concepts F1 (controlled 252-concept vocab)
29
+ value: 0.54
30
+ - type: f1
31
+ name: concepts F1 (fuzzy / semantic match)
32
+ value: 0.58
33
+ - type: f1
34
+ name: domain_tags F1 (free-text)
35
+ value: 0.55
36
+ - type: accuracy
37
+ name: entities field-accuracy
38
+ value: 0.53
39
+ - type: accuracy
40
+ name: JSON validity
41
+ value: 1.0
42
+ - type: accuracy
43
+ name: all-3-facets present
44
+ value: 1.0
45
  ---
46
+
47
+ ## Inference contract
48
+
49
+ Running this model correctly requires its **frozen inference contract**: the exact
50
+ system prompt, user-turn format, decode settings, and output JSON schema it was
51
+ trained against. See [`inference_contract/`](./inference_contract):
52
+
53
+ - [`INFERENCE.md`](./inference_contract/INFERENCE.md): wiring guide: system prompt, user turn (`Extract ontology from this chunk:\n\nCHUNK:\n<text>`), decode settings (`enable_thinking=False`, greedy/temp 0, `max_new_tokens` ~1024), and the note that `semantic.concepts` is constrained to the **252-concept controlled vocabulary** (`concept_taxonomy.yaml`, shipped at repo root).
54
+ - [`prompt_mapper_sys_v1.txt`](./inference_contract/prompt_mapper_sys_v1.txt): the system prompt, verbatim.
55
+ - [`schema_mapper_v1.json`](./inference_contract/schema_mapper_v1.json): output JSON Schema (structural / semantic / governance).
56
+
57
+ Prompt version `mapper_sys_v1`. Do not edit the prompt/schema; the weights are trained against them.
58
+
59
+ # FlowX Semantic Mapper (4B): the regulation-to-ontology tagger
60
+
61
+ **Reads one chunk of regulatory or legal text and emits a structured ontology JSON: where the clause sits in its source, what it is about, and how it maps to policy and escalation.** Languages: EN, FR, DE, RO. Domains: banking, insurance, logistics, labor.
62
+
63
+ Given a single clause (a subsection of a state insurance code, an EU regulation article, a labor-code paragraph, an ADR packing rule), the Mapper returns three facets: a **structural** locator (source id, document type, and the full hierarchy path), a **semantic** payload (free-text `domain_tags`, controlled-vocabulary `concepts`, and an actor/action/object/constraint entity triple), and a **governance** payload (`PDP.<domain>.<rule>` policy references plus a single `if ... THEN escalate` guard clause).
64
+
65
+ The product insight that shapes everything: **an ontology field is only useful if it is consistent enough to join on.** The first version emitted `concepts` as open free text and produced 1062 unique ids, 88% of them singletons: unlearnable for the model and unmeasurable for us (concepts F1 sat at 0.24). Collapsing that to a **252-concept controlled taxonomy** (`concept_taxonomy.yaml`) is the central design choice of this release: it made `concepts` both learnable and scoreable (F1 0.24 → 0.54), and gave downstream policy code a stable key to join on. `domain_tags` stay free-text on purpose (for recall and human browsing) and are honestly noisier as a result.
66
+
67
+ The Mapper is the first stage of a pipeline, not a standalone product: **Mapper tags a chunk → a policy layer → the [`flowxai/sentinel-gate`](https://huggingface.co/flowxai/sentinel-gate) escalation model decides DECIDE vs ESCALATE.** It is part of the FlowX on-device model family alongside [`flowxai/sentinel-gate`](https://huggingface.co/flowxai/sentinel-gate), [`flowxai/caveat`](https://huggingface.co/flowxai/caveat), and the consumer scam detectors [`flowxai/scam-guard-qwen06b`](https://huggingface.co/flowxai/scam-guard-qwen06b) / [`flowxai/scam-guard-qwen17b`](https://huggingface.co/flowxai/scam-guard-qwen17b).
68
+
69
+ LoRA-fine-tuned from Apache-2.0 **`Qwen/Qwen3-4B`**. Formats: fp16 safetensors (transformers / CUDA / vLLM), **MLX-quantized** int4 + int8 (Apple Silicon), and **GGUF** `Q8_0` + `Q4_K_M` (llama.cpp / CPU / Ollama).
70
+
71
+ ---
72
+
73
+ ## How do I use it?
74
+
75
+ Two copy-pasteable ways to turn a regulatory chunk into ontology JSON, plus a note.
76
+
77
+ Real example input (a real clause from the California Insurance Code, section 790.03(h)(2)):
78
+
79
+ ```
80
+ Failing to acknowledge and act reasonably promptly upon communications with respect
81
+ to claims arising under insurance policies.
82
+ ```
83
+
84
+ The user turn wraps it in the fixed template (see the inference contract):
85
+
86
+ ```
87
+ Extract ontology from this chunk:
88
+
89
+ CHUNK:
90
+ Failing to acknowledge and act reasonably promptly upon communications with respect to claims arising under insurance policies.
91
+ ```
92
+
93
+ ### (a) transformers / CUDA (fp16 safetensors)
94
+
95
+ ```python
96
+ from transformers import AutoModelForCausalLM, AutoTokenizer
97
+
98
+ SYSTEM_PROMPT = (
99
+ "You are a legal and regulatory ontology extractor.\n"
100
+ "Extract structured tags from document chunks. Output ONLY valid JSON."
101
+ )
102
+
103
+ tok = AutoTokenizer.from_pretrained("flowxai/semantic-mapper")
104
+ model = AutoModelForCausalLM.from_pretrained("flowxai/semantic-mapper", device_map="cuda")
105
+
106
+ chunk = ("Failing to acknowledge and act reasonably promptly upon communications "
107
+ "with respect to claims arising under insurance policies.")
108
+ user = f"Extract ontology from this chunk:\n\nCHUNK:\n{chunk}"
109
+
110
+ prompt = tok.apply_chat_template(
111
+ [{"role": "system", "content": SYSTEM_PROMPT},
112
+ {"role": "user", "content": user}],
113
+ add_generation_prompt=True, enable_thinking=False, tokenize=False,
114
+ )
115
+ ids = tok(prompt, return_tensors="pt").to(model.device)
116
+ out = model.generate(**ids, max_new_tokens=1024, do_sample=False, temperature=0.0)
117
+ raw = tok.decode(out[0][ids.input_ids.shape[1]:], skip_special_tokens=True)
118
+ ```
119
+
120
+ ### (b) MLX (Apple Silicon)
121
+
122
+ Off the MLX-quantized weights (int4 / int8):
123
+
124
+ ```bash
125
+ mlx_lm.generate --model flowxai/semantic-mapper-mlx-int4 --temp 0 --max-tokens 1024 \
126
+ --system-prompt "$(printf 'You are a legal and regulatory ontology extractor.\nExtract structured tags from document chunks. Output ONLY valid JSON.')" \
127
+ --prompt "$(printf 'Extract ontology from this chunk:\n\nCHUNK:\nFailing to acknowledge and act reasonably promptly upon communications with respect to claims arising under insurance policies.')"
128
+ ```
129
+
130
+ ```python
131
+ from mlx_lm import load, generate
132
+
133
+ SYSTEM_PROMPT = (
134
+ "You are a legal and regulatory ontology extractor.\n"
135
+ "Extract structured tags from document chunks. Output ONLY valid JSON."
136
+ )
137
+
138
+ model, tok = load("flowxai/semantic-mapper-mlx-int4")
139
+ chunk = ("Failing to acknowledge and act reasonably promptly upon communications "
140
+ "with respect to claims arising under insurance policies.")
141
+ user = f"Extract ontology from this chunk:\n\nCHUNK:\n{chunk}"
142
+ prompt = tok.apply_chat_template(
143
+ [{"role": "system", "content": SYSTEM_PROMPT},
144
+ {"role": "user", "content": user}],
145
+ add_generation_prompt=True, enable_thinking=False,
146
+ )
147
+ raw = generate(model, tok, prompt=prompt, max_tokens=1024, verbose=False)
148
+ ```
149
+
150
+ Either backend returns the **same JSON** for this chunk (this is the real held-out target for the example above):
151
+
152
+ ```json
153
+ {
154
+ "structural": {
155
+ "source_id": "US_CA_INS_790_03_h_2",
156
+ "hierarchy": [
157
+ "state_code", "california_insurance_code",
158
+ "section", "790.03",
159
+ "subdivision", "h",
160
+ "paragraph", "2"
161
+ ],
162
+ "document_type": "state_insurance_code"
163
+ },
164
+ "semantic": {
165
+ "domain_tags": ["insurance_regulation", "claims", "unfair_claims_practices", "settlement"],
166
+ "concepts": ["claim_acknowledgement", "prompt_communication", "settlement_timeline"],
167
+ "entities": {
168
+ "actor": "insurer",
169
+ "action": "acknowledge and act reasonably promptly upon communications",
170
+ "object": "claim_communication",
171
+ "constraint": { "condition": "reasonably_prompt" }
172
+ }
173
+ },
174
+ "governance": {
175
+ "policy_references": ["PDP.insurance.claims_settlement", "PDP.insurance.unfair_practices"],
176
+ "escalation_trigger": "if !claim_communication_acknowledged_promptly THEN escalate"
177
+ }
178
+ }
179
+ ```
180
+
181
+ ### (c) A short note on decoding
182
+
183
+ `enable_thinking=False` is not optional. Qwen3-4B is a thinking model, but this
184
+ adapter was trained on pure JSON with no reasoning block, so the default template
185
+ yields empty output. Decode greedily (temperature 0), one chunk per call, and cap
186
+ at ~1024 new tokens. GGUF (`Q8_0` / `Q4_K_M`) runs the same contract on
187
+ llama.cpp / Ollama. Full wiring is in [`inference_contract/INFERENCE.md`](./inference_contract/INFERENCE.md).
188
+
189
+ ---
190
+
191
+ ## How it works
192
+
193
+ ```
194
+ regulatory
195
+ text chunk ---> FlowX Semantic Mapper 4B ---> structural (source_id, hierarchy, document_type)
196
+ |
197
+ +--> semantic (domain_tags, concepts, entities)
198
+ |
199
+ +--> governance (policy_references, escalation_trigger)
200
+ ```
201
+
202
+ Pipeline line:
203
+
204
+ ```
205
+ chunk --> Semantic Mapper (this model) --> policy layer --> Sentinel Gate --> DECIDE | ESCALATE
206
+ ```
207
+
208
+ Under the hood the Mapper is: `tokenizer → Qwen3-4B (LoRA fine-tuned) → greedy JSON
209
+ decode (thinking disabled) → parse against schema_mapper_v1.json`. The
210
+ `semantic.concepts` field targets the 252-concept controlled taxonomy
211
+ (`concept_taxonomy.yaml`), so downstream code can filter out-of-vocabulary ids and
212
+ join the rest straight into the policy layer.
213
+
214
+ ---
215
+
216
+ ## Output schema
217
+
218
+ A single JSON object with three facets (full schema:
219
+ [`schema_mapper_v1.json`](./inference_contract/schema_mapper_v1.json)):
220
+
221
+ **`structural`**: where the clause lives.
222
+ - `source_id`: stable id for the chunk, usually an uppercased normalization of the citation path (e.g. `US_CA_INS_790_03_h_2`).
223
+ - `hierarchy`: ordered flat array of alternating `[level, value]` pairs from the outermost container down to the leaf.
224
+ - `document_type`: snake_case document class (e.g. `state_insurance_code`, `eu_regulation`, `labor_code`, `adr_agreement`).
225
+
226
+ **`semantic`**: what the clause is about.
227
+ - `domain_tags`: free snake_case topic tags (**open vocabulary**; noisier by design, tuned for recall/browsing).
228
+ - `concepts`: concept ids from the **252-concept controlled taxonomy** (`concept_taxonomy.yaml`). Treat any id not in the taxonomy as out-of-vocabulary.
229
+ - `entities`: `{actor, action, object, constraint}`: who the obligation falls on, what must/may be done, on what, and a free key/value `constraint` map of qualifying conditions (e.g. `{"condition":"reasonably_prompt"}`).
230
+
231
+ **`governance`**: how it maps to policy and escalation.
232
+ - `policy_references`: ids in the form `PDP.<domain>.<rule>` (e.g. `PDP.insurance.claims_settlement`).
233
+ - `escalation_trigger`: a single guard clause `if <condition> THEN escalate`, consumed by the Sentinel Gate.
234
+
235
+ The controlled taxonomy holds **252 canonical concepts** across 6 categories
236
+ (money, rights_waived, time_renewal, lease, insurance, data), each with an `id`, a
237
+ `definition`, and a `primary_domain`. It ships as `concept_taxonomy.yaml` at the
238
+ repo root.
239
+
240
+ ---
241
+
242
+ ## Evaluation
243
+
244
+ Held-out set: **n = 112**, **document-disjoint** from training, multilingual
245
+ (EN / FR / DE / RO). Read the honest numbers first, then the caveat.
246
+
247
+ | Metric | Value |
248
+ | --- | --- |
249
+ | JSON validity | **1.00** |
250
+ | all-3-facets present | **1.00** |
251
+ | `domain_tags` F1 (free-text) | 0.55 |
252
+ | `concepts` F1 (controlled 252-concept vocab) | **0.54** |
253
+ | `concepts` F1 (fuzzy / semantic match) | 0.58 |
254
+ | `entities` field-accuracy | 0.53 |
255
+
256
+ **The headline improvement, quantified.** Adopting the controlled 252-concept
257
+ vocabulary plus a real-regulation data expansion moved `concepts` F1 from **0.24**
258
+ (open free-text vocab) to **0.54**, and `entities` from **unmeasured** (the facet
259
+ was empty in the first corpus) to **0.53**. The format facets are saturated: JSON
260
+ validity and all-three-facets-present are both 1.00, so the model reliably produces
261
+ a parseable, structurally-complete object every time.
262
+
263
+ **Honest reading.**
264
+ - `concepts` is **partial** at ~0.54: it surfaces many of the right controlled ids, not all of them. Use it as a strong signal, not a complete tagging.
265
+ - `domain_tags` are **free-text** and therefore less consistent than the controlled `concepts` (this is the intended trade: recall/browsability over join-stability).
266
+ - `entities` field-accuracy of 0.53 reflects a genuinely hard four-slot extraction (actor/action/object/constraint), now measurable where before it was not.
267
+ - **EN is strongest.** FR, DE, RO are supported and evaluated, but a bit weaker.
268
+
269
+ > **Home-field caveat (read before citing these numbers).** The held-out set is
270
+ > **document-disjoint** (no shared source document leaks between train and test),
271
+ > which is the right discipline. But the annotations across train and test share a
272
+ > **lineage**: the same controlled taxonomy and the same frontier-model-assisted
273
+ > annotation style. The scores above measure how well the model reproduces *that
274
+ > annotation convention* on unseen documents, which is exactly the deployment task,
275
+ > but they are **not** a claim of agreement with an independent human legal
276
+ > annotator. Treat 0.54 concepts F1 as "reproduces the FlowX taxonomy convention on
277
+ > held-out documents at ~0.54", not "a legal-grade ontology at 0.54".
278
+
279
+ ---
280
+
281
+ ## Formats
282
+
283
+ | Format | Path | Runtime | Notes |
284
+ | --- | --- | --- | --- |
285
+ | fp16 safetensors | repo root | transformers / CUDA / vLLM | full precision |
286
+ | MLX int4 | `mlx-int4/` | Apple Silicon (MLX) | smallest Apple footprint |
287
+ | MLX int8 | `mlx-int8/` | Apple Silicon (MLX) | higher-fidelity Apple path |
288
+ | GGUF `Q8_0` | `gguf/` | llama.cpp / CPU / Ollama | recommended GGUF quant |
289
+ | GGUF `Q4_K_M` | `gguf/` | llama.cpp / CPU / Ollama | smallest GGUF |
290
+
291
+ All formats run the same frozen inference contract (system prompt, user-turn
292
+ template, `enable_thinking=False`, greedy decode).
293
+
294
+ ---
295
+
296
+ ## Intended use & limitations
297
+
298
+ **Intended use.** A **first-stage extractor** in a regulatory-compliance pipeline:
299
+ it turns a clause of legislation into a structured ontology record that a policy
300
+ layer and the [`flowxai/sentinel-gate`](https://huggingface.co/flowxai/sentinel-gate)
301
+ escalation model can act on. It is meant to run over a knowledge base of regulatory
302
+ chunks and produce consistent, joinable tags, with a human reviewing anything the
303
+ Sentinel routes for escalation.
304
+
305
+ **Out of scope & limitations.**
306
+ - **Not legal advice.** The Mapper labels the structure and topic of a text; it does not interpret the law, give a legal opinion, or determine compliance. A human with legal responsibility owns any decision.
307
+ - **Extraction is partial.** `concepts` F1 ~0.54 and `entities` accuracy ~0.53: expect misses and the occasional wrong slot. Do not treat the output as an exhaustive or authoritative tagging.
308
+ - **Controlled vocab, not free-form.** `semantic.concepts` targets the 252-concept taxonomy. Filter out-of-vocabulary ids before joining. `domain_tags` are free-text and inherently noisier.
309
+ - **Language skew.** EN is strongest; FR / DE / RO are supported but weaker.
310
+ - **One chunk at a time.** Trained on single-chunk turns; batching multiple clauses into one user turn degrades output.
311
+ - **Domain coverage.** Banking, insurance, logistics, labor. Text outside these domains is out of distribution.
312
+ - **Annotation-lineage caveat** (see Evaluation): scores measure reproduction of the FlowX annotation convention, not agreement with an independent legal annotator.
313
+
314
+ ---
315
+
316
+ ## Training data
317
+
318
+ - **763 records total**, split **651 train / 112 held-out**, **document-disjoint** (no source document appears in both splits).
319
+ - **Real, verbatim regulatory / legislative text** from official public sources: US eCFR & state codes, EU EUR-Lex, France Légifrance, Germany Gesetze im Internet, Romania legislatie.just.ro, and UNECE/ADR. Every record carries a source URL. Official legislation is reproduced with source acknowledgement.
320
+ - Annotated to the **controlled 252-concept taxonomy** with frontier-model assistance, then validated: concepts checked in-vocabulary, entities checked populated, source URL required per record.
321
+ - Coverage spans four domains (banking / insurance / logistics / labor) and four languages (EN / FR / DE / RO).
322
+
323
+ The controlled taxonomy (`concept_taxonomy.yaml`) is the pivot of this dataset:
324
+ open free-text concepts (1062 unique, 88% singletons) were collapsed to 252
325
+ canonical ids, which is what made the `concepts` facet learnable and measurable.
326
+
327
+ ---
328
+
329
+ ## Fine-tuning
330
+
331
+ **LoRA** on **`Qwen/Qwen3-4B`** with **MLX-LM on Apple Silicon**.
332
+
333
+ | Hyperparameter | Value |
334
+ | --- | --- |
335
+ | method | LoRA |
336
+ | rank | 32 |
337
+ | scale (alpha) | 16 |
338
+ | dropout | 0.05 |
339
+ | layers | `num_layers -1` (all) |
340
+ | epochs | ~3 |
341
+ | LR schedule | cosine 1e-4 → 1e-5 |
342
+ | max_seq_length | 2048 |
343
+ | seed | 42 |
344
+ | thinking | disabled |
345
+
346
+ The assistant turn in training is the exact ontology JSON the model must emit (no
347
+ reasoning block), which is why inference must set `enable_thinking=False`.
348
+
349
+ ---
350
+
351
+ ## Not legal advice
352
+
353
+ This model extracts structure and topic tags from legal/regulatory text. It does
354
+ not provide legal advice, does not interpret the law, and does not determine
355
+ compliance. Its output is an aid to a compliance pipeline under human ownership,
356
+ not a substitute for a qualified professional.
357
+
358
+ ## Links
359
+
360
+ - **Sibling (escalation gate):** [`flowxai/sentinel-gate`](https://huggingface.co/flowxai/sentinel-gate): consumes a case (with the Mapper's governance facet) and decides DECIDE vs ESCALATE.
361
+ - **FlowX on-device family:** [`flowxai/caveat`](https://huggingface.co/flowxai/caveat), [`flowxai/scam-guard-qwen06b`](https://huggingface.co/flowxai/scam-guard-qwen06b), [`flowxai/scam-guard-qwen17b`](https://huggingface.co/flowxai/scam-guard-qwen17b).
362
+
363
+ ## License
364
+
365
+ Apache-2.0 (weights and code). Base model `Qwen/Qwen3-4B` is Apache-2.0. Copyright
366
+ 2026 FlowX.AI; see the `NOTICE` file. Author: Bogdan Răduță, Head of Research,
367
+ FlowX.AI.