没有不值得去解决的问题,也没有不值得去学习的技术!

每天批量处理 20 篇 WordPress 历史文章:代码块迁移、摘要补全与英文覆盖翻译完整 SOP

图1:仓库显示所有历史批次已经完成,并允许创建下一批。

作者:

WordPress AI 翻译工程实战

WordPress 经典编辑器代码块无损迁移:批量转换 SyntaxHighlighter 短代码为 Gutenberg 区块

(1) WordPress 经典编辑器代码块无损迁移:批量转换 SyntaxHighlighter 短代码为 Gutenberg 区块

点击翻译按钮后,遇到一个弹窗“神秘”闪退问题及解决方法

(2) 点击翻译按钮后,遇到一个弹窗“神秘”闪退问题及解决方法

图5:DeepL API 注册页面,国家或地区列表中没有中国大陆

(3) 中国大陆 WordPress 博客想提升英文翻译质量,为什么这么麻烦?一次 Polylang + AutoPoly + DeepL API 排查复盘

图5:DeepSeek 的评分结果截图

(4) Chrome Built-in AI、Yandex Translate、ChatGPT Plus 翻译质量对比:我为什么决定重点英文文章改用 ChatGPT Plus

[截图 2:浏览器 Network 中直接请求 translate.yandex.net]

(5) 从 AutoPoly 到 Yandex 网页版:一次 WordPress 英文自动翻译质量验证

【图1:AutoPoly Free 与 Pro 功能对比截图】

(6) AutoPoly Pro + OpenAI API 能否自动化 WordPress 英文翻译?一次从技术到支付的可行性分析

【图2:点击“立即试用”后,ChatGPT 输入框中出现“代理模式”】

(7) ChatGPT Agent 能否替代 ChatGPT Plus 完成 WordPress 英文重译?

【图 7,SlyTranslate 显示翻译完成并创建文章 19426】

(8) SlyTranslate + 智谱 GLM-5.2 长文翻译排障:从 600 秒超时到完整生成英文 WordPress 文章

【图 5,54 个代码块精确匹配、差异数量为 0】

(9) WordPress SlyTranslate + GLM-5.2 长文单次翻译实战:排查 invalid_translation_runaway_output 假失败

【图 2,SlyTranslate 的 Additional Instructions 保持为空】

(10) SlyTranslate + GLM-5.2 翻译质量继续优化:从关闭随机采样、内置提示词到深度思考与模型 A/B 测试

【图6,终端中的完整测试结果】

(11) 在 GLM 5.2 之后测试 DeepSeek:一次面向 WordPress 长文翻译的模型对比与取舍

【图 4,“英文博客翻译质量优化”项目中已经整理完成的会话列表】

(12) 为英文博客翻译质量优化建立独立 ChatGPT 项目:整理历史会话并统一后续评测标准

【图3:Gemini 3.5 Flash 首次成功调用结果】

(13) 暂缓 OpenAI 后,我在本地测试了 Gemini:与 GLM 5.2 的 WordPress 技术文章翻译对比

图3:清理英文 Host 下的对象与术语关系缓存后,英文文章重新显示 SEO Tools、Yoast SEO 和 Blog SEO Log

(14) 为 SlyTranslate 的 GLM 5.2 整篇翻译补上 Plaintext 智能处理,并修复 Code Block Pro 区块失效

图5:英文文章发布后,前台特色图片、分类、标签和 Series 顺序均正常

(15) SlyTranslate + GLM-5.2 翻译 WordPress 长文章后,分类、标签、Series 与特色图片同步问题排查

图1:Codex 输出 Token 校验诊断结果

(16) SlyTranslate 全文翻译报错排查:完善 GLM 5.2 的段落与占位符约束

图1:创建 wordpress-ai-translation-pipeline 私有 GitHub 仓库

(17) 将 WordPress AI 翻译优化项目提交到 GitHub:一次阶段性的收尾

图1:文章列表中因摘要为空而重复出现两个 Post Views

(18) 从空摘要、历史格式混杂到重新翻译:我如何规划 WordPress AI 翻译工程的下一阶段

图4:最终批处理输出 42/42 完成、待处理 0、异常状态 0。

(19) 从只读审计到 42/42 完成:用 GLM 与 SlyTranslate 安全补全 WordPress 历史文章摘要

图3:GitHub 已正确识别仓库的 MIT License。

(20) 将 WordPress AI 翻译优化仓库从私有切换为公共:从敏感信息审计到最小改动发布

图1:仓库显示所有历史批次已经完成,并允许创建下一批。

(21) 每天批量处理 20 篇 WordPress 历史文章:代码块迁移、摘要补全与英文覆盖翻译完整 SOP

图 2:复杂 Classic 一次转换成多个 Gutenberg 区块

(22) 每天批量处理 20 篇 Mixed WordPress 历史文章:Classic 转 Gutenberg、SyntaxHighlighter 迁移、摘要补全与英文覆盖翻译完整 SOP

GLM-5.2 整篇翻译再次出现 Protected Token 校验失败:从尾部 Token 丢失到 Plaintext 结构重排的完整排查记录

(23) GLM-5.2 整篇翻译再次出现 Protected Token 校验失败:从尾部 Token 丢失到 Plaintext 结构重排的完整排查记录

图 1:历史文章 4652 在 GLM-5.2 整篇翻译时返回 HTTP 400 安全检测错误

(24) GLM-5.2 整篇翻译持续返回 HTTP 400:从安全检测拦截到 Plaintext 载荷缩减的完整排查与修复

图 1:历史文章批处理返回 HTTP 400,并提示内容安全检查未通过。

(25) GLM-5.2 整篇翻译返回 400:一次内容安全误拦截的逐步定位与恢复记录

图 5:文章最终已经 completed,但原来的 excerpt_generation_failed、HTTP 400 和人工完成记录同时保留。

(26) WordPress 历史文章自动化遇到 GLM 1301:从 HTTP 400 到 ChatGPT 人工兜底完成

图 1:index 814 的 INLINE Token 换序及中英文 Gutenberg 对照

(27) WordPress AI 翻译排错实战:一次合法的行内代码换序,为什么会阻断尾部结构自动修复?

图 1:完整文章首次整篇翻译失败,SlyTranslate 返回受保护标记校验错误

(28) WordPress 超长 Gutenberg 文章整篇 AI 翻译踩坑:从 1000+ 受保护标记到完整翻译成功

【图1:历史文章迁移最终状态,67 个固定批次、1281 篇文章全部完成】

(29) WordPress 历史文章迁移终于收尾:全量审计 1438 篇中文文章,并停用 SyntaxHighlighter Evolved

我的 WordPress 博客积累了大量历史技术文章。其中一部分文章仍然使用旧的 SyntaxHighlighter 代码区块,中文摘要为空,对应的英文文章也需要重新覆盖翻译。

如果完全依赖人工处理,每篇文章都要经历:

Plaintext
转换代码区块
→ 核对代码语言
→ 生成中文摘要
→ 点击覆盖翻译
→ 等待翻译完成
→ 检查结果

英文覆盖翻译通常还需要等待一段时间。一天处理 20 篇时,人工会不断在 WordPress 后台、终端和翻译状态之间来回切换。

因此,我最终整理出一套相对稳定的批处理流程:

Plaintext
仓库筛选并固定当天的 20 篇文章
→ 人工迁移 SyntaxHighlighter,并对 Code Block Pro 核对代码语言
→ 仓库记录人工转换完成
→ 仓库执行生产只读验证
→ 批量生成中文摘要
→ 批量覆盖英文译文
→ 单篇失败有限重试
→ 分析最终失败原因
→ 修复根因并恢复失败文章
→ 确认整个批次全部完成

本文既记录这套流程的形成过程,也是一份可以在以后每天直接照着执行的操作手册。


一、两个仓库分别负责什么

这套流程涉及两个独立仓库。

1. WordPress 翻译管线仓库

Plaintext
/home/wangqiang/code/wordpress-ai-translation-pipeline

主要负责:

  • SlyTranslate 与 GLM 翻译定制;
  • Gutenberg 结构保护;
  • HTML、短代码和代码块保护;
  • Plaintext 可翻译区域;
  • 受保护令牌验证;
  • 英文覆盖翻译;
  • WordPress 生产 MU 插件。

2. 历史文章摘要补全仓库

Plaintext
/home/wangqiang/code/wordpress-ai-excerpt-backfill

主要负责:

  • 筛选待处理历史文章;
  • 创建固定批次;
  • 保存中英文文章关系;
  • 生成中文摘要;
  • 调用生产环境覆盖翻译;
  • 保存写入前备份;
  • 记录执行状态;
  • 批量重试与恢复;
  • 汇总整个批次的完成情况。

两个仓库的职责不能混在一起:

Plaintext
翻译结构问题
→ wordpress-ai-translation-pipeline

摘要生成和批次调度问题
→ wordpress-ai-excerpt-backfill

二、人工和仓库的职责边界

仓库负责

仓库负责:

  • 筛选摘要为空的中文文章;
  • 排除已经进入旧批次的文章;
  • 确认文章存在已发布的英文译文;
  • 固定每天需要处理的 20 篇文章;
  • 保存中文文章 ID 和英文文章 ID;
  • 保存后台编辑地址;
  • 记录原 SyntaxHighlighter 数量;
  • 以原 SyntaxHighlighter 数量作为迁移后 Code Block Pro 数量上限;
  • 执行生产只读验证;
  • 调用 GLM 生成中文摘要;
  • 写入中文摘要;
  • 调用 SlyTranslate 覆盖英文文章;
  • 保存执行证据和备份;
  • 对单篇失败进行有限重试;
  • 汇总 completed、failed 和 remaining。

人工负责

人工只负责:

  • 根据仓库清单打开文章;
  • 逐块判断旧 SyntaxHighlighter 内容:代码、命令和日志通常迁移为 Code Block Pro;更适合其他 Gutenberg 区块的内容可以迁移为对应区块;
  • 检查代码内容是否完整;
  • 逐块核对代码语言;
  • 检查 Gutenberg 是否存在损坏区块;
  • 保存文章;
  • 明确确认人工转换已经完成。

人工不再负责:

  • 按发布时间寻找文章;
  • 逐篇判断摘要是否为空;
  • 手动点击每一篇覆盖翻译;
  • 守着 WordPress 后台等待翻译完成。

最终职责边界可以概括为:

Plaintext
仓库决定处理哪些文章
→ 人工完成必须人工判断的代码块迁移
→ 仓库完成验证、摘要生成和英文翻译

三、完整状态流转

每篇文章必须按照以下顺序推进:

Plaintext
awaiting_manual_conversion
→ mark-converted
→ awaiting_readonly_validation
→ validate-live
→ ready_for_execution
→ run-ready --execute
→ completed

上面是正常主路径。真实批处理遇到网络中断时,还可能暂时停在 excerpt_generatedready_for_translation_resumeblocked。这些状态不能统一用一个 resume 命令处理:ready_for_execution 继续交给 run-ready;明确处于翻译续跑阶段时才使用 resumeblocked 则先核对生产实际状态,再决定是否执行协调恢复。

其中最容易遗漏的是:

Plaintext
mark-converted

即使已经在 WordPress 后台完成代码块转换,仓库也不会自动知道这件事。

如果文章仍然处于:

Plaintext
awaiting_manual_conversion

就直接运行:

Plaintext
validate-live

仓库会拒绝验证:

Plaintext
ERROR: cannot validate live from awaiting_manual_conversion

因此,人工转换完成后,必须先将结果记录到本地协调状态,再执行生产只读验证。


四、每天开始前检查仓库状态

进入仓库:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

执行:

Bash
python3 bin/history-migration.py status
python3 bin/history-migration.py summary

重点查看:

Plaintext
最新未完成批次
下一步
建议创建下一批

如果某一轮看到:

Plaintext
建议创建下一批: False
建议: continue latest incomplete batch; do not create a new batch

说明仍然存在未完成批次,不能创建新的 20 篇。

只有看到:

Plaintext
最新未完成批次: 无
建议创建下一批: True
建议: all batches complete

才可以创建下一批。

需要注意:

Bash
python3 bin/history-migration.py summary

是全局汇总命令,不支持:

Plaintext
--batch-id
图1:仓库显示所有历史批次已经完成,并允许创建下一批。
图1:仓库显示所有历史批次已经完成,并允许创建下一批。

五、创建新的 20 篇固定批次

确认所有旧批次完成后,在普通终端执行。这里必须读取 summary 的结果,因为 建议创建下一批: True 来自 summary,不是 status

Bash
cd ~/code/wordpress-ai-excerpt-backfill

summary_output="$(
    python3 bin/history-migration.py summary
)"

printf '%s\n' "$summary_output"

if ! grep -q '^建议创建下一批: True$' <<<"$summary_output"; then
    echo
    echo "当前不允许创建下一批,请先完成最新未完成批次。"
else
    date_tag="$(date +%Y%m%d)"
    batch_id=""
    batch_file=""

    for number in $(seq -w 1 99)
    do
        candidate_id="syntaxhighlighter-${date_tag}-${number}"
        candidate_file="data/analysis/syntaxhighlighter-migration-batch-${date_tag}-${number}.csv"
        candidate_state_dir="data/state/history-migration/${candidate_id}"

        if [ ! -e "$candidate_file" ] && [ ! -d "$candidate_state_dir" ]; then
            batch_id="$candidate_id"
            batch_file="$candidate_file"
            break
        fi
    done

    if [ -z "$batch_id" ]; then
        echo "无法生成可用的新批次编号。"
    else
        mapfile -t preview_files < <(
            find data/analysis \
                -maxdepth 1 \
                -type f \
                -name '*syntaxhighlighter*preview*.csv' \
                -printf '%T@ %p\n' \
            | sort -nr \
            | cut -d' ' -f2-
        )

        if [ "${#preview_files[@]}" -eq 0 ]; then
            echo "没有找到 SyntaxHighlighter preview CSV。"
        else
            preview_file="${preview_files[0]}"

            echo
            echo "使用候选文件:$preview_file"
            echo "新批次 ID:$batch_id"
            echo "新批次文件:$batch_file"

            python3 bin/build-syntaxhighlighter-batch.py \
                --preview "$preview_file" \
                --output "$batch_file" \
                --expected-count 20 \
                --batch-id "$batch_id" \
                --pilot-manifest \
                    data/analysis/gutenberg-syntaxhighlighter-migration-pilot-candidates.csv \
                --old-phase1-manifest \
                    data/analysis/gutenberg-cbp-empty-excerpt-candidates.csv \
            && python3 bin/history-migration.py init-state --apply \
            && echo \
            && echo "新批次已经创建:" \
            && echo "batch_id=\"$batch_id\"" \
            && echo "csv_file=\"$batch_file\""
        fi
    fi
fi

正常完成后,会输出类似:

Plaintext
batch_id="syntaxhighlighter-20260725-01"
csv_file="data/analysis/syntaxhighlighter-migration-batch-20260725-01.csv"

记下这两个值。后面的命令都需要使用它们。

这里不要再提交批次 CSV。当前 data/analysis/ 属于本地分析数据并被 Git 忽略;当天固定批次 CSV 和 data/state/history-migration/ 都继续保留在本地即可。

尤其不要为了强行提交这个 CSV 使用 git add -f,也不要执行:

Bash
git add .
git add -A
git clean
git reset --hard

真正需要提交的只有后续确实发生的源码、测试或文档修改,并且应精确指定文件。


六、查看文章清单和后台编辑地址

设置当天批次变量:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

batch_id="syntaxhighlighter-20260725-01"
csv_file="data/analysis/syntaxhighlighter-migration-batch-20260725-01.csv"

执行:

Bash
python3 - "$batch_id" "$csv_file" <<'PY'
import csv
import json
import sys
from pathlib import Path

batch_id = sys.argv[1]
csv_path = Path(sys.argv[2])
state_dir = Path("data/state/history-migration") / batch_id

with csv_path.open(
    encoding="utf-8-sig",
    newline="",
) as handle:
    rows = list(csv.DictReader(handle))

remaining = []

for row in rows:
    post_id = int(row["chinese_post_id"])
    state_path = state_dir / f"chinese-{post_id}.json"

    state = {}

    if state_path.is_file():
        state = json.loads(
            state_path.read_text(encoding="utf-8")
        )

    workflow_status = state.get(
        "workflow_status",
        "uninitialized",
    )

    if workflow_status == "completed":
        continue

    edit_url = row.get("edit_url") or (
        "https://admin.shuijingwanwq.com/wp-admin/"
        f"post.php?post={post_id}&action=edit"
    )

    remaining.append(
        {
            "post_id": post_id,
            "english_id": row["english_post_id"],
            "title": row["chinese_title"],
            "syntax_before": row[
                "before_syntaxhighlighter_count"
            ],
            "cbp_expected": row[
                "expected_code_block_pro_count_after"
            ],
            "status": workflow_status,
            "edit_url": edit_url,
        }
    )

print(f"批次:{batch_id}")
print(f"批次总数:{len(rows)}")
print(f"当前待处理:{len(remaining)}")
print()

for index, item in enumerate(remaining, 1):
    print(
        f"{index:02d}. "
        f"zh={item['post_id']} "
        f"en={item['english_id']} "
        f"SH={item['syntax_before']} "
        f"预期CBP={item['cbp_expected']} "
        f"status={item['status']}"
    )
    print(f"    标题:{item['title']}")
    print(f"    编辑:{item['edit_url']}")
    print()
PY

新批次应显示:

Plaintext
批次总数:20
当前待处理:20
图2:仓库列出当天 20 篇文章、代码块数量和后台编辑地址。
图2:仓库列出当天 20 篇文章、代码块数量和后台编辑地址。

七、人工迁移 SyntaxHighlighter

按照清单中的后台地址逐篇打开中文文章。这里的目标不是机械地把每一个 SyntaxHighlighter 都变成 Code Block Pro,而是把旧区块迁移到更合适的 Gutenberg 结构。

每篇文章需要完成:

  • 逐块判断旧 SyntaxHighlighter 内容:代码、命令、日志等通常迁移为 Code Block Pro;更适合其他 Gutenberg 区块的内容可以迁移为对应区块;
  • 确保最终 SyntaxHighlighter 数量为 0;
  • 检查迁移后的内容是否完整,没有因为转换而丢失代码、命令、日志或正文;
  • 凡是迁移成 Code Block Pro 的区块,都逐块核对代码语言;
  • 检查 Gutenberg 是否存在损坏区块;
  • 保存文章。

原区块明确声明了语言

迁移为 Code Block Pro 时,设置为对应语言,例如:

  • PHP
  • JavaScript
  • Bash
  • JSON
  • HTML
  • CSS
  • SQL
  • Python
  • Nginx
  • YAML

.yml.yaml 文件都选择:

Plaintext
YAML

原区块没有声明语言

如果该内容仍然适合 Code Block Pro,但原区块没有声明语言,则设置为:

Plaintext
Plaintext

Code Block Pro 有时会继承上一次使用的语言,因此保存前必须逐块核对。

每篇保存前应确认:

Plaintext
SyntaxHighlighter 数量已经归零
Code Block Pro 数量不超过批次记录的原 SyntaxHighlighter 数量
允许部分旧区块迁移为其他更合适的 Gutenberg 区块
迁移后的内容没有丢失
Code Block Pro 的语言设置正确
Gutenberg 没有损坏区块提示

因此,生产只读验证采用的核心数量规则是:SH = 0,并且 CBP ≤ 原 SH 数量。Code Block Pro 比原 SyntaxHighlighter 少并不自动代表迁移失败。

不需要为了保险,每篇保存后都额外切到源代码编辑器搜索 SyntaxHighlighter。只有只读验证明确返回 syntaxhighlighter-remains 时,再进入源代码编辑器搜索旧区块并修复即可。

图3:历史文章中原有的 SyntaxHighlighter 代码区块。
图3:历史文章中原有的 SyntaxHighlighter 代码区块。
图4:转换为 Code Block Pro 后,逐个核对代码语言。
图4:转换为 Code Block Pro 后,逐个核对代码语言。

八、记录人工转换已经完成

WordPress 后台保存成功后,还需要将人工完成结果写入本地协调状态。

本节负责:

Plaintext
awaiting_manual_conversion
→ awaiting_readonly_validation

执行:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

batch_id="syntaxhighlighter-20260725-01"
csv_file="data/analysis/syntaxhighlighter-migration-batch-20260725-01.csv"

read -r -p \
  "确认当前批次待处理文章均已完成代码块转换和逐块语言核对,输入 YES 继续:" \
  answer

if [ "$answer" != "YES" ]; then
    echo "未确认,已停止,没有修改状态。"
else
    python3 - "$batch_id" "$csv_file" <<'PY'
import csv
import importlib.util
import json
import sys
from pathlib import Path

root = Path.cwd().resolve()
batch_id = sys.argv[1]
csv_path = Path(sys.argv[2])
state_dir = (
    root
    / "data/state/history-migration"
    / batch_id
)

module_path = root / "bin/history-migration.py"

spec = importlib.util.spec_from_file_location(
    "history_migration_runtime",
    module_path,
)

if spec is None or spec.loader is None:
    raise SystemExit(
        "无法加载 history-migration.py"
    )

module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)

with csv_path.open(
    encoding="utf-8-sig",
    newline="",
) as handle:
    rows = list(csv.DictReader(handle))


def read_state(post_id):
    state_path = (
        state_dir
        / f"chinese-{post_id}.json"
    )

    if not state_path.is_file():
        raise RuntimeError(
            f"缺少状态文件:{state_path}"
        )

    return json.loads(
        state_path.read_text(encoding="utf-8")
    )


targets = []

for row in rows:
    post_id = int(row["chinese_post_id"])
    status = read_state(post_id).get(
        "workflow_status"
    )

    if status == "awaiting_manual_conversion":
        targets.append(row)

print(f"批次:{batch_id}")
print(f"等待记录人工转换:{len(targets)}")
print()

success = []
failed = []

for index, row in enumerate(targets, 1):
    post_id = int(row["chinese_post_id"])
    syntax_before = int(
        row["before_syntaxhighlighter_count"]
    )
    cbp_after = int(
        row[
            "expected_code_block_pro_count_after"
        ]
    )

    try:
        module.mark_converted(
            root,
            post_id,
            syntax_before,
            cbp_after,
            True,
        )

        final_status = read_state(
            post_id
        ).get("workflow_status")

        if (
            final_status
            == "awaiting_readonly_validation"
        ):
            success.append(post_id)
            print(
                f"[{index}/{len(targets)}] "
                f"已记录:zh={post_id} "
                f"status={final_status}"
            )
        else:
            failed.append(post_id)
            print(
                f"[{index}/{len(targets)}] "
                f"状态异常:zh={post_id} "
                f"status={final_status}"
            )

    except Exception as error:
        failed.append(post_id)
        print(
            f"[{index}/{len(targets)}] "
            f"记录失败:zh={post_id} "
            f"{type(error).__name__}: {error}"
        )

print()
print("========== 人工转换记录汇总 ==========")
print(f"待记录:{len(targets)}")
print(f"成功:{len(success)}")
print(f"失败:{len(failed)}")

if failed:
    print(
        "失败文章:"
        + ", ".join(
            str(value) for value in failed
        )
    )
    raise SystemExit(1)
PY
fi

新批次全部记录成功后,应显示:

Plaintext
========== 人工转换记录汇总 ==========
待记录:20
成功:20
失败:0

这一步只更新本地状态,不会:

  • 调用 GLM;
  • 调用 SlyTranslate;
  • 修改 WordPress;
  • 生成中文摘要;
  • 覆盖英文文章。

九、执行生产只读验证

本节负责:

Plaintext
awaiting_readonly_validation
→ ready_for_execution

validate-live 是单篇命令,只支持 --post-id,不支持 --batch-id。批次验证仍然由脚本读取当天 CSV,再逐篇调用。

先确认登录环境变量仍然存在:

Bash
for name in WP_ADMIN_COOKIE WP_REST_NONCE
do
    if [ -z "${!name-}" ]; then
        echo "缺少环境变量:$name"
    else
        echo "已设置:$name"
    fi
done

如果当前终端缺少 Cookie 或 Nonce,不要把值粘贴到聊天记录或 Shell 命令参数里。可以在当前终端静默输入:

Bash
read -rsp "粘贴 Cookie,然后回车:" WP_ADMIN_COOKIE
echo
export WP_ADMIN_COOKIE

read -rsp "粘贴 X-WP-Nonce,然后回车:" WP_REST_NONCE
echo
export WP_REST_NONCE

Cookie 和 X-WP-Nonce 可以从浏览器开发者工具的 Network 请求中重新取得。确认两项均已设置后,再执行批次只读验证:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

batch_id="syntaxhighlighter-20260725-01"
csv_file="data/analysis/syntaxhighlighter-migration-batch-20260725-01.csv"

missing=0

for name in WP_ADMIN_COOKIE WP_REST_NONCE
do
    if [ -z "${!name-}" ]; then
        echo "缺少环境变量:$name"
        missing=1
    fi
done

if [ "$missing" -ne 0 ]; then
    echo "已停止,未执行生产只读验证。"
else
    python3 - "$batch_id" "$csv_file" <<'PY'
import csv
import json
import subprocess
import sys
from pathlib import Path

batch_id = sys.argv[1]
csv_path = Path(sys.argv[2])
state_dir = (
    Path("data/state/history-migration")
    / batch_id
)

with csv_path.open(
    encoding="utf-8-sig",
    newline="",
) as handle:
    rows = list(csv.DictReader(handle))


def read_state(post_id):
    state_path = (
        state_dir
        / f"chinese-{post_id}.json"
    )

    if not state_path.is_file():
        raise RuntimeError(
            f"缺少状态文件:{state_path}"
        )

    return json.loads(
        state_path.read_text(encoding="utf-8")
    )


targets = []
already_ready = []
completed = []
unexpected = []

for row in rows:
    post_id = int(row["chinese_post_id"])
    status = read_state(post_id).get(
        "workflow_status"
    )

    if status == "awaiting_readonly_validation":
        targets.append(post_id)
    elif status == "ready_for_execution":
        already_ready.append(post_id)
    elif status == "completed":
        completed.append(post_id)
    else:
        unexpected.append(
            (post_id, status)
        )

print(f"批次:{batch_id}")
print(f"本次待验证:{len(targets)}")
print(f"已经 ready:{len(already_ready)}")
print(f"已经 completed:{len(completed)}")
print()

success = []
failed = []

for index, post_id in enumerate(targets, 1):
    print(
        f"[{index}/{len(targets)}] "
        f"开始只读验证:zh={post_id}"
    )

    result = subprocess.run(
        [
            sys.executable,
            "bin/history-migration.py",
            "validate-live",
            "--post-id",
            str(post_id),
        ],
        text=True,
        capture_output=True,
        check=False,
    )

    final_status = read_state(
        post_id
    ).get("workflow_status")

    if (
        result.returncode == 0
        and final_status
        == "ready_for_execution"
    ):
        success.append(post_id)
        print(
            f"[{index}/{len(targets)}] "
            f"验证通过:zh={post_id} "
            f"status={final_status}"
        )
    else:
        failed.append(post_id)

        output = (
            result.stderr
            or result.stdout
            or ""
        ).strip().splitlines()

        error = (
            output[-1]
            if output
            else "没有错误摘要"
        )

        print(
            f"[{index}/{len(targets)}] "
            f"验证失败:zh={post_id} "
            f"status={final_status} "
            f"returncode={result.returncode} "
            f"error={error}"
        )

    print()

status_counts = {}

for row in rows:
    post_id = int(row["chinese_post_id"])
    status = read_state(post_id).get(
        "workflow_status",
        "unknown",
    )

    status_counts[status] = (
        status_counts.get(status, 0) + 1
    )

print("========== 只读验证最终汇总 ==========")
print(f"本次待验证:{len(targets)}")
print(f"本次通过:{len(success)}")
print(f"本次失败:{len(failed)}")
print(
    "批次当前 ready:"
    f"{status_counts.get('ready_for_execution', 0)}"
)
print(
    "批次已经 completed:"
    f"{status_counts.get('completed', 0)}"
)

if unexpected:
    print(
        "非预期状态:"
        + ", ".join(
            f"{post_id}:{status}"
            for post_id, status in unexpected
        )
    )

if failed:
    print(
        "验证失败文章:"
        + ", ".join(
            str(value) for value in failed
        )
    )

if failed or unexpected:
    raise SystemExit(1)
PY

    result=$?

    echo

    if [ "$result" -eq 0 ]; then
        echo "========== 验证后的待执行文章 =========="

        python3 bin/history-migration.py run-ready \
            --batch-id "$batch_id"
    else
        echo "存在验证失败或非预期状态,暂不进入正式执行。"
    fi
fi

新批次全部通过时,应看到 20 篇进入 ready_for_execution,并且 run-ready 预览为 selected_count=20allowed_count=20

只读验证的关键迁移规则是:

Plaintext
SyntaxHighlighter = 0
Code Block Pro <= 原 SyntaxHighlighter 数量
Gutenberg 结构完整
未知代码格式为 0
中英文文章关系、状态和现有哈希检查继续通过

如果某篇只是 SSH、Timeout、RemoteDisconnected 等网络故障,且没有形成新的验证证据,直接对这一篇重新执行普通 validate-live 即可:

Bash
python3 bin/history-migration.py validate-live --post-id 文章ID

如果某篇已经进入 validation_failed,并且已经在 WordPress 后台人工修复内容,则必须强制重新读取生产环境,使用:

Bash
python3 bin/history-migration.py validate-live --post-id 文章ID --refresh

例如出现 syntaxhighlighter-remains 时,再进入源代码编辑器搜索 <!-- wp:syntaxhighlighter/code,修复残留旧区块后执行 --refresh

图5:20 篇文章完成生产只读验证,全部进入 ready 状态。
图5:20 篇文章完成生产只读验证,全部进入 ready 状态。

十、批量生成摘要并覆盖英文译文

正式执行需要三个环境变量:

Plaintext
WP_ADMIN_COOKIE
WP_REST_NONCE
ZHIPU_API_KEY

如果 ZHIPU_API_KEY 在新的终端会话中不存在,可以静默输入并导出:

Bash
read -rsp "粘贴 ZHIPU_API_KEY,然后回车:" ZHIPU_API_KEY
echo
export ZHIPU_API_KEY

确认三个变量均已设置后执行:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

batch_id="syntaxhighlighter-20260725-01"
missing=0

for name in WP_ADMIN_COOKIE WP_REST_NONCE ZHIPU_API_KEY
do
    if [ -z "${!name-}" ]; then
        echo "缺少环境变量:$name"
        missing=1
    else
        echo "已设置:$name"
    fi
done

if [ "$missing" -ne 0 ]; then
    echo "已停止,未执行批次。"
else
    python3 bin/history-migration.py run-ready \
        --batch-id "$batch_id" \
        --execute
fi

执行过程中会实时显示“第 1/20 篇”“第 2/20 篇”等进度;单篇失败后会有限次数自动重试,最终失败也不会立即终止整个批次,而是继续处理后面的文章。

现在的 excerpt_generated 恢复逻辑已经补齐。某次运行已经生成摘要、但后续网络中断时,下一次尝试会先只读观察生产中文摘要:

  • 生产中文摘要非空:自动切换到翻译续跑,不重新调用 GLM;
  • 生产中文摘要仍为空:重新走普通 run,允许再次调用 GLM 生成并覆盖摘要;
  • 只读观察遇到 Timeout、RemoteDisconnected、HTTP 502/503:不猜测、不错误推进状态,在当前有限次数内继续重试;
  • 已经 completed 的文章不会重新执行。

这里的优先级是可靠完成高于极致节省 Token。只要生产摘要确认仍为空,重新调用一次 GLM 是可以接受的;但摘要已经保存时会直接续跑翻译。

批次命令结束后,不要因为还有未完成文章就立刻把 resume 当成统一恢复命令。先查看 statussummaryshow-current,再根据实际状态选择 run-readyresume 或协调恢复。

图6:终端实时显示中文摘要生成和英文覆盖翻译进度。
图6:终端实时显示中文摘要生成和英文覆盖翻译进度。

十一、有限重试能解决什么

当前批量入口会对单篇文章进行有限次数自动尝试,并在单篇最终失败后继续处理下一篇。这对于无人值守批次非常重要。

它适合解决:

  • 临时请求超时;
  • RemoteDisconnected 等短暂网络失败;
  • 偶发 HTTP 502 或 503;
  • 服务端短时间不可用;
  • 一次性的模型响应异常;
  • 摘要已生成后发生的网络中断,只要生产实际状态能够安全确认。

但它不能替代确定性问题的根因修复,例如提示词与校验规则冲突、占位符遗漏、Gutenberg 结构损坏、校验正则误判等。

三次尝试后仍有未完成文章时,先看协调状态,而不是统一执行 resume

Plaintext
ready_for_execution
→ 继续使用 run-ready

ready_for_translation_resume / translation_failed
→ 使用 resume

blocked
→ 先核对 last_failure 和生产实际状态,再做协调恢复

excerpt_failed
→ 先检查被拒绝摘要或确定性校验原因

resume 不是“所有未完成文章的统一恢复命令”。例如文章仍是 ready_for_execution 时,直接执行 resume 可能得到 selected_count: 0,这并不代表批次已经完成。


十二、几轮真实批次中暴露的三个问题

此前一个 20 篇批次首次批量执行后:

Plaintext
16 篇完成
3 篇 translation_failed
1 篇 excerpt_failed

表面上看,最终失败率达到了 20%。

进一步检查后发现,4 篇失败并不是随机网络问题,而是两个确定性规则问题。

1. Plaintext 行数校验过严

三篇文章分别报错:

Plaintext
Plaintext line count changed during translation:
source=3, translated=1

Plaintext line count changed during translation:
source=11, translated=10

Plaintext line count changed during translation:
source=94, translated=93

中文翻译成英文时,模型自然可能合并或拆分换行。

因此,对于允许翻译的普通 Plaintext 内容,不应该要求译前译后物理行数完全相同。

最终修复为:

  • 取消可翻译 Plaintext 的逐行等长要求;
  • 保留 START/STOP 区域边界;
  • 保留令牌数量、唯一性、编号和顺序校验;
  • 非空 Plaintext 不能整体变空;
  • Plaintext 不能吞入相邻 Gutenberg 或受保护区域;
  • 代码、命令、路径、URL、状态码等不可翻译内容继续原样保护;
  • 结构错误使用 HTTP 422 和 retryable=false

生产 MU 插件版本更新为:

Plaintext
2026.07.24.18

提示词策略版本更新为:

Plaintext
2026-07-24-v12

离线测试结果:

Plaintext
115 项通过
0 项失败

部署后,三篇失败文章依次执行:

Bash
python3 bin/history-migration.py resume \
    --post-id 文章ID \
    --execute

三篇全部成功完成。

2. Ctrl + S 被误判为 Markdown 列表

剩余一篇文章生成的摘要实际上是正常单段文本,例如:

Plaintext
在 VS Code 中按下 Ctrl + S 时文件内容意外被还原……

但本地校验器使用的旧正则是:

Python
r"(^|\s)(?:#{1,6}\s|[-*+]\s|\d+[.)]\s)|[*_]{2}"

其中:

Plaintext
(^|\s)

允许匹配正文任意位置的空白。

因此:

Plaintext
Ctrl + S

中的:

 + 

被误判成 Markdown 的 + 项目符号。

修复后的正则只允许在行首识别列表:

Python
r"(?m)^[ \t]*(?:#{1,6}[ \t]+|[-*+][ \t]+|\d+[.)][ \t]+)"

修复后:

  • Ctrl + S 不再误判;
  • A + B 不再误判;
  • x - y 不再误判;
  • C++ 不再误判;
  • 行首的 - 第一项+ 第一项1. 第一项 仍然会被拒绝。

同时,GLM 初始摘要提示词也明确要求:

  • 一个连续的中文纯文本段落;
  • 不使用 Markdown;
  • 不使用项目符号和编号列表;
  • 不使用标题、引用、表格或代码块;
  • 不换行;
  • 不添加“摘要:”等前缀;
  • 内容可以直接保存到 WordPress post_excerpt

完整测试结果:

Plaintext
334 项通过
0 项失败

3. excerpt_generated 后恢复到普通 run,导致摘要非空时反复预检失败

随后一次严格按照本文 SOP 进行的 20 篇真实演练,又暴露了一个恢复分支问题。首次执行后有文章停在 execution=excerpt_generated:GLM 已经生成摘要,甚至摘要已经成功写入 WordPress,但后续 GET、翻译请求或网络连接发生中断。

旧逻辑会把 preparedexcerpt_generated 都重新映射成普通 run/restart。如果生产摘要实际上已经保存,下一次普通 preflight 会因为:

Plaintext
chinese_excerpt_empty: false
preflight_passed: false

而稳定失败,继续重试也只是在重复同一个错误。

最终修复保持得很小:重试 excerpt_generated 前,先通过现有只读生产数据源确认中文摘要实际状态:

  • 摘要已保存:进入 ready_for_translation_resume,自动使用 resume 继续英文覆盖翻译;
  • 摘要仍为空:回到 ready_for_execution,允许重新调用 GLM;
  • 生产状态暂时无法确认:记录为可重试的观察失败,不盲目执行 run 或 resume。

修复后,剩余 4 篇真实文章全部在下一轮第一次尝试中完成;完整测试套件为 344 项通过。对应修复提交为 b02bbf4


十三、批次结束后的完整检查

批量执行结束后,不能只看:

Plaintext
selected_count: 0

因为它只代表当前没有处于 ready_for_execution 的文章,不一定代表全部完成。

必须同时执行:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

batch_id="syntaxhighlighter-20260725-01"

python3 bin/history-migration.py run-ready \
    --batch-id "$batch_id"

python3 bin/history-migration.py status
python3 bin/history-migration.py summary

真正完成时,应同时满足:

Plaintext
selected_count: 0
allowed_count: 0
completed=20
remaining=0
excerpt_failed=0
translation_failed=0
next_action=当前批次已完成

十四、列出未完成文章和失败原因

如果看到:

Plaintext
completed=16
remaining=4

先执行只读诊断:

Python
cd ~/code/wordpress-ai-excerpt-backfill

batch_id="syntaxhighlighter-20260725-01"

python3 - "$batch_id" <<'PY'
import json
import sys
from pathlib import Path

batch_id = sys.argv[1]
state_dir = (
    Path("data/state/history-migration")
    / batch_id
)
execution_dir = Path(
    "data/backups/single-candidate"
)

items = []

for state_path in state_dir.glob(
    "chinese-*.json"
):
    state = json.loads(
        state_path.read_text(encoding="utf-8")
    )

    if state.get("workflow_status") == "completed":
        continue

    post_id = int(
        state["chinese_post_id"]
    )

    execution_path = (
        execution_dir
        / f"chinese-{post_id}.execution.json"
    )

    execution = {}

    if execution_path.is_file():
        execution = json.loads(
            execution_path.read_text(
                encoding="utf-8"
            )
        )

    failure = state.get("last_failure") or {}

    items.append(
        {
            "position": state.get(
                "batch_position",
                999999,
            ),
            "post_id": post_id,
            "english_id": state.get(
                "english_post_id"
            ),
            "workflow": state.get(
                "workflow_status"
            ),
            "execution": execution.get(
                "status"
            ),
            "error": execution.get("error"),
            "error_response": execution.get(
                "error_response"
            ),
            "last_stage": failure.get("stage"),
            "last_reason": failure.get("reason"),
            "retry_counts": state.get(
                "retry_counts"
            ),
        }
    )

items.sort(
    key=lambda item: item["position"]
)

print(f"批次:{batch_id}")
print(f"未完成文章:{len(items)}")
print()

for index, item in enumerate(items, 1):
    print(
        f"{index:02d}. "
        f"zh={item['post_id']} "
        f"en={item['english_id']}"
    )
    print(
        f"    workflow={item['workflow']} "
        f"execution={item['execution']}"
    )
    print(
        f"    last_stage={item['last_stage']} "
        f"last_reason={item['last_reason']}"
    )
    print(f"    error={item['error']}")
    print(
        f"    error_response="
        f"{item['error_response']}"
    )
    print(
        f"    retry_counts="
        f"{item['retry_counts']}"
    )
    print()
PY


十五、不同未完成状态怎样处理

1. ready_for_execution

这类文章仍然由 run-ready 处理,不要改用通用 resume。可以先预览:

Bash
python3 bin/history-migration.py run-ready \
    --batch-id "$batch_id"

如果文章的 execution evidence 已经是 excerpt_generated,当前代码会在真正重试前自动观察生产中文摘要:已保存则续跑翻译,仍为空则重新 run;不需要人工先把状态改成 translation resume。

2. translation_failedready_for_translation_resume

如果中文摘要已经确认写入,而英文覆盖翻译需要继续,使用:

Bash
python3 bin/history-migration.py resume \
    --post-id 文章ID \
    --execute

这个命令只继续英文翻译,不重新生成中文摘要。但执行前仍要看真实错误;确定性的结构或占位符问题要先修复翻译管线。

3. excerpt_failed

先检查最近保存的被拒绝摘要:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

post_id="文章ID"

python3 - "$post_id" <<'PY'
import sys
from pathlib import Path

post_id = sys.argv[1]

rejected_dir = Path(
    "data/backups/single-candidate/rejected"
)

files = sorted(
    rejected_dir.glob(
        f"chinese-{post_id}-glm47-rejected-attempt-*.txt"
    ),
    key=lambda path: path.stat().st_mtime,
)[-3:]

print(f"找到最近被拒绝的摘要:{len(files)}")
print()

for index, path in enumerate(files, 1):
    text = path.read_text(encoding="utf-8")

    print("=" * 80)
    print(f"第 {index} 次:{path}")
    print("-" * 80)
    print(text)
    print("-" * 80)
    print(repr(text))
    print()
PY

检查摘要是真的包含 Markdown、列表或其他不合格格式,还是被校验器误判。不要在不知道真实模型输出的情况下无限重试。

如果需要在修复提示词或校验器后直接重新执行单篇完整流程,可以执行:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

batch_id="syntaxhighlighter-20260725-01"
post_id="文章ID"

python3 bin/execute-single-candidate.py \
    --post-id "$post_id" \
    --manifest \
      "data/analysis/history-migration-validation/${batch_id}/chinese-${post_id}.execution-candidate.csv" \
    --expected-candidate-count 1 \
    --backup-dir data/backups/single-candidate \
    --execute

成功时会显示:

Plaintext
{"chinese_post_id": 文章ID, "english_post_id": 英文文章ID, "status": "completed"}

直接调用 execute-single-candidate.py 后,如果执行证据已经完成但协调状态没有同步,再先预览:

Bash
python3 bin/history-migration.py sync-execution \
    --json

确认只计划同步目标文章后,再执行:

Bash
python3 bin/history-migration.py sync-execution \
    --apply \
    --json

4. blocked / stale_execution_state

如果 status 提示需要人工排查 blocked,先用 show-current 找到目标文章,再读取该文章状态里的 last_failureexecution_evidenceworkflow_status。不要直接清状态文件。

如果确认是 run 阶段留下的孤立尝试,并且 execution evidence 为 excerpt_generated,需要先确认生产中文摘要究竟为空还是已保存。之后先做 preview:

Bash
# 生产中文摘要仍为空
python3 bin/history-migration.py reconcile-attempts \
    --post-id 文章ID \
    --stage run \
    --chinese-excerpt-empty

# 生产中文摘要已经保存
python3 bin/history-migration.py reconcile-attempts \
    --post-id 文章ID \
    --stage run \
    --chinese-excerpt-saved

只有 preview 显示 eligible=Trueallowed=True 后,才在同一条命令末尾增加 --apply。摘要为空会恢复到 ready_for_execution;摘要已保存会恢复到 ready_for_translation_resume

如果 blocked 的原因不是上述孤立 run 尝试,就按真实 last_failure 排查,不能套用这组命令。


十六、怎样判断整个批次真正完成

最终执行:

Bash
cd ~/code/wordpress-ai-excerpt-backfill

python3 bin/history-migration.py status
python3 bin/history-migration.py summary

真正完成时,重点不是某一个命令的 selected_count,而是整个协调状态已经收敛:

Plaintext
completed = 批次总数
ready = 0
in_progress = 0
translation_resume = 0
excerpt_failed = 0
translation_failed = 0
validation_failed = 0
blocked = 0
remaining = 0
integrity = ok

2026 年 7 月 26 日完成本轮 20 篇真实回归后,仓库状态已经达到:

Plaintext
固定批次: 6
固定文章: 108
执行证据: completed=108 failed=0 pending=0 translation_started=0 other=0 no_execution_evidence=0
完整性: ok conflicts=0 errors=0
流程: ready=0 in_progress=0 translation_resume=0 excerpt_failed=0 translation_failed=0 validation_failed=0 blocked=0 remaining=0
下一步: 当前批次已完成
syntaxhighlighter-20260725-01: total=20 completed=20 remaining=0
最新未完成批次: 无
建议创建下一批: True
建议: all batches complete

retry_exhausted 是历史累计重试耗尽记录,不代表当前仍有同样数量的失败文章。本轮最终状态中即使它仍是非零,只要当前 remaining=0、所有失败/blocked 桶为 0,就已经完成。


十七、本地运行状态不要提交

批次处理后,git status 可能出现:

Plaintext
M data/state/history-migration/某批次/chinese-文章ID.json
M data/state/history-migration/某批次/events.jsonl
?? data/state/history-migration/某批次/

这些属于本地运行状态和执行证据。

应当:

  • 保留;
  • 不提交;
  • 不删除;
  • 不执行 git add .
  • 不执行 git add -A
  • 不为了让工作区看起来干净而清除状态。

需要保留的内容包括:

Plaintext
data/state/
events.jsonl
data/backups/
*.execution.json
*.pre-write.json
rejected/

代码修改应精确指定文件暂存,例如:

Bash
git add \
    src/candidate_execution.py \
    src/glm47_excerpt_client.py \
    tests/test_candidate_execution.py \
    tests/test_excerpt_clients.py

十八、每天真正需要执行的固定顺序

以后每天按照下面这条固定路径执行。批次 CSV 和运行状态都保留本地,不再加入“提交批次 CSV”这一步:

Plaintext
1. 执行 status 和 summary
2. 确认最新未完成批次为无,并且建议创建下一批: True
3. 创建新的 20 篇固定批次并执行 init-state
4. 记下 batch_id 和 csv_file;批次 CSV 保留本地,不提交 Git
5. 输出 20 篇文章清单和后台编辑地址
6. 人工迁移 SyntaxHighlighter;最终要求 SH=0
7. 迁移为 Code Block Pro 的区块逐块核对语言;CBP 数量允许小于原 SH 数量
8. 批量执行 mark-converted
9. 检查 WP_ADMIN_COOKIE 和 WP_REST_NONCE;缺失时在当前终端静默重新加载
10. 批量执行 validate-live
11. 网络型验证失败直接重试;人工修复后的 validation_failed 使用 --refresh
12. run-ready 预览,确认目标文章全部 allowed
13. 检查 ZHIPU_API_KEY;缺失时静默重新加载
14. 执行 run-ready --execute,让单篇有限重试并继续后续文章
15. 执行 status、summary 和必要时 show-current
16. ready_for_execution 继续交给 run-ready
17. ready_for_translation_resume / translation_failed 才使用 resume
18. blocked 先核对真实状态,必要时使用 reconcile-attempts preview 后再 --apply
19. excerpt_failed 先检查真实模型输出或确定性校验原因
20. 确认 completed=20、remaining=0、blocked=0、integrity=ok
21. 保留 data/state、events、backup 和 execution evidence,不清理本地运行状态

最关键的原则是:

Plaintext
不要把 resume 当成所有未完成文章的统一恢复命令。

不要因为 selected_count=0,就认定批次已经完成。

不要因为 Code Block Pro 数量少于原 SyntaxHighlighter,就自动判定迁移失败。

不要为了节省少量 GLM Token,把恢复逻辑做得比实际需求更复杂。

不要在没有查看真实错误、真实 execution evidence 和生产实际状态前盲目改状态。

十九、最终结论

这套流程的目标并不是建立一个理论上最复杂的自动化系统,而是在可接受的时间、成本和维护量下,稳定完成 WordPress 历史文章迁移。

最终工作方式是:

Plaintext
仓库筛选并固定文章
→ 人工按内容选择合适的 Gutenberg 迁移方式
→ 生产只读验证确认 SH=0、CBP 不超过原数量并检查其他安全条件
→ 仓库批量生成摘要和覆盖英文译文
→ 单篇偶发故障有限重试并继续后续文章
→ excerpt_generated 自动根据生产摘要选择 resume 或 restart
→ 人工只介入真正需要判断的 blocked 或确定性错误
→ completed=批次总数、remaining=0 后才进入下一批

这次严格按照文章 SOP 重新走一遍非常有价值,因为真正暴露出来的问题并不是“批处理框架需要整体重写”,而是几个很具体的流程缺口:创建批次时读取错命令输出、忽略目录里的 CSV 不应提交、迁移数量规则过严、验证失败缺少恢复分支、环境变量缺少重新加载说明,以及 excerpt_generated 的恢复路径不完整。

最后一个问题修复后,真实剩余 4 篇全部在下一轮第一次尝试中完成;整个仓库最终达到 108/108 completed,最新 20 篇批次达到 20/20 completed。

因此,接下来不需要继续扩展一个更复杂的多轮调度系统。更合适的做法是保持现有实现简单:单篇有限重试、失败隔离、状态可恢复;每天只按本文固定顺序执行,并以最终 remaining=0 作为完成标准。

将 WordPress AI 翻译优化仓库从私有切换为公共:从敏感信息审计到最小改动发布 每天批量处理 20 篇 Mixed WordPress 历史文章:Classic 转 Gutenberg、SyntaxHighlighter 迁移、摘要补全与英文覆盖翻译完整 SOP

需要长期技术维护或远程问题排查?

我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。

如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:

  • ✅ PHP / Laravel / Yii2 老项目无人维护
  • ✅ Go / Gin 后端接口需要排查或优化
  • ✅ WordPress 网站访问慢、报错或插件冲突
  • ✅ Nginx / MySQL / Redis / Linux 服务器异常
  • ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
  • ✅ 需要长期远程技术支持或兼职维护

更多介绍请查看:关于我 & 合作

微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan

评论

一条对“每天批量处理 20 篇 WordPress 历史文章:代码块迁移、摘要补全与英文覆盖翻译完整 SOP”的回复