What this recipe does
The "ChatGPT, summarize this contract" workflow has the failure mode you'd expect: a number that isn't in the contract appears in the summary. This recipe makes that mode unreachable. Every label points to a byte-exact span in the input. If the model hallucinates a "liability cap of $5M" that isn't in the contract, the verifier rejects it before the artifact returns.
The output schema is closed-vocab on clause type (32 categories from your firm's playbook) and free-text on span. Atypical clauses get bucketed to "other" with a span and a free-text label — these are the rows your associates should look at first.
The spec
{
"output_kind": "json",
"schema": {
"required": ["clauses", "governing_law", "parties"],
"properties": {
"clauses": { "type": "array", "items": {
"required": ["type", "span", "start", "end"],
"properties": {
"type": { "$ref": "clause-types.json" },
"span": { "type": "string" },
"start": { "type": "integer" },
"end": { "type": "integer" },
"monetary_thresholds": { "type": "array", "items": {"type":"string"} },
"label_if_other": { "type": "string" }
}
} },
"governing_law": { "type": "string" },
"parties": { "type": "array", "items": {"type":"string"} }
}
},
"verifier": {
"span_must_byte_match_input": true,
"clause_type_must_be_in_taxonomy": true,
"monetary_threshold_must_appear_in_span": true
}
}
Compile
kolm compile "contract clause extractor with span-grounded labels" \ --base qwen2.5-7b-instruct \ --pairs ./gold-contracts/ \ --taxonomy ./clause-types.json \ --verifier span-byte-grounded,closed-vocab \ --k-floor 0.86 \ --output legal-clause-extract.kolm ok wrote legal-clause-extract.kolm k_score=0.89 signature=hmac-sha256
K-score gate
Run-time profile
Deploy
# DMS hook — every contract uploaded gets a clause-list as a sidecar: on_contract_upload = (file) => { const x = kolm.run('legal-clause-extract.kolm', pdf.text(file)); dms.attach(file.id, 'clause-list.json', x); if (x.clauses.some(c => c.type === 'liability_cap' && c.monetary_thresholds.length === 0)) { review.queue(file.id, 'liability cap clause has no number — review'); } };