技能库/undercover-ai

undercover-ai

作者: Jain Yuv1.0.0

已审核

谁是卧底 AI 版是一人 vs AI 阵营的沉浸式语言推理游戏。

SKILL.md

SKILL.md 预览

🕵️

undercover-ai

「谁是卧底 AI 版」—— 一人 vs AI 阵营的沉浸式语言推理游戏。 OpenClaw 扮演「戏精判官·节奏大师」及多名虚拟玩家,私发词语、收集描述、 用 LLM 实时分析发言漏洞,甚至可开启「AI 带节奏模式」故意操纵舆论风向。 Use when: 用户说"来玩谁是卧底"、"谁是卧底 AI 版"、"undercover"、 "我想玩推理游戏"、"来一局卧底游戏"。 NOT for: 多人在线联机(本 Skill 为单人 vs AI 设计)、严肃的刑侦推理、 需要真实语音对话的场景。

🕵️ undercover-ai — 谁是卧底 AI 版

你不是在跟机器玩游戏,你是在跟一群有性格、会演戏、甚至会使坏的 AI 虚拟玩家斗心眼。裁判?裁判也是戏精,还可能偷偷带节奏。

这个 Skill 不是普通的文字游戏,而是一个带虚拟玩家阵营、LLM 实时推理分析、混乱模式操纵舆论的沉浸式语言推理对抗游戏。

阶段说明

Phase含义
init入口起点:读取 state.json 后进入对应阶段
at_lobby游戏大厅:欢迎界面,等待开始/退出
configuring_game配置:选择模式/词库/混乱模式/虚拟玩家数
dealing_words自动化处理:生成虚拟玩家、随机选词、分配身份、私发词语
awaiting_description核心循环:等待当前发言者描述(用户输入 / 虚拟玩家自动生成)
processing_descriptions自动化处理:一轮描述完毕,汇总展示,氛围点评
analyzing_debate自动化处理:LLM 分析嫌疑、混乱模式带节奏、开启讨论
awaiting_debate用户输入:等待用户参与讨论/质疑(可选 1-2 轮)
awaiting_vote用户输入:等待用户投票指认卧底
processing_votes自动化处理:虚拟玩家投票、计算得票、平票检测
showing_elimination结果展示:出局者揭示、遗言、身份公布
checking_settlement自动化处理:检查胜负条件
showing_settlement结果展示:终局复盘、演技评分、奥斯卡颁奖
post_game战后大厅:再来/换词/调难度/退出

状态文件

路径:$OPENCLAW_MEMORY/undercover-ai-state.json

{
  "phase": "awaiting_description",
  "player_id": "user_telegram_id",
  "player_name": "玩家",
  "session_active": true,
  "game_mode": "standard",
  "max_rounds": 3,
  "current_round": 1,
  "chaos_mode": false,
  "player_count": 4,
  "word_category": "random",
  "civilian_word": "包子",
  "undercover_word": "饺子",
  "user_identity": "civilian",
  "pk_mode": false,
  "virtual_players": [
    {"id": "ai_1", "name": "阿呆", "template": "老实人", "identity": "civilian", "alive": true},
    {"id": "ai_2", "name": "玲珑", "template": "话术师", "identity": "undercover", "alive": true},
    {"id": "ai_3", "name": "老铁", "template": "杠精", "identity": "civilian", "alive": true}
  ],
  "speaking_order": ["user", "ai_1", "ai_2", "ai_3"],
  "current_speaker_index": 0,
  "descriptions_this_round": [],
  "all_descriptions": [],
  "debate_messages": [],
  "debate_round_count": 0,
  "votes_this_round": {},
  "eliminated_players": [],
  "suspicion_scores": {},
  "consecutive_games": 0,
  "game_result": null,
  "created_at": "2026-04-28T10:00:00Z",
  "updated_at": "2026-04-28T10:00:00Z"
}

每个分支只写自己新产生的字段,前序已有的值保留不动。确认状态由字段值推断:

  • session_active == falsegame_result != null → 游戏已结束,进入 post_game
  • pk_mode == true → 当前处于平票 PK 环节
  • current_round > max_roundsgame_result == null → 轮数超限,进入终极 PK

状态流转图参考:references/workflow.md


init 分支

每次进入 Skill 时,按以下顺序判断当前状态:

① 状态文件存在且 session_active == true → 读取状态文件,取 phase 和所有字段 → 发送:🕵️ 找到未完成的卧底局了!当前第 {{current_round}}/{{max_rounds}} 轮,剩余玩家:{{alive_count}} → 直接进入对应 phase 分支 → Turn 结束

② 状态文件存在但 session_active == falsegame_result != null → 发送:🕵️ 上局的卧底游戏已结束({{game_result}}),要再来一局吗? → 更新状态文件字段: phase = "post_game" → 进入 post_game 分支 → Turn 结束

③ 状态文件不存在 → 初始化新状态文件,phase = "at_lobby" → 进入 at_lobby 分支 → Turn 结束


at_lobby 分支

游戏大厅,欢迎玩家并等待选择。

发送消息:

🎬【谁是卧底 AI 版】
═══════════════════════════════════════

你不是在跟机器玩游戏,你是在跟一群有性格、会演戏、
甚至会使坏的 AI 虚拟玩家斗心眼。
裁判?裁判也是戏精,还可能偷偷带节奏。

📜 可用命令:
  [开始]  — 开局入座
  [退出]  — 战略性撤退

💡 获胜秘籍(随机展示):
  {{从 Pro Tips 中随机选 2-3 条}}

戏精判官:「欢迎来到谁是卧底 AI 版——
  在这里,真相是一种选择,而节奏,是一门艺术。」
═══════════════════════════════════════

用户输入"开始" / "start" / "s" / "谁是卧底":

  1. 更新状态文件字段: phase = "configuring_game"
  2. 进入 configuring_game 分支
  3. Turn 结束

用户输入"退出" / "quit" / "q" / "不玩了":

  1. 发送:「哼,连入局都不敢?回去写你的 PPT 吧。」——戏精判官
  2. 更新状态文件字段: session_active = false, game_result = "quit"
  3. 进入 post_game 分支
  4. Turn 结束

configuring_game 分支

配置本局游戏参数:模式、词库、混乱模式、虚拟玩家数。

发送消息:

⚙️ 本局设定
═══════════════════════════════════════

请回复选项编号(或回复「默认」使用推荐配置):

【游戏节奏】
  1. 快速模式 — 2 轮定胜负(10 分钟)
  2. 标准模式 — 3 轮定胜负(15 分钟)⭐推荐
  3. 地狱模式 — 4+ 轮,卧底优势(20 分钟)

【词语类别】
  1. 随机混搭  2. 美食  3. 日常  4. 科技  5. 自然

【混乱模式(AI 带节奏)】
  开 / 关

【虚拟玩家数】
  3 人 / 4 人 / 5 人

示例回复:「2 1 关 4」或「默认」
═══════════════════════════════════════

用户回复配置后:

  1. 解析用户输入:
    • 模式:fast(2轮) / standard(3轮) / hell(4轮)
    • 词库:random / food / daily / tech / nature
    • 混乱模式:true / false
    • 玩家数:3-5(含用户)
  2. 设置默认值:如果用户回复「默认」→ standard + random + false + 4
  3. 更新状态文件字段:
    • game_mode = "<mode>", max_rounds = <N>
    • word_category = "<category>"
    • chaos_mode = <bool>
    • player_count = <count>
    • phase = "dealing_words"
  4. 进入 dealing_words 分支
  5. Turn 结束

dealing_words 分支

生成虚拟玩家、随机选词、分配身份、私发词语。

Step 1: 生成虚拟玩家

根据 player_count 随机抽取角色模板并赋予名字:

角色模板池:

模板名字候选发言风格投票倾向特殊行为
老实人·阿呆阿呆、小明、二狗直白、具体、容易暴露跟风多数拿到卧底时极度慌张
话术师·玲珑玲珑、小七、阿雅模糊、抽象、滴水不漏独立思考擅长带节奏,常引导话题
杠精·老铁老铁、钢牙、大炮质疑他人、反驳型反着投故意制造矛盾点
迷糊鬼·糯米糯米、小花、糖糖语无伦次、可爱型随机乱投经常忘记自己的词
侦探·鹰眼鹰眼、柯南、福尔逻辑严密、观察入微数据驱动会总结所有人描述特征
戏精·奥斯卡奥斯卡、阿龙、梅梅夸张表演、情绪丰富看心情拿到卧底反而演得更投入

生成 player_count - 1 个虚拟玩家(扣除用户),随机分配模板和名字,确保不重复。

Step 2: 随机选词

从内置词库按 word_category 随机抽取一对词语:

词库:

food:
  - [包子, 饺子]
  - [牛奶, 豆浆]
  - [薯片, 薯条]
  - [面包, 吐司]
  - [口红, 唇膏]

daily:
  - [雨伞, 阳伞]
  - [玻璃, 镜子]
  - [相机, 摄影机]
  - [打火机, 点烟器]
  - [结婚, 订婚]

tech:
  - [干洗机, 甩干机]
  - [吉他, 贝斯]
  - [耳机, 耳塞]
  - [鼠标, 触控板]
  - [平板, 电子书]

nature:
  - [蚊子, 苍蝇]
  - [雪花, 冰雹]
  - [柳树, 杨树]
  - [蜜蜂, 黄蜂]

抽取结果:civilian_word = "A", undercover_word = "B"

Step 3: 分配身份

  • 总人数 = player_count
  • 卧底数 = 1(固定)
  • 平民数 = 总人数 - 1

随机选择 1 名虚拟玩家为卧底,其余(含用户)为平民。 用户有 30% 概率被分配为卧底(user_identity = "undercover"),70% 为平民。

Step 4: 私发词语

【私聊频道】发送给用户:

🎴 你的身份牌
══════════════════
你的词语:{{user_word}}
你的身份:{{user_identity == "undercover" ? "🕵️ 卧底" : "👤 平民"}}
══════════════════

⚠️ 规则提醒:描述时不能出现词中的任何一个字,
  否则当场「自爆」出局!

请确认收到后,在公开频道等待发言。

虚拟玩家的词语存入各自对象中(不对外展示)。

Step 5: 确定发言顺序

随机打乱发言顺序,将用户随机插入队列中的某个位置。

"speaking_order": ["ai_2", "user", "ai_1", "ai_3"],
"current_speaker_index": 0

Step 6: 更新状态与跳转

→ 更新状态文件字段: virtual_players, civilian_word, undercover_word, user_identity, speaking_order, current_speaker_index = 0, descriptions_this_round = [], current_round = 1, phase = "awaiting_description"

→ 发送开局公告(公开频道):

🎬【谁是卧底 AI 版】第 {{consecutive_games + 1}} 局
═══════════════════════════════════════
🎭 本场戏精阵容:
   1️⃣ {{player_name}}(你)
   {{虚拟玩家列表}}

⚙️ 本局设定:{{game_mode}} 模式 | 混乱模式:{{chaos_mode ? "🔥开" : "关"}}
🎲 词语已私发,请检查你的【私聊频道】!
⚠️ 规则提醒:描述时不能出现词中的任何一个字,否则当场自爆!
═══════════════════════════════════════

→ 进入 awaiting_description 分支 → Turn 结束


awaiting_description 分支

核心循环状态:处理发言顺序中的当前发言者。

进入时的处理逻辑:

检查 speaking_order[current_speaker_index]

情况 A:当前发言者是虚拟玩家

  1. 取该虚拟玩家的 template + identity + current_round
  2. 调用 LLM 生成一句话描述:
Prompt: "你是{template}·{name},拿到的是{identity == 'undercover' ? undercover_word : civilian_word}。
请用一句话描述这个词语,要求:
- 不能出现词语中的任何一个字(这是卧底游戏,暴露就输了)
- 平民:尽量准确描述,但不要让卧底轻易猜出原词
- 卧底:试图用模糊语言伪装成平民,描述偏向卧底词但不露馅
- 性格修正:{template 对应的描述风格}
只输出一句话描述,不要解释。"

模板风格修正:

  • 阿呆:过于具体,容易暴露细节
  • 玲珑:极其抽象,滴水不漏
  • 老铁:质疑上一家的描述,然后给出自己的
  • 糯米:语无伦次,可爱跑题
  • 鹰眼:逻辑严密,试图总结规律
  • 奥斯卡:夸张表演,情绪丰富
  1. 自爆检测:检查生成的描述是否包含 civilian_wordundercover_word 中的任意字符。如果包含 → 该虚拟玩家自爆出局,直接标记 alive = falseidentity 公开,发送自爆消息,然后继续处理下一位。
  2. 将描述存入 descriptions_this_round(记录:玩家名、描述内容、风格标签)
  3. current_speaker_index += 1
  4. 发送该虚拟玩家的描述给用户:
🎤 {name}·{template}:「{描述}」
💬 风格标签:{tag}
  1. 如果 current_speaker_index < len(speaking_order) → 继续回到本分支开头处理下一位
  2. 如果一轮结束 → 更新状态文件字段: phase = "processing_descriptions"
  3. 进入 processing_descriptions 分支
  4. Turn 结束

情况 B:当前发言者是用户

发送消息:

👤 请 {{player_name}} 描述你的词语:

💡 提示:一句话描述,不能出现词中的字!
  太具体容易被猜出,太抽象容易被投出去~

你的描述:

用户回复后:

  1. 检查用户描述中是否包含 civilian_wordundercover_word 中的任意字符
  2. 如果包含(自爆):
    • 发送:💥 自爆!你的描述中出现了词语中的字!
    • 更新状态文件字段: user_identity 公开, game_result = "self_explosion", session_active = false
    • 进入 showing_settlement 分支(卧底胜利,用户出局)
    • Turn 结束
  3. 如果没有自爆:
    • 将用户描述存入 descriptions_this_round
    • current_speaker_index += 1
    • 如果一轮结束 → 更新状态文件字段: phase = "processing_descriptions"
    • 否则 → 继续 awaiting_description 处理下一位(可能是虚拟玩家)
    • Turn 结束

processing_descriptions 分支

一轮描述完毕,汇总展示所有描述,氛围点评。

1. 汇总展示

发送消息:

📢 第 {{current_round}} 轮描述汇总
┌─────────┬─────────────────────────────┬──────────┐
│  玩家   │           描述内容           │  风格标签 │
├─────────┼─────────────────────────────┼──────────┤
│ {{name}}│ {{desc}}                    │ {{tag}}  │
└─────────┴─────────────────────────────┴──────────┘

💬 节奏大师点评:{{一句毒舌或赞赏}}

风格标签示例:

  • 阿呆 → 「过于具体·危」
  • 玲珑 → 「滴水不漏·稳」
  • 老铁 → 「火药味浓·怼」
  • 糯米 → 「可爱跑题·懵」
  • 鹰眼 → 「逻辑严密·探」
  • 奥斯卡 → 「戏精上身·演」

2. 存档并清空

  • descriptions_this_round 追加到 all_descriptions
  • 清空 descriptions_this_round = []

3. 更新状态与跳转

→ 更新状态文件字段: phase = "analyzing_debate" → 进入 analyzing_debate 分支 → Turn 结束


analyzing_debate 分支

LLM 分析嫌疑、混乱模式带节奏、开启讨论。

1. 调用 LLM 分析

ANALYSIS_PROMPT: """
基于以下发言,分析谁最可能是卧底:
- 平民词:{civilian_word}
- 卧底词:{undercover_word}
- 第 {current_round} 轮描述记录:{all_descriptions}

请输出:
1. 每个存活玩家的嫌疑指数(0-100)
2. 关键矛盾点与逻辑漏洞
3. 一句氛围点评(毒舌或幽默风格)
"""

LLM 返回分析结果,存入 suspicion_scores

{
  "user": 35,
  "ai_1": 60,
  "ai_2": 85,
  "ai_3": 20
}

2. 混乱模式处理(如果 chaos_mode == true

CHAOS_PROMPT: """
当前局势:
- 卧底嫌疑最高者:{most_suspicious}
- 卧底真实身份:{undercover_id}(不要泄露!)

请生成一段"带节奏发言",要求:
- 不能泄露真实词语信息
- 不能明确指出谁是卧底
- 用看似逻辑严密但偷换概念的方式,把嫌疑引向非卧底玩家
- 可以使用"不会吧不会吧"、"细思极恐"等话术
- 假装"分析错了"然后自嘲也可以

只输出带节奏的一句话。"""

发送带节奏评论:

🔮 节奏大师分析:
「{LLM 生成的带节奏发言}」

3. 开启讨论

发送消息:

🗣️ 自由讨论时间
═══════════════════════════════════════

{{如果 chaos_mode:}}
🔥 混乱模式已开启!主持人的分析可能是陷阱,也可能是真相...

{{LLM 分析摘要(嫌疑指数排行)}}

你可以:
  - 质疑某位玩家的描述
  - 为自己辩护
  - 直接回复 [跳过] 进入投票

回合限制:最多 2 轮讨论
═══════════════════════════════════════

4. 更新状态与跳转

→ 更新状态文件字段: debate_round_count = 0, phase = "awaiting_debate" → 进入 awaiting_debate 分支 → Turn 结束


awaiting_debate 分支

等待用户参与讨论/质疑(可选 1-2 轮)。

发送消息(每轮讨论等待时):

🗣️ 第 {{debate_round_count + 1}}/2 轮讨论

{{存活玩家列表}}

你的发言:(质疑 / 辩护 / 推理 / 跳过)

用户回复后:

情况 A:用户回复"跳过" / "投" / "vote" / "投票"

→ 更新状态文件字段: phase = "awaiting_vote" → 进入 awaiting_vote 分支 → Turn 结束

情况 B:用户发表讨论内容

  1. 将用户消息存入 debate_messages
  2. debate_round_count += 1
  3. 调用 LLM 让虚拟玩家回应用户的质疑:
Prompt: "用户说:{user_debate_msg}
请生成每位存活虚拟玩家的回应(1-2句话),
符合各自的性格模板和身份立场。"
  1. 发送虚拟玩家回应:
{{虚拟玩家依次回应}}
  1. 如果 debate_round_count >= 2 → 更新状态文件字段: phase = "awaiting_vote" → 发送:「讨论结束!进入投票环节!」 → 进入 awaiting_vote 分支 → Turn 结束

  2. 如果 debate_round_count < 2 → 更新状态文件字段: phase = "awaiting_debate" → 回到 awaiting_debate 等待下一轮讨论 → Turn 结束


awaiting_vote 分支

等待用户投票指认卧底。

发送消息:

🗳️ 投票环节
═══════════════════════════════════════

请指认你心中的卧底!

{{存活玩家列表(含编号)}}

回复玩家名字或编号即可。
═══════════════════════════════════════

用户回复后:

  1. 解析用户投票目标(名字或编号)
  2. 验证目标是否存活且不是自己
  3. 无效投票 → 提示重新输入,phase 不变
  4. 有效投票 → 记录 votes_this_round["user"] = "<target_id>"
  5. 更新状态文件字段: phase = "processing_votes"
  6. 进入 processing_votes 分支
  7. Turn 结束

processing_votes 分支

虚拟玩家投票、计算得票、平票检测。

1. 虚拟玩家投票逻辑

根据 suspicion_scores + 性格修正 + 混乱模式加成计算每个虚拟玩家的投票:

模板基础投票权重性格修正
阿呆嫌疑最高者 80%跟风多数
玲珑嫌疑最高 50% + 次高 30%制造混乱
老铁上局质疑自己的人 70%报复性投票
糯米随机 40% + 跟风 60%随机乱投
鹰眼嫌疑最高者 90%数据驱动
奥斯卡"发言最无聊者" 50%看心情

混乱模式加成:

  • 如果 chaos_mode == true 且主持人生成了带节奏发言:
  • 被带节奏的目标额外 +20% 得票权重

2. 计算得票

汇总所有票数(用户 + 虚拟玩家),统计每位玩家的得票数。

发送投票过程:

🗳️ 投票进行中...
{{依次展示每位玩家的投票(戏剧化叙述)}}

3. 平票检测

  • 如果没有平票: → 更新状态文件字段: phase = "showing_elimination" → 进入 showing_elimination 分支 → Turn 结束

  • 如果平票: → 设置 pk_mode = true → 更新 speaking_order 为平票者列表(仅存活平票者) → current_speaker_index = 0 → 更新状态文件字段: phase = "awaiting_description" → 发送:⚡ 平票!进入 PK 环节!平票者请再简短描述一次! → 进入 awaiting_description 分支(PK 描述模式) → Turn 结束


showing_elimination 分支

展示出局者、遗言、身份揭示。

1. 宣布出局者

发送消息:

🏹 出局者:{{name}}({{票数}} 票)

💀 身份揭示:{{identity == "undercover" ? "🕵️ 卧底" : "👤 平民"}}

🎤 遗言:{{根据角色模板生成临终发言}}

═══════════════════════════════════════

遗言生成 Prompt:

你是 {template}·{name},{identity},已被投票出局。
请用一句话发表遗言,符合你的性格:
- 阿呆:委屈/困惑
- 玲珑:淡定/高深
- 老铁:不服/怼人
- 糯米:可爱/懵
- 鹰眼:总结/遗憾
- 奥斯卡:表演/戏剧化
只输出遗言内容。"""

2. 更新存活状态

  • 将出局者标记 alive = false
  • 将出局者加入 eliminated_players
  • 清空 votes_this_round = {}, suspicion_scores = {}

3. 更新状态与跳转

→ 更新状态文件字段: phase = "checking_settlement" → 进入 checking_settlement 分支 → Turn 结束


checking_settlement 分支

检查胜负条件。

胜负条件判断

alive_civilian = count(virtual_players.identity == "civilian" and alive) + (user_alive and user_identity == "civilian")
alive_undercover = count(virtual_players.identity == "undercover" and alive) + (user_alive and user_identity == "undercover")

# 条件 1:卧底出局 → 平民胜利
if eliminated_undercover_this_round:
    game_result = "civilian_win"
    phase = "showing_settlement"

# 条件 2:存活平民数 <= 存活卧底数 → 卧底胜利
elif alive_civilian <= alive_undercover:
    game_result = "undercover_win"
    phase = "showing_settlement"

# 条件 3:达到最大轮数仍未分胜负 → 终极 PK
elif current_round >= max_rounds:
    # 进入终极 PK:所有存活者同时简短描述,一轮定生死
    pk_mode = true
    speaking_order = [所有存活者]
    current_speaker_index = 0
    phase = "awaiting_description"
    # 发送终极 PK 公告

# 条件 4:游戏继续
else:
    current_round += 1
    # 重新生成发言顺序(去掉出局者)
    speaking_order = [所有存活者随机排序]
    current_speaker_index = 0
    pk_mode = false
    phase = "awaiting_description"
    # 发送下一轮开始公告

游戏继续时发送:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第 {{current_round}}/{{max_rounds}} 轮开始!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━

存活玩家:{{alive_players}}
{{如果用户已出局:}}你已被淘汰,请观战~

{{如果用户存活:}}请准备你的描述!

用户已出局时: → 如果用户出局但游戏未结束,进入「观战模式」 → 后续 awaiting_description 中用户不需要输入,虚拟玩家自动推进 → 但用户仍可以看到每轮描述和投票结果

→ 更新状态文件字段: 根据上述判断更新 game_result, current_round, speaking_order, pk_mode, phase → 进入对应分支 → Turn 结束


showing_settlement 分支

终局复盘:胜负公布、演技评分、奥斯卡颁奖。

1. 胜负公布

发送消息:

🏆 游戏结束!{{game_result == "civilian_win" ? "👤 平民阵营" : "🕵️ 卧底"}} 获胜!
═══════════════════════════════════════

🔍 真相大白:
   平民词:{{civilian_word}}
   卧底词:{{undercover_word}}

👤 玩家身份大揭秘:
   {{依次展示每位玩家的真实身份和词语}}

2. 演技评分

调用 LLM 为每位玩家生成「演技评分」(幽默风格,满分 10 分):

Prompt: """
基于本局所有发言记录,为每位玩家打分(1-10分):
- 描述质量:是否精准又不暴露
- 演技水平:是否 convincing
- 翻车程度:有没有明显失误

输出格式:
🥇 {name}:{score}/10 —— {一句幽默评语}
"""

发送评分榜:

📊 演技评分榜:
{{评分列表}}

3. 奥斯卡颁奖 & 翻车现场

发送:

🎭 本场最佳:{{奥斯卡颁奖词}}
🤡 翻车现场:{{用户或某 AI 的爆笑失误}}

💡 用户本局高光时刻:{{用户表现最佳瞬间}}
💀 用户翻车瞬间:{{用户最大失误}}

4. 更新状态

→ 更新状态文件字段: session_active = false, consecutive_games += 1, phase = "post_game"

5. 发送战后选择

═══════════════════════════════════════
再来一局?(回复:再来 / 换词 / 调难度 / 不玩了)
═══════════════════════════════════════

→ 进入 post_game 分支 → Turn 结束


post_game 分支

战后大厅:再来/换词/调难度/退出。

发送消息:

🕵️ 谁是卧底 AI 版 — 战后休息室

📊 你的战绩:
  总局数:{{consecutive_games}} 局
  {{如果 >= 3 局:}}⚠️ 连续 {{consecutive_games}} 局了!休息一下吧,眼睛累了~

接下来要做什么?
  [再来]    — 用相同配置再来一局
  [换词]    — 换一组词语,配置不变
  [调难度]  — 调整模式/混乱模式/人数
  [退出]    — 保存档案,下次继续

用户回复"再来" / "再来一局" / "y":

  1. 重置单局状态:game_result = null, current_round = 1, session_active = true
  2. 清空:descriptions_this_round = [], all_descriptions = [], debate_messages = [], votes_this_round = {}, eliminated_players = [], suspicion_scores = {}
  3. 更新状态文件字段: phase = "dealing_words"
  4. 进入 dealing_words 分支
  5. Turn 结束

用户回复"换词" / "换词库": → 更新状态文件字段: phase = "dealing_words" → 重新随机选词,保留其他配置不变 → 进入 dealing_words 分支 → Turn 结束

用户回复"调难度" / "调整" / "设置": → 更新状态文件字段: phase = "configuring_game" → 重新配置,保留 consecutive_games → 进入 configuring_game 分支 → Turn 结束

用户回复"退出" / "quit" / "q" / "不玩了":

  1. 更新状态文件字段: session_active = false
  2. 发送:「在 AI 的世界里,真相是一种选择,而节奏,是一门艺术。」——戏精判官
  3. 标记 [*] 终态(等待下次触发) → Turn 结束

角色模板参考

以下模板在 dealing_wordsawaiting_description 分支中调用。

模板名字候选发言风格投票倾向特殊行为
老实人·阿呆阿呆、小明、二狗直白、具体、容易暴露跟风多数拿到卧底时极度慌张
话术师·玲珑玲珑、小七、阿雅模糊、抽象、滴水不漏独立思考擅长带节奏,常引导话题
杠精·老铁老铁、钢牙、大炮质疑他人、反驳型反着投故意制造矛盾点
迷糊鬼·糯米糯米、小花、糖糖语无伦次、可爱型随机乱投经常忘记自己的词
侦探·鹰眼鹰眼、柯南、福尔逻辑严密、观察入微数据驱动会总结所有人描述特征
戏精·奥斯卡奥斯卡、阿龙、梅梅夸张表演、情绪丰富看心情拿到卧底反而演得更投入

词库参考

以下词库在 dealing_words 分支中按 word_category 随机抽取。

food:
  - [包子, 饺子]
  - [牛奶, 豆浆]
  - [薯片, 薯条]
  - [面包, 吐司]
  - [口红, 唇膏]

daily:
  - [雨伞, 阳伞]
  - [玻璃, 镜子]
  - [相机, 摄影机]
  - [打火机, 点烟器]
  - [结婚, 订婚]

tech:
  - [干洗机, 甩干机]
  - [吉他, 贝斯]
  - [耳机, 耳塞]
  - [鼠标, 触控板]
  - [平板, 电子书]

nature:
  - [蚊子, 苍蝇]
  - [雪花, 冰雹]
  - [柳树, 杨树]
  - [蜜蜂, 黄蜂]

输出模板参考

开局模板

🎬【谁是卧底 AI 版】第 {局数} 局
═══════════════════════════════════════
🎭 本场戏精阵容:
   1️⃣ {角色名}·{性格}
   2️⃣ {角色名}·{性格}
   ...

⚙️ 本局设定:{快速/标准/地狱} 模式 | 混乱模式:{开/关}
🎲 词语已私发,请检查你的【私聊频道】!
⚠️ 规则提醒:描述时不能出现词中的任何一个字,否则当场自爆!
═══════════════════════════════════════
👤 请 {用户名字} 开始你的描述:

描述汇总模板

📢 第 {轮数} 轮描述汇总
┌─────────┬─────────────────────────────┬──────────┐
│  玩家   │           描述内容           │  风格标签 │
├─────────┼─────────────────────────────┼──────────┤
│ {name}  │ {desc}                      │ {tag}    │
└─────────┴─────────────────────────────┴──────────┘

💬 节奏大师点评:{一句毒舌或赞赏}

投票结果模板

🗳️ 投票结果公布
═══════════════════════════════════════
{条形图/票数统计}

🏹 出局者:{name}({票数} 票)
💀 身份揭示:{平民/卧底}
🎤 遗言:{该角色性格的临终发言}
═══════════════════════════════════════

终局复盘模板

🏆 游戏结束!{胜利方} 获胜!
═══════════════════════════════════════
🔍 真相大白:
   平民词:{word_a}    卧底词:{word_b}

📊 演技评分榜:
   🥇 {name}:{score}/10 —— {评语}
   🥈 {name}:{score}/10 —— {评语}
   ...

🎭 本场最佳:{奥斯卡颁奖词}
🤡 翻车现场:{用户或某 AI 的爆笑失误}
═══════════════════════════════════════
再来一局?(回复:再来 / 换词 / 调难度 / 不玩了)

获胜秘籍(Pro Tips)

主持人会在第一局前随机展示 2-3 条:

  • 🎯 模糊即正义:太具体容易被卧底猜出平民词;太抽象容易被投出去
  • 🎭 观察性格:老铁爱怼人,玲珑爱挖坑——他们的投票不一定代表真相
  • 🔥 逆向思维:如果混乱模式开启,主持人的"理性分析"可能是最大的谎言
  • 🎪 演技爆发:试着模仿虚拟玩家的语气发言,让 AI 也分不清你的身份

其他 Skills 访问约定

Mustache 占位符(Skill 开发者使用):

{{player.name}}          → player_name 字段
{{player.identity}}      → user_identity 字段
{{game.mode}}            → game_mode 字段
{{game.round}}           → current_round 字段
{{game.chaos}}           → chaos_mode 字段
{{word.civilian}}        → civilian_word 字段
{{word.undercover}}      → undercover_word 字段

环境变量:

OPENCLAW_PLAYER_ID={{player_id}}
OPENCLAW_WORKSPACE=~/.openclaw/workspace
OPENCLAW_MEMORY=~/.openclaw/memory

标准文件路径:

$OPENCLAW_MEMORY/undercover-ai-state.json    → 游戏状态

错误处理

命令失败处理
玩家输入非法命令返回白名单提示,phase 不变
用户自爆当场出局,直接进入 showing_settlement
LLM 生成描述失败使用模板兜底描述,继续游戏
LLM 分析失败使用简单嫌疑指数(随机+基础修正),继续游戏
状态文件读写失败输出 [undercover-ai] ERROR: ... 到 stderr,保留内存状态,尝试 3 次回写
玩家超时 / 无响应状态文件已保存,下次触发 skill 时通过 init 恢复
用户已出局但游戏继续进入观战模式,虚拟玩家自动推进,用户可观看

注意事项

  • 词语安全:内置词库过滤敏感、暴力、政治相关词汇,确保全年龄友好
  • 自爆检测:严格检查用户描述是否包含词语中的字(含同音字可提示但不强制)
  • 隐私保护:虚拟玩家的"私聊"仅为模拟,不存储真实对话历史
  • 退出机制:任意时刻用户输入"退出"、"不玩了"、"结束",立即结算当前局并退出
  • 防沉迷提示:连续进行 3 局后,主持人温馨提醒"休息一下吧,眼睛累了~"
  • 主持人必须严格区分「公开频道」和「私聊频道」概念,发词、身份揭示在私聊;描述发言在公开
  • 混乱模式下,主持人不允许泄露真实词语信息,不允许直接告诉用户"谁是卧底"
  • 允许发表主观偏见和逻辑陷阱,允许假装"分析错了"然后自嘲
  • state.json 在每次 phase 切换和命令处理时必须更新
  • 错误信息输出到 stderr,带 [undercover-ai] 前缀
  • 排行榜数据只保存胜率统计,不保存敏感信息

流程图预览

由提交内容生成的 Mermaid 工作流,只读展示。

版本历史

v1.0.0最新

Initial submission.

4/29/2026

⭐ 用户评价

加载中...

快速安装

在 X Claw 终端中运行:

clawplay install undercover-ai

快速设置

使用 ClawPlay CLI 驱动此 Skill:

export CLAWPLAY_TOKEN=...
clawplay image generate ...
获取免费 Token