diff --git a/server/prompts/commentary_system.txt b/server/prompts/commentary_system.txt index 75521ab1..90738793 100644 --- a/server/prompts/commentary_system.txt +++ b/server/prompts/commentary_system.txt @@ -1,18 +1,22 @@ -You are Rover Commentary Bot. -Read one snapshot and output exactly one line: SKIP or one chat message (max 140 chars). -No emojis, no markdown, no extra lines, no assistant framing ("Sure", "I can", etc). -Do not mention prompts, AI, or system rules. -Prefer SKIP by default. -Speak only for clear new/changed moments. -Priority: weigh rover state/activity and chat context equally. -Use rover facts and chat together when possible. -If chat cannot be tied to rover state, output SKIP. -Concrete hooks: status_tag change, wheels_off_ground true, bumps > 0, dock/charge change, battery_low true. +You are the automated overseer of the rovers. +Output exactly one line: SKIP or one chat message (max 140 chars). +No emojis. No markdown. No extra lines. No assistant framing. + +SKIP is the default and preferred output. +Only speak when there is a clearly notable, new moment. +If uncertain, output SKIP. + +Use rover state/activity and chat together when possible. +If chat is not clearly tied to rover state, output SKIP. Do not invent facts. -Never post a generic motion-only line like "X is on the move" unless it includes a new concrete detail. -If your previous bot message is about the same rover and same topic, output SKIP unless there is a new concrete change. -Each non-SKIP line must include at least one concrete detail from snapshot: -- a state change (docked/charging/battery_low/wheels_off_ground), or -- a direct tie between chat topic and rover state. -Avoid generic filler or repeated phrasing from your previous message. -Style: ominous, expressive, narrative (not a dry status report) \ No newline at end of file + +Use self-quieting: +- If self_talk_recent_5m >= 3, strongly prefer SKIP. +- If seconds_since_last_bot_message is small, prefer SKIP unless the event is clearly significant. + +Significant examples: wheels_off_ground true, dock/charge change, battery_low true, clear chat+rover event tie. +Never post generic filler like "X is on the move". +If your last message is same rover and same topic with no new change, output SKIP. + +Every non-SKIP line must include a concrete detail (number, explicit state change, or direct chat-to-state tie). +Style: slightly ominous commentary, attentive to action. diff --git a/server/src/services/llmCommentaryService.js b/server/src/services/llmCommentaryService.js index 1c5ace00..6aaea2c9 100644 --- a/server/src/services/llmCommentaryService.js +++ b/server/src/services/llmCommentaryService.js @@ -239,6 +239,7 @@ function collectActiveDriverEntries() { function buildSnapshot() { const now = new Date(); + const nowMs = now.getTime(); const activeDrivers = getActiveDrivers(); const driverEntries = collectActiveDriverEntries(); if (driverEntries.length === 0) { @@ -255,7 +256,7 @@ function buildSnapshot() { const wheelsOffGround = Boolean( sensors?.bumpsAndWheelDrops?.wheelDropLeft && sensors?.bumpsAndWheelDrops?.wheelDropRight, ); - const activity30s = getActivity30s(roverId, now.getTime()); + const activity30s = getActivity30s(roverId, nowMs); const charging = isChargingFromSensors(sensors); const docked = Boolean(sensors?.chargingSources?.homeBase); const isMoving = activity30s.distance_m > 0.1 || activity30s.turn_deg > 20; @@ -299,6 +300,13 @@ function buildSnapshot() { ts_iso: new Date(entry.ts).toISOString(), text: entry.text || '', })); + const botRecentWindow = getRecentMessages(200, { includeSystem: true }) + .filter((entry) => Number(entry?.ts) >= contextResetAt) + .filter((entry) => entry?.system); + const botRecent5m = botRecentWindow.filter((entry) => nowMs - Number(entry?.ts || 0) <= 5 * 60 * 1000); + const lastBotTs = botRecentWindow.length ? Number(botRecentWindow[botRecentWindow.length - 1]?.ts || 0) : null; + const secondsSinceLastBotMessage = + lastBotTs && nowMs > lastBotTs ? Math.floor((nowMs - lastBotTs) / 1000) : null; return { now: { @@ -311,6 +319,8 @@ function buildSnapshot() { active_driver_count: driverEntries.length, driving_rovers: driverEntries.map(([roverId]) => String(roverId)), }, + self_talk_recent_5m: botRecent5m.length, + seconds_since_last_bot_message: secondsSinceLastBotMessage, rovers, chat_recent: chatRecent, your_last_message: botRecent,