山东大学创新实训(六)--基于Multi-Agent的剧本杀平台博客
这段时间,我们组线下讨论一下,觉得作为一个Agent系统,项目虽然可以跑,但我们事前的功能都是相当于在部分主文件不断的加功能,功能越来越多,但主链路越来越模糊。我们考虑收口主链路。因为一个Agent最怕的,不是暂时缺少某个 feature,而是同一类逻辑在不同地方各写一份。就比如我们这个项目一个重心是NPC Agent文字的输出,我们会考虑
- 它到底在做什么决策
- 它输出的文本是什么
- 它有没有 tool intent
- 它有没有越权泄漏
- 它要不要转成语音
- 它要不要进 trace
- 它会不会更新 belief 或房间状态
如果这些事情在项目里没有统一链路,而是散落在多个局部函数里,我们后续不管是修改功能还是测试,都会愈发困难。我们要先统一主链路,继续做升级。
Agent“决策”和“输出”拆开
我先做的第一件事,是新建 backend/app/core/agent_protocol.py,把几个核心协议对象单独定义出来:
- AgentDecision
- MessageRender
- AudioRenderTask
- BeliefUpdate
- GuardrailResult
这一步看起来像是“加了几个 dataclass”,但它其实是这次收口的起点。
因为以前很多逻辑默认把“NPC 想做什么”和“NPC 最后说了什么”混在一起。
一旦混在一起,后面所有层都会痛苦:文本、语音、字幕、复盘、工具调用、状态写入都会互相打架。
所以我把这几个对象先抽出来,目的很明确:
- AgentDecision 负责表达“它打算做什么”
- MessageRender 负责表达“它最终向玩家展示什么”
- AudioRenderTask 负责表达“这条文本是否要异步转成语音”
- BeliefUpdate 负责表达“这一轮输出对内部判断产生了什么更新”
这一步做完后,后面的 Structured Outputs、Voice、Replay、Behavior Evals 才有了统一落点。
公共输出收口到统一链路
接下来我把公共输出尽量统一收口到 backend/app/core/agent_output_pipeline.py。
这条 pipeline 现在负责的事情包括:
- output mode metadata 规范化
- guardrail 检查
- decision/render 结构补齐
- trace 打点
- tool intent 分发
- belief update 写入
- 房间消息落盘
- SSE 发布
- 语音异步任务调度
也就是说,以前那些“消息发出去就算完”的逻辑,现在不再只是简单 publish,而是走完整的主链路。
我这样做的原因很现实:
如果以后我要继续做 BeliefState、voice-safe rewrite、tool dispatch side effect、replay explainability,它们都必须挂在同一条链路上,否则每加一层都要反复补分支。
所以这一步不是“抽象得更漂亮”,而是把系统未来所有能力的落点先定死。
阶段推进迁到workflow
如果说 service 层是在拆“职责”,那 workflow 层就在拆“流程”。我这几天做的另一条主线,是把阶段推进继续迁进 backend/app/core/workflows/game_workflow.py。
例如advance_discussion_turn,这段逻辑原来要同时处理:
- 当前轮到谁发言
- NPC 是陈述还是提问
- NPC 对 NPC 的即时问答
- human turn ready 提示
- discussion round 结束后的收尾
它本质上就是 orchestration,而不是 route 应该承担的职责。不应该存在于game.py,应该迁徙到独立的workflow
Belief、Voice、Recovery
BeliefState 不是只存字段,而是真正写进 room state,而且 belief 已经开始影响vote target,discussion question target,private reply 策略,也就是说,Belief 已经不只是一个结构化字段,而开始参与 Agent 后续行为。而对于Voice-safe rewrite, voice_persona 和 voice_safe_rewrite,并且把它们挂回主链路。
现在文本仍然是业务事实,语音是渲染层;但在 TTS 之前,系统会先尝试做 voice-safe rewrite,而不是直接拿原始文本朗读。Durable recovery不只是能落盘,我不满足于仅仅把任务存到 durable_tasks.json,我把他们推进到了回复失败记录,最大回复次数等,使得方便后续测试。
