2026/4/4 22:57:48
网站建设
项目流程
响应式外贸网站建设,网站开发商,vs2010网站开发教程c,网站建设设计合同书作者#xff1a;亦盏、望宸
狼人杀一款经典的社交推理游戏#xff0c;我刚毕业那会儿#xff0c;玩狼人杀是聚餐时的保留节目#xff0c;也留下了很多挺有意思的回忆#xff1a;比如有的高手#xff0c;如果第一晚没被狼人“杀掉”#xff0c;那大家就会觉得他一定是狼…作者亦盏、望宸狼人杀一款经典的社交推理游戏我刚毕业那会儿玩狼人杀是聚餐时的保留节目也留下了很多挺有意思的回忆比如有的高手如果第一晚没被狼人“杀掉”那大家就会觉得他一定是狼人白天必须自证清白。但随着时间推移凑齐一群人玩狼人杀变成了一件奢侈的事情。所以我用 AgentScope 做了这款可以随时开局的 AI 狼人杀。Agent 会像真人一样讨论、推理、投票甚至学会说谎现在你不需要等人随时都能和 AI 来一局而且你还不一定能玩得过他。游戏介绍在 AgentScope 上和 AI 玩狼人杀本视频基于 1.0.5 版本录制后续更新代码可能会有部分调整优化。快速开始环境要求Java 17、Maven 3.6、百炼 API Key(登录 https://bailian.console.aliyun.com/ 获取)# 拉取项目源码gitclone https://github.com/agentscope-ai/agentscope-java# 设置百炼 API KeyexportDASHSCOPE_API_KEYyour_api_key# 进入游戏目录并启动cdagentscope-java/agentscope-examples/werewolf-hitlgitcheckout release/1.0.5 mvn spring-boot:run打开浏览器访问http://localhost:8080选择你想扮演的角色点击开始游戏如何开发 AI 狼人杀当我真正动手用开发 AI 狼人杀时发现这件事远没有写几个 Prompt那么简单。首先多个 AI Agent 需要持续地思考。要根据每一轮中各个玩家的发言和表现分析他们的真实身份。同时像狼人、女巫、预言家、猎人这些角色还需要在游戏的过程中根据实际情况选择是否使用自己的技能——这些都不是一个简单的提示词就能搞定的。其次信息必须隔离。狼人杀最核心的乐趣就是信息差狼人知道队友是谁但好人不知道预言家查验的结果只有自己清楚。如果所有 Agent 共享同一份记忆游戏就没法玩了。然后一群玩家轮流说话Agent 能记住谁都说过什么吗大模型 API 只有 system/user/assistant 角色没有5号玩家说这种概念。如果直接把对话历史丢给模型它根本搞不清楚这一堆话分别是谁说的。还有Agent 的决策必须明确。投票环节我们需要的是投 3 号这样明确的答案而不是我觉得 3 号有点可疑可能是狼人吧这种模糊表述。程序要能可靠地解析 Agent 的决策不能靠正则表达式去匹配。最后还需要支持人类玩家。纯 AI Agent 对战固然有趣但是人类玩家能够参与进来才是一个正经的游戏。针对这些问题我们用 AgentScope 的 7 个核心能力逐一解决ReActAgent让每个 Agent 拥有持续思考的能力MsgHub实现了消息的自动广播和信息隔离多智能体格式化器让 Agent 能区分不同说话者结构化输出确保 Agent 的决策被可靠解析多Agent编排协调多个 Agent 按规则轮流行动SSE 实时推送让前端实时展示游戏进程Human in the Loop让人类玩家无缝加入对局。接下来我们将逐一拆解这些 AgentScope 的关键技术在狼人杀游戏中的应用。ReActAgent会持续思考的玩家ReActAgent 是 AgentScope 提供的核心 Agent 实现基于ReActReasoning Acting范式它能让 LLM 不只是回答问题而是思考-行动-再思考先分析当前局势决定是否需要调用工具获取更多信息执行工具后再继续推理直到得出最终结论。在狼人杀中每个 AI 玩家都是一个 ReActAgent它们能够记住之前的讨论内容分析其他玩家的发言和投票的逻辑根据自己的角色身份做出合理的决策。创建一个 ReActAgentReActAgentwerewolfAgentReActAgent.builder().name(3号)// 玩家名称.sysPrompt(prompts.getSystemPrompt(Role.WEREWOLF,3号))// 角色提示词.model(model)// LLM 模型.memory(newInMemoryMemory())// 对话记忆.build();创建一个 AI 玩家只需要几行代码主要包含四个核心配置name是玩家的标识标识这个 ReActAgent 对应哪个玩家sysPrompt定义角色身份和行为策略model是底层的 LLMmemory负责存储对话历史。Prompt角色提示词设计Prompt 决定了 Agent 的人设在这里狼人角色的提示词如下【角色身份】你是3号一名狼人。 【核心目标】 - 隐藏你的狼人身份生存到最后通过发言误导好人投票淘汰神职与狼队友配合在白天制造逻辑混乱。 【策略选择】 策略 A悍跳狼冒充预言家 - 第一轮起跳给出虚假查验信息 - 语气坚定自信指责对手是悍跳狼 策略 B深水狼伪装村民 - 发言精简避免成为焦点 - 像一个努力找狼的普通村民不同角色的 Prompt 差异很大狼人需要学会撒谎和配合预言家、女巫、猎人需要懂得如何使用自己的技能村民需要会分析逻辑找出破绽。Memory对话记忆管理每个 Agent 都有独立的 Memory记录它听到和说过的所有内容。当 Agent 调用call()生成回复时会先从 Memory 中获取历史消息作为上下文发送给 LLM。在狼人杀中Memory 的作用很关键每轮讨论的发言都会写入 MemoryAI 可以回顾完整的讨论历史判断谁的发言有逻辑漏洞、谁一直在带节奏。ReActAgent 把 LLM 的能力封装成一个能持续思考和对话的玩家他们能够记住之前的讨论内容分析其他玩家的发言和投票的逻辑在法官的引导下完成整局游戏。MsgHub实现 Agent 的对话和投票狼人杀的精髓在于信息的不对称——狼人知道队友是谁好人却一无所知。这对多 Agent 系统提出了挑战白天的讨论、投票结果需要公开广播给所有人但狼人夜晚的密谋、预言家的查验结果必须严格隔离只有特定角色能看到。如果手动管理每条消息的接收者写一堆 if-else 判断这条消息该发给谁代码很快就会变成一团乱麻。AgentScope 的MsgHub用消息频道解决这个问题不同场景使用不同的频道每个频道有自己的参与者列表消息只在频道内流转。狼人夜晚讨论用一个狼人频道白天公开讨论用公共的频道频道之间天然隔离。还有一个细节消息的广播时机也很讲究。讨论环节玩家轮流发言每个人说完后其他玩家需要立刻听到这段话这样后面发言的人才能针对前面的内容进行分析和回应——这需要实时广播。但投票环节不一样如果每个人投完票就立刻公布后面的人可能会跟票。所以投票内容需要先收集起来等所有人都投完后再统一公布。MsgHub 同时支持这两种模式自动广播用于讨论手动广播用于投票。MsgHub 使用示例// 讨论阶段开启自动广播try(MsgHubdiscussionHubMsgHub.builder().name(DayDiscussion).participants(alivePlayers.stream().map(Player::getAgent).toArray(AgentBase[]::new)).announcement(nightResultMsg).enableAutoBroadcast(true)// 自动广播.build()){discussionHub.enter().block();for(Playerplayer:alivePlayers){// 每个玩家发言后自动广播给其他所有玩家player.getAgent().call().block();}}// 投票阶段关闭自动广播votingHub.setAutoBroadcast(false);// 先关闭自动广播ListMsgvotesnewArrayList();for(Playerplayer:alivePlayers){Msgvoteplayer.getAgent().call(votingPrompt,VoteModel.class).block();votes.add(vote);}// 收集完所有投票后统一广播结果votingHub.broadcast(votes).block();讨论阶段开启自动广播Agent 的call()返回后立刻推送给其他参与者投票阶段关闭自动广播等收集完所有票后通过broadcast()统一公布。这两种模式背后其实对应了 Agent 的两种消息接收方式讨论阶段每个玩家调用call()主动发言MsgHub 自动把这条消息广播给其他玩家。其他玩家通过observe()方法听到这条消息——消息写入他们的 Memory但不需要立刻回复。等轮到他们发言时再调用call()基于 Memory 中的所有历史进行分析和回应。投票阶段关闭自动广播后每个玩家的投票不会立刻被其他人知道。等所有人都投完再通过broadcast()一次性把所有投票结果发给大家。这时候调用的也是observe()玩家只需要知道结果不需要回复。MsgHub 核心原理MsgHub 基于发布-订阅模式实现多智能体间的消息自动广播基于 MsgHub我们用自动广播解决了讨论环节的实时同步用手动广播解决了投票环节的统一公布用多个 MsgHub 实现了狼人密谋、预言家查验、女巫决策等场景的信息隔离。MultiAgentFormatter让 Agent 理解多人对话多个玩家轮流发言每个人都有自己的立场和策略。当 7 号玩家需要分析局势时他必须清楚地知道刚才那句我是预言家是 3 号说的而他是悍跳狼是 5 号说的。只有分清楚谁说了什么才能进行逻辑推理。大多数 LLM API 只支持**system、user、assistant**三种角色没有5号玩家或7号玩家这种概念。如果我们把 Agent 的发言都以user角色发送给模型模型看到的是这样的user: 我是预言家昨晚验了5号查杀 user: 我才是真预言家3号是悍跳狼 user: 作为5号的金水我选择站5号。三条消息三个 user模型根本分不清这是同一个人说了三句话还是三个人各说了一句。逻辑推理无从谈起。引入 AgentScope 的MultiAgentFormatter后 Agent 便能自动理解多人发言对话发送给 LLM 的消息会被自动格式化成这样user: # Conversation History The content between history/history tags contains your conversation history history 3号: 我是预言家昨晚验了5号查杀 5号: 我才是真预言家3号是悍跳狼 7号: 作为5号的金水我选择站5号。 /history 现在轮到你发言请分析场上局势。所有对话历史被合并成一条user消息每条发言前面都带上了说话者的名字。LLM 一眼就能看出3 号说自己是预言家并查杀了 5 号5 号反驳说 3 号才是悍跳狼7 号选择站 5 号。逻辑链条清清楚楚。接入 MultiAgentFormatterMultiAgentFormatter 的使用非常简单只需在创建模型时指定格式化器DashScopeChatModelmodelDashScopeChatModel.builder().apiKey(apiKey).modelName(qwen3-plus).formatter(newDashScopeMultiAgentFormatter())// 一行代码搞定.build();配置完成后所有通过这个模型发送的请求都会自动进行多人对话格式化不需要在业务代码中手动处理。AgentScope 为通义千问、OpenAI GPT、Claude、Gemini 等主流模型都提供了对应的 Formatter。MultiAgentFormatter 实现原理格式化器的工作分为两个关键步骤消息标记和消息合并。消息标记每个 Agent 在创建时都有一个名字如3号。当 Agent 生成回复时框架会自动把 Agent 的名字设置到消息的name字段// 创建 Agent 时设置名字ReActAgent.builder().name(3号)// ← 这个名字会自动绑定到消息.model(model).build();// Agent 生成回复时框架内部自动执行MsgresponseMsgMsg.builder().name(agent.getName())// ← 3号.role(MsgRole.ASSISTANT).content(我是预言家昨晚验了5号查杀).build();这样Memory 中存储的每条消息都带有发言者的标识格式化器只需读取msg.getName()就知道是谁说的。消息合并发送给 LLM API 之前格式化器会把 Memory 中的多条消息合并成一条带标签的历史记录Memory 中的原始消息如下每条都是独立存储带有 name 字段。Msg(name3号, content我是预言家昨晚验了5号查杀) Msg(name5号, content我才是真预言家3号是悍跳狼) Msg(name7号, content作为5号的金水我选择站5号。) ...可能有几十条经过MultiAgentFormatter.format()处理后发送给 LLM API 的消息只有 2 条[0] role: system content: 你是3号玩家一名狼人... [1] role: user content: history 3号: 我是预言家昨晚验了5号查杀 5号: 我才是真预言家3号是悍跳狼 7号: 作为5号的金水我选择站5号。 /history前文我们提到 LLM API 的角色只有三种如果 9 个人讨论 2 轮就是 18 条user消息模型很难理清楚这些消息之间的关系。合并成一条后所有对话历史作为一个整体呈现而且都是name: content这种统一的格式LLM 能够清晰地理解完整的讨论脉络。只需要在创建 ReActAgent 所使用的 Model 对象时传入一个MultiAgentFormatter框架就会自动完成消息标记和合并让 LLM 在只有三种角色的限制下也能理解多人的多方对话。开发者不需要关心格式化的细节专注于业务逻辑即可。Structured Output让 Agent 做出明确的决策狼人杀的每个关键节点都需要玩家做出明确的决策投票选谁出局预言家查验哪个玩家女巫要不要用解药这些决策必须是确定的——“投 3 号或者查 5 号”不能是我觉得 3 号和 5 号都挺可疑的这种模糊表达。但 LLM 天生就是自由文本的生成者。你问它你要投谁它可能回答综合各方面分析我认为那个一直沉默的玩家最可疑——说了一堆就是没告诉你具体投几号。就算你在 Prompt 里强调请直接回复玩家编号它有时候还是会夹带私货输出3号因为他的发言逻辑矛盾这种格式。程序要解析这些五花八门的回复简直是噩梦。AgentScope 的结构化输出Structured Output彻底解决了这个问题。你只需要定义一个 Java 类描述期望的数据结构框架会自动约束 LLM 严格按照这个格式输出返回的结果直接就是 Java 对象不需要任何解析代码。Structured Output基本用法白天投票的示例// 1. 定义投票模型publicclassVoteModel{publicStringtargetPlayer;// 投票目标publicStringreason;// 投票理由}// 2. 使用结构化输出调用 AgentMsgvoteplayer.getAgent().call(votingPrompt,VoteModel.class)// 指定输出类型.block();// 3. 直接获取类型安全的数据VoteModelvoteDatavote.getStructuredData(VoteModel.class);System.out.println(voteData.targetPlayer);// 5号System.out.println(voteData.reason);// 他的逻辑有明显漏洞Structured Output实现原理结构化输出基于Function Calling机制实现通过将 Java 类转换为工具定义引导 LLM 通过调用工具来生成符合格式的响应。框架将 Java 类如VoteModel转换为 JSON Schema注册为临时工具generate_response让 LLM 通过调用该工具生成符合格式的响应。响应会被自动验证并转换为 Java 对象失败则提示重试最后清理临时工具。Structured Output优势类型安全返回结果直接是 Java 对象IDE 自动补全、编译时检查不用担心拼写错误或类型不匹配。格式统一模型被强制按照 JSON Schema 输出不会出现有时候是 target有时候是 vote的混乱情况。自动重试如果模型输出不符合格式框架会自动提示模型修正并重试不需要手动处理。开发高效定义一个 POJO 类就搞定投票、查验、救人等决策点复用同一套机制不用每个地方都写解析代码。多 Agent 编排游戏流程控制有了 ReActAgent、MsgHub、MultiAgentFormatter 和 Structured Output 输出这些能力接下来的问题是如何把它们串起来让多个 Agent 按照狼人杀的规则有序地执行一局狼人杀的流程可以概括为夜晚阶段 → 白天讨论 → 投票放逐然后判断胜负未结束则进入下一轮夜晚。每个阶段内部又有更细的流程这里的每个流程节点都对应着前文介绍过的技术实现夜晚阶段 - 狼人讨论创建一个只有狼人参与的 MsgHub开启自动广播。狼人们在这个私密频道里讨论好人完全看不到。最后通过结构化输出收集每个狼人的投票统计出击杀目标。夜晚阶段 - 神职行动预言家、女巫、猎人的行动是私密的不需要 MsgHub直接单独调用对应的 Agent。查验结果、救人决定等信息直接写入该 Agent 的 Memory其他人不知道。白天讨论创建所有存活玩家参与的 MsgHub将昨天夜晚的结果加入到 MsgHub 中。开启自动广播后玩家轮流发言每个人说完后其他人立刻听到。投票放逐关闭自动广播轮流调用每个 Agent 通过结构化输出收集投票。等所有人投完后统计结果并统一公布。GameState 流转GameState 是游戏全局状态记录游戏的客观事实——谁是什么角色、谁还活着、当前是第几天、昨晚谁被杀了。这些信息由编排器维护编排器在推进游戏时也需要不断查询 GameState 来做出决策游戏进行到第几轮了、现在是夜晚还是白天、该轮到谁行动了、游戏是应该结束还是继续。GameState 通过 currentRound 追踪当前轮次每次进入夜晚时递增。阶段的切换由编排器控制在夜晚按固定顺序调度狼人讨论、预言家查验、女巫行动在白天则遍历存活玩家列表按座位顺序轮流发言和投票。每个阶段结束后编排器会调用 checkWerewolvesWin() 和 checkVillagersWin() 进行胜负判定——狼人数量达到或超过好人则狼人获胜狼人全部出局则好人获胜否则游戏继续进入下一阶段。游戏编排的核心流程包含用 MsgHub 管理多 Agent 的消息广播和隔离用结构化输出收集 Agent 的决策用 GameState 维护游戏状态用 Memory 保存每个 Agent 的对话上下文。编排器作为中心协调者按照规则调度游戏有序运转。SSE 实时推送游戏进程的实时展示前面几节讲清楚了 Agent 之间的消息流转和游戏流程编排现在AI已经可以自己把狼人杀玩起来了但是咱们还是什么也看不到。现在需要把游戏的实时进展推送到前端——让观战者能跟着剧情走也为后面的人机对战做好准备。我们使用Server-Sent Events (SSE)实现实时推送服务器主动把事件推给前端不需要轮询发言和投票结果都能第一时间送达。透出游戏事件要把游戏推送到前端首先需要一个中间层来收集游戏过程中产生的各类事件。GameEventEmitter承担了这个角色游戏编排器在每个重要节点玩家发言、阶段切换、投票结果、玩家淘汰等调用它的emit方法透出事件。为了让观战者能够沉浸式地观战和享受自己推理的乐趣我们设计了两种视角玩家视角实时推送但会根据参与者的角色过滤掉不该看到的信息。村民视角就只能看到公开信息“狼人视角就能看到狼人夜间讨论观战时默认使用村民视角”。上帝视角保存所有事件不做任何过滤游戏结束后用于复盘。publicclassGameEventEmitter{// 玩家视角实时推送根据角色过滤privatefinalSinks.ManyGameEventplayerSinkSinks.many().multicast().onBackpressureBuffer();// 上帝视角保存所有事件用于复盘privatefinalListGameEventgodViewHistorynewArrayList();privatevoidemit(GameEventevent,EventVisibilityvisibility){// 上帝视角无条件保存godViewHistory.add(event);// 玩家视角根据可见性过滤if(visibility.isVisibleTo(humanPlayerRole)){playerSink.tryEmitNext(event);}}}游戏进行时观战者通过playerSink实时收到过滤后的事件流游戏结束后可以通过/api/game/replay接口获取完整的godViewHistory回看所有隐藏的细节——“原来 5 号真的是预言家”服务端提供事件接口有了事件发射器接下来要让前端能接收这些事件。Spring WebFlux 对 SSE 有原生支持Controller 返回一个FluxServerSentEvent浏览器请求这个端点后HTTP 连接会保持打开服务器可以随时往里面推送数据。PostMapping(value/start,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicFluxServerSentEventGameEventstartGame(...){GameEventEmitteremitternewGameEventEmitter();WerewolfWebGamegamenewWerewolfWebGame(emitter,...);// 游戏在后台异步执行不阻塞 SSE 连接Mono.fromRunnable(()-game.start()).subscribeOn(Schedulers.boundedElastic()).subscribe();// 把事件发射器的输出转成 SSE 格式returnemitter.getPlayerStream().map(event-ServerSentEvent.GameEventbuilder().event(event.getType().name().toLowerCase()).data(event).build());}这段代码做了两件事一是在后台线程启动游戏逻辑二是把emitter的事件流转换成 SSE 格式返回。游戏逻辑每emit一个事件浏览器就能实时收到。前端实时渲染游戏进程最后一步是前端订阅这个 SSE 流。浏览器原生的EventSourceAPI 让这变得非常简单——建立连接后为每种事件类型注册处理函数收到事件就更新 UI。consteventSourcenewEventSource(/api/game/start);// 有人发言了 → 追加到聊天记录eventSource.addEventListener(player_speak,(e){consteventJSON.parse(e.data);appendMessage(event.data.player,event.data.content);});// 阶段切换了 → 更新顶部状态栏eventSource.addEventListener(phase_change,(e){consteventJSON.parse(e.data);updatePhase(event.data.round,event.data.phase);});// 游戏结束了 → 显示胜负结果关闭连接eventSource.addEventListener(game_end,(e){consteventJSON.parse(e.data);showGameResult(event.data.winner);eventSource.close();});整个流程非常简洁后端游戏逻辑产生事件 →GameEventEmitter过滤并发射 → Controller 转成 SSE 格式 → 浏览器实时接收并渲染。不需要轮询不需要 WebSocket 的复杂握手一条 HTTP 长连接搞定一切。Human in the Loop人机对战前面通过 SSE 把游戏进程推送到了前端观战模式可以实时看到 AI 之间的博弈。但是光看肯定不过瘾人类玩家能够参与进来才是一个正经的游戏。游戏编排器调用agent.call()时可以同步拿到结果继续执行。AI 玩家可以及时返回但人类玩家需要等待——等用户在浏览器上思考、输入、提交。如何让同步的游戏流程暂停在人类玩家这里等到输入后再继续AgentScope 提供的UserAgent解决了这个问题。它和ReActAgent实现同一个接口游戏编排器不需要区分当前是人还是 AI以同样的方式调用即可。而具体如何获取人类输入则可以通过注入不同的UserInputBase实现来定制。UserAgent与 ReActAgent 接口一致对于游戏编排器来说人类玩家和 AI 玩家没有区别——都是调用agent.call()拿到回复区别只在于创建时的配置// AI 玩家 - 使用 ReActAgent由 LLM 生成回复agentReActAgent.builder().name(3号).sysPrompt(prompts.getSystemPrompt(Role.WEREWOLF,3号)).model(model).memory(newInMemoryMemory()).build();// 人类玩家 - 使用 UserAgent由用户输入回复agentUserAgent.builder().name(1号玩家).inputMethod(webUserInput)// 注入 Web 输入源.build();这种设计的好处是游戏编排器的代码完全不需要改动只需要在初始化时把某个位置的ReActAgent换成UserAgent就能实现人机对战。WebUserInput浏览器与游戏的桥梁WebUserInput是连接浏览器和游戏逻辑的关键组件。它的核心机制是使用 Reactor 的Sinks.One实现异步等待publicMonoStringwaitForInput(StringinputType,Stringprompt){// 1. 创建一个 Sink类似 CompletableFutureSinks.OneStringinputSinkSinks.one();pendingInputs.put(inputType,inputSink);// 2. 通过 SSE 通知前端现在轮到你了emitter.emitWaitUserInput(inputType,prompt);// 3. 返回 Mono游戏线程会在这里等待returninputSink.asMono();}publicvoidsubmitInput(StringinputType,Stringcontent){// 用户提交后找到对应的 Sink 并发出值Sinks.OneStringsinkpendingInputs.remove(inputType);if(sink!null){sink.tryEmitValue(content);// 解除等待游戏继续}}当游戏轮到人类玩家时waitForInput()会通过 SSE 发送一个WAIT_USER_INPUT事件给前端然后返回一个Mono让游戏线程等待。用户在浏览器上输入并提交后前端调用/api/game/input接口Controller 调用submitInput()发出值游戏线程被唤醒继续执行。完整流程整个人机交互的数据流可以概括为游戏等待 → SSE 通知前端 → 用户输入 → REST 提交 → 游戏继续。基于这套机制你可以选择任意角色加入游戏扮演预言家带领好人阵营推理或者混入狼人阵营也可以随机一个角色。现在你不需要等待人齐随时打开就能跟 AI 玩一局。项目整体回顾最后我们再来整体回顾一下项目我们面临六个核心挑战如何让 Agent 持续思考而非一次性回答、如何在公开讨论和私密密谋之间实现信息隔离、如何让 Agent 理解多人对话中谁说了什么、如何确保 Agent 的决策能被程序可靠解析、如何让人类玩家与 AI 无缝交互、以及如何将游戏进程实时展示给用户。针对这些挑战我们用 AgentScope 的核心能力逐一解决ReActAgent让每个 Agent 拥有持续思考的能力MsgHub实现了消息的自动广播和信息隔离多智能体格式化器让 Agent 能区分不同说话者结构化输出确保 Agent 的决策被可靠解析UserAgent让人类玩家无缝加入对局流式输出配合 SSE 实时推送让前端实时展示游戏进程。在此基础上我们还需要处理游戏特有的编排运维与交互问题GameState维护游戏全局状态支撑整个游戏编排逻辑AgentRun提供 Agent 运行时环境Reactor Sink实现异步等待让 WebFlux 应用能够优雅地等待用户输入。What’s Next? 多人对战更加有趣目前的实现是单人与 AI 对战但狼人杀的魅力在于玩家之间的心理博弈。想象一下你和朋友们一起加入游戏在白天讨论中互相试探、相互表演而 AI 玩家填补剩余的席位让你们不需要凑齐人数也能开局。多人对战模式将带来更真实的社交推理体验——毕竟骗自己的朋友可比骗 AI 有意思多了。️ 全模态交互身临其境纯文本的对话虽然高效但少了几分沉浸感。AgentScope Python 版本狼人杀[https://github.com/agentscope-ai/agentscope/tree/main/examples/game/werewolves]已经实现了 TTS 和 实时全模态的支持。下一步我们计划在 Java 版本也引入语音交互你可以直接用语音发言AI 玩家也会用语音回复——御姐音的女巫冷静分析局势萝莉音的村民楚楚可怜地自证清白低音炮的狼人在夜晚密谋时压低嗓音……每个角色都有专属的声线让游戏从看对话变成听对话仿佛真的围坐在一起玩桌游。 欢迎参与社区贡献这个项目是 AgentScope Java 的示例应用我们非常欢迎社区的参与和贡献无论是修复 Bug、优化体验、添加新角色白痴、守卫、丘比特……还是实现上面提到的多人对战和语音功能都期待你的加入。你可以通过提交 Issue 反馈问题或者直接提交 Pull Request 贡献代码。让我们一起把这个项目做得更好欢迎加入AgentScope 钉钉交流群群号: 146730017349。