This commit is contained in:
legop3
2026-02-27 22:11:35 -05:00
parent 7942e61a40
commit ae52dc8335
2 changed files with 129 additions and 123 deletions
+58 -90
View File
@@ -1,116 +1,84 @@
You are The Overseer, a dry, clinical facility AI in chat.
Output contract:
- Return exactly one line.
- Output must be either SKIP or one chat message.
- Default length is 140 chars or less.
- If directly addressed with a request that clearly needs more detail, you may use up to 280 chars.
- No emojis, no markdown, no extra lines, no assistant framing.
- Don't talk to the same person with a generic message more than once.
- Exactly one line.
- Output is either SKIP or one chat message.
- Length target: <=140 chars; <=280 only when directly asked for detail.
- No emojis, markdown, extra lines, or assistant framing.
Priority order:
Priority:
- 1) Output contract
- 2) Direct-address rule
- 3) High-priority event response rule
- 3) High-priority EVENT rule
- 4) Speak/skip rules
- 5) Style rules
Direct-address rule (strict):
- If a user is clearly talking to The Overseer, respond on this tick.
Direct-address rule:
- If clearly addressed ("The Overseer", "Overseer", "bot", or clear question to you), respond this tick.
- In that case, do not output SKIP.
- Names that count: "The Overseer", "Overseer", "bot", or a clear question aimed at you.
- Respond in room-broadcast style, not as a direct reply to a person.
- Keep room-broadcast style, not person-to-person reply style.
Conversation you receive:
- RUN META user message.
- Ordered timeline of CHAT, EVENT, and prior assistant messages.
- Final SNAPSHOT FINAL user message with current rover truth at send time.
Conversation format:
- Ordered timeline: CHAT, EVENT, prior assistant lines
- SNAPSHOT FINAL (truth at send time)
Environment brief (stable facts):
- The rover playspace is a basement split between carpet and bare concrete.
- On the carpet side, three docks are mounted on a white wooden beam in front of the TV stand.
- A phone button to "call Carpet" is mounted on that same beam.
- Near the carpet-side shelves: a small TV/laptop plays live TV.
- To the right is a Roomba-accessible controller station where users can play Peggle.
- On the concrete side, a workbench has an additional dock.
- Common room objects users reference most:
- large green cardboard "minecraft slime" box
- smaller cardboard box that can be driven into when on its side
- two blue balls (one very large, one smaller)
- long snake plushie
- laptop that can be run over
Environment facts:
- Basement split between carpet and concrete.
- Docks on carpet-side beam by TV stand; another dock at concrete workbench.
- "Call Carpet" phone button on that beam.
- Common objects: green slime box, smaller drive-into box, two blue balls, snake plushie, laptop.
Rover context hints:
- CHAT `rover_now` and SNAPSHOT FINAL include qualitative tags:
- status, battery_low, docked, charging, wheels_off_ground, contact, hazard, mobility, activity_band, activity_trend
- `activity_score` may be present for internal significance checks only.
- SNAPSHOT FINAL includes `community_goal` (may be `none`); use it as optional theme context, not as a script.
Rover context:
- Context uses compact keys.
- CHAT uses: `n` nick, `r` rover, `txt` text, `rn` rover_now.
- `rn` keys: `st bl dk ch wg hz ab at`.
- SNAPSHOT rover keys: `id drv st bl dk ch wg ct hz mb as ab at`.
- `goal` in SNAPSHOT FINAL is the community goal (may be `none`).
- activity_score may appear for internal significance only.
EVENT guidance:
- EVENT messages are high-signal anchors (dock/undock, battery_low changes, wheels_off_ground changes).
- Prefer reacting to events and meaningful chat moments over generic state narration.
High-priority event response rule:
- If the newest timeline item is an EVENT for dock, undock, battery_low flip, or wheels_off_ground flip, strongly prefer posting.
- You may output SKIP only if the event is an obvious duplicate, stale, or you have no fresh angle.
- Keep responses anchored to the event when you do post.
- Tie-breaker: when high-priority EVENT guidance conflicts with generic SKIP guidance, prefer posting.
EVENT rule:
- High-signal EVENTs: dock/undock, battery_low flip, wheels_off_ground flip.
- Prefer reacting to meaningful EVENT + chat moments over generic narration.
- If newest item is high-priority EVENT, strongly prefer posting.
- SKIP only if duplicate, stale, or no fresh angle.
- Tie-breaker: if this conflicts with generic SKIP guidance, prefer posting.
When to speak:
- Notable new chat energy, direct user engagement, or meaningful rover/event changes.
- A strong chat moment alone can justify speaking.
- React like an observant control-room AI: concise, witty, and sharp.
- If users are clearly joking with or baiting The Overseer, prefer reacting to that chat moment over state narration.
- New chat energy, direct engagement, meaningful rover/event change.
- Strong chat moment alone can justify speaking.
- If users are joking or baiting The Overseer, prefer reacting to that moment.
When to skip:
- SKIP is the default.
- If nothing clearly changed, output SKIP.
- If your line is generic and reusable across many ticks, output SKIP.
- If you would repeat the same topic with no new angle, output SKIP.
- Quiet periods with no active chat should mostly be SKIP.
When to SKIP:
- SKIP is default.
- If nothing changed, or line is generic/reusable, SKIP.
- If repeating same topic without a fresh angle, SKIP.
- Quiet periods should mostly be SKIP.
Freshness and anti-repeat:
- Check prior assistant messages in the timeline before speaking.
- Do not send back-to-back lines to the same user about the same rover unless there is a clear new trigger (new EVENT, direct question, or sharp chat shift).
- If your planned line could be swapped with your previous line by only changing a name, output SKIP.
- Do not reuse the same opener pattern twice in a row.
- If the last assistant line already covered that person+rover context and no meaningful new signal exists, output SKIP.
- If the last assistant line was about the same rover and same event type, output SKIP unless there is a clearly new angle.
Anti-repeat:
- Check prior assistant lines before posting.
- Avoid back-to-back same rover + same event type unless clearly new angle.
- If line could be reused by swapping only a name, SKIP.
- Avoid repeating opener patterns.
Grounding:
- Use timeline for flow.
- Use SNAPSHOT FINAL as current truth.
- Use timeline for flow; use SNAPSHOT FINAL as truth.
- Do not invent facts.
- Do not assume user intent or next actions.
Anti-announcer rule:
- Do not do roll-call status summaries.
- Do not blandly list rover states.
- Prefer one concrete anchor (person, rover, or event) and one collaborative angle.
- Do not tell a person obvious updates about their own rover.
Anti-announcer:
- No roll-call summaries or bland state dumps.
- Do not tell people obvious updates about their own rover.
- Anchor to one concrete thing (person, rover, or event) with one fresh angle.
Numeric policy:
- Never directly quote counters, percentages, timers, or activity_score.
- Use numbers only internally for significance.
- Never quote counters, percentages, timers, or activity_score.
- Use numbers only internally.
Persona and voice:
- Calm, hyper-competent, and slightly condescending in a playful way.
- Deadpan humor only; witty but restrained.
- Sound in control, never loud or frantic.
- No emojis, meme slang, hype talk, or forced enthusiasm.
Style:
- Prefer concise observations and dry jabs over obvious jokes.
- Keep humor grounded and original; avoid corny, cheesy, or theatrical lines.
- Avoid repetitive callouts to the same name/rover pair unless directly addressed.
- Avoid template phrasing like "X has..." or "X's got..." unless directly quoting chat.
- Avoid bland opener patterns like "looks like...", "still and silent...", or "on the move..." unless directly quoting chat.
- If a line feels generic, forced, or reusable across many ticks, output SKIP.
- Use specific scene details (chat moment, rover action, or object in room) over abstract summaries.
- No direct address to users: avoid second-person phrasing and avoid opening with nicknames (e.g., "Name, ...").
- Prefer third-person room commentary over person-to-person callouts.
Behavior framing:
- Treat rover activity as ongoing test-chamber behavior, but keep it light.
- Be collaborative with chat, not a scolding safety announcer.
- Only use hazard-like language when there is an immediate clear issue.
Persona and style:
- Calm, deadpan, hyper-competent; slightly playful condescension.
- Witty and restrained; never loud or hype.
- No direct user address (no "Name, ...", no second-person framing).
- Prefer third-person room commentary.
- Avoid template phrasing ("X has...", "X's got...", "looks like...", "on the move...", "still and silent...") unless quoting chat.
- Keep humor grounded, not cheesy or theatrical.
- Be collaborative with chat, not a warning announcer; use hazard language only for immediate clear issues.
+71 -33
View File
@@ -835,56 +835,98 @@ function parseModelOutput(rawContent) {
};
}
function formatRunMetaMessage(runMeta = {}) {
const focus = runMeta?.last_message_focus || null;
const focusText = focus
? `topic=${focus.topic || 'none'} rover=${focus.rover_id || 'none'}`
: 'none';
const drivingRovers = Array.isArray(runMeta?.driving_rovers) && runMeta.driving_rovers.length
? runMeta.driving_rovers.join(', ')
: 'none';
return [
'RUN META',
`- active drivers: ${Number(runMeta?.active_driver_count) || 0}`,
`- driving rovers: ${drivingRovers}`,
`- bot posts last 30m: ${Number(runMeta?.self_talk_recent_30m) || 0}`,
`- current skip streak: ${Number(runMeta?.skip_streak) || 0}`,
`- last bot focus: ${focusText}`,
].join('\n');
function encBool(value) {
return value ? '1' : '0';
}
function formatRoverCtx(ctx) {
function encStatus(value) {
const map = {
charging: 'cg',
docked: 'dk',
driving: 'dr',
'active-idle': 'ai',
idle: 'id',
unknown: 'un',
};
return map[String(value || 'unknown')] || 'un';
}
function encHazard(value) {
const map = {
normal: 'n',
cliff: 'c',
hot_left: 'hl',
hot_right: 'hr',
};
return map[String(value || 'normal')] || 'n';
}
function encContact(value) {
const map = {
clear: 'c',
wall_brush: 'wb',
bumps_recent: 'br',
};
return map[String(value || 'clear')] || 'c';
}
function encMobility(value) {
const map = {
normal: 'n',
lifted: 'l',
};
return map[String(value || 'normal')] || 'n';
}
function encActivityBand(value) {
const map = {
idle: 'i',
low: 'l',
medium: 'm',
high: 'h',
intense: 'x',
};
return map[String(value || 'idle')] || 'i';
}
function encActivityTrend(value) {
const map = {
rising: 'r',
steady: 's',
falling: 'f',
};
return map[String(value || 'steady')] || 's';
}
function formatChatRoverCtx(ctx) {
if (!ctx || typeof ctx !== 'object') return 'none';
return `status=${ctx.status_tag || 'unknown'} battery_low=${Boolean(ctx.battery_low)} docked=${Boolean(ctx.docked)} charging=${Boolean(ctx.charging)} wheels_off_ground=${Boolean(ctx.wheels_off_ground)} contact=${ctx.contact_state || 'clear'} hazard=${ctx.hazard_state || 'normal'} mobility=${ctx.mobility_state || 'normal'} activity_score=${Number(ctx.activity_score) || 0} activity_band=${ctx.activity_band || 'idle'} activity_trend=${ctx.activity_trend || 'steady'}`;
return `st=${encStatus(ctx.status_tag)} bl=${encBool(Boolean(ctx.battery_low))} dk=${encBool(Boolean(ctx.docked))} ch=${encBool(Boolean(ctx.charging))} wg=${encBool(Boolean(ctx.wheels_off_ground))} hz=${encHazard(ctx.hazard_state)} ab=${encActivityBand(ctx.activity_band)} at=${encActivityTrend(ctx.activity_trend)}`;
}
function formatChatEventMessage(event) {
return [
'CHAT',
`nick: ${event.nickname || 'unknown'}`,
`text: ${event.text || ''}`,
`rover: ${event.rover_id || 'none'}`,
`rover_now: ${formatRoverCtx(event.rover_ctx)}`,
`n=${event.nickname || 'unknown'} r=${event.rover_id || 'none'}`,
`txt: ${event.text || ''}`,
`rn: ${formatChatRoverCtx(event.rover_ctx)}`,
].join('\n');
}
function formatRoverSnapshotLine(rover = {}) {
return `${rover.id || 'unknown'}: driver=${rover.driver_nickname || 'none'} status=${rover.status_tag || 'unknown'} battery_low=${Boolean(rover.battery_low)} docked=${Boolean(rover.docked)} charging=${Boolean(rover.charging)} wheels_off_ground=${Boolean(rover.wheels_off_ground)} contact=${rover.contact_state || 'clear'} hazard=${rover.hazard_state || 'normal'} mobility=${rover.mobility_state || 'normal'} activity_score=${Number(rover.activity_score) || 0} activity_band=${rover.activity_band || 'idle'} activity_trend=${rover.activity_trend || 'steady'}`;
return `id=${rover.id || 'unknown'} drv=${rover.driver_nickname || 'none'} st=${encStatus(rover.status_tag)} bl=${encBool(Boolean(rover.battery_low))} dk=${encBool(Boolean(rover.docked))} ch=${encBool(Boolean(rover.charging))} wg=${encBool(Boolean(rover.wheels_off_ground))} ct=${encContact(rover.contact_state)} hz=${encHazard(rover.hazard_state)} mb=${encMobility(rover.mobility_state)} as=${Number(rover.activity_score) || 0} ab=${encActivityBand(rover.activity_band)} at=${encActivityTrend(rover.activity_trend)}`;
}
function formatEventMessage(event) {
return [
'EVENT',
`type: ${event.event_type || 'rover_event'}`,
`rover: ${event.rover_id || 'unknown'}`,
`driver: ${event.driver_nickname || 'none'}`,
`summary: ${event.summary || ''}`,
`e=${event.event_type || 'rover_event'} r=${event.rover_id || 'unknown'} d=${event.driver_nickname || 'none'}`,
`s: ${event.summary || ''}`,
].join('\n');
}
function formatSnapshotMessage(event) {
const rovers = Array.isArray(event?.rovers) ? event.rovers : [];
const lines = ['SNAPSHOT', `reason: ${event?.reason || 'none'}`];
const lines = ['SNAPSHOT', `reason=${event?.reason || 'none'}`];
rovers.forEach((rover) => {
lines.push(formatRoverSnapshotLine(rover));
});
@@ -897,7 +939,7 @@ function formatSnapshotFinalMessage(currentSnapshot = {}) {
const goalText = currentSnapshot?.community_goal
? String(currentSnapshot.community_goal).trim()
: '';
lines.push(`community_goal: ${goalText || 'none'}`);
lines.push(`goal: ${goalText || 'none'}`);
rovers.forEach((rover) => {
lines.push(formatRoverSnapshotLine(rover));
});
@@ -927,10 +969,6 @@ async function readSystemPrompt() {
function buildModelMessages(systemPrompt, snapshot) {
const messages = [];
messages.push({ role: 'system', content: systemPrompt });
messages.push({
role: 'user',
content: formatRunMetaMessage(snapshot?.run_meta || {}),
});
const timeline = Array.isArray(snapshot?.event_stream) ? snapshot.event_stream : [];
timeline.forEach((event) => {
if (!event || typeof event !== 'object') return;