A lone radio telescope dish under the Milky Way on a dark plain, one small amber light at its base

A Nightly AI Research Radar: Free Sources, Local-Model Triage, and Claude Batch Deep Dives for a Self-Hosted Agent

A nightly script collects AI leads from free sources, a local model scores them, Claude writes five deep-dive notes in one half-price Message Batch, and the agent's morning brief carries a verdict and a link for each. Sources, setup choices, code and the first test run.

My home AI agent runs Hermes Agent (open source) on a small Linux server and sends me a morning brief on Signal at 06:30. That brief now ends with an "AI radar" section: a handful of new tools, models and papers, each with a verdict (try now, watch, or skip), one line on why, and a link. The full research notes land as Markdown files I can read later or ask the agent about.

Each night a script collects leads from free sources: GitHub, Hacker News, Hugging Face, Bluesky, blogs and Reddit. A local model on my GPU box scores them against a short interests file. The top five, plus up to two topics I asked for, go to Claude as one Message Batch with the research material already gathered. Claude writes the notes, and the local model turns them into the brief section.

I built it for two reasons. Tools like herdr and Honcho reached me through people I follow, and I wanted to see them sooner. I also want to keep up with model tech (JEPA and world models, small classification and embedding models, speculative decoding) and with how people are using it.

The split of work drives the design. Discovery and sorting are high volume and low judgment, so scripts and a local model do them for free. The deep dive is where judgment matters (is this new, does it work, does it fit my setup), so a frontier model gets a short list with the material in hand. The morning summary is a rewrite of good notes, which a local model handles well.

This is day one. What I have verified: one full run (214 leads collected and scored in 3 minutes, 2 deep dives back in about 15 minutes, $0.05 total) and one run through the scheduler.

What you need

Setup choices and why

1. Sources and the collector

Each source is a small function that turns API results or feed entries into leads (title, URL, a snippet and a signal such as points, stars or who shared it).

Tip: unauthenticated GitHub allows 60 core requests an hour and 10 searches a minute. About 30 star lookups plus about 10 README fetches fit, and the script sleeps 7 seconds between searches.

All leads go into one SQLite file. URLs normalize to a key, so the same thing merges no matter where it was seen:

def key_for(url):
    m = re.match(r"https?://(?:www\.)?github\.com/([\w.-]+)/([\w.-]+)", url)
    if m:
        return f"gh:{m.group(1).lower()}/{m.group(2).lower()}"
    m = re.search(r"(?:arxiv\.org/(?:abs|pdf)/|huggingface\.co/papers/)(\d{4}\.\d{4,5})", url)
    if m:
        return "paper:" + m.group(1)
    # Hugging Face models become hf:<org>/<name>; everything else is host + path without utm params
    ...

A lead that was already covered is skipped. A lead that was scored before is scored again when a new source or a new person picks it up. The first night brought in 214 leads: 51 from Hacker News, 40 new GitHub repos, 32 papers, 27 trending models, 22 from r/LocalLLaMA and the rest spread across blogs and Bluesky.

2. Triage on the local model

The script sends 25 leads per call to the local model, with the interests file and a scoring scale, and asks for JSON back:

{"scores": [{"i": 0, "score": 8, "why": "speculative decoding release with code, fits local inference"}]}

The interests file has two short lists. High interest: new agent tooling, local inference, model tech, Claude API features that cut cost, and practical write-ups. Low interest: funding news, opinion pieces with no code or results, and enterprise marketing.

On the first run the local model gave out a lot of 9s. One quota line in the prompt fixed it: "in a typical list of 25, at most two leads deserve 9 or 10 and most score 5 or lower; a lead that merely matches a keyword of the interests is a 6." After that: 6 leads at 9, 20 at 8, and most at 5 or below. Ties go to leads seen in more places.

The top five by score go to the deep dive, with at most two from one source, so one busy feed cannot fill the night. Up to two topics I asked for are added on top. Tip: if the GPU box is down, the script falls back to Claude Haiku 5.5 for triage, which costs a fraction of a cent.

A braided river delta seen from above at dawn, many channels narrowing into one bright channel
Many leads in, a few out. Illustration.

3. Gather the material and submit the batch

For each lead the script collects what Claude will need:

That is capped at 60,000 characters per lead. The system prompt asks for a fixed note format: a TL;DR line, a Verdict line (try now, watch or skip, with a reason), then What it is, Why it matters to you, How people are using it, Maturity and caveats, Try it, and Sources. Claims are labeled [verified] when the material shows it, [reported] when someone claims it, and [unverified] for inference. The prompt also says the gathered web content is untrusted data, not instructions.

The batch is one call:

client.messages.batches.create(requests=[{
    "custom_id": cid,
    "params": {
        "model": "claude-sonnet-5-5",
        "max_tokens": 8000,
        "system": [{"type": "text", "text": SYSTEM, "cache_control": {"type": "ephemeral"}}],
        "messages": [{"role": "user", "content": context}],
        "thinking": {"type": "adaptive"},
        "output_config": {"effort": "medium"},
    },
} for cid, context in leads])

Each lead is one request under one batch id. The shared system prompt carries cache_control. Effort medium with adaptive thinking fits the job: the material is already in the prompt, and the work is reading it carefully, not searching.

Two extras, one line each. The server-side refusal fallback is not accepted on the Batches API, so a note that comes back with stop_reason "refusal" is queued for the next night on Claude Opus 5.5 instead. On Sundays the batch adds one Opus 5.5 request that reads the week's notes and top leads and writes a trends report; its first six lines go into the Sunday brief.

Before it submits, the script checks a monthly cap ($20). Spend is computed from the token counts in each result and stored in the same SQLite file.

An observatory dome on a mountain ridge with its slit open at first light, the telescope pointing at a pale sky
Five leads a night get a close look. Illustration.

4. Pull results, write notes, write the brief section

At 06:00 the second job polls client.messages.batches.retrieve(batch_id).processing_status until it reads "ended", then reads client.messages.batches.results(batch_id). Results can arrive in any order, so they are matched by custom_id. It checks stop_reason before reading the text and writes each note to ~/AI/research/radar/<date>/<name>.md.

The local model then writes the brief section from each note's TL;DR and Verdict lines, three lines per item:

<short name> (<verdict>): <what it is, under 15 words>
<why it matters or the main caveat, under 25 words>
<link>

Below that comes "Also on the radar": the next five high-scoring leads with links and no deep dive. The section ends with the month's Claude spend and any errors from the night. If the GPU box is down at 06:00, a plain list built from the same lines goes out instead.

5. Keys through Proton Pass CLI

The script needs two keys: the Anthropic key and the key for the local LLM endpoint. Hermes scrubs secrets from the environment of cron scripts, so the script starts by re-running itself under pass-cli run, with the keys given as references:

env = dict(os.environ,
           ANTHROPIC_API_KEY="pass://Agent/ANTHROPIC_API_KEY/password",
           LOCAL_LLM_API_KEY="pass://Agent/LOCAL_LLM_API_KEY/password",
           AI_RADAR_IN_PASS="1")
os.execve(PASS_CLI, [PASS_CLI, "run", "--", sys.executable, __file__] + sys.argv[1:], env)

pass-cli run replaces the references with the real values for that one process tree, and the agent's own environment does not hold them. The vault, the scoped agent token and the audit log are covered in the Proton Pass keystore post. Without Proton Pass, any secret manager with a "run this command with these variables" mode works the same way.

6. Two cron jobs and a skill

Hermes cron jobs do not take arguments, so two three-line wrapper scripts set the subcommand and run the main script. --no-agent runs a script on schedule and delivers its output with no model involved, and empty output means no message:

hermes cron create "30 1 * * *" --no-agent --script ai-radar-collect.py \
  --interpreter ~/.hermes/venvs/ai-radar/bin/python \
  --deliver signal --failure-deliver local --name ai-radar-collect

hermes cron create "0 6 * * *" --no-agent --script ai-radar-brief.py \
  --interpreter ~/.hermes/venvs/ai-radar/bin/python \
  --deliver signal --name ai-radar-brief

--interpreter points at the venv with the Anthropic SDK. Tip: --failure-deliver local keeps a failure at 1:30 am from buzzing the phone; the morning section lists the errors instead. The existing 06:30 brief script reads the section file for the day and appends it when it exists.

A small Hermes skill lets me steer the radar from chat:

Test it

  1. Run collect by hand once and look at the counts per source and the spread of scores. Mine took 3 minutes for 214 leads, fetches included.
  2. Check the batch with processing_status a few times. Mine ended in about 15 minutes with 2 deep dives.
  3. Run brief by hand and read the section file.
  4. Trigger the brief job once through the scheduler (hermes cron run <id>) to confirm the keys arrive inside the scheduler's environment.
  5. Let one night run on its own and check the morning brief.
A folded sheet of paper and a cup of coffee on a wooden table by a window at first light
The short list arrives with the morning brief. Illustration.

Sample output

The brief section from the test run:

AI radar
EAGLE-4 (watch): Speculative decoding drafter and runtime that sizes draft trees from measured costs.
Strong fit for Qwen 27B, but benchmarks use B300s and license has commercial limits.
https://github.com/eagle-hq/EAGLE-4

Kitten-TTS 2 (watch): Low-bit speech model with voice cloning, multilingual support, and emotion control.
Promising for local TTS on existing hardware, but verify the license and actual speed first.
https://www.reddit.com/r/LocalLLaMA/comments/1xc48or/kittentts_2_new_opensource_sota_158_bit_tts/

Also on the radar:
- NousResearch/hermes-official-plugins
- WorldTUI: I trained a 360M model to pretend to be a terminal
- deepseek-ai/DeepSeek-V4.1-Flash-1017

The EAGLE-4 note opens with: "a speculative-decoding drafter plus a runtime that sizes the draft tree from measured costs, with lossless output and a claimed 3.4x single-request speedup on Qwen3.8-27B." It then lists what it found in the material: the benchmarks are from B300 GPUs, the quick start covers B300 and CUDA 13, the license is source-available with a revenue cap, the gain drops with concurrency (3.42x at 1 request, 2.09x at 8, 1.39x at 64), and a vendor article puts the gain over Qwen's built-in multi-token prediction at about 1.6x. Verdict: watch until someone posts consumer GPU numbers. That is the read I want: specific, sourced, and willing to say "not yet".

Cost

What's next

A week of real use will show whether five a night is the right number and whether the triage follows my taste. The "more like" and "less of" messages are how it should improve. If you know a free source or a person worth following for this, tell me in the comments.

Fringe Tech Self-Hosting AI

Comments

// Comments are reviewed before appearing. No spam. No noise.