In Switzerland, a letter from a public office can start a clock. A Zahlungsbefehl (payment order) gives you 10 days to object. An Einsprache period is often 30. Miss the date and you lose a right. Many people read these letters in a language that isn't their first.
I built a small local tool, "Sportello chiaro", to explain such a letter in plain Italian and point out the deadline. It's an MVP. I want to describe the one design decision that I think matters, and be clear about what is and isn't tested.
The rule: the model never produces the critical facts
A language model is good at explaining. It is not reliable at date arithmetic or at quoting the right number of days. So the deadline does not come from the model. It comes from code:
SERVED_RE = re.compile(
r"(?:zugestellt am|zustellung am|zugestellt:|notifié le|notifiée le|reçu le)"
r"\s*(\d{1,2})[.\/](\d{1,2})[.\/](\d{4})", re.I)
The tool finds the notification date with this pattern, picks the number of days from the document type (10 for a Zahlungsbefehl or Strafbefehl, 30 for an Einsprache), and computes end = served + timedelta(days=days).
The model then gets the letter and a system prompt that says, in short: use only information present in the letter, and do not invent dates, amounts or law articles. Its job is the readable explanation, in four fixed sections.
Graceful failure
The call to the local model is plain urllib, and it returns the text of the first choice. If the model call fails, the analysis falls back to the rule-based output and records the error. The user still sees the deadline and a response template, just without the prose.
What is tested
- Stack: Python 3.10 standard library only (
urllib,http.server,re,datetime,json), about 300 lines. A CLI and a local web page on127.0.0.1:8080. - Model: Gemma 3 1B (Q4_K_M) through
llama-cpp-pythonfor my tests. The intended default is a larger model through Ollama, which I have not tested. - Six pytest tests, rules level only. One checks that a notification on 12.10 plus 10 days gives 22.10.2026. Another checks that a response template is selected. A third checks that an unrelated text gets low urgency.
- One end-to-end run on one invented letter. About 40 seconds with the 1B model.
What is not done
- No schema check on the model's output. The tool does not verify that the answer contains the four sections, or that the deadline in the prose matches the computed one. That's the next thing I'd add.
- Weekends and public holidays are not handled in the deadline calculation.
- No OCR and no PDF input. You paste text.
- No users yet. The repository is not public.
- It is not legal advice, and I wouldn't present it as such.
Why local
These letters contain names, addresses and debts. Keeping the model on the user's machine means the text never leaves it. A small model is slower and less fluent, which is why the rules carry the parts that must be right.
Takeaway
When a task mixes language and facts, split it. Let code produce what must be exact and let the model do the explaining. Then say plainly which parts you have tested.
Top comments (0)