先说我这边最终跑通的效果:一个 Claude Skill,放在.claude/skills/tweet-writer目录下,每天早上十点、下午三点、晚上八点各发一条推。整个过程从选题、写正文、贴图、加话题标签到点击发布,全都不需要人工介入。我连续跑了三个星期,发了六十多条,中间只手动处理过两次,一次是推文里带了统计口径错误,另一次是账号被风控要求验证设备。
这个活儿乍一听很唬人,真拆开其实就三块:Claude 的 Skills 机制负责“会写”,X 的 v2 API 负责“能发”,cron 负责“按时发”。三块都不需要很深的底层知识,但拦路坑非常多。这篇文章把每一步怎么搭、每个坑怎么踩、每个参数怎么选,全部展开写清楚,方便你照着复现。
1. 先聊清楚:Claude Skills 到底解决了什么问题
1.1 自动写推这事难在哪
想在 X(也就是推特)上稳定日更,最麻烦的其实不是“发推”这个动作,而是“持续产出内容”。
手动一个个编文案是最笨的做法。用 Claude 网页版或 App 生成文案也麻烦,生成完你还得复制、打开 X、粘贴、配图、点发送。一两天能坚持,连续一个月你会疯。市面上也有各种 AI 发推工具,按月付费,有的还要求绑定独立开发者账号,授权环节动不动就把你的账号搞成只读状态。
我建议的第一步是做减法:把“全自动”拆成三个可以独立解决的问题。第一,谁来生成内容;第二,生成的内容怎么变成合法推文;第三,怎么调度执行。第一步我选了 Claude 的 Skills 机制,第二步选了 X 官方 v2 API,第三步用系统自带的 cron。一整套做下来,成本约等于零,没有月费,只有一个基础设施费用。
1.2 三个方案对比后为什么选了 Skills
写推文不只靠一个“API Key”就完事。你先要解决“让 AI 知道今天该发什么主题、语气是什么、带不带话题标签、要不要避雷”这些规则问题。市面上做这类事的方案大致有三种。
第一种,走现成的自动化平台。把 Claude 接到某个工作流平台上,配置触发器和动作。优势是可视化,不用写代码;劣势是这些平台对 X 这个渠道的覆盖很粗糙,授权流程复杂,免费额度极其有限,跑两周就开始提示升级付费套餐。
第二种,直接用 Claude 的 API 写一个 Python 调度程序。这个思路对,但你很快会发现要维护的东西太多:对话历史、上下文、重试逻辑、格式化输出、敏感词过滤,全都要自己写在代码里。想改一句“推文风格别太正式”,得改代码重发一次。改十次你就烦了。
第三种,也就是我最终用的方案:把规则写进 Claude 的 Skill 里,让 Skill 自己管理逻辑。你可以把它理解成给 Claude 装了一个“发推专用外挂”。Skil 里包含一个 SKILL.md 说明文件,里面写了完整的流程规则,比如“每篇推文必须 280 字符以内”“禁止说确切数值除非有来源”,再配一个 Python 脚本负责真正调接口发推。Claude 读取 SKILL.md 之后,遇到符合场景的任务会自动调用配套脚本,不需要我再手动复制粘贴文本。这样好处很明显:规则改起来只需要改 SKILL.md 里的文字,逻辑和代码完全解耦;发布路径又完全自己掌控,不依赖任何中间平台。
实际对比下来,第三种方案最适合个人开发者和小团队,因为你能完全控制发布流程,避免平台夹在中间造成的信息差和“黑箱”问题。
2. 准备工作:账号、密钥、环境一次性配齐
2.1 X开发者账号:权限要给够
要想用程序发推,必须注册 X 的开发者账号并创建一个应用。
这里有个非常关键的细节:创建应用的时候,应用权限一定要选“读、写”级别,不要只选“读”。我在这一步踩过坑,默认权限是只读,导致后面所有发推请求都返回 403 错误。
另外,X 开发者平台现在区分试用版(Free)和付费企业版。个人弄自动发推、频率不高、每天几条的话,试用版足够用了,每个月有免费发推额度。创建应用后必须去 User Authentication 设置里确认 OAuth 1.0a 权限是开启状态,并且勾选“可以代表用户发推”。很多教程不强调这个,实际少配置一步,后面全白搭。
2.2 四把钥匙一个都不能少
X 的 v2 API 发推走的是 OAuth 1.0a,不是 OAuth 2.0。这意味着你需要四样东西,缺一不可:
- API Key(也叫 Consumer Key)
- API Key Secret(Consumer Key Secret)
- Access Token
- Access Token Secret
这四个值在开发者平台的 Keys and tokens 页面都能看到,要点“生成”才会有完整字符串。获取 Access Token 那一步,需要你授权的账号是被这个应用绑定过权限的账号,别用错账号。
注意这四把钥匙里任何一把都不能出现在公开仓库或前端代码中。最安全的做法是写进本机环境变量,比如.env文件,然后让 Python 脚本用os.getenv()读取。别问我为什么强调这个,GitHub 搜代码搜索,你能搜到大量因为密钥写在代码注释里而被盗刷账号的案例。
2.3 本地环境:Python 3.11 + tweepy
我本机用的是 Python 3.11,装了一个发推专用的第三方库tweepy。这个库把 OAuth 1.0a 的签名流程封装好了,你只需要传四个密钥进去就能直接发推,比直接裸调requests实现 OAuth 签名省下一百多行代码。
安装命令很简单:
pip install tweepy python-dotenv装完python-dotenv,再在项目根目录放一个.env文件,填上:
X_API_KEY=你的APIKey X_API_SECRET=你的APISecret X_ACCESS_TOKEN=你的AccessToken X_ACCESS_TOKEN_SECRET=你的AccessTokenSecret然后写段代码验证一下能不能发出一条测试推:
import tweepy import os from dotenv import load_dotenv load_dotenv() client = tweepy.Client( consumer_key=os.getenv("X_API_KEY"), consumer_secret=os.getenv("X_API_SECRET"), access_token=os.getenv("X_ACCESS_TOKEN"), access_token_secret=os.getenv("X_ACCESS_TOKEN_SECRET"), ) response = client.create_tweet(text="Hello from Python!") print(response.data)能打印出tweet_id就是成功。这一步是后面所有动作的地基,建议先跑通再说其他。
3. 从0搓一个Skill:目录、SKILL.md和脚本
3.1 Skill 目录结构长这样
Claude 的 Skills 机制本身不复杂,它就是一个约定的目录结构。你把目录放在 Claude 能识别的位置,Claude 遇到相应场景就会加载里面的说明文件。
我建立的目录长这样:
~/.claude/skills/tweet-writer/ ├── SKILL.md └── scripts/ ├── post_tweet.py └── check_text.pySKILL.md是技能的大脑,里面用自然语言写清楚“这个技能是干什么的、触发条件是什么、执行步骤是什么、输出格式是什么”。scripts目录放着真正干活的 Python 脚本。Claude 会在需要的时候读取SKILL.md,并按照里面的指令来调用脚本。
这个设计的好处是,技能的“行为规则”和“执行代码”是分开存放的。你想让推文风格变活泼一点,只改 SKILL.md 里的 prompt 描述就行,不用动代码。反过来,万一 X 接口调用方式改了,只需要更新post_tweet.py,规则文案完全不受影响。
3.2 SKILL.md 的 YAML 头
任何 Skill 的SKILL.md都必须以 YAML frontmatter 开头,包含name和description。description尤其关键,因为它决定了 Claude 什么情况下会调用这个 Skill。
我写的头是这样的:
--- name: tweet-writer description: Generate and publish short-form social media posts to X (Twitter). Use this when the user wants to create a new tweet, schedule a daily post, or set up automatic posting. ---这里要注意description不要写“发推文”这三个字就完了,要写清楚触发场景。Claude 靠它来判断何时加载该技能。写得越具体,触发越精准。建议把“daily”“automatic”“tweet”“X post”这些相近语义都涵盖进去。
3.3 核心 prompt:让 Claude 懂规矩
YAML 头结束后,正文部分就是技能的使用说明书。我把它设计成一段结构化指令,Claude 执行时必须严格遵守。
我的 SKILL.md 正文精简版:
# Tweet Writer Skill ## 目标 生成高质量、原创、符合平台调性的推文并直接发布。 ## 触发时机 - 用户要求“发推”“自动发推”“写一条X内容”时 - 用户要求“日更”“定时发推”时 ## 执行步骤 1. 根据当前时间和前一天发布的选题记录,确定今天发布的 3 个主题范围,分别对应早、中、晚三个时段。 2. 为每个主题撰写一版推文,要求: - 正文不超过 280 字符(必须用 check_text.py 做字符校验) - 语气偏轻松、口语化,但不要用 emoji - 开头第一句直接给用户可感知的价值,不绕弯 - 不使用绝对化用语,比如“绝对”“最好”“100%” - 需要配图时,脚本会输出一个纯文本占位标记 3. 将推文文本写入临时文件,调用 scripts/post_tweet.py 发布。 4. 发布后读取返回结果,保留 tweet_id 写在运行日志中。 ## 输出格式 - 若发布成功:返回推文原文与 tweet_id - 若失败:返回错误码与原因,并整理为“已失败,待人工处理”你看,这里的关键是把“人的要求”转成“机器能执行的步骤”。Claude 读完这段,它会先构思主题,再写内容,再调用脚本发出去,最后记录结果。我自己的经验是:不要只写“写一条推文”,而是要告诉它完整流程。
3.4 发推脚本:把文案变成真实 Tweet
post_tweet.py的核心就是调用 tweepy 发推。但我不建议直接把推文内容传给脚本,因为 Claude 生成文本时偶尔会带出引号、换行符、Markdown 符号等不该出现的东西。更稳妥的做法是让 Claude 先把文本写入draft.txt,脚本再读这个文件,预处理后再发布。
我的脚本大概是这样的:
import os import re import sys import tweepy from dotenv import load_dotenv load_dotenv() def clean_draft(raw_text: str) -> str: # 去掉多余引号和 Markdown 符号 text = raw_text.strip() text = text.strip('"').strip("'") text = text.replace("*", "").replace("#", "") # 压缩多个换行为一个 text = re.sub(r"\n{2,}", "\n", text) return text.strip() def main() -> int: draft_path = sys.argv[1] if len(sys.argv) > 1 else "draft.txt" with open(draft_path, "r", encoding="utf-8") as f: raw = f.read() text = clean_draft(raw) if len(text) > 280: print("ERROR: text exceeds 280 characters") return 1 client = tweepy.Client( consumer_key=os.getenv("X_API_KEY"), consumer_secret=os.getenv("X_API_SECRET"), access_token=os.getenv("X_ACCESS_TOKEN"), access_token_secret=os.getenv("X_ACCESS_TOKEN_SECRET"), ) resp = client.create_tweet(text=text) print(resp.data["id"]) return 0 if __name__ == "__main__": sys.exit(main())发推前清理字符这件事很多人不重视。我之前就让 Claude 生成的文字直接输出,结果出现过推文里带一个星号的情况,看起来很业余。脚本里做一次净化,问题直接消失。字符校验也不能全依赖 Claude,因为英文 280 字符限制是按码点算的,某些特殊符号和表情占长度跟普通字符不一样,写进脚本里强制检查最稳。
另外,post_tweet.py不接受从命令行直接传文本,而是让它读文件。原因很简单:文件方式能避免引号嵌套和转义地狱,Claude 也不需要在命令行里拼接长字符串,出错率低得多。
4. 全自动流水线:定时触发 + 校验 + 复盘
4.1 三种定时方案
Skill 本身只解决“会发布”的问题,真正实现“全自动”还差一个调度器。我评估过三种方案:
第一种是个人电脑的 cron。配置简单,执行稳定,缺点是电脑不能关机,而且要处理睡眠唤醒问题。如果你用笔记本,还要考虑电池策略。
第二种是云服务器的 cron。这台服务器 24 小时在线,我最后用的就是这种。成本和一顿外卖差不多。
第三种是 GitHub Actions。免费额度对个人足够,但它的触发有分钟级延迟,且运行环境每次都是全新容器,要把密钥配在仓库 Secrets 里。我测试过,最终因为延迟问题没有采用——早晨那条推文如果晚 10 分钟问题不大,但如果你对时间要求卡到分钟级,还是服务器更可靠。
4.2 调度配置
我用云服务器的 cron 实现三段时间控制。配置方式:
crontab -e写入:
0 10 * * * cd /path/to/tweet-writer && source /path/to/venv/bin/activate && python run_daily.py --slot morning 0 15 * * * cd /path/to/tweet-writer && source /path/to/venv/bin/activate && python run_daily.py --slot afternoon 0 20 * * * cd /path/to/tweet-writer && source /path/to/venv/bin/activate && python run_daily.py --slot evening这里有个特别容易踩的坑:cron 的环境变量跟终端不一样,直接写python经常找不到命令,必须用绝对路径。而且source那步不能省,否则 Python 虚拟环境没激活,tweepy 就 import 不到。我在这个位置上至少耗了一个小时才排查明白。
run_daily.py做的事情很简单:读入当天的主题配置文件,调用 Claude Skill 生成草稿,把草稿传给post_tweet.py,再把日志追加到post_log.csv。这样一个完整的三段发布流程就是闭环了。
4.3 发布前的自检
光能发还不算完,还得让 Claude 在发布前做一轮自检。我在 SKILL.md 里加了一条硬性规则:发布前必须逐项检查以下内容,全部通过才允许调用 post_tweet.py:
- 正文长度是否小于等于 280 字符
- 是否包含绝对化用语(最高、最好、史上等)
- 是否包含不完整语句或明显逻辑断裂
- 是否包含未经查证的具体数字
- 是否带有会让读者误以为是官方账号的表述
这一轮自检非常重要。AI 生成内容速度快,但它在事实准确性和风格一致性上并不稳定。没有自检直接发的后果是:偶尔冒出一条没头没尾的推文,粉丝一看就觉得是机器人乱发。不是不能有机器味,而是你得保证即便机器味,内容质量也在线。
5. 常见问题速查与踩坑实录
5.1 高频问题速查表
我把三个星期里遇到的所有问题汇总成了一张表,方便你排查。
| 现象 | 原因 | 解法 |
|---|---|---|
| 发推返回 403 Forbidden | 应用权限不是“读+写” | 去开发者平台改权限并重新生成密钥 |
| 发推返回 401 Unauthorized | 密钥错误或 OAuth 1.0a 配置没开 | 检查 .env 四个字段,确认 User Authentication 开启 |
| 返回 187 Duplicate | 推文内容和历史推文完全重复 | 在 SKILL.md 中增加“避免和过去三条完全一样的开头” |
| 推文显示不全 | 长度超限被截断 | 脚本强制 280 字符校验,不要依赖模型自觉 |
| cron 不执行 | 环境变量或 python 路径错误 | 用绝对路径,命令先手动跑通再加 cron |
| 文本多出星号和引号 | Claude 习惯了 Markdown 语法 | 脚本中做字符清理 |
| 连续发两条内容雷同 | 主题规划没做去重 | 让 Claude 先读历史日志再选主题 |
这张表覆盖了我 95% 的问题场景。每次新增问题我都往这个表里加一条,现在排查难度大幅下降。
5.2 三个值得单独展开的坑
第一个坑是 OAuth 权限配置。创建 X 应用时,默认权限是“只读”,如果你不看清楚就直接生成密钥,后面所有发推请求都会失败。改权限后,注意旧的密钥要重新生成,旧的 Access Token 也会失效。这一步必须从头刷新一遍。
第二个坑是重复内容检测。X 对完全相同或高度相似的推文有硬性去重机制。刚开始跑的时候,上午十点和下午三点各发一条,结果第二条约十分钟后收到错误码 187。原因是两条推文开头句式太像,被系统判定为重复。后来我要求 Claude 在写下午和晚上推文之前,先读一条当天早上的推文,换句式、换切入点,问题就消失了。
第三个坑是 Claude 生成的文本内容带了 Markdown 痕迹。它会把词语包上星号或者行首加井号,明晃晃的**重点**出现在推文里,非常难看。单纯在 prompt 里说“不要 Markdown”效果不稳定,最后还是在代码层做了全面清理才彻底解决。经验就是:AI 输出别全信,能代码校验就代码校验。
5.3 内容调优的复盘笔记
运行三周后,我拉了一份数据对比。第一周发的内容比较“AI 味重”,也就是那种“今天我们来聊聊XX”的句式,点赞和转发都少。第二周开始强制改开头,要求必须用结论或观点开头,互动量明显上升。
比如这两个开头:
- 差:“今天我们聊聊如何提高个人生产力”
- 好:“个人生产力提升的秘诀,不在于做更多事,而在于减少‘伪工作’”
同样一个主题,换个开头,阅读体验完全不同。这个调整在 SKILL.md 的 prompt 里写死之后,整体互动数据稳定了一个档位。这也是我推荐把规则写进 SKILL.md 而不是写进代码的原因——你可以一直做内容层面的快速迭代,而不需要重新部署代码。
写在最后
运行这套东西到现在,最大的体会是:自动化不是“把人力变成 0”,而是“把低水平的重复工作交给机器,自己只留判断力”。我现在每天打开后台看一眼发布日志,偶尔手动调整一下第二天要发布的主题范围,真正需要人工干预的时间不超过五分钟。
最后再分享一个小习惯:每周我会把发布日志导出来,统计一下互动量最高的三条推文分别是什么主题、什么句式,然后把“高互动主题方向”追加到 SKILL.md 的 prompt 里。这套 Skill 不是一次搭完就固定不变,它更像一个持续进出的内容机器人,规则你可以反复在文本层面调整,成本基本为零。你要是也想搞一套属于自己的全自动发推流水线,按这个流程走一遍,应该比我想象中顺利不少。