主题
10 从本地演示走到可维护的真实站点
本交付能让你运行一条持久化的本地闭环,看见一次保留和一次回滚,并检查程序怎样拒绝没有依据的改动。它没有购买域名、连接真实账号、提交站点或发布生产。迈向真实运营时,先把数据接入、审批与部署分别验收,避免一次操作同时放开所有权限。
10.1 先划清交付边界
| 能力 | 状态 | 接下来还要验证什么 |
|---|---|---|
| SQLite 状态、版本、审计、两轮合成演示 | 原型已实现 | 换业务输入后重跑测试,不推导真实效果 |
| 标题更新白名单、绑定 hash 的本地批准 | 原型已实现 | 正式身份认证与生产权限未实现 |
| 预览静态站、浏览器转换工具 | 原型已实现 | 真实浏览器、托管 HTTP 行为及无障碍人工验收 |
| GSC、GA4 与模型适配器 | 配置后可接入 | 有账户授权才可调用;当前只做 mock 合同测试 |
| AdSense 数据 | 文件导入接口 | 仅导入获准导出数据,未实现直接账户 API 接入 |
| 广告、真实收录、收入结算 | 未执行 | 独立申请、政策与隐私检查、真实观察 |
| PostgreSQL、多机队列、RBAC、远程发布 | 生产扩展未实现 | 设计、实现、权限及故障演练 |
fixture 表示固定测试数据。它让相同输入可重复验证规则,不是当前市场采样。两轮演示的日期与数据均为教学构造;程序没有等待搜索引擎数周,也没有测得因果增长。生产使用前应把这种显著标注保留到所有报告和界面。
10.2 在干净目录复现结果
以下命令从交付包的 prototype 目录执行,使用 Python 3.12。工作目录选择一个专用的新目录,不能指向仓库根目录、已有网站目录或生产目录。代码不要求为了演示先安装第三方 Python 包;个别浏览器脚本测试对可用运行时的要求见第 9 章。
bash
cd prototype
python --version
python -m unittest discover -s tests -v
python -m seo_loop demo --workdir ./runs/first-demo
python -m seo_loop status --workdir ./runs/first-demo
python -m seo_loop recover --workdir ./runs/first-demo命令以零退出只是第一步。打开工作目录中的 report.json 和 audit.json,检查 mode 是 synthetic-local-preview、live_seo_gain_measured 为 false、两轮判定分别包含 keep 与 rollback、审计有效、没有待修复渲染。再用同一目录运行一次 demo,确认业务动作没有重复增加。报告字段与更完整故障操作见第 9 章。
本章编写期间在 Python 3.12.14 中实跑了 demo、status、recover;当时整套测试为 95 项通过。后续修订可能增加测试,以交付包最终 QA 记录为准。这次测试证明的是本地规则与合成路径,不包括真实 API、Google 收录、广告收入或长期并发负载。
预览可用 Python 自带的本地 HTTP 服务。它适合本机检查,不是生产服务器。[D01]
bash
python -m http.server 8000 --bind 127.0.0.1 \
--directory ./runs/first-demo/site在自己的浏览器打开 http://127.0.0.1:8000/,完成工具页与指南页检查,然后用 Ctrl+C 停止服务。不要将包含状态数据库的工作目录作为站点根目录,也不要把本机服务绑定到公共网络来代替正式发布。预览中的 noindex 是防误索引标记,不是访问控制;需要保密的预发布站仍要有认证或网络隔离。
10.3 第一次接真实服务,先手动认清资源
先由站主在官方控制台确认资源和归属,记录资源 ID、用途、时区、货币及负责人。不要让模型根据一个域名猜是哪一个账号。接入顺序如下;每步失败就停在该步,不用后面成功的报告掩盖前面的错误。
| 步骤 | 输入与动作 | 产物与验收 | 失败时怎么做 |
|---|---|---|---|
| 确认站点 | 已授权域名与托管项目,检查正式域名 | 项目、域名、证书、回滚方法明确 | 不购买或替换域名来绕过问题 |
| Search Console | 站主按官方流程验证资源 | 页面检查结果与资源 ID 能对应 | 等验证解决,不能编造索引状态 |
| GA4 或替代测量 | 先确定事件与隐私范围,再建立配置 | 合成任务的成功、失败、重复能区分 | 修埋点,不用倍率“补齐”数据 |
| 只读报告 | 授予最小必要只读权限,导出小范围 | 原始响应、查询参数、时间与缺口齐全 | 权限或口径错误停止依赖分析 |
| 模型服务 | 确认供应商、数据用途和费用上限 | 小份非敏感样本可校验,预算留痕 | 缺报价或权限就保持离线 |
| 广告服务 | 站主独立处理申请、条款和付款资料 | 政策、同意和布局验收通过 | 不在审核前假定收益 |
Search Console 的官方入门流程包含资源验证及重要页面检查。[D02] 站点地图提交只是发现渠道,提交成功不表示已经索引。URL Inspection 的读取也不是远程触发实时索引的承诺。把后台返回状态和实际取得时间原样记录,后续再据此检查。
凭据应由站主放到其批准的安全配置中。不要写进源码、示例 JSON、PDF、日志或共享工作目录。本书没有代填秘密,也没有创建长期访问令牌。可选 live 适配器的授权配置不等于允许任意账户动作;目前本地标题执行器还会拒绝真实数据直接进入合成实验。
10.4 发布经过四道门
生产发布器属于扩展设计。它应将构建与发布分开:先用批准的内容生成固定构建产物,检查完再把同一个产物交给部署端。不能审批一个 HTML 文件,发布时又让模型重新生成另一个版本。
正在绘制图示…
查看图源代码
flowchart TD
A[离线 dry-run] --> B{规则和事实验收}
B -- 通过 --> C[隔离 staging]
C --> D{功能与真实 HTTP 验收}
D -- 通过 --> E[批准少量 canary 页面]
E --> F{护栏稳定吗}
F -- 是 --> G[逐批扩大]
F -- 否 --> H[恢复已知版本]
H --> I[核对线上结果并记录原因]
I --> A
第一道门是 dry-run。输出将修改的页面 ID、旧新 hash、差异、来源、成本、风险及恢复目标;对不认识的动作一律拒绝。检查应该拒绝的输入,和检查正常输入同样重要。
第二道门是 staging。使用隔离环境验证 HTTP 状态、链接、移动端、工具边界样例、404、canonical、sitemap、robots 和隐私标签行为。代码生成了某个文件,不代表托管平台一定以预期状态返回它。例如一个文件名叫 404.html,服务器仍可能把未知地址统一返回 200,必须在托管环境实测。
第三道门是小范围 canary,即只放行少量独立页面。数量按回滚范围和人工检查能力决定,本书建议初次 1—3 页作为操作起点,并非 SEO 规则。批准绑定明确页面、产物 hash、域名和版本;模板、全站导航及索引策略另行审批。先验证实际线上内容与构建产物一致,再开始观察业务指标。
第四道门才是扩张。严重事实错误、工具故障、私密数据泄露、错误 canonical、关键路径不可用等情况,先暂停扩张并处理事故,不必等待流量达到统计门槛。低流量、短窗口或普通排名波动则不触发机械回滚;它们需要第 7 章的诊断与判断。
恢复分两种。内容错误恢复上一已知版本,并验证工具及链接;隐私泄露还要处理缓存、日志、第三方传播及通知义务,单纯覆盖页面不等于问题已解决。数据库与外部发布不同步时,先核对实际状态,避免旧程序自动把已经修正的线上内容再次覆盖。
10.5 自动任务只做已获准的事
下面是一份可供站主审核后启用的 GitHub Actions 示例,当前没有上传或启用。它只检查离线演示,没有发布、提交索引或付费调用。示例用常见版本标签便于阅读;正式采用时应根据官方安全建议审查依赖,并固定到审核过的提交 SHA。[D03]
yaml
name: offline-seo-check
on:
workflow_dispatch:
schedule:
- cron: '17 3 * * 1'
permissions:
contents: read
concurrency:
group: offline-seo-check
cancel-in-progress: false
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Test and rehearse locally
working-directory: prototype
run: |
python -m unittest discover -s tests -v
python -m seo_loop demo --workdir "$RUNNER_TEMP/seo-demo"
python -m seo_loop status --workdir "$RUNNER_TEMP/seo-demo"此处 cron 使用 UTC,每周一 03:17。GitHub 的计划任务可能延迟,不能作为严格的实时调度保证。[D04] 它只是验证新代码仍能跑合成闭环。临时 runner 的数据库不会自然成为长期生产状态;要持续经营,另行选择持久运行环境、状态备份与互斥调度方案。
这个任务不需要云端 API 秘密,也没有写仓库权限。以后接只读报表时,把数据拉取与生产发布放到不同身份和任务里;只读分析任务不应拿发布凭据。部署审批应在可信系统中发生,不能把模型生成的“approved: true”当作批准。
若已有单机调度环境,先用本地计划任务运行测试或只读报告,记录开始、结束、退出码和数据新鲜度。避免两个任务同时写同一工作目录。原型使用工作目录锁;这不是分布式锁,也不能解决跨机器重复投递。调度器失约、数据库锁等待及连续失败都应有可见告警,而不是悄悄跳过。
10.6 用恢复演练证明备份可用
先停掉正在写入的任务,或者采用 SQLite 在线备份 API 取得一致备份。下面命令只做本地备份示例,在 prototype 目录执行。备份目录需要用户自行选择访问控制和加密存储;代码没有将它传到外部。
bash
python - <<'PY'
from pathlib import Path
import sqlite3
source = Path('runs/first-demo/state.sqlite').resolve()
if not source.is_file():
raise SystemExit('Source database does not exist')
backup = Path('runs/first-demo.backup.sqlite').resolve()
if backup.exists():
raise SystemExit('Choose a new backup path; existing file preserved')
with sqlite3.connect(source.as_uri() + '?mode=ro', uri=True) as src:
with sqlite3.connect(backup) as dst:
src.backup(dst)
result = dst.execute('PRAGMA integrity_check').fetchone()[0]
if result != 'ok':
raise SystemExit(result)
print('Backup integrity_check: ok')
PYSQLite 的备份 API 用于复制数据库的一致状态。[D05] 不要在运行中的 WAL 模式数据库上只复制主文件,再假定所有事务都已包含。恢复演练在新目录操作,保留原件。注意 CLI 的 recover 只处理已经记录为 render_pending 的构建中断;它不负责从空目录恢复全部文件。只有数据库备份时,可在专用新目录显式重建本地预览:
bash
python - <<'PYRESTORE'
from pathlib import Path
import shutil
from seo_loop.engine import Engine
source = Path('runs/first-demo.backup.sqlite')
target = Path('runs/restored-demo')
if not source.is_file() or target.exists():
raise SystemExit('Need a valid backup and a new restore directory')
target.mkdir(parents=True)
shutil.copy2(source, target / 'state.sqlite')
engine = Engine(target)
try:
engine.store.verify_audit()
engine.render()
report = engine.report()
print(report['mode'], report['audit_valid'])
finally:
engine.close()
PYRESTORE核对恢复后的页面版本与决策,再用本地浏览器检查工具。此脚本只调用已实现的本地 Python 方法,没有远程发布。数据库备份不包含外部对象和秘密配置,因此生产还需要与之配套的构建清单、获准证据及配置版本。
为正式运营写下两个数字:最多能接受丢失多少时间内的新数据,以及多久需要恢复服务。这分别是恢复点目标和恢复时间目标。数值由经营需求决定;不能用“每天备份”替代回答。演练时实际计时并记录差距,至少覆盖数据库丢失、构建目录损坏和配置版本不匹配。
10.7 验收清单要包含拒绝和漂移
下面分为本地可测与上线后待测,不把未执行的验收写成通过。第 9 章提供更具体的测试定位;生产演练须在获准的隔离环境进行。
| 检查项 | 应出现的结果 | 验收层级 |
|---|---|---|
| 重跑同一 demo | 不重复业务动作,审计仍有效 | 原型测试 |
| 提案没有证据或证据属于别页 | 拒绝,现有页不变 | 原型测试 |
| 审批后改标题或版本 | 拒绝旧批准 | 原型测试 |
| 指标缺失、未最终、分母为零 | 保持观察,不制造增长结论 | 原型测试 |
| 同 revision 的指标被改写 | 拒绝覆盖 | 原型测试 |
| 已结束决策的数据后续修订 | 提示人工复核,不偷偷重写历史 | 原型测试 |
| 动作超预算、冷却未结束、STOP 存在 | 停止受控动作 | 原型测试 |
| HTML、路径和 URL 注入 | 转义或拒绝,不能执行外部指令 | 原型测试 |
| 发布中断与恢复 | 使用持久状态重建,不丢已登记变更 | 本地模拟;远程待测 |
| 正式 404、TLS、跳转与缓存 | 与路由合同一致 | 生产接入后待测 |
| 同意拒绝与撤回 | 标签实际行为符合已批准方案 | 生产接入后待测 |
| 备份恢复到另一环境 | 版本与必要证据齐全,恢复时间有记录 | 运维演练待测 |
漂移不只来自模型。API 字段改变、来源下线、产品规格更新、时区变化、页面模板变更,都可能使旧规则不再适用。安排固定的边界样本回归测试;新增来源先跑隔离合同测试;重新计算历史报告时写明版本与口径。模型升级后,至少比较严重错误、无依据断言、拒绝正确率、人工审核时间与总成本,不只比较文风。
10.8 用 30/60/90 天安排交付,不安排排名
下表是工作节奏示例。它假设有时间进行研究、开发、审稿和运营,具体按资源调整。任何阶段没有满足退出条件,都可以延长或缩小范围;90 天结束时没有稳定搜索流量,也不能据此断言项目必定失败或即将成功。
| 阶段 | 主要工作 | 进入下一阶段的证据 |
|---|---|---|
| 第 1—30 天 | 明确主题、资料权利、页面任务;跑本地闭环与工具验收;准备试点 | 能复现演示;真实内容可核查;范围与预算明确 |
| 第 31—60 天 | 经批准上线少量页面;接只读数据;建立口径与变更基线 | 技术与测量验收通过;已知缺口有人处理;有真实使用反馈或明确待观察项 |
| 第 61—90 天 | 完成一次有边界改进;核算成本;备份与权限演练;决定是否扩张 | 保留、撤回、等待或停止都有证据;交接人能复现操作 |
若第 60 天仍没有足够搜索样本,就先检查可发现性、功能、内容及需求证据,不强行做显著性报告。若用户任务能完成但广告经济不成立,可以评估其他商业方向,也可以保留有价值的内容并停止扩张。没有义务为了让自动系统一直运行而继续付费。
每周看的是阻塞与用户问题,每月看的是队列、成本和证据,每次版本更新看的是风险与恢复。长期记录应保存已经确认的事实、有效失败样例、实验结论及其限制;新模型只读取这些材料提出建议,不能擅自改变经营目标、预算上限和审批规则。
10.9 交给下一位维护者的最小包
最终交接至少包含:资源清单与负责人、代码和配置版本、数据字典、当前页面及来源清单、未完成实验、批准与发布记录、费用口径、异常处理步骤、备份恢复记录,以及已知未验证项。秘密通过获准的安全渠道管理,不夹在这个包里。
让接手人独立完成一次本地复现、一次来源纠错、一项待审核差异和一次恢复演练。若必须靠原作者口头解释才能完成,补文档或简化流程。一个真正可运营的闭环,应当在模型失败、来源过期和负责人休息时仍能安全停住,并让下一次继续有据可查。
本章依据
- [D01] Python 3.12 http.server,仅作为本地检查工具,非生产推荐。
- [D02] Google Search Console 入门,访问 2026-10-14。
- [D03] GitHub GITHUB_TOKEN 最小权限、安全使用 Actions。
- [D04] GitHub schedule 触发规则,访问 2026-10-14。
- [D05] SQLite 备份 API、Python 3.12 sqlite3。
- 原型合同:
prototype/seo_loop/cli.py、store.py、policy.py、engine.py、models.py;测试:prototype/tests/。生产扩展与排期均为本书方案,未在真实账户实施。