The annoying part of running a blog isn't writing — it's picking topics. What do I write today? It's guesswork, and it eats time.
So I gave it an agent: every Monday it reads my published posts (to avoid repeats) and proposes 5 new topics. It runs on GitHub Actions — free, scheduled, no server.
And the essence never changes: an agent is a loop — call the API to ask the model → run the tool → feed the result back.
What it actually does
One loop, one tool:
TOOLS = [{ "type": "function", "function": { "name": "list_existing_titles", "description": "read existing post titles to avoid duplicates", "parameters": {"type": "object", "properties": {}}, }, }] def run_tool(name, args): if name == "list_existing_titles": xml = urlopen(FEED).read().decode("utf-8", "ignore") # read the site RSS titles = re.findall(r"<title>(.*?)</title>", xml, re.S) return "\n".join(titles[1:]) Model is DeepSeek (OpenAI-compatible). The loop:
def agent(user_input): messages = [{"role": "user", "content": user_input}] while True: msg = client.chat.completions.create(model=MODEL, messages=messages, tools=TOOLS).choices[0].message messages.append(msg) if not msg.tool_calls: return msg.content # no more tools -> answer for call in msg.tool_calls: # run the tool -> feed it back args = json.loads(call.function.arguments or "{}") messages.append({"role": "tool", "tool_call_id": call.id, "content": run_tool(call.function.name, args)}) Output goes to weekly/<date>.md. That's it — no framework, just a loop.
Why GitHub Actions
To me this is the neat part:
- Free: public repo, unlimited minutes;
- Scheduled:
on: schedule: cron, runs weekly; - No server: nothing to host;
- Internet access: runners are in the US, reaching DeepSeek / Google APIs directly;
- Repo access: it can commit the result back.
on: schedule: [{ cron: '0 1 * * 1' }] workflow_dispatch: {} permissions: { contents: write } # ... pip install -> python agent/topics.py -> git commit weekly/ Secrets go in Secrets (DEEPSEEK_API_KEY); site config in Variables (SITE_FEED/SITE_BRIEF) — never in code.
It actually delivered
On its first run it read 17 published posts and replied (excerpt):
The existing content is mostly low-level build / framework internals, but "running an agent on an edge device" is almost a blank spot. The 5 topics below all land in that gap…
Then it gave 5 topics plus a scheduling plan (lead-gen → conversion → moat). Honestly, better than my gut.
My principle: the agent does the dirty work, the human judges
That's also why I kept it small and open-sourced only the framework:
- Agent does the dirty / half-done work: topics, drafts, retros, distribution copy;
- Human judges and publishes: which one, how to write, whether to ship;
- Open architecture, private data: the framework is open; but data, keys, keyword lists, business strategy stay private.
For instance, my real prompt, keyword list, and GSC data live in a private repo — what's public is only how it's built.
Next
- Add a tool to read a GSC export (topics with impressions but no clicks first);
- Add a "draft agent" (first-pass drafts);
- But publishing always stays with the human.
📦 Reusable template (private bits removed): github.com/zishuowang696/weekly-topics-agent
Questions or feedback? Open an issue on the repo above.
