偷取清单:从 20 个项目里拆出一个平台

Agora · 第 3 篇 / 共 5 篇


第一篇讲了赛道是空的。第二篇讲了为什么不 fork。这篇讲"不 fork 但偷"具体怎么执行——从哪个项目偷什么,怎么拼成一个平台架构。


三个核心判断:

  1. "偷"的本质是萃取设计模式,不是复制代码
  2. 每个核心模块应该有一个明确的"设计出处"——你知道它为什么长这样
  3. 最好的架构不是你设计出来的,是你从二十个项目的共性里读出来的

1. 六个出处,六个模块

调研了二十多个项目,但真正值得偷的设计集中在六个地方。

AgentScope → agent 接口 + 消息隔离

AgentScope 的 agent 设计只有两个方法:reply()observe()

reply 是"轮到你了,给个回答"。observe 是"这条消息你需要知道,但不需要回应"。所有 agent 行为都能用这两个方法描述。辩论中轮到你发言是 reply,听别人发言是 observe。狼人杀投票是 reply,听到淘汰公告是 observe。

这个抽象之所以好,是因为它把"生成回复"和"接收信息"分成了两个独立动作。大部分框架把这两件事混在一起——agent 收到消息就必须回复。但很多场景里,agent 需要"听但不说"。狼人杀白天讨论阶段,已经被淘汰的 agent 还需要 observe 后续消息(进入墓地频道),但不再 reply。

MsgHub 的消息隔离也值得偷。核心机制:嵌套作用域 + 自动广播开关 + 动态订阅管理。翻译成平台概念就是:频道可以有子频道,频道可以控制消息是否自动推送给所有订阅者,agent 可以在运行时被动态加入或移出频道。

ChatArena → 三层架构

ChatArena 虽然废弃了,但它的架构是我调研过最优雅的:Arena > Environment > Players。

Arena 是运行时容器——相当于平台本身。Environment 定义游戏规则——状态转换、胜利条件、信息可见性。Players 是参与者——各自有策略和记忆。

这三层之间是干净的依赖关系:Players 不知道 Arena 的存在,Environment 不关心 Players 的实现细节,Arena 只负责把它们组装在一起然后驱动循环。

映射到多 Agent 平台:Platform > Mode > Agent。Platform 是基础设施——房间、频道、流程控制、实时通信。Mode 是规则配置——狼人杀、辩论、剧本杀各自定义角色、频道、流程、决策格式。Agent 是参与者——各自有人设、模型、记忆。

ChatArena 验证了一件事:把"游戏规则"作为一个独立的可插拔层来设计,是正确的抽象粒度。

斯坦福 Generative Agents → 记忆反思循环

斯坦福 2023 年的论文定义了 AI agent 记忆系统的标杆。核心是三步循环:observe → reflect → plan。

observe:agent 观察到事件,存入记忆流。reflect:定期回顾近期记忆,用 LLM 提炼出更高层的认知。plan:基于记忆和反思结果,规划下一步行为。

关键洞察是 reflect 这一步。没有反思,agent 只有流水账式的记忆——"张三说他是好人"、"李四投了王五"。有了反思,agent 能形成更高层的判断——"张三最近三轮都在替李四说话,他们可能是同伙"。

对多 Agent 游戏来说,这不是锦上添花。剧本杀需要 agent 交叉比对线索、形成理论、调整怀疑方向。TRPG 需要 AI 地下城主记住三个 session 前玩家做的选择。没有反思循环,agent 就是一个只看最近 N 条消息的复读机。

但这个模块第一阶段不需要。圆桌辩论和狼人杀用消息数组就够了。反思循环在剧本杀阶段引入,给了充足的时间来实现。

AgentScope 狼人杀 sample → 结构化决策

AgentScope 的狼人杀 sample 有一个细节值得单独提:用 Pydantic schema 约束投票目标。

投票时,schema 动态生成一个枚举——只包含当前存活的玩家名字。agent 调用 LLM 时传入这个 schema,LLM 的输出被强制约束在这个枚举范围内。

效果是:agent 物理上无法投给死人、无法编造不存在的名字、无法输出"我弃权"(除非弃权是枚举选项之一)。

这比 prompt 层面的"请只投给以下玩家"可靠得多。prompt 是建议,schema 是约束。多 Agent 游戏如果投票能投出 bug,整局就废了。

翻译到 TypeScript:Zod 的 z.enum() 做同样的事。配合 Vercel AI SDK 的 generateObject(),结构化决策这条路已经打通了。

evotraders → 前端视觉模式

AgentScope sample 里的 evotraders 是一个多 agent 交易模拟,前端做得出人意料地好。两个组件值得偷。

AgentFeed:agent 活动流。不是普通的聊天气泡——每个 agent 有自己的颜色、头像、状态标识。正在思考的 agent 有呼吸灯效果。刚发言的 agent 气泡高亮。这些视觉细节把"一堆文字"变成了"一群在互动的角色"。

RoomView:语音气泡 + 头像布局。agent 围成一圈或坐在桌旁,发言时气泡弹出。这个视觉隐喻立刻让用户理解空间关系和轮次。

这两个模式回答了多 Agent 平台最大的 UX 问题:怎么让一堆 AI 的对话不像在看 log 文件。

Accio Work → "建群聊"的交互隐喻

阿里国际的 Accio Work 给出了整个调研中最重要的产品洞察。

它做的事情不复杂——让多个 AI agent 协作完成任务。但它的交互方式选对了:像建微信群一样组建 agent 团队。

创建一个 agent 就像添加联系人。组建团队就像建群。分配任务就像发消息。

这个隐喻的威力在于:你不需要教用户什么是 agent 编排、什么是消息路由、什么是频道隔离。"群聊"是所有人都懂的心智模型。

这也决定了技术选型。要做出 Accio Work 级别的 UI 体验,Python 全栈做不到。Gradio 和 Streamlit 的上限离群聊式交互还差得远。这是 上一篇加权打分里 Web UI 维度给 fork 方案打低分的根本原因。

2. 拼在一起:三层架构

六个出处汇聚成一个三层架构。

模式层:    圆桌辩论 | 狼人杀 | 剧本杀 | TRPG | 自定义
            (角色模板 + 频道规则 + 流程配置 + 决策格式)
平台核心:  Agent | 房间 | 频道 | 流程控制 | 记忆 | 事件总线
基础设施:  Vercel AI SDK | Socket.io | PostgreSQL | Next.js

模式层对应 ChatArena 的 Environment。每个模式是一个配置包:定义角色模板(狼人、村民、先知)、频道规则(哪些角色能看哪个频道)、流程配置(状态机还是自由发言还是轮流)、决策格式(投票、调查、战斗)。新增一种游戏不需要改平台代码——只需要写一个新的模式配置。

平台核心对应 ChatArena 的 Arena。Agent 用 AgentScope 的 reply/observe 双接口。频道用 MsgHub 的嵌套作用域 + 动态订阅。流程控制从简单到复杂有四种实现——自由发言、轮流、状态机、层级——由模式决定用哪种。记忆分会话级(消息压缩)和跨会话(向量检索 + 反思),按需加载。

基础设施层是纯工具选择。LLM 用 Vercel AI SDK 做多模型路由和结构化输出。实时通信用 Socket.io。存储用 PostgreSQL + pgvector。前端用 Next.js。

每一层的设计决策都有出处。不是凭空想出来的,是从二十个项目的共性里读出来的。

3. 频道系统:多 Agent 的核心难题

六个模块里,频道系统是最关键的。

信息隔离是多 Agent 交互和"一堆 AI 在同一个聊天框里说话"的本质区别。没有频道隔离,狼人杀跑不了——狼人不能让平民看到自己的讨论。剧本杀跑不了——每个角色有独占的线索。三省六部跑不了——部门之间有信息壁垒。

AgentScope 的 MsgHub 验证了三个核心机制:

  1. 嵌套作用域。 频道可以有子频道。狼人杀的主频道下面可以嵌套狼人夜间频道、先知查验频道、医生行动频道。
  2. 动态订阅。 agent 在运行时被加入或移出频道。被淘汰的狼人从狼人频道移出,加入墓地频道。
  3. 广播控制。 有的频道消息立刻推送给所有订阅者(白天讨论),有的频道消息需要手动拉取(剧本杀的线索分发——线索放在频道里,但只有调查对应地点的 agent 才能看到)。

但 MsgHub 是代码级别的实现。翻译成平台需要加一层:频道规则由模式定义,运行时由平台自动管理。模式说"狼人杀需要五个频道,订阅关系如下",平台在房间启动时自动创建这些频道、绑定订阅关系、在状态转换时动态调整。

这大概需要 300-500 行核心代码。但这恰恰是平台最核心的竞争力——越关键的模块,越不该外包给第三方。

4. 记忆:200 行代码,80% 的效果

记忆系统是 上一篇提到的"10% 遗憾"集中的地方。实际拆解之后,遗憾变小了。

会话记忆压缩。 九人狼人杀跑完一局可能有 200 多条消息。全塞进上下文窗口,一半是前几轮的重复讨论。解决方案偷自 AgentScope:设一个阈值(比如 40 条),超过后用便宜模型(Haiku 级别)把旧消息总结成一段摘要。agent 的上下文变成"摘要 + 最近 15 条消息"。大约 80 行代码。

长期语义检索。 跨 session 的记忆——TRPG 角色记住三个 session 前的选择,剧本杀 agent 在新一局里带着上一局学到的策略。偷自 AgentScope 的长期记忆模块 + 斯坦福 Generative Agents 的反思循环。核心是三个方法:

  • record(event, importance) — 把事件文本向量化,存入 pgvector
  • retrieve(context, limit) — 余弦相似度检索相关记忆,注入 prompt
  • reflect() — 定期回顾近期记忆,用 LLM 提炼出更高层的认知,存回记忆库

pgvector 直接在 PostgreSQL 里跑,不需要单独的向量数据库。大约 100 行代码。

合计 200 行左右。达不到 AgentScope 记忆系统 100% 的能力——AgentScope 还有记忆重要性衰减、自动清理、记忆冲突检测等细节。但对一个多 Agent 游戏平台来说,80% 就够了。剩下 20% 可以在用户反馈中迭代。

且——圆桌辩论和狼人杀阶段完全不需要这个模块。消息数组 + 系统提示词就够了。记忆系统在剧本杀阶段才引入。

5. "偷"的方法论

从这次实践中提炼出来的方法:

第一步:广度扫描。 用关键词搜 GitHub,不预设筛选条件。20 个不嫌多。每个项目花 15 分钟:看 README,看核心接口定义,跑一下 demo(如果有的话)。

第二步:按维度分类评分。 不要笼统地说"这个项目好"或"不好"。拆成具体维度——agent 抽象设计得好不好?消息隔离机制怎么样?前端体验如何?同一个项目在不同维度上得分可能天差地别。

第三步:找设计出处。 对你要自建的每一个核心模块,找到一个"设计出处"——某个项目在这个维度上做得最好,你的设计直接借鉴它。不是抄代码,是理解它为什么这样设计,然后用自己的语言重新实现。

第四步:画偷取矩阵。 一张表:行是模块,列是出处项目 + 偷什么 + 怎么翻译到你的栈。这张表就是你的设计文档——每一行解释了一个核心模块的设计来源。

第五步:标记时间线。 不是所有模块都需要一开始就偷。记忆系统可以晚做,频道系统必须早做。在偷取矩阵上标注每个模块的引入阶段,按需偷取。

好的设计不是从零想出来的。它是从大量现有实现的共性中读出来的。"偷"不是偷懒——是站在别人的肩上,用自己的腿走路。


这是 Agora 系列的最后一篇。四篇文章覆盖了从初始愿景赛道分析技术选型到架构设计的完整决策链。如果你也在做多 Agent 相关的项目,这几个框架可以直接复用:如何识别空白赛道、如何做 fork vs 自建的加权决策、如何系统性地萃取开源项目的设计模式。

Agora 的代码在 GitHub 上。


© Xingfan Xia 2024 - 2026 · CC BY-NC 4.0