跳到正文
DEV Community· Pranesh Nikhar·· 2 小時前AI 評分55

Off-Peak:以等待用戶離開後再執行為特色的 AI 智能體

Off-Peak: the AI agent whose best feature is doing nothing

AI 導讀

Pranesh Nikhar 開發的 Off-Peak 是一款 AI 智能體,會根據筆記本電腦的閒置時間、電量及温度,等用戶離開後才處理文件任務。在十份文件的壓縮日程演示中,Off-Peak 延後執行 21 次,於模擬晚上 10:30 的截止時間前完成全部文件,期間沒有打斷用戶;即時執行基準完成 9/10,耗時 130 秒,Off-Peak 則使用 398 秒模型時間。

正文

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

What I Built

I have a folder of ten documents I keep not summarizing. Meeting notes, an invoice, a sleep study, a paper I saved, receipts. The obvious fix is an agent that runs immediately. That agent is a bad roommate: it spins the fan, drains the battery, and interrupts me to tell me it is working.

Off-Peak is the opposite. You hand it a pile and a deadline. It watches your laptop's idle time, battery, and thermal state, predicts how long the work will take, and waits until you leave. If you come back early, it pauses after the current item. If the lid closes, it resumes from the last finished item. In the morning, it leaves a report.

It is a scheduler, not a chatbot. The model does the work; the scheduler decides whether the work is worth doing now.

Off-Peak dashboard

Demo

The recorded run used ten documents and a compressed day. The human is away at lunch and in the evening. Off-Peak deferred 21 times, worked only in those away windows, and finished all ten before the simulated 10:30 PM deadline.

Baseline: run everything now Off-Peak
Documents finished 9/10 (one provider 503) 10/10
Interruptions 9 0
Energy while present 1.01 Wh 0.00 Wh
Energy while away 0.00 Wh 3.09 Wh
Model time 130 s 398 s

The baseline finished in 130 seconds and lost one document to a provider 503. Off-Peak took 398 seconds of model time across the day. It is slower. That is the point. It moves the work out of the time I am using the laptop. Nine context switches became zero.

The honest caveat: the Gemma 4 31B endpoint returned intermittent 500/503s during the run. Three documents used Gemma; the rest fell back to Gemini 3.8 Flash. The router kept the queue alive, which is what a durable scheduler is for. The repo also has a fully local Ollama path, and the default model is open-weight Gemma 4 31B.

Code

github.com/praneshnikhar/off-peak

The core of the scheduler is small enough to read:

if state.user_present and self.config.only_when_away:
    if self.config.deadline_override and now >= latest_safe:
        return Decision(
            action="run",
            reason="deadline pressure: the latest safe start has arrived",
        )
    return Decision(
        action=wait_action,
        reason="you are at the keyboard; the latest safe start is ...",
    )

The other three gates are battery, thermal, and window length. If they all pass, the worker runs at most two items per tick, writes the output, checkpoints the item, and yields. Pause is checked before every item, so coming back to the desk is a soft stop, not a lost batch.

How I Built It

Stack: Python 3.10, SQLite, psutil, the Gemini API for Gemma 4 31B, Ollama as the local path, a stdlib HTTP dashboard, and 13 tests.

Queue and checkpointing. Every file is an item with a status. The output is written before the item is marked done. On startup, any running job becomes paused; the worker resumes from the first pending item. Sleep, a crash, or a closed lid costs at most the item in flight.

Telemetry. macOS idle time comes from ioreg, battery and thermal state from pmset, CPU and load from psutil. Linux uses xprintidle and /sys. The scheduler only sees a SystemState dataclass, so the same policy runs against real telemetry or the compressed day.

Prediction. The default predictor is a transparent heuristic calibrated on the agent's own runs: items * overhead + chars/1000 * seconds_per_kchar. The repo also includes a TabPFN adapter that trains on the local run log and reports holdout MAE against a median baseline. The local TabPFN weights need a one-time license token; without it, the calibrated heuristic is used. I chose not to fake a learned predictor for the demo.

Model routing. Gemma 4 31B is the default. The router filters thought parts from the response, parses strict JSON, retries rate limits, and falls back to Gemini 3.8 Flash on provider 500/503. A circuit breaker skips the primary for ten minutes after a failure, so the rest of the queue does not pay the timeout again. Every run records the model that actually handled it. The fallback is visible in the report, not hidden.

Dashboard. One HTML file, one JSON endpoint, no build step, no external fonts or scripts. It reads the same SQLite file as the CLI. The timeline is the audit log: green worked, grey waited, amber paused for you.

What broke. The first version ran the whole pile immediately, which is exactly the behavior I was trying to avoid. The second version paused on user presence but did not have a latest-safe-start calculation, so a job could wait forever. The deadline math is what makes laziness safe. The Gemma endpoint was the third surprise: open-weight models are not automatically more reliable than closed ones. The answer is not to hide the failure; it is to route around it and log it.

Why Does Open Innovation Matter?

The decision layer has to be local. A hosted agent cannot see my battery, my thermal state, or the fact that I just walked back to the desk. Off-Peak's scheduler is a small policy over local telemetry. That is the part that makes the project work. The model is replaceable; the context is not.

Patience is only free with open weights. A metered API makes "run 500 small jobs overnight" an economic decision. A downloaded model makes it a scheduling decision. The marginal cost of deferring is zero, so the agent can afford to be picky about when it runs.

The model is swappable. Gemma 4 31B through the Gemini API for throughput, Gemma through Ollama for a fully offline run, or another model behind the same summarize interface. The policy does not change. When the Gemma endpoint failed, the fallback was a configuration, not a rewrite.

My files stay mine. Off-Peak never uploads files. In API mode, the text of a document is sent as a prompt to the model endpoint; the queue, outputs, telemetry, and history stay in SQLite on the laptop. In Ollama mode, nothing leaves the machine.

The failure is inspectable. When the endpoint returned 500/503, the fallback and the checkpoints were visible in the report. I could see which model handled each document. There was no opaque service deciding to drop the queue.

The Lazy Payoff

  • 9 interruptions avoided.
  • 0 minutes watching a progress bar.
  • 3.09 Wh of estimated model energy moved off my active hours.
  • 10/10 documents finished before the deadline.
  • The most productive thing my laptop did this week was while I was not there.

The point is not that the agent is fast. It is that the agent is patient. Slowness is not a bug; it is a scheduling decision.

Limitations

The recorded day is compressed, and the energy numbers are estimates from model time and a fixed laptop wattage. Ten documents is a demo, not a benchmark. The Gemma endpoint was intermittent, so the model mix is reported honestly. Calendar integration is the obvious next step for the idle horizon.

Prize Categories

Best Use of Gemma. Gemma 4 31B is the default model for the work. The recorded run used it for the documents it could, and the local Ollama path uses Gemma for a fully offline run. The scheduler is model-agnostic, but Gemma is what makes the no-API-key path real. I am also entering the overall prize; the TabPFN adapter is in the repo, but I am not claiming that category because I could not run the gated weights in this environment.

What shaped this

Try it

Repo: github.com/praneshnikhar/off-peak

git clone https://github.com/praneshnikhar/off-peak
cd off-peak
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env  # add GEMINI_API_KEY
python -m offpeak demo --backend gemini --model gemma-4-31b-it
python -m offpeak serve --port 8765

Fully local:

ollama serve
ollama pull gemma3:1b
python -m offpeak demo --backend ollama --model gemma3:1b

If the agent is going to do the work, it should at least have the manners to wait until I am gone.

來源:DEV Community · dev.to