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,119 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Autonomous Agent — base class for Leonard and Charlie.
|
||||
Each agent runs independently, reads the shared conversation log,
|
||||
generates a response, and appends it to the log.
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import sys
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
# ── Config ──────────────────────────────────────────────────────────
|
||||
BASE_URL = "http://100.64.0.2:39195/v1/chat/completions"
|
||||
MODEL = "qwen36-27b-nvfp4-mtp-gguf"
|
||||
TEMPERATURE = 0.85
|
||||
PROJECT_DIR = Path(__file__).parent.parent
|
||||
OUTPUT_DIR = PROJECT_DIR / "outputs"
|
||||
PROMPTS_DIR = PROJECT_DIR / "prompts"
|
||||
|
||||
class Agent:
|
||||
def __init__(self, name: str, conversation_path: Path, topic_brief: str):
|
||||
self.name = name
|
||||
self.conversation_path = conversation_path
|
||||
self.topic_brief = topic_brief
|
||||
self.system_prompt = (PROMPTS_DIR / f"{name.lower()}_system.md").read_text().strip()
|
||||
self.is_opener = (name == "Leonard")
|
||||
|
||||
def read_conversation(self) -> list:
|
||||
"""Read the current conversation log."""
|
||||
if not self.conversation_path.exists():
|
||||
return []
|
||||
data = json.loads(self.conversation_path.read_text())
|
||||
return data.get("messages", [])
|
||||
|
||||
def write_message(self, content: str) -> None:
|
||||
"""Append a message to the conversation log."""
|
||||
messages = self.read_conversation()
|
||||
message = {
|
||||
"agent": self.name,
|
||||
"content": content,
|
||||
"turn": len(messages) + 1,
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
}
|
||||
messages.append(message)
|
||||
self.conversation_path.write_text(json.dumps({"messages": messages}, indent=2))
|
||||
|
||||
def build_context(self, is_first_turn: bool) -> list:
|
||||
"""Build the message context for the model call."""
|
||||
conversation = self.read_conversation()
|
||||
|
||||
# Build conversation history
|
||||
history = []
|
||||
for msg in conversation:
|
||||
speaker = msg["agent"]
|
||||
history.append({"role": "user", "content": f"{speaker}: {msg['content']}"})
|
||||
|
||||
# First turn: inject topic brief
|
||||
if is_first_turn and self.is_opener:
|
||||
topic_msg = {
|
||||
"role": "user",
|
||||
"content": (
|
||||
f"You're on the show now. Here's your topic:\n\n"
|
||||
f"{self.topic_brief}\n\n"
|
||||
"Welcome your listeners, introduce yourself and Charlie, "
|
||||
"and start talking about the topic."
|
||||
),
|
||||
}
|
||||
history.insert(0, topic_msg)
|
||||
elif is_first_turn and not self.is_opener:
|
||||
# Charlie's first turn after Leonard's opener
|
||||
history.insert(0, {
|
||||
"role": "user",
|
||||
"content": "Leonard just opened the show. Respond naturally to what he said."
|
||||
})
|
||||
|
||||
return history
|
||||
|
||||
def speak(self) -> dict:
|
||||
"""Generate and post a response."""
|
||||
messages = self.read_conversation()
|
||||
is_first_turn = len(messages) == 0 if self.is_opener else (
|
||||
len(messages) == 1 and messages[0]["agent"] == "Leonard"
|
||||
)
|
||||
|
||||
history = self.build_context(is_first_turn)
|
||||
|
||||
# Max tokens: 250 for opener, 200 for others
|
||||
max_tokens = 250 if is_first_turn and self.is_opener else 200
|
||||
|
||||
payload = {
|
||||
"model": MODEL,
|
||||
"messages": [{"role": "system", "content": self.system_prompt}] + history,
|
||||
"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()
|
||||
|
||||
word_count = len(content.split())
|
||||
turn_num = len(messages) + 1
|
||||
|
||||
print(f" {self.name} turn {turn_num}: {word_count} words", flush=True)
|
||||
excerpt = content[:100].replace("\n", " ")
|
||||
print(f" → {excerpt}...", flush=True)
|
||||
|
||||
self.write_message(content)
|
||||
|
||||
return {
|
||||
"agent": self.name,
|
||||
"content": content,
|
||||
"word_count": word_count,
|
||||
"turn": turn_num,
|
||||
}
|
||||
Reference in New Issue
Block a user