很多团队接 AI 的第一反应是:找到流程里最费人的那一步,接个大模型把它替掉。
这篇想说一个工程上更稳的顺序:先给流程做一次体检,把每一步分进四个桶,再决定哪一步值得交给 AI 。我们拿自己「一篇文章发 10 个平台」的流程做了一遍,结论有点反直觉:9 次返工里,有 8 次根本不需要 AI,一条规范加一个校验脚本就能拦下来。
文末附这个校验脚本(Python,标准库,无依赖,带自测)。
一、方法:四桶法
方法本身不新。Michael Hammer 1990 年在 HBR 上写过《Reengineering Work: Don't Automate, Obliterate》:一个 20 步的流程,每步都加速,整体可能一点没快,瓶颈在步骤之间的交接和等待。最近做「AI 部署工程师」(Forward Deployed Engineer,FDE)的人把它和 AI 落地结合起来,多了一个 Agent 桶:
| 桶 | 判据 | 工程上对应什么 |
|---|---|---|
| 删除 | 这一步在 AI 时代根本不该存在 | 删掉流程节点、合并数据源 |
| 确定性代码 | 「如果 X 就 Y」,没有判断 | 纯函数、规则表、单元测试 |
| Agent | 有历史样本 + 需要判断 + 存在例外 | LLM + 结构化输出 + 评测集 |
| 人工决策 | 风险最高、不可逆的最终动作 | 审批闸门、人工确认 |
判桶时按顺序问四个问题,前一个答「是」就停:能删吗 → 能写成规则吗 → 需要判断且有样本吗 → 是不是高风险的最终动作。
工程师最容易犯的错,是把第 2 桶的事放进第 3 桶:一个能穷举条件的判断,交给 LLM 去「理解」,换来的是更贵、更慢、不可复现。
二、体检对象:一稿多发,名义 5 步、实际 18 步
问我发一篇文章有几步,我会说写稿、排版、发布、分发、看数据,5 步。按发布日志真实数一遍是 18 步:
- 选题、写母稿 2. 做配图(HTML 设计稿 → 无头浏览器出图) 3. 公众号排版、建草稿 4. 知乎重写深度版 5. 知乎导入、补图注 6. 小红书拆 8 篇 7. 小红书逐篇上传 8. 百家号 9. 头条 10. CSDN 重写工程版 11. LinkedIn 英文改写 12. 即刻短动态 13. 等各平台审核 14. 审核后逐个补链接 15. 收各平台数据 16. 写发布记录 17. 更新账号状态表 18. 复盘
期间 9 次返工:
| # | 返工 | 根因 |
|---|---|---|
| 1 | 小红书定时把美东时间当北京时间填,提前发出 | 时间没带时区 |
| 2 | 话题超上限,还被平台自动换词 | 平台限制没提前校验 |
| 3 | 知乎删段后本地稿和线上不一致 | 两个信源 |
| 4 | LinkedIn 观点卡排版改 4 轮 | 审美判断 |
| 5 | 头条企业号入驻没通过 | 没先查平台条件 |
| 6 | 头条作品声明没保存住 | 发布前没检查 |
| 7 | CSDN 简介被驳回 | 含站外引流写法 |
| 8 | 从预览复制,空行全丢 | 复制源不对 |
| 9 | 操作太快被限流 | 没有节奏控制 |
三、分桶结果
- 删除:手工换算时区(统一按北京时间、时间一律带时区);从预览复制(只从纯文本源复制);多处维护账号资料(一份资料表);不查规则就申请(先对照平台限制清单)。
- 确定性代码 :母稿转各平台格式、卡片出图;发布前校验平台限制;汇总数据、生成发布记录。
- Agent:按平台读者改写(知乎深度版、本文这种工程版、英文版、小红书拆条);起草封面文案、生成无文字配图背景;审核驳回时读规则给修改建议。
- 人工决策:选题和观点、原创与 AI 声明、引流口径、点下「发布」、客户内容授权。
逐条对返工:#1 #3 #5 #8 进删除桶,#2 #6 #7 #9 进确定性代码桶,只有 #4 需要判断,交给 AI 出几版、人来拍板。
结论:这次体检最大的收益来自理顺流程,而不是 AI。一期只做前两桶,Agent 列二期。
四、确定性代码桶的样子:一个发布前校验器
下面这个脚本对应「发布前自动查平台限制」这一步。设计上刻意保持简单:
- 规则是数据 :
RULES字典按平台登记硬限制,平台改了规则只改这里; - 只报不改 :校验器返回
Issue列表,不替你截断标题、不删话题------改什么是人的决定; - 时间必须带时区 :
schedule没有+08:00之类的偏移直接报错。这条就是返工 #1 的根治办法:本机在美东、平台按北京时间解释,「看起来对」的 08:00 会差 12 小时; - 可测试 :
--test跑一组断言,每条规则一个正例一个反例。
python
"""发布前校验器:把「一稿多发」里能写成规则的返工,在点发布之前拦下来。
四桶法里的「确定性代码桶」示例:规则写死、不需要 AI 判断、可测试、可复现。
用法:python3 publish_lint.py # 跑内置示例
python3 publish_lint.py --test # 跑自测
"""
from __future__ import annotations
import re
import sys
from dataclasses import dataclass, field
from datetime import datetime, timezone, timedelta
BEIJING = timezone(timedelta(hours=8))
# 平台规则:只放「写得死」的硬限制。数值以各平台当前后台提示为准,变了改这里。
RULES: dict[str, dict] = {
'wechat': {'title_max': 32, 'digest_max': 120},
'xiaohongshu': {'title_max': 20, 'body_max': 1000, 'topics_max': 10},
'linkedin': {'body_max': 3000},
'toutiao': {'nickname_max': 10},
'csdn': {'summary_max': 256, 'no_offsite_in_bio': True},
}
# 站外引流的常见写法:简介、正文里出现就提示(平台不一定说明驳回原因)
OFFSITE = re.compile(r'(公众号|微信|vx|v信|加我|私信领取)[::]?', re.I)
@dataclass
class Post:
platform: str
title: str = ''
body: str = ''
digest: str = ''
topics: list[str] = field(default_factory=list)
nickname: str = ''
bio: str = ''
schedule: str = '' # 定时发布时间,必须带时区,如 2026-10-11T08:00+08:00
source_has_blank_lines: bool | None = None # 母稿有空行段落时,检查粘贴后是否丢了
@dataclass
class Issue:
rule: str
message: str
def _len(s: str) -> int:
"""按字符计(中英文都算 1),与多数平台后台的计数方式一致。"""
return len(s)
def lint(post: Post, now: datetime | None = None) -> list[Issue]:
r = RULES.get(post.platform)
if r is None:
return [Issue('platform', f'未知平台 {post.platform},先在 RULES 里登记它的限制')]
now = now or datetime.now(BEIJING)
issues: list[Issue] = []
def over(key: str, value: str, label: str):
if key in r and _len(value) > r[key]:
issues.append(Issue(key, f'{label} {_len(value)} 字,超过上限 {r[key]}'))
over('title_max', post.title, '标题')
over('body_max', post.body, '正文')
over('digest_max', post.digest, '摘要')
over('summary_max', post.digest, '摘要')
over('nickname_max', post.nickname, '昵称')
if 'topics_max' in r:
if len(post.topics) > r['topics_max']:
issues.append(Issue('topics_max', f'话题 {len(post.topics)} 个,超过上限 {r["topics_max"]}'))
dup = {t for t in post.topics if post.topics.count(t) > 1}
if dup:
issues.append(Issue('topics_dup', f'话题重复:{"、".join(sorted(dup))}'))
if r.get('no_offsite_in_bio') and OFFSITE.search(post.bio):
issues.append(Issue('offsite', f'简介含站外引流写法「{OFFSITE.search(post.bio).group(0)}」,可能被驳回'))
if post.schedule:
try:
t = datetime.fromisoformat(post.schedule)
except ValueError:
issues.append(Issue('schedule', f'定时时间格式不对:{post.schedule}'))
else:
if t.tzinfo is None:
issues.append(Issue('schedule_tz', '定时时间没写时区;平台按北京时间解释,本机时区不同时会填错'))
elif t <= now:
issues.append(Issue('schedule_past', f'定时时间 {t.astimezone(BEIJING):%m-%d %H:%M}(北京)已过,审核通过会立即发出'))
if post.source_has_blank_lines and '\n\n' not in post.body:
issues.append(Issue('blank_lines', '母稿有空行分段,待发正文里一个空行都没有:可能是从预览复制丢了格式'))
return issues
def _demo() -> None:
now = datetime(2026, 10, 10, 23, 0, tzinfo=BEIJING)
posts = [
Post('wechat', title='名义 5 步,实际 18 步:用 AI 改流程之前,先做一次「四桶」体检'),
Post('xiaohongshu', title='用AI改流程前,先做这个体检',
topics=['AI应用', '人工智能', '流程优化', '效率提升', 'AI工具', '企业管理', '数字化转型', '工作流', '复盘', '职场', '干货'],
schedule='2026-10-11T08:00'),
Post('csdn', bio='某公司创始人,公众号:某某AI实践'),
Post('xiaohongshu', title='9次返工,8次根本不用AI', body='第一段。第二段。', source_has_blank_lines=True,
schedule='2026-10-10T20:00+08:00'),
]
for p in posts:
found = lint(p, now)
print(f'[{p.platform}] {p.title or p.bio}')
for i in found or [Issue('ok', '通过')]:
print(f' - {i.rule}: {i.message}')
def _test() -> None:
now = datetime(2026, 10, 10, 23, 0, tzinfo=BEIJING)
rules = lambda p: [i.rule for i in lint(p, now)]
assert rules(Post('wechat', title='名义5步,实际18步:用AI改流程之前,先做一次「四桶」体检')) == []
assert rules(Post('wechat', title='x' * 33)) == ['title_max']
assert rules(Post('xiaohongshu', topics=['a'] * 2)) == ['topics_dup']
assert rules(Post('xiaohongshu', topics=[str(i) for i in range(11)])) == ['topics_max']
assert rules(Post('xiaohongshu', schedule='2026-10-11T08:00')) == ['schedule_tz']
assert rules(Post('xiaohongshu', schedule='2026-10-10T20:00+08:00')) == ['schedule_past']
assert rules(Post('xiaohongshu', schedule='2026-10-11T08:00+08:00')) == []
# 美东 10-10 20:00 = 北京 10-11 08:00,带时区就不会填错
assert rules(Post('xiaohongshu', schedule='2026-10-10T20:00-04:00')) == []
assert rules(Post('csdn', bio='公众号:某某AI实践')) == ['offsite']
assert rules(Post('csdn', bio='日新开物创始人,写 AI 落地')) == []
assert rules(Post('xiaohongshu', body='a\nb', source_has_blank_lines=True)) == ['blank_lines']
assert rules(Post('toutiao', nickname='老孙 | Ethan Sun')) == ['nickname_max']
assert rules(Post('weibo')) == ['platform']
print('全部通过')
if __name__ == '__main__':
_test() if '--test' in sys.argv else _demo()
运行效果(python3 publish_lint.py):
text
[wechat] 名义 5 步,实际 18 步:用 AI 改流程之前,先做一次「四桶」体检
- title_max: 标题 36 字,超过上限 32
[xiaohongshu] 用AI改流程前,先做这个体检
- topics_max: 话题 11 个,超过上限 10
- schedule_tz: 定时时间没写时区;平台按北京时间解释,本机时区不同时会填错
[csdn] 某公司创始人,公众号:某某AI实践 - offsite: 简介含站外引流写法「公众号:」,可能被驳回
[xiaohongshu] 9次返工,8次根本不用AI
- schedule_past: 定时时间 10-10 20:00(北京)已过,审核通过会立即发出
- blank_lines: 母稿有空行分段,待发正文里一个空行都没有:可能是从预览复制丢了格式
第一条是真事:给这篇文章的公众号版建草稿时,我们的工具就因为标题 36 字(上限 32)拒绝建稿,去掉半角空格后变成 30 字才过。这种错误在发布前被规则拦下,成本是一秒;发布后被平台拦下,成本是一次返工。
五、Agent 放在哪:AI 生成,规则把关
二期接 Agent 时,校验器不会被替代,反而更重要。我们现在的做法是:
- Agent 负责「需要判断」的部分:按平台改写标题和正文、选话题;
- Agent 的输出先过一遍校验器 ,不通过就把
Issue原文回传给 Agent 重写,最多重试 N 次; - 通过后进入人工决策桶:人看一眼、点发布。
也就是 生成交给模型,约束交给代码,最终动作交给人。这样模型的不确定性被关在一个可测试的边界里,出了问题也能定位是哪一层。
六、你可以照抄的部分
- 选一条流程,写下「名义流程」;
- 找实际干活的人,把最近真实的几单走一遍,记下谁在做、在等谁、哪里退回;
- 每一步按「能删 → 能写规则 → 需要判断且有样本 → 高风险最终动作」归桶;
- 先做删除和确定性代码,规则写成数据 + 测试;
- 汇报时把「流程再造省下的」和「AI 省下的」分开列。
如果你们团队也有「名义 5 步、实际 20 步」的流程,欢迎在评论区聊聊是哪一条。
参考资料
- Michael Hammer, Reengineering Work: Don't Automate, Obliterate, Harvard Business Review, 1990
- Greg Isenberg 播客,Varick Agents 创始人 Vas 访谈:AI 部署工程师的流程再造方法论,2026-10-01