cd /news/ai-tools/doze-teach-it-once-let-the-ai-sleep · home › topics › ai-tools › article
[ARTICLE · art-154963] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

Doze: teach it once. Let the AI sleep.

A developer built Doze, an MIT-licensed open-source tool that uses a local Gemma model once to map ambiguous column headers between payment-processor and bank exports, saves the approved mapping as a reviewable JSON artifact, and then reconciles subsequent exports with exact Python arithmetic and no further inference. The model only sees header lists and human-approved explanations and returns either a mapping or a refusal, while Python handles sums and settlement-reference checks; the project's latest local suite ran 120 tests with 119 passing and one Windows symlink-permission test skipped.

by read7 min views1 publishedOct 22, 2026

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady.

Doze refuses to close a week that is one cent short. It can do that with inference disabled.

Most recurring AI workflows keep calling the model. I wanted to see how much of the job could outlive it.

The chore is reconciling payment exports against bank exports. A payment processor calls a column “Payout group.” The bank calls the same reference “Settlement reference.” Customers paid a gross amount, the processor deducted fees, and a net amount reached the bank. Someone has to connect those meanings, check the sums and investigate what does not match.

Doze uses local Gemma to learn the column mapping, then saves it as a small, reviewable JSON artifact. Humans approve what the columns mean. Python does the arithmetic and checks exact settlement references. The model does not read the transaction rows or decide that a discrepancy is probably fine.

The next export does not need another conversation. Reuse the mapping, review the result and leave inference asleep.

That is my interpretation of Slow and Steady: do the uncertain translation once, preserve the result, and stop asking a model to repeat a settled job.

Watch the 68-second walkthrough

The walkthrough uses synthetic exports and a clearly labelled recorded Gemma result, not a fresh teaching call. The saved result comes from an actual local inference run; replaying it requires no model.

Watch for three things:

These are synthetic outcomes, not a year of customer history or a claim of hours saved.

The application replay uses recorded model output. This screenshot is synthetic evidence, not customer history.

Source, tests and saved model evidence

MIT-licensed application code. Python standard library backend, plain JavaScript UI and local Ollama inference. Gemma weights are not distributed with the project and retain their own license terms.

The repository includes the model outputs, failed attempts, synthetic fixtures and verification scripts. The latest local test suite ran 120 tests: 119 passed and one Windows symlink-permission test was skipped. The CI workflow covers Python 3.11 and 3.12 on Windows and Linux, including the offline proof.

To try the recorded-result demo, clone the repository, enter its directory and run:

python -m doze.server

Open http://127.0.0.1:8741, choose Replay the verified Gemma lesson / recorded result, review the mapping and run its tests. Reconcile the messy week, inspect an exception, then try the next clean week and the 52-Friday replay. Python 3.11+ is enough. No account, packages or model download is needed for this route.

TEACH ONCE, LOCALLY
Headers + approved explanations
             |
           Gemma
             |
      Reviewable mapping + fixed tests
             |
      Test and human approval
-------------|-------------------------
REUSE WITHOUT INFERENCE
New exports + approved mapping
             |
      Exact Python reconciliation
             |
      Exceptions or reviewed handoff

Gemma's job is deliberately small. It sees the two header lists, the chosen decimal separator and an explicitly approved explanation of ambiguous columns. It returns either a mapping or a refusal. Each mapped role must use an actual header from the appropriate export.

For example, the human explanation establishes that “Payout group” and “Settlement reference” contain the same shared payout ID. Gemma translates that explanation into this part of the contract:

{
  "payments": { "batch": "Payout group" },
  "bank": { "batch": "Settlement reference" }
}

That is an excerpt, not the whole schema. The complete mapping also identifies unique record IDs, gross, fees, bank amount and currency. It contains no generated code, formulas or fuzzy matching instructions.

Could someone fill in those dropdowns manually? Yes. Doze supports that too. The AI contribution is translating an explanation into a reusable proposal, not making simple arithmetic look intelligent. Its output is useful precisely because it can be inspected, corrected and used without the model.

The first attempts did not produce a usable mapping. Even after the shared reference was explained, Gemma returned a refusal. Those records are kept in the repository.

The successful schema was passed to Ollama's output-format constraint, but its full structure was absent from the model-visible prompt. The only complete JSON example in the prompt was the refusal.

Following Ollama's structured-output guidance, I added the successful schema to the prompt and constrained each role to the actual header names. I kept the refusal branch and runtime checks.

The bounded check used three different inputs, with no retries: the explained studio schema mapped correctly, a different header vocabulary mapped correctly, and explicitly unknown column meanings still refused. The studio call took 4.85 seconds on this machine and its output passed all eight fixture tests.

That supports the combined fix on these cases. It does not establish broad model accuracy or prove which individual change caused the improvement.

A valid JSON shape is not proof of correct meaning. Swapping gross and fees can produce perfectly well-formed nonsense.

Doze's studio workflow therefore carries a mapping and executable witnesses: synthetic CSVs with fixed expected outcomes. Those expectations are authored separately from the proposed mapping. The model cannot mark itself correct.

Imports do not trust an exported “passed” badge. Doze reruns its installed witness suite. A changed mapping, forged result or rewritten bundled expectation cannot make a failing workflow bundle pass. These fixtures apply to the known studio schema, not arbitrary providers; a plain, human-reviewed mapping remains a separate path without that fixture gate.

The reconciliation engine uses decimal arithmetic and exact shared settlement references. Duplicate IDs, ambiguous transfers, unsupported or conflicting currencies, malformed rows and changed schemas become explicit problems, not guesses. A one-cent difference is a mismatch.

A clean result is still not permission to export a packet. The user reviews the report, and approval binds its hash to the exact inputs and mapping. Change an input and the old approval no longer applies. A workflow report also binds the witness-suite identity.

The handoff ZIP contains reconciliation CSV, audit JSON, mapping, source hashes and a read-me, plus the tested workflow when used. Selected source CSV contents are not bundled. The source exports are never edited.

The artifact can run through a standard-library CLI:

python -m doze.close_cli --profile workflow.json --payments payments.csv --bank bank.csv --mapping-approved

The browser, web server and Ollama are unnecessary for that step. Packet creation requires the exact report hash from a previous review and a new output filename.

I checked this in fresh Python processes with socket creation blocked. Twelve checks passed, including clean and bad-week verdicts, approved ZIP creation, rejection of stale approval and preservation of existing output files. This is stronger evidence for offline reuse than a sleeping icon alone.

The open-weight model makes the teaching step local. Header meanings and the user's explanation go to a loopback Ollama service, not a hosted inference endpoint. Doze has no automatic cloud fallback and does not download a model behind the user's back.

The open application code makes the boundary inspectable: what enters inference, what JSON it can return, how money is calculated and what blocks a handoff. Another installed local model can propose the same contract without replacing the execution engine.

Most importantly, the result is not trapped in a chat session. Once a mapping is reviewed, the recurring job needs no inference service at all. A closed API could generate JSON too, but it would require a remote call for teaching. Local open weights let this project avoid that dependency from the start.

There are limits. Matching exports prove agreement between those exports, not completeness: a transaction missing from both is invisible. Doze does not connect to bank accounts, move money, convert currencies, resolve split transfers or give tax advice. There has been no real customer trial, and I have not measured human time saved or energy use.

The practical reduction is specific: stop re-explaining an unchanged export format and stop asking a model to do the same sums. Keep the human review where it matters, on the mapping and the exceptions.

Built with AI assistance through OpenCode, including implementation, verification tooling and drafting this write-up. Ollama provides the local inference runtime; Gemma provides the open-weight model. Chrome automation was used for development verification, not shipped as application code.

Best Use of Gemma: local gemma4:12b-it-qat turns approved column explanations into constrained mappings. Actual model outputs, two earlier refusals and the bounded three-case check are preserved. Its successful mapping is then reused without further inference.

── more in #ai-tools 4 stories · sorted by recency
── more on @doze 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/doze-teach-it-once-l…] indexed:0 read:7min 2026-10-22 · —