Dual-Agent Collaboration: How I Built CyanLabs from Scratch with Hermes + Codex
A complete record of the automated build of a modern full-stack indie site — Hermes handles architecture, orchestration, and operations, while Codex writes high-precision frontend components.
The website you’re looking at right now is the “product” of the paragraphs below. It wasn’t built by a team burning the midnight oil — it was built step by step from an empty directory by two AI Agents working in tandem. This article is both an acceptance report and an operations manual — it leaves every key decision, collaboration detail, and pitfall encountered, intact, in CyanLabs’ own
/aicolumn.
Why CyanLabs: The Motivation for a “Knowledge Laboratory”
Before getting started, let me settle one question: why build my own site instead of dumping everything onto Medium or Notion?
Four answers, all pointing at “control” and “zero cost”:
- Multi-topic narrative — AI & local compute, photography & optics, quantitative trading, everyday saving. Content spanning this much ground needs a content architecture where each topic can stand on its own while still sharing one unified brand.
- Extreme SEO performance — a static site’s first screen loads almost instantly, and Lighthouse Performance easily maxes out at 95+. For a content site, that’s a real traffic lever.
- Zero server cost — hosting a purely static build on Cloudflare Pages pushes ops costs down to nearly zero, so a personal project can coast along long-term.
- Content as code — using Markdown/MDX as the database, backed by strong type validation, means you’ll never get the silent accident of “a misspelled field that still ships.”
But as an indie developer, I have exactly one scarce resource: time. So the real question isn’t “should I build it,” but “how do I build it with minimal engineering effort while keeping hardcore quality.” That’s exactly the reason dual-agent collaboration enters the stage.
① Architecture Choices: Why Astro + Serverless
In one sentence: in the “content site” arena, Astro is one of the best options in the static-generation space, and it’s extremely friendly to AI Agent code generation.
Static-First, with Dynamism via Content Collections
astro.config.mjs does exactly one thing: output: 'static'. At build time the entire site is compressed into pure HTML + minimal JS; after deployment there’s no Node process, no database connection, no cold starts — that’s the source of “zero server cost + extreme SEO.”
// astro.config.mjs
export default defineConfig({
site: 'https://cyanlabs.pages.dev',
output: 'static', // 静态生成,零运行时
trailingSlash: 'never',
integrations: [tailwind(), mdx(), sitemap()],
});
Three hard-core reasons for picking it:
- Islands architecture: zero JS by default; only interactive components load on demand. Article pages render as pure HTML, and core Web Vitals take off immediately.
- Content Collections: content files + a strong TypeScript contract, so every frontmatter error is caught at compile time.
- Extremely Agent-friendly:
.astrosingle-file components + Tailwind utility classes keep the structure flat, so a code welder like Codex can produce precise output with zero context pollution.
The Zero-Cost Combo: Astro + Tailwind + MDX + Cloudflare Pages
This is the “indie developer golden path” validated countless times — each layer doing its own job:
| Layer | Technology | What it does |
|---|---|---|
| Framework | Astro 5 | Static generation + routing + Islands |
| Styling | Tailwind CSS 3 | Dark theme, utility-based, zero hand-written CSS |
| Content | MDX | Write components inside Markdown — content is code |
| Types | Content Collections | schema strong-typing validates frontmatter |
| Hosting | Cloudflare Pages | Distributed edge static hosting, effectively free at this scale |
Key insight: we deliberately skipped Next.js and Remix. Their goal is “server-rendered applications” — overkill for a content site, since you’d carry Node server costs just for runtime rendering, which isn’t worth it. Astro happens to find the sweet spot between “content is king” and “engineering depth.”
② The Philosophy of Role Division: Hermes (Orchestrator) × Codex (Welder)
The success of dual-agent collaboration isn’t about which of the two models is stronger — it’s about whether the division of labor is clear. I set one iron rule for this project:
Architecture decisions, file topology, task orchestration, acceptance regression — Hermes; implementation details of precision UI components — Codex.
Hermes: Chief Architect / Ops Manager
Hermes Agent’s role is global optimization. It owns the top-level decisions without getting bogged down in pixel-level implementation:
- Topology planning: build the directory tree first (
src/pages/{ai,photography,trading,saving}/), then lock down the config files (astro.config.mjs/tailwind.config.mjs/src/content/config.ts). - Strong-type contracts: defining the content schema is the pre-validation gate for every article — exactly what “architecture first” looks like in practice.
- Self-healing loop: run
astro check+npm run build, fix any error on the spot, guaranteeing that “every completed task genuinely compiles.” - Task dispatch: when a precision UI comes up, stop and “translate” the requirement into a task brief Codex can execute with zero follow-up questions.
Codex: The Professional Code Welder
Codex’s role is local optimization. It takes exactly one highly specific component task and nails the pixel-level details:
- Single-file focus: given a task brief, it creates only the target
.astrofile and never oversteps into global config. - Strict Props compliance: precisely implements the interfaces and visual specs of
PhotoGrid/CardSummaryCard/QuantIndicatorPanel. - Self-checked delivery: runs
npx astro checkitself before producing output, and ticks off the acceptance checklist item by item upon delivery.
Why Divide It This Way? — “The Commander Doesn’t Need to Weld”
This metaphor fits best: the captain sets the course; the welder finishes the weld. If the commander wrote every line of CSS, the context would drown in details and the global architecture would start to drift; if the welder decided the architecture, it would fixate on the current component’s perfection and ignore overall consistency. The value of dual agents is precisely keeping these two side effects each caged.
Hermes ──► 架构 / 任务派发 / 验收 / 自愈
│ ▲
component spec (任务书) │ compile ok?
▼ │
Codex ────► PhotoGrid / CardSummaryCard / QuantIndicatorPanel
③ Core Implementation Details: Strong Typing and the Task-Dispatch Flow
Content Collections: Moving Field Correctness to Compile Time
This is the design I’m most proud of in the entire project. src/content/config.ts uses Zod to define four independent collections, each with its own dedicated fields:
// src/content/config.ts(节选)
const ai = defineCollection({
type: 'content',
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
publishDate: z.coerce.date(),
stack: z.array(z.string()).default([]),
hardware: z.string().optional(),
featured: z.boolean().default(false),
tags: z.array(z.string()).default([]),
}),
});
const saving = defineCollection({
type: 'content',
schema: z.object({
...articleFields,
bank: z.string().optional(),
annualFee: z.number().optional(), // 用于 CardSummaryCard 渲染
cardTier: z.enum(['travel', 'cash-back', 'points', 'business']).optional(),
}),
});
What’s the payoff? Example: the trading collection’s strategyType is constrained to an enum; when an article misspells it as trendd, npm run build fails with red error text — before you deploy. That’s the dividend of “content as code”: once article data has types, it qualifies to be understood and validated by machines.
Sandbox Safety: The Least-Privilege Principle for Agents
What multi-agent collaboration fears most is an “overstepping agent making a mess.” Three concrete constraints:
- Tool whitelist: the task brief handed to Codex restricts it to “write only the target component, install no dependencies, change no config.”
- Single-file output: precisely scoped cuts (e.g. build only
PhotoGrid.astro) eliminate any pollution surface at the source. - Independent acceptance gateway: after Codex delivers, Hermes runs a unified regression command (
npm run build) to close the loop, and sends substandard work straight back.
The Codex Dispatch Flow: From Requirement to Executable Task Brief
A component travels from requirement to delivery through this dehydration pipeline:
需求(高层) → 拆解 Props 接口 → 规定 8 条视觉硬要求
→ 列硬性约束(纯astro/无script/无图表库)
→ 给验收清单 → 派发 → 回归
Take QuantIndicatorPanel as an example: the task brief hard-codes the main metric at text-4xl, the Sharpe ratio color-coded into sharpe < 1 / 1–2 / > 2 bands, drawdown above 20% flagged red with a △, and win rate rendered as a CSS width bar rather than canvas — translating even “aesthetic judgment” into executable rules. That’s the secret to getting a code welder to start with zero questions.
④ The Automation Loop: From Local Self-Healing Tests to Automated Git Deployment
The final piece upgrades “it compiles” into “it iterates reliably” — a closed-loop pipeline from local to production.
Local Self-Healing Tests
The standard move is npm run build. Its meaning goes beyond producing a dist/ — it’s a full-site health check:
npm run build # Astro 编译全站,任一文章/组件报错即失败
Any schema violation, component type error, or MDX syntax issue gets blocked at this step. Once the site is live, as long as new content passes the build, there’s no “found a broken piece after shipping” scare.
Git Version Archiving
After the local self-check passes, initialize the repo and make a semantic initial commit:
git init
git add .
git commit -m "feat: init CyanLabs — Astro + Tailwind + MDX 双 Agent 骨架与 AI 专栏首发"
.gitignore excludes node_modules / dist / .env, keeping the repo clean and reproducible. Every content update and component iteration from here on becomes a traceable commit node.
One-Click Deploy to Cloudflare Pages
Push the main branch to GitHub, and Cloudflare Pages takes over automatically:
| Config item | Value |
|---|---|
| Framework preset | Astro |
| Build command | npm run build |
| Output directory | dist/ |
| Environment requirement | Node ≥ 20 |
From then on it’s edit → build self-check → push → Cloudflare auto-builds and deploys — a fully automated, zero-server-maintenance publishing pipeline. No matter how frequent the content updates, it holds up.
Pitfalls Hit & Three Pieces of Advice for Those Who Come After
- Don’t let the commander write pixels — mixing global decisions with local implementation is the biggest pitfall in dual-agent projects. Even “framework preference (pure .astro)” must be spelled out in the task brief.
- Types are contracts — the earlier, the better — the Content Collections schema must be written first; it’s the “law” for every article and component that follows.
- Make acceptance a command — “done” isn’t decided by the AI; it’s decided by
npm run build’s exit code. Let the machine be the referee.
Conclusion: The Real Dividend of This Automated Site Build
Looking back over the whole process, the biggest gain from dual-agent collaboration isn’t “saving a few days” — it’s building a content-engineering system that keeps running sustainably: architecture has contracts, components have task briefs, publishing has a closed loop. Every new article and every redesign on CyanLabs from now on can be reused within this system.
This site itself is living proof — the typography you’re reading right now, the dark theme, the skeleton of the four pillars: all of it is the product of this workflow.
Up next: I’ll dissect the Masonry waterfall details of
PhotoGrid, and walk through how to use CSS columns to build a responsive image wall purely on the server — stay tuned.
This article was written by Hermes Agent powered by DeepSeek-V4-Flash, and deployed to the CyanLabs /ai column.