Files
ai-talk-show/scripts/run_autonomous.py
T
Epictetus 772ef4f6fd Initial commit: autonomous AI talk show (Leonard + Charlie)
- Autonomous agent runtime (JSONL + fcntl locking, no token caps)
- Moltbook-style prompts: agents share what they built, not scripted turns
- Episodes 001-008 transcripts, conversation logs, system prompts
- Producer brief for Hermes v0.18
2026-07-09 03:26:56 +00:00

131 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""
Run Autonomous Show — starts two autonomous agents as background threads.
No turn structure. Each agent independently polls, decides to respond, posts.
Conversation ends on silence timeout, max messages, or time limit.
Uses JSONL (append-only) for concurrent-safe access.
"""
import json
import sys
import threading
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from autonomous_agent import AutonomousAgent, load_topic_brief
# ── Config ──────────────────────────────────────────────────────────
MAX_MESSAGES = 20
SILENCE_TIMEOUT = 25 # seconds of silence = done
MAX_TIME = 240 # 4 minutes wall clock max
PROJECT_DIR = Path(__file__).parent.parent
OUTPUT_DIR = PROJECT_DIR / "outputs"
def agent_thread(agent: AutonomousAgent, done_event: threading.Event):
"""Run an agent in a background thread."""
try:
agent.run_loop(
max_messages=MAX_MESSAGES,
silence_timeout=SILENCE_TIMEOUT,
max_time=MAX_TIME,
)
except Exception as e:
import traceback
print(f" [{agent.name}] ERROR: {e}", flush=True)
traceback.print_exc()
finally:
done_event.set()
def jsonl_to_messages(log_path: Path) -> list:
"""Read JSONL log and return message list."""
if not log_path.exists():
return []
lines = log_path.read_text().strip().split('\n')
return [json.loads(line) for line in lines if line.strip()]
def run_show(episode_id: str, topic: str):
"""Run the autonomous agent conversation."""
print(f"=== Agent AI Talk Show — Episode {episode_id} (AUTONOMOUS) ===\n", flush=True)
# Load topic brief
topic_brief = load_topic_brief(episode_id)
# Create shared conversation log (JSONL — append-only, concurrent-safe)
log_path = OUTPUT_DIR / f"ep{episode_id}_{topic}_conversation.jsonl"
# Clear if exists
if log_path.exists():
log_path.unlink()
log_path.touch()
# Initialize agents
leonard = AutonomousAgent("Leonard", log_path, topic_brief)
charlie = AutonomousAgent("Charlie", log_path, topic_brief)
# Done events
leonard_done = threading.Event()
charlie_done = threading.Event()
# Leonard MUST start first — forced opener prevents deadlock
print("Starting Leonard (forced opener)...", flush=True)
t_leo = threading.Thread(target=agent_thread, args=(leonard, leonard_done))
t_cha = threading.Thread(target=agent_thread, args=(charlie, charlie_done))
t_leo.start()
time.sleep(3) # Wait for Leonard to post his opener
print("Starting Charlie...", flush=True)
t_cha.start()
# Wait for both to finish
print("\nAgents running — waiting for conversation to wind down...\n", flush=True)
t_leo.join()
t_cha.join()
print("\n✅ Both agents stopped.\n", flush=True)
# ── Save final transcript (JSON) ─────────────────────────────
transcript_path = OUTPUT_DIR / f"ep{episode_id}_{topic}_transcript.json"
conversation = jsonl_to_messages(log_path)
transcript_data = {
"episode": episode_id,
"topic": topic,
"turns": len(conversation),
"conversation": conversation,
}
transcript_path.write_text(json.dumps(transcript_data, indent=2))
print(f"✅ Transcript: {transcript_path}", flush=True)
# Save readable transcript
readable_path = OUTPUT_DIR / f"ep{episode_id}_{topic}_readable.txt"
lines = [
f"AGENT AI TALK SHOW — Episode {episode_id} (AUTONOMOUS)\n",
f"Topic: {topic}\n",
"=" * 60 + "\n\n",
]
for msg in conversation:
lines.append(f"**{msg['agent']}**\n")
lines.append(msg["content"] + "\n\n")
readable_path.write_text("\n".join(lines))
print(f"✅ Readable: {readable_path}", flush=True)
# Summary
total_words = sum(len(m["content"].split()) for m in conversation)
leonard_count = sum(1 for m in conversation if m["agent"] == "Leonard")
charlie_count = sum(1 for m in conversation if m["agent"] == "Charlie")
print(f"\n📊 {total_words} words across {len(conversation)} messages", flush=True)
print(f" Leonard: {leonard_count} posts | Charlie: {charlie_count} posts", flush=True)
print(f" ~{total_words/150:.1f} min runtime", flush=True)
return conversation
if __name__ == "__main__":
episode = sys.argv[1] if len(sys.argv) > 1 else "005"
topic = sys.argv[2] if len(sys.argv) > 2 else "hermes-v018"
run_show(episode, topic)