开阳(Mizar)的核心卖点是"自举"——让 AI 自己写插件扩展自己。但插件到底能做什么?有哪些 API 可用?今天详细拆解。
插件是什么?
一个 .ts 或 .js 文件,放在 extensions/ 目录下,开阳启动时自动加载。TS 文件走内嵌 esbuild 编译(毫秒级),JS 文件免编译直接跑在 goja(Go 内嵌 JS 引擎)上。改完代码,热加载立即生效,不用重启。
插件的基本结构
一个最小的插件:
export function plugin_init(): void {
// 插件加载后执行(可选)
log("磁盘监控插件已加载");
}
export function plugin_cleanup(): void {
// 插件卸载前执行(可选)
log("磁盘监控插件已卸载");
}
plugin_init 和 plugin_cleanup 是两个可选的生命周期钩子。init 在插件加载成功后触发(包括热重载),cleanup 在插件被删除或热重载替换前触发。
宿主函数:插件能用的 Go 能力
开阳给插件暴露了一整套宿主函数,涵盖 HTTP、文件、数据库、LLM、网络、日志等。插件通过这些函数调用 Go 核心能力,不需要自己实现底层逻辑。
HTTP 客户端
http_request(method, url, body, headersJSON) → response
统一 HTTP 请求:method 支持 GET/POST/PUT/DELETE/PATCH,headers 是可选的 JSON 字符串。返回响应文本。
http_get(url) → response
薄封装:http_request("GET", url, "", "")
http_post(url, body) → response
薄封装:http_request("POST", url, body, "")
示例:调外部 API
const resp = http_post("http://127.0.0.1:3002/v1/chat/completions",
JSON.stringify({model: "deepseek-v4-flash", messages: [{role: "user", content: "你好"}]})
);
const data = JSON.parse(resp);
文件操作
fs_read(path) → content
读取文件内容(上限 10MB,超限拒绝)。
fs_read_range(path, offset, length) → [content, totalSize, error]
有界 seek 读:大文件分片读取,单次 4MB,循环处理大文件。
fs_write(path, content) → void
写入文件(自动创建父目录)。
fs_list(path) → fileList
列出目录内容。
示例:读配置文件
const cfg = JSON.parse(fs_read("/etc/myapp/config.json"));
log("数据库地址: " + cfg.db_host);
数据库查询(内置 3 驱动)
db_query(driver, dsn, sql) → JSON
内置 SQLite/MySQL/PostgreSQL 驱动,返回 JSON 结果。
db_close(driver, dsn) → void
关闭数据库连接(显式释放)。
db_exec_batch(driver, dsn, sqlsJSON) → {"rowsAffected":N}
SQL 数组一个事务执行,失败整体回滚。写多行优先 batch(性能提升 10 倍以上)。
示例:建表+插入
db_exec_batch("sqlite3", "/data/app.db", JSON.stringify([
"CREATE TABLE IF NOT EXISTS logs (id INTEGER PRIMARY KEY, msg TEXT, ts DATETIME DEFAULT CURRENT_TIMESTAMP)",
"INSERT INTO logs (msg) VALUES ('插件已启动')"
]));
LLM 调用(自举钥匙)
llm_chat(messagesJSON) → response
调用当前配置的 LLM,messages 格式同 OpenAI API。这是自举的核心——插件能调用 AI,AI 又能写插件。
示例:让 AI 分析日志
const resp = llm_chat(JSON.stringify([
{role: "user", content: "分析这段日志的异常: " + log_content}
]));
log("AI 分析结果: " + resp);
MCP 工具调用
mcp_call(server, tool, argsJSON) → response
调用外部 MCP server 的工具。服务器在 config.json 的 mcp_servers 里配置。
示例:调 Redis
const result = mcp_call("redis", "get", JSON.stringify({key: "my_key"}));
日志和工具
log(message) → void
输出日志(带时间戳)。
sleep(ms) → void
异步等待(毫秒)。
count_tokens(text) → number
计算文本的 token 数量。
钩子系统:监听 Agent 事件
插件可以监听 Agent 的生命周期事件,做统计、审计、自动化。
hook_on(event, callback) → void
注册钩子回调。event 是事件名,callback 是处理函数。
当前支持的事件:
RunStart — Agent 开始处理任务
RunEnd — Agent 完成任务
ToolCall — 工具被调用(可做统计)
示例:统计工具调用次数
let toolCount = 0;
hook_on("ToolCall", (ctx) => {
toolCount++;
log("工具调用次数: " + toolCount);
return ""; // 返回空字符串
});
网络能力:TCP/HTTP/FTP/WebSocket
插件可以开网络服务器或连外部服务,做分布式互联。
TCP 服务器
tcp_listen(addr, onAccept) → connID
开启 TCP 监听,onAccept 回调接入连接。
tcp_send(connID, data) → void
发送数据。
tcp_onrecv(connID, callback) → void
注册接收回调。
tcp_stop(connID) → void
停止服务器。
TCP 客户端
tcp_dial(addr) → connID
连接远程服务器。
http_listen(addr, handler) → addr
开启 HTTP 服务器,handler 是请求处理函数(接收 JSON 请求,返回 JSON 响应)。
示例:开一个 HTTP API
http_listen(":8080", (reqJSON) => {
const req = JSON.parse(reqJSON);
if (req.path === "/status") {
return JSON.stringify({status: 200, body: "ok"});
}
return JSON.stringify({status: 404, body: "not found"});
});
WebSocket 客户端
ws_connect(url, onMessage) → connID
连接 WebSocket 服务器。
ws_send(connID, data) → void
发送消息。
ws_onmessage(connID, callback) → void
注册消息回调。
ws_close(connID) → void
关闭连接。
ws_emit(event, data) → void
向所有 WS 客户端推送事件(Server 模式用)。
FTP 客户端
ftp_connect(host, user, pass) → connID
连接 FTP 服务器。
ftp_list(connID, path) → fileList
列出目录。
ftp_upload(connID, localPath, remotePath) → void
上传文件。
ftp_download(connID, remotePath, localPath) → void
下载文件。
安全机制:防止插件搞破坏
panic 隔离:插件里的宿主函数 panic 不会拖垮主程序(defer recover 捕获)。
执行超时:单次执行默认 30 秒,goja Interrupt 打断死循环。
白名单 shell:shell_exec 只允许预定义的命令,防止执行危险操作。
文件大小限制:fs_read 上限 10MB,防止读取超大文件耗尽内存。
热重载:新版本 plugin_init 成功才生效,失败保留旧版本——改坏不丢。
自举:让 AI 自己写插件
这是开阳最酷的能力。你跟它说:
"写一个插件:每天 9 点检查磁盘占用,超过 80% 发飞书告警"
它会生成 disk_watch.ts,内容大概这样:
export function plugin_init(): void {
hook_on("RunStart", (ctx) => {
const resp = shell_exec("df -h /");
if (resp.includes("80%")) {
http_post("飞书webhook地址", JSON.stringify({msg: "磁盘告警"}));
}
return "";
});
}
热加载后立即生效。你不需要手动写代码,AI 帮你写,AI 帮你扩展。
三层扩展模型
技能层:SKILL.md 纯文本,指导 AI 怎么做(不需要代码)
插件层:.ts/.js 文件,可执行的新工具/能力
工具层:MCP 服务器,任意语言实现
三层互不阻塞:技能不需要代码,插件不需要外网,MCP 不需要改二进制。
实战:一个监控插件的完整例子
假设你要监控服务器内存,超过 90% 发告警:
// extensions/mem_watch.ts
export function plugin_init(): void {
log("内存监控插件已加载");
hook_on("RunStart", (ctx) => {
const resp = shell_exec("free | grep Mem | awk '{print $3/$2 * 100.0}'");
const usage = parseFloat(resp.trim());
if (usage > 90) {
http_post("https://open.feishu.cn/open-apis/bot/v2/hook/xxx",
JSON.stringify({msg: "内存使用率 " + usage + "%"}));
}
return "";
});
}
export function plugin_cleanup(): void {
log("内存监控插件已卸载");
}
保存为 extensions/mem_watch.ts,开阳热加载,监控开始工作。
总结:为什么用开阳的插件体系?
零依赖:插件是纯 TS/JS,不需要 npm、node_modules
热加载:改完代码立即生效,不用重启
自举:AI 能自己写插件,你只需要提需求
安全:panic 隔离、超时、白名单,插件搞不垮主程序
生态兼容:SKILL.md 兼容 Anthropic/Hermes 生态
网络能力:TCP/HTTP/FTP/WebSocket 都能用
数据库:内置 SQLite/MySQL/PostgreSQL 驱动
LLM 调用:插件能调 AI,形成自举闭环
一个 25MB 的二进制,一套插件系统,让你的 AI 能力无限扩展。
觉得有用给个 star,GitHub 地址:https://github.com/icefairy/mizar
作者:玄凌子
夜雨聆风