Overview
Turning a single long‑form asset — such as a conference talk, a deep‑dive blog post, or a video tutorial — into seven daily social‑media posts is a repeatable engineering problem. The pipeline below treats the source as immutable data, runs a series of deterministic transforms, and hands the final artefacts to a scheduler. All steps are scriptable, version‑controlled, and can be inspected locally before any external API is called.
Extract source material
Start by pulling the raw transcript or markdown into a local directory. If the source is a video, yt-dlp can download the best‑quality audio and ffmpeg can convert it to a 16 kHz mono WAV that works well with most speech‑to‑text models.
# download audio only
yt-dlp -x --audio-format wav --audio-quality 0 "https://example.com/talk" -o "source/%(title)s.%(ext)s"
# normalize to 16 kHz mono
ffmpeg -i source/*.wav -ar 16000 -ac 1 source/normalized.wav
For a written article, a simple git clone or curl fetch is enough. Keep the original file under source/ so the pipeline remains idempotent.
Chunk and embed
Large language models have a token window; feeding the whole transcript at once leads to loss of detail. Split the text into overlapping windows of ~1,200 tokens (≈900 words) using a lightweight tokenizer such as tiktoken. Store each chunk with its character offsets for later citation.
import tiktoken, json, pathlib
enc = tiktoken.get_encoding("cl100k_base")
text = pathlib.Path("source/normalized.txt").read_text()
tokens = enc.encode(text)
chunks = []
for i in range(0, len(tokens), 1000):
window = tokens[i:i+1200]
chunks.append({
"index": len(chunks),
"text": enc.decode(window),
"start_token": i,
"end_token": min(i+1200, len(tokens))
})
pathlib.Path("intermediate/chunks.json").write_text(json.dumps(chunks, indent=2))
If you need semantic search later, embed each chunk with a 7–8 B parameter model quantized to 4 bits (Q4_K_M). The file is roughly 5 GB and runs comfortably on a 16 GB laptop. The embeddings can be stored in a local vector DB (e.g., sqlite-vec) to avoid external calls.
Generate draft posts
Feed each chunk (or a small group of adjacent chunks) to an LLM with a strict prompt that asks for a single‑platform post: a hook, three bullet insights, a call‑to‑action, and a hashtag set. Request JSON output so the downstream step can validate schema.
# example curl to a local llama.cpp server
curl -s http://localhost:8080/completion \
-H "Content-Type: application/json" \
-d '{
"prompt": "<|system|>You are a concise technical copywriter. Return JSON with fields: hook, bullets[3], cta, tags[5].<|user|>Chunk: '"$(jq -r '.[0].text' intermediate/chunks.json)'"'
}'
Run the request in a loop over all chunks, writing each response to drafts/post_<n>.json. Because webhooks (or any async callback) are delivered at least once, the generator must be idempotent: include the chunk index in the output file name and skip already‑existing files.
Guardrails and validation
Before publishing, run a deterministic validation suite:
-
Schema check –
jqensures every draft has the required keys. - Length check – enforce platform limits (e.g., 280 characters for the hook).
- Fact‑check pass – a second, smaller model scans each bullet for hallucinated numbers. Only numbers that appear in the source chunk or in the FACTS list (e.g., model size, GPU constraints) are allowed.
- Deduplication – compute a hash of the hook+bullets; discard any draft whose hash matches a previous day.
# schema + length validation
for f in drafts/post_*.json; do
jq -e 'has("hook") and has("bullets") and has("cta") and has("tags")' "$f" >/dev/null || { echo "bad schema: $f"; exit 1; }
hook_len=$(jq -r '.hook|length' "$f")
if (( hook_len > 280 )); then echo "hook too long: $f"; exit 1; fi
done
Schedule and publish
A tiny scheduler (cron, systemd timer, or GitHub Actions) picks the next unpublished draft in lexical order, calls the platform’s posting API, and marks the file as posted_<timestamp>.json. Because the same webhook can arrive twice, the publish step records the platform’s returned post ID and refuses to re‑post an ID that already exists in a local SQLite log.
Operational notes
-
Mac GPU limitation – Docker Desktop, Colima, and OrbStack run containers inside a Linux VM, so they cannot address the Apple GPU via Metal. When I rebuilt a Mac‑cluster demo I moved the inference to native
llama.cppbinaries that link directly to Metal. - Model size vs. hardware – A 7–8 B model at 4‑bit quantization (~5 GB) fits on a 16 GB laptop, leaving headroom for the OS and the vector DB.
- Weight conversion – Converting fp32 weights to fp16 halves the download size, which speeds up the initial model pull.
- Human‑in‑the‑loop – I run a small digital‑product business where an AI agent drafts content; I review every claim before anything goes live. This mirrors the validation step above.
- Content audit experience – After auditing my own catalogue of nine products I discovered missing video lessons, a private repo, and a community forum. Rewriting those promises before promotion saved later credibility issues.
- Early‑article metrics – My first 26 technical articles each received 30 views or fewer, reminding me that distribution mechanics matter more than raw output volume.
- Browser‑only estimator – I built a free 10‑question tool that estimates weekly automation hours; it runs entirely in the browser, proving that heavy‑lifting can stay client‑side.
By keeping every transformation pure, versioned, and locally verifiable, the workflow scales from a single talk to a month of content without surprise costs or hidden dependencies.
Want the complete, tested version? I wrote it up as a step-by-step guide: https://coldriver8.gumroad.com/l/solo-operator-os.
Not sure what to automate first? Try the free 5-minute AI Automation Audit.
This article was drafted with AI assistance.
Top comments (0)