wjs-x-improving-content

$npx mdskill add jianshuo/claude-skills/wjs-x-improving-content

Iterate on X content prompt to maximize tweet impressions via A/B testing.

  • Systematically improve X (Twitter) content by iterating on the content-generation prompt.
  • Depends on git for versioned prompt experiments and X impression data per tweet.
  • Judges prompt versions by median impressions per tweet, mining content features for next edit.
  • Delivers prompt edits as numbered, hypothesis-driven experiments with git SHA versioning.

SKILL.md

.github/skills/wjs-x-improving-contentView on GitHub ↗
---
name: wjs-x-improving-content
description: Use when 王建硕 wants to systematically improve his X (Twitter) content by iterating on the content-generation prompt (prompts/x/prompt.md, used by the every-6h tweet Action) and finding which prompt version produces the highest-reach tweets. Each prompt edit is a git-SHA-versioned, numbered experiment with a hypothesis; tweets are attributed to the version live at post time and judged on median impressions per tweet. Also mines per-tweet impression data for content-feature signals (angle / length / topic) that feed the next prompt edit. North-star = impressions per tweet. Triggers — "改 X 的 prompt", "X 内容改进", "哪版 prompt 最好", "什么内容 impression 高", "improve my tweets", "A/B test the X prompt", "/wjs-x-improving-content".
---

# wjs-x-improving-content

把「写好推」当工程做:**不断改 `prompts/x/prompt.md`,用 impression 数据看哪版最好**,并挖出「什么内容特征和高 impression 相关」反哺下一版。是 [[wjs-x-increasing-follower]] 的孪生——那个测 profile→关注转化率,这个测 **prompt→每条推的 impression**。

## Core Principle

**impression 主要由源文章 / 话题决定,prompt 只是二阶因素。** 一篇好文章配任何 prompt 都能爆。所以诚实地分两层看:

| 看什么 | 信号强度 | 怎么用 |
|---|---|---|
| **prompt 版本对比**(哪版 prompt 的推中位 impression 高) | 弱(被文章支配,需大量样本) | 方向性参考,攒够样本才下判决 |
| **内容特征**(angle A/B/C、长度、钩子——prompt 直接控制的东西) | 较强(同样话题下,特征差异才显出 prompt 的手艺) | **真正反哺 prompt 的依据** |

**所以:版本对比给方向,内容特征给抓手。** 别把版本判决当因果。

**判决用中位数不用均值**(impression 极度长尾,一条爆款骗死均值);**每版至少 5 条成熟推**才下版本级判决;**成熟窗 = 发布满 3 天**(impression 还在涨的太新推不计入)。

**回滚是一等公民**:prompt 在 git 里,回滚 = `git checkout <旧SHA> -- prompts/x/prompt.md`。

## 版本 = prompt.md 的 git short-SHA

每条推归到哪版 prompt,**按时间推导**:推发布时间 T → `prompts/x/prompt.md` git 历史里时间 ≤ T 的最后一次提交 = 那条推用的版本。**不用改 Action**,历史推也能回填。早于 prompt 文件存在的推 → `prompt_sha=null`(pre-prompt)。

## 数据从哪来

每条推的 impression **X API 不稳**,靠 **Content CSV 导出**:`x.com/i/account_analytics` → **Content** 标签 → 导出 CSV(含 Post id / Impressions / Engagements …)→ 丢进 `inbox/`。`Post id` 就是 `tweet_id`,和发推历史对得上。

## When This Skill Fires

- 「改 X 的 prompt」「哪版 prompt 最好」「什么内容 impression 高」「X 内容改进」
- 跑 `/wjs-x-improving-content`

## When NOT to use

- 涨粉 / 改 profile → [[wjs-x-increasing-follower]]
- 只是发一条推 → `/wjs-tweeting-from-articles`
- 推广 skill → `/wjs-promoting-skills`

---

## Workflow

脚本在 `scripts/`,状态在 `state/`。先 `cd` 到 skill 目录。

### Step 1 — 吃数据

```bash
python3 scripts/ingest-tweets.py /path/to/content.csv
```

join Content CSV + 发推历史(`~/.claude/skills/wjs-tweeting-from-articles/state/history.jsonl`,带 slug/angle)→ `state/tweets.jsonl`,按日期推导 `prompt_sha`,算 `char_len` 和 `mature`(≥3天)。upsert,重跑更长导出安全。

### Step 2 — 挖内容特征(核心,立刻有用)

```bash
python3 scripts/analyze-content.py          # 成熟推
python3 scripts/analyze-content.py --all     # 含未成熟(angle 样本更全)
```

按 angle / 长度 / 来源拆 impression 中位数 + 互动率,列最高/最低推。**这层告诉你 prompt 该往哪改。**

### Step 3 — 提一版 prompt 改动(带假设)

据 Step 2 的信号,对 `prompts/x/prompt.md` 做**一个可证伪的改动**(例:「偏短句」「在拿不准时优先选金句 angle」)。改完 commit:

```bash
cd ~/code/wechat-publish
# 编辑 prompts/x/prompt.md ...
git add prompts/x/prompt.md && git commit -m "x prompt: <一句话改了啥>"
NEW_SHA=$(git log -1 --format=%h -- prompts/x/prompt.md)
```

登记成编号实验:

```bash
python3 ~/.claude/skills/wjs-x-improving-content/scripts/ledger.py register "$NEW_SHA" \
  --hypothesis "短句比长句 impression 高,prompt 收紧到 80 字以内"
```

之后每 6h 的 Action 自动用新版生成推。**一次只改一处**,否则分不清哪个改动起的作用。

### Step 4 — 攒够样本后判决

```bash
python3 scripts/evaluate.py     # 各版本中位 impression + 相邻版本 Δ% 判决
```

`Δ ≥ +10%` → **keep**;`Δ ≤ -10%` → **rollback**;之间 → **flat**。样本不足(<5 条成熟推)显示 measuring。

- keep:`ledger.py keep <SHA> --note "短句 +18%"`
- rollback(先问王建硕):
  ```bash
  git -C ~/code/wechat-publish checkout <旧SHA> -- prompts/x/prompt.md
  git -C ~/code/wechat-publish commit -m "x prompt: rollback to <旧SHA>"
  ```
  再 `ledger.py rollback <SHA> --note "短句反而掉了"`

### Step 5 — 看板

```bash
python3 scripts/scoreboard.py    # 写并打印 state/SCOREBOARD.md
```

现状 + 版本排行榜 + 内容特征(angle)+ to-do。给王建硕看就发这个。

---

## 数据模型(state/)

- `tweets.jsonl` —— 一推一行:`{tweet_id, date, impressions, engagements, likes, replies, reposts, new_follows, char_len, text, slug, angle, source(bot|manual), prompt_sha, age_days, mature}`
- `versions.jsonl` —— 一 prompt 版本一行:`{id, prompt_sha, hypothesis, registered, status(active|kept|rolled_back), verdict, notes}`
- `SCOREBOARD.md` —— 生成物

## 默认参数(要改传 flag / 改 _common.py)

- 成熟窗 `MATURITY_DAYS=3`
- 版本判决最少样本 `MIN_TWEETS_PER_VERSION=5`
- keep/rollback 阈值 `--threshold 0.10`
- 回滚 prompt **永远先问**

## 路径假设(_common.py 顶部,换机器改这里)

- 发推历史:`~/.claude/skills/wjs-tweeting-from-articles/state/history.jsonl`
- prompt 文件:`~/code/wechat-publish/prompts/x/prompt.md`

More from jianshuo/claude-skills

SkillDescription
wangjianshuo-perspective|
wjs-auditing-projectUse when the user asks to audit what's wrong with a project, "make it right", "看看项目出了什么问题", "为什么用户的需求还没上线", "为什么没提交App Store", "为什么没新build", or wants a holistic state-of-the-project check covering unmerged branches, stalled PRs, failed GitHub Actions, stale builds, plan drift (TODOS.md / ROADMAP), unreleased commits, and log errors. Runs read-only investigation, presents a grouped checklist, fixes only after explicit user confirmation. Aware of the Cathier iOS app workflow (Xcode + fastlane + auto-merge @claude PRs from in-app feedback).
wjs-burning-subtitlesUse when the user has a video + an SRT and wants the subtitles either burned into the pixels (libass, always-visible) or soft-muxed as a togglable track. Also handles the final composite step for the localization pipeline — burn subs, mix a dub track, and keep the original audio as a low-volume bed, all in ONE ffmpeg encode (no cascade). Verifies libass availability and auto-downloads a static evermeet ffmpeg build when Homebrew's stripped binary lacks it. Triggers — "烧字幕", "硬字幕", "burn subtitles", "burn-in subs", "embed subtitle", "soft mux SRT", "把字幕烧进视频", "做最终合成".
wjs-cleaning-spamUse when the user complains about spam on his X/Twitter posts — 同城面付 / 寻固炮 / 线下上门 / 免费破处 这类引流号在他推文下刷的 emoji 垃圾回复 — and wants them removed. Covers the last 7 days (X recent-search window). Triggers — "把这些spam删掉", "清理X垃圾回复", "推文下面好多引流号", "clean spam replies", "/wjs-cleaning-spam".
wjs-converting-text-to-videoUse when the user wants a 王建硕-style WeChat article (article.md) turned into a narrated short MP4 video — TTS voiceover via 火山引擎 Volcano TTS, HyperFrames CSS/GSAP animation per scene, subtle SFX, abstract watercolor background, full pipeline rendering to 1080×1920 portrait MP4 (30-90s). Triggers — "把这篇文章做成视频", "做一个解说视频", "讲解视频", "/wjs-converting-text-to-video".
wjs-converting-wp-to-hugoUse when migrating a WordPress site to a Hugo static site on GitHub Pages from a WXR export (.xml) plus the wp-content/uploads folder — preserving /archives/<id>/ URLs, localizing images, and deploying via GitHub Actions. Triggers — "把 WordPress 迁成 Hugo", "wordpress 转静态站", "migrate WordPress to Hugo", "WXR to Hugo", "publish WordPress to GitHub Pages", "/wjs-converting-wp-to-hugo".
wjs-distilling-styleUse when the user wants to capture a writer's voice from a few sample articles and rewrite text to sound like that author — "蒸馏文风", "提炼XX的风格", "把这篇改成XX的味儿", "学某某笔法改写", "mimic this writer", "match this author's style", "style transfer". Works for any author, 王建硕 included.
wjs-dubbing-videoUse when the user has a video + a target-language SRT and wants the video to actually speak that language — generates a time-aligned TTS voice dub. Routes by voice ID — Volcano (豆包) TTS for Chinese, edge-tts neural for any language. Defaults to one voice (single-speaker); opt-in multi-speaker via visual diarization. Outputs `*_<lang>_dub.mp4` with the dub audio in place of the original. Final mixing (audio bed + burn-in) is handed off to `/wjs-burning-subtitles`. Triggers — "配音", "中文配音", "Chinese dub", "voice over this", "dub the video", "TTS this SRT", "different voice for each speaker".
wjs-eating-and-growing吃一堑长一智 — 走完 5 步交互式反思(堑 → 自动输出 → 旧权重 → 新参数 → 替代动作),从「情绪复盘」推进到「行为训练」,把第一反应这一层 L3 权重练新。Use when 王建硕 reflects on a personal setback, mistake, or recurring pattern (反思, 复盘, 回顾, 总结教训, 吃一堑, 长一智, "这次又栽了", "怎么又这样", "为什么我总是…", "想开点都做不到", "知道道理但做不到"). For the user as a human, not for Claude's task post-mortems.
wjs-editing-multicamUse when the user has 2+ recordings of the same event (each with a `.sync.json` sidecar from wjs-syncing-multicam) and wants them combined into a single MP4 — auto-switching between cams second-by-second on audio energy, with optional picture-in-picture inset. Triggers — "auto-edit multicam", "做个剪辑", "切几个机位", "把这几个视频合成一个", "combine these angles", "PiP overlay".