iii 0-19-0 函数编写完全指南:注册、Schema、HTTP 调用与动态注销
发布时间:2026/9/14 17:10:19来源:尧图网络
iii 0-19-0 函数编写完全指南注册、Schema、HTTP 调用与动态注销【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii本篇指南以 iii 项目 0-19-0 文档 Functions 为核心讲解一个 Worker 如何通过 SDK 向 iii 系统贡献能力Function包括service::name形式的函数 id、三种语言Node/TypeScript、Python、Rust的注册方式、请求/响应 JSON Schema 的绑定与语义边界、注册级与调用级两类元数据、把外部 HTTP 端点包装成普通函数的HttpInvocationConfig以及运行时注销函数。读完本文你将能独立编写、暴露、调试并安全下线一个可被worker.trigger、iii trigger与各类触发器调用的 iii 函数。编写一个函数到底指什么在 iii 中一个 Worker 通过注册函数function向系统贡献能力。每个函数由三部分构成id形如service::name例如math::add。这个 id 就是触发器使用的function_id也是调用方寻址函数的唯一凭据。handler接收调用方传入的 payload、返回结果的处理器函数。可选的 JSON Schema描述请求与响应形状request_format/response_format用于文档化与工具链消费。调用方如何触发函数worker.trigger/iii trigger/ 事件绑定触发器属于 Triggers 文档 的范畴本指南聚焦编写新函数这一侧。一个值得注意的边界每种触发器对函数的入参结构有各自的约定。例如cron会无参调用函数而http会提供包含body、headers等字段的标准 HTTP 风格 payload。函数本身不关心调用方是谁但 handler 需要按触发器的约定解析入参。注册一个函数在 Worker 内部通过 SDK 注册函数注册时传入的id即后续所有调用路径使用的function_id。三种语言的写法如下// Node / TypeScript import { registerWorker } from iii-sdk; const url process.env.III_URL; if (!url) throw new Error(III_URL must be set); const worker registerWorker(url); worker.registerFunction(math::add, async (payload: { a: number; b: number }) { return { c: payload.a payload.b }; });# Python import os from iii import register_worker, InitOptions worker register_worker( os.environ.get(III_URL), InitOptions(worker_namemath-worker), ) def add_handler(payload: dict) - dict: return {c: payload[a] payload[b]} worker.register_function(math::add, add_handler)// Rust use iii_sdk::{InitOptions, RegisterFunction, register_worker}; let url std::env::var(III_URL).expect(III_URL must be set); let worker register_worker(url, InitOptions::default()); worker.register_function(RegisterFunction::new(math::add, |input: AddInput| { Ok(serde_json::json!({ c: input.a input.b })) }));从 SDK 源码可以看到注册背后的实际约束Node 端的registerFunctionsdk/packages/node/iii/src/iii.ts会先校验id非空、且未被重复注册function id already registered: id会直接抛错随后构造RegisterFunctionMessage通过 WebSocket 发送给引擎并在本地维护函数表。Python 端的register_functionsdk/packages/python/iii/src/iii/iii.py同样以关键字参数形式接收description、metadata、request_format、response_format。这意味着id在同一个 Worker 内必须唯一重复注册会被 SDK 拒绝注册本质上是通过 WebSocket 向引擎发布一条RegisterFunction消息函数只有注册成功后才会出现在系统可发现的能力列表中。绑定请求与响应 Schema注册函数时可以附加 JSON Schema把请求/响应形状与函数本体一起文档化。这些 Schema 随函数一起存储并出现在 iii console、iii trigger --help以及 Agent 可读的 skill 中。// Node / TypeScript worker.registerFunction( math::add, async (payload) ({ c: payload.a payload.b }), { request_format: { type: object, properties: { a: { type: number }, b: { type: number } }, required: [a, b], }, response_format: { type: object, properties: { c: { type: number } }, required: [c], }, }, );# Python worker.register_function( math::add, add_handler, request_format{ type: object, properties: {a: {type: number}, b: {type: number}}, required: [a, b], }, response_format{ type: object, properties: {c: {type: number}}, required: [c], }, )// Rust use iii_sdk::{InitOptions, RegisterFunction, register_worker}; use schemars::JsonSchema; use serde::Deserialize; #[derive(Deserialize, JsonSchema)] struct AddInput { a: f64, b: f64 } #[derive(serde::Serialize, JsonSchema)] struct AddOutput { c: f64 } let url std::env::var(III_URL).expect(III_URL must be set); let worker register_worker(url, InitOptions::default()); // Rust 通过 schemars::JsonSchema 从闭包的输入/输出类型自动推导 // request_format 与 response_format无需手工编写 Schema worker.register_function(RegisterFunction::new( math::add, |input: AddInput| - ResultAddOutput, String { Ok(AddOutput { c: input.a input.b }) }, ));重要Schema 是元数据不是运行时校验必须强调运行时校验目前尚不支持。附加的 Schema 只是元数据引擎不会据此强制校验 payload、拒绝不合 Schema 的入参也不会校验 handler 返回值是否匹配response_format。请把 Schema 视为函数调用契约文档供函数调用方、Agent 与 console 消费iii trigger function::id --help正是依赖这些 Schema 才能展示函数参数与说明详见 Using iii / Functions 中的说明Schema 同样会投喂给 iii console 与 Agent 可读的 skill帮助调用方自动生成正确参数。绑定注册级元数据metadatametadata是注册时随函数存储的任意 JSON 对象。引擎从不解释它的内容只是原样存储供系统其他部分消费。它出现在engine::functions::info、iii console 中并常与iii-worker-manager/ 基于角色的访问控制RBAC配合使用。// Node / TypeScript worker.registerFunction(math::add, handler, { metadata: { owner: math-team, public: true }, });# Python worker.register_function( math::add, add_handler, metadata{owner: math-team, public: True}, )// Rust worker.register_function( math::add, RegisterFunction::new(|input: AddInput| { Ok(serde_json::json!({ c: input.a input.b })) }) .metadata(serde_json::json!({ owner: math-team, public: true })), );因为形状完全由你定义metadata 是一种可扩展的设施例如 worker-manager 的 RBAC 允许列表 可以通过metadata:选择器暴露函数——给函数打上{ public: true }或{ tier: premium }这类标签就能基于自定义字段做访问控制。安全警告所有访问控制必须在系统侧引擎/管理侧实施。绝不应依赖不受信任的 Worker 自己定义它能访问什么、不能访问什么——metadata 只是声明真正的 gate 在系统侧。接收调用级元数据per-invocation metadata与上面的注册级 metadata 不同每次调用还可以携带一个可选的metadata对象任意 JSON。当触发器触发函数时可以通过这个对象把触发器或执行上下文带给被调函数——这正是 Triggers 文档中的 trigger metadata 机制触发器注册时的metadata字段会与 payload 一起、作为一个独立参数投递给目标函数。// Node / TypeScript // 可选的第二个参数在调用未携带 metadata 时为 undefined worker.registerFunction(audit::write, async (data, metadata) { return { ok: true, metadata }; });# Python # 只有当 handler 声明了名为 metadata 的参数时才会转发 # 位置参数或关键字参数均可其他签名的 handler 照旧被调用 async def write(data, metadataNone): return {ok: True, metadata: metadata} worker.register_function(audit::write, write)// Rust // 双参闭包接收 OptionValue 形式的 sidecar // 单参闭包保持原有行为不变 worker.register_function( audit::write, RegisterFunction::new_async(|input: Value, metadata: OptionValue| async move { Ok(serde_json::json!({ ok: true, metadata: metadata })) }), );Python SDK 对该行为有专门测试sdk/packages/python/iii/tests/test_invocation_metadata.pySDK 通过_metadata_passing_mode检测 handler 的参数个数——两个参数的 handler 按 position 模式传递 metadata单参 handler 完全不破坏原有行为。一个被多个触发器共享的函数可以借此恢复是哪次注册触发了本次调用、带着什么上下文。HTTP 可调用函数把外部端点包装成 iii 函数除了进程内 handler你还可以把外部 HTTP 端点注册为函数引擎在函数被调用时发起 HTTP 请求Worker 只需要声明端点即可。这非常适合把已有的 API Gateway、webhook、serverless 平台Lambda、Azure Functions、Google Cloud Functions或任何第三方 API 统一暴露成常规的 iii 函数。注册之后它与其他函数完全同等地可被触发worker.trigger、iii trigger以及 queue、cron、state、http 等任意绑定触发器都能直接使用无需任何额外改动。以把外部 webhook 注册为notifications::send为例// Node / TypeScript import { registerWorker } from iii-sdk; const url process.env.III_URL; if (!url) throw new Error(III_URL must be set); const worker registerWorker(url); worker.registerFunction( notifications::send, { url: https://hooks.provider.example.com/notify, method: POST, timeout_ms: 5000, headers: { X-Service: iii-worker }, auth: { type: bearer, token_key: PROVIDER_API_TOKEN }, }, { description: POST a notification to the provider webhook }, );# Python import os from iii import HttpInvocationConfig, InitOptions, register_worker from iii.iii_types import HttpAuthBearer worker register_worker( os.environ.get(III_URL), InitOptions(worker_namenotifications-worker), ) worker.register_function( notifications::send, HttpInvocationConfig( urlhttps://hooks.provider.example.com/notify, methodPOST, timeout_ms5000, headers{X-Service: iii-worker}, authHttpAuthBearer(token_keyPROVIDER_API_TOKEN), ), descriptionPOST a notification to the provider webhook, )// Rust use std::collections::HashMap; use iii_sdk::{ HttpAuthConfig, HttpInvocationConfig, HttpMethod, InitOptions, RegisterFunctionMessage, register_worker, }; let url std::env::var(III_URL).expect(III_URL must be set); let worker register_worker(url, InitOptions::default()); let mut headers HashMap::new(); headers.insert(X-Service.into(), iii-worker.into()); worker.register_function(( RegisterFunctionMessage::with_id(notifications::send.into()) .with_description(POST a notification to the provider webhook.into()), HttpInvocationConfig { url: https://hooks.provider.example.com/notify.into(), method: HttpMethod::Post, timeout_ms: Some(5000), headers, auth: Some(HttpAuthConfig::Bearer { token_key: PROVIDER_API_TOKEN.into(), }), }, ));HttpInvocationConfig 字段说明普通函数接收id handlerHTTP 可调用函数接收idHttpInvocationConfig。各字段含义如下字段类型默认值说明urlstring必填函数被调用时引擎请求的端点。methodGET \| POST \| PUT \| PATCH \| DELETEPOSTHTTP 方法。timeout_msnumber30000单次请求超时毫秒。headersRecordstring, string{}每次调用都会附加的请求头。authHttpAuthConfig无认证配置bearer、hmac或api_key配合token_key、secret_key或value_key使用。仓库中 Node 端的类型定义sdk/packages/node/helpers/src/http/index.ts给出了HttpAuthConfig的三种具体形态{ type: hmac; secret_key: string }基于共享密钥的 HMAC 签名校验{ type: bearer; token_key: string }Bearer Token 认证{ type: api_key; header: string; value_key: string }通过自定义 header 发送 API Key。关键安全机制token_key、secret_key、value_key填写的都是环境变量的名字而不是密钥本身。引擎在注册时从自身进程环境中解析这些变量因此密钥始终停留在引擎宿主上永远不会经 SDK 的 WebSocket 传输。HTTP 错误处理引擎把调用 payload 作为 JSON 请求体发送并将任何非 2xx 响应或网络错误视为调用失败该失败会原样传播回调用方。这类 HTTP 可调用函数会出现在engine::functions::list中见 Using iii / Functions 的 engine 函数表并像进程内 handler 一样可在 console 中被发现。从 SDK 源码看sdk/packages/node/iii/src/iii.tsregisterFunction会区分传入的是函数还是配置对象非函数时把url、method缺省POST、timeout_ms、headers、auth组装进invocation字段随RegisterFunctionMessage发布本地函数表里只存消息、不存 handler。返回值与错误传播一个函数要么返回一个值handler 负责把返回值塑造成与其声明的 response schema 匹配的形状要么返回一个错误。handler 内部抛出的错误会作为调用错误传播回调用方并附带 Worker 侧的堆栈信息Node 转发error.stackPython 转发traceback.format_exc()Rust 转发底层错误的堆栈。引擎不会吞掉这些错误。基于这一区分你可以表达两类失败预期失败返回一个结构化的错误值业务层面的失败结果非预期失败直接throw/raise/ 返回Err让调用方拿到异常与堆栈。注销一个函数registerFunction会返回一个句柄其unregister()方法可以在运行时把函数从引擎移除。当 Worker 断开连接时它注册的所有函数会被自动清理未决pending的调用会直接报错。// Node / TypeScript const add worker.registerFunction(math::add, async (payload) { return { c: payload.a payload.b }; }); add.unregister();# Python add worker.register_function(math::add, add_handler) add.unregister()// Rust let add worker.register_function(RegisterFunction::new(math::add, |input: AddInput| { Ok(serde_json::json!({ c: input.a input.b })) })); add.unregister();从 Node SDK 的实现可以看到sdk/packages/node/iii/src/iii.tsunregister会向引擎发送UnregisterFunction消息并从本地函数表删除该 id。这也解释了函数生命周期与 Worker 连接绑定的语义函数是随 Worker 在线状态动态上下线的能力而非静态配置。延伸阅读触发函数的方式worker.trigger、iii trigger、TriggerAction 的同步/异步/入队语义Using iii / Functions把函数绑定到事件源http、cron、state、queue 等触发器类型Using iii / Triggers基于 metadata 选择器做函数级 RBAC 允许列表worker-manager引擎内置的engine::functions::list等发现与生命周期函数engine protocol 参考【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网