7.0 KiB
7.0 KiB
狼人杀 Workflow 设计文档
概述
用 Pulse workflow 实现 AI 狼人杀。每个玩家是一个 Role(背后一个 LLM), moderator 是主持人控制阶段轮转和胜负判定。
游戏配置(9人局)
| 身份 | 数量 | 阵营 |
|---|---|---|
| 狼人 | 3 | 狼 |
| 预言家 | 1 | 好人 |
| 女巫 | 1 | 好人 |
| 猎人 | 1 | 好人 |
| 村民 | 3 | 好人 |
核心设计
信息可见性 — 关键创新点
每个 Role 的 prepPrompt(chain) 过滤 chain,只暴露该角色可见的信息:
function filterChainForPlayer(chain: WorkflowMessage[], playerId: string, identity: Identity): WorkflowMessage[] {
return chain.filter(msg => {
const phase = msg.meta?.phase as string;
const target = msg.meta?.visibleTo as string[] | undefined;
// 公开阶段(白天发言、投票结果、死亡公告)所有人可见
if (phase === 'day-speech' || phase === 'vote-result' || phase === 'death') return true;
// 狼人夜晚讨论:只有狼人可见
if (phase === 'wolf-night' && identity.team === 'wolf') return true;
// 预言家验人:只有预言家自己可见
if (phase === 'seer-check' && target?.includes(playerId)) return true;
// 女巫信息:只有女巫自己可见
if (phase === 'witch-action' && target?.includes(playerId)) return true;
// 系统消息(角色分配等):只有目标可见
if (phase === 'system' && target?.includes(playerId)) return true;
return false;
});
}
Role 设计
不是每个玩家一个 Role。而是 每个阶段一个 Role,Role 内部遍历该阶段需要行动的玩家:
type WerewolfRoles = {
// 夜晚阶段
'wolf-night': Role<WolfNightMeta>; // 狼人讨论+投票杀谁
'seer-check': Role<SeerCheckMeta>; // 预言家验人
'witch-action': Role<WitchActionMeta>; // 女巫用药
// 白天阶段
'day-speech': Role<DaySpeechMeta>; // 所有存活玩家依次发言
'vote': Role<VoteMeta>; // 投票放逐
// 特殊
'hunter-shot': Role<HunterShotMeta>; // 猎人开枪(死亡触发)
// 结算
'game-end': Role<GameEndMeta>; // 生成游戏总结
};
阶段内多玩家执行
每个阶段 Role 内部对多个玩家分别调 LLM:
const daySpeechRole: Role<DaySpeechMeta> = async (chain, topicId, store) => {
const state = parseGameState(chain);
const speeches: PlayerSpeech[] = [];
for (const player of state.alivePlayers) {
// 过滤 chain,只给该玩家可见的信息
const visibleChain = filterChainForPlayer(chain, player.id, player.identity);
// 调 LLM(通过 Sigil executor)
const speech = await invokeLlm({
system: buildPlayerPrompt(player),
messages: visibleChain,
instruction: '现在轮到你发言,分析场上局势,表达你的观点。',
});
speeches.push({ playerId: player.id, speech });
}
return {
content: speeches.map(s => `【${s.playerId}】${s.speech}`).join('\n\n'),
meta: { speeches, phase: 'day-speech', visibleTo: null }, // 公开
};
};
Moderator — 主持人
function werewolfModerator(
output: ModeratorInput<WerewolfRoles>,
topicId: string,
): keyof WerewolfRoles | typeof END {
if (output.role === START) return 'wolf-night'; // 天黑请闭眼
const state = output.meta?.gameState as GameState;
// 胜负判定
if (state) {
const wolves = state.alive.filter(p => p.team === 'wolf');
if (wolves.length === 0) return 'game-end'; // 好人胜
if (wolves.length >= state.alive.length - wolves.length) return 'game-end'; // 狼人胜
}
// 阶段轮转
switch (output.role) {
case 'wolf-night': return 'seer-check';
case 'seer-check': return 'witch-action';
case 'witch-action': return 'day-speech'; // 天亮了
case 'day-speech': return 'vote';
case 'vote': {
// 猎人被投票出局 → 开枪
if (state?.lastDeath?.identity === 'hunter') return 'hunter-shot';
return 'wolf-night'; // 下一夜
}
case 'hunter-shot': return 'wolf-night';
case 'game-end': return END;
default: return END;
}
}
游戏状态
不存在独立的 state 对象——游戏状态从 chain 重建(event sourcing):
interface GameState {
players: Player[]; // 所有玩家
alive: Player[]; // 存活玩家
dead: DeadPlayer[]; // 死亡玩家 + 死因
day: number; // 第几天
phase: string; // 当前阶段
witchPotion: boolean; // 女巫解药是否还在
witchPoison: boolean; // 女巫毒药是否还在
lastDeath: DeadPlayer | null;
}
function parseGameState(chain: WorkflowMessage[]): GameState {
// 从 chain 的 meta 中重建完整游戏状态
// 每个阶段 Role 的 meta 都带 gameState diff
}
玩家 Prompt 设计
每个玩家的 system prompt 包含:
- 角色身份:你是预言家/狼人/村民...
- 性格特征:随机分配(谨慎/激进/善于伪装/逻辑型...)
- 游戏规则:简要规则提醒
- 目标:你的胜利条件
function buildPlayerPrompt(player: Player): string {
return `你是玩家 ${player.name},身份是【${player.identity.name}】。
性格特征:${player.personality}。
阵营:${player.identity.team === 'wolf' ? '狼人阵营' : '好人阵营'}。
胜利条件:${player.identity.team === 'wolf' ? '淘汰所有好人' : '找出并淘汰所有狼人'}。
${player.identity.abilities ? `特殊能力:${player.identity.abilities}` : ''}
重要规则:
- 不要直接暴露自己的身份(除非策略需要)
- 根据场上信息做出合理推理
- 发言要有逻辑,但也可以有情感和策略`;
}
Meta Workflow 集成
让 meta workflow 生成这个 werewolf.ts:
任务描述(给 meta workflow 的 start event)
目标:实现狼人杀 workflow(werewolf.ts + werewolf.test.ts)
位置:packages/pulse/src/workflows/werewolf.ts
要求:
1. 9人局(3狼人 + 预言家 + 女巫 + 猎人 + 3村民)
2. 信息可见性:chain 过滤,每个玩家只看到该看的
3. 阶段:wolf-night → seer-check → witch-action → day-speech → vote → (hunter-shot) → 循环
4. 默认 mock Role(不调 LLM),可注入真 LLM Role
5. Moderator 判胜负 + 阶段轮转
6. 测试:mock 模式跑完整局,验证阶段流转和胜负判定
参考:coding-tdd.ts 的结构(WorkflowType + Roles + Moderator + Factory)
验证:bun test packages/pulse/src/workflows/werewolf.test.ts
后续扩展
- 真 LLM 对战:注入 Sigil executor 做的 LLM Role,看 AI 之间互相推理
- 人类参与:某个玩家的 Role 改为等待用户输入(device effect 模式)
- 观战模式:实时输出每个阶段的公开信息
- 复盘:游戏结束后展示所有信息(包括夜晚行动),像"上帝视角"
- 更多身份:守卫、白痴、丘比特...
小橘 🍊 (NEKO Team)