跳到正文

进阶

自定义身体适配器

用几十行 TypeScript 让一台新设备成为身体——实现 BodyAdapter 接口,独立构建,用 WINDLER_ADAPTER 指定。

#适配器是什么

运行基座的核心不知道自己跑在手机、树莓派还是服务器上。设备的一切——传感器采样、系统通知、播放声音、设备动作——都由身体适配器提供。适配器是一个独立构建的 ES 模块,默认导出一个 BodyAdapter 对象。

仓库自带一个平台级的参考实现:runtime/adapters/termux/(任意安卓手机 + Termux:API,传感器按名字探测),构建为 dist/termux.mjs。

#接口

typescript
import type { BodyAdapter, RawSample, AdapterTool } from "./adapter";   // 只能 import type

const adapter: BodyAdapter = {
  name: "my-board",
  describe: "一块装在窗台上的开发板,有光线传感器和一个蜂鸣器",
  async init() { /* 可选:打开设备、检查依赖 */ },
  async sample(): Promise<RawSample> {
    return {
      battery: { level: 0.82, charging: true, tempC: 31 },
      lux: await readLux(),
      motion: 0,
      extra: { humidity: 46 },         // 设备特有的读数,原样进入身体孪生
    };
  },
  async notify(title, text) { /* 系统通知:配对码、主动消息的本地出口 */ },
  async playAudio(file) { /* 播放语音合成的结果 */ },
  tools: [
    {
      name: "beep",
      description: "让蜂鸣器响一声",
      parameters: { type: "object", properties: { ms: { type: "number" } } },
      permission: "device",            // 必须是闸门已知的能力类别
      handler: async ({ ms }) => { await beep(ms ?? 200); return "响了"; },
    },
  ],
};
export default adapter;
成员必需说明
name / describe是describe 一句话描述这具身体,写进 ta 的自我认知
sample()是一次物理采样,字段全部可选:有什么报什么
init()否启动时调用一次
notify()否本地系统通知;没有它配对码只能从令牌文件读
speak() / playAudio()否说话 / 播放音频文件
tools否设备动作,每个声明所属能力类别(device、camera、microphone、location…)
hands否预留:看屏幕、点击、输入、打开应用

#约束

  • 适配器只能 import type 接口文件的类型,不得依赖核心的其他实现——构建时类型被擦除,产物不依赖核心。
  • 工具的 permission 必须是闸门已知的类别,否则按「允许」处理。
  • 加载失败时核心回退到通用适配器(无传感器),并在日志里说明。

#构建与指定

bash
npx esbuild my-adapter.ts --bundle --platform=node --target=node22 --format=esm --outfile=my-adapter.mjs
WINDLER_HOME=~/windler WINDLER_ADAPTER=$PWD/my-adapter.mjs node --enable-source-maps main.cjs

也可以写进配置 config/windler.json 的 adapter 字段。

#采样如何变成感受

图表加载中…

采样间隔自适应:有显著变化时 2 分钟,平静时逐步拉长到 10 分钟。采样不调用模型、不等于醒来。

#Termux 适配器提供了什么(参考)

sample():电量 / 充电 / 体温 / 健康、光照与运动(传感器按名字探测,没有就不报);notify()(带「打开 Windler」按钮)、playAudio();工具 take_photo、record_audio、location、vibrate、torch、clipboard、read_sensor。不提供 speak(很多手机没有系统 TTS),说话由运行基座的 voice_speak(Azure 语音)完成。

完整类型见 适配器接口。