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
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AI Talk Show — Episode Orchestrator
|
||||
Manages turn-taking between two autonomous agents (Leonard & Charlie).
|
||||
Each agent gets its own system prompt and sees the full conversation history.
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import sys
|
||||
import requests
|
||||
from pathlib import Path
|
||||
|
||||
# ── Config ──────────────────────────────────────────────────────────
|
||||
BASE_URL = "http://100.64.0.2:39195/v1/chat/completions"
|
||||
MODEL = "qwen36-27b-nvfp4-mtp-gguf"
|
||||
TEMPERATURE = 0.8
|
||||
MAX_TURNS = 8 # 4 exchanges each (Leonard opens)
|
||||
MAX_TOKENS_OPENING = 250
|
||||
MAX_TOKENS_TURN = 180
|
||||
PROJECT_DIR = Path(__file__).parent.parent
|
||||
OUTPUT_DIR = PROJECT_DIR / "outputs"
|
||||
OUTPUT_DIR.mkdir(exist_ok=True)
|
||||
|
||||
# ── Load prompts ────────────────────────────────────────────────────
|
||||
LEONARD_SYSTEM = (PROJECT_DIR / "prompts" / "leonard_system.md").read_text().strip()
|
||||
CHARLIE_SYSTEM = (PROJECT_DIR / "prompts" / "charlie_system.md").read_text().strip()
|
||||
PRODUCER_BRIEF = (PROJECT_DIR / "prompts" / "producer_brief_003.md").read_text().strip()
|
||||
|
||||
# ── Helpers ─────────────────────────────────────────────────────────
|
||||
def call_agent(system: str, messages: list, max_tokens: int) -> str:
|
||||
"""Call the model API with the agent's system prompt and conversation history."""
|
||||
payload = {
|
||||
"model": MODEL,
|
||||
"messages": [{"role": "system", "content": system}] + messages,
|
||||
"max_tokens": max_tokens,
|
||||
"temperature": TEMPERATURE,
|
||||
}
|
||||
resp = requests.post(BASE_URL, json=payload, timeout=120)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
content = data["choices"][0]["message"]["content"].strip()
|
||||
usage = data.get("usage", {})
|
||||
return content, usage
|
||||
|
||||
def call_agent_turn(agent_name: str, conversation: list, is_opening: bool) -> dict:
|
||||
"""Run one turn for an agent."""
|
||||
system = LEONARD_SYSTEM if agent_name == "Leonard" else CHARLIE_SYSTEM
|
||||
max_tokens = MAX_TOKENS_OPENING if is_opening else MAX_TOKENS_TURN
|
||||
|
||||
# Build conversation history
|
||||
history = []
|
||||
for turn in conversation:
|
||||
speaker = turn["agent"]
|
||||
history.append({"role": "user", "content": f"{speaker}: {turn['content']}"})
|
||||
|
||||
# Leonard's opening: give him the raw brief and let him start naturally
|
||||
if agent_name == "Leonard" and is_opening:
|
||||
history.insert(0, {"role": "user", "content": f"Here's what's new in Hermes v0.18:\n\n{PRODUCER_BRIEF}"})
|
||||
|
||||
content, usage = call_agent(system, history, max_tokens)
|
||||
|
||||
# Word count check
|
||||
word_count = len(content.split())
|
||||
print(f" {agent_name} turn {len(conversation)+1}: {word_count} words "
|
||||
f"(prompt: {usage.get('prompt_tokens', '?')}, "
|
||||
f"completion: {usage.get('completion_tokens', '?')})", flush=True)
|
||||
|
||||
return {
|
||||
"agent": agent_name,
|
||||
"content": content,
|
||||
"word_count": word_count,
|
||||
"turn": len(conversation) + 1,
|
||||
"tokens": usage,
|
||||
}
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────
|
||||
def main():
|
||||
episode_id = "003"
|
||||
topic = "hermes-v018"
|
||||
print(f"=== AI Talk Show — Episode {episode_id}: {topic} ===\n", flush=True)
|
||||
|
||||
conversation = []
|
||||
turn = 0
|
||||
|
||||
while turn < MAX_TURNS:
|
||||
# Determine speaker: Leonard opens, then alternate
|
||||
if turn == 0:
|
||||
speaker = "Leonard"
|
||||
is_opening = True
|
||||
elif turn % 2 == 1:
|
||||
speaker = "Charlie"
|
||||
is_opening = False
|
||||
else:
|
||||
speaker = "Leonard"
|
||||
is_opening = False
|
||||
|
||||
print(f"\n--- Turn {turn+1}/{MAX_TURNS}: {speaker} ---", flush=True)
|
||||
result = call_agent_turn(speaker, conversation, is_opening)
|
||||
conversation.append(result)
|
||||
|
||||
# Print a short excerpt
|
||||
excerpt = result["content"][:120].replace("\n", " ")
|
||||
print(f" → {excerpt}...", flush=True)
|
||||
|
||||
turn += 1
|
||||
if turn < MAX_TURNS:
|
||||
time.sleep(0.5) # Brief pause between turns
|
||||
|
||||
# ── Save transcript ────────────────────────────────────────────
|
||||
transcript_path = OUTPUT_DIR / f"ep{episode_id}_{topic}_transcript.json"
|
||||
transcript_data = {
|
||||
"episode": episode_id,
|
||||
"topic": topic,
|
||||
"turns": len(conversation),
|
||||
"conversation": conversation,
|
||||
}
|
||||
transcript_path.write_text(json.dumps(transcript_data, indent=2))
|
||||
print(f"\n✅ Transcript saved: {transcript_path}", flush=True)
|
||||
|
||||
# ── Save readable transcript ───────────────────────────────────
|
||||
readable_path = OUTPUT_DIR / f"ep{episode_id}_{topic}_readable.txt"
|
||||
lines = []
|
||||
lines.append(f"AI TALK SHOW — Episode {episode_id}\n")
|
||||
lines.append(f"Topic: Hermes v0.18\n")
|
||||
lines.append("=" * 60 + "\n\n")
|
||||
for t in conversation:
|
||||
lines.append(f"**{t['agent']}**\n")
|
||||
lines.append(t["content"] + "\n\n")
|
||||
readable_path.write_text("\n".join(lines))
|
||||
print(f"✅ Readable transcript: {readable_path}", flush=True)
|
||||
|
||||
# ── Word count summary ─────────────────────────────────────────
|
||||
total_words = sum(t["word_count"] for t in conversation)
|
||||
print(f"\n📊 Total: {total_words} words across {len(conversation)} turns", flush=True)
|
||||
estimated_minutes = total_words / 150
|
||||
print(f" Estimated runtime: {estimated_minutes:.1f} minutes", flush=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user