1200+ distilled gold examples, journal system, redstone mastery, safety awareness
Distilled Training Data (1,203 examples): - 341 initial gold (plugins, enchantments, builds, effects, god, errors) - 165 buildings + pipeline (100 structures built on dev, 65 request→query→act) - 24 safety-aware (worldborder, safe tp, intentional harm, gamemode checks) - 17 advanced logic (decanonized items, redstone gates, iterative builds) - 12 redstone mastery (NOT/OR/AND/XOR/RS-latch/T-flip-flop/comparator/clock) - 7 circuit verification and diagnosis - 1 compact comparator gates - 10 redstone methodology (build→test→save→recall→learn from mistakes) - 8 player journal usage - 29 creative+uncommon+pipeline+god with full tool chains Player Journal System: - agent/tools/player_journal.py — per-player text files (1-10 lines) - journal.read + journal.write tool schemas added - Cross-contaminated: God and Sudo share same journal per player - Includes sentiment, relationship, builds, preferences, skill level Redstone Engineering: - agent/prompts/redstone_rules.md — baked-in wall torch, dedicated lead, repeater rules - Learned from 4 iterations of 8-switch circuit: wall_torch on back face, not top - T-junction bypass prevention: dedicated lead wire between merge and NOT block - RCON limitation: can build circuits but cannot test them (lever toggle doesn't propagate) Training Data Cleaning: - 466 @s→@p fixes, 10 template commands removed - 12 outdated refusals replaced with correct plugin commands - Data de-duped across all sources Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
Player Journal — lightweight per-player memory files.
|
||||
|
||||
Each player gets a small text file (1-5 lines) that the model reads before
|
||||
responding and updates after meaningful interactions. The model has free reign
|
||||
to overwrite these files to keep them short and contextually relevant.
|
||||
|
||||
Files stored at: <config.journal_path>/<player_name>.txt
|
||||
|
||||
For God mode: includes sentiment, relationship history, divine interactions.
|
||||
For Sudo mode: includes past builds, preferences, saved locations, skill level.
|
||||
|
||||
Usage:
|
||||
from agent.tools.player_journal import read_journal, write_journal, format_journal_context
|
||||
|
||||
# Read before responding
|
||||
context = read_journal(config, player)
|
||||
|
||||
# Update after interaction
|
||||
write_journal(config, player, new_content)
|
||||
|
||||
# Inject into system prompt
|
||||
prompt += format_journal_context(config, player)
|
||||
"""
|
||||
|
||||
import os
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
_lock = threading.Lock()
|
||||
|
||||
|
||||
def _journal_dir(config: dict) -> Path:
|
||||
return Path(config.get("journal_path", config.get("memory_path", "/tmp").replace("aigod_memory.json", "journals")))
|
||||
|
||||
|
||||
def _journal_path(config: dict, player: str) -> Path:
|
||||
d = _journal_dir(config)
|
||||
# Sanitize player name
|
||||
safe = "".join(c for c in player if c.isalnum() or c in "_-").strip()
|
||||
if not safe:
|
||||
safe = "unknown"
|
||||
return d / f"{safe}.txt"
|
||||
|
||||
|
||||
def read_journal(config: dict, player: str) -> str:
|
||||
"""Read a player's journal. Returns empty string if none exists."""
|
||||
path = _journal_path(config, player)
|
||||
try:
|
||||
with open(path) as f:
|
||||
return f.read().strip()
|
||||
except FileNotFoundError:
|
||||
return ""
|
||||
|
||||
|
||||
def write_journal(config: dict, player: str, content: str):
|
||||
"""Overwrite a player's journal. Aim for 1-5 lines but longer is fine for
|
||||
players with complex history. Max 10 lines — if longer, the model should
|
||||
condense older entries to make room for new context."""
|
||||
path = _journal_path(config, player)
|
||||
d = _journal_dir(config)
|
||||
|
||||
with _lock:
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
lines = [l.strip() for l in content.strip().split("\n") if l.strip()]
|
||||
if len(lines) > 10:
|
||||
lines = lines[-10:]
|
||||
with open(path, "w") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
|
||||
|
||||
def append_journal(config: dict, player: str, line: str):
|
||||
"""Add a line to a player's journal, keeping it under 5 lines.
|
||||
Oldest line gets dropped if at capacity."""
|
||||
current = read_journal(config, player)
|
||||
lines = [l for l in current.split("\n") if l.strip()]
|
||||
lines.append(line.strip())
|
||||
if len(lines) > 5:
|
||||
lines = lines[-5:]
|
||||
write_journal(config, player, "\n".join(lines))
|
||||
|
||||
|
||||
def format_journal_context(config: dict, player: str) -> str:
|
||||
"""Format journal for injection into the system prompt.
|
||||
Returns empty string if no journal exists."""
|
||||
content = read_journal(config, player)
|
||||
if not content:
|
||||
return ""
|
||||
return f"\n=== PLAYER JOURNAL: {player} ===\n{content}\n=== END JOURNAL ===\n"
|
||||
|
||||
|
||||
def list_journals(config: dict) -> list:
|
||||
"""List all players who have journals."""
|
||||
d = _journal_dir(config)
|
||||
if not d.exists():
|
||||
return []
|
||||
return [f.stem for f in d.glob("*.txt") if f.stat().st_size > 0]
|
||||
|
||||
|
||||
# ── Tool handlers ──────────────────────────────────────────────────────────
|
||||
|
||||
def handle_journal_read(config: dict, arguments: dict) -> dict:
|
||||
"""Tool handler for reading a player journal."""
|
||||
player = arguments.get("player", "")
|
||||
if not player:
|
||||
return {"ok": False, "error": "player name required"}
|
||||
content = read_journal(config, player)
|
||||
if content:
|
||||
return {"ok": True, "player": player, "journal": content}
|
||||
return {"ok": True, "player": player, "journal": "", "note": "No journal exists for this player yet."}
|
||||
|
||||
|
||||
def handle_journal_write(config: dict, arguments: dict) -> dict:
|
||||
"""Tool handler for writing/overwriting a player journal.
|
||||
The model has free reign — it decides what to keep and what to overwrite."""
|
||||
player = arguments.get("player", "")
|
||||
content = arguments.get("content", "")
|
||||
if not player:
|
||||
return {"ok": False, "error": "player name required"}
|
||||
if not content:
|
||||
return {"ok": False, "error": "content required"}
|
||||
write_journal(config, player, content)
|
||||
lines = len([l for l in content.strip().split("\n") if l.strip()])
|
||||
return {"ok": True, "player": player, "lines": lines}
|
||||
Reference in New Issue
Block a user