24/7 LLM-Powered Thought Leadership Agent — Architecture & Implementation Guide
How to run automated algorithm cultivation and content creation continuously with AI intelligence.
Architecture Diagram
┌──────────────────────────────────────────────────────────────────────────────┐
│ THOUGHT LEADER AGENT │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ ORCHESTRATOR │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────────────┐ │ │
│ │ │ Scheduler│ │ Session │ │ Rate Limiter │ │ Health Monitor │ │ │
│ │ │ (cron + │ │ Manager │ │ (per-action │ │ (crash recovery, │ │ │
│ │ │ variance)│ │ (cookies)│ │ daily caps) │ │ alert on block) │ │ │
│ │ └────┬─────┘ └────┬─────┘ └──────┬───────┘ └────────┬─────────┘ │ │
│ │ └──────────────┴───────────────┴───────────────────┘ │ │
│ └──────────────────────────────┬──────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────────┼──────────────────────────────────────────┐ │
│ │ BROWSER LAYER │ │ │
│ │ ┌────────────────────────────▼─────────────────────────────────────┐ │ │
│ │ │ Puppeteer + Stealth Plugin │ │ │
│ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Page Actions: │ │ │ │
│ │ │ │ navigate() | search() | scroll() | click() | type() │ │ │ │
│ │ │ │ screenshot() | extractText() | waitForSelector() │ │ │ │
│ │ │ └────────────────────────────────────────────────────────────┘ │ │ │
│ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Anti-Detection: │ │ │ │
│ │ │ │ stealth plugin | mouse simulation | scroll variance │ │ │ │
│ │ │ │ viewport rotation | UA rotation | timezone matching │ │ │ │
│ │ │ └────────────────────────────────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ LLM BRAIN │ │
│ │ │ │
│ │ ┌────────────────┐ ┌─────────────────┐ ┌──────────────────────────────┐ │ │
│ │ │ Content Scorer │ │ Reply Generator │ │ Content Creator │ │ │
│ │ │ (fast model) │ │ (mid model) │ │ (smart model) │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ Input: tweet │ │ Input: tweet + │ │ Input: topic + persona │ │ │
│ │ │ Output: 0-100 │ │ thread context │ │ Output: tweet/thread/quote │ │ │
│ │ │ + reason │ │ Output: reply │ │ + schedule time │ │ │
│ │ └────────────────┘ └─────────────────┘ └──────────────────────────────┘ │ │
│ │ ┌────────────────┐ ┌─────────────────┐ ┌──────────────────────────────┐ │ │
│ │ │ Trend Analyzer │ │ Strategy Engine │ │ Persona Enforcer │ │ │
│ │ │ (what's hot?) │ │ (adapt tactics) │ │ (consistency check) │ │ │
│ │ └────────────────┘ └─────────────────┘ └──────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ DATA LAYER │ │
│ │ ┌───────────┐ ┌──────────────┐ ┌──────────┐ ┌───────────────────────┐ │ │
│ │ │ SQLite DB │ │ Action Log │ │ Metrics │ │ Session Store │ │ │
│ │ │ (state, │ │ (every click │ │ (growth │ │ (cookies, tokens, │ │ │
│ │ │ follows, │ │ timestamped)│ │ charts) │ │ fingerprint config) │ │ │
│ │ │ likes) │ │ │ │ │ │ │ │ │
│ │ └───────────┘ └──────────────┘ └──────────┘ └───────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
Technology Stack
| Layer | Technology | Purpose |
|---|---|---|
| Runtime | Node.js 20+ | Async orchestration |
| Browser | Puppeteer Extra + Stealth | Headless Chrome, anti-detection |
| LLM Provider | OpenRouter / OpenAI / Ollama | Model routing, cost control |
| Database | SQLite (better-sqlite3) | Action log, state, metrics |
| Scheduler | node-cron + custom variance | Circadian rhythm simulation |
| Process Mgmt | PM2 / Docker | 24/7 uptime, auto-restart |
| Monitoring | XActions Dashboard / Webhooks | Alerts, metrics visualization |
Component Details
1. Orchestrator (thoughtLeaderAgent.js)
The main event loop that coordinates all other components:
// Pseudocode — see src/agents/thoughtLeaderAgent.js for full implementation
import { BrowserDriver } from './browserDriver.js';
import { LLMBrain } from './llmBrain.js';
import { Scheduler } from './scheduler.js';
import { Database } from './database.js';
class ThoughtLeaderAgent {
constructor(config) {
this.config = config;
this.browser = new BrowserDriver(config.browser);
this.llm = new LLMBrain(config.llm);
this.scheduler = new Scheduler(config.schedule);
this.db = new Database(config.dbPath);
this.running = false;
}
async start() {
this.running = true;
await this.browser.launch();
await this.browser.restoreSession();
while (this.running) {
const activity = this.scheduler.getNextActivity();
if (activity.type === 'sleep') {
await this.sleep(activity.duration);
continue;
}
try {
await this.executeActivity(activity);
} catch (err) {
this.handleError(err);
}
await this.humanPause();
}
}
async executeActivity(activity) {
switch (activity.type) {
case 'search-engage':
return this.searchAndEngage(activity.query, activity.tab);
case 'home-feed':
return this.browseHomeFeed();
case 'influencer-visit':
return this.visitInfluencer(activity.username);
case 'create-content':
return this.createContent(activity.contentType);
case 'engage-replies':
return this.engageWithReplies();
case 'explore':
return this.browseExplore();
case 'own-profile':
return this.visitOwnProfile();
}
}
async searchAndEngage(query, tab) {
await this.browser.navigate(searchUrl(query, tab));
const tweets = await this.browser.extractTweets();
for (const tweet of tweets) {
// LLM scores relevance
const score = await this.llm.scoreRelevance(tweet.text, this.config.niche);
if (score > 60 && this.rateLimiter.canLike()) {
await this.browser.likeTweet(tweet);
this.db.logAction('like', tweet.id);
}
if (score > 80 && this.rateLimiter.canComment()) {
// LLM generates contextual reply
const reply = await this.llm.generateReply(tweet, this.config.persona);
await this.browser.replyToTweet(tweet, reply);
this.db.logAction('comment', tweet.id, { reply });
}
await this.humanScroll();
}
}
async createContent(type) {
// LLM analyzes trends and generates content
const trending = await this.browser.getTrendingTopics();
const content = await this.llm.generateContent({
type, // 'tweet' | 'thread' | 'quote'
persona: this.config.persona,
niche: this.config.niche,
trends: trending,
recentPosts: this.db.getRecentPosts(20),
});
await this.browser.postContent(content);
this.db.logAction('post', null, { content });
}
}
2. Browser Driver (browserDriver.js)
Wraps Puppeteer with X.com-specific actions:
import puppeteer from 'puppeteer-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
puppeteer.use(StealthPlugin());
class BrowserDriver {
async launch() {
this.browser = await puppeteer.launch({
headless: 'new',
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-blink-features=AutomationControlled',
`--window-size=${randomInt(1280,1920)},${randomInt(720,1080)}`,
],
});
this.page = await this.browser.newPage();
await this.page.setViewport({ width: randomInt(1280,1920), height: randomInt(720,1080) });
}
async restoreSession() {
const cookies = JSON.parse(fs.readFileSync('session.json', 'utf8'));
await this.page.setCookie(...cookies);
await this.page.goto('https://x.com/home', { waitUntil: 'networkidle2' });
}
async saveSession() {
const cookies = await this.page.cookies();
fs.writeFileSync('session.json', JSON.stringify(cookies));
}
async navigate(url) {
await this.page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
await this.page.waitForSelector('[data-testid="primaryColumn"]', { timeout: 15000 });
await sleep(randomInt(1000, 2000));
}
async extractTweets() {
return this.page.evaluate(() => {
const tweets = document.querySelectorAll('article[data-testid="tweet"]');
return Array.from(tweets).map(tweet => ({
id: tweet.querySelector('a[href*="/status/"]')?.href?.match(/status\/(\d+)/)?.[1],
text: tweet.querySelector('[data-testid="tweetText"]')?.textContent || '',
author: tweet.querySelector('a[href^="/"]')?.getAttribute('href')?.replace('/', ''),
isAd: !!tweet.querySelector('[data-testid="placementTracking"]'),
})).filter(t => t.id && !t.isAd);
});
}
async likeTweet(tweet) {
await this.page.evaluate((tweetId) => {
const tweets = document.querySelectorAll('article[data-testid="tweet"]');
for (const t of tweets) {
const link = t.querySelector(`a[href*="/status/${tweetId}"]`);
if (link) {
const btn = t.querySelector('[data-testid="like"]');
if (btn) btn.click();
break;
}
}
}, tweet.id);
await sleep(randomInt(500, 1500));
}
async replyToTweet(tweet, text) {
// Click reply, wait for compose box, type text, click post
await this.page.evaluate((tweetId) => {
const tweets = document.querySelectorAll('article[data-testid="tweet"]');
for (const t of tweets) {
const link = t.querySelector(`a[href*="/status/${tweetId}"]`);
if (link) {
t.querySelector('[data-testid="reply"]')?.click();
break;
}
}
}, tweet.id);
await sleep(1500);
await this.page.waitForSelector('[data-testid="tweetTextarea_0"]', { timeout: 5000 });
await this.humanType('[data-testid="tweetTextarea_0"]', text);
await sleep(800);
await this.page.click('[data-testid="tweetButton"]');
await sleep(1500);
}
async humanType(selector, text) {
await this.page.focus(selector);
for (const char of text) {
await this.page.keyboard.type(char, { delay: randomInt(30, 100) });
}
}
async humanScroll(pixels = randomInt(400, 800)) {
await this.page.evaluate((px) => {
window.scrollBy({ top: px, behavior: 'smooth' });
}, pixels);
await sleep(randomInt(1000, 3000));
}
async postContent(content) {
if (content.type === 'tweet') {
await this.navigate('https://x.com/compose/tweet');
await this.humanType('[data-testid="tweetTextarea_0"]', content.text);
await sleep(1000);
await this.page.click('[data-testid="tweetButton"]');
}
// Thread posting, quote posting, etc.
}
async getTrendingTopics() {
await this.navigate('https://x.com/explore/tabs/trending');
return this.page.evaluate(() => {
return Array.from(document.querySelectorAll('[data-testid="trend"]')).map(el => ({
text: el.textContent,
}));
});
}
}
3. LLM Brain (llmBrain.js)
Tiered model routing for cost efficiency:
class LLMBrain {
constructor(config) {
this.apiKey = config.apiKey;
this.baseUrl = config.baseUrl || 'https://openrouter.ai/api/v1';
this.models = {
fast: config.fastModel || 'deepseek/deepseek-chat', // Scoring, classification
mid: config.midModel || 'anthropic/claude-3.5-haiku', // Reply generation
smart: config.smartModel || 'anthropic/claude-sonnet-4', // Content creation + strategy
};
this.persona = config.persona;
}
async call(model, messages, options = {}) {
const response = await fetch(`${this.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model,
messages,
temperature: options.temperature || 0.7,
max_tokens: options.maxTokens || 500,
}),
});
const data = await response.json();
return data.choices[0].message.content;
}
async scoreRelevance(tweetText, niche) {
const result = await this.call(this.models.fast, [
{ role: 'system', content: `Score this tweet 0-100 for relevance to: ${niche.keywords.join(', ')}.
Respond with only a JSON object: { "score": N, "reason": "one sentence" }` },
{ role: 'user', content: tweetText },
], { temperature: 0.1, maxTokens: 100 });
try {
return JSON.parse(result).score;
} catch {
return 50; // Default mid-score on parse failure
}
}
async generateReply(tweet, persona) {
const result = await this.call(this.models.mid, [
{ role: 'system', content: `You are ${persona.name}, a ${persona.niche} thought leader.
Tone: ${persona.tone}
Generate a short reply (1-2 sentences) to this tweet. Be natural, add value.
Don't use hashtags. Vary style (question, agreement, insight, mild pushback).
Never start with "Great point" or "Interesting" — be more creative.` },
{ role: 'user', content: `Tweet by @${tweet.author}: "${tweet.text}"` },
], { temperature: 0.8, maxTokens: 150 });
return result.trim();
}
async generateContent({ type, persona, niche, trends, recentPosts }) {
const recent = recentPosts.map(p => p.text).join('\n---\n');
const trendText = trends.map(t => t.text).join(', ');
const prompt = type === 'thread'
? `Write a 4-6 tweet thread about a ${niche.name} topic. Current trending: ${trendText}.
Format: Each tweet separated by ---. First tweet should hook. Last should have a takeaway.`
: `Write a single tweet about ${niche.name}. Under 280 characters.
Make it insightful, potentially controversial, or actionable.`;
const result = await this.call(this.models.smart, [
{ role: 'system', content: `You are ${persona.name}, writing as yourself on X/Twitter.
Tone: ${persona.tone}
Expertise: ${persona.expertise.join(', ')}
Recent posts (avoid repetition): ${recent}
${prompt}` },
{ role: 'user', content: `Generate a ${type} now.` },
], { temperature: 0.9, maxTokens: type === 'thread' ? 1000 : 300 });
return { type, text: result.trim() };
}
async analyzeStrategy(metrics) {
const result = await this.call(this.models.smart, [
{ role: 'system', content: `Analyze these X/Twitter growth metrics and recommend specific adjustments.
Be concrete: more/less of what, specific timing changes, content type shifts.` },
{ role: 'user', content: JSON.stringify(metrics, null, 2) },
], { temperature: 0.5, maxTokens: 500 });
return result;
}
}
4. Scheduler (scheduler.js)
Circadian rhythm with realistic variance:
class Scheduler {
constructor(config) {
this.timezone = config.timezone || 'America/New_York';
this.schedule = config.schedule || this.defaultSchedule();
}
defaultSchedule() {
return {
// hour: { active: bool, intensity: 0-1, activities: [...] }
0: { active: false }, 1: { active: false }, 2: { active: false },
3: { active: false }, 4: { active: false }, 5: { active: false },
6: { active: true, intensity: 0.3, activities: ['home-feed'] },
7: { active: true, intensity: 0.6, activities: ['search-engage', 'home-feed'] },
8: { active: true, intensity: 0.8, activities: ['search-engage', 'engage-replies'] },
9: { active: true, intensity: 1.0, activities: ['create-content', 'search-engage'] },
10: { active: true, intensity: 0.9, activities: ['search-engage', 'influencer-visit'] },
11: { active: true, intensity: 0.6, activities: ['home-feed'] },
12: { active: true, intensity: 0.7, activities: ['explore', 'home-feed'] },
13: { active: true, intensity: 0.5, activities: ['home-feed'] },
14: { active: false }, // Afternoon break
15: { active: true, intensity: 0.6, activities: ['search-engage'] },
16: { active: true, intensity: 0.8, activities: ['search-engage', 'search-people'] },
17: { active: true, intensity: 1.0, activities: ['create-content', 'engage-replies'] },
18: { active: true, intensity: 0.9, activities: ['search-engage', 'influencer-visit'] },
19: { active: true, intensity: 0.8, activities: ['home-feed', 'engage-replies'] },
20: { active: true, intensity: 0.7, activities: ['home-feed'] },
21: { active: true, intensity: 0.5, activities: ['home-feed', 'own-profile'] },
22: { active: true, intensity: 0.3, activities: ['home-feed'] },
23: { active: false },
};
}
getNextActivity() {
const now = new Date();
const hour = now.getHours();
const slot = this.schedule[hour];
if (!slot?.active) {
// Sleep until next active hour
const nextActive = this.findNextActiveHour(hour);
const sleepMs = this.msUntilHour(nextActive);
return { type: 'sleep', duration: sleepMs };
}
// Add ±15 min variance
const variance = (Math.random() - 0.5) * 30 * 60 * 1000;
// Pick random activity from this hour's options
const activity = slot.activities[Math.floor(Math.random() * slot.activities.length)];
return {
type: activity,
intensity: slot.intensity,
scheduledFor: Date.now() + variance,
};
}
findNextActiveHour(currentHour) {
for (let i = 1; i <= 24; i++) {
const h = (currentHour + i) % 24;
if (this.schedule[h]?.active) return h;
}
return 7; // fallback
}
msUntilHour(targetHour) {
const now = new Date();
const target = new Date(now);
target.setHours(targetHour, Math.floor(Math.random() * 30), 0, 0);
if (target <= now) target.setDate(target.getDate() + 1);
return target - now;
}
}
Deployment Options
Option A: Docker (Recommended)
FROM node:20-slim
# Install Chrome dependencies
RUN apt-get update && apt-get install -y \
chromium \
fonts-liberation \
libasound2 libatk-bridge2.0-0 libgtk-3-0 \
--no-install-recommends && \
rm -rf /var/lib/apt/lists/*
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
CMD ["node", "src/agents/thoughtLeaderAgent.js"]
# docker-compose.agent.yml
version: '3.8'
services:
thought-leader:
build: .
restart: unless-stopped
volumes:
- ./data/session.json:/app/data/session.json
- ./data/agent.db:/app/data/agent.db
environment:
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
- AGENT_TIMEZONE=America/New_York
- AGENT_NICHE=ai-engineering
- AGENT_MODE=normal
mem_limit: 2g
cpus: '1.0'
Option B: PM2 (Direct VPS)
// ecosystem.config.js
module.exports = {
apps: [{
name: 'thought-leader',
script: 'src/agents/thoughtLeaderAgent.js',
cron_restart: '0 4 * * *', // Restart daily at 4 AM (session refresh)
max_memory_restart: '1G',
env: {
OPENROUTER_API_KEY: 'sk-...',
AGENT_TIMEZONE: 'America/New_York',
},
}],
};
Option C: Railway/Fly.io
Both are supported natively by the project. Deploy with:
# Railway
railway up
# Fly.io
fly deploy
Cost Estimates
LLM Costs (Monthly)
| Activity | Model Tier | Calls/Day | Tokens/Call | Monthly Cost |
|---|---|---|---|---|
| Relevance scoring | Fast (DeepSeek) | 200 | ~200 | ~$0.80 |
| Reply generation | Mid (Haiku) | 20 | ~300 | ~$1.50 |
| Content creation | Smart (Sonnet) | 5 | ~800 | ~$3.00 |
| Strategy analysis | Smart (Sonnet) | 1 | ~1000 | ~$0.50 |
| Total | ~$5-10/mo |
If using local models (Ollama with Llama 3.1):
- $0/month for LLM costs
- Requires machine with 16GB+ RAM (for 7B model) or GPU (for 70B)
Infrastructure Costs
| Option | Monthly Cost |
|---|---|
| Hetzner CAX11 (ARM VPS) | $4.15 |
| DigitalOcean Basic | $6.00 |
| Railway (Hobby) | $5.00 |
| Fly.io (shared-cpu-1x) | $3.19 |
| Home server | $0 (electricity only) |
Total all-in cost: $8-15/month for a fully autonomous 24/7 thought leadership agent.
Session Management
The most critical piece for 24/7 operation is maintaining authenticated sessions:
class SessionManager {
constructor(cookiePath = 'data/session.json') {
this.cookiePath = cookiePath;
}
async save(page) {
const cookies = await page.cookies('https://x.com');
fs.writeFileSync(this.cookiePath, JSON.stringify(cookies, null, 2));
log('Session saved');
}
async restore(page) {
if (!fs.existsSync(this.cookiePath)) {
throw new Error('No session file — manual login required first');
}
const cookies = JSON.parse(fs.readFileSync(this.cookiePath, 'utf8'));
await page.setCookie(...cookies);
log('Session restored');
}
async isValid(page) {
await page.goto('https://x.com/home', { waitUntil: 'networkidle2' });
const isLoggedIn = await page.evaluate(() => {
return !!document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
});
return isLoggedIn;
}
async refresh(page) {
// Sessions typically last 30-90 days
// Periodic cookie refresh prevents expiration
await this.save(page);
}
}
Initial setup requires one manual login:
- Launch browser in headed mode (non-headless)
- Navigate to x.com and log in manually
- Script captures and saves cookies
- All subsequent runs use saved cookies
Monitoring & Alerts
Webhook Notifications
const notify = async (event, data) => {
if (!process.env.WEBHOOK_URL) return;
await fetch(process.env.WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event,
timestamp: new Date().toISOString(),
...data,
}),
});
};
// Example alerts:
// notify('session_expired', { action: 'manual re-login required' })
// notify('rate_limited', { action: 'likes', remaining: 0 })
// notify('daily_summary', { likes: 142, follows: 23, comments: 8 })
// notify('content_posted', { type: 'thread', topic: 'AI agents' })
Supports: Discord webhooks, Slack, Telegram bots, email via SMTP, or custom endpoints.
Security Considerations
- Credentials: Never store passwords. Use cookie-based session persistence only.
- API keys: Use environment variables, never commit to code.
- Session files: Encrypt at rest (
session.jsoncontains auth cookies). - Network: Run behind VPN if IP reputation matters.
- Logs: Redact sensitive data from action logs.
- Access: Restrict SSH/admin access to the deployment server.
Getting Started
# 1. Install dependencies
npm install puppeteer-extra puppeteer-extra-plugin-stealth better-sqlite3
# 2. Create config
cp config/agent-config.example.json config/agent-config.json
# Edit with your niche, persona, API keys
# 3. Initial login (headed browser)
node src/agents/setup.js --login
# → Opens browser, you log in manually, cookies saved
# 4. Test run (5-minute quick session)
node src/agents/thoughtLeaderAgent.js --test
# 5. Start 24/7 operation
pm2 start ecosystem.config.js
# or
docker compose -f docker-compose.agent.yml up -d
See the full research paper at docs/research/algorithm-cultivation.md and the build prompts at prompts/09-algorithm-cultivation-system.md.
⚡ Explore XActions
100% free and open-source. No API keys, no fees, no signup.
Browse All Documentation