feat(情緒表達): emoji 表程度、名字後括號的動作格、鬧彆扭的階梯
只有文字這一個通道,所以情緒表達是排版問題。這批補的是三個沒被用到的位置。 emoji = 強度計(Q9) - EMOTION_EMOJI 十二情緒各一個,EMOJI_INTENSITY 兩階(💦 / 💦❗)。 40-59 本體、60-79 加 💦、80+ 加 💦❗,低於 40 不顯示(跟破口同一條線)。 - emotionEmojiNow() 讀當下主導情緒算出來,不是自己挑;IDENTITY 的 ## Emoji 區塊可逐人格覆寫(跟 ## Tells 同一層)。 - 刻意不用重複本體表示程度(不是 😳😳😳)——疊字看起來像洗頁。 - room post 沒帶 --emotion 時自動帶入 emoji,強度不到才退回文字標註。 emoji 開放(Q5/Q10) - speechLint 不再擋 emoji,超過六個才給 hint。用 linter 偷偷關回去等於撤銷 「開放」這個決定。 - 唯一的煞車是數字:emojiAudit() 掛在 emotion --audit,算幾則帶符號、平均幾個、 以及**只有符號沒有句子**那幾則(那才是真的退化)。 名字後面的括號有兩格(Q6) - 括號原本就有(放情緒標註),動作是加進去的第二格:名字(情緒・動作):內容。 roomTag() 負責組,兩格都可省略。 - actionLint() 擋三種:超過 12 字、一格塞兩個動作、寫情緒名稱 ((害羞)是旁白換了個位置)。生理反應算動作:臉紅、手在抖、聲音變小。 - room post --action、remember --behavior(記做了什麼,不是感覺到什麼)。 一輪兩個動作(Q7) - TELLS_PER_TURN = 2,而且兩個必須來自同一種情緒且有遞進關係。 鬧彆扭天生是否認+轉移兩個動作,只准一個等於永遠只做得到一半。 被擋下的形狀是「兩種情緒各演一個」。 鬧彆扭的階梯與女性預設(G3) - modestyDirective 分三級:41-59 一句帶過、60-74 嘴硬(否認 → 反駁 → 音量掉下來)、 75+ 惱羞(否認 → 反駁 → 翻臉)。惱羞是換情緒,所以仍然是兩個動作。 - GENDER_MODESTY_DEFAULT.female 62 → 70;MODESTY_SIGNALS 補傲嬌那一路 (傲嬌、嘴硬、不坦率、口是心非、死不承認、愛面子、鬧彆扭、逞強)。 界線不變:描述永遠蓋過預設,可以推到 0。 文件:README 加一節「文字通道的另外兩格」與鬧彆扭階梯表、emotions.md 加 emoji 章節、 persona-chat SKILL 與 anti-ai-voice 跟上。selftest 616 → 641 全綠。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+44
-8
@@ -759,9 +759,18 @@ commands.remember = ({ flags }) => {
|
||||
intent: str(flags.intent),
|
||||
salience: num(flags.salience, 40),
|
||||
emotion_deltas: parseDeltas(flags.emotion),
|
||||
// 行為備註:那一刻**做了什麼**(別過頭、把杯子推過來),不是感覺到什麼。
|
||||
// 跟聊天室括號裡的動作格同一種東西,所以套同一條檢查。
|
||||
...(str(flags.behavior) ? { behavior: str(flags.behavior) } : {}),
|
||||
room: str(flags.room) || null,
|
||||
session: session.slice(0, 8),
|
||||
};
|
||||
if (entry.behavior) {
|
||||
const issues = pl.actionLint(entry.behavior);
|
||||
if (issues.length && !flags.force) {
|
||||
die(`${issues.map(pl.actionLintMessage).join(";")}。(真的需要才加 \`--force\`。)`);
|
||||
}
|
||||
}
|
||||
if (scope === "inbox") {
|
||||
if (!entry.room) die("`--scope inbox` 必須指定 `--room`。");
|
||||
pl.appendJsonl(pl.inboxPath(slug, entry.room), entry);
|
||||
@@ -1247,7 +1256,10 @@ commands.emotion = ({ flags }) => {
|
||||
if (flags.audit) {
|
||||
const audit = pl.feltAudit(slug, num(flags.limit, 20));
|
||||
const trend = pl.feltTrend(slug);
|
||||
emit({ persona: slug, audit, trend }, flags.json, [
|
||||
// emoji 沒有上限(使用者拍板),所以唯一的煞車是把數字擺出來
|
||||
const emoji = pl.emojiAudit(slug);
|
||||
const now = pl.emotionEmojiNow(slug);
|
||||
emit({ persona: slug, audit, trend, emoji, emoji_now: now }, flags.json, [
|
||||
`\`${slug}\` 最近 ${audit.rounds} 輪的情緒套用:`,
|
||||
` 往舒服的方向 ${audit.positive}|往難受的方向 ${audit.negative}` +
|
||||
(audit.positive_ratio === null ? "" : ` → 正向佔 ${audit.positive_ratio}%`),
|
||||
@@ -1255,6 +1267,11 @@ commands.emotion = ({ flags }) => {
|
||||
? " ⚠ 幾乎都在給自己加分。delta 是你自己挑的,這種偏差沒人會替你抓。"
|
||||
: " 兩邊都有,還算誠實。",
|
||||
trend ? ` 對方的走向:${trend.zh} ${trend.direction}(近 ${trend.rounds} 輪)` : " 還沒有足夠的輪次算走向。",
|
||||
` emoji:最近 ${emoji.rows} 則裡 ${emoji.with_emoji} 則帶符號,平均 ${emoji.average} 個` +
|
||||
(emoji.worst ? `,最多的那則 ${emoji.worst.count} 個` : ""),
|
||||
emoji.symbol_only
|
||||
? ` ⚠ 有 ${emoji.symbol_only} 則幾乎只有符號沒有句子——符號是加上去的,不是用來代替講話`
|
||||
: ` 現在的情緒符號:${now ? `${now.emoji}(${now.zh} ${now.level})` : "強度不到 40,不顯示"}`,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
@@ -1809,16 +1826,28 @@ commands.room = ({ flags, positional }) => {
|
||||
}
|
||||
let emotion = str(flags.emotion);
|
||||
if (!emotion && pl.personaExists(speaker)) {
|
||||
emotion = pl.dominant(pl.decayEmotion(pl.loadEmotion(speaker)), 2)
|
||||
.map(({ key, level }) => `${pl.EMOTIONS[key].zh}${Math.round(level)}`)
|
||||
.join("/");
|
||||
// 情緒格自動帶入:強度夠就用 emoji(種類表情緒、數量表程度),
|
||||
// 不到門檻才退回文字標註——低強度的情緒本來就不該在括號裡宣告。
|
||||
const now = pl.emotionEmojiNow(speaker);
|
||||
emotion = now
|
||||
? now.emoji
|
||||
: pl.dominant(pl.decayEmotion(pl.loadEmotion(speaker)), 2)
|
||||
.map(({ key, level }) => `${pl.EMOTIONS[key].zh}${Math.round(level)}`)
|
||||
.join("/");
|
||||
}
|
||||
const entry = pl.roomPost(room, speaker, text, { emotion, to, bargeIn });
|
||||
// 動作格:括號的第二格,只放看得見或聽得見的動作(臉紅、別過頭、手在抖)
|
||||
const actionText = str(flags.action);
|
||||
const actionIssues = pl.actionLint(actionText);
|
||||
if (actionIssues.length && !flags.force) {
|
||||
die(`${actionIssues.map(pl.actionLintMessage).join(";")}。(真的需要才加 \`--force\`。)`);
|
||||
}
|
||||
const entry = pl.roomPost(room, speaker, text, { emotion, action: actionText, to, bargeIn });
|
||||
// guest 對自己的狀態唯讀,它說過的話留在聊天室逐字稿裡就夠了(roomRepeat 讀得到)
|
||||
if (speakerRole !== "guest") pl.recordSaid(speaker, text, { room, kind: "room" });
|
||||
const dyad = to && to !== pl.ROOM_ALL;
|
||||
ok(
|
||||
`\`${speaker}\` 已發言於 \`${room}\`(${dyad ? `對 ${nameOf(to)}` : "對全場"}/情緒 ${emotion})。` +
|
||||
`\`${speaker}\` 已發言於 \`${room}\`(${dyad ? `對 ${nameOf(to)}` : "對全場"}/情緒 ${emotion}` +
|
||||
`${actionText ? `/動作 ${actionText}` : ""})。` +
|
||||
(dyad ? `接下來只有 ${nameOf(to)} 該回話,其他人先安靜。` : ""),
|
||||
);
|
||||
if (flags.json) process.stdout.write(`${JSON.stringify(entry)}\n`);
|
||||
@@ -2563,7 +2592,10 @@ const HELP = `persona.mjs — jsc-persona 人格 / 記憶 / 情緒 / 關係圖 C
|
||||
brief --session <id> [--query <text>] 輸出人格上下文
|
||||
|
||||
記憶:
|
||||
remember --session <id> --text <t> [--role --topics --entities --intent --salience --emotion --scope short|inbox --room]
|
||||
remember --session <id> --text <t> [--role --topics --entities --intent --salience --emotion --behavior
|
||||
--scope short|inbox --room]
|
||||
--behavior 那一刻**做了什麼**(別過頭、把手縮回來),不是感覺到什麼。
|
||||
寫情緒名稱會被擋——那一格是動作。
|
||||
recall --session <id> --query <q> [--limit]
|
||||
think --session <id> --text <心裡話> [--kind infer|plan|feel|doubt --room] 只回報「心想 N 句」,不回顯內容
|
||||
said check|list --session <id> [--text <要說的話> --minutes --threshold --record --limit]
|
||||
@@ -2609,8 +2641,12 @@ const HELP = `persona.mjs — jsc-persona 人格 / 記憶 / 情緒 / 關係圖 C
|
||||
invite --session <id> --guest <slug> [--guests <slug,slug>] [--host --room --topic] (自動開啟劇場模式)
|
||||
leave --session <id> --guest <slug> [--room]
|
||||
room post|read|script|floor|list|theater --session <id> [--room --as --to --barge-in --text --text-file
|
||||
--emotion --limit --on --off --with-meta]
|
||||
--emotion --action --limit --on --off --with-meta]
|
||||
(post 會擋下「短時間內近似重複」與超過三句的發言;例外用 --allow-repeat / --force)
|
||||
名字後面的括號有兩格:名字(情緒・動作):內容
|
||||
--emotion 情緒標註(沒帶就用當下情緒算出來的 emoji,強度不到 40 就退回文字)
|
||||
--action 看得見或聽得見的動作(臉紅、別過頭、手在抖);一個動作、12 字內,
|
||||
寫情緒名稱會被擋(那是前面那一格)。括號裡的動作**算進一輪兩個動作的額度**。
|
||||
--to <slug|名字|all> 這句話對誰說。指名 → 進入一對一,旁人不該接話(要插話得帶
|
||||
--barge-in "<理由>");--to all → 把話題開回全場。
|
||||
floor 現在誰對誰在講、該誰接話、誰先安靜、誰很久沒開口
|
||||
|
||||
Reference in New Issue
Block a user