灵渠 LingQu
中间件管道与扩展点 SDK

自定义插件开发

自定义插件的模型与扩展点的关系、生命周期与接口契约、执行时序与编排位次、构建产物与动态加载、启停与失败处置,以及最小示例契约

灵渠平台的中间件管道是可编排的,也是可扩展的。扩展点 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)。
导出 导出 导出 挂载到 挂载到 纳入 加载/卸载时调用 自定义插件 前置 Hook 后置 Hook 生命周期回调 中间件节点 路由/缓存检查点/安全护栏 管道编排 前置-内建-后置

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 多次调用,末块汇总落账。initcleanup 各只在加载、卸载时调用一次,与单次请求无关。

3. 执行时序与编排位次

多个插件、加上内建中间件,同处一条管道时,谁先谁后由编排位次决定。位次模型完全沿用 概念与编排 §2扩展点 SDK §1

  • 段位(placement):前置 pre → 内建 builtin → 后置 post,段间顺序固定不可调。落在前置段的 Hook,请求去程最先执行、回程最后收尾;落在后置段的 Hook 反之。
  • 组内位次(order):同一段位内按 order 升序执行(值小者先),相同 order 按注册先后保序。
  • 前后对称:前置 Hook 正序、后置 Hook 逆序(先进后出),保证状态对称收敛。

下例展示三个自定义插件相对内建中间件的执行次序。假设两个插件挂在前置段(pre)、一个挂在后置段(post):

插件 / 内建段位order前置去程后置回程
入站校验插件pre10第 1第 5(最后)
路由提示增强插件pre20第 2第 4
内建中间件(路由 / 缓存检查点 / 护栏)builtin第 3第 3
出站脱敏插件post10第 4第 2
计量上报插件post20第 5(最后)第 1
前置去程 1; 前置去程 2; 前置去程 3; 后置去程 4; 后置去程 5; 后置回程 1; 后置回程 2; 后置回程 3; 后置回程 4; 后置回程 5; 查询请求; 入站校验插件 pre; 路由提示增强 pre; 内建中间件 builtin; 出站脱敏 post; 计量上报 post;

何时选哪个段位:

段位适合的插件
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_pluginsfalse,请先联系部署方切换为支持动态加载的构建,再注册自定义插件。

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 / placementHook 类型与执行段位(须匹配,见 §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_policyfail_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" }

直通骨架的价值在于先跑通"加载 → 挂载 → 调用 → 卸载"全链路,再逐步把 actioncontinue / 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 参考:管道编排与扩展点接口的完整端点契约。

On this page