目录

OpenClaw 博客流水线配置问题排查:从构建失败到规范强化

一次关于 Hugo 构建错误、文件格式规范和路径推导的完整复盘

背景

OpenClaw 博客流水线是一个多阶段自动化流程,涵盖写稿、质检、SEO、配图、审核、发布等环节。近期在发布过程中连续遇到三个典型问题,暴露出配置规范执行不到位的问题。

本文记录完整排查过程、根因分析和修复方案。


问题排查流程图:三个问题的发现和修复过程

问题一:Hugo 构建失败 — alt 属性中文引号

现象

Cloudflare Pages 构建报错:

ERROR error building site: assemble: failed to create page from pageMetaSource
/opt/buildhome/repo/content/posts/digital-asset/clean-body-clear-home-toxic-places-toxic-people/index.md:87:170:
got positional parameter '身体状态'. Cannot mix named and positional parameters

根因

Hugo shortcode 的 alt 属性值中包含中文引号 "(U+201C/U+201D),Hugo 解析器将其误认为字符串边界,导致参数截断。

错误示例:

{没这字{< image src="..." alt="一张「运气生成机制」流程图,左侧是「身净」输入..." >}}

正确写法:

{没这字{< image src="..." alt="一张「运气生成机制」流程图,左侧是「身净」输入..." >}}

修复

批量替换所有 Hugo 文件中的中文引号为「」:

# 检查是否有中文引号
grep -P '[\x{201C}\x{201D}]' /data/oklifeme/content/posts/*/*.md | grep 'alt='

# 修复(使用 Python 脚本)
python3 << 'EOF'
import re
with open('file.md', 'r') as f:
    content = f.read()
content = re.sub(r'[""]', '「', content)
content = re.sub(r'[""]', '」', content)
with open('file.md', 'w') as f:
    f.write(content)
EOF

中文引号替换前后对比图

问题二:frontmatter 包含不支持的 url 字段

现象

构建继续失败,错误指向 frontmatter 解析错误。

根因

Hugo 不支持在 frontmatter 中使用完整 URL 格式的 url 字段。部分文章在导出时意外包含了此字段。

错误示例:

url: "https://oklife.me/clean-body-clear-home-toxic-places-toxic-people"

正确做法:删除该字段,Hugo 会根据 slug 自动生成 URL。

修复

# 查找并删除 url 字段
grep -l "url:" /data/oklifeme/content/posts/*/*.md | xargs -I {} sed -i '/^url:/d' {}

问题三:index.md vs slug.md 格式混乱

现象

检查发现部分文章使用 index.md 目录格式,部分使用 slug.md 直接文件格式:

# 旧格式(目录)
/data/oklifeme/content/posts/digital-asset/clean-body-clear-home-toxic-places-toxic-people/index.md

# 新格式(直接文件)
/data/oklifeme/content/posts/digital-asset/su-qin-pe-liu-guo-xiang-yin.md

根因

blog-pipeline/SKILL.md 和 AGENTS.md 中的发布命令存在矛盾:

  • Phase 5 模板使用 index.md 格式
  • AGENTS.md 文档说明使用 slug.md 格式

错误配置:

# blog-pipeline/SKILL.md 第 596 行
cp <源文件> "$POST_DIR/<slug>/index.md"  # 错误!

正确配置:

cp <源文件> "$POST_DIR/<slug>.md"  # 正确!

修复

更新两处配置:

1. blog-pipeline/SKILL.md

# Phase 5 发布命令
cp <源文件> "$POST_DIR/<slug>.md"

2. AGENTS.md

**最后复制到 Hugo**:`cp Obsidian文件 Hugo目录/<slug>.md`

index.md vs slug.md 新旧格式对比表

问题四:路径推导逻辑遗漏 OpenClaw 子目录

现象

所有 Code Art Studio 文章都发布到 code-art-studio/ 根目录,未区分 OpenClaw 子目录:

# 错误:OpenClaw 文章应该放在 openclaw/ 子目录
/data/oklifeme/content/posts/code-art-studio/openclaw-blog-pipeline-troubleshooting.md

# 正确:应该放在 openclaw/ 子目录
/data/oklifeme/content/posts/code-art-studio/openclaw/openclaw-blog-pipeline-troubleshooting.md

根因

derive_post_dir 函数只根据 categories 判断,未检查文章内容是否包含 OpenClaw 相关关键词。

修复

增强路径推导逻辑,根据内容关键词自动判断:

def derive_post_dir(frontmatter_yaml_path, hugo_root="/data/oklifeme"):
    """从 frontmatter categories 和 content 自动推导 Hugo post_dir"""
    with open(frontmatter_yaml_path) as f:
        fm = yaml.safe_load(f.read())
    
    categories = fm.get("categories", [])
    if not categories:
        return f"{hugo_root}/content/posts/digital-asset/"
    
    primary = categories[0].lower()
    
    if primary == "code art studio":
        # 读取文章内容判断是否 OpenClaw 相关
        with open('/tmp/blog_content.md') as f:
            content = f.read().lower()
        
        openclaw_keywords = ['openclaw', 'agent', 'gateway', 'skill', 'obsidian']
        is_openclaw = any(kw in content for kw in openclaw_keywords)
        
        if is_openclaw:
            return f"{hugo_root}/content/posts/code-art-studio/openclaw/"
        else:
            return f"{hugo_root}/content/posts/code-art-studio/"
    
    elif primary == "digital asset":
        return f"{hugo_root}/content/posts/digital-asset/"
    else:
        return f"{hugo_root}/content/posts/{primary}/"

正确的发布格式:

OpenClaw 文章: /data/oklifeme/content/posts/code-art-studio/openclaw/{slug}.md
其他技术文章:  /data/oklifeme/content/posts/code-art-studio/{slug}.md
Digital Asset:  /data/oklifeme/content/posts/digital-asset/{slug}.md

路径推导逻辑决策流程图

预防措施

为防止类似问题再次发生,在流水线中添加强制检查点:

Phase 4 完成后必须执行

# 1. 检查中文引号
if grep -P '[\x{201C}\x{201D}]' /data/oklifeme/content/posts/*/*.md 2>/dev/null | grep -q 'alt='; then
  echo "❌ 发现中文引号,必须修复"
  grep -nP '[\x{201C}\x{201D}]' /data/oklifeme/content/posts/*/*.md | grep 'alt='
  exit 1
fi
echo "✅ 无中文引号"

# 2. 检查 frontmatter url 字段
if grep -l "^url:" /data/oklifeme/content/posts/*/*.md 2>/dev/null | grep -q .; then
  echo "❌ 发现不支持的 url 字段"
  exit 1
fi
echo "✅ 无 url 字段"

# 3. 检查文件格式
for f in /data/oklifeme/content/posts/*/*.md; do
  if [ -d "$(dirname $f)/$(basename $f .md)" ]; then
    echo "❌ 发现 index.md 格式: $f"
  fi
done
echo "✅ 全部为 slug.md 格式"

铁律:检查未通过前,禁止进入 HITL 2 审核阶段。


经验教训

1. 配置规范需要物理检查点

单纯在文档中写规则不够,必须在流水线中设置物理检查点:

  • Phase 4 完成后强制检查中文引号
  • 检查未通过禁止进入下一环节
  • 让违规在技术上不可行

2. 格式统一的重要性

混合格式(index.md + slug.md)会增加维护成本:

  • 统一使用 slug.md 直接文件
  • 旧文章保持原格式不变
  • 新文章强制执行新格式

3. 路径推导需要多维度判断

仅根据 category 判断路径不够:

  • 需要结合内容关键词
  • OpenClaw 相关文章放 openclaw/ 子目录
  • 其他技术文章放 code-art-studio/ 根目录

参考来源


关联阅读

  • [[OpenClaw 运维日记:一次 Doctor 健康检查与 13 个问题修复实战]]
  • [[blog-pipeline 流水线优化实践]]
  • [[Hugo 构建优化与 Cloudflare Pages 部署]]