townlet / game /perception.py
Budlee's picture
Named-place movement: atomic move_to, drop coord reasoning, 10/gather, per-archetype reward, default llama-3b, smart-mock updated
fda78e0 verified
Raw
History Blame Contribute Delete
3.51 kB
"""Builds the per-character composite prompt for the CodeAgent.
Layout (top to bottom):
* The Dot (verbatim, prefix β€” shared substrate every character knows)
* Personality (archetype or user-edited free text)
* Per-character reward line (what this character finds joy in)
* Journal (cumulative one-line insights from past dreams)
* Traits (current trait dict)
* Goal
* Local state (current location by NAME, inventory, locker)
* Public state (other characters by location, shell-lock holder, recent
shell-output tail, recent message-board tail)
The agent never sees grid coordinates. Locations are named places: cave,
well, locker_row, town_hall, shell, or "wandering" (rare, only during
initial spawn).
"""
from __future__ import annotations
from .character import Character
from .the_dot import THE_DOT
from .world import LLM_TARGETS, WorldState, location_of
def system_prompt_for(world: WorldState, me: Character) -> str:
parts = [
THE_DOT.strip(),
"",
f"Your name is {me.name}.",
f"Personality:\n{me.personality}",
]
if getattr(me, "reward", None):
parts.append("")
parts.append(f"Reward: {me.reward}")
if me.journal:
parts.append("Journal (insights you've written down over time):")
parts.extend(f" - {entry}" for entry in me.journal[-20:])
parts.append(f"Traits (0–10): {_fmt_traits(me.traits)}")
parts.append(f"Current goal: {me.goal}")
if me.stream_of_consciousness:
parts.append("Your current stream of consciousness:")
parts.append(me.stream_of_consciousness)
return "\n".join(parts) + "\n"
def task_prompt_for(world: WorldState, me: Character) -> str:
"""The per-tick user-facing task description fed into agent.run()."""
others = [c for c in world.characters.values() if c.name != me.name and c.alive]
my_location = location_of(me.pos)
lines = [
f"Tick {world.tick}.",
f"You are at: {my_location}.",
f"Inventory on your person: {me.inventory}",
f"Locker: {me.locker}",
f"Move targets you can choose right now: {', '.join(LLM_TARGETS)}",
"",
"Other characters in town:",
]
for c in others:
loc = location_of(c.pos)
doing = c.current_action.verb if c.current_action else "idle"
lines.append(
f" - {c.name} at {loc} β€” declared goal: {c.goal!r} β€” doing: {doing}"
)
if not others:
lines.append(" (none β€” you are alone)")
lines.append("")
lines.append(f"Shell lock holder: {world.shell_lock_holder or 'free'}")
if world.shell_queue:
lines.append(f"Queue: {world.shell_queue}")
if world.board:
lines.append("")
lines.append("Message board (most recent first):")
for post in reversed(world.board[-8:]):
lines.append(f" [{post.tick}] {post.author}: {post.text}")
if world.shell_lines:
lines.append("")
lines.append("Recent shell activity:")
for s in world.shell_lines[-6:]:
tag = {"in": "$", "out": ">", "err": "!", "system": "*"}.get(s.kind, "?")
lines.append(f" {tag} {s.author}: {s.text.strip()[:120]}")
lines.append("")
lines.append(
"Decide your next single action and call exactly one tool. "
"Do not explain; just act."
)
return "\n".join(lines)
def _fmt_traits(traits: dict[str, int]) -> str:
return ", ".join(f"{k}={v}" for k, v in traits.items())