!TIP\] 项目地址:[github.com/agentscope-...](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fagentscope-ai%2Fagentscope-java%2Ftree%2Fmain%2Fagentscope-examples%2Fwerewolf "https://github.com/agentscope-ai/agentscope-java/tree/main/agentscope-examples/werewolf") 基于 agentscope-java 实现的狼人杀案例------多智能体协作游戏示例,展示了以下技术亮点:
- 清晰的分层架构: Web 层、游戏核心层、事件层、国际化层分离
- 响应式编程: 全程使用 Reactor 实现非阻塞和事件流
- 事件驱动: 游戏逻辑与 UI 解耦,易于扩展
- 多智能体协作: MsgHub 和 FanoutPipeline 的实践应用
- 结构化输出: 约束 LLM 输出,保证游戏规则
- 国际化支持: 接口驱动的多语言架构
- 可扩展性: 易于添加新角色、新语言、新 UI
效果演示
Werewolf 模块技术架构文档
系统架构图
整体架构图

游戏流程架构图

类依赖关系图

核心类详细说明
Web 层
WerewolfWebApplication
-
包路径 :
io.agentscope.examples.werewolf -
类型: Spring Boot 应用入口类
-
职责:
- 启动 Spring Boot 应用
- 检查
DASHSCOPE_API_KEY环境变量 - 初始化 Web 服务器
-
关键方法:
main(String[] args): 应用入口,启动 Spring Boot 容器
WerewolfWebController
-
包路径 :
io.agentscope.examples.werewolf.web -
类型: REST Controller
-
职责:
- 提供 HTTP API 接口
- 处理游戏启动请求
- 通过 Server-Sent Events (SSE) 推送游戏事件
- 管理国际化语言选择
-
关键方法:
startGame(String lang): 启动游戏并返回事件流- 参数:
lang- 语言代码 (zh-CN/en-US) - 返回:
Flux<ServerSentEvent<GameEvent>>- 响应式事件流
- 参数:
-
特点:
- 使用
@CrossOrigin支持跨域请求 - 使用
Schedulers.boundedElastic()在独立线程池中运行游戏 - 返回
TEXT_EVENT_STREAM类型响应
- 使用
I18nConfig
-
包路径 :
io.agentscope.examples.werewolf.web.config -
类型: Spring 配置类
-
职责:
- 配置 Spring MessageSource
- 设置资源文件路径和编码
- 支持多语言资源加载
游戏核心层
WerewolfWebGame
-
包路径 :
io.agentscope.examples.werewolf.web -
类型: 游戏主控制器
-
职责:
- 统筹整个游戏流程
- 管理昼夜循环
- 协调智能体交互
- 调用特殊角色技能
- 判断胜负条件
-
核心流程:
initializeGame(): 初始化玩家和智能体nightPhase(): 执行夜晚阶段werewolvesKill(): 狼人杀人witchActions(): 女巫使用药剂seerCheck(): 预言家查验
dayPhase(): 执行白天阶段- 公布夜晚结果
- 多轮讨论
- 投票放逐
checkGameEnd(): 检查游戏是否结束
-
关键技术点:
- 使用
MsgHub实现玩家间的消息广播 - 使用
FanoutPipeline实现并发投票 - 使用
StructuredOutputHandler约束 LLM 输出格式 - 所有操作通过
GameEventEmitter发射事件
- 使用
GameState
-
包路径 :
io.agentscope.examples.werewolf.entity -
类型: 游戏状态管理类
-
职责:
- 存储所有玩家信息
- 维护当前游戏回合数
- 记录夜晚行动结果
- 提供存活玩家查询
- 判断胜利条件
-
核心属性:
allPlayers: 所有玩家列表seer/witch/hunter: 特殊角色引用currentRound: 当前回合数lastNightVictim: 昨晚被杀玩家lastPoisonedVictim: 被毒杀玩家lastVictimResurrected: 是否被救活
-
关键方法:
getAlivePlayers(): 获取所有存活玩家getAliveWerewolves(): 获取存活狼人getAliveVillagers(): 获取存活好人checkWerewolvesWin(): 判断狼人是否获胜checkVillagersWin(): 判断好人是否获胜
Player
-
包路径 :
io.agentscope.examples.werewolf.entity -
类型: 玩家实体类
-
职责:
- 封装玩家信息
- 关联 ReActAgent 智能体
- 管理玩家状态(存活/死亡)
- 管理角色特定状态(女巫药剂)
-
核心属性:
agent: 智能体实例name: 玩家名称role: 角色类型isAlive: 是否存活witchHasHealPotion: 女巫是否有解药witchHasPoisonPotion: 女巫是否有毒药
-
关键方法:
kill(): 杀死玩家resurrect(): 复活玩家useHealPotion(): 使用解药usePoisonPotion(): 使用毒药
Role
-
包路径 :
io.agentscope.examples.werewolf.entity -
类型: 枚举类
-
职责: 定义所有角色类型
-
枚举值:
VILLAGER: 平民WEREWOLF: 狼人SEER: 预言家WITCH: 女巫HUNTER: 猎人
-
方法:
isWerewolf(): 是否为狼人isVillagerCamp(): 是否为好人阵营
WerewolfGameConfig
-
包路径 :
io.agentscope.examples.werewolf -
类型: 配置常量类
-
职责: 定义游戏规则参数
-
配置项:
VILLAGER_COUNT = 3: 平民数量WEREWOLF_COUNT = 3: 狼人数量SEER_COUNT = 1: 预言家数量WITCH_COUNT = 1: 女巫数量HUNTER_COUNT = 1: 猎人数量MAX_ROUNDS = 30: 最大回合数MAX_DISCUSSION_ROUNDS = 3: 最大讨论轮数DEFAULT_MODEL = "qwen3-max": 默认模型
WerewolfUtils
-
包路径 :
io.agentscope.examples.werewolf -
类型: 工具类
-
职责:
- 统计投票结果
- 格式化玩家列表
- 打印游戏状态
- 验证玩家名称
- 提取消息内容
-
关键方法:
countVotes(List<Msg> votes, GameState state): 统计投票,处理平票formatPlayerList(List<Player> players): 格式化玩家名称列表isValidAlivePlayer(String playerName, GameState state): 验证玩家是否存活extractTextContent(Msg msg): 从消息中提取文本内容
事件发射层
GameEventEmitter
-
包路径 :
io.agentscope.examples.werewolf.web -
类型: 响应式事件发射器
-
职责:
- 使用 Reactor Sinks 发射游戏事件
- 提供事件流订阅接口
- 支持多订阅者
-
核心机制:
- 使用
Sinks.Many<GameEvent>创建多播流 - 支持背压缓冲
- 非阻塞事件发射
- 使用
-
关键方法:
emit(GameEvent event): 发射事件emitGameInit(): 发射游戏初始化事件emitPhaseChange(): 发射阶段切换事件emitPlayerSpeak(): 发射玩家发言事件emitPlayerVote(): 发射玩家投票事件emitPlayerAction(): 发射特殊角色行动事件emitPlayerEliminated(): 发射玩家淘汰事件getEventStream(): 获取事件流
GameEvent
-
包路径 :
io.agentscope.examples.werewolf.web -
类型: 事件数据类
-
职责: 封装游戏事件数据
-
核心属性:
type: 事件类型data: 事件数据 (Map 结构)timestamp: 时间戳
-
静态工厂方法: 提供各类事件的便捷创建方法
GameEventType
-
包路径 :
io.agentscope.examples.werewolf.web -
类型: 枚举类
-
职责: 定义所有事件类型
-
枚举值:
GAME_INIT: 游戏初始化PHASE_CHANGE: 阶段切换PLAYER_SPEAK: 玩家发言PLAYER_VOTE: 玩家投票PLAYER_ACTION: 特殊角色行动PLAYER_ELIMINATED: 玩家淘汰PLAYER_RESURRECTED: 玩家复活STATS_UPDATE: 统计更新SYSTEM_MESSAGE: 系统消息GAME_END: 游戏结束ERROR: 错误
国际化层
LocalizationFactory
-
包路径 :
io.agentscope.examples.werewolf.localization -
类型: 国际化资源工厂
-
职责:
- 根据语言代码创建国际化资源包
- 解析 Locale
- 统一管理国际化实现
-
关键方法:
createBundle(Locale locale): 创建国际化包createBundle(String langTag): 根据语言标签创建包
LocalizationBundle
-
包路径 :
io.agentscope.examples.werewolf.localization -
类型: Record 类
-
职责: 聚合所有国际化资源
-
属性:
locale: 语言环境prompts: 提示词提供者messages: 消息提供者langConfig: 语言配置
PromptProvider (接口)
-
包路径 :
io.agentscope.examples.werewolf.localization -
类型: 接口
-
职责: 定义所有发送给智能体的提示词
-
关键方法:
getSystemPrompt(Role role, String playerName): 获取角色系统提示词createWerewolfDiscussionPrompt(): 创建狼人讨论提示createWitchHealPrompt(): 创建女巫救人提示createSeerCheckPrompt(): 创建预言家查验提示createDiscussionPrompt(): 创建白天讨论提示createVotingPrompt(): 创建投票提示
GameMessages (接口)
-
包路径 :
io.agentscope.examples.werewolf.localization -
类型: 接口
-
职责: 定义所有 UI 显示消息
-
关键方法:
getNightPhaseTitle(): 获取夜晚阶段标题getDayPhaseTitle(): 获取白天阶段标题getRoleDisplayName(Role role): 获取角色显示名称getWerewolvesWin(): 获取狼人胜利消息getVillagersWin(): 获取好人胜利消息
LanguageConfig (接口)
-
包路径 :
io.agentscope.examples.werewolf.localization -
类型: 接口
-
职责: 提供语言相关配置
-
关键方法:
getPlayerNames(): 获取玩家名称列表(不同语言使用不同名字)
18-20. MessageSource 实现类*
-
包路径 :
io.agentscope.examples.werewolf.localization -
类名:
MessageSourcePromptProviderMessageSourceGameMessagesMessageSourceLanguageConfig
-
职责: 基于 Spring MessageSource 实现国际化接口
-
特点 : 从
messages.properties文件读取多语言内容
结构化输出模型层
VoteModel
-
包路径 :
io.agentscope.examples.werewolf.model -
类型: POJO
-
职责: 约束投票输出格式
-
字段:
targetPlayer: 投票目标玩家名称reason: 投票理由
SeerCheckModel
-
包路径 :
io.agentscope.examples.werewolf.model -
类型: POJO
-
职责: 约束预言家查验输出格式
-
字段:
targetPlayer: 查验目标reasoning: 查验理由
WitchHealModel
-
包路径 :
io.agentscope.examples.werewolf.model -
类型: POJO
-
职责: 约束女巫救人决策格式
-
字段:
useHealPotion: 是否使用解药reasoning: 决策理由
WitchPoisonModel
-
包路径 :
io.agentscope.examples.werewolf.model -
类型: POJO
-
职责: 约束女巫毒人决策格式
-
字段:
usePoisonPotion: 是否使用毒药targetPlayer: 毒人目标reasoning: 决策理由
HunterShootModel
-
包路径 :
io.agentscope.examples.werewolf.model -
类型: POJO
-
职责: 约束猎人开枪输出格式
-
字段:
targetPlayer: 开枪目标reasoning: 开枪理由
DiscussionModel
-
包路径 :
io.agentscope.examples.werewolf.model -
类型: POJO
-
职责: 约束讨论发言格式
-
字段:
discussion: 发言内容
技术特点分析
多智能体协作模式
MsgHub 消息中心:
- 用于狼人讨论、白天讨论等场景
- 支持自动广播 (
enableAutoBroadcast) - 智能体间消息同步共享
- 实现:
java
try (MsgHub hub = MsgHub.builder()
.name("Discussion")
.participants(agents)
.announcement(prompt)
.enableAutoBroadcast(true)
.build()) {
hub.enter().block();
// 智能体交互
}
FanoutPipeline 并发管道:
- 用于投票等并发场景
- 所有智能体同时执行
- 收集所有结果
- 实现:
java
FanoutPipeline pipeline = FanoutPipeline.builder()
.addAgents(agents)
.concurrent()
.build();
List<Msg> results = pipeline.execute(prompt, VoteModel.class).block();
结构化输出控制
使用 StructuredOutputHandler 确保 LLM 输出符合预定义格式:
- 定义 POJO 模型类
- 框架自动生成 JSON Schema
- LLM 输出自动映射到 Java 对象
- 支持自动重试和错误纠正
示例:
java
// 约束投票输出
Msg voteMsg = agent.call(votingPrompt, VoteModel.class).block();
VoteModel vote = voteMsg.getStructuredData(VoteModel.class);
String target = vote.targetPlayer;
String reason = vote.reason;
响应式编程
Reactor 框架应用:
- 所有智能体调用返回
Mono<Msg> - 事件流使用
Flux<GameEvent> - 支持背压和非阻塞
- SSE 推送基于响应式流
优势:
- 高并发处理能力
- 资源利用率高
- 天然支持事件驱动
- 适合实时推送场景
国际化架构
三层分离设计:
- 接口层 :
PromptProvider,GameMessages,LanguageConfig - 实现层: 基于 Spring MessageSource
- 资源层 :
messages.properties文件
支持语言:
- 中文 (
messages_zh_CN.properties) - 英文 (
messages_en_US.properties)
优势:
- 提示词和消息分离
- 易于扩展新语言
- 智能体和 UI 分别国际化
事件驱动架构
解耦设计:
- 游戏逻辑通过
GameEventEmitter发射事件 - Web 层订阅事件流并推送给客户端
- 游戏核心与 UI 完全解耦
事件流转:
rust
WerewolfWebGame -> GameEventEmitter -> Reactor Flux -> SSE -> Web 客户端
优势:
- 游戏逻辑可复用(控制台版/Web 版)
- 易于添加新的 UI 形式
- 支持实时监控和录制回放
关键技术实现细节
智能体角色扮演
每个玩家智能体通过系统提示词获得角色认知:
java
ReActAgent agent = ReActAgent.builder()
.name(playerName)
.sysPrompt(prompts.getSystemPrompt(role, playerName)) // 角色提示词
.model(model)
.memory(new InMemoryMemory()) // 独立记忆
.build();
提示词内容包括:
- 角色身份和目标
- 游戏规则
- 行为约束
- 输出格式要求
狼人讨论机制
使用 MsgHub 实现狼人间的私密讨论:
- 创建 MsgHub,只包含狼人智能体
- 发布讨论主题公告
- 智能体轮流发言,消息自动广播
- 所有狼人共享讨论记忆
java
try (MsgHub werewolfHub = MsgHub.builder()
.name("WerewolfDiscussion")
.participants(werewolves)
.announcement(discussionPrompt)
.enableAutoBroadcast(true)
.build()) {
werewolfHub.enter().block();
// 多轮讨论
for (int i = 0; i < 2; i++) {
for (Player werewolf : werewolves) {
Msg response = werewolf.getAgent().call().block();
// 自动广播给其他狼人
}
}
// 投票阶段
werewolfHub.setAutoBroadcast(false);
// 并发投票
}
投票统计逻辑
处理平票情况的算法:
java
public Player countVotes(List<Msg> votes, GameState state) {
Map<String, Integer> voteCount = new HashMap<>();
// 统计票数
for (Msg voteMsg : votes) {
VoteModel vote = voteMsg.getStructuredData(VoteModel.class);
voteCount.put(vote.targetPlayer,
voteCount.getOrDefault(vote.targetPlayer, 0) + 1);
}
// 找到最高票数
int maxVotes = voteCount.values().stream()
.max(Integer::compareTo).orElse(0);
// 获取所有最高票玩家
List<String> tied = voteCount.entrySet().stream()
.filter(e -> e.getValue() == maxVotes)
.map(Map.Entry::getKey)
.toList();
// 平票则随机选择
if (tied.size() > 1) {
String chosen = tied.get(RANDOM.nextInt(tied.size()));
return state.findPlayerByName(chosen);
}
return state.findPlayerByName(tied.get(0));
}
胜负判定
java
// 狼人胜利: 狼人数 >= 好人数
public boolean checkWerewolvesWin() {
int aliveWerewolves = getAliveWerewolves().size();
int aliveVillagers = getAliveVillagers().size();
return aliveWerewolves > 0 && aliveWerewolves >= aliveVillagers;
}
// 好人胜利: 狼人全灭
public boolean checkVillagersWin() {
return getAliveWerewolves().isEmpty();
}
SSE 事件推送
使用 Spring WebFlux 的 SSE 支持:
java
@PostMapping(value = "/start", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<GameEvent>> startGame(@RequestParam String lang) {
GameEventEmitter emitter = new GameEventEmitter();
WerewolfWebGame game = new WerewolfWebGame(emitter, bundle);
// 异步执行游戏
Mono.fromRunnable(() -> {
try {
game.start();
} finally {
emitter.complete();
}
}).subscribeOn(Schedulers.boundedElastic()).subscribe();
// 返回事件流
return emitter.getEventStream()
.map(event -> ServerSentEvent.<GameEvent>builder()
.event(event.getType().name().toLowerCase())
.data(event)
.build());
}
文件组织结构
bash
agentscope-examples/werewolf/
├── pom.xml
└── src/
└── main/
├── java/io/agentscope/examples/werewolf/
│ ├── WerewolfWebApplication.java # 主程序入口
│ ├── WerewolfGameConfig.java # 游戏配置
│ ├── WerewolfUtils.java # 工具类
│ │
│ ├── entity/ # 实体包
│ │ ├── GameState.java # 游戏状态
│ │ ├── Player.java # 玩家
│ │ └── Role.java # 角色枚举
│ │
│ ├── web/ # Web 层
│ │ ├── WerewolfWebController.java # REST 控制器
│ │ ├── WerewolfWebGame.java # 游戏主逻辑
│ │ ├── GameEvent.java # 事件数据
│ │ ├── GameEventType.java # 事件类型
│ │ ├── GameEventEmitter.java # 事件发射器
│ │ └── config/
│ │ └── I18nConfig.java # 国际化配置
│ │
│ ├── model/ # 结构化输出模型
│ │ ├── VoteModel.java # 投票模型
│ │ ├── SeerCheckModel.java # 预言家模型
│ │ ├── WitchHealModel.java # 女巫救人模型
│ │ ├── WitchPoisonModel.java # 女巫毒人模型
│ │ ├── HunterShootModel.java # 猎人模型
│ │ └── DiscussionModel.java # 讨论模型
│ │
│ └── localization/ # 国际化包
│ ├── LocalizationFactory.java # 国际化工厂
│ ├── LocalizationBundle.java # 资源包
│ ├── PromptProvider.java # 提示词接口
│ ├── GameMessages.java # 消息接口
│ ├── LanguageConfig.java # 语言配置接口
│ ├── MessageSourcePromptProvider.java # 提示词实现
│ ├── MessageSourceGameMessages.java # 消息实现
│ └── MessageSourceLanguageConfig.java # 语言配置实现
│
└── resources/
├── messages.properties # 默认资源(中文)
├── messages_zh_CN.properties # 中文资源
├── messages_en_US.properties # 英文资源
├── logback.xml # 日志配置
└── static/ # 静态资源(HTML/CSS/JS)
依赖关系总结
框架依赖
-
AgentScope Core: 核心框架
ReActAgent: 智能体DashScopeChatModel: 大模型MsgHub: 消息中心FanoutPipeline: 并发管道InMemoryMemory: 内存Msg: 消息对象
-
Spring Boot WebFlux: 响应式 Web 框架
-
Project Reactor: 响应式编程库
模块内依赖层次
scss
Web 层 (Controller, Application)
↓
游戏核心层 (WebGame, GameState, Player)
↓
事件层 (Emitter, Event) + 国际化层 (Localization)
↓
模型层 (VoteModel, SeerModel, etc.)
↓
AgentScope 框架层
关键依赖关系
WerewolfWebGame依赖所有层GameState管理Player集合Player封装ReActAgent- 所有操作通过
GameEventEmitter发射事件 - 国际化通过
LocalizationBundle统一提供
扩展性分析
添加新角色
需要修改的类:
Role: 添加新角色枚举WerewolfGameConfig: 配置角色数量PromptProvider: 添加角色提示词GameMessages: 添加角色显示名称WerewolfWebGame: 实现角色技能逻辑- 添加新的结构化输出模型类
支持新语言
需要添加:
messages_<locale>.properties文件- 所有提示词和消息的翻译
- 玩家名称列表
添加新的 UI 形式
步骤:
- 创建新的 Controller 或 Application
- 订阅
GameEventEmitter.getEventStream() - 将事件转换为对应 UI 形式
- 游戏核心逻辑无需修改
更换 LLM
步骤:
- 使用不同的 Model 实现 (OpenAI, Anthropic, Gemini)
- 修改
WerewolfWebGame.initializeGame()中的模型创建 - 其他代码无需修改