自定义插件开发
自定义插件的模型与扩展点的关系、生命周期与接口契约、执行时序与编排位次、构建产物与动态加载、启停与失败处置,以及最小示例契约
灵渠平台的中间件管道是可编排的,也是可扩展的。扩展点 SDK 参考 讲的是"在既定位次挂一对前置/后置 Hook"这一接口契约;本页讲的是更上一层的问题——如何把一组治理逻辑打包成一个可独立部署、可动态加载、可启停的「自定义插件」,并把它接入到管道的扩展点上。
一句话区分二者:扩展点是管道开放给外部逻辑的挂载位,插件是承载这些逻辑的可分发单元。一个插件向平台导出若干符合扩展点签名的 Hook,平台在加载该插件后,把它的 Hook 按声明的段位与位次纳入管道编排。
本页定位。 本页是 概念与编排 与 扩展点 SDK 参考 的延伸——前者讲管道编排骨架、后者讲单个 Hook 的接口契约,本页讲"把 Hook 组织成插件、构建、加载、启停"的完整工程闭环。术语(前置/内建/后置、
order、短路、逐 chunk、fail-open/fail-closed)与上述两页完全一致,不重复其内容。
1. 插件模型与扩展点的关系
一个自定义插件本质上是一个导出了一组约定符号的可加载单元。平台加载它时,按符号名识别其生命周期回调与 Hook,把声明的 Hook 接到对应的扩展点上。
| 概念 | 归属 | 说明 |
|---|---|---|
| 中间件节点 | 内建 | 路由 / 缓存检查点 / 安全护栏三类治理中间件,平台内建,不由插件提供 |
| 扩展点 | 平台开放 | 挂载在中间件节点上的前置 / 后置对称 Hook 位(见 扩展点 SDK) |
| 插件 | 自定义 | 可独立分发的单元,向平台导出一组生命周期回调 + 若干 Hook |
| Hook | 插件提供 | 插件导出的、符合扩展点签名的前置 / 后置函数 |
- 一个插件可以只导出前置 Hook、只导出后置 Hook,或两者都导出——平台对每个 Hook 符号都按"可选导出"处理:缺失即不挂载,多余的命名错误也只静默禁用对应 Hook,不会让整个插件加载失败。
- 一个插件可以挂载到不同段位(前置 / 后置),但单个 Hook 的段位由其类型固定:前置 Hook 只能落前置段,后置 Hook 只能落后置段(编排约束见 扩展点 SDK §4)。
- 插件不能新增中间件节点类型,也不能替代内建中间件——它只在既定扩展点上叠加治理逻辑,且同样受统一能力边界约束(仅治理面处置,不实质性生成、加工或合成内容,见 §6)。
2. 生命周期与接口契约
插件以"一组约定符号"对接平台。下表给出插件须(或可)导出的符号及其语义。命名沿用站内既有签名风格——以接口契约层面描述,不绑定任何具体编程语言。
| 符号 | 必选 | 何时调用 | 职责 |
|---|---|---|---|
get_name | 是 | 加载时 | 返回插件的全局唯一标识,用于编排清单、运行状态与日志归集 |
init | 是 | 加载后、首次处理请求前一次 | 接收插件配置,完成一次性初始化(外部连接、参数解析、前置校验等) |
pre_hook | 否 | 每次请求转发上游前 | 前置 Hook:校验 / 改写入站请求、补充路由提示、短路返回(签名见扩展点 SDK §2.1) |
post_hook | 否 | 每次上游响应返回后 | 后置 Hook:复核 / 脱敏出站响应、采集计量、写缓存、逆序收尾(签名见扩展点 SDK §2.2) |
cleanup | 是 | 卸载 / 平台停机时一次 | 释放资源(关闭连接、刷写缓冲、保存状态) |
接口契约要点(与 扩展点 SDK §2 一致,不重复 Hook 的入参出参 JSON):
- 统一形态:每个 Hook 接收一个治理上下文对象、返回一个处置结果对象;上下文以约定字段(如
phase/request_id/api_key_id)表达,平台与插件之间一律以结构化数据交换,不暴露内部数据结构。 - 上下文对称传递:前置 Hook 写入
context通道的值,会在配对的后置 Hook 中原样回传,用于跨阶段携带状态;后置 Hook 逆序执行,保证"先建立的上下文后清理"。 - 短路语义复用:前置 Hook 返回
short_circuit即跳过内建段上游调用,但已执行前置 Hook 的配对后置 Hook 仍会被逆序收尾(见 概念与编排 §2.4)。 - 流式逐 chunk:标记逐 chunk 的后置 Hook 在流式响应下被多次调用,
request_id全程一致(见 扩展点 SDK §2.3)。
一次非流式请求中,单个插件的回调调用顺序如下:
加载阶段: get_name → init
每次请求: pre_hook →(内建段:上游推理)→ post_hook
卸载阶段: cleanup流式请求下,
pre_hook仍只在请求转发前调用一次;post_hook中标记逐 chunk 的部分按 chunk 多次调用,末块汇总落账。init与cleanup各只在加载、卸载时调用一次,与单次请求无关。
3. 执行时序与编排位次
多个插件、加上内建中间件,同处一条管道时,谁先谁后由编排位次决定。位次模型完全沿用 概念与编排 §2 与 扩展点 SDK §1:
- 段位(placement):前置
pre→ 内建builtin→ 后置post,段间顺序固定不可调。落在前置段的 Hook,请求去程最先执行、回程最后收尾;落在后置段的 Hook 反之。 - 组内位次(order):同一段位内按
order升序执行(值小者先),相同order按注册先后保序。 - 前后对称:前置 Hook 正序、后置 Hook 逆序(先进后出),保证状态对称收敛。
下例展示三个自定义插件相对内建中间件的执行次序。假设两个插件挂在前置段(pre)、一个挂在后置段(post):
| 插件 / 内建 | 段位 | order | 前置去程 | 后置回程 |
|---|---|---|---|---|
| 入站校验插件 | pre | 10 | 第 1 | 第 5(最后) |
| 路由提示增强插件 | pre | 20 | 第 2 | 第 4 |
| 内建中间件(路由 / 缓存检查点 / 护栏) | builtin | — | 第 3 | 第 3 |
| 出站脱敏插件 | post | 10 | 第 4 | 第 2 |
| 计量上报插件 | post | 20 | 第 5(最后) | 第 1 |
何时选哪个段位:
| 段位 | 适合的插件 |
|---|---|
pre(前置) | 需要在内建治理之前介入:入站校验、归属补全、为内建路由/护栏注入提示或元数据、在消耗内建处置前短路 |
post(后置) | 需要在内建治理之后收尾:出站复核与脱敏、对最终请求/响应做计量上报、收尾清理 |
拿不准时默认选
post。大多数自定义插件——计量上报、出站改写、记录归集——都更适合在内建中间件处置完成之后运行。段位与位次均可在「中间件编排」页拖拽调整,或经PUT /api/v1/pipeline/order提交(见 概念与编排 §2.2)。
4. 构建产物与动态加载
自定义插件以构建产物形态交付:插件源经构建后产出一个可加载单元,平台在运行时按配置载入。这里只描述工程契约,不涉及平台实现栈。
4.1 构建产物
| 概念 | 说明 |
|---|---|
| 构建产物 | 插件源构建后产出的可加载单元,含导出符号表(get_name / init / Hook / cleanup) |
| 加载形态 | 平台支持两种加载形态:原生可加载单元(随平台运行环境,性能最优、无跨边界开销)与沙箱可加载单元(跨边界以结构化数据交换、隔离执行、跨环境可移植) |
| 兼容契约 | 原生形态要求与平台运行环境的构建参数对齐(运行环境家族、处理器架构、构建工具版本一致);沙箱形态对运行环境无此要求,单一产物可跨环境加载 |
两种加载形态的取舍:
| 维度 | 原生可加载单元 | 沙箱可加载单元 |
|---|---|---|
| 执行开销 | 最低(同进程内调用) | 含结构化数据序列化开销 |
| 跨环境移植 | 需按目标环境分别构建 | 单一产物处处可加载 |
| 构建对齐要求 | 须与平台运行环境严格对齐 | 无对齐要求 |
| 隔离性 | 与平台同进程 | 沙箱隔离、内存受限 |
选型建议:对延迟极敏感、且部署环境可控的治理逻辑,优先原生形态;需要跨多种部署环境分发、或希望沙箱隔离的逻辑,选沙箱形态。两种形态对外导出的符号语义一致,业务逻辑可在二者间平移。
4.2 启用动态加载
动态加载并非默认开启——以构建产物形态加载外部插件,要求平台实例以支持动态加载的构建运行。默认构建为追求部署便捷与可移植性,可能不含动态加载能力。
- 私有化部署:若需自定义插件,部署时选用「支持动态加载」的构建参数;具体由部署方在交付时确认。
- SaaS 部署:自定义插件的接入与加载由平台运营侧统一受理,按治理审批流程开启。
可调用部署状态接口确认当前实例是否支持动态加载:
curl https://<平台入口地址>/api/v1/deployment \
-H "Cookie: <控制台会话凭证>"响应在部署能力字段中标明动态加载支持状态(示意):
{
"deploy_mode": "private",
"deploy_status": "ready",
"capabilities": {
"dynamic_plugins": true
}
}若
dynamic_plugins为false,请先联系部署方切换为支持动态加载的构建,再注册自定义插件。
4.3 注册插件
确认实例支持动态加载后,注册一个插件即声明其标识、构建产物位置、初始配置与编排位次:
curl https://<平台入口地址>/api/v1/pipeline/extensions \
-X POST \
-H "Content-Type: application/json" \
-H "Cookie: <控制台会话凭证>" \
-d '{
"name": "出站脱敏插件",
"artifact_ref": "<构建产物引用>",
"node_id": "<挂载的中间件节点>",
"hook_type": "post",
"placement": "post",
"order": 10,
"enabled": true,
"config": {
"<插件自定义配置项>": "<值>"
},
"fail_policy": "fail_closed"
}'| 字段 | 含义 |
|---|---|
name | 插件标识(须与 get_name 返回一致) |
artifact_ref | 构建产物引用(加载位置 / 版本标识) |
node_id | 挂载到哪个内建中间件节点 |
hook_type / placement | Hook 类型与执行段位(须匹配,见 §1) |
order | 段位内执行位次(值小者先) |
enabled | 是否启用(见 §5) |
config | 传给插件 init 的初始配置 |
fail_policy | 失败策略 fail_open / fail_closed(见 §5) |
注册后插件即对后续请求生效。读取已注册插件清单用
GET /api/v1/pipeline/extensions(见 扩展点 SDK §3)。artifact_ref含版本标识——升级插件时更新版本即触发平台重新加载该插件。
5. 启停与失败处置
5.1 启停
- 启用 / 停用:
enabled控制单个插件的执行状态。停用的插件保留在编排清单中、运行时被跳过——便于灰度上线与快速回退,无需注销重注册。 - 位次调整:插件段位内位次随其挂载节点,经
PUT /api/v1/pipeline/order重排(见 概念与编排 §2.2)。 - 热替换:更新
artifact_ref的版本标识即触发平台对该插件的重新加载——先以新产物完成init,再切换执行,旧实例cleanup收尾,过程不打断在途请求的整体管道。
启停与位次变更均为编排面操作,仅运营控制台可见,只影响治理面行为,不触及上游凭证与计量底座。
5.2 失败处置
插件加载或 Hook 执行过程中的异常,按"治理可控、请求可解释"原则处置。失败策略 fail_policy 随插件注册声明:
| 情形 | 处置 |
|---|---|
加载 / init 失败 | 该插件不纳入编排;其余插件与内建管道照常运行;运营控制台「中间件编排」页标注该插件为加载失败 |
| 前置 Hook 返回拒绝 | 请求被结构化拒答,不再转发上游;逐条记录护栏处置结果为对应类别 |
| 前置 Hook 执行报错 | 按 fail_policy:fail_open 放行、fail_closed 拒绝;安全护栏类插件默认 fail_closed |
| 后置 Hook 返回拦截 | 出站响应被拦截、结构化返回;已发生的计量按实落账 |
| 后置 Hook 执行报错 | 不影响已完成的上游调用与计量落账;按 fail_policy 决定是否放行出站内容,护栏类默认拦截 |
| 短路路径上的后置异常 | 逆序收尾继续执行其余后置 Hook,单个插件异常不阻断整条收尾链 |
涉及安全合规的护栏类插件一律默认
fail_closed——"宁可拒绝,不放行未经复核的内容",与平台安全护栏的治理基线一致(见 扩展点 SDK §5)。单个插件的异常被隔离在该插件范围内,不会让整条管道连带失败。
6. 最小示例契约
下面给出一个"直通"插件的最小契约——它什么也不改,只演示导出符号与入参出参的最小形态。实际治理逻辑(脱敏、校验、计量上报等)在此骨架上填充。
四个导出符号的直通形态(出参以注释标注返回值):
get_name() -> "出站脱敏插件" # 唯一标识,须与注册名一致
init({ config }) -> { "ok": true } # 接收配置、完成一次性初始化
cleanup() -> { "ok": true } # 卸载时释放资源pre_hook —— 入站直通(不改写、不短路,放行后续节点,并向后置传一个标记):
// 入参
{ "phase": "pre", "request_id": "<追踪标识>", "request": { "model": "<模型中性名>", "messages": [ ] }, "context": {} }
// 出参
{ "action": "continue", "context": { "<向后置传递的标记>": "<值>" } }post_hook —— 出站直通(放行响应,逆序回收前置写入的 context):
// 入参
{ "phase": "post", "request_id": "<追踪标识>", "response": { "model": "<实际路由的上游模型中性名>", "content": "..." }, "usage": { "input_tokens": 0, "output_tokens": 0, "price_tier": "<适用单价档(6 档之一)>" }, "context": { "<前置传递来的标记>": "<值>" } }
// 出参
{ "action": "pass" }直通骨架的价值在于先跑通"加载 → 挂载 → 调用 → 卸载"全链路,再逐步把
action从continue/pass改为rewrite/short_circuit/redact/block,填入真实治理逻辑。各action取值的完整语义见 扩展点 SDK §2;填入逻辑时务必恪守统一能力边界——只在治理面处置,不实质性生成、加工或合成内容。
相关链接
- 概念与编排:三类治理中间件、执行位次、条件分支、短路语义与流式逐 chunk 处理。
- 扩展点 SDK 参考:前置 / 后置对称 Hook 模型、Hook 签名与生命周期、注册启停、编排约束与失败处置、能力边界声明。
- 用 Go 编写原生插件:§4.1「原生可加载单元」的 Go 落地路径——
go build -buildmode=plugin产出.so、同进程加载。 - 用 TypeScript 编写插件:§4.1「沙箱可加载单元」的 TypeScript(AssemblyScript→WASM)落地路径。
- 用 WebAssembly 编写插件:§4.1「沙箱可加载单元」的通用 host-guest ABI 与跨语言(Rust / TinyGo)编写路径。
- API 参考:管道编排与扩展点接口的完整端点契约。