ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Claude Skills + X API + cron 实现全自动发推实战指南

Claude Skills + X API + cron 实现全自动发推实战指南

先说我这边最终跑通的效果:一个 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.py

SKILL.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 不是一次搭完就固定不变,它更像一个持续进出的内容机器人,规则你可以反复在文本层面调整,成本基本为零。你要是也想搞一套属于自己的全自动发推流水线,按这个流程走一遍,应该比我想象中顺利不少。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进