Rust 模块(linked 与 DLL)
模块是 Agent OS 的打包与扩展单元。它可以向其中贡献
元素类型、注册表及其中的元素、配置包、服务、
HTTP 路由、后台任务和原生库——并打包为
linked crate(链接进二进制文件),或打包为 DLL(.dll/.so/.dylib),
在运行时加载。同一个 contribute() 管理这两种
传输方式。
这是一份实用指南。模块系统的活文档真相来源是
crates/module-system/runtime/agent_os_modules/README.md;设计与论证见
内部笔记 docs/22-modules.md、docs/25-module-abi.md、
docs/26-feature-modules.md、docs/27-core-modularization.md、
docs/30-dll-ready-modules.md(不在网站菜单中显示)。
32.1 Crate
| Crate | 角色 |
|---|---|
agent_os_modules |
组合模型:Module、ModuleBuilder、图、注册表、元素、服务、Access、module.yaml、ModuleLifecycle。不含 axum/kernel |
agent_os_abi |
稳定接口:AStr/AoSlice、Handle、OwnedObject、VtableHeader + 宏 #[abi_trait]/#[abi_struct]/abi_registry! |
agent_os_abi_macro |
这些宏的实现 |
agent_os_sdk |
轻量 SDK:RegistryHost、declare_module!、declare_plugin!、service(id)、Value、Json |
agent_os_web |
作为 stable 对象 Route 的 HTTP 路由 + WebModule(注册表 routes 的所有者) |
agent_os_host_api |
host 端口契约(agent_os.Log、.State、.Http、……)+ client::* |
agent_os_plugin_api |
稳定的 C-ABI「头」(用于 raw/legacy 插件) |
agent_os_modules_runtime |
驱动:激活/停用、state.json、version-manifest、bridge DLL |
agent_os_plugin_host |
DLL 加载以及 ABI → 宿主类型适配器 |
agent_os_module_install |
工件安装(download + sha256 + zip/tar.gz)、bundle 物化 |
DLL 模块的依赖规则:仅限 ABI-safe crate。禁止
agent_os_core、agent_os_tools、agent_web_bot、tokio、axum/hyper。
改用 host 端口。详见 docs/30-dll-ready-modules.md §2.2。
32.2 特征 Module 与 ModuleBuilder
use agent_os_modules::{Module, ModuleBuilder, RegistryType};
use serde_json::json;
struct Analytics;
impl Module for Analytics {
fn id(&self) -> &str { "analytics" }
fn version(&self) -> &str { "1.2.0" } // semver, по умолчанию "0.0.0"
fn requires(&self) -> Vec<String> { vec!["admin".into()] }
fn capabilities(&self) -> Vec<String> { vec!["agent_os.Secrets".into()] }
fn register(&self, b: &mut ModuleBuilder) {
b.declare_type(RegistryType::data("analytics.panel"));
b.provide_registry("admin.panels", "analytics.panel");
b.contribute("admin.panels", json!({ "title": "Аналитика" }));
}
}
构建生命周期分为三个阶段:
register—— 在ModuleBuilder中声明贡献(唯一必需的 阶段)。wire(&mut ModuleBuilder, &WireContext)—— 依赖最终图的可选贡献 (ctx.is_enabled、ctx.has_registry、ctx.has_service、……)。bind(&BindContext)—— 持有运行期间所需的服务 (ctx.service(id)→Arc<OwnedObject>),按拓扑顺序排在wire之后。
ModuleBuilder:
| 方法 | 用途 |
|---|---|
declare_type(RegistryType) |
声明新的元素类型 |
provide_registry(id, type_id) |
声明类型 type_id 的注册表 |
provide_data_registry(id) / provide_slot(id) |
JSON-data 元素注册表 |
provide_stable_registry(id, interface) |
接口 stable 对象注册表 |
contribute(id, json) |
贡献 JSON 数据 |
contribute_stable(id, OwnedObject) |
贡献 stable 对象 |
contribute_handle(Handle) |
同上,但注册表名称取自 vtable |
provide_service(id, obj) / provide_service_shared(id, Arc<..>) |
声明服务并放入 origin |
decorate_service(id, priority, obj) |
为服务添加层(装饰器) |
into_batch() |
用于实时 Registry 的快照 |
该特征的其他属性:optional_dependencies()(不传递性拉取)、always_on()(始终启用且最先;仅用于内核)、
dependencies()(形如 "admin@^2.1" 的版本要求)。
32.3 ABI 契约
跨越边界的接口(stable 对象注册表、服务)
由带 #[abi_trait] 的特征描述:
use agent_os_abi::abi_trait;
#[abi_trait(id = "DocSource", name = "documentation", version = 1)]
pub trait DocSource {
fn manifest(&self) -> Result<String, String>;
fn markdown(&self, id: &str, lang: &str) -> Result<String, String>;
}
语法受限(否则编译错误):
- 方法:
fn name(&self, args...) -> Result<Ret, String>;—— 同步; - 参数:基本类型(
i8..u64、f32/f64、bool)、&str/String、&[u8]/Vec<u8>、#[abi_struct](按值或&T)、其他 特征的 handle(<Trait>Handle、&<Trait>Handle、Owned<Trait>); Ret:()、基本类型、String、Vec<u8>、#[abi_struct](out)、handle;- 不使用 JSON —— 每个方法 = 类型化的
extern函数。
#[abi_struct] 标记跨越 ABI 的 #[repr(C)] 结构体:
use agent_os_abi::abi_struct;
#[abi_struct]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ItemRow { pub id: i64, pub score: f64, pub name: agent_os_abi::AStr }
该宏生成带 VtableHeader { trait_id, name, abi_version, size, destroy }, Copy-handle, owning-обёртку Owned<T> 的 #[repr(C)] vtable、
Borrowed<T>、dispatch-impl 以及 producer 擦除(into_owned()/into_object())。
handle 可以存放在 OwnedObject 中并通过边界传递。
导出对象的模块(用于 DLL)声明其工厂:
use agent_os_abi::abi_registry;
abi_registry! {
BuiltinSource as DocSource;
}
abi_registry! 生成 abi_objects() -> Vec<Handle>(用于 linked)和符号
ao_module_registry(用于 DLL)。
工具模块也以 stable 对象 agent_os.ToolEngine
(agent_os_tool_api)形式提供到注册表 tool.engines;宿主桥将每个对象
转换为 YAML 工具工厂。ToolEngine —— ABI v2:kind、describe(schema)和
execute,外加带
回调对象 agent_os.ToolProgress 的 execute_ctx(context_json, config, args, progress)。结果可以以「信封」
{ content, success, data, visual, cost } 返回。使用 sensitive 端口
(例如 agent_os.Fs/agent_os.Process)的模块需在 capabilities 中声明它们
(declare_module!(…, capabilities = ["agent_os.Fs"]))。带工具的 dual-transport
模块示例 —— agent_os_code(见 模块 code)。
32.4 HTTP 路由
路由是注册表 routes 中的一个 stable 对象 agent_os_web::Route:
use agent_os_web::{Route, RouteMeta, HttpRequest, HttpResponse, ACCESS_ADMIN};
struct ListAgents;
impl Route for ListAgents {
fn meta(&self) -> Result<RouteMeta, String> {
Ok(RouteMeta::new("GET", "/v1/analytics/agents", ACCESS_ADMIN))
}
fn handle(&self, _req: HttpRequest) -> Result<HttpResponse, String> {
Ok(HttpResponse::json(200, &serde_json::json!({ "agents": [] })))
}
}
Access —— public / user / admin;宿主以所需的 gate
挂载路由(require_admin / require_user),而未知级别不挂载
(fail closed)。对于典型场景有现成的构造函数:
contribute_get、contribute_route、contribute_asset(静态资源)、
respond_cached/weak_etag(ETag)、mime_for。注册表的所有者是 always-on
模块 agent_os_web::WebModule;可以在它之前贡献(元素
会被推迟)。
32.5 服务与 host 端口
领域服务(模块 ↔ 模块)是形如 Service 的注册表,恰好有一个
origin 和 N 个层:
// provider (origin)
b.provide_service("dbsvc", MyDb::new().into_owned().into_object());
// декоратор (слой), priority ниже — ближе к origin
b.decorate_service("dbsvc", 0, MetricsDb::new().into_owned().into_object());
// потребитель: requires: ["db-provider"], затем
fn bind(&self, ctx: &BindContext) {
if let Some(obj) = ctx.service("dbsvc") {
self.db.set(SharedDbService::from_object(obj).expect("interface mismatch"));
}
}
Host 端口(内核 → 模块)可通过
agent_os_sdk::service(id) + agent_os_host_api::client::* 以传输中立方式访问:
if let Some(svc) = agent_os_sdk::service(agent_os_host_api::ids::STATE) {
if let Some(state) = agent_os_host_api::client::state(svc.handle()) {
let _ = state.set("analytics", "last_run", "2026-01-01");
}
}
主要端口:Log、Config、State、Metrics、System、Agents、
Tools、Llm、Script、Http、Secrets、Fs、Process、Messages、
Events、Sessions、Tasks(+ Job)、RequestContext、Extension。完整
列表 —— agent_os_host_api::ids。
后台任务不运行自己的运行时:模块提供同步的 Job::step
(JSON { "continue": bool, "next_ms": u64 }),而宿主在自己的
运行时上、在模块的 capability 作用域下执行各步骤:
let tasks = agent_os_host_api::client::tasks(svc.handle()).unwrap();
let handle = tasks.spawn("analytics", my_job.into_owned())?; // my_job: impl Job
// …
tasks.cancel(&handle)?;
Capabilities。 敏感端口(Secrets、Http、Messages、
Sessions、control-plane 等)fail-closed:模块只有在
capabilities() / module.yaml → capabilities 中声明了 id 才能看到它们。宿主设置
每个模块代码入口(路由处理器、
Job::step、事件回调)周围的 capability 作用域。
32.6 module.yaml 描述符
对 linked 和 DLL 模块统一。Rust 模块从 Module 推导出它;DLL 可以
读取文件或发送 register_module_descriptor。
id: analytics
version: 1.2.0
abi: 8 # требуемая версия ABI для libs
requires: [auth, "admin@^2.1"] # граф: топосорт + авто-включение
optional: ["gpu@^1"] # только если цель уже включена
capabilities: [agent_os.Secrets] # чувствительные порты (fail-closed)
libs:
linux: lib/libanalytics.so
windows: lib/analytics.dll
macos: lib/libanalytics.dylib
bundle:
root: config
points: [agents, hooks, tools, routes, workflows]
install:
verify: sha256
code: true
artifacts:
- id: runtime
dest: runtime
platforms:
linux: { url: "https://…/rt.tgz", sha256: "…", extract: tar.gz }
windows: { url: "https://…/rt.zip", sha256: "…", extract: zip }
provides: [analytics.panel] # свои слоты/сервисы
contributes: [admin.nav] # в чужие слоты
启用 — 在 config/server.yaml 中:
modules:
enabled: [auth, admin, docs, distrib, analytics]
auto_install: false
enabled ≠ installed:列表是「启动时激活什么」;安装工件
(网络)是单独的操作。requires 会传递性地触达;未知 id
— warning;模块缺少 bundle/DLL 或 ABI 不匹配 — 模块加载
错误,系统其余部分照常启动。
32.7 两种传输与打包
agent_os_sdk::declare_module! 从同一个函数生成两种传输:
fn contribute(h: &mut dyn RegistryHost) {
h.data_registry("analytics.items", "{}");
h.data_item("analytics.items", r#"{"title":"x"}"#);
}
agent_os_sdk::declare_module!(AnalyticsModule, "analytics", contribute);
Cargo:
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["host"]
host = ["agent_os_sdk/host", "dep:agent_os_modules", "dep:serde", "dep:serde_json"]
[dependencies]
agent_os_sdk = { path = "…/agent_os_sdk" }
agent_os_abi = { path = "…/agent_os_abi" }
agent_os_modules = { path = "…/agent_os_modules", optional = true }
serde_json = { version = "1", optional = true }
- linked(
cargo build,featurehost):pub struct AnalyticsModule带impl Module— 宿主通过server.yaml包含。 - lean DLL(
cargo build --no-default-features):cdylib带ao_plugin_init(+ao_module_registry,如果有 stable 对象)。
宿主以相同方式加载两者 — 通过 ModulePackage:
// internal Rust-модуль
let pkg = agent_os_modules::ModulePackage::internal(AnalyticsModule)
.with_bundle(files);
// DLL: package собирается из register_module_descriptor + register_bundle_file + регистраций
let pkg = agent_os_modules_runtime::plugin_package("analytics").unwrap();
let rt = ModuleRuntime::build_packages_with_host(
packages, &enabled, "data/modules", host_services,
)?;
rt.activate()?;
bundle 的物化 — edit-preserving,在 data/builtin/<id>/ 中带
version-manifest(.manifest.json)。
32.8 Lifecycle
use agent_os_modules::{ModuleLifecycle, InstallContext, InstallReport,
UninstallContext, RuntimeContext};
impl ModuleLifecycle for AnalyticsLifecycle {
fn install(&self, ctx: &mut InstallContext) -> Result<InstallReport, String> {
// скачать/материализовать артефакты в ctx.install_dir
Ok(InstallReport::default())
}
fn activate(&self, _rt: &mut RuntimeContext) -> Result<(), String> { Ok(()) }
fn deactivate(&self, _rt: &mut RuntimeContext) -> Result<(), String> { Ok(()) }
fn uninstall(&self, _ctx: &mut UninstallContext) -> Result<(), String> { Ok(()) }
}
所有方法默认都是 no-op。运行时按拓扑顺序激活模块,
并按相反顺序停用;状态 — data/modules/<id>/state.json
(version、status、工件)。激活失败会将模块标记为 failed,但
系统照常启动。DLL 不会被卸载;deactivate — graceful stop。
32.9 完整示例
典型的 dual-transport 模块 — 就是这份参考手册(agent_os_docs):
// crates/modules/surfaces/agent_os_docs/src/interface.rs
#[abi_trait(id = "DocSource", name = "documentation", version = 1)]
pub trait DocSource {
fn manifest(&self) -> Result<String, String>;
fn markdown(&self, id: &str, lang: &str) -> Result<String, String>;
}
// crates/modules/surfaces/agent_os_docs/src/lib.rs
fn contribute(h: &mut dyn RegistryHost) {
h.stable_registry(DOCS_REGISTRY, DOC_SOURCE_ID);
}
agent_os_sdk::declare_module!(DocsModule, "docs", contribute, abi_objects);
abi_registry! {
BuiltinSource as DocSource;
}
最小的 cdylib 路由引擎 — crates/modules/fixtures/agent_os_route_demo
(纯 impl,没有 unsafe/thunk)。更多示例:agent_os_widget
(dual-transport 表面)、agent_os_test_plugin(raw C ABI,所有类别)、
agent_os_test_host_client(从 cdylib 消费 host 服务)。
32.10 构建与测试
cargo build -p <module> # linked (feature host, по умолчанию)
cargo build -p <module> --no-default-features # lean DLL
./scripts/build-module-dlls.sh # Windows: scripts\build-module-dlls.ps1
./scripts/test-module-system.sh # stage DLLs + все module-system тесты
Lean 库会被暂存到单独目录 target/modules/<profile>/ —
否则 linked 构建(包括 cargo build --workspace)会用
没有导出的空占位符覆盖真正的 DLL。加载 DLL 的测试
(agent_os_plugin_host、agent_os_modules_runtime、agent_os_route_abi)
通过 agent_os_test_support::require_dll 获取工件,如果不存在则失败(而不是
skip)。
传输对等性由
agent_os_modules_runtime/tests/transport_parity.rs 固定:linked 和 DLL 必须给出
相同的注册表/路由目录。
32.11 接下来去哪
- 20-native-plugins — raw ABI、
dll_tool/dll_hook、 DLL 加载、ABI 版本。 crates/module-system/runtime/agent_os_modules/README.md— 模块系统的 活参考手册(图、活注册表、host services)。- 内部笔记
docs/22-modules.md…docs/30-dll-ready-modules.md— 设计:图、lifecycle、ABI 边界、将模块拆分为 DLL 的计划。