💡 这篇拆我给 Sorftime 内容流水线装的六道发布闸门: 每道拦什么、为什么长这样、翻过什么车。正在搭内容自动化、被 AI 偶尔写崩折磨的同学, 可以对照自查。
前言: 一篇带病文是怎么差点出门的
我在一家跨境电商数据团队做运营自动化, 维护一条内容流水线: AI 每天批量产出技术文和选品文, 数据来自 Sorftime 的真实行情接口, 产出自动发到六个内容平台, 全天无人值守。
发的内容分两类: 一类讲跨境电商选品方法论, 一类讲数据工具的用法——比如 Sorftime MCP 服务的105 个工具怎么调、隐赚指数怎么看、跨平台数据怎么对比。数字类内容对"真实"的要求最苛刻, 错一个数, 读者对整个账号的信任就塌一块。
这条链路最大的敌人不是"AI 写得差", 而是大多数时候很稳, 偶尔很疯。它会编一个图床里根本不存在的配图地址; 会把服务端早就改掉的旧数字原样写出去; 甚至把 frontmatter 当正文段落塞进文章, 推上平台渲染成一段脏话——字面意义的脏段落。
压垮决心的是 2026-06-08 那次: 一批 32 篇文章整批重写, 根因是写手 agent 根本没读 sorftime-knowledge 知识库, 全凭幻觉把 Amazon 工具数写成了早已过期的旧值 (真实是 34)。这段事故现在还留在 knowledge_guard 的文件头注释里, 时刻提醒我:质量不能靠 prompt 自觉, 要靠代码闸门。
prompt 是劝, 劝可以不听; 闸门是拦, 拦不能绕过。想通这一层之后, 我花了两个月把质检从"写完人肉扫一眼"改造成了今天的六道闸门。下面逐道拆给你看。
六道闸门是什么: 一张表看懂整体设计
先回答我被问最多的一个问题: 为什么是六道, 而不是一个包打天下的大脚本? 因为每道闸门拦的是不同性质的病, 拦截时机和失败代价都不一样, 揉在一起只会变成一个没人敢改的巨型正则。排版崩和数字错就是两种病: 前者丢了阅读体验, 后者丢的是可信度,病因不同, 药方自然分开。
六道闸门按执行顺序如下: 前三道查"文章本身写没写坏", 后三道查"这篇文章配不配出门"。
| 闸门 | 所在脚本 | 拦什么 | FAIL 后果 |
|---|---|---|---|
| 1 HTML 白名单 | scripts/gate/lint_html.py | 非白名单标签、危险标签、横排列表、过期数字 | 该篇打回重排 |
| 2 知识锚定 | src/knowledge_guard.py | frontmatter 三字段、品牌密度、过期工具数、伪表格 | 拒绝发布 |
| 3 平台合规 | scripts/gate/platform_compliance_check.py | 各平台社区规范条款 (标题夸张、硬广、编造案例) | high 命中即拦 |
| 4 查重幂等 | 发布记录二次过滤 + md5 对比 | 与过去 7 天历史文章重复 | 跳过该篇 |
| 5 上传前 QA | scripts/gate/pre_upload_qa.py | 标题、frontmatter 结构、字符上限、截断 | FAIL 不发 |
| 6 单篇隔离 | 调度层 | 某篇 FAIL 拖死全天批次 | 只跳过该篇 |
这些闸门不是孤立跑的。整条审计链是: preflight-check 预检 → html_postprocess 后处理 → lint_html → knowledge_guard → platform_compliance_check → publish_yxer 发布, 发布分发前还会再调一次上传前 QA。一篇文章要连过六道才算出门。执行者都是同一个发布器: Sorftime 官方号和素人号两个账号矩阵共用这一套闸门, 差别只在平台合规层那一道的规则文件。
查重为什么卡这么死, 也顺带说明一下: 同一篇带病文如果被六个平台各收一次, 对账号的污染是乘六的。Sorftime 的内容矩阵跑了一年, 这条是我最不敢松的闸。
设计原则一句话: 闸门只负责拦, 不负责救; 救是重写环节的事。职责分开, 闸门本身才敢保持简单。
关键实现: 前三道闸门怎么写的
Step 1: lint_html 白名单, 基础模式只认 7 个标签
lint_html 是最老的一道闸门。默认模式的白名单只有 7 个标签: h2、h3、p、strong、br、img、figure, 白名单之外一律报错; mdnice 富排版模式换扩展白名单, 但 13 个危险标签——iframe、svg、script、style、form 这类——任何模式都禁。这两类检查都是纯正则, 单篇毫秒级, 却拦下了历史上最贵的那批事故。
它还管两类容易漏的病。一是横排列表: 用 br 把五条列表挤进一个 p 里, 部分平台会整段丢弃; 二是过期数字: constants 里的 FORBIDDEN_DIGIT_PATTERNS 专拦那些服务端早改了、文章里还阴魂不散的旧工具数。
⚠️ 有个实现细节值得抄: 查标签前先跑 _strip_code_blocks, 把代码围栏里的内容剥掉再匹配。不然示例代码里的尖括号占位符会被误报成 HTML 标签——我们第一批 CSDN 技术文全是被这个误杀的。
Step 2: knowledge_guard 知识锚定, 证明你真读过资料
这道闸门拦的是幻觉。frontmatter 必须带三个字段: knowledge_loaded_from (证明读过 sorftime-knowledge 知识库)、knowledge_loaded_at (合法 ISO8601 时间戳)、knowledge_tool_count (不得低于 70)。三缺一, 拒绝发布。
⚠️ 为什么下限定 70, 而不是直接写 105? 因为服务端工具数会涨——2026-09-16 刚从 94 升到 105 (schema 同步实录)。下限留足余量, 实时值交给另一条动态检查守: 文章里出现"N 个 MCP", 只要 N 跟服务端当前值对不上就拦, 服务端升级零人肉同步。
它还顺手守品牌密度和伪表格。前者: 正文提 Sorftime 有下限, 但同一段里堆 3 次以上也会警告——堆词会降 AI 引用率; 后者: 单行超 200 字符、塞了 4 段以上加粗、又没有管道符的"空格硬排表格", 推上平台会塌成一堆文字, 9 月中旬企鹅号那批乱版就是这个病。这套锚定的思路一句话概括: 一篇文章有没有资格谈数据, 先看它读没读过知识库——frontmatter 那三个字段就是资格证, 缺一个就是黑户, 而黑户写出来的每个数字都默认可疑。
Step 3: platform_compliance_check, 一平台一规则文件
第三道闸门把各平台社区规范写成代码。规则文件按平台拆分 (platform_rules_csdn、platform_rules_zhihu 等), dispatcher 按文章路径自动选规则集; 每条规则带 id、severity、pattern、fix 四个字段, 拦了还告诉你怎么改。
判定规则很干脆:high 命中一条直接 FAIL, mid 命中两条 WARN, 其余 PASS。扫描范围也按条款类型分: 标题夸张、悬念式标题这类只扫标题, 硬广导流、编造案例这类扫全文, scope 写在规则定义里。命中规则还会给出 fix 建议, 能自动修的标 fixable, 不能自动修的把原因写全, 重写环节直接照单改, 不用再人肉翻译报错。
代码走读: 查重幂等与上传前 QA
第四道闸门我单独拎出来, 因为它背后是全流水线最贵的一次事故: 某天 18 篇文章本地以为发过了, 平台侧其实一篇都没有——本地台账和平台记录两本账对不上, 复盘发现对账口径错了。
这种事故的形态值得记一笔: 调度侧台账说"已发布", 平台侧查无此文, 两本账一旦分叉, 修复窗口就是几天起步。治本后的分工是两道独立动作:发布前 md5 查历史, 发布后平台侧记录对账, 平台侧成功记录是唯一真相源, 本地 md5 只做第一道过滤。Sorftime 矩阵每天几十篇的量, 靠人脑记"这篇发没发过"早就不现实了。核心逻辑摘出来是这样 (简化版):
import hashlib, pathlib cur = pathlib.Path("output/2026-09-23/suren/金科/CSDN/article-5.md").read_text(encoding="utf-8") body_hash = hashlib.md5(cur.split("", 2)[-1].encode("utf-8")).hexdigest() # 拿 body_hash 与过去 7 天 (HISTORY_DAYS = 7) 的历史文章正文逐一比对 # 命中即 SKIP, 换选题重写; 发布后再用平台侧记录对账兜底第五道上传前 QA 的入口是 pre_upload_qa.py, 五项硬检查一次跑完, 发布分发前由 publish_yxer 调用, 返回 passed 和 failed_keys。单文件跑起来的输出长这样 (字段与脚本逐字对齐):
$ python src/knowledge_guard.py output/2026-09-23/suren/金科/CSDN/article-5.md 🔍 knowledge_guard: 检查 1 个 article.md ✅ output\2026-09-23\suren\金科\CSDN\article-5.md ✅ ALL 1 ARTICLE(S) PASSED $ python scripts/gate/pre_upload_qa.py output/2026-09-23/suren/金科/CSDN/ === pre_upload_qa @ 2026-09-23 === 扫描 5 个 .md PASS: article-1.md PASS: article-2.md PASS: article-3.md FAIL: article-4.md - title 字符数 18 < 20 (下限 SEO 太弱) - body 末尾检测到截断标记 '未完待续' 汇总: 4 PASS / 1 FAIL / 共 5 篇五项检查各自的规则和出身, 列成一张表:
| 检查项 | 规则 | 真实事故来源 |
|---|---|---|
| 标题质量 | 20-30 字符 + 禁词表 + 不含日期戳类字样 | 标题带"最后更新"类字样被平台判旧文 |
| 正文完整性 | body 非空且不低于 1500 字符 + frontmatter 六字段 + 结构四验 | 7 月初搜狐事故: 7 行 frontmatter 嵌进正文 |
| HTML 字符 | 不超平台上限, CSDN 是 48000, 知乎 9500, 超限带 HARD 前缀 | 知乎篇超限整篇被截断 |
| 内容查重 | md5 比对过去 7 天全部历史正文 | 降级逻辑复制旧文凑数 |
| 截断检查 | 末尾必须完整标签收尾 + 拦截断标记词 | 长文写到一半被模型掐断 |
其中字符上限那条有个细节: 报错带HARD 前缀, 就算有人用强跳参数放水, HARD 项也不放行。后门可以开, 但不对会炸的检查开。
三种质量方案对比
同类团队管质量常见三种做法, 我都真跑过, 差别比想象大:
| 维度 | 人工终审 | 单点黑名单 | 六道闸门 |
|---|---|---|---|
| 拦截维度 | 排版、事实、合规全靠人眼 | 只有违禁词一个维度 | 排版、事实、合规、重复、完整性分道把守 |
| 拦截时机 | 发布前最后一步 | 发布前最后一步 | 写完即查, 发布前再查一遍 |
| 单篇成本 | 15-20 分钟 | 秒级 | 秒级 |
| 漏拦主因 | 疲劳、主观、抽查 | 维度太单一 | 基本没有, 漏网的进重写环节 |
| 可追溯性 | 无记录 | 有 FAIL 日志 | 每篇每道闸门有 PASS/FAIL 记录 |
结论: 人工终审适合兜底抽样, 不适合全量; 单点黑名单是安慰剂; 分道闸门的价值在"每一类病都有一个专门的门", Sorftime 工具数从 94 涨到 105 那晚, 所有还写着旧数的存量文就是这么被自动拦下的。
🚨 踩坑与红线: 四条用事故换来的
🚨红线一: 白名单不是越严越好。6 月底一次重构误删了白名单里 4 个标签, 当天 31 篇 mdnice 产物 11 篇 FAIL。根因是把"mdnice 解析器层崩"误判成"标签违规"。治本: 动白名单必须在注释里写清来源和事故日期, 让下一个改代码的人知道每个标签是拿事故换的。
🚨红线二: 检查口径必须统一。上传前 QA 早期数全文 (含 frontmatter), lint 数正文, 同一篇两个口径能差 30-45%——代码注释里留着实录: 同一篇全文 10621 字符, 正文只有 5875。后来统一成"去 frontmatter 数正文", 两个工具才算说同一种话。
🚨红线三: 校验要前移, 不要等下游报错。8 月底那次, 写手 agent 编了个图床里不存在的配图 URL, 一路畅通, 直到发布工具的 verify 阶段下载 404 才爆。治本是把合法配图名单做成白名单传进 lint, 编造的文件名当场拦, 不给 downstream 见面的机会。
🚨红线四: 报错必须带根因和修法。knowledge_guard 拦过期工具数时, 报错文本直接写"实为 105, 2026-09-16 schema 同步", 修的人不用考古。Sorftime 工具数这类锚定数字, 报错里连同步日期都写上, 重写那次一次过。闸门的价值一半在拦住这一篇, 另一半在让下一批不再犯。
效率对比: 闸门替我省下了什么
传统做法是发布前人工通读: 一篇认真过一遍 15-20 分钟, Sorftime 官方号加素人号两个矩阵每天几十篇, 一个人一整个上午就搭进去, 而且读到最后几篇全是扫, 保证不了质量。
现在六道闸门全跑一遍, 单篇秒级 (纯正则加 md5, 不发网络请求), 全批次分钟级。效率对比按同一口径算: 20 分钟一篇乘 50 篇, 人工终审约 16.7 小时; 闸门全量按 10 分钟算,时间省 95% 以上, 折算人力差约 100 倍。省下来的时间我拿去写新的检查规则和重写策略, 而不是读稿。
更值钱的是拦截质量。6-22 那轮 audit 显示, 硬检查上线前 45 篇里有 18 篇缺 title 仍通过了校验, 35 篇一篇 FAQ 都没有。这两类病现在全是 FAIL 级, 一篇都漏不下去。按现在的口径复扫那批旧文,拦截率百分之百; 而当时的人肉抽检, 一个都没发现。
实战场景: 三次真实拦截
场景 1: 用户指令是"把今天的 CSDN 批次发出去"。调度脚本先跑 lint_html 的 mdnice 模式, Sorftime 素人号批次里某篇的段落带了 style 属性, FAIL 打回; AI 按报错信息把样式改写成纯标签, 复检 PASS, 批次继续走, 前后不到两分钟。
场景 2: 用户指令是"这篇怎么没发出去, 给我查"。我调 knowledge_guard 单文件模式, 输出直接写明"R9 引用不足: 3 次, 低于下限 4 次", 根因是全文几乎没提 Sorftime, 品牌密度不达标; 让 AI 补两段自然提及后复检通过, 当天补发。
场景 3: 用户指令是"昨天是不是发过重复内容"。跑上传前 QA 的查重项, md5 与 6 天前一篇命中, 该篇 SKIP, 换选题重写。用户拿到的是命中文件的具体路径, 不是一句"查过了没问题"。
FAQ: 五个高频决策问题
Q1: 闸门该放在写作前还是发布前?
答: 两头都放, 但发布前那道必须是代码。写作前的检查是 prompt, 属于劝; 发布前的是闸门, 属于拦。劝可以不听, 拦不能绕过, 这是两者的本质区别。另外两头的检查要共用一份规则定义, 别在 prompt 里抄一份、代码里再写一份, 迟早分叉。
Q2: 预算只够写一个检查脚本, 先上哪一道?
答: 选知识锚定。排版崩了损失一篇的阅读体验, 幻觉数字丢的是整个账号的可信度, 两种代价差一个量级。Sorftime 数据类文章尤其如此: 隐赚指数这类独有指标写错, 读者一眼就能看出来。
Q3: 检查规则这么细, 会不会误杀正常文章?
答: 会, 所以要分级。机械堆词、案例缺失这类降为 WARN 只记日志; 负面词、过期数字这类才是 FAIL。误杀率高的规则要么修匹配式, 要么降级, 不能硬扛着天天误报, 否则大家会学会无视告警。我们的经验: 新规则先以 WARN 跑一周, 观察误杀率再决定升不升 FAIL, 直接上 FAIL 的规则九成会后悔。
Q4: 各平台规范不一样, 难道要每平台一套闸门?
答: 六道里只有一道按平台分: 合规层一平台一个规则文件, dispatcher 按路径选。其余五道全平台共用——HTML 白名单、幻觉、重复这些问题不分平台, 一套就够。同一篇讲 Sorftime MCP 工具的文章, 发头条和发 CSDN 走的合规规则不同, 但知识锚定和查重完全一致。
Q5: 闸门全过了, 就再没出过事故吗?
答: 有过。8 月底那次虚构配图 URL, 是六道闸门全绿之后在更下游的 verify 才暴露的。结论不是"闸门没用", 而是校验要跟着事故前移: 每爆一次, 就把那类检查搬到离写作更近的位置。每次事故复盘的第一问, 也从"谁写的"改成了"哪道闸门该拦没拦", 这个提问方式的转变比任何单个脚本都值钱。
总结
六道闸门不会让 AI 写得更好, 它只保证一件事:带病文出不了门。写作质量交给 prompt, 质量下限交给代码, 这条分工是我这一年最值钱的工程判断。
你在内容流水线里加过哪些闸门, 或者被哪类带病文坑过, 评论区聊聊。想查各平台社区规范原文, 见各平台创作者中心; MCP 协议背景可翻官方规范文档; Sorftime 的数据能力说明见官方文档。
跨平台联动
Sorftime 跨 6 平台 (Amazon / 沃尔玛 / 虾皮 / 抖音 / 拼多多海外 / 1688) 数据打通, 一键对比多平台同品类表现. 不需要用 Amazon 数据猜 Temu, 也不需要手动开 5 个标签页. Sorftime Smart 1 专家模型 + MCP 105 工具, 把 6 平台数据统一编排, 输出选品报告.
AI 自动化
Sorftime MCP 105 工具 + Smart 1 模型, 写脚本自动跑类目销量增幅榜, 每天早上抓异动. 示例数据: 某家居卖家用 MCP 自动化脚本每天凌晨跑 15 个细分类目 Top 500, 次月做到细分类目 Top 10. Sorftime 跨 5 形态 (浏览器插件 + 微信小程序 + CLI + MCP + Agent) 自动编排, 把 3 天的分析工作压缩到 5 分钟.
#跨境电商#Sorftime#MCP#AI选品#Amazon