Skip to content

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
放大图示

原始尺寸下可横向或纵向滚动;按 Escape 关闭。

正在绘制图示…

查看图源代码
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')
PY

SQLite 的备份 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 交给下一位维护者的最小包 ​

最终交接至少包含:资源清单与负责人、代码和配置版本、数据字典、当前页面及来源清单、未完成实验、批准与发布记录、费用口径、异常处理步骤、备份恢复记录,以及已知未验证项。秘密通过获准的安全渠道管理,不夹在这个包里。

让接手人独立完成一次本地复现、一次来源纠错、一项待审核差异和一次恢复演练。若必须靠原作者口头解释才能完成,补文档或简化流程。一个真正可运营的闭环,应当在模型失败、来源过期和负责人休息时仍能安全停住,并让下一次继续有据可查。

本章依据 ​

保留各书来源与许可 · 示例运行条件以正文为准