Spaces:
Runtime error
Decisions, in plain English
This explains what we changed and why, without jargon. Read top to bottom.
1. Which branch do we build on?
We have three branches:
mainβ the original working app.feature/complete-uiβmainplus bug fixes and 5 new features (Risk Heatmap, Contract Comparison, Chat, Defined-Terms glossary). It also swapped the AI model that tags clauses to "LegalBERT" β that swap was a mistake (see below).dynamicβ the one we use. Built onmain, it adds the dynamic baseline, all the genuine bug/accuracy fixes, the 5 features, and the fine-tuning tools β while keepingmain's better DeBERTa model (no LegalBERT).
Decision: use dynamic. It is the best of all worlds, and merges cleanly into main.
2. LegalBERT vs DeBERTa β why we did NOT use LegalBERT
The app tags each clause with a type ("this is a liability clause", "this is an auto-renewal clause"). It uses an AI model to do that.
- The name "LegalBERT" sounds better because it has "legal" in it. But LegalBERT, on its own, cannot classify anything β it only understands legal words. To make it classify, you must train it first.
- The
feature/complete-uibranch tried to use LegalBERT without training, by comparing "how similar" a clause is to a description. That method is weak and was never measured. It also came with a stricter software licence. - The original model (DeBERTa) can classify out of the box with no training, scored a real 0.61 in our tests, and has a clean licence.
Decision: keep DeBERTa. "Legal" in the name did not make it better.
3. The dynamic baseline β removing the hardcoded numbers
The app flags risky clauses by comparing them to "what's normal" (e.g. "a liability cap above 12 months is risky"). The problem: those numbers (12 months, 60 days, 99% uptimeβ¦) were typed directly into the code. If a judge asks "where does 12 come from?", the honest answer was "we made it up."
We fixed this:
- The numbers now live in a data file with a source, not in the code.
- A script (
extract_cuad_baselines.py) can calculate those numbers from 500+ real contracts, so the answer becomes "it's the median of 500 real contracts" instead of a guess. - As you upload your own contracts, the baseline can adapt toward them β but it never drops below the safe default, so nothing breaks.
- A safety test proves the new system produces the exact same flags as before on our demo contract, so accuracy did not drop.
Decision: baselines are now data, not hardcoded β and provably safe.
4. Fine-tuning β making the clause tagger smarter
"Fine-tuning" = taking the DeBERTa model and teaching it our exact task using thousands of example clauses that experts already labelled (the CUAD dataset, 13,000+ labels).
- Today (no training): 0.61 accuracy.
- After fine-tuning: expected 0.75β0.85 (a real, measurable jump).
We built the full toolkit so the team isn't stuck:
prepare_cuad.pyβ turns the raw dataset into training examples.train_classifier.pyβ trains the model (one command).eval/run_eval.py --classifier finetunedβ measures the result.
Training the full model is faster on a GPU (e.g. Google Colab); the tools run anywhere. See FINE_TUNING.md.
Decision: fine-tune DeBERTa (not LegalBERT).
5. Do we train Qwen? No.
Qwen is the separate AI that writes out obligations in plain English. It is a different kind of model (it generates text, it doesn't tag).
- It already works without training.
- Training it would need a GPU, a special dataset we don't have, and lots of time β for little gain.
- The cheap, same-day way to improve it is better instructions/examples in the prompt, not training.
Decision: do NOT train Qwen. Improve it with prompting if needed.
Summary table
| Question | Answer |
|---|---|
| Which branch? | dynamic |
| LegalBERT or DeBERTa? | DeBERTa (LegalBERT was worse + unmeasured) |
| Hardcoded baselines? | Replaced with data-derived, adaptive baselines |
| Fine-tune the tagger? | Yes β DeBERTa on CUAD (0.61 β ~0.75β0.85) |
| Train Qwen? | No β prompt-tune instead |