做了一个LLM狼人杀项目

#default

使用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毛钱。

关于氛围编程

从当前的代码生成效果看,我的个人排名如下:

  1. Claude Code + Claude 4 Sonnet;
  2. Cursor + Claude 4 Sonnet;
  3. Augment + Claude 4 Sonnet;
  4. Kiro + Claude 4 Sonnet;
  5. Claude Code + Kimi K2。

Claude 4(只用过Sonnet,没💴没用过Opus)毫无疑问是当前的编程SOTA,其他方案均不推荐。特别批评Trae,明明用的也是Claude 4,但就是一坨。

虽然Cursor和Augment都有白嫖大法,但现阶段还是建议先白嫖Kiro。