Skip to content

Ninadnj/meta-social-agent

Repository files navigation

Meta Social Agent

An autonomous, LLM-driven agent that runs a brand's social presence across all of Meta — Facebook, Instagram, and Threads — from a single command.

An LLM brain decides what to post (it picks the post type and writes fresh, on-brand copy, grounded in the real menu and steered away from recent posts) and writes the caption in the brand's voice. The agent then renders a branded post image and publishes it to every enabled Meta platform. Designed to run on a schedule (cron/heartbeat) with no human in the loop, and built so you can demo the entire pipeline with --dry-run — no tokens, nothing posted.

Graceful degradation: the LLM brain is optional. With no API key it falls back to a deterministic template engine, so the full pipeline still runs and demos with zero credentials and zero cost.

Deployed for a real client — Lord of the Wings, a wings restaurant in Greece (captions in Greek). The brand is just config; the agent is reusable.

Why

Small businesses need a steady stream of on-brand posts across Facebook, Instagram and Threads. Doing that by hand is the chore that never gets done. The hard part isn't generating one post — it's the multi-platform publishing, where each Meta surface has different rules (Instagram and Threads won't accept a file upload; they need a public image URL). This agent handles that fan-out automatically.

What it does

plan post  ──▶  write caption  ──▶ render image (HTML→PNG) ──▶ publish to all Meta platforms
 (LLM brain)      (LLM brand voice)   (Playwright)                       │
                                                                           ▼
                                            ┌──────────────┬───────────────┬──────────────┐
                                            ▼              ▼               ▼              
                                        Facebook       Instagram       Threads          
                                     (file upload)   (URL bridge)    (URL bridge)        
                                            │              ▲               ▲
                                            └──── public image URL ────────┘

The bridging trick: Instagram and Threads can't take a local file — they need a publicly reachable image URL. Facebook can upload a local file and hands back a public CDN URL. So the agent publishes to Facebook first, then reuses that URL to post the same image to Instagram and Threads. (Without Facebook, set public_image_base_url to your own host.)

Platform support

Platform How it publishes Needs
Facebook Page Local file upload → /{page}/photos META_ACCESS_TOKEN
Instagram Business Create media container → publish (2-step) public image URL (bridged from FB) + linked IG account
Threads Create container → publish (2-step) THREADS_ACCESS_TOKEN + public image URL

Each platform is a small pluggable class in engine/platforms/ — add a new surface by writing one Platform subclass and registering it. One platform failing never blocks the others (partial-success by design).

Quickstart

# 1. credentials (secrets live only in .env, never in config)
cp .env.example .env          # add META_ACCESS_TOKEN; add OPENAI_API_KEY for the LLM brain

# 2. dependencies (rendering engine + LLM SDK)
pip install -r requirements.txt && python3 -m playwright install chromium
pip install pytest && python3 -m pytest tests/ -q   # 7 offline tests (no credentials needed)

# 3. see the whole pipeline without posting anything
#    (with OPENAI_API_KEY the LLM plans + writes; without it, template fallback)
python3 autopilot.py --dry-run

# compare: force the deterministic engine instead of the LLM
python3 autopilot.py --dry-run --no-llm

# 4. generate one specific post to Facebook + Instagram
python3 create_post.py --type product_spotlight \
  --headline "CRUNCH HOUR" --subline "Crispy wings, hot sauce, zero patience." \
  --publish --platforms facebook,instagram

Go fully autonomous with cron:

0 18 * * *  cd /path/to/meta-social-agent && python3 autopilot.py >> memory/logs/cron.log 2>&1

Configure

config.json (no secrets here) controls which platforms run:

{
  "platforms": {
    "facebook":  { "enabled": true,  "page_id": "" },
    "instagram": { "enabled": true,  "ig_user_id": "" },
    "threads":   { "enabled": false, "threads_user_id": "" }
  }
}

Architecture

create_post.py / autopilot.py     CLI + autonomous driver
        │
        ▼
engine/
  brain.py         ◀── LLM brain: plan_post() decides what to post, write_caption() writes it
  assets.py        pick a brand photo by tag/mood
  caption.py       deterministic caption fallback (hook → sensory → CTA → signature → hashtags)
  templates.py     fill an HTML post template
  renderer.py      HTML → PNG via headless Chromium (Playwright)
  publisher.py     ◀── orchestrator: publish_everywhere() fans out across platforms
  platforms/
    base.py        PublishResult + shared Graph API helpers
    facebook.py    local upload + produces the bridged public URL
    instagram.py   container → publish
    threads.py     container → publish

Results

output/ contains real generated posts from production runs (product spotlights, promos, memes, seasonal cards) across several months. The brand voice and templates are tuned for Lord of the Wings.

Tech

Python · OpenAI API (LLM brain — planning + brand-voice copywriting, JSON-mode structured output) · Playwright (headless Chromium rendering) · Meta Graph API (Facebook, Instagram) · Threads API · HTML/CSS templating. No heavyweight framework — the agent loop, LLM brain, multi-platform publisher, and renderer are hand-built.

Notes

  • LLM brain, optional by design. The brain plans the post and writes the caption; if the openai SDK or OPENAI_API_KEY is missing, or llm.enabled is false in config.json, every brain call returns None and the agent falls back to the deterministic engine. The pipeline never breaks for lack of a key.
  • Grounded, non-repetitive copy. The planner is fed the real menu catalog (from assets/brand/) and the last few posts (from memory/logs/) so it references real items and avoids repeating recent angles.
  • Structured output. Planning uses JSON mode and the result is validated (post type must be a real template) before it touches the pipeline.
  • Dry-run everywhere. Every platform supports dry_run, so the full pipeline is demoable with zero credentials and zero live posts.
  • Secrets discipline. Tokens live only in .env (gitignored). config.json never holds a secret.
  • Reusable. Swap the brand by editing config.json, post_config.json (the voice spec the LLM follows) and templates/ — the engine is brand-agnostic.

About

Autonomous LLM agent that runs a brand's Facebook, Instagram & Threads from one command — Playwright rendering, URL bridging, dry-run safe

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors