[KongchangAI]
· 2 min read· 1,373 words

Code Counts the Facts, the Model Explains: A Local LLM for Swiss Administrative Letters

Code Counts the Facts, the Model Explains: A Local LLM for Swiss Administrative Letters

A local LLM assistant for Swiss administrative letters uses code to calculate deadlines, the model only explains, and it honestly labels untested boundaries.

The developer built a small local tool called Sportello chiaro that explains Swiss administrative letters in plain Italian and flags statutory deadlines. Its core design principle is: the language model never produces key facts. Deadlines are calculated by regex matching the service notification date, selecting days by document type, and then computing with `timedelta`; the model is only responsible for generating four readable explanation sections. When the model call fails, it automatically falls back to rule-based output, and users still see the deadline and reply template. The tool uses only about 300 lines of Python standard library, tests cover the rule level, the model's default configuration is untested, there is no output schema validation, and weekends and holidays are not handled. The author emphasizes that separating language from facts and honestly labeling untested parts is the foundation of credibility for such tools.

Letters from Swiss public agencies often come with deadlines that cannot be ignored. A Zahlungsbefehl (payment order) typically gives you only ten days to file an objection, while the statutory deadline for an Einsprache (objection) is commonly thirty days. Miss the date and a right is gone. And the language these letters are written in is not the native tongue of many recipients.

To address this, a developer built a small local tool called "Sportello chiaro" that explains such letters in plain Italian and flags the deadlines. It is an MVP (minimum viable product), and its core design philosophy is well worth discussing: in scenarios involving legal facts, where should the language model stand?

rss source: Let the model explain, let the code count: a local LLM helper for Swiss administrative letters

Core Principle: The Model Never Produces Key Facts

Language models are good at explaining, but they are unreliable at date arithmetic and accurately citing the number of days. Therefore, deadlines are not generated by the model but produced by code.

The tool uses a regex to match the service notification date, extracting the date string following German zugestellt am or French notifié le:

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)

Once the notification date is obtained, the tool selects the corresponding number of days based on the document type—10 days for Zahlungsbefehl or Strafbefehl, 30 days for Einsprache—and then executes end = served + timedelta(days=days) to complete the calculation.

Only then does the model enter the scene, receiving the original letter text and a system prompt. The core constraint in the prompt is straightforward: use only the information already present in the letter; do not fabricate dates, amounts, or legal provisions. Its sole task is to generate a readable explanation in four fixed sections.

Statutory objection periods for Zahlungsbefehl and Strafbefehl

A Zahlungsbefehl (payment order) is the start of Swiss debt collection proceedings, issued by the cantonal Betreibungsamt (collection agency). Upon receipt, if the debtor believes the debt does not exist or the amount is incorrect, they can file a Rechtsvorschlag (legal objection) with the same agency within ten days; after that, the debt is deemed undisputed and can proceed to enforcement. A Strafbefehl (penal order) is a penalty decision made directly by a prosecutor without a main hearing. If the recipient disagrees, they must also file an Einsprache within ten days, otherwise the judgment takes effect. The term Einsprache has slightly different meanings in the two procedures: in a penal order, it refers to objecting to the penal order itself; in an administrative decision, it broadly refers to an objection to an official ruling. The tool switches between 10 and 30 days according to document type, precisely corresponding to these different statutory frameworks.

Graceful Degradation: If the Model Fails, the Facts Remain

The part that calls the local model uses plain urllib, taking the text of the first choice in the returned result. If the model call fails, the analysis pipeline automatically falls back to the rule-based output and logs the error.

Users can still see the deadline and a reply template, just without the explanatory text. This design decouples "must be correct" from "reads smoothly"—the former never disappears due to model instability.

Plain urllib and local model services

The code directly uses the Python standard library urllib to make HTTP requests to the local inference service, taking the text of the first choice from the returned JSON. This typically means the model is exposed via an OpenAI-compatible /v1/chat/completions interface; Ollama and llama-cpp-python both provide such endpoints. Compared to requests or the official SDK, urllib requires no additional dependencies, at the cost of more cumbersome error handling, hand-written timeouts and retries, and limited streaming output support. For an MVP of about 300 lines aimed at a single-machine user, this is a deliberate choice of minimal dependencies; but in production, a more mature HTTP client would typically be used to handle connection pooling, timeout strategies, and backend switching.

What Has Been Tested

The developer is restrained in describing the testing boundaries, and it is worth a closer look:

  • Tech stack: Only Python 3.10 standard library (urllib, http.server, re, datetime, json), about 300 lines. Provides a CLI and a local web page running at 127.0.0.1:8080.
  • Model: In testing, Gemma 3 1B (Q4_K_M) running via llama-cpp-python was used. The intended default configuration is to call a larger model via Ollama, but that configuration has not been tested.
  • Testing: Six pytest tests, all at the rule level. One verifies that service on October 12 plus 10 days yields October 22, 2026; another verifies the reply template selection logic; a third verifies that irrelevant text is classified as low urgency.
  • End-to-end: A complete run with one fictitious letter took about 40 seconds with the 1B model.

Gemma 3 1B and Q4_K_M quantization

Gemma 3 1B is a small open-source model released by Google, with about one billion parameters, capable of running on an ordinary laptop CPU. Q4_K_M is a 4-bit quantization scheme in the GGUF format: K indicates k-quant block quantization, and M is a medium compression level, typically achieving a good balance between model size and output quality. After quantization, the model size drops from several GB to less than 1 GB, with faster inference, but the coherence and factual consistency of generated text decrease somewhat. This explains why running a fictitious letter with the 1B model takes about 40 seconds, and why date calculation is separated from readable explanation: even if the small model explains stiffly, it does not affect the correctness of the deadline itself.

What Is Not Yet Done

The gaps also need to be clearly stated:

  • There is no schema validation of model output. The tool does not verify whether the answer contains the four sections, nor does it check whether the deadlines in the text match the calculated dates. This is what the developer plans to add next.
  • The deadline calculation does not handle weekends and public holidays.
  • No OCR support, and no PDF input; only pasted text is supported.
  • There are no users yet, and the repository is not public.
  • It is not legal advice, and the developer does not intend to present it as legal advice.

Why Run Locally

These letters contain names, addresses, and debt information. Keeping the model on the user's own machine means the text never leaves the local environment. The cost is that small models are slower and stiffer in expression—which is exactly why the parts that must be correct are handled by rules.

A Reusable Approach

When a task involves both language and facts, split it apart. Let code produce what must be precise, and let the model handle explanation. Then, clearly state which parts you have tested and which you have not.

The applicability of this principle goes far beyond Swiss administrative letters. Any scenario that requires extracting hard facts from documents and supplementing them with natural language explanations—insurance claim notices, tax letters, court documents—can borrow this division of labor: "code counts, model expresses." And honest labeling of untested boundaries is itself part of the credibility of such tools.

One-line Summary

A local LLM assistant for Swiss administrative letters uses code to calculate deadlines, the model only explains, and it honestly labels untested boundaries.

Paragraph Summary

The developer built a small local tool called Sportello chiaro that explains Swiss administrative letters in plain Italian and flags statutory deadlines. Its core design principle is: the language model never produces key facts. Deadlines are calculated by regex matching the service notification date, selecting days by document type, and then computing with timedelta; the model is only responsible for generating four readable explanation sections. When the model call fails, it automatically falls back to rule-based output, and users still see the deadline and reply template. The tool uses only about 300 lines of Python standard library, tests cover the rule level, the model's default configuration is untested, there is no output schema validation, and weekends and holidays are not handled. The author emphasizes that separating language from facts and honestly labeling untested parts is the foundation of credibility for such tools.

#local LLM#Swiss administrative letters#statutory deadlines#regex date extraction#Gemma 3 1B
Share:

Related articles