A self-hosted, autonomous on-page SEO auditor & monitor. Point it at your pages (local HTML or live URLs); it scores each one 0-100 against a transparent on-page rule set and emits JSON + Markdown reports — so you catch SEO regressions before search engines do.
Overview
agent-seo-v1 audits the things you actually control on a page: the title and
meta description, heading structure, image alt coverage, canonical and viewport
tags, Open Graph metadata, indexability (robots), content depth, and whether
internal links resolve. Every check is deterministic and explainable — there
is no black box and no third-party SaaS call. You get a numeric score, a letter
grade (A-F), and a per-check table telling you exactly what to fix.
The core runs with no API key and no LLM at all. If you connect a coding-agent CLI (Claude Code / Kimi Code / Codex), the agent additionally drafts improved titles and meta descriptions for weak pages via the Rewriter prompt — your CLI subscription is the only billing relationship.
This pack targets operators of content sites, docs, landing pages and small catalogues who want a recurring, scriptable SEO health check on their own infrastructure. It is not a rank tracker, a crawler for the whole web, or a backlink tool — it audits the pages you give it.
Who it is for
- Engineers shipping marketing/landing pages who want a CI-style SEO gate.
- Docs and content teams watching for accidental
noindex, missing meta, or thin pages after a migration. - Anyone running a static site who wants a nightly score with zero SaaS lock-in.
Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ Targets (URLs / .html files) │
└──────────────────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ DISCOVER → FETCH → AUDIT → SCORE → REPORT │
│ OODA monitoring loop │
└──────────────────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Deterministic on-page rules → Score 0-100 + grade A-F │
│ (title, meta, headings, alt, canonical, OG, robots, links…) │
└──────────────────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ JSON + Markdown report → optional BYO-LLM title/meta rewrite │
└─────────────────────────────────────────────────────────────────────┘
Pack contents
| File | Role |
|---|---|
README.md |
This file — overview and quick-start |
SOUL.md |
Mission, values and the SEO-specific OODA states |
PROMPTS.md |
Named, versioned prompts loaded by agent.py |
CHECKLIST.md |
20+ deployment & operating steps with commands |
SPEC.md |
Technical spec, scoring weights, configurable params |
CUSTOMIZE.md |
Where to plug in your own pages and thresholds |
OPERATING_COST_ESTIMATE.md |
Provider-agnostic cost method |
CHANGELOG.md |
Version history |
agent.py |
Runnable auditor (deterministic core + BYO-LLM) |
llm_adapter.py |
BYO-LLM CLI adapter (claude/kimi/codex, no key) |
install.sh |
One-shot Debian installer (venv + systemd) |
agent-seo-v1.service |
Reference systemd unit (template) |
test_agent.py / smoke_test.sh |
Offline test suite (no keys, no network) |
samples/*.html |
Two demo pages (one strong, one weak) |
templates/*.{json,yaml} |
Targets list + tuning template |
Quick start
# 1. Install (Debian/Ubuntu): system deps + venv + optional systemd service
sudo bash install.sh
# 2. Audit the bundled samples (fully offline — no keys, no network)
python3 agent.py audit samples/ --format md
# 3. Audit a single page or a live URL
python3 agent.py audit samples/poor.html
python3 agent.py audit https://example.com
# 4. Run one monitoring cycle over the configured targets
python3 agent.py run --once
Expected on the samples: samples/good.html scores ~95 (grade A),
samples/poor.html scores low (grade F) and lists exactly what is wrong.
LLM vs deterministic — an honest table
| Capability | Without LLM (default) | With a connected CLI |
|---|---|---|
| On-page scoring & grading | ✅ full | ✅ full |
| Per-check pass/warn/fail detail | ✅ | ✅ |
| Broken internal-link detection | ✅ | ✅ |
| JSON + Markdown reports | ✅ | ✅ |
| Title / meta rewrite suggestions | ⚠️ length-based guidance | ✅ drafted copy |
The audit is 100% useful with no LLM. The LLM only makes the fix suggestions smarter; it never gates the score.
Common operations
python3 agent.py audit ./public --format md --out seo.md # audit a built site dir
python3 agent.py rewrite samples/poor.html # suggest better title/meta
python3 agent.py run --once --deterministic # force no-LLM cycle
python3 agent.py status # backend + last cycle
Configuration
All thresholds are environment variables (tune without editing code) — see
SPEC.md and templates/seo_rules.yaml. Monitoring targets live in
templates/seo_targets.json (see CUSTOMIZE.md).
Use a local model (Ollama)
The optional LLM step can also run on your own machine through Ollama, so no text leaves your server. No account and no API key.
ollama pull <model> # any chat model you like
export PACK_LLM=ollama # prefer Ollama even if a coding-agent CLI is installed
export PACK_OLLAMA_MODEL=<model> # optional: default is the first chat model the server lists
python3 -c "import llm_adapter; print(llm_adapter.detect())" # prints: ollama
PACK_OLLAMA_HOSTpoints to another server (defaulthttp://127.0.0.1:11434);OLLAMA_HOSTis also read whenPACK_LLM=ollama.PACK_OLLAMA_NUM_CTXsets the context size if your reports or prompts are long.- Without
PACK_LLM, a local Ollama server is used only when no Claude Code, Kimi Code or Codex CLI is found. - If the server is down or the model is missing, the pack keeps working in its deterministic mode.
Learning memory (local and private)
Every LLM call this pack makes is journaled in ./.pack_memory/ next to the code, so the pack can improve for you. Nothing is ever sent anywhere.
- What is kept: the task name, the masked input, the masked answer, status and latency. Keys, tokens, e-mails, phone numbers, IP addresses, card and IBAN numbers and personal paths are masked before writing; the system prompt is never stored.
- Teach it:
python3 pack_memory.py feedback last okconfirms a result,python3 pack_memory.py feedback last bad --fix "the right answer"corrects it. Confirmed and corrected runs are reused as validated examples when a similar input comes in. Unrated runs are never injected into a prompt. - Find similar cases:
python3 pack_memory.py similar "some text"andpython3 pack_memory.py clusters(groups of near-identical failures) work in plain Python, no model needed. Optional meaning-based matching uses an embedding model you already run locally:python3 pack_memory.py enable-semantic(Ollama on this machine only). - Share, only if you want:
python3 pack_memory.py exportwrites an anonymised summary (counts, statuses, size buckets, no text) for you to review. - Control:
PACK_MEMORY=0turns it off,PACK_MEMORY_STORE_TEXT=0keeps fingerprints only,python3 pack_memory.py purge --yeswipes it.
License
Personal use per purchase. Resale prohibited.
