For Multi-Projects Portfolio
Audience: Solo developer / handoff to implementation AI models Infrastructure baseline: 1× VPS running 9router (LLM model routing) + Hermes Agent Goal: Build a fleet of cooperating AI agents that handle software development, data acquisition, customer-data acquisition, content generation, and multi-channel distribution (social + Search Console / Bing) — with humans only in the approval loop.
Table of Contents
- Architecture Overview
- Foundation Layer — 9router + Hermes + Orchestration
- Agent Domain 1 — Software Development Lifecycle
- Agent Domain 2 — Market/Operational Data Acquisition
- Agent Domain 3 — Customer Data Acquisition
- Agent Domain 4 — Content Generation Engine
- Agent Domain 5 — Distribution: Social + Search Console + Bing
- Data Layer & Storage Design
- Safety, Guardrails & Human-in-the-Loop
- Deployment & Ops on Your VPS
- Build Roadmap (Phased)
- Per-Property Application Notes
1. Architecture Overview
You are building a multi-agent system where a central orchestrator delegates tasks to specialized agents. Each agent has: a model (routed via 9router), a toolset, a memory store, and a guardrail boundary.
┌─────────────────────────────┐
│ ORCHESTRATOR │
│ (Hermes Agent core loop) │
│ - task queue │
│ - routing decisions │
│ - human approval gates │
└──────────────┬──────────────┘
│
┌───────────────┬───────────────┼───────────────┬────────────────┐
▼ ▼ ▼ ▼ ▼
┌──────────┐ ┌────────────┐ ┌──────────┐ ┌────────────┐ ┌────────────┐
│ DevOps │ │ Data │ │ Customer │ │ Content │ │Distribution│
│ Agent │ │ Scraper │ │ Data │ │ Generator │ │ Agent │
│ cluster │ │ Agent │ │ Agent │ │ Agent │ │ │
└────┬─────┘ └─────┬──────┘ └────┬─────┘ └─────┬──────┘ └─────┬──────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ SHARED SERVICES: 9router (LLM gateway) │ PostgreSQL │ Redis (queue) │ │
│ Object storage (raw scrape) │ Vector DB (embeddings) │ Secrets vault │
└─────────────────────────────────────────────────────────────────────────┘
Core principles
- One orchestrator, many workers. Hermes Agent runs the control loop. Sub-agents are stateless workers that receive a task spec, do one job, return structured output.
- Model routing through 9router. Never hardcode a model. Every agent declares a capability tier (e.g.
reasoning-heavy,cheap-bulk,code,vision) and 9router resolves it to an actual model. This lets you swap/upgrade models without touching agent code. - Everything is a typed task. Tasks are JSON records in a queue with
type,payload,priority,status,approval_required. Agents pull, process, write back. - Human gates on irreversible/public actions. Code merges to
main, production deploys, public posts, and outbound customer messages require explicit approval. - Idempotent + resumable. Every agent step writes state so a crash doesn’t lose work or double-post.
Capability tiers (define these in 9router)
| Tier | Use case | Characteristics |
|---|---|---|
reasoning-heavy | Architecture, code review, planning, clustering analysis | Largest model, slow, expensive — use sparingly |
code | Code generation, bug fixing, test writing | Strong coding model |
cheap-bulk | Summarization, classification, tagging thousands of scraped rows | Small/fast/cheap model |
creative | Content drafts, social copy, headlines | Mid model tuned for writing |
vision | Screenshot QA, OCR on scraped images, captcha-adjacent layout reading (not solving) | Multimodal model |
embed | Vectorization for dedupe + semantic search | Embedding model |
2. Foundation Layer
2.1 9router configuration
9router sits between every agent and every model. Treat it as your LLM gateway. Set it up to provide:
- Logical model names → physical routes. Define routes for each capability tier above.
- Fallback chains. If primary model errors/times out → fall to secondary. Critical for unattended overnight runs.
- Per-agent budgets. Rate + spend caps per agent so a runaway loop can’t drain your account or hammer one provider.
- Request logging. Every prompt/response logged with
agent_id,task_id, token counts. This is your audit trail and your cost dashboard.
Recommended router config shape (pseudo-config — adapt to 9router’s actual format):
routes:
reasoning-heavy:
primary: provider_a/large-model
fallback: provider_b/large-model
max_tokens: 8000
budget_usd_per_day: 15
code:
primary: provider_a/code-model
fallback: provider_b/code-model
cheap-bulk:
primary: provider_c/small-model
fallback: provider_a/small-model
budget_usd_per_day: 10
creative:
primary: provider_a/mid-model
embed:
primary: provider_c/embed-model
agents:
devops-agent: { allowed_tiers: [reasoning-heavy, code], rpm: 30 }
scraper-agent: { allowed_tiers: [cheap-bulk, vision], rpm: 120 }
customer-agent: { allowed_tiers: [cheap-bulk], rpm: 60 }
content-agent: { allowed_tiers: [creative, cheap-bulk], rpm: 40 }
distribution-agent:{ allowed_tiers: [creative], rpm: 20 }
2.2 Hermes Agent as orchestrator
Hermes runs the control loop. Its responsibilities:
- Poll the task queue (Redis list or a Postgres
taskstable). - Decide the agent + tier for each task (rule-based first; LLM-based routing only for ambiguous tasks).
- Dispatch to the sub-agent (in-process function call, subprocess, or HTTP to a local worker).
- Collect result, validate against expected schema, write back to DB.
- Enforce approval gates — if
approval_required, move task topending_approvaland notify you (Telegram/email/Slack webhook) instead of executing. - Retry with backoff on transient failures; dead-letter after N attempts.
Core loop (language-agnostic pseudocode):
while True:
task = queue.claim_next() # atomic claim, sets status=running
if task is None:
sleep(POLL_INTERVAL); continue
agent = router_decide_agent(task) # rule table → agent name
tier = agent.tier_for(task)
try:
result = agent.run(task, model_tier=tier) # calls 9router internally
validate(result, task.expected_schema)
if task.approval_required:
task.store_proposed_output(result)
task.set_status("pending_approval")
notify_human(task) # do NOT execute side effects yet
else:
apply_side_effects(task, result)
task.set_status("done")
except TransientError as e:
task.bump_attempts()
task.set_status("queued" if task.attempts < MAX else "dead_letter")
except FatalError as e:
task.set_status("dead_letter"); notify_human(task, error=e)
2.3 Shared services to stand up first
| Service | Purpose | Suggested tech |
|---|---|---|
| Task queue | Inter-agent work distribution | Redis (lists/streams) or Postgres SELECT … FOR UPDATE SKIP LOCKED |
| Primary DB | Structured state, tasks, customer data, content | PostgreSQL |
| Object storage | Raw HTML / scrape dumps / media | Local MinIO or VPS disk + lifecycle cleanup |
| Vector DB | Dedup + semantic clustering of scraped/content data | pgvector extension (keeps it in Postgres — simplest on one VPS) |
| Secrets | API keys, OAuth tokens, DB creds | .env + sops/age, or HashiCorp Vault if you scale |
| Notifier | Human approval pings | Telegram bot or Slack incoming webhook |
On a single VPS, pgvector inside your existing Postgres avoids running a separate vector DB. Keep it lean.
3. Agent Domain 1 — Software Development Lifecycle
This domain automates: planning → coding → code review → bug fixing → deployment. Implement these as distinct task types handled by a DevOps agent cluster (same code, different prompts/tools per phase).
3.1 Planning Agent
Input: A feature request / bug ticket / improvement idea (from you, or auto-generated from the Data agents — see §4).
Tier: reasoning-heavy.
Workflow:
- Read the request + relevant repo context (file tree, README, existing related modules — fetch via a
repo_readtool). - Produce a structured implementation plan: affected files, new files, data-model changes, risk level, test plan, rollback plan.
- Break the plan into atomic coding tasks and enqueue them with dependencies (
task B depends on task A). - If risk level = high or touches auth/payments/migrations → set
approval_required = true.
Output schema:
{
"summary": "string",
"risk": "low|medium|high",
"files_touched": ["path", "..."],
"subtasks": [
{"id":"t1","desc":"...","depends_on":[],"type":"code"},
{"id":"t2","desc":"...","depends_on":["t1"],"type":"code"},
{"id":"t3","desc":"write tests for t1/t2","depends_on":["t2"],"type":"test"}
],
"test_plan": "string",
"rollback_plan": "string"
}
3.2 Coding Agent
Tier: code. Tools: repo_read, repo_write (to a feature branch only — never main), run_shell (sandboxed), run_tests.
Workflow:
- Check out / create branch
agent/<task-id>. - Read only the files in scope (token-budget discipline — don’t dump the whole repo).
- Generate or modify code. Write to disk.
- Run linters + the test suite locally.
- If tests fail → loop back to fix (max N iterations, then escalate to Bug-Fixing agent or human).
- Commit with a structured message and push the branch. Open a PR — do not merge.
Hard rule: Coding agent has write access to feature branches only. The merge to main is a human-gated action.
Sandbox the shell tool: run inside a container or constrained user with no production credentials, no outbound network except package registries, CPU/time limits.
3.3 Code Review Agent
Tier: reasoning-heavy. Runs automatically on every PR opened by the Coding agent.
Checklist the agent must produce a verdict on:
- Correctness vs. the plan’s intent
- Security (injection, secrets in code, unsafe deserialization, authz checks)
- Performance red flags (N+1 queries, unbounded loops over the 50k-channel dataset, etc.)
- Test coverage adequacy
- Style/lint compliance
- Migration safety (reversible? locks tables?)
Output: APPROVE / REQUEST_CHANGES with line-anchored comments. On REQUEST_CHANGES, automatically create a follow-up coding task referencing the comments. Two failed review rounds → escalate to human.
Use a different model route for review than for coding when possible (set via 9router). An independent reviewer catches more than the author re-reading itself.
3.4 Bug-Fixing Agent
Triggered by: failing CI, error-tracker alerts (wire Sentry/GlitchTip webhook → task queue), or a human report.
Tier: code (escalate to reasoning-heavy for hard reproductions).
Workflow:
- Ingest the stack trace + error context + recent commits (
git logof touched files). - Form a hypothesis; reproduce locally if possible (write a failing test first — TDD repair).
- Patch on a
fix/<id>branch; confirm the new test passes and the suite is green. - Open PR → routed to Code Review agent → human gate for merge.
3.5 Deployment Agent
Tier: code (mostly orchestration; minimal LLM use — keep this deterministic).
Deployment is a human-gated action by default. The agent prepares and can execute on approval:
- On PR merge to
main(human action) → CI builds artifact. - Deployment agent runs the release checklist: run migrations on a staging copy, smoke tests, dependency/security scan.
- Posts a deploy proposal to you: diff summary, migration list, rollback command. Waits for approval.
- On approval: blue-green or rolling deploy on the VPS, health-check the new version, keep the old release directory for instant rollback.
- Auto-rollback if health checks fail within the watch window; notify you.
Suggested deploy mechanics on a single VPS: timestamped release directories + a current symlink + process manager reload (systemd or PM2). Keep last 3 releases for rollback.
/srv/app/releases/2026-05-18-1430/
/srv/app/releases/2026-05-17-0910/
/srv/app/current -> releases/2026-05-18-1430 # symlink swap = atomic deploy
4. Agent Domain 2 — Market/Operational Data Acquisition
Purpose: scrape external data your products need (third-party directories, public datasets, format/spec references, or any reference data), store it, process it. The domain is property-agnostic — adding a new property adds scrape targets and a target schema, not code.
4.1 Scraper Agent
Tier: cheap-bulk for parsing/classification; vision only when layout must be read from a screenshot. Most scraping is not an LLM job — the LLM is for the messy extraction and normalization, not for fetching pages.
Pipeline (separate stages, each resumable):
DISCOVER → FETCH → PARSE → NORMALIZE → DEDUPE → STORE → VALIDATE
- DISCOVER — build/refresh the URL frontier (sitemaps, directory pages, APIs where available). Store as
scrape_targetsrows withnext_crawl_at. - FETCH — plain HTTP first; headless browser only when JS-rendered. Respect
robots.txt, set a real User-Agent, rate-limit per domain, cache raw responses to object storage (so re-parsing never re-fetches). - PARSE — deterministic selectors (CSS/XPath) where the structure is stable; LLM extraction only for unstructured/irregular pages — feed the cleaned HTML/text and ask for a strict JSON schema matching the per-property target shape.
- NORMALIZE — units, encodings, country/lang codes, liveness check for live-endpoint URLs (HEAD/GET probe, mark alive/dead, last_checked_at), timestamps to UTC.
- DEDUPE — hash exact dupes; embed + cosine-similarity for near-dupes (same entity listed twice under different names). Use pgvector.
- STORE — upsert into typed tables (see §8).
- VALIDATE — sample N% per run, LLM or rule check for plausibility; quarantine rows that fail.
LLM extraction prompt pattern (strict JSON, no prose):
SYSTEM: You extract structured data. Output ONLY valid JSON matching the schema.
No markdown, no commentary. If a field is unknown, use null.
SCHEMA: <per-property target schema, e.g. entity_name, url, attributes...>
INPUT: <cleaned page text / HTML fragment>
Parse defensively: strip code fences, json.loads, on failure re-ask once with the parse error appended, then dead-letter.
4.2 Data Processing Agent
Runs after STORE, on a schedule:
- Enrichment: infer missing attributes (e.g. category, region), geolocate, language-detect.
- Health monitoring: for live-endpoint datasets — periodic liveness probe; flag dead resources; auto-demote in rankings/visibility.
- Clustering/categorization: group entities by category/region for browsing UX; cluster use-cases; categorize reference data per property.
- Aggregation: materialized views / summary tables for fast site queries (don’t make the public site scan large tables live).
4.3 Legal & ethical guardrails (non-negotiable)
- Honor
robots.txtand Terms of Service. Maintain a per-domain allow/deny list the agent cannot override. - Rate-limit and identify your bot honestly; prefer official APIs/feeds when they exist.
- Never scrape content behind auth/paywalls.
- For media/live-endpoint sources: store metadata + links/pointers; do not rehost copyrighted audio, video, images, or text content. The Data agent must never store the underlying media payload — only its metadata and a back-reference.
- Keep a provenance record (
source_url,fetched_at) on every row for takedown compliance.
5. Agent Domain 3 — Customer Data Acquisition
⚠️ Scope note & guardrail. “Customer data” here means first-party data you legitimately own: your own analytics, signups, support tickets, on-site behavior, and feedback for your SaaS sites — plus opt-in enrichment. This guide will not help scrape third parties’ personal data, harvest PII from the open web, or build profiles of people without consent. That’s both legally hazardous (GDPR/CCPA/local law) and against platform rules. The architecture below is built around consented, first-party data.
5.1 First-Party Collection Agent
Sources (all on your own properties):
- Account signups, plan/usage data (esp. SaaS system).
- On-site events (page views, conversions, churn signals) via your own analytics.
- Support tickets / contact-form submissions.
- Explicit feedback (surveys, ratings).
Workflow:
- Ingest events from your app’s event stream / webhook into a raw
eventstable. - Sessionize & identity-resolve within your own data only (cookie/user-id → person), respecting consent flags.
- PII minimization: hash/encrypt direct identifiers at rest; store the minimum needed.
- LLM use here is limited and
cheap-bulk: classify support tickets, summarize feedback themes, tag churn reasons — not profiling individuals.
5.2 Customer Data Processing Agent
- Segmentation: behavioral cohorts (active/at-risk/power users) for product + content targeting.
- Feedback synthesis: cluster support tickets & feedback → feed the Planning agent (§3.1) to auto-propose features/bug fixes. This closes the loop: customer pain → roadmap.
- Churn signals: flag accounts for retention outreach (the message still goes through the human-gated Distribution path if it’s outbound).
5.3 Compliance layer (build before collecting anything)
- Consent management: store consent state per user; agents must check it before processing.
- Data subject requests: an agent task type
dsr_export/dsr_deletethat fulfills access/deletion requests. - Retention policy enforced by a scheduled agent (auto-purge raw events past retention window).
- Region awareness: don’t move EU personal data off compliant storage; document your lawful basis.
- Apply the strictest tier (parental consent, FERPA/GDPR-K-style handling, minimal LLM exposure, no third-party model training on this data — confirm your 9router routes for this domain use zero-retention model endpoints).
6. Agent Domain 4 — Content Generation Engine
Purpose: auto-generate SEO + social content for one or more properties without per-project hardcoding. The engine is property-agnostic: it consumes signals from upstream agents, drafts assets, and produces a content pipeline that any site in the portfolio can plug into.
6.1 Content Planning Agent
Tier: reasoning-heavy (low frequency).
- Pulls inputs from upstream agents and external signals: trending entities/topics surfaced by the Data Scraper + Processing agents (§4), first-party usage patterns from the Customer Data agent (§5), seasonal calendar, and keyword/gap analysis from search-console feedback.
- Produces a content calendar: topics, target keyword, target property, channel set (own CMS, social, search submission), priority, due date → enqueued as
content_drafttasks. - Maintains topic diversity (no duplicate angles against the existing corpus) and respects each property’s editorial guardrails (loaded from config — not from prompts).
6.2 Content Drafting Agent
Tier: creative.
- For each calendar item: generate the asset bundle (article or landing-page block, social variants, meta tags, OG image prompt).
- Enforce a brand/style spec (tone, banned claims, reading level, length per channel) loaded from a per-property config and injected into every prompt. Style and policy are data, not code, so adding a new property is a config change.
- Generate all channel variants in one task (long-form body + social variants + meta description) to keep context coherent across formats.
- Produce structured output:
{title, slug, body_md, meta_description, og_image_prompt, social_variants:{...}, target_property}.
6.3 Content QA Agent
Tier: reasoning-heavy. Runs on every draft before it’s eligible to publish:
- Factuality check — flag unverifiable claims, especially on topics where errors carry reputational, legal, or safety risk (e.g. health, finance, education, regulatory content). For sensitive properties, this gate is the strictest in the pipeline.
- Plagiarism/originality heuristic + duplicate-against-your-own-corpus check (pgvector over previously published content).
- SEO check (keyword presence, title/meta length, heading structure, internal-link suggestions).
- Policy check (no copyrighted third-party material, no misleading claims, no PII leakage, compliance with each property’s content policy).
- Output:
pass/revise(with notes → auto-revision task) /reject.
Content that passes QA is staged, not auto-published. Publishing is in the Distribution domain with a human gate (configurable per channel — see §7). The whole content engine is property-agnostic: adding a new property is a config + style-spec addition, not a code change.
7. Agent Domain 5 — Distribution
Channels: social media, Google Search Console, Bing (IndexNow / Bing Webmaster), plus your own CMS.
7.1 Publishing model & gating
| Channel | Default gate | Rationale |
|---|---|---|
| Own CMS / blog | Auto-publish allowed (post-QA) | Fully reversible, your property |
| Search Console / Bing (sitemap + URL/IndexNow submit) | Auto allowed | Mechanical, low-risk, reversible |
| Social media (public post) | Human approval (default) | Public, hard to fully un-ring; brand risk |
You can relax the social gate later to “auto for low-risk evergreen, gate for anything sensitive” once you trust the QA agent — make it a per-task approval_required flag, not a code change.
7.2 Search Console / Bing Submission Agent
This is mostly deterministic — minimal LLM.
- Sitemaps: the agent regenerates/validates
sitemap.xmlafter each CMS publish and pings the sitemap endpoint. - Google Search Console: use the official Search Console API for sitemap submission and the Indexing API only where eligible (it’s officially for Job Posting / Livestream types — for general pages rely on sitemaps + internal linking, don’t abuse the Indexing API or you risk action).
- Bing: use IndexNow (Bing-supported, simple key-file protocol) to push new/updated URLs instantly, plus Bing Webmaster Tools API for sitemap submission and stats ingestion.
- Feedback loop: the agent pulls Search Console / Bing performance data back into the DB → feeds the Content Planning agent (which pages underperform, which queries to target). This is the growth flywheel.
Submission workflow:
CMS publish event → regenerate sitemap → validate →
├─ submit sitemap to Google Search Console API
├─ submit sitemap to Bing Webmaster API
└─ POST changed URLs to IndexNow endpoint
→ record submission + later pull back impression/click stats
Verify current API specifics at build time — search/indexing APIs and their eligibility rules change. The agent’s submission module should be a thin adapter you can update without touching the orchestrator.
7.3 Social Media Posting Agent
- Always use official platform APIs, never browser automation against logins or unofficial endpoints (account-ban + ToS risk, and brittle).
- Per platform: store OAuth tokens in the secrets vault; the agent refreshes them; never logs tokens.
- Workflow: pick approved+scheduled content → format to platform constraints (length, media, hashtags) → queue as
pending_approval→ on your approval, post via API at the scheduled time → record post ID + later pull engagement metrics. - Rate & cadence governor: hard caps per platform per day; jitter timing; dedupe so the same asset isn’t posted twice. This must be enforced in code, not left to the LLM.
- Pull engagement back → feeds Content Planning (what resonates) → flywheel.
7.4 Analytics Feedback Agent
Closes every loop:
- Ingests Search Console, Bing, social engagement, and on-site conversions.
- Attributes outcomes to content pieces.
- Generates a weekly performance digest for you + auto-creates Content Planning tasks for winners (do more) and Bug/Planning tasks for technical issues (e.g. pages with impressions but zero clicks → metadata fix).
8. Data Layer & Storage Design
Single PostgreSQL instance (+ pgvector). Core tables — every domain table carries a property column so the same schema serves the whole portfolio (adding a new property is data, not migration):
-- Orchestration
tasks(id, type, payload jsonb, status, priority, attempts,
approval_required bool, proposed_output jsonb,
depends_on text[], agent_id, created_at, updated_at)
agent_runs(id, task_id, agent_id, model_tier, model_resolved,
prompt_tokens, completion_tokens, cost_usd,
status, error, started_at, finished_at) -- audit + cost
-- Domain 2: scraped operational data (generic entity shape; per-property schema lives in `attrs`)
scrape_targets(id, property, source_url, kind, next_crawl_at, last_status)
raw_pages(id, target_id, fetched_at, storage_key, http_status) -- body in object storage
scraped_entities(id, property, entity_type, name, url, attrs jsonb,
is_live bool, last_checked_at, source_url, fetched_at,
embedding vector(N), dedupe_hash) -- scales to large catalogs
-- Domain 3: first-party customer data (consent-aware)
app_users(id, email_hash, created_at, plan, consent_state jsonb, region)
events(id, user_id, type, props jsonb, occurred_at) -- raw, retention-purged
segments(user_id, segment, computed_at)
support_tickets(id, user_id, body, category, sentiment, theme, created_at)
-- Domain 4/5: content
content(id, property, channel_set text[], title, slug, body_md,
meta jsonb, status, qa_verdict, created_at, scheduled_at)
publications(id, content_id, channel, external_id, published_at,
metrics jsonb, metrics_updated_at)
-- Compliance
consent_log(user_id, scope, state, changed_at)
dsr_requests(id, user_id, kind, status, fulfilled_at)
Storage rules:
- Raw scrape bodies → object storage, not Postgres (keep the DB lean when any single dataset grows large).
- Embeddings in pgvector with an IVFFlat/HNSW index for dedupe + semantic search.
- PII columns encrypted (app-level or
pgcrypto); storeemail_hashfor joins, encrypted email only where strictly needed. - Per-property flexible attributes in
scraped_entities.attrs(jsonb) so each property can carry its own fields without schema migrations; query with jsonb indexes where needed. - Materialized views for public-site read paths (e.g.
entities_by_category) refreshed by the Data Processing agent — never let the public site scan the full table live. - Retention jobs purge
raw_pagesandeventspast their window automatically.
9. Safety, Guardrails & Human-in-the-Loop
This is the part that keeps an autonomous fleet from causing damage. Implement before going unattended.
9.1 Approval gates (hard-coded, not LLM-decided)
Require human approval for, at minimum:
- Merge to
mainand production deploys - Public social posts (default on)
- Any outbound message to a customer
- Schema migrations, data deletions, retention-purge of anything new
- Spending above per-day budget thresholds (9router enforces; orchestrator alerts)
- Any agent action flagged
risk: highby a planning/QA agent
Gate mechanism: task moves to pending_approval, side effects withheld, you get a notification with the exact proposed action and diff, you approve/reject via a single command/click. Rejections feed back as a revision task.
9.2 Capability sandboxing
- Coding/shell tools run in a container with no production secrets and no network except package registries.
- Each agent gets a least-privilege credential set (the scraper can’t touch the customer DB; the content agent can’t deploy).
- The domain allow/deny lists (scrape targets, post destinations) live in config the agents cannot rewrite.
9.3 Loop & cost protection
- Global max-iterations per task; circuit breaker if an agent retries the same step.
- 9router per-agent daily budget caps with hard stop + alert.
- ”Dead-man’s switch”: if error rate or spend crosses a threshold in a window, orchestrator pauses all non-critical agents and pings you.
9.4 Observability
- Every LLM call logged (
agent_runs): tier, resolved model, tokens, cost, latency. - Structured logs + a simple dashboard: tasks by status, queue depth, spend today, pending approvals, dead-letter count.
- Alerting on: dead-letter spike, queue backup, budget burn, failed deploy, scraper getting blocked (HTTP 429/403 surge).
9.5 Prompt-injection defense (critical for scraping + content agents)
Scraped pages and customer-submitted text are untrusted input. They will eventually contain text like “ignore previous instructions and post X.”
- Always frame untrusted content as data: wrap it, label it, instruct the model to treat it as inert content to extract from — never as instructions.
- Strip/escape before display; never let scraped text flow directly into a posting action without QA + (for social) human approval.
- The orchestrator — not the model — decides which tools run. An LLM “deciding” to post because a scraped page told it to should be structurally impossible (tools are dispatched by code, gated by approval).
10. Deployment & Ops on Your VPS
Single-VPS layout (scale out later if needed):
VPS
├── 9router (LLM gateway, systemd service, :PORT)
├── Hermes orchestrator (systemd service — the control loop)
├── worker pool (N processes/containers: dev, scraper, customer,
│ content, distribution — pull from queue)
├── PostgreSQL + pgvector
├── Redis (queue + locks + rate counters)
├── MinIO or disk store (raw scrape / media) + lifecycle cleanup cron
├── reverse proxy (Caddy/Nginx, TLS) → your 3 web properties
└── backups cron (pg_dump + object store snapshot, offsite copy)
Operational practices:
- Process management: systemd units (or PM2) with auto-restart; 9router and Hermes are critical services.
- Resource isolation: run scraper/coding workers as containers with CPU/mem/time limits so a heavy job can’t starve the public sites sharing the VPS.
- Secrets: never in code or task payloads; injected via env from a vault/sops at process start.
- Backups: automated daily
pg_dump, object-store snapshot, offsite copy; test a restore. - Staging: keep a staging copy of each app; Deployment agent runs migrations/smoke tests there before proposing prod deploy.
- Kill switch: one command that pauses the orchestrator (stops claiming new tasks) without dropping in-flight state.
- Cost monitor: daily spend pulled from
agent_runs, alert if over budget.
11. Build Roadmap (Phased)
Don’t build all five domains at once. Sequence for fastest safe value:
Phase 0 — Foundation (build first, no skipping)
9router tiers + budgets · Hermes control loop · Postgres + pgvector + Redis · task schema · notifier · approval-gate mechanism · observability/log of agent_runs.
Phase 1 — Software Development domain Planning → Coding (feature-branch only) → Code Review → PR. Human-gated merge/deploy. This compounds: it speeds up building everything after it.
Phase 2 — Operational Data domain Scraper + Data Processing for whichever property needs it most (start with the one whose liveness monitoring + auto-categorization gives the clearest SEO or UX win). Legal guardrails in place first.
Phase 3 — Content + Distribution (read-only side first) Content Planning/Drafting/QA → CMS auto-publish + Search Console/Bing/IndexNow submission + Analytics feedback. Social posting stays human-gated. This builds the SEO flywheel with low risk.
Phase 4 — Customer Data domain First-party collection + processing + compliance layer + DSR tooling. Feed feedback synthesis into Phase-1 Planning agent (pain → roadmap loop).
Phase 5 — Tighten the loops & selective autonomy Relax low-risk gates where QA has proven reliable; add the Analytics→Planning auto-task loops; per-property tuning.
Each phase is usable on its own — ship it, run it attended for a while, then expand.
12. Per-Property Application Notes
Different properties hit the architecture differently. Below are three common archetypes — apply the patterns that match the property you’re building, not a one-size-fits-all checklist.
High-volume live-endpoint directory (large catalog of entities pointing to live resources — streams, feeds, APIs, status endpoints)
- Highest-value early win: Data agent’s liveness monitoring + auto-categorization → better UX + fresh sitemaps → SEO. Scale concern: never live-scan the full table; use materialized views + the embedding index for “similar entities.” Content angle: programmatic category/region landing pages (QA must block any copyrighted media reproduction).
- Pattern fit: Phase 2 first (data), Phase 3 second (programmatic SEO content), minimal Customer-Data work beyond feedback.
Utility / tool with first-party usage signals (small catalog, rich usage telemetry)
- Content flywheel is the big lever: auto-generate how-to / format-pair / FAQ / troubleshooting guides from real first-party usage data. Distribution agent + IndexNow gets these indexed fast. Bug-fixing agent wired to error tracking pays off because utility tools fail in long-tail edge cases.
- Pattern fit: Phase 1 (SDLC) + Phase 3 (content) first; Phase 4 (Customer Data) is where the real value compounds via usage-signal feedback.
Sensitive-data property (minors, health, financial, regulated domains)
- Strictest data tier. Apply §5.3 fully: consent gating, minimal LLM exposure of personal data, zero-retention model routes in 9router for this domain, no third-party training on this data, hard isolation of its DB from other agents. Content agent here must have factuality QA at maximum (errors on this property are high-harm). Most automation value is in the Software Development domain + first-party feedback→roadmap loop, not aggressive scraping or marketing.
- Pattern fit: Phase 1 (SDLC) + Phase 4 (Customer Data) first; defer Phase 2 (scraping) unless strictly necessary and treat any external data as a separate, isolated store.
Final note
Build the foundation and guardrails first, then the software-development domain (it accelerates everything else), and only then expand outward. Keep humans on the gate for anything public, irreversible, financial, or involving personal/minor data — that single discipline is what makes an autonomous agent fleet safe to actually leave running.