和 AI 讨论这篇文章
ChatGPTClaude

Agora 每个核心模块的设计都有出处

Agora · 第 3 篇 / 共 7 篇


第一篇讲了这个赛道基本是空的,第二篇讲了为什么不 fork。这篇讲具体怎么干。不 fork,但把这些项目里好的设计全偷过来,讲清楚从哪个项目偷了什么,又是怎么拼成一个平台的架构。

这轮调研做下来,我慢慢想清楚了几件事。

  1. 偷的是设计模式,不是代码。代码一行不抄,只看它为什么这么设计。
  2. 每个核心模块最好都有个出处,你说得清它为什么长这样,是从哪个项目学来的。
  3. 架构其实用不着从零去想。二十多个项目翻一遍,大家共同的东西就摆在那,读出来就行。

真正值得偷的就六处

二十多个项目看下来,真正值得偷的设计就集中在六个地方,下面一个一个讲。

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 年那篇 Generative Agents 论文,基本就是 agent 记忆系统的标杆。它的核心是一个三步循环,observe → reflect → plan。

observe 是 agent 看到了什么事,就存进记忆流。reflect 是隔一段时间回头翻翻最近的记忆,用 LLM 从里面提炼出高一层的认识。plan 是根据记忆和反思出来的东西,规划下一步做什么。

关键在 reflect 这一步。没有反思,agent 的记忆就是一本流水账,「张三说他是好人」「李四投了王五」。有了反思,它就能得出高一层的判断,比如「张三最近三轮都在替李四说话,他们可能是同伙」。

对多 agent 游戏来说,这个真少不了。剧本杀里 agent 得把线索交叉比对,自己推出一套说法,再调整怀疑谁;TRPG 里 AI 地下城主得记住玩家三个 session 前做过什么选择。没有反思循环,agent 就是个只看最近 N 条消息的复读机。

但是这个模块第一阶段用不上。圆桌辩论和狼人杀,一个消息数组就够了。等做到剧本杀再把反思循环加进来,完全来得及。

AgentScope 狼人杀 sample → 结构化决策

AgentScope 的狼人杀 sample 里有个细节,我想单独讲讲。它用 Pydantic schema 来限制投票能投给谁。

每次投票,schema 都会现生成一个枚举,里面只放当前还活着的玩家名字。agent 调 LLM 的时候把这个 schema 传进去,LLM 就只能从这个枚举里挑。

这样 agent 根本投不了死人,编不出不存在的名字,也没法说「我弃权」,除非你把弃权也放进枚举里。你在 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。它做的事不复杂,就是让多个 agent 一起把任务干完,但交互方式选得特别对,组 agent 团队就跟建微信群一样。你创建一个 agent,就跟加了个联系人一样;要组个团队就建个群;派任务就往群里发消息。

这个比方厉害在,你完全不用教用户什么是 agent 编排、什么是消息路由、什么是频道隔离,因为群聊怎么用,所有人都懂。

这一点也直接影响了技术选型。想做出 Accio Work 那种水平的 UI,Python 全栈做不到,Gradio 和 Streamlit 就算做到头,离群聊那种交互也还差得远。上一篇加权打分的时候,Web UI 这一项给 fork 方案打了低分,根本原因就在这。

拼在一起:三层架构

六个出处拼起来,就是一个三层架构:

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

模式层对应 ChatArena 的 Environment。每个模式就是一个配置包,里面定好角色模板、频道规则、流程配置和决策格式。以后要加一种新游戏,不用动平台代码,写一份新的模式配置就行。

平台核心对应 ChatArena 的 Arena。agent 用 AgentScope 的 reply/observe 双接口。频道用 MsgHub 的嵌套作用域加动态订阅。流程控制从简单到复杂做四种,自由发言,轮流,状态机,层级,用哪种由模式来定。记忆分两级,session 里面做消息压缩,跨 session 做向量检索加反思,用到了再加载。

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

这样每一层怎么设计的都有出处,随便挑一个模块问它为什么长这样,都能指到一个具体的项目上。

频道系统是最关键的一块

六个模块里,频道系统最关键。多 agent 交互和「一堆 AI 在同一个聊天框里说话」,根本的差别就在信息隔离上。没有频道隔离,狼人杀跑不了,因为狼人的讨论不能让平民看到;剧本杀跑不了,因为每个角色手里都有只有自己知道的线索;三省六部也跑不了,因为部门之间有信息壁垒。

AgentScope 的 MsgHub 帮我验证了三个核心机制。

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

但 MsgHub 是写在代码里的,搬到平台上还得再加一层,频道规则由模式来定,跑起来以后由平台自动管。模式说「狼人杀需要五个频道,订阅关系如下」,平台就在房间启动的时候自动把这些频道建好,把订阅关系绑好,游戏状态一变,再跟着调整。

这块核心代码大概 300-500 行。这么关键的模块我不会交给第三方,频道系统本身就是这个平台最核心的竞争力,得自己写。

记忆系统

上一篇说的「10% 遗憾」,主要就在记忆系统上。真拆开看了一遍,发现遗憾比我想的小。

会话记忆压缩。 九人狼人杀跑完一局,大概有 200 多条消息,全塞进 context 的话,一半都是前几轮重复的讨论。办法直接偷 AgentScope 的,设个阈值,比如 40 条,超过了就用便宜的模型(Haiku 级别的就行)把旧消息总结成一段摘要,agent 的 context 就变成「摘要 + 最近 15 条消息」。这块大概 80 行代码。

长期语义检索。 这个管跨 session 的记忆,比如 TRPG 角色要记住三个 session 前做过的选择,剧本杀 agent 开新一局时要带上上一局学到的策略。做法是偷 AgentScope 的长期记忆模块,再加上斯坦福那个反思循环。核心就三个方法:

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

pgvector 直接跑在 PostgreSQL 里,不用再单独起一个向量数据库。大概 100 行代码。

两块加起来 200 行左右。当然比不上 AgentScope 记忆系统 100% 的能力,它还有记忆重要性衰减、自动清理、记忆冲突检测这些细节。但对一个多 agent 游戏平台来说,有 80% 就够用了,剩下 20% 等用户有反馈了再慢慢补。而且做圆桌辩论和狼人杀的时候完全用不上这个模块,消息数组加系统提示词就够了,到剧本杀阶段才会把记忆系统加进来。

偷的方法论

最后把这次的做法整理一下,以后调研别的方向应该也能照着干。大概五步:

第一步:先广撒网。拿关键词搜 GitHub,别事先设筛选条件。每个项目花 15 分钟,看 README,看核心接口怎么定义,跑一下 demo。

第二步:分维度打分。别笼统地说这个项目好还是不好,要拆开一项一项看。agent 抽象设计得怎么样?消息隔离做得怎么样?前端体验怎么样?同一个项目换个维度,分数可能天差地别。

第三步:给每个模块找出处。你要自己做的每个核心模块,都去找一个在这方面做得最好的项目,设计直接跟它学。学不是抄代码,是搞明白它为什么这么设计,再用自己的语言重新写一遍。

第四步:画偷取矩阵。就是一张表,每行是一个模块,列分别是出处项目、偷什么、怎么搬到你自己的栈上。这张表本身就是设计文档,每一行都说清楚了一个核心模块的设计是从哪来的。

第五步:标时间线。不是所有模块都得一开始就偷,记忆系统可以晚点做,频道系统必须早做。在矩阵上标清楚每个模块在哪个阶段加进来,用到了再偷。


这是 Agora 系列的最后一篇。四篇文章从初始愿景、赛道分析、技术选型一路讲到架构设计,整个决策过程都在这了。

Agora 的代码在 GitHub 上。

和 AI 讨论这篇文章
ChatGPTClaude

订阅更新

新文章发布时发到你的邮箱,不发别的。


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