undercover-ai
作者: Jain Yu•v1.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 == false且game_result != null→ 游戏已结束,进入post_gamepk_mode == true→ 当前处于平票 PK 环节current_round > max_rounds且game_result == null→ 轮数超限,进入终极 PK
状态流转图参考:references/workflow.md
init 分支
每次进入 Skill 时,按以下顺序判断当前状态:
① 状态文件存在且 session_active == true
→ 读取状态文件,取 phase 和所有字段
→ 发送:🕵️ 找到未完成的卧底局了!当前第 {{current_round}}/{{max_rounds}} 轮,剩余玩家:{{alive_count}}
→ 直接进入对应 phase 分支
→ Turn 结束
② 状态文件存在但 session_active == false 或 game_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" / "谁是卧底":
- 更新状态文件字段: phase = "configuring_game"
- 进入
configuring_game分支 - Turn 结束
用户输入"退出" / "quit" / "q" / "不玩了":
- 发送:
「哼,连入局都不敢?回去写你的 PPT 吧。」——戏精判官 - 更新状态文件字段: session_active = false, game_result = "quit"
- 进入
post_game分支 - Turn 结束
configuring_game 分支
配置本局游戏参数:模式、词库、混乱模式、虚拟玩家数。
发送消息:
⚙️ 本局设定
═══════════════════════════════════════
请回复选项编号(或回复「默认」使用推荐配置):
【游戏节奏】
1. 快速模式 — 2 轮定胜负(10 分钟)
2. 标准模式 — 3 轮定胜负(15 分钟)⭐推荐
3. 地狱模式 — 4+ 轮,卧底优势(20 分钟)
【词语类别】
1. 随机混搭 2. 美食 3. 日常 4. 科技 5. 自然
【混乱模式(AI 带节奏)】
开 / 关
【虚拟玩家数】
3 人 / 4 人 / 5 人
示例回复:「2 1 关 4」或「默认」
═══════════════════════════════════════
用户回复配置后:
- 解析用户输入:
- 模式:
fast(2轮) /standard(3轮) /hell(4轮) - 词库:
random/food/daily/tech/nature - 混乱模式:
true/false - 玩家数:3-5(含用户)
- 模式:
- 设置默认值:如果用户回复「默认」→ standard + random + false + 4
- 更新状态文件字段:
game_mode = "<mode>",max_rounds = <N>word_category = "<category>"chaos_mode = <bool>player_count = <count>phase = "dealing_words"
- 进入
dealing_words分支 - 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:当前发言者是虚拟玩家
- 取该虚拟玩家的
template+identity+current_round - 调用 LLM 生成一句话描述:
Prompt: "你是{template}·{name},拿到的是{identity == 'undercover' ? undercover_word : civilian_word}。
请用一句话描述这个词语,要求:
- 不能出现词语中的任何一个字(这是卧底游戏,暴露就输了)
- 平民:尽量准确描述,但不要让卧底轻易猜出原词
- 卧底:试图用模糊语言伪装成平民,描述偏向卧底词但不露馅
- 性格修正:{template 对应的描述风格}
只输出一句话描述,不要解释。"
模板风格修正:
- 阿呆:过于具体,容易暴露细节
- 玲珑:极其抽象,滴水不漏
- 老铁:质疑上一家的描述,然后给出自己的
- 糯米:语无伦次,可爱跑题
- 鹰眼:逻辑严密,试图总结规律
- 奥斯卡:夸张表演,情绪丰富
- 自爆检测:检查生成的描述是否包含
civilian_word或undercover_word中的任意字符。如果包含 → 该虚拟玩家自爆出局,直接标记alive = false,identity公开,发送自爆消息,然后继续处理下一位。 - 将描述存入
descriptions_this_round(记录:玩家名、描述内容、风格标签) current_speaker_index += 1- 发送该虚拟玩家的描述给用户:
🎤 {name}·{template}:「{描述}」
💬 风格标签:{tag}
- 如果
current_speaker_index < len(speaking_order)→ 继续回到本分支开头处理下一位 - 如果一轮结束 → 更新状态文件字段:
phase = "processing_descriptions" - 进入
processing_descriptions分支 - Turn 结束
情况 B:当前发言者是用户
发送消息:
👤 请 {{player_name}} 描述你的词语:
💡 提示:一句话描述,不能出现词中的字!
太具体容易被猜出,太抽象容易被投出去~
你的描述:
用户回复后:
- 检查用户描述中是否包含
civilian_word或undercover_word中的任意字符 - 如果包含(自爆):
- 发送:
💥 自爆!你的描述中出现了词语中的字! - 更新状态文件字段:
user_identity公开,game_result = "self_explosion",session_active = false - 进入
showing_settlement分支(卧底胜利,用户出局) - Turn 结束
- 发送:
- 如果没有自爆:
- 将用户描述存入
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:用户发表讨论内容
- 将用户消息存入
debate_messages debate_round_count += 1- 调用 LLM 让虚拟玩家回应用户的质疑:
Prompt: "用户说:{user_debate_msg}
请生成每位存活虚拟玩家的回应(1-2句话),
符合各自的性格模板和身份立场。"
- 发送虚拟玩家回应:
{{虚拟玩家依次回应}}
-
如果
debate_round_count >= 2: → 更新状态文件字段:phase = "awaiting_vote"→ 发送:「讨论结束!进入投票环节!」→ 进入awaiting_vote分支 → Turn 结束 -
如果
debate_round_count < 2: → 更新状态文件字段:phase = "awaiting_debate"→ 回到awaiting_debate等待下一轮讨论 → Turn 结束
awaiting_vote 分支
等待用户投票指认卧底。
发送消息:
🗳️ 投票环节
═══════════════════════════════════════
请指认你心中的卧底!
{{存活玩家列表(含编号)}}
回复玩家名字或编号即可。
═══════════════════════════════════════
用户回复后:
- 解析用户投票目标(名字或编号)
- 验证目标是否存活且不是自己
- 无效投票 → 提示重新输入,phase 不变
- 有效投票 → 记录
votes_this_round["user"] = "<target_id>" - 更新状态文件字段:
phase = "processing_votes" - 进入
processing_votes分支 - 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":
- 重置单局状态:
game_result = null,current_round = 1,session_active = true - 清空:
descriptions_this_round = [],all_descriptions = [],debate_messages = [],votes_this_round = {},eliminated_players = [],suspicion_scores = {} - 更新状态文件字段:
phase = "dealing_words" - 进入
dealing_words分支 - Turn 结束
用户回复"换词" / "换词库":
→ 更新状态文件字段: phase = "dealing_words"
→ 重新随机选词,保留其他配置不变
→ 进入 dealing_words 分支
→ Turn 结束
用户回复"调难度" / "调整" / "设置":
→ 更新状态文件字段: phase = "configuring_game"
→ 重新配置,保留 consecutive_games
→ 进入 configuring_game 分支
→ Turn 结束
用户回复"退出" / "quit" / "q" / "不玩了":
- 更新状态文件字段:
session_active = false - 发送:
「在 AI 的世界里,真相是一种选择,而节奏,是一门艺术。」——戏精判官 - 标记
[*]终态(等待下次触发) → Turn 结束
角色模板参考
以下模板在 dealing_words 和 awaiting_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 工作流,只读展示。
⭐ 用户评价
加载中...
快速安装
在 X Claw 终端中运行:
clawplay install undercover-ai