{"slug": "i-built-an-agent-that-reads-so-i-don-t-have-to", "title": "I Built an Agent That Reads So I Don't Have To", "summary": "A developer built Overnight, a roughly 250-line Python agent that reads files, URLs, or stdin and generates two-sentence briefs per item using a local open-weight GGUF model (Qwen 2.5 1.5B Q4_K_M) via llama-cli or llama-server, writing results to a markdown file. The tool runs nightly on a free Oracle Cloud instance with no vendor SDK and no data sent to an external provider, which the developer says keeps sensitive incident notes and customer tickets on hardware they control.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady](https://dev.to/challenges/hacktoberfest-week3-2026-10-19)*\n\nOvernight is a small agent that reads the things I don't want to read, overnight,\n\nand hands me a two-line brief per item in the morning.\n\nYou point it at a folder, a single file, a URL, or stdin. It reads every item,\n\nruns an open-weight model **on my own machine**, and writes a two-sentence brief\n\nper item to a markdown file. I wake up, open one file, I'm caught up.\n\nThe effort it saves is attention, not time. I read a lot of things that are three\n\nparagraphs of setup for two sentences of content — support tickets, changelogs,\n\nincident reports. Reading them is not hard, it's wasteful. And the timing makes it\n\nworse: nobody wants to open forty tabs at 8am. So the fix isn't a better reader,\n\nit's doing the reading while I'm asleep.\n\nThat's the laziness it rewards. Not \"I don't want to work\" — \"this task should not\n\ncost me attention.\"\n\n```\n# Morning brief — 2026-10-22 20:26 UTC\n\n_2 item(s), local mode, model `qwen2.5-1.5b-instruct-q4_k_m.gguf`_\n\n- **a.txt** — The first sentence is here. The second sentence is beyond it.\n- **b.txt** — The server is up. The team is working.\n```\n\nThree commands cover everything:\n\n```\npython3 overnight.py --watch ~/inbox --out ~/morning-brief.md\npython3 overnight.py --url https://example.com/feed --out brief.md\ncat notes.txt | python3 overnight.py --out brief.md\n```\n\nThat's the whole tool. There is no cleverness in it — it's a watcher, a model, and\n\na file. Which is exactly why it fits \"Slow and Steady\": it runs every night\n\nwithout being asked, and it produces the same correct result every time.\n\n**Live: [https://92.5.41.244:8000/overnight/](https://92.5.41.244:8000/overnight/)** — paste any text, click the button,\n\nget a two-sentence brief from a local model. Nothing is sent to a provider.\n\nVideo: [https://github.com/Reggiebro/overnight/releases/download/v1.0.0/demo.mp4](https://github.com/Reggiebro/overnight/releases/download/v1.0.0/demo.mp4)\n\nIt runs on a free Oracle Cloud instance (8 vCPU / 24 GB) with the model on disk.\n\nIt also proves the thing works on hardware that isn't my laptop — I had to move it\n\nbecause my old machine has a firmware thermal trip and was shutting itself down\n\nunder CPU load. The agent now lives somewhere it can't cook itself.\n\nThe summariser is a GGUF model sitting on disk — currently Qwen 2.5 1.5B Q4_K_M.\n\nThe harness is about 250 lines of Python with no SDK from any vendor. It talks to\n\n`llama-cli` as a subprocess, or to `llama-server` on localhost:8080 when a server\n\nis already running (which keeps the model resident, so a second item costs one\n\nforward pass instead of a full reload).\n\nThat open-source core is not decorative — it's what makes the project work. Here's\n\nthe concrete reason a closed API would have made most of the above impossible:\n\n`--model` at any GGUF and the pipeline is\nunchanged. Better model comes out? Drop it in. Want to fine-tune on your own\nvocabulary? You can, because you own the weights.\nThe last one mattered most to me. A closed model would be a black box that can\n\nchange under me without notice. An open one is a file I can inspect.\n\nBecause the reading stays mine. That's the whole argument.\n\nI read things that are awkward to hand to a third party: incident notes with\n\ninternal hostnames, draft docs that aren't published, tickets with customer names.\n\nWith a closed API I'd be choosing between not using the tool and shipping that\n\ntext somewhere I don't control. With an open model on disk, there's no third party\n\nto leak to, no account to suspend, and no provider that can change its terms\n\nunder me. The tool works on a plane. It costs nothing when I run it every night.\n\nAnd if the model gets worse, I swap it — I'm not waiting for someone else's\n\nroadmap.\n\nOpen innovation made the project possible. Closed would have made it a trade-off.\n\nThe tool is honest about what it did, and that took more care than the summary\n\nlogic.\n\nThe output header says `local mode` only when the model actually produced the\n\nbrief. If no model is found, it says `extractive mode` and takes the first and\n\nlast sentence — a real compression, not an invention. And if there's genuinely\n\nnothing to summarise, it exits non-zero rather than quietly writing an empty file.\n\nThere's a subtler version of the same problem. `find_model()` searches a few\n\ndirectories and picks the smallest usable GGUF. If it just returned the first file\n\nit found, a truncated download would be reported as \"local mode\" while every call\n\nsilently fell back to the extractive path — the header would be lying. So it probes\n\neach candidate with a real load before returning it, and rejects anything that\n\ndoesn't actually load. The header can be trusted because the check is real.\n\nA failed URL fetch is handled the same way: it becomes an item that literally says\n\n`[fetch failed: HTTP 502]`, so a Cloudflare error page never gets summarised into\n\nfake news in your morning brief.\n\nThat's the standard I held it to: the tool never claims more than it did.\n\nI'd rather say this than have you find out:\n\nI'm listing these because the interesting part of the project isn't that it\n\nsummarises text — that's easy. The interesting part is that it does so with\n\nnothing leaving the machine, no account, and no bill, and still tells you\n\ntruthfully what happened.\n\n```\ngit clone https://github.com/Reggiebro/overnight\ncd overnight\npython3 -m pytest tests/ -q\npython3 overnight.py --watch ~/inbox --out ~/morning-brief.md\n```\n\nThe tests are worth a look — they cover the claims I made above rather than just\n\nthat functions return things. A corrupt GGUF is rejected, a failed fetch becomes\n\nan explicit error, the extractive fallback is deterministic, and the header never\n\nclaims `local mode` without a model. CI runs the same suite on Python 3.10–3.12\n\nand is green.\n\nThere's a `--json` flag if you want to pipe it into something else, and a cron\n\nline in the README if you want it to actually run while you sleep.\n\nI'm entering for **Best Use of GitHub Copilot** — the workflow automation here is\n\nGitHub Actions: the test suite runs automatically on every push across three\n\nPython versions, plus a no-model smoke test that verifies the tool still produces\n\na correct extractive brief with no model present. That's the category where this\n\nproject's automation actually shows.\n\nI've been running it nightly. The payoff is small and specific: I stopped opening\n\nthe inbox folder in the morning. I open one file instead of forty tabs, and the\n\nday starts without the \"read forty things before coffee\" tax.\n\nThe tool has no cleverness in it. It's a watcher, a model, and a file. It works\n\nbecause it runs every night without being asked and produces the same correct\n\nresult. Slow and steady: run it every night, trust it in the morning.", "url": "https://wpnews.pro/news/i-built-an-agent-that-reads-so-i-don-t-have-to", "canonical_source": "https://dev.to/reggiebro/i-built-an-agent-that-reads-so-i-dont-have-to-346a", "published_at": "2026-10-22 23:23:45+00:00", "updated_at": "2026-10-22 23:41:23.603755+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "large-language-models", "ai-infrastructure", "developer-tools"], "entities": ["Overnight", "Qwen 2.5 1.5B", "Oracle Cloud", "llama-cli", "llama-server", "Hacktoberfest"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/i-built-an-agent-that-reads-so-i-don-t-have-to", "markdown": "https://wpnews.pro/news/i-built-an-agent-that-reads-so-i-don-t-have-to.md", "text": "https://wpnews.pro/news/i-built-an-agent-that-reads-so-i-don-t-have-to.txt", "jsonld": "https://wpnews.pro/news/i-built-an-agent-that-reads-so-i-don-t-have-to.jsonld"}}