实验性:使用其他语言扩展 Bub
本教程是实验性的。它展示的是如何从其他语言扩展 Bub,而不是把 Bub 本体改写成多语言项目。
很多团队希望把业务逻辑写在自己熟悉的语言里,或者让运行时边界更贴近现有的技术栈。如果把 Bub 核心拆成多个语言版本,维护成本会显著上升。Bub 选择的是另一条路:保留 Python 宿主,借助 pluggy、WebAssembly 和 Extism,让其他语言来实现部分 hook。
这里的目标不是替换 Bub 内核,而是提供一个务实的扩展边界——当某个 hook 用 Go、Rust 或其他语言实现更合适时,这条路径会非常有用。
开始前,请确保:
- 已经把 Bub 安装到一个激活的虚拟环境中(
bub install必须在 venv 内运行),并且bub --help可以正常工作。 - 已安装来自
bub-contrib的bub-extism插件。 - 如果要构建 Go 示例,需要安装 Go 工具链。
- 如果要构建 Rust 示例,需要安装 Rust 工具链,并添加
wasm32-unknown-unknowntarget。
在动手之前,建议先浏览这些参考链接:
第一步:安装实验性插件
Section titled “第一步:安装实验性插件”将 bub-extism 安装到 Bub 所在的同一环境:
bub install bub-extism@main
安装后,确认 Bub 已经识别到它:
uv run bub hooks
你应该能在输出中看到 extism 与 builtin 并列出现。
第二步:用 Go 和 Rust 各实现一个 hook
Section titled “第二步:用 Go 和 Rust 各实现一个 hook”我们使用 bub-extism 自带的两个经过验证的示例模块:
go-build-prompt实现了build_prompthookrust-run-model实现了run_modelhook
构建 Go 示例:
cd bub-contrib/packages/bub-extism/examples/go-build-prompt
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o go-build-prompt.wasm .
构建 Rust 示例:
cd bub-contrib/packages/bub-extism/examples/rust-run-model
cargo build --release --target wasm32-unknown-unknown
如果需要完整的构建命令,请参考 examples 指南。
第三步:用 extism.json 连接两个模块
Section titled “第三步:用 extism.json 连接两个模块”在项目根目录创建 extism.json,将 prompt hook 绑定到 Go 模块,model hook 绑定到 Rust 模块:
{
"plugins": {
"prompt": {
"manifest": {
"wasm": [
{
"path": "bub-contrib/packages/bub-extism/examples/go-build-prompt/go-build-prompt.wasm"
}
]
},
"wasi": true,
"hooks": {
"build_prompt": "build_prompt"
}
},
"model": {
"manifest": {
"wasm": [
{
"path": "bub-contrib/packages/bub-extism/examples/rust-run-model/target/wasm32-unknown-unknown/release/bub_extism_rust_run_model.wasm"
}
]
},
"hooks": {
"run_model": "run_model"
}
}
}
}
这个配置文件的关键点:
manifest遵循标准 Extism manifest 格式。hooks把 Bub 的 hook 名称映射到 wasm 模块的导出函数。- Bub 仍然负责整体调度和优先级管理。
配置完成后,设置环境变量让 Bub 找到它:
export BUB_EXTISM_CONFIG_PATH=./extism.json
第四步:跑一次跨语言的对话轮次
Section titled “第四步:跑一次跨语言的对话轮次”现在让 Bub 先用 Go 构建 prompt,再交给 Rust 执行 model hook:
uv run bub run "Say hello from a prompt built outside Python."
如果一切配置正确,整个流程是:
- Bub 调用 Go 模块中的
build_prompt - 将返回的 prompt 传递给 Rust 模块中的
run_model - Rust 模块返回最终结果,完成本次对话轮次
CLI 会按 [channel:chat_id] 的格式输出消息,默认的 bub run 通常会先显示 [cli:local],然后是实际内容。
示例输出大致如下:
[cli:local]
[rust-run-model:cli:local] [go-build-prompt:cli:local] Say hello from a prompt built outside Python.
Go 模块会在入站 prompt 前加上 [go-build-prompt:<chat_id>] 前缀;Rust 模块再把 Go 的输出包一层 [rust-run-model:<chat_id>]。开头的 [cli:local] 是 CLI 通道发出的频道头。
第五步:理解这条路径的边界
Section titled “第五步:理解这条路径的边界”这条扩展路径适合以下场景:
- 某个 hook 用其他语言实现确实更自然
- 团队希望更直接地掌控和共同维护具体实现
- 仍然想让 Bub 作为核心编排宿主
不要把它当作把 Bub 核心拆成多个独立语言版本的理由——那样会把这套架构原本要避免的维护负担重新引回来。这是一个扩展路径,不是重写计划。
- 构建插件 — 了解第一方 Bub 插件的结构。
bub-extismREADME — 阅读 package 约定及支持的 hooks。- Extism 概览 — 了解 Extism 如何在不同语言中运行 wasm 模块。