-
-
Four agents orchestrated as a graph with Google ADK: Classifier, Filer, Wiki-Synthesizer, Brief-Generator.
-
The organized foundation before the agent: one clear layer per kind of content, one writer per layer, human-only capture, agent-only output.
-
A real, 5am brief: project states, stale items, and the uncomfortable item, the thing my notes keep circling but I've been avoiding.
-
The guardrail in action. A project status change never applies silently, it lands here as a proposal for me to approve myself.
-
Live proof it's running on Google Cloud, not a local demo, request logs from the actual pipeline runs shown in the video.
Inspiration
My notes have always been scattered: tasks in Notion, quick stuff in Apple Notes, a running pile of things I meant to come back to and never did. I was already migrating to Obsidian, and I realized a second brain is only as good as your first one. If you're not organized to begin with, an agent won't fix that, it just builds a prettier prison around the same mess.
So instead of automating the chaos, I built the organization first: one clear layer per kind of content, one writer per layer. Then I built an agent to run it.
What It Does
Second Brain Agent is a multi-agent pipeline that runs my Obsidian vault autonomously, so I never have to touch the upkeep myself.
Four agents, one job each:
- Classifier — reads every new note the moment it lands and decides: task, project update, or knowledge
- Filer — routes tasks straight into
Tasks.md, no approval needed - Wiki-Synthesizer — rewrites the knowledge base as one clean current page, merging into an existing page if the topic's already there instead of appending endlessly
- Brief-Generator — runs nightly, unattended, and writes me a real morning brief: project states, stale items, connections between notes, and the one thing my notes keep circling but I've been avoiding
The guardrail that matters most:
Tasks are low-risk, so they get filed immediately. Project status changes are not. Those are never applied silently, they get written as a proposal to Firestore and surfaced in the next brief for me to approve myself. Nothing rewrites Projects.md without me seeing it first.
How I Built It
Stack:
- Gemini, via Vertex AI
- Google ADK, orchestrating the four agents as a graph
- Cloud Run, hosting the deployed service
- Firestore, for operational state only
- Cloud Storage, backing the vault itself
- Pub/Sub + Cloud Scheduler, driving the two triggers
rclone, bridging the local Obsidian vault to the cloud
The two triggers:
A new note landing in the bucket fires a Cloud Storage → Pub/Sub → Cloud Run event chain, so the real-time pipeline runs without any polling. A separate Cloud Scheduler job drives the nightly brief, independent of the per-note path.
State, kept honest:
Firestore holds only derived, operational data, pending approvals, pipeline run logs, never a duplicate of the actual vault content. The bucket stays the single source of truth. rclone's sync direction is set per path to match a strict single-writer-per-layer rule, so I (the human) and the agent never write to the same file, which quietly solves the sync-conflict problem for free.
Challenges I Ran Into
- The inbox isn't append-only. I edit and clear my capture file the same way I used to with a Notion inbox, so a Pub/Sub-message-ID or GCS-generation-based idempotency check would misfire on every edit. I redesigned it around a content-hash ledger instead, one mechanism that correctly handles new, unchanged, edited, and re-added notes.
- A timezone bug hiding in the math. The server computed staleness in UTC while I'm in Central time, causing an off-by-one on task and project age. It only surfaced once a real threshold got exercised during eval, not in casual testing.
- A real live-data incident, caught the same session. A routine eval run once briefly polluted my actual live
Tasks.md. I fixed it immediately and then isolated eval infrastructure onto its own bucket, so it can't happen again.
Accomplishments That I'm Proud Of
- The full loop is genuinely hands-off, end to end: I write a note into my real vault and, without touching anything else, it gets classified, filed or synthesized, and shows up correctly, or as a pending proposal if it's a project update.
- The nightly brief runs unattended at 5am and has already surfaced a real approval queue from my actual life, not test data.
- This isn't a demo I'll throw away after tonight, it's infrastructure I'm actually using.
What I Learned
The hardest part of an agentic system isn't the LLM calls, it's state management at the edges: idempotency against a file that isn't append-only, timezone correctness across local dev and Cloud Run, and IAM scoped tightly enough that a bug in the code can't cause damage a bug in the prompt already shouldn't allow. The single-writer-per-layer rule turned out to solve two problems at once, drift prevention and sync-conflict avoidance, for free.
What's Next
- Switching from
rcloneto the Remotely Save Obsidian plugin, for in-app sync with no terminal step - Moving connections and cross-linking off the current approach, which leans on stuffing the whole vault into the LLM's context window each run, and onto a graph database (Cloud Spanner's graph support) or a RAG-based retrieval layer, so it keeps working as the vault grows past what fits in context
- Adding the Sunday vault health audit pass, designed for but not yet built
Log in or sign up for Devpost to join the conversation.