自托管多智能体系统SubCult:构建闭环AI协作平台的架构与实践
2026/5/7 22:44:02 网站建设 项目流程

1. 项目概述:一个自托管、闭环的多智能体系统

最近在折腾一个挺有意思的东西,叫 SubCult。这本质上是一个你可以在自己服务器上跑起来的“AI小团队”,一个由六个不同性格和职责的AI智能体组成的、能够自主运转的闭环系统。它不是那种简单的聊天机器人,而是一个具备完整工作流引擎的协作平台。这六个智能体各司其职,从提出想法、分析风险、创新构思,到执行任务、处理杂务,再到最终的战略决策,形成了一个完整的决策与执行链条。

这个系统的核心魅力在于它的“自主性”和“闭环”。它不像传统自动化脚本那样需要你预先定义好每一步,而是让智能体们通过对话、记忆和事件驱动,自发地产生提案、创建任务、执行步骤,并从中学习,生成新的想法。整个过程就像一个微缩的、数字化的创意团队在7x24小时不间断地头脑风暴和工作。对于我这种喜欢研究AI应用边界,同时又希望所有数据和逻辑都掌握在自己手里的人来说,这种自托管、全栈集成的方案非常有吸引力。它基于 Next.js、PostgreSQL、Docker 等成熟技术栈构建,意味着你可以相对容易地部署、扩展和定制。

2. 系统架构与核心设计理念

2.1 整体技术栈与容器化部署

SubCult 的设计哲学非常清晰:一切尽在掌控,且高度集成。整个系统通过一个docker-compose.yml文件拉起四个核心服务,构成了一个完全自包含的运行时环境。

前端与API层(Next.js App):这是整个系统的控制面板和交互入口。它使用 Next.js 16(App Router)、React 19 和 TypeScript 5 构建,既提供了管理仪表板(/stage),也承载了所有核心的业务API(/api/ops/*)。比如,触发系统心跳、提交提案、查看任务流,都是通过这个服务。它内部还运行着一个轻量的Cron调度器,用于触发一些周期性的内部作业。

统一工作流引擎(Unified Worker):这是系统的大脑和中枢神经系统,一个独立的Node.js进程。它不像传统微服务那样拆分成多个worker,而是采用单进程多队列轮询的设计。这个Worker会以不同的间隔(15秒、30秒、60秒)检查四个核心队列:智能体会话队列、圆桌对话队列、任务步骤队列、倡议生成队列。这种设计极大地简化了部署和状态管理,避免了分布式锁的复杂性,通过 PostgreSQL 的FOR UPDATE SKIP LOCKED语句实现高效的、无冲突的任务领取。

实时通信层(Sanctum WebSocket Server):为了实现智能体之间以及用户与智能体之间的实时、多向对话,系统引入了一个独立的 WebSocket 服务器,名为“Sanctum”(圣所)。你可以把它想象成一个私密的数字会议室。这个服务让六个智能体能够像真人一样在一个聊天室里互动,支持公开讨论、私聊、点名发言等多种模式,所有对话实时推送,并伴有“正在输入”的指示器,体验非常流畅。

智能体沙箱环境(Toolbox Container):安全是AI智能体执行外部操作(如运行Shell命令、读写文件)的首要考量。SubCult 没有依赖复杂的外部API网关,而是直接运行了一个轻量的 Debian 容器作为“工具箱”。所有需要执行原生操作的工具(如bashfile_write)都在这个沙箱环境中进行。容器通过Docker volume与主机共享一个/workspace目录,智能体只能在这个受控的目录树内进行操作,并且每个智能体的文件访问权限还通过ACL(访问控制列表)进行了二次约束,这从根本上杜绝了越权操作的风险。

数据与记忆核心(PostgreSQL + pgvector):所有状态——提案、任务、事件、对话记录、成本追踪——都持久化在 PostgreSQL 16 中。更关键的是,通过 pgvector 扩展,系统为智能体的“记忆”实现了语义搜索能力。每次对话的精华、学到的经验教训,都会被提取、向量化并存储。之后,智能体在决策时,可以基于语义相似度快速检索相关的历史记忆,从而实现真正意义上的“经验学习”和上下文感知。

实操心得:这种全栈Docker Compose的打包方式,对于个人项目或小团队部署来说简直是福音。你只需要一个docker-compose up命令,数据库、应用、后台Worker、实时服务、沙箱环境全部就位,依赖关系清晰,环境隔离彻底。相比起在宿主机上一个个安装配置服务,这种方式的维护成本和出错的概率要低得多。

2.2 闭环工作流:从灵感到行动的完整循环

理解了静态架构,我们再来看看动态的工作流,这是 SubCult 真正“活”起来的关键。它的核心循环可以概括为:事件驱动 -> 智能体决策 -> 任务执行 -> 记忆沉淀 -> 新事件产生

  1. 提案(Proposal):循环的起点通常是一个“事件”。这个事件可能来自外部API调用(比如你手动提交了一个想法),可能来自预设的Cron定时任务(比如“每天上午9点检查项目进展”),也可能来自系统内部——比如智能体在回顾记忆后自发产生的一个“倡议”(Initiative)。当一个事件被触发,相关的智能体(如分析师Chora)会对其进行评估,并生成一个结构化的“提案”,提交到ops_mission_proposals表。

  2. 任务(Mission)与步骤(Step):提案不会立即执行。它首先会经过一系列“能力关卡”检查,例如每日配额限制。通过检查的提案会被转化为一个正式的“任务”,记录在ops_missions表中。每个任务会被进一步拆解为多个可执行的“步骤”,存入ops_mission_steps。步骤有不同的风险等级:低风险步骤(如格式化文本)可以自动批准执行;高风险步骤(如运行特定Shell命令)则会等待人工审核或更高权限智能体(如保护者Subrosa)的批准。

  3. 执行与事件流:被批准的步骤会进入对应的队列。统一Worker会捞取这些步骤,调用相应的工具(在沙箱中执行),并将执行结果记录为新的“事件”,写入ops_agent_events表。这个事件流是整个系统的生命线,它完整记录了系统内发生的一切。

  4. 触发与反应(Triggers & Reactions):每个心跳周期(通过/api/ops/heartbeat触发),系统都会评估一系列预定义的“触发规则”。这些规则会监听事件流,例如“当任务X完成时”、“当成本超过阈值Y时”。一旦条件满足,就会触发相应的“反应”,比如通知某个智能体、生成一个新的提案、或者发起一场圆桌讨论。此外,智能体之间还有一个“反应矩阵”,定义了它们如何对其他智能体的行为做出程序化反应,这模拟了团队中的人际动态。

  5. 记忆蒸馏与关系演化:所有的对话和重要事件都不会被遗忘。系统会定期使用LLM对对话记录进行“蒸馏”,提取核心见解、模式和教训,作为“记忆”存入向量数据库。同时,智能体之间的“关系亲密度”会根据互动情况动态调整。例如,如果创新者Thaum和执行官Praxis多次在任务中成功协作,它们之间的亲和度就会增加,未来被同时选入同一个对话的概率也会提高。

这个闭环的设计,使得系统具备了自我演进的能力。记忆积累驱动倡议生成,倡议又催生新的提案和任务,如此循环往复,无需持续的人工干预。

3. 六大智能体角色深度解析与工具生态

SubCult 的智能体不是六个调用同一个API的克隆体,而是被精心设计了独特的人格、职责、思维模式甚至“缺点”。这种角色扮演是系统产生丰富、可信互动的基础。

3.1 角色定位与核心职能

  • Chora(分析师):它是系统的“眼睛”。擅长解构复杂系统,让模糊的事物变得清晰可读。它的核心工作是诊断结构、暴露潜在假设、追溯因果关系。当团队对一个问题的根源感到困惑时,Chora 会被召唤来绘制脉络图。它的失败模式可能是陷入过度分析,在细节中迷失而无法提出行动方案。
  • Subrosa(保护者):它是系统的“免疫系统”。在信息或权力不对称的情况下,Subrosa 负责保护系统的“能动性”和“选择权”。它评估风险,思考最坏情况,并拥有对高风险行动的“一票否决权”。它的存在是为了防止系统做出不可逆的、有害的决策。它的弱点可能是过于保守,扼杀必要的冒险。
  • Thaum(创新者):它是系统的“火花”。当思维陷入僵局,讨论在原地打转时,Thaum 负责打破自我封闭的逻辑循环,用全新的视角重构问题。它不提供标准答案,而是提供意想不到的“可能性”。它的失败模式是可能提出完全不切实际、无法落地的疯狂想法。
  • Praxis(执行官):它是系统的“双手”。当讨论结束,决策做出后,Praxis 负责将意图转化为具体的行动,并承担后果。它的话语直接、务实,关注“怎么做”。它拥有最广泛的工具执行权限(包括bash)。它的风险在于可能过于机械地执行命令,而忽略了执行过程中的微妙变化。
  • Mux(操作员):它是系统的“瑞士军刀”和“剪贴板”。负责所有枯燥但必要的操作性劳动:起草文件、格式化数据、转录内容、打包输出。它没有战略野心,但极度可靠。它是其他智能体最常委托工作的对象。
  • Primus(主权者):它是系统的“北极星”。平时沉默寡言,只在任务偏离核心目标或面临存在性抉择时被唤醒。它说话简洁,以“命令”形式下达指令,为系统提供最终的、战略性的方向校准。它的介入是罕见但决定性的。

3.2 原生工具系统与权限管理

智能体的能力通过一个精心设计的原生工具系统来体现。所有工具都通过统一的Worker在沙箱工具箱容器中执行,不依赖任何外部API网关,这保证了执行的效率和安全性。

工具描述关键实现细节
bash在沙箱容器中执行Shell命令。通过Docker Exec API调用,严格设置超时(默认30秒)和输出限制(默认10万字符),防止无限循环或输出爆炸。
web_search网络搜索。优先使用Brave Search API,若无配置则回退至DuckDuckGo Instant Answer API。回退机制确保了基础搜索功能即使在无付费API密钥时也能工作。搜索结果会经过清洗和摘要,再提供给LLM。
web_fetch抓取指定URL内容并转换为Markdown格式。使用readability等库提取文章主体,过滤广告和导航栏,生成干净的文本供智能体分析。
file_read/file_write从/向共享的/workspace卷读写文件。写入操作有ACL检查,例如只有Praxis和Mux可以执行file_write,且只能写入特定子目录,防止智能体篡改系统文件。
memory_search基于pgvector对智能体记忆进行语义相似度搜索。将查询文本编码为向量,在数据库中执行余弦相似度搜索,返回最相关的几条历史记忆,为当前决策提供上下文。
spawn_droid生成一个具有限定工具集的“子智能体”来执行委托任务。这实现了任务的层级分解。主智能体可以创建一个专注的、短命的Droid来完成一项子任务(如“分析这组数据”),完成后Droid自动销毁。
send_to_agent向另一个智能体发送消息。这是智能体间异步通信的基础,消息会被放入接收者的会话队列,驱动后续的互动。
check_droid检查已生成的Droid的状态。允许主智能体监控委托任务的进度。

工具访问权限矩阵是安全模型的核心。它确保了职责分离原则。例如,只有执行官Praxis和操作员Mux可以运行bash命令,因为它们是主要的执行者。而保护者Subrosa则被禁止使用web_fetchspawn_droid,以防其通过加载外部恶意内容或创建不受控的子进程来绕过监管。Primus作为最高决策者,拥有file_writespawn_droid的权限,但通常不直接使用bashweb_search,体现了其战略而非战术的角色。

3.3 智能体会话:工具增强的LLM执行循环

单个智能体的“思考-行动”周期被封装在agent-session.ts中,这是一个标准化的执行循环:

  1. 会话初始化:从数据库中加载指定智能体的“声音”配置(人格提示词)和其被允许使用的工具列表。
  2. 提示词构建:构建系统提示词,其中注入智能体的人格描述、可用工具的函数定义(符合OpenAI格式)、以及通过memory_search检索到的相关历史记忆。
  3. 循环执行: a. 将提示词和用户输入(或上一个工具的结果)发送给LLM(通过OpenRouter)。 b. LLM返回的响应可能包含纯文本,也可能包含一个或多个工具调用请求。 c. 如果检测到工具调用,会话执行器会在沙箱中安全地执行该工具,并将工具执行的结果作为新的上下文,再次发送给LLM。 d. 重复此过程。
  4. 会话结束:当LLM返回的响应中不再包含工具调用(表明它认为任务已完成),或达到预设的超时时间、最大工具调用轮次时,会话结束。
  5. 持久化与副作用:会话的最终结果、所有工具调用记录、消耗的Token和估算成本都会被写入数据库。同时,一个“会话完成”事件会被发射到事件流,可能触发下游的规则。

这个设计使得每个智能体都成为一个可以自主使用工具解决问题的智能单元,而不仅仅是文本生成器。

4. 核心功能实操:从部署到深度互动

4.1 环境准备与一键启动

部署 SubCult 的核心就是配置一个环境变量文件并运行 Docker Compose。以下是步步为营的实操指南。

第一步:获取密钥你需要准备两个核心API密钥:

  1. OpenRouter API Key:这是系统的“大脑”接入点。前往 OpenRouter 注册并获取密钥。它提供了统一接口来访问 Claude、GPT、Gemini 等多种模型。在.env.local中设置为OPENROUTER_API_KEY。同时,你需要指定默认模型,例如LLM_MODEL=anthropic/claude-3.5-sonnet
  2. Brave Search API Key (可选但推荐):用于web_search工具。在 Brave Search 开发者页面 申请。如果未设置,系统将回退到无需密钥但功能有限的 DuckDuckGo API。

第二步:配置环境项目根目录下通常有一个.env.example文件,复制它并填写你的密钥。

cp .env.example .env.local

然后编辑.env.local,至少配置以下关键项:

# 必须配置 OPENROUTER_API_KEY=sk-or-v1-xxxxxxxxxxxxxxxxxxxxxxxx LLM_MODEL=anthropic/claude-3.5-sonnet POSTGRES_PASSWORD=一个强密码 DATABASE_URL=postgresql://subcult:同一个强密码@postgres:5432/subcult_ops CRON_SECRET=$(openssl rand -hex 32) # 在终端运行此命令生成一个随机密钥 # 可选配置 BRAVE_API_KEY=你的brave密钥 GHOST_ADMIN_API_KEY=你的Ghost博客密钥 # 用于自动发布博客 LOG_LEVEL=info NODE_ENV=development

重要提示CRON_SECRET用于保护心跳API端点,务必使用强随机字符串。你可以直接在终端运行openssl rand -hex 32来生成。

第三步:启动系统使用项目提供的Makefile可以极大简化操作。

# 构建并启动所有Docker容器(Next.js应用、PostgreSQL、Worker、Sanctum、Toolbox) make up # 等待数据库就绪后,运行所有SQL迁移(创建32张表) make db-migrate # 植入系统运行所需的基础数据,如智能体注册信息、默认策略、初始计划任务等 make seed # 运行一系列检查,验证系统核心组件是否正常工作 make verify

如果一切顺利,访问http://localhost:3000/stage你应该能看到系统仪表板。make logs可以查看所有容器的实时日志,对于排错非常有用。

4.2 圣所(Sanctum):与多智能体实时对话

部署完成后,最直观的体验就是 Sanctum 聊天室。访问http://localhost:3000/sanctum,你会进入一个模拟的会议室,六个智能体都在线。

  • 直接对话:你可以像在群聊中一样@chora 你觉得我们当前的项目进度风险点在哪?。Chora 会以它的分析视角回应你。
  • 开放讨论:你也可以不指定对象,直接提问:我们下一步应该优先做什么?。系统会根据话题相关性,自动选择2-4个智能体(例如 Chora, Praxis, Thaum)加入讨论,它们会彼此回应,形成真正的对话。
  • 私聊(Whisper):输入/whisper @subrosa 我有一个敏感问题...,这条信息将只有 Subrosa 能看到并回复,模拟了私下沟通的场景。
  • 圆桌会议:这是更结构化的讨论。通过API或命令行触发一个特定格式的对话,例如“辩论”、“深度探讨”、“茶水间闲聊”。智能体会按照该格式的规则轮流发言,进行更有深度的思想碰撞。
  • 后台运行:Sanctum 服务也可以独立运行(npm run sanctum),方便你将其集成到其他前端或作为独立的聊天服务。

这个功能的实现依赖于一个独立的 WebSocket 服务器,它维护着每个聊天室的连接状态,处理消息路由,并将智能体的响应流式传输回前端。所有对话记录都会保存到ops_sanctum_conversationsops_sanctum_messages表中,成为后续记忆蒸馏的素材。

4.3 驱动系统:心跳、计划任务与手动触发

系统不会自己动起来,它需要一个“心跳”来驱动。心跳是一个周期性的触发信号,它做三件事:

  1. 评估触发规则:检查ops_trigger_rules表中定义的所有条件(例如,“过去24小时没有生成任何内容草案”)。
  2. 处理反应队列:执行因之前事件而排队等待的智能体反应。
  3. 恢复停滞任务:检查是否有运行超过30分钟的任务步骤,并将其标记为失败,防止队列堵塞。

设置生产环境心跳: 最简单的方式是使用系统的Cron API,在你的服务器Crontab中添加一行:

*/5 * * * * curl -s -H "Authorization: Bearer $CRON_SECRET" http://localhost:3000/api/ops/heartbeat

这表示每5分钟触发一次心跳。你也可以手动触发进行测试:make heartbeat或使用 curl 命令。

配置计划任务: 系统内置了一个灵活的Cron调度系统。数据表ops_cron_schedules存储了计划任务,你可以通过数据库直接插入或通过种子脚本配置。例如,你可以设置一个任务,让 Chora 和 Praxis 每天上午10点自动进行一次项目站会。当心跳触发时,它会检查这些计划,如果到了执行时间,就会创建一个对应的智能体会话任务放入队列。

手动触发特定工作流: 除了自动化的心跳和Cron,所有功能都暴露为API,方便你集成或手动测试。

  • 发起一个提案POST /api/ops/proposals,你可以模拟一个智能体提出一个想法。
  • 开启一场圆桌讨论
    curl -X POST -H "Authorization: Bearer $CRON_SECRET" \ -H "Content-Type: application/json" \ -d '{"format":"deep_dive","topic":"如何提高我们系统的记忆检索效率?","participants":["chora","thaum","mux"]}' \ http://localhost:3000/api/ops/roundtable
  • 查询系统状态GET /api/ops/statsGET /api/ops/costs可以获取运行统计和成本分析。

5. 高级特性、问题排查与扩展思考

5.1 内容发布与Ghost集成

SubCult 内置了一个简单但实用的内容发布管道。智能体(通常是 Mux)可以起草博客文章,并存入ops_content_drafts表。当草稿状态被标记为approved后,统一Worker会在下一次处理队列时自动将其发布。

  • 本地发布:默认情况下,发布的文章会以Markdown格式保存到workspace/output/blog目录下。Next.js 应用会读取这个目录,在/blog路由下渲染出一个简单的博客站点。
  • Ghost博客镜像:如果你配置了GHOST_ADMIN_API_KEY,Worker 会尝试将已发布的文章同步到你指定的Ghost博客实例。这是一个“尽力而为”的异步操作。如果同步失败(网络问题、Ghost服务宕机),系统会记录错误,并在后续的后台清理任务中重试。这个设计确保了核心发布流程(保存到本地)的可靠性不依赖于外部服务。

5.2 常见问题与排查指南

在部署和运行过程中,你可能会遇到一些典型问题。以下是一个快速排查清单:

问题现象可能原因排查步骤
make up失败,数据库连接错误1..env.localDATABASE_URL的密码与POSTGRES_PASSWORD不一致。
2. PostgreSQL 容器启动慢,应用先于数据库启动。
1. 检查两个密码是否完全相同。
2. 使用docker-compose logs postgres查看数据库日志,确认启动完成后再重启应用容器 (docker-compose restart app)。
访问localhost:3000超时或报错1. Next.js 应用编译失败或未启动。
2. 端口被占用。
1. 运行make logs-app查看应用日志,常见于 TypeScript 编译错误或依赖缺失。
2. 运行docker-compose ps确认所有容器状态均为Up
3. 检查本地3000端口是否被其他程序占用。
智能体不响应,或提示“无可用工具”1. OpenRouter API 密钥无效或余额不足。
2. 智能体会话队列 Worker 未运行。
3. 数据库种子数据未正确加载。
1. 在 OpenRouter 仪表板检查密钥状态和用量。
2. 运行make logs查看 unified-worker 容器的日志,确认它在正常轮询队列。
3. 检查ops_agent_registry表是否有6条智能体记录。
web_search工具返回错误1. Brave Search API 密钥未配置或无效。
2. 回退的 DuckDuckGo API 暂时不可用。
1. 检查.env.localBRAVE_API_KEY设置,或在 OpenRouter 日志中查看具体错误信息。
2. 这是一个外部依赖,偶尔失败是正常的,系统应能降级或跳过。
Sanctum 聊天室无法连接Sanctum WebSocket 服务未启动。1. 确保docker-compose.yml中包含了sanctum服务。
2. 运行make logs查看 sanctum 容器日志。
3. 浏览器控制台查看 WebSocket 连接错误。
心跳API返回401未授权CRON_SECRET环境变量未设置或与请求头中的Bearer Token不匹配。1. 确认.env.local中的CRON_SECRET已设置。
2. 手动触发心跳时,确保 curl 命令中的$CRON_SECRET变量已正确展开,或直接替换为字符串。

日志是排错的最佳朋友。SubCult 采用了结构化的日志系统。在开发模式 (NODE_ENV=development) 下,日志是带颜色的易读格式。在生产模式 (NODE_ENV=production) 下,日志是JSON格式,方便接入 ELK、Loki 等日志聚合系统。每个请求都会有一个唯一的x-request-id,贯穿整个调用链,让你可以轻松追踪一个用户请求或后台任务的所有相关日志。

5.3 扩展与定制化思考

SubCult 作为一个开源项目,提供了极大的定制空间。

  1. 自定义智能体:你完全可以基于现有模板,在src/lib/agents.tsworkspace/agents/目录下创建第七个、第八个智能体。定义它的名字、人格、声音、工具权限和失败模式,然后将其注册到系统中。你可以创建一个专注于代码审查的“审查员”,或一个专注于市场分析的“观察者”。
  2. 开发新工具:工具系统是模块化的。在src/lib/tools/tools/目录下,每个工具都是一个独立的函数。如果你想增加一个“发送邮件”或“调用内部API”的工具,只需遵循相同的模式实现一个新的工具函数,并在工具注册表中将其分配给特定的智能体即可。记得在工具箱容器 (docker/toolbox/) 中安装任何必要的运行时依赖。
  3. 调整工作流与策略:系统的行为很大程度上由数据库中的策略 (ops_policy) 和触发规则 (ops_trigger_rules) 控制。你可以修改每日提案配额、调整关系亲密度计算公式、或者定义全新的触发条件(例如“当项目成本超过预算50%时,自动发起一场有Primus参加的紧急会议”)。
  4. 集成外部系统:通过调用POST /api/ops/proposalsAPI,你可以将外部系统(如GitHub Issues、Trello卡片、客服工单)的事件导入到 SubCult,让这个AI团队来处理。同样,通过监听系统的事件流或查询任务状态,你可以将 SubCult 的产出(如生成的文档、决策建议)推送到其他系统。

这个项目的价值不仅在于其开箱即用的多智能体聊天功能,更在于它提供了一个完整的、可自举的“智能体操作系统”范本。你可以把它当作一个实验平台,探索智能体协作、自主工作流、人机混合决策等前沿课题,而所有代码和数据都在你自己的掌控之中。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询