分层架构
Zhin.js 由几十个小包组成,但写 Bot 和插件时,你几乎只需要安装和导入 zhin.js。本页解释这些包怎样分层、为什么这样分,适合想理解内部原理或参与贡献的读者。
先记住三件事
- 对外只有一个入口。
zhin.js重新导出插件作者需要的 API:definePlugin从zhin.js导入,defineCommand等从zhin.js/command、zhin.js/middleware等子路径导入。 - AI 是可选的。 默认安装只有 IM 核心(不到 10MB);
@zhin.js/agent、@zhin.js/ai是可选依赖,通过zhin.js/agent、zhin.js/ai使用。 - 依赖只能向下。 上层包可以依赖下层,下层永远不知道上层的存在。这条规则由
pnpm check:architecture在 CI 中强制检查,不是口头约定。
| 你要做的事 | 依赖哪里 |
|---|---|
| 写 Bot、普通插件 | 只依赖 zhin.js |
| 写 AI 工具 | 再加 @zhin.js/tool(npx zhin setup --ai 会处理) |
| 写一种新的能力类型(Feature) | 只依赖 @zhin.js/feature-kit / @zhin.js/plugin-runtime,不要依赖 @zhin.js/core |
| 给框架本身贡献代码 | 遵守下文的分层规则 |
分层总览
依赖关系以各包的 package.json 为准。从下往上读这张图:
底座是两个彼此独立的零依赖契约:@zhin.js/plugin-runtime 提供 generation、snapshot、dispose 与 token;@zhin.js/im-contract(含 /interaction 子路径)提供传输中立的身份、消息段、会话事件、投递语义与用户交互的请求及结论。往上一层,@zhin.js/feature-kit 只依赖运行时底座,提供 Feature provider 的注册、发现与投影机制。Feature 层各包(adapter / command / component / middleware / handler / tool / skill / …)只依赖这些底层契约,彼此不互相依赖。
再往上,@zhin.js/core 把 adapter / command / component / middleware 四个 Feature 和 kernel 组装成 IM 层(Plugin、Adapter、Endpoint、消息收发)。门面包 zhin.js 把核心包重新导出为统一入口——插件作者只需 import { ... } from 'zhin.js'。@zhin.js/agent、@zhin.js/ai 等是可选 peer 依赖,默认安装只含 IM 核心,AI 按需加装。
Adapter、Endpoint 与单次消息
Plugin 正常注册 command、component、page、middleware 等能力,Adapter 只是其中的 IM Channel 能力。Adapter<E, Config, Events> 基类统一持有所属账号的 desired 状态、连接代次和操作租约;平台子类负责创建 SDK Endpoint 及平台特殊逻辑。AdapterIndex 提供目录与路由,不另建 Controller 或 Worker 管理账号。
平台 Endpoint 继承真实 SDK 或实质协议 Client,通过 endpointSpec 实现框架规范并保留原生 SDK API。Endpoint 是结构 interface;唯一构造入口是 defineAdapter({ createAdapter }),平台类型只注册到实际 AdapterRegistry,不保留旧基类、紧凑 create 或 Client token。
Core 从实际入站实例捕获单次操作 Session,提供 Adapter、Endpoint、场景、发言者、规范消息与原生事件。Command、Handler、Middleware 读取同一数据的作者绑定视图;平台类型通过开放注册表与 adapter: 'icqq' 推导。这个操作 Session 与持久化的个人 AI Session 分开。
账号的运行时配置加密持久化,账号停止/重连只取消目标实例的操作,Root generation 与其他账号保持运行。框架回复和群管端口持有具体 incarnation 租约,不能在停止后改用同名新连接。私有账号仓储只交给所属 Feature 的构建阶段,不进入公开 snapshot 或普通插件资源。详细合同与实施验收见重构方案。
各层职责
| 层 | 包 | 职责 |
|---|---|---|
| 基础层 | basic/logger schema schedule database(亦由 @zhin.js/kernel 统一再导出) | 日志、配置校验、定时、数据库,零/近零依赖 |
| 运行时底座与 IM 契约 | @zhin.js/plugin-runtime(含 /im-contract 与 /interaction,用户从 zhin.js 导入) | generation 事务、快照租约、handoff、显式能力图(definePluginCapabilityGraph),以及 Endpoint、会话、消息段与结构化用户交互契约 |
| Feature 机制 | @zhin.js/feature-kit | 声明 Feature provider、按约定发现能力、投影成运行时索引 |
| 内核 | @zhin.js/kernel | 插件系统、错误体系与基础机制重导出,无 IM 概念 |
| Feature 层 | @zhin.js/adapter command component middleware … | 一类能力的契约(如 defineAdapter)+ 投影索引(如 AdapterIndex) |
| AI 引擎 | @zhin.js/ai(含 provider-auth 与 model-registry) | Provider、认证、模型注册表、agentLoop、会话、记忆,无 IM 概念 |
| Agent Harness | @zhin.js/harness | 多 Agent 协作微内核:Actor 串行信箱(Mailbox)、权限收窄(Authority)、守恒预算账本(BudgetLedger)、确定性准入(GuardKernel)、共享黑板(Blackboard)与唯一事件账本(engagement_events Journal) |
| IM 层 | @zhin.js/core(含 handler、actor 与 @zhin.js/core/runtime IM Host 安装器) | Plugin、消息入站脊柱(inbound-spine)、出站链路、命令分发、多 Bot 协作场所(Venue)及 Database/Outbound/Inbox Host 实现 |
| Agent | @zhin.js/agent(含 @zhin.js/agent/runtime Agent/Inference Host 运行时) | 统一信箱入站脊柱(unified-mailbox-spine)、多模型编排、协作工具、工具安全策略、MCP client |
| Console | @zhin.js/console-protocol(含 contract / review-kit)、@zhin.js/pagemanager、@zhin.js/client | Server-Driven Blueprints(definePageBlueprint)、多端投影(Web / IM Card / CLI TUI)、Console 外壳渲染(renderConsoleShell)与浏览器 SDK |
| 门面 | zhin.js | 再导出 core 创作面;zhin.js/agent、zhin.js/ai 子路径按需暴露 |
| 运行时 | @zhin.js/runtime(含 /config-file、/isolate 子路径) | RootRuntime:扫描项目图、组合配置、执行 generation 事务、HMR 及通用 Host 生命周期 |
| 装配 | @zhin.js/cli | composition root:zhin runtime start 把各域 Host 装配成进程,并持有 HTTP/Protocol(MCP、A2A)/Speech/HTML-Renderer/Schedule 等通用 Host 安装器(basic/cli/src/plugin-runtime/*-host-installer.ts) |
图展示主要依赖,完整依赖仍以各包的 package.json 为准。允许的分层边界由 scripts/check-architecture-layers.mjs 执行;新增依赖时同时核对文档与门禁,不能只修改其中一处。
Composition root:@zhin.js/cli
框架本身不"启动"任何东西——各层只提供机制。把机制组装成一个可运行进程的入口集中在 @zhin.js/cli(basic/cli/src/commands/runtime.ts 与 basic/cli/src/plugin-runtime/),这是全仓库唯一允许跨层组合所有包的 composition root。IM 与 Agent 的领域 Host 实现已下沉到所属领域包(@zhin.js/core/runtime、@zhin.js/agent/runtime 与 @zhin.js/pagemanager),CLI 不直接持有领域状态机;HTTP、Protocol、Speech、HTML-Renderer 等通用 Host 的安装器留在 CLI 自己的 basic/cli/src/plugin-runtime/。
zhin runtime start 做的事:
- 用
createConfigDocument(@zhin.js/runtime)把唯一的 YAML/JSON Root 配置包装成带事务的ConfigDocumentPort; - 创建
RootRuntime(@zhin.js/runtime),注入模块加载器(开发模式为NativeDevelopmentModuleRuntime)、配置端口和 Root 资源安装器; - 通过
installResources组装各域 Host 级资源:HTTP/Protocol/Speech/HTML-Renderer Host(CLI 内的*-host-installer.ts)、Database/Outbound/Inbox/IM Host(@zhin.js/core/runtime)、Agent/Inference Host(@zhin.js/agent/runtime)、Console 外壳与 Blueprint 路由(@zhin.js/pagemanager); - 启动后挂上
HmrCoordinator,文件变更触发 generation 重载或进程重启。
用户项目的入口因此可以极小。examples/minimal-bot/package.json:
{
"name": "minimal-bot",
"type": "module",
"scripts": {
"dev": "zhin runtime start",
"start": "zhin runtime start --mode production --no-watch"
},
"dependencies": {
"zhin.js": "workspace:*"
},
"zhin": {
"protocol": 1,
"type": "plugin",
"entry": "./plugin.ts",
"engine": "^1.0.0",
"runtime": "trusted",
"features": [],
"plugins": []
}
}项目本身就是一个插件(Root Plugin),CLI 负责发现并启动它。插件清单字段的完整解释见 插件模型,配置的声明与校验见 配置即数据。
分层规则的推论
写 Feature(新能力类型)时只依赖 feature-kit / plugin-runtime,不要 import core。 kernel、ai 不知道群、私聊这些 IM 概念。im-contract 定义 IM 契约,Adapter 和 Core 消费这些契约。 Host 均由 CLI 装配,但不属于同一依赖层:HTTP / WebSocket Host 只依赖基础服务、 Plugin Runtime、HTTP 契约和 Console wire 契约,不依赖 Core 或 Agent;MCP / A2A Host 可以消费 Core、AI 与 Kernel,通过 HTTP 契约接入路由,不依赖具体 HTTP Host 实现。 插件不直接依赖 Host 进程。
@zhin.js/harness 仅依赖基础契约与内核,不感知具体 IM 平台或 LLM 实现;@zhin.js/core 的 venue/ 模块负责多 Bot 群聊场所的路由与投影,@zhin.js/agent 将本地与跨实例(@zhin.js/a2a)Actor 协作统一接入 Harness;该单向依赖边界由 pnpm check:architecture 强制检查。
JSX 与展示组件
@zhin.js/jsx 提供惰性 JSX tree 与 HTML serializer,仅依赖基础 im-contract 的类型。Core 默认把 JSX 排版成有序 Segment;只有 <html> / <Html> 子树转为 segment.html。小写原生标签具有类型提示且无需导入,注册组件可按名字作为标签使用;zhin.js/jsx 是作者门面。可选 @zhin.js/components 使用 JSX 基础包及 Markdown/代码高亮依赖,展示位接受 JSXRenderable,不依赖 Core、React 或图片引擎。图片工具只消费 HTML,平台差异由 Adapter 声明策略承接。
// 1. 原生消息段 JSX(自动降级为各平台原生卡片/按钮或文本菜单)
<card title="构建结果">
<text>全部测试已通过</text>
<button onClick={async () => '已触发发布'}>发布到生产</button>
</card>
// 2. HTML 子树(经 @zhin.js/html-renderer 渲染为高清图片段发出)
<html>
<div style={{ padding: '24px', background: '#0f172a', color: '#f8fafc' }}>状态看板</div>
</html>可选 @zhin.js/tailwind 使用官方 Tailwind 编译器将静态工具类转换为内联样式,输出可用于 JSX、组件局部样式和共享主题。它不依赖 Core、组件库或渲染引擎;JSX 基础包也不加载它。图片渲染统一由可选 @zhin.js/html-renderer 使用 @pixel.js/shotium 完成,不再维护 Satori 图片工具包。