跳到主要内容

架构概览

AISCouncil 是一个完全在浏览器中运行的零托管机器人管理平台。生产输出是一个由 80 个模块化源文件组装而成的单个 index.html 文件(约 980 KB)。没有外部运行时依赖 —— 只有 JavaScript 和 WASM 交付到浏览器。

单文件架构

应用程序是一个单一的自包含 HTML 文件。在开发期间,它被拆分为 src/ 部分,以便更小的上下文窗口和专注编辑。build.sh 脚本按严格顺序将所有 80 个部分串联回 index.html

src/shell-head.html        \
src/shell-style.html |
src/shell-body.html |
src/core-boot.js |
src/core-auth-main.js | build.sh
... | ---------> index.html (~980 KB)
src/settings-main.js |
src/miniprograms.js |
src/pwa.js |
src/shell-bottom.js /
注意

永远不要直接编辑 index.html。始终编辑 src/ 中的相应文件,然后运行 ./build.sh 重新组装。

全局命名空间

所有模块在 window.AIS 全局命名空间上注册。命名空间在 src/core-boot.js 中初始化:

window.AIS = { version: "2.0.0", type: "aiscouncil" };
AIS.PLATFORM_VERSION = "1.0.0";
AIS.ABI_VERSION = 1;

跨模块通信使用轻量级事件总线:

AIS.on("event", handler); // 订阅
AIS.off("event", handler); // 取消订阅
AIS.emit("event", data); // 发布

模块系统:AIS.lazy()

模块使用 Qwik 风格的延迟水化模式。工厂函数被推迟,直到通过 AIS 上的属性 getter 首次访问模块:

if (!AIS.Council)
AIS.lazy("Council", function () {
"use strict";
// 模块代码在这里
return { run, renderCouncilMessage, estimateCost };
});

首次访问时(例如 AIS.Council.run()),getter 触发,执行工厂,用返回的模块对象替换自身,并返回它。后续访问直接访问普通值,没有开销。

WASM 内核模块仍然可以在首次访问之前通过设置 AIS.Council = wasmModule 来覆盖 JS 模块,因为该属性定义为 configurable: true

模块类别

核心模块(始终加载)

核心模块在单个 <script> 块内定义(core-boot.jscore-end.js)。它们在页面加载时急切执行,形成应用程序的基础。

模块文件用途
AIS.Authcore-auth-main.js, core-auth-local.js, core-auth-init.jsWebAuthn/通行密钥 + OAuth 登录、会话管理、身份验证 cookie
AIS.Billingcore-billing.js订阅级别、试用、托管计划
AIS.Codeccore-codec.jsBase80 编码、VLQ 版本控制、deflate 压缩
AIS.Storagecore-storage.jsIndexedDB + 可选 SQLite WASM、离线优先
AIS.Providerscore-providers.js, core-providers-builtin.jsLLM 提供商注册表、SSE 流式工厂
AIS.UIcore-ui.jsDOM 工具、markdown 渲染器、toast 通知
AIS.Sessioncore-session.js机器人会话 CRUD(IndexedDB 支持)
AIS.Chatcore-chat.js聊天历史、流式消息发送/接收
AIS.Configcore-config.js机器人配置面板绑定、URL 同步
AIS.Appcore-app-botlist.js, core-app-switch.js, core-app-events.js, core-app-search.js, core-app-init.js应用程序控制器、初始化、路由、机器人管理

WASM 可替换模块(延迟加载)

每个 WASM 可替换模块位于自己的 <script> 块中,使用 AIS.lazy() 模式。它们只在首次访问时执行。

模块文件用途
AIS.Registryregistry.js社区模型注册表、24 小时缓存、GitHub 回退
AIS.Gridgrid.js模型表格/卡片渲染器(从 TypeScript 编译)
AIS.Councilcouncil.js多模型审议引擎(7 种风格)
AIS.Wizardwizard.js双模式首次运行设置向导
AIS.Visionvision.js视觉模型的图像输入(粘贴/上传)
AIS.Memorymemory.js持久化每机器人键值记忆
AIS.ImageGenimagegen.js图像生成(DALL-E、Grok Imagine、OpenRouter)
AIS.Toolstools.js工具/函数调用格式标准化
AIS.Remindersreminders.js通过 /remind 命令的定时消息
AIS.Themesthemes.js视觉主题系统
AIS.Templatestemplates-registry.js系统提示词模板、欢迎屏幕
AIS.ModelPickermodel-picker.js可排序的模型浏览器

基础设施模块(延迟加载)

模块文件用途
AIS.ModuleLoadermoduleloader.js热交换模块生命周期、OPFS 缓存
AIS.Pluginsplugins.js插件系统:清单验证、钩子
AIS.MCPmcp.js模型上下文协议(工具 + 资源)
AIS.Channelschannels-core.js, channels-whatsapp.js, channels-adapters.js, channels-stubs.js渠道适配器(Telegram、Discord、Matrix、Slack、WhatsApp)
AIS.Sandboxsandbox.jsWASM 工具沙箱(Pyodide、QuickJS、SQLite)
AIS.Publishpublish.jsSEO + 静态 HTML 发布
AIS.Perfperf.js性能监控
AIS.P2Pp2p.js通过 WebRTC + CRDT 的 P2P 协作

平台模块

模块文件用途
AIS.Settingssettings-main.js全局设置对话框(所有部分)
AIS.I18ni18n.js国际化
AIS.MiniProgramsminiprograms.js小程序运行时(沙箱 iframe)
AIS.Docsdocs.js内联文档查看器
AIS.Profilesprofiles.js多模型配置文件/委员会模板
AIS.Croncron.js基于浏览器的调度器

CSS 架构

应用程序使用无类 CSS 方法。样式针对语义 HTML 元素和 ID,而不是类。

选择器类型示例用例
ID#header, #sidebar-left, #config-body唯一布局容器
语义元素#messages > article, article menu button消息、操作、导航
数据属性[data-from="user"], [data-variant="primary"]变体(消息角色、按钮类型)
状态类.active, .collapsed, .mobile-openJS 切换状态
组件类.council-member-row, .status-dot动态 JS 创建的组件
实用类.f1, .sc, .dim一次性内联样式替换

使用的语义 HTML 元素:<header><main><aside><section><footer><nav><article><menu><details><dialog><output>

信息

类仅用于三种情况:状态切换(.active.collapsed)、动态 JS 组件(.council-member-row)和实用简写(.f1.sc)。如果您可以用语义选择器或数据属性定位元素,请这样做而不是添加类。

存储架构

存储分为同步和异步存储:

存储原因
localStorage(同步)ais-theme, ais-apikey-*, ais-user, ais-ollama-endpoint启动时需要同步读取
IndexedDB(异步,无限制)ais-bots, ais-chat-*, ais-addon-manifests大数据,无 5 MB 限制
SQLite WASM(可选,OPFS)二进制 blob延迟加载以进行并发访问

首次启动时,AIS.Storage.init() 自动将数据从 localStorage 迁移到 IndexedDB。

提供商架构

提供商通过 AIS.Providers.register() 注册。大多数提供商使用共享的 openaiCompatible() SSE 流式工厂,它处理:

  • 服务器发送事件解析
  • 逐 token 流式回调
  • AbortController 信号支持
  • 从响应头计算 token 数

六个内置提供商在 core-providers-builtin.js 中注册:

提供商API 风格身份验证说明
Anthropic原生(Messages API)x-api-key自定义流式格式
OpenAIOpenAI 兼容Bearer token标准 SSE
xAIOpenAI 兼容Bearer tokenGrok 模型
Google Gemini原生(Gemini API)?key= 查询参数避免 CORS 预检
OpenRouterOpenAI 兼容Bearer token300+ 模型,免费额度
OllamaOpenAI 兼容本地 LLM,自动检测模型

所有 API 密钥本地存储(localStorage['ais-apikey-{provider}'])。密钥直接从浏览器发送到提供商 —— 永远不通过任何服务器代理。

设计原则

代码库遵循每个更改必须满足的严格设计原则:

  1. HTML5 原生优先 —— 在 JS 等效之前使用 <dialog><output>hidden 属性、CSS 动画。
  2. 零轮询 —— 没有 requestAnimationFrame 循环、没有 setInterval、没有 setTimeout 用于动画。仅事件驱动。
  3. 被动监听器 —— 所有不可取消事件使用 { passive: true }
  4. 事件委托 —— 容器上的单个监听器,而不是动态列表中每项的监听器。
  5. 14px 最小字体 —— 所有文本必须可被视觉 LLM 读取。没有低于 14px 的文本。
  6. VLM 友好布局 —— 48px+ 点击目标、大切换开关、高对比度。设置和菜单在 1920x1080 上无需滚动即可显示所有主要项目。
  7. 减少动画 —— @media (prefers-reduced-motion: reduce) 禁用所有动画。
  8. CSS 包含 —— 侧边栏使用 contain: strict,可滚动列表使用 content-visibility: auto
提示

在添加任何功能之前,问四个决策门问题:

  1. 浏览器能否原生执行此操作?
  2. 这需要服务器吗?(如果是,使其可选。)
  3. 这能否扩展到每设备 100 万个机器人?
  4. 内存和 CPU 成本是多少?

WASM 内核

可选的 WASM 内核(kernel/)用 Zig 编写,编译后约 5.5 KB。它为模块系统提供低级原语:槽管理、钩子分发、环形缓冲区 I/O 和预写日志。

内核在 64 MB SharedArrayBuffer 上运行,定义了模块段、环形缓冲区、WAL 和暂存区的内存区域。JS 模块可以在运行时被 WASM 等效模块替换,而不会破坏延迟加载契约。

API 架构

平台针对不同关注点使用分离的 Worker:

Worker用途
aiscouncil-apiapi.aiscouncil.net计费、使用量、密钥分发、地理位置
aiscouncil-authauth.aiscouncil.netOAuth 回调、令牌验证

API 基础 URL 在启动时从域检测设置,可以通过 localStorage['ais-api-base'] 为本地开发覆盖。