AI 狼人杀 · Portfolio Case Study
项目背景
做这个项目的起点很直接:我想找一个场景,能把 agentic systems 里最难讲清楚的几个概念——multi-step planning、tool use、failure modes、model routing——全部用一个可交互的 demo 呈现出来,而不是停留在架构图上。
狼人杀是个天然的选择。它本身就是一个信息不对称的博弈游戏:每个玩家只知道自己该知道的,需要推理其他人的行为,还要在多个回合里动态调整策略。这跟 agentic system 设计里的核心挑战几乎是同构的——不同 agent 的信息隔离、跨步骤的状态管理、不确定环境下的决策。
另一个考量是叙事的差异化。作品集前几个 demo 都锚定在游戏行业场景,这个项目刻意选了一个「用游戏化场景展示通用 AI 能力」的角度,目的是证明技能可迁移。面试时「你做了一个 AI 狼人杀?」这句话本身就是话题的开场白,比「我做了一个 workflow agent」更容易让对话展开。
方案设计
核心设计决策是选择 AI vs AI 纯观赏模式,而不是人机混合。原因是人机混合需要处理用户输入等待、超时、合法性校验等一堆额外复杂度,而这些对展示 agentic 能力没有增量价值。AI vs AI 反而更干净:面试官打开页面点「开始游戏」,完整的 multi-agent 博弈过程自动跑完,同时能看到每个 agent 的推理过程——这才是上帝视角真正想展示的东西。
游戏规模定在 6 人局(2 狼人 + 1 预言家 + 1 女巫 + 2 村民),这是最小完备的配置:角色种类够丰富,能展示不同的 agent 策略差异;局面复杂度可控,不会让 demo 跑太久。
前端设计围绕「上帝视角」展开,分三层信息:玩家卡片(快速状态概览)、右侧思维链面板(完整推理文字)、Agent 行为 tab(工具调用完整轨迹)。每个 agent 的卡片上直接显示 temperature 和策略标签,把 model routing 的 PM 设计决策变成可见的东西,而不是藏在代码里。
技术架构
后端用 FastAPI + WebSocket,通过 WebSocket 实时推送每一步游戏事件到前端,消息结构包含事件类型、调用的工具、参数、返回值、当前置信度。游戏状态完全在内存里维护,单局不需要持久化。
Agent 层的核心挑战是信息隔离:每个 agent 调用 LLM 时,context 里只包含该角色应该知道的信息。狼人知道队友是谁,但不知道预言家查了谁;预言家知道查验结果,但不知道女巫是否用了药。这个隔离完全通过构建每个 agent 独立的 context 来实现,没有共享 state 直接读取。
工具层把每个角色技能注册为独立的 tool:check_identity()、speak()、vote()、use_potion()、kill()、update_belief()。每次工具调用都会记录完整的 params 和 result,这是 Agent 行为 tab 里工具调用轨迹的数据来源。
Model routing 通过差异化的 temperature 和 system prompt 策略实现:狼人用 temperature=0.9 配合伪装型 prompt,需要创意性的话术生成;预言家用 temperature=0.3,决策需要尽可能确定性;女巫用 temperature=0.6,处于二者之间,因为它的决策空间是有限状态的(解药/毒药各只能用一次)。模型统一走 DashScope / Qwen。
核心功能
上帝视角观战界面
用户可以同时看到所有 6 个 agent 的实时状态:当前推理内容、置信度、行动阶段。这是这个 demo 最核心的体验——信息不对称的博弈过程被完全透明化,狼人以为预言家是 3 号而实际是另一人的「信息差」直接可视化呈现。
工具调用完整轨迹(Agent 行为 tab)
独立 tab 记录所有工具调用的 function name、params、result、时间戳,可折叠展开。这是 tool use 能力最直接的呈现方式——不是描述「agent 用了工具」,而是让面试官看到 check_identity(target="玩家一") → {"role": "werewolf"},以及 agent 据此如何更新推理。
置信度追踪与低置信警告
每个 agent 的每次判断都带置信度分数,跌破 50% 阈值时卡片出现红色边框和警告标志,同时在日志里记录「低置信触发重推理」事件。这让 agent 的不确定性状态从隐性变成显性,是面试时讲「AI 系统如何处理不确定性」的具体抓手。
昼夜视觉切换
游戏在夜晚(深色调)和白天(浅色调)之间切换,不仅是视觉装饰,也是游戏阶段的明确信号——夜晚是技能行动阶段,白天是公开发言和投票阶段,两个阶段的信息结构和 agent 行为模式完全不同。
Failure Modes 与兜底设计
这是整个项目设计里花时间最多的部分。一个 agentic system 能跑起来不难,难的是它在各种边界情况下的行为是不是可预期的。我在设计阶段就枚举了 7 种 failure mode,每种都有明确的触发条件和 fallback 策略。
信息泄露检测是最有意思的一个。狼人 agent 在生成发言时,可能会在措辞里带出只有狼人才知道的信息(比如提到「我们」或「队友」)。解决方案是在 speak() 工具里加一层输出过滤:发言发布前先扫描私有词汇列表,触发则清空 draft 并注入约束 prompt 重试,最多 3 次。这个拦截事件在 Agent 行为 tab 里完整可见。
低置信触发重推理解决的是 agent「将错就错」的问题。如果一个 agent 对某个判断的置信度持续下降,不应该继续沿着原有推理链走下去。系统设定 50% 为阈值,跌破时自动清空当前目标假设,让 agent 重新扫描存活玩家、从头推理。这个机制让 agent 有「认错」的能力,而不是一根筋到底。
投票死锁的处理是游戏逻辑层面的 failure mode。平票时系统进入决选发言轮,每人额外发言一次,之后再投;如果仍然平票,跳过本轮放逐。这个设计的关键是「游戏不能卡死」——宁可放弃一次放逐机会,也要保证状态机能继续推进。
狼人协商失败也是需要显式处理的场景。两只狼人 agent 独立推理,可能选择不同的击杀目标。解决方案是夜晚阶段引入两轮协商:第一轮各自提出目标,第二轮确认或妥协;仍然不一致时随机选其一。这个协商过程在 Agent 行为 tab 里显示为两次 negotiate() 调用。
其余 failure mode(推理链断裂、发言跑题/幻觉、模型超时)都有对应的 fallback:状态校验拒绝非法操作、重试机制 + 裁判占位文本兜底、30 秒超时后执行随机合法操作。共同原则是:系统的降级行为需要是可预期的,不能因为一个 agent 出问题让整局游戏卡住。