做了一个LLM狼人杀项目
使用Claude Code + Kimi K2来自动生成这个项目。基本全程是在氛围编程,没有自己去修改代码。
项目介绍
10人狼人杀标准局,包括3狼人、4村民、1女巫、1猎人、1预言家。
所有的玩家都是LLM,能独立设定API URL、API Key、model。通过严格的提示词来规范LLM们的返回信息的格式,然后通过解析格式来获得他们所选择的内容。
详情见 WereWolf-LLM。
玩家状态
通过系统(主持人/上帝)实时更新所有玩家的状态,将玩家的生死状态作为上下文信息。
if context:
# Add speaking order context for day discussions
if "speaking_context" in context:
speaking = context["speaking_context"]
full_prompt += f"\n\n=== 发言顺序信息 ==="
full_prompt += f"\n- 你的发言顺序:第{speaking.get('my_position', 0)}位"
before_players = [f"{p['name']}({p['id']})" for p in speaking.get('players_before_me', [])]
after_players = [f"{p['name']}({p['id']})" for p in speaking.get('players_after_me', [])]
full_prompt += f"\n- 已发言玩家:{before_players or '无'}"
full_prompt += f"\n- 未发言玩家:{after_players or '无'}"
full_prompt += f"\n- 重要提醒:{speaking.get('strict_warning', '')}"
full_prompt += f"\n\n当前游戏状态:"
if "game_state" in context:
game_state = context["game_state"]
full_prompt += f"\n- 当前轮次:第{game_state.get('round', 0)}轮"
full_prompt += f"\n- 当前阶段:{game_state.get('phase', '未知')}"
full_prompt += f"\n- 存活的玩家:{game_state.get('alive_players', [])}"
full_prompt += f"\n- 死亡的玩家:{game_state.get('dead_players', [])}"
if "night_events" in context:
night_events = context["night_events"]
full_prompt += f"\n- 昨夜事件:{night_events}"
# Add strict speaking order rules for day phase
if context.get("game_state", {}).get("phase") == "day":
full_prompt += f"\n\n=== 发言规则提醒 ==="
full_prompt += f"\n⚠️ 严格规则:"
full_prompt += f"\n1. 只能分析已经发言的玩家"
full_prompt += f"\n2. 不能提及未发言玩家的观点或行为"
full_prompt += f"\n3. 使用'根据前面发言'、'从已发言玩家来看'等限定词"
full_prompt += f"\n4. 避免绝对判断,使用'可能'、'倾向于'等表述"
if "discussion" in context:
full_prompt += f"\n- 当前讨论:{context['discussion']}"
分类上下文
制定了预言家、女巫、狼人、白天等4套上下文系统。玩家状态会自动加入到所有上下文系统中。同时,白天上下文会自动加入到其他的3套上下文里。
def _werewolf_action(self, context: Dict[str, Any]) -> Dict[str, Any]:
"""Wolf team coordination - unified decision making"""
alive_players = context.get("alive_players", [])
wolf_team = context.get("wolf_team", [])
non_wolf_players = [p for p in alive_players if p not in wolf_team]
if not non_wolf_players:
return {}
# Get player names for display
player_names = {}
for pid in non_wolf_players:
player_info = context.get("game_state", {}).get("players", {}).get(pid)
if player_info:
player_names[pid] = player_info.get("name", f"玩家{pid}")
display_targets = [(pid, player_names.get(pid, f"玩家{pid}"))
for pid in non_wolf_players]
严格提示
为每个角色制定了精细的提示词,符合他们的行动人设。我觉得如果让LLM来分析真实人类的狼人杀,他们应该也可以从这微小的对话情绪里分析出身份。
# Add general strategic context
context_parts.append("=== 当前局面分析 ===")
context_parts.append("- 分析已发言玩家的逻辑一致性")
context_parts.append("- 观察是否有预言家跳出并报查杀")
context_parts.append("- 注意是否有玩家为被查杀者辩护")
context_parts.append("- 考虑发言动机:好人找狼 vs 狼人混淆")
if self.team.value == "villager":
context_parts.append("\n=== 好人阵营重要提醒 ===")
context_parts.append("- 如果预言家明确查杀且无对跳,这是最可靠的信息")
context_parts.append("- 优先投票给被查杀的玩家")
context_parts.append("- 警惕为被查杀玩家辩护的人,可能是狼队友")
else:
context_parts.append("\n=== 狼人阵营高级策略 ===")
context_parts.append("- **弃车保帅判断**:如果队友被预言家查杀且无法反驳,评估是否需要切割")
context_parts.append("- **票数对比分析**:计算狼队vs好人的票数,如果明显处于劣势则考虑放弃队友")
context_parts.append("- **暴露风险评估**:如果继续为队友辩护会暴露自己,果断投票给队友")
context_parts.append("- **团队利益优先**:保护未暴露的队友比救一个暴露的队友更重要")
context_parts.append("- **伪装好人思维**:投票给暴露队友时要表现出'正义'的好人逻辑")
context_parts.append("- **避免过度辩护**:适度质疑预言家可以,但不要成为唯一为队友说话的人")
怎么玩
拉取项目,然后自行修改
config/game_config_template.json
即可运行项目
python main.py config/game_config_template.json
请注意,我使用了10个DeepSeek-V3进行测试,游戏能正常推进,但未测试其他模型。理论上可以给10个玩家分配不同的模型来玩。
这是运行状态:
=== 狼人杀LLM游戏 ===
=== 狼人杀游戏开始 ===
游戏ID:20250717_163225
=== 玩家列表 ===
1. A - 女巫 - 有解药和毒药各一瓶
2. B - 狼人 - 每晚可以杀人
3. C - 村民 - 无特殊技能,通过推理找出狼人
4. D - 村民 - 无特殊技能,通过推理找出狼人
5. E - 狼人 - 每晚可以杀人
6. F - 预言家 - 每晚可以查验一名玩家身份
7. G - 狼人 - 每晚可以杀人
8. H - 村民 - 无特殊技能,通过推理找出狼人
9. I - 村民 - 无特殊技能,通过推理找出狼人
10. J - 猎人 - 死亡时可以开枪带走一名玩家
=== 第1轮 ===
=== 第1轮夜晚开始 ===
🔮 预言家 F(6) 开始行动...
🔮 预言家 F(6) 的查验决策:CHECK: 5
(选择查验E(5),因为作为中间位置的玩家,通常容易被忽视但可能是狼人隐藏的位置。作为预言家第一晚查验,选择中间位可以获取关键信息。)
✅ 预言家 F(6) 选择查验玩家 5
🔮 预言家 F(6) 查验了 E(5),结果是:狼人
🐺 狼人开始行动... (B, E, G)
🐺 狼人团队(B(2), E(5), G(7))开始投票...
可选击杀目标:A(1), C(3), D(4), F(6), H(8), I(9), J(10)
消耗
这个项目建立过程基本就是在反复调整LLM的提示词,在这反复拉扯下,消耗了差不多30元RMB的KIMI账户余额🤣。而且,运行差强人意,最后我用了Kiro来进一步升级代码。在Kiro升级后,整体没有问题。从经验看,我不认为Claude Code会比Kiro差,那么这个差距大概率是来源于Kimi K2和Claude 4。
后续我接着使用Kiro进行了大量的调整,主要是提示词的设计,要降低模型的幻觉。我感觉这些调整,如果不是能白嫖Kiro的话,没有500块搞不下来😀。
实际玩一次,消耗5万tokens左右,按DeepSeek-V3的计费来说,大概是2毛钱。
关于氛围编程
从当前的代码生成效果看,我的个人排名如下:
- Claude Code + Claude 4 Sonnet;
- Cursor + Claude 4 Sonnet;
- Augment + Claude 4 Sonnet;
- Kiro + Claude 4 Sonnet;
- Claude Code + Kimi K2。
Claude 4(只用过Sonnet,没💴没用过Opus)毫无疑问是当前的编程SOTA,其他方案均不推荐。特别批评Trae,明明用的也是Claude 4,但就是一坨。
虽然Cursor和Augment都有白嫖大法,但现阶段还是建议先白嫖Kiro。