跳到正文
DEV Community· Ernest Achuka $DROPEE·· 4 小時前AI 評分55

Sloth Sort 以本機開源權重模型整理檔案

Sloth Sort 🠥 — a local, open-weight file tidier that works while you nap

AI 導讀

Ernest Achuka 製作 Sloth Sort,一個透過 Ollama 在本機執行 397 MB Qwen2.5-0.5B-Instruct、用來整理檔案的 Agent。

正文

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

What I Built

Don't sort your Downloads folder. Outsource it to a sloth.

My todo list.txt had carried the item "sort downloads folder (lol, never)" for years. So I hired an animal with the correct energy policy: a local file-tidying agent whose brain is a 397 MB open-weight model (Qwen2.5-0.5B-Instruct, Apache-2.0) served by Ollama (MIT) on my own machine.

Point it at any messy folder. It reads each file — real text extraction for PDFs, DOCX, notes, CSVs, JSON; filename-only for photos and media — asks the local model where it belongs and what a decent name would be, then files everything into tidy per-topic folders. In small shifts: eight files, a siesta, eight more.

The sloth design spec:

  • --pace 8 — never moves more than N files per shift
  • --siesta 2 — rests between files, like a professional
  • watch --nap 6h, or cron — short night shifts, forever
  • Archive, never delete — junk is parked in _archive/; deleting is a decision the sloth refuses to make
  • Dry-run first, undo always — every run writes a manifest and can be fully reversed

Who gets to be lazier? Me, obviously — but also anyone whose "Downloads" is a geological record, searching receipts with Spotlight at 11pm. The whole thing costs them nothing, needs no internet, and never sends a single byte of their files anywhere.

Demo

This is a real, unedited run. I built a synthetic-but-painfully-true mess (make_mess.py, included): 24 files with real PDFs, a real DOCX, camera imports, temp junk — and, deliberately, two files you'd never want to leave your machine.

Before:

1password-backup-2019.txt     invoice_INV-2204_final_FINAL (2).pdf
IMG_8102.PNG                  jollof recipe grandma.md
PXL_20250914_181204.jpg       letter to landlord.docx
Screenshot 2025-10-02 .png    rental_agreement_signed.pdf
api_keys_OLD_ROTATED.json     tax_summary_2024.pdf
bank_statement_sept.csv       tmp9F31.tmp
data_pipeline.py              tsdump_error_20251009.log …(24 total)

Dry-run (it's the default verb — you review the plan before anything moves):

$ python3 sloth_sort.py scan demos/messy_downloads --pace 24
  1password-backup-2019.txt        → _archive/1password-backup-2019.txt
      ↳ stale password export, archive safely
  api_keys_OLD_ROTATED.json        → _archive/api-keys-old-rotated.json
      ↳ old credentials export, archive safely
  rental_agreement_signed.pdf      → Documents/rental-agreement-signed.pdf
      ↳ rental agreement signed, not finance
  tax_summary_2024.pdf             → Finance/tax-summary-2024.pdf
      ↳ personal income tax summary
  tmp9F31.tmp                      → _archive/tmp9f31.tmp
      ↳ temporary file
  … (24/24 classified by the model, zero fallbacks)

After apply:

Finance/{ark-cafe-october-receipt, bank-statement-sept, invoice-…, tax-summary-2024}
Documents/{letter-to-landlord.docx, rental-agreement-signed.pdf, jollof-recipe-grandma.md}
Notes/{lecture-w4-notes, meeting-notes-monday, todo-list …}
Photos/{img-8102.png, pxl-….jpg, cat-meme-final.jpg}
Screenshots/  Software/  Code/
_archive/{1password-backup-2019.txt, api-keys-old-rotated.json, tmp9f31.tmp}

Undo, verified live: 🦥 Undid run 20261025-031510: 23 file(s) back home. — then I re-applied. Full, unedited logs are in the repo under demo/*.log.

🎧 There's also a 75-second narrated walkthrough (audio/sloth-sort-audio.mp3 in the repo).

Code

The whole harness is one dependency-light file (sloth_sort.py, ~450 LOC; the only optional pip import is pypdf for PDF text). Two excerpts give the flavor.

The brain conversation — a 0.5B model is very coachable if you give it a closed category list, strict JSON, and few-shot examples:

PROMPT = """You are a meticulous file organizer. Classify ONE file ...
Categories: Finance, Documents, Notes, Photos, Screenshots, Code, Media, Software, Personal, Junk
Rules: answer with ONLY a single JSON object; filename keeps the original extension;
"archive": true only for junk; if content mentions passwords/credentials/API keys,
the category MUST be Personal; leases and landlord letters are Documents, never Finance.

Example:
file: tenancy_agreement_v2.pdf | content: "RESIDENTIAL TENANCY AGREEMENT ... Monthly rent: 200,000"
{"category":"Documents","filename":"tenancy-agreement.pdf","archive":false,"reason":"lease, not finance"}

Now classify this file.
file: {name} | type: {kind} | size: {size} | content: "{snippet}"
JSON answer:"""

The responsible-part-of-the-sloth loop — tolerant parsing, closed-list guard rails, manifest logging, no overwrites:

data = parse_answer(raw) or fallback(path, size)          # never crash mid-pile
if data["category"] not in CATEGORIES:                    # closed list, enforced
    data["category"] = fallback(path, size)["category"]
folder = "_archive" if data["archive"] else data["category"]
dst = unique_path(root / folder / slug(data["filename"])) # never overwrite
shutil.move(src, dst)                                     # only after dry-run approval
log_manifest_line(run_id, src, dst)                       # makes `undo` possible

Everything else — mess generator, README, logs, hero image, narration — is in the repo:

sloth-sort/  sloth_sort.py  make_mess.py  README.md  demo/*.log  images/  audio/

How I Built It

The stack is open all the way down, and the openness is load-bearing:

Layer Thing License Role
Brain qwen2.5:0.5b-instruct, Q4_K_M, 397 MB Apache-2.0 reads each file, returns strict JSON: category, clean filename, archive?, reason
Server Ollama 0.40 on 127.0.0.1:11434 MIT serves the GGUF via /api/generate; CPU-only, no GPU needed
Harness sloth_sort.py, stdlib HTTP client MIT agent loop: sniff → classify → (dry-run

Built and demoed entirely inside a 2-core cloud sandbox — deliberately weak hardware, because sloths don't need a gym:

  • ~2.5–3 s per file end-to-end (~35 tokens/s generation on two threads)
  • 24-file folder sorted in ~65 s, including model load
  • RAM stayed flat and boring; the model weighs less than the PDFs of some people's tax years

Honest process notes (this took three prompt iterations, and it shows why local iteration matters): pass 1 leaked a legacy password export into "Documents"; fixed with a hard rule — anything mentioning credentials is Personal. Pass 2 filed a signed lease under "Finance" and a crash log under "Finance"; fixed with category-clarifying rules and two more few-shots. Final pass: 20/24 placements exactly right, 4 defensible-but-imperfect (a .mp4 screen recording landed in Notes; grandma's jollof recipe reasoned "recipe notes" then chose Documents). For a 0.5B model, I'll take it — and when I want better, --model qwen2.5:1.5b-instruct is a one-flag upgrade; the harness doesn't change.

Why Does Open Innovation Matter?

Not decoratively. For this specific tool, the closed option simply fails the requirements list:

1. My Downloads folder contains a tax summary, a signed lease, and a password export.
A cloud classifier means uploading exactly those to a server I don't control. With local inference, privacy isn't a settings toggle on a privacy policy — it's physics. Nothing leaves the machine because nothing can.

2. It works with the internet off, forever.
After a one-time 397 MB pull, sorting runs on a plane, in a blackout, in a village with carrier pigeons. File hygiene shouldn't have network availability as a dependency.

3. Marginal cost is a solid, constitutional $0.00.
A pass over 24 files is ~25k input tokens. Pennies per run on even a cheap API — but forever, nightly, on every machine in the house. Local costs nothing at any scale and burns watts you already own.

4. The brain is swappable and tunable — no product roadmap required.
My remaining misfires have two open fixes: bump model size with one flag, or fine-tune the small weights on my definition of tidy. Closed APIs deprecate models mid-project and cap customization behind enterprise tiers; Apache-2.0 weights just sit on disk being mine.

5. Auditable behavior on sensitive files.
The full decision loop — prompt, categories, fallback rules — is ~450 lines that fit in a head. When software is moving a file named 1password-backup-2019.txt, "trust us" is not an architecture.

Where open beat closed during the build, concretely: I re-ran the full 24-file classification about six times while tuning the prompt — zero financial friction, no tokens-in-a-tab counting. temperature=0 plus pinned local weights gave me bit-for-bit reproducible plans, which a silently-updated hosted model cannot. And "does it run on my potato?" stopped being a billing question and became a laptop question.

My Agent Session

This project was built in a single AI-agent session (Arena Agent Mode): environment probes → direct-download Ollama binary → pull model → three prompt iterations, with every intermediate misclassification and the exact fixes preserved in demo/*.log. The session included a real failure recovery you can audit: when the model server briefly raced its own model load, the harness cleanly took the extension-rule fallback path — no crash, no wrong move; that fallback exists because sloths should never panic.

The Lazy Payoff

Manual triage of a folder like this is ~15–25 minutes of pure tedium: open, skim, decide, drag, rename, second-guess. The sloth does it in ~65 seconds of not my time — and in watch/cron mode, it does it at 2am every night, forever, for free:

0 2 * * *  cd ~/sloth-sort && python3 sloth_sort.py apply "$HOME/Downloads" --pace 8 --siesta 2 >> "$HOME/.slothsort-nightly.log" 2>&1

This week it sorted the 24-file demo mess end-to-end multiple times, survived a full undo/re-apply cycle, and did its final run on hardware slower than my phone. Tonight it's going on cron for the real Downloads folder — which means the todo list.txt item "sort downloads folder (lol, never)" can finally be deleted. By me. Some work a sloth shouldn't do.

Prize Categories

  • Best Use of Entire — the full build session is shared in My Agent Session above via DevRelay.

<!-- Thanks for participating! -->This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady

What I Built

Demo

Code

How I Built It

Why Does Open Innovation Matter?

My Agent Session

The Lazy Payoff

Prize Categories

來源:DEV Community · dev.to