这段时间我一直被“写推文”这件事烦着:灵感来了没时间,时间来了没灵感,好不容易挤出一段话,又得复制到X(推特)网页端,点个发布还要纠结半天配图有没有、链接放哪里、话题标签怎么排。于是我想清楚了一件事——与其每天手动作业,不如把这条链路打包成一个Claude Skills,让AI不只是“给建议”,而是把“生成内容到调API发布”整套动作都接过去。我从0到1搓出了这个技能,现在只要给一个主题,它就能自动生成推文并发出去,全程基本不需要我再碰发布页面。这篇教程我尽量写完整:Skills的文件结构、提示词设计、X平台API接入、定时自动发布、常见问题,全部摊开讲。适合会用Python、但还没碰过平台技能扩展开发的朋友,照着一路做下去,差不多半天就能跑通。
1. 为什么非要搓一个“自动发推”的技能,而不是多开几个对话窗口
1.1 真正的负担不在“写”,而在“写完到发出去”之间
刚开始我也天真地以为,AI写作已经这么成熟了,想一段推文还不简单?直接在对话里说“帮我写一条关于AI绘画的推文”就行。实际上,偶尔一两条确实没问题,但每天都要更新账号内容的人会发现,真正的耗时点根本不是生成文案这件事,而是那套重复劳动:内容生成以后要自己检查长度、加话题标签、调整语气,再打开X网页版或手机App,粘贴,排版,检查有没有漏掉链接,最后才敢点发布。这个过程一天重复三五次,心情很快就没了。
所以我的核心目标不是“让AI内容更强”,而是“把发布推文这个动作本身自动化”。内容可以慢慢调,但步骤必须能一键跑完。想清楚这一点后,我决定做的东西不是普通的聊天prompt,而是一个可复用的技能包:以后不管谁来问,只要输入主题,系统就按固定流程把草稿、导出、发布整串动作执行完。
1.2 为什么选Skills,而不是直接写脚本完事
其实这个需求用纯脚本也能做:写一个Python程序,从命令行接收主题,组合一批模板句子,再调用接口发出去。但只写脚本有几个很现实的问题。第一,模板生成的文案像复读机,没有语义理解能力,稍微换个不常见的话题,内容就变得莫名其妙。第二,每次想调整语气、换角度、模仿新风格,都要去改代码模板,维护成本很高。
Skills的价值在于把“模型的理解能力”和“脚本的执行能力”拼到一起。模型负责动脑子,脚本负责动手。我只需要在这个技能包中定义清楚什么场景触发、按什么顺序执行、调用哪个脚本,之后模型会在合适的时候主动把脚本捡起来用。这比纯脚本灵活得多,也比每次面对空对话窗口从头交代上下文靠谱得多。
1.3 动手之前,我确认了三件最基础的事
我不是那种“想到就干、干到一半发现平台没开权限”的人,所以开工前先把前提条件列了一下。一是确认目标账号是否有开发者权限,至少能创建应用并且拿到读写权限;二是本地得有能跑Python脚本的环境,最好已经装过 requests 这类常用库;三是想清楚发推文的用途,纯个人分享和营销运营的玩法完全不同,这会影响提示词风格和发布频率。
这三件事都确认没问题之后,我才开始搭目录、写SKILL.md。如果你打算照做,也先把这三个前提过一遍,免得后面做到API接入那一步才发现账号权限不够,又要回头去申请。
2. Skills到底是什么:先搞清楚这把“螺丝刀”的结构
2.1 一个最小可用的Skill,由哪几个文件组成
在我动手之前,我对Skills的第一印象是“大概是个配置文件”。等我拆开看过之后发现,事情没那么神秘,核心其实就是三块东西:一份说明文档、一个或多个可执行脚本、一些辅助资源。
说明文档是入口,通常命名为 SKILL.md,里面写清楚这个技能叫什么、解决什么问题、在什么场景下被触发、具体操作步骤是什么。脚本用来完成实际动作,比如生成草稿、调用发布接口。辅助资源可以是示例模板、参考推文、常用话题清单等,模型会根据任务内容自行参考。
这三个部分不是随便堆在一起,而是有组织方式的。我给自己的技能建了一个目录,结构大约长这样:
skill-x-auto-poster/ ├── SKILL.md ├── scripts/ │ ├── generate_draft.py │ └── publish_tweet.py ├── templates/ │ └── tweet_style.md └── assets/ └── examples.mdSKILL.md 在最外层,跟 scripts、templates 平级。scripts 里放可执行脚本,templates 里放提示词模板和风格参考,assets 里放一些辅助数据。这样做的好处是,模型发现技能后能很快定位到该看什么文件,不会在几十个文件夹里迷路。
2.2 SKILL.md 是怎么“告诉”模型该什么时候出手的
SKILL.md 很像一份给“新员工”看的操作手册。文件的顶部通常需要填一组元信息,包括技能名称和描述。这个描述非常重要,因为模型在处理用户请求时,会先根据描述判断当前这个任务匹配不匹配该技能。描述写得太宽,模型什么任务都可能会尝试调用,导致误触发;写得太窄,模型又识别不出来,技能形同虚设。
我最初写的版本一直没被触发,后来才发现是描述不够准确。我用了“和使用者互动”这种模糊说法,模型根本不知道该技能是干嘛的。改成“当用户要求生成或发布X平台推文、处理推特内容更新时使用”之后,情况立刻就好了。原理不复杂:描述就是给模型看的“检索标签”,把触发场景描述得越具体、越贴近用户真实表达,命中率越高。
2.3 它和普通API封装或者插件的区别在哪里
有些人会问:既然最后还是调用脚本,那为什么不直接做一个API服务?我个人的理解是,API服务解决的是“系统之间怎么通信”的问题,而Skill解决的是“模型在对话中如何自主决定使用什么工具”的问题。前者你把一切流程写死,后者你把判断权交给模型,让模型根据上下文动态决定要不要调用脚本、调用哪个脚本、传什么参数。
这带来一个明显好处:交互方式变得非常自然。我不需要记住命令行参数,只要说“帮我发一条关于远程工作的推文”,模型就会拆解需求、套用模板、生成内容,再决定调用发布脚本。整套流程对我来说只有一句话,剩下的全部在技能内部完成。这种体验是传统脚本封装很难给的。
3. 动手搭骨架:环境准备、目录结构与第一版SKILL.md
3.1 本地环境依赖清单
我建议先列一个最小的环境清单,避免中途手忙脚乱。我自己的电脑上是这样一套组合:
| 依赖 | 用途 | 备注 |
|---|---|---|
| Python 3.10+ | 脚本运行环境 | 3.8应该也能跑,但3.10更省心 |
| requests、requests_oauthlib | 调用X平台发布接口 | OAuth签名时用到 |
| python-dotenv | 管理本地密钥 | 避免密钥写死在脚本里 |
| 支持加载Skills的客户端环境 | 验证技能触发和调用逻辑 | 不一定需要GUI,命令行方式也行 |
如果你不打算本地跑定时任务,只是想验证技能能不能用,那 Python 环境和 requests 库就够了。密钥管理很重要,千万别偷懒,后面我会专门讲。
3.2 目录这样建,后面扩展最舒服
我一开始文件的命名很随意,后来吃了亏才整理成上面那个结构。核心建议就一条:把“生成草稿”和“发布推文”拆成两个独立脚本。为什么?因为日常调试时,你大概率只想先生成一条内容看看效果,并不想真的把它发出去。两个脚本分开以后,我可以单独跑生成脚本做内容调试,等文案满意了再单独跑发布脚本。混在一起的话,每次调试都会发一条测试推文,账号很容易被弄成“垃圾内容账号”。
另外,templates 目录里我放了一份 tweet_style.md,里面记录了目标账号常用的口吻、常用话题标签、典型句式。模型生成推文前会参考这份文件,这比把风格要求堆在提示词里干净得多。
3.3 第一版 SKILL.md 这样写,基本能跑
我参考了常见的技能包写法,写出来的第一版 SKILL.md 大概长这样:
--- name: x-auto-poster description: 当用户要求生成、编辑、发布X平台推文,或想自动更新自己的社交账号内容时使用。 --- # X自动发推技能 ## 使用场景 - 用户给出一个话题、一段想法或一篇长文,希望浓缩成推文。 - 用户希望按固定时间或固定频率发布内容。 - 用户希望自动把生成的草稿发布到X平台。 ## 工作流程 1. 先读取 templates/tweet_style.md,了解目标账号的语感和风格。 2. 根据用户的输入主题,在草稿中生成至少3条候选推文。 3. 将候选推文展示给用户确认,除非用户明确表示不需要确认。 4. 调用 scripts/publish_tweet.py 发布确认后的推文。 ## 注意事项 - 推文长度不能超过280个字符。 - 不要在一条推文里堆超过3个话题标签。 - 发布前必须再次检查文案中是否有明显事实错误。这个版本的流程是“先给候选,再确认,再发布”。为什么不上来就全自动?因为早期调试阶段,模型的输出风格还不稳定,你让它直接发出去风险很大。等观察一段时间,确定内容风格没问题了,再把“是否需要确认”这个节点改成默认跳过。
3.4 第一轮验证:先分别跑脚本,再整链路测
我已经把目录结构和SKILL.md写好了,接下来要验证两件事:脚本本身有没有bug,SKILL.md能不能被模型正确识别。验证方法很简单,第一步手动跑发布脚本。
python scripts/publish_tweet.py --text "这是一条测试推文"这能确认API密钥、权限、网络连通性都没问题。第二步回到对话环境,向模型发出一个自然语言请求,比如“用这个技能写一条关于效率工具的推文草稿”,看看模型会不会主动去读templates里那份风格文件,而不是凭空发挥。如果模型完全无视技能,说明SKILL.md的描述和写法有问题;如果模型能正确提到要调用哪个脚本,就说明技能已经被识别到了。
我当时的测试结果很典型:第一条自然语言请求,模型写出来的文案确实像样,结构完整,也有话题标签,但风格完全不对——它用了非常正式的书面语,一看就不是社交平台原生内容。这说明脚本没问题,问题出在提示词模板和风格参考文件写得太笼统。于是我把工作重心转到了提示词设计上。
4. 让AI写出“有网感”的推文:提示词模板是这样反复调出来的
4.1 为什么通用prompt写不出平台原生内容
直接让模型“写一条不错的推文”,十次有八次会得到一段规规矩矩、放在官网新闻稿里都毫无违和感的文字。原因是模型在没有明确约束时,会倾向于输出四平八稳的“标准答案”:结构完整、语法正确、语气中性。但X平台上的好内容恰恰相反:口语化、有棱角、像真人在说话,甚至允许有一点不完整句子。
所以问题的本质不是模型不会写,而是你没有给它“平台内容风格约束”。这就像让一个写惯了公文的人去发朋友圈,他不一定写不好,只是没人告诉他该用什么调子。Skills里的提示词模板正好用来做这件事。
4.2 我最终沉淀下来的提示词模板结构
经过几轮测试,我把模板固定成以下几个字段,每次生成推文时按这个结构填空:
| 字段 | 作用 | 示例 |
|---|---|---|
| 内容主题 | 推文要表达的核心信息 | 远程办公的三种状态切换 |
| 目标读者 | 写给谁看的 | 自由职业者、远程工作者 |
| 语气风格 | 口语/专业/幽默/犀利 | 轻快但不轻浮 |
| 调用动作 | 希望读者做什么 | 评论区聊聊你的状态切换方法 |
| 字数上限 | 硬性卡点 | 不超过260字符 |
| 话题标签 | 关键流量词 | #远程办公 #效率 |
对应的提示词模板我放在 templates 里,大概是这样一段:
你是目标账号的资深运营编辑。现在需要为以下主题生成一条X平台推文。 主题:{topic} 目标读者:{audience} 语气风格:{tone} 推文要求: - 口语化表达,像真人分享,不要新闻稿腔。 - 最多2个话题标签。 - 全文不超过260字符。 - 结尾自然引导互动,不要生硬喊“点赞收藏”。 参考templates/tweet_style.md中的历史风格,先列出3条候选,每条控制在1-2句话,有独立角度。这里最关键的是“像真人分享”和“不要生硬喊点赞收藏”这两句。我试过没有这两句的版本,模型写出来的内容要么过于热情,要么过于机械;“自然引导互动”这句加上以后,效果明显改善。
4.3 实测反馈:什么样的内容会被一眼识破
调试过程中我积累了几个很典型的反面案例。第一种是空泛金句型,比如“效率不是做更多事,而是做最重要的事”,听起来没毛病,但没有信息增量,放在任何账号上都可以,等于没说。第二种是术语堆砌型,把所有流行概念都塞进去,读起来像机翻论文。第三种是过度互动型,每条都要“评论区告诉我你怎么看”,看多了像营销号。
针对这三种问题,我调整模板时加入了一条硬性要求:内容里必须包含一个具体细节或一个明确观点,不许写正确的废话。少一点空话,多一点“我就是这么干的,效果如下”式的表达,内容质感提升非常明显。这个过程只能靠反复测试,没有捷径。我建议你也像我一样,把每次生成失败的类型记录下来,再反哺给提示词模板。
5. 打通“写”到“发”的最后一公里:X平台API接入
5.1 权限申请:先确认账号有“写”的能力
发布脚本本身不难写,难的是账号权限。X平台的API接入和其他开放平台大同小异:你需要先拥有开发者账号,然后创建一个应用,申请到读、写权限,拿到几组密钥。
这里最容易踩的坑是权限等级。开发者在申请的时候默认可能是只读权限,你调用发布接口时会一直报权限错误。所以在创建应用的时候就要把权限选到“读+写”,Save之后还要重新生成一遍密钥,因为权限变更之前生成的密钥未必会同步更新。申请通过之后,你会得到四样东西:API Key、API Secret、Access Token、Access Token Secret。这四样好好收着,别发给任何人。
5.2 发布脚本:一次完整的v2接口调用
我用的发布脚本逻辑很简单:接收文本参数,加上OAuth签名,POST到发布接口。核心代码差不多是这样:
import os import requests from requests_oauthlib import OAuth1 API_KEY = os.getenv("X_API_KEY") API_SECRET = os.getenv("X_API_SECRET") ACCESS_TOKEN = os.getenv("X_ACCESS_TOKEN") ACCESS_TOKEN_SECRET = os.getenv("X_ACCESS_TOKEN_SECRET") X_API_BASE = os.getenv("X_API_BASE", "https://api.x.com/2/tweets") def post_tweet(text: str): auth = OAuth1(API_KEY, API_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET) payload = {"text": text} resp = requests.post(X_API_BASE, json=payload, auth=auth) if resp.status_code == 201: return resp.json().get("data", {}).get("id") else: raise RuntimeError(f"发布失败: {resp.status_code} {resp.text}") if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument("--text", required=True) args = parser.parse_args() tweet_id = post_tweet(args.text) print(f"发布成功,推文ID: {tweet_id}")代码里所有密钥都是从环境变量读取的,脚本本身不保存任何敏感信息。这是安全基线,不是可选项。413、401、403这些状态码我后面会专门整理一个排查表,你遇到的时候可以对照着看。
5.3 密钥安全:差点把Token写进SKILL.md的那次教训
前面提到我用 .env 管理密钥,这里多说几句。第一次写发布脚本时,我真的图省事把某个密钥直接写死在脚本里了,想着“反正本地跑,没关系”。后来要把这个技能分享给别人参考时,才意识到这会造成多大风险——一旦文件外传,等于直接把账号管理权拱手送人。
所以现在我的规矩是:所有密钥一律放 .env 文件,脚本里只使用os.getenv()读取;.env 和 .gitignore 要把 env 文件排除掉;SKILL.md 和模板里绝对不能出现密钥明文。这个习惯一旦养成,后续无论做多少自动化项目都受益。
5.4 发一条真测试推文:看到返回ID才算通
写完全套代码之后,我先用一条“测试推文”验证链路:
python scripts/publish_tweet.py --text "测试自动化发布,稍后删除。"脚本运行成功后,控制台会打印一个推文ID。然后可以登录账号手动删除这条测试内容。看到返回ID的那一刻,说明整个技术链路已经通了:模型生成内容、脚本接收参数、API完成发布。后面要做的就只剩下把模型输出和脚本调用串成一条完整的自动链路。
6. 端到端联调:从一句“发条推文”到推文自动落地
6.1 一次完整调用的流程拆解
链路打通之后,一次完整的调用大概是这样走的。用户说“整理一篇关于时间管理工具的推文”,模型先判断这属于技能的使用范围,于是读取 SKILL.md;接着读取 templates/tweet_style.md 学习账号风格;然后根据模板生成内容。如果配置为需要确认,模型会把内容先展示出来;如果确认通过或配置为自动发布,模型就把文本传给 publish_tweet.py,脚本完成接口调用,最后把推文链接或ID返回给对话。
这个流程在对话里看起来只是几句话,背后其实涉及文件读取、模板块、脚本执行、网络请求好几个环节。任何一个环节出问题,表现都会像技能“不听话”。所以我用了一个笨办法定位问题:手动一步步执行,看问题出在哪一层。
6.2 定时自动化:让系统每天自己发推
要让整个系统“全自动”,还需要一个定时触发机制。最轻量的方式是使用系统自带的计划任务。
我实际用的是 cron,配置大概长这样:
0 9 * * * cd /path/to/skill-x-auto-poster && python scripts/scheduled_post.py --topic "今日AI资讯整理" >> logs/poster.log 2>&1这里的scheduled_post.py是一个调度脚本,它和对话环境无关,直接读取当天想发的话题,调用生成脚本,再把内容交给发布脚本。如果某些内容需要模型参与生成,调度脚本也可以先调用对应的模型API拿到结果再发布。定时任务引出一个新的问题:时区。cron默认按服务器本地时间跑,你得先确认机器时区是不是自己想要的时区,别让每天九点的发出时间变成凌晨三点。
6.3 异常处理:限流、重复发布、凭据失效的兜底方案
自动发布最怕的就是出问题还没有提示。我在这套流程里加了几层基本的保护。第一层是限流处理,如果接口返回429或者超时,脚本就等一段时间再重试,而不是立刻报错退出。第二层是防重复发布,本地维护一个 last_post.json 文件,记录最近一次发布的内容,如果定时任务被误触发或者不小心重复运行,脚本会拒绝再次发布相同内容。
{ "last_text": "测试自动化发布,稍后删除。", "last_time": "2025-01-12T09:00:00", "last_tweet_id": "123456789" }第三层是凭据失效检测,401状态码基本说明密钥有问题,这种情况脚本会把连带内容写到一份待审核队列里,而不是卡死或者无限重试。有了这三层保护,就算某个环节出错,也能保证不会把账号刷爆或者发出一堆重复内容。
7. 我踩过的坑,以及这套方案还能往哪个方向长
7.1 三个最痛的教训,先帮你避开
第一个坑就是SKILL.md描述写得太模糊,导致模型经常不触发技能。这个问题排查起来非常费时间,因为表面上一切正常,没有报错,但结果就是模型完全不按技能走。最后我只能逐字读描述,把它改得和用户日常表达高度一致,触发率才稳定下来。
第二个坑是密钥差点外泄,这个前面讲过了,就不再重复,但值得再强调一遍:密钥永远不要写进任何会被分享、上传、备份的文件里。第三个坑是定时任务时区错乱,我一开始没注意机器时区,导致推文全部在凌晨发布,账号粉丝在那个时间段活跃度很低,内容效果差很多。调整时区后发布数据立刻恢复正常。
下面这张表是我后来整理的常见问题排查表,直接对着看能省很多时间:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 401 Unauthorized | 密钥错误或权限不足 | 重新生成密钥,确认读写权限 |
| 403 Forbidden | 无发布权限 | 检查开发者应用权限设置 |
| 429 Too Many Requests | 触发限流 | 放慢频率,加退避重试 |
| 201但未出现推文 | 发布的是草稿或时间线延迟 | 检查账号时间线,稍等刷新 |
| 模型不触发Skill | SKILL.md描述不精确 | 把描述改成贴近用户表达的话 |
| 定时任务不运行 | 时区/路径/环境变量问题 | 检查cron日志,改用绝对路径 |
7.2 这套方案的适用边界,别盲目指望全自动
必须说清楚,这套“全自动发推”方案并不是所有场景都适合。对我这种个人账号、垂直领域内容、需要持续但有节奏产出的人来说,它确实帮了大忙。但如果你运营的是品牌官方账号,或者内容涉及公关、法律、医疗等高风险领域,我不建议全程无人审核。平台对机器化发布内容也有一定的风控机制,过于高频或模板痕迹过重的发布行为可能影响账号正常使用。
我这里做了一个很务实的取舍:生成环节完全自动,发布环节默认仍然有人工确认。只有在某个固定专题、已经连续验证了一个月、内容风格也稳定之后,我才敢让它跳过确认直接发。全自动和可控之间,平衡点要靠自己把控。
7.3 后续的几个扩展方向,我觉得很值得试
这套技能包的扩展空间其实很大。我下一步准备做三件事:一是接入RSS订阅源,让脚本自动抓取几个固定网站的更新摘要,作为“今日主题”直接喂给生成脚本,这样连选题都不用手动设了;二是通过X平台的公开数据分析不同话题标签下的互动量,把表现好的话题自动沉淀成模板参考,反哺给提示词;三是把发布范围从单条推文扩展到多平台内容矩阵,同样的文案改成不同平台的语感风格再分发。
做完这套之后我一个很深的体会是:工具能替你省掉八成重复动作,但剩下两成判断,比如什么内容值得发、以什么频率发,还是得自己来。第一次跑通全自动发布那个晚上,我盯着准备发出的那条推文看了十分钟,才舍得让它真的发出去。现在我已经习惯睡前把第二天的选题丢进去,醒来再看结果。最后分享一个小技巧:新技能上线头两周,建议先发到测试账号,或者至少保持二次确认发布,稳定后再切换到正式账号。别一上来就对着主账号全自动,给彼此一点磨合期。