工作台技术说明
工作台是怎么跑起来的、清单文件怎么写,以及它能调用的每项 Floatboat 能力(模型、语音识别、Agent、Workflow、连接的服务、日程、自动化、空间)能做什么、怎么调、有哪些限制。
这一页写给想把工作台做深一点的人:你自己写代码,或者想看懂 Agent 替你写的代码。平时用工作台不用读这页,看工作台就够了。
这里的内容对应当前正式版里的工作台 SDK 1.0.0。SDK 就是工作台用来调用 Floatboat 的那套代码接口。
一个工作台由什么组成
先有个整体印象:工作台其实是一个跑在你电脑上的小网页应用,分三块。
| 部分 | 用什么写 | 做什么 |
|---|---|---|
| 页面 | React + TypeScript(TSX) | 你在 Floatboat 里看到、点到的界面 |
| 服务端 | TypeScript,跑在 Node 22 上 | 存取数据、调用 Floatboat 的能力、给页面提供 /api/* 接口 |
| 数据库 | SQLite | 这个工作台自己的业务数据 |
Node 随 Floatboat 一起装好了,你不用另外装。每个空间最多放一个工作台,它在窗口里作为一栏显示,和文件浏览器、浏览器、对话并排。
目录结构
想自己改代码、或者让 Agent 改某个文件时,先知道东西都放在哪。用模板新建的工作台长这样:
manifest.json 工作台的身份、版本、要用的能力
package.json npm 依赖
pnpm-lock.yaml 依赖锁文件
index.html 页面入口
src/ui/ 页面代码(React)
src/server/index.ts 服务端入口
src/contracts/ 页面和服务端共用的类型
migrations/ 数据库迁移,0001_initial.sql 起
tests/ 测试
ENGINEERING.md 写给 Agent 的工程说明
DESIGN.md 写给 Agent 的设计说明在 Floatboat 里用对话做的工作台,源码在 ~/.floatboat/<空间 ID>/workbench/。用外部 Agent 开发时结构一样,只是放在你自己的仓库里,业务代码在 app/ 下。
构建和运行
改完代码没生效、或者构建报错时,看这一节。每次你保存或 Agent 改完,Floatboat 会依次做四件事:
- 检查类型。 用严格模式,不允许显式的
any,也不允许用注释跳过类型检查。 - 打包服务端。 用 esbuild 打成
dist/server/index.mjs。 - 打包页面。 用 Vite 打成
dist/ui。工作台自己的 Vite、PostCSS 配置和scripts不会被执行,所以改这些不会有效果。 - 启动服务。 用随包的 Node 运行服务端,只监听本机
127.0.0.1的一个随机端口,别的电脑连不进来。
页面代码里如果引入了 node:*、服务端代码或 SDK 的服务端模块,构建会报错(WORKBENCH_BROWSER_SERVER_IMPORT)。也就是说,读文件、连数据库这类事要放在服务端做,页面再通过 /api/* 去拿。
任何一步没通过,工作台会继续跑上一个能用的版本。你改坏了也不会把正在用的工作台弄崩。
什么时候在运行
如果你在意后台任务会不会被打断,看这几条:
- 关掉面板不会停。 点「关闭面板(工作台继续运行)」后,服务还在后台跑,正在做的任务不受影响。
- 退出 Floatboat 会停。 服务有 3 秒收尾,没做完的任务会标记为中断。
- 「随 Floatboat 启动」(默认关):打开后,每次启动 Floatboat 都会把工作台跑起来。它只跑上次能用的版本,不会去编译还没检查过的改动。这个开关只在你这台电脑上生效,分享或上架时不会带走。
- 自动更新(默认关):点「开启自动更新」后,源码一保存就自动检查、构建并换成新版本;检查不通过就继续用原来的版本。
第一次运行一个工作台时,会弹出「信任本地工作台」,写着「此程序及随包依赖可以访问计算机上的文件和网络。请仅运行你信任来源的代码。」点「信任并运行」才会启动。
清单文件 manifest.json
清单写明工作台是谁、哪个版本、要用哪些能力。Floatboat 会严格检查,写了它不认识的字段就会拒绝,所以拼写要和下面完全一致。
{
"schemaVersion": 1,
"appId": "workbench.2f1c9a7e-3b8d-4e51-9c0a-6d4f8b2e1a37",
"registryName": "shop-inventory",
"name": "网店库存",
"description": "记录进货和销量,提醒补货",
"version": "1.2.0",
"minDesktopVersion": "0.5.11",
"sdkVersion": "1.0.0",
"runtime": "node22-sqlite-v1",
"build": {
"profile": "typescript-react-v1",
"server": "src/server/*.ts",
"ui": "index.html"
},
"capabilities": ["llm", "calendar"]
}| 字段 | 说明 |
|---|---|
schemaVersion | 固定 1 |
appId | 本机唯一 ID,Floatboat 新建时生成,不用改 |
registryName | 上架后的永久名字,之后改不了。只能用小写字母、数字、- 和 _,见规则一览 |
name / description / changelog | 名称(最多 120 字)、简介(最多 2000 字)、更新说明(最多 10000 字) |
version | 版本号,主.次.修订,新建时是 0.1.0 |
minDesktopVersion | 可选。需要的最低 Floatboat 版本,低于它会拒绝构建(WORKBENCH_UPDATE_CLIENT_TOO_OLD) |
sdkVersion / runtime / build | 固定为上面示例里的值 |
migrations | 数据库迁移目录 |
capabilities | 要用的 Floatboat 能力,见下一节,最多 8 项 |
contributions | 可选。在工作台里加命令、侧栏、工具栏或面板入口,最多 30 个 |
dependencies | 可选。依赖的 Workflow 或 Combo,最多 64 个 |
resources | 可选。要用的文件或文件夹,最多 64 个 |
connectedServices | 可选。要用的连接服务,最多 32 个;写了它,capabilities 里也要有 connected-services |
license | 可选。许可证,最多 100 字 |
能调用哪些 Floatboat 能力
写文字、听写、请 Agent 干活、读你的日程这些事,工作台要借 Floatboat 的能力来做。
调用方式是在服务端用 app.host。用哪项,就在 capabilities 里写哪项,没写的调用会被拒绝。写了也不等于一定能用:每次调用 Floatboat 都会再查一遍,比如工作台有没有启用、你有没有登录、账号有没有开通工作台。
capabilities 里写 | 界面上叫 | 能做什么 |
|---|---|---|
llm | 模型调用 | 让模型写一段文字、按格式输出 JSON,或者边写边显示 |
asr | 语音识别 | 把对着麦克风说的话实时转成文字 |
agent | Agent 任务 | 把一件要动手的事交给 Agent 在后台做完,或者和 Agent 来回对话 |
workflows | 工作流 | 运行你已有的 Workflow |
connected-services | 连接的服务 | 用你在 Floatboat 里连接过的账号调用外部服务 |
calendar | 日程 | 查、建、改、删日程 |
automation | 自动化 | 创建和管理定时让 Agent 执行的自动化 |
workspaces | — | 列出、新建、切换空间,或者在空间里打开一个网页 |
有一个调用不用声明就能用:capabilities.list。它会告诉你每项能力此刻能不能用(available 能用、not-declared 没声明、account-required 需要登录或开通账号),以及每个操作的参数格式。开发时先调它,比看文档更准。
所有调用默认 120 秒超时,单次请求不超过 2 MiB。耗时长的活交给 agent 或 workflows,再用它们的 events 跟进度。
模型调用 llm
需要 AI 帮你写一段话、改写、归类、提取信息时用它。比如老师把全班的作文评语交给它,让它按「优点 / 要改进」整理成固定格式;做市场的把一周的活动数据交给它,让它写成一段周报。
app.route('POST', '/api/summary', async ({ body, host }) => {
const result = await host.llm.generateText({
system: '你是网店运营助手,回答简短。',
prompt: `把今天的入库记录写成一段话:${JSON.stringify(body.records)}`,
maxOutputTokens: 800,
});
return { summary: result.text };
});三个操作,按你想怎么拿结果来选:
| 操作 | 用途 |
|---|---|
generateText | 一次返回完整文字 |
generateObject | 按你给的 JSON Schema 输出,返回结果里带解析好的 object,适合直接存进数据库 |
streamText | 一边生成一边返回,适合在页面上逐字显示 |
- 输入:
prompt最多 20 万字符,system最多 3 万字符,maxOutputTokens1–32768(默认 4096),temperature0–2。generateObject的 Schema 最多 5 万字符。 - 模型:
model可以不填,用平台默认的轻量模型;要填的话,得是你账号能用的模型。 - 只有单轮文字:不能输入图片,也不能让模型调用工具。它只会「写」,不会「做」:要它读文件、跑命令、上网查资料,用
agent。 - 用量:和平时对话一样,算在你当前登录的账号上,会用掉积分。返回结果里有
usage,写着输入、输出各用了多少 token。
语音识别 asr
想让工作台「听写」时用它:说话,实时出文字。比如店主边盘货边念「A 款进了 20 件」,直接记进库存;自由职业者开会时把客户的要求实时记下来;学生把课上的要点口述成笔记。
声明了 asr,工作台页面才会被允许用麦克风。流程是三段接力:页面在用户点按钮后用 getUserMedia 录音,把音频交给自己的服务端,服务端再转给 Floatboat 识别。
const { streamId } = await host.asr.start({ sampleRate: 16000, format: 'pcm', hotwords: ['补货', '入库'] });
await host.asr.sendFrame({ streamId, sequence: 0, pcmBase64 });
for await (const event of host.asr.events({ streamId })) {
// 实时拿到识别出的文字
}
await host.asr.stop({ streamId });- 音频格式:16 kHz、单声道、16 位 PCM,每帧不超过 128 KiB,用 base64 传。浏览器录到的音频通常不是这个格式,页面或服务端要先转一下。
- 热词:最多 100 个,每个最多 100 字。热词是你提前告诉它「这些词会出现」,比如店名、学生名字、产品型号。不常见的词放进去,识别更准。
- 可选:
semanticPunctuationEnabled自动加标点,speakerDiarizationEnabled区分不同说话人(适合会议记录)。 - 限制:同时最多 8 路;60 秒没收到新的音频帧会自动结束。录音中途停掉过,要重新
start一路。 - 操作:
start、sendFrame、events、get、stop、cancel。
Agent 任务 agent
有些事不是「写一段话」,而是要真的动手:打开文件、跑命令、上网查。这时交给 Agent。比如做销售的让它「读这个文件夹里的报价单,整理成表」;行政让它把一堆发票 PDF 汇总成报销清单。
它在工作台所在的空间里后台运行,用的是和平时对话一样的 Floatboat Agent,能跑命令、改文件、查网页。
const run = await host.agent.start({
prompt: '把 ~/报价单 里的 PDF 汇总成一张 CSV,按供应商分组。',
workingDirectory: '/Users/me/报价单',
idempotencyKey: `quote-${Date.now()}`,
});
for await (const event of host.agent.events({ runId: run.runId })) {
// 进度和结果
}- 两种用法:
start跑一次就结束;conversations.create/send是和 Agent 来回对话,聊天记录会保存下来。 - 输入:
prompt最多 10 万字符;workingDirectory要填一个已存在的绝对路径,Agent 就在这个文件夹里干活。 idempotencyKey必填。 它是你给这次任务起的唯一编号。网络抖了一下、你的代码重试时,Floatboat 认出是同一个编号,就不会把同一件事做两遍。所以每件不同的任务要用不同的编号,同一件任务重试时用同一个。- 时长:默认最多 10 分钟,可以设到 60 分钟。同时最多 8 个。耗时很长的活要拆开,或者改用
workflows。 - Combo:清单里依赖的 Combo 准备好后,会一起加载给 Agent 用;必需的 Combo 没准备好时报
WORKBENCH_CONFIGURATION_REQUIRED。 - 用量:和平时让 Agent 做事一样,会用掉积分。
- 操作:
start、get、list、findByIdempotencyKey、events、cancel,以及conversations.create、list、get、messages、send。
工作流 workflows
如果你已经有一条做好的 Workflow,工作台可以直接按一下就跑它,不用重新写一遍。比如做市场的在工作台里放个按钮,一点就跑「每周竞品汇总」那条 Workflow。
- 清单里写了
dependencies时,用dependency指定依赖的 ID;没写时,可以用name按名字找。 - 可以指定
workspaceId在别的空间里跑,最长 24 小时。 - 收费的 Workflow 照常按它的价格收费。
cancel只能取消这个工作台自己发起的运行,你在别处手动跑的它管不到。- 操作:
start、get、list、findByIdempotencyKey、events、cancel。
连接的服务 connected-services
想让工作台读写你在别处的数据时用它,前提是这个账号已经在 Floatboat 里连接过。比如行政读一份在线表格里的报名名单,产品经理把整理好的需求写进团队的在线表格。
- 先在清单的
connectedServices里写要用哪家,比如{ "id": "sheet", "provider": "connector:<名字>" },再在capabilities里声明connected-services。 - 还没连接时,调
requestConnection或connect,Floatboat 会弹出「连接」加服务名的窗口,让你授权。连接请求 10 分钟内有效,过期了要重新发起。 - 工作台拿不到你的密码或令牌。 账号授权都留在 Floatboat 里,返回的结果也会去掉敏感信息。
- 写入类的调用失败后不会自动重试,免得同一条数据写两遍。要不要重试,由你的代码自己判断。
- 想知道有哪些服务、每个服务能做什么,用
searchProviders、search、describe查目录。 - 操作:
searchProviders、search、describe、listConnections、getConnection、requestConnection、getConnectionRequest、cancelConnectionRequest、connect、call。
日程 calendar
让工作台帮你排时间、查安排。比如自由职业者的接单工作台,客户确认后自动把交付日期放进日程;学生把这学期的考试安排一次性建进日历。
- 可以操作 Floatboat 日历(默认),也可以操作电脑的系统日历。
- 新建日程要同时给时间和时区,标题最多 300 字。
- 删除重复日程时,要说明删这一次、以后的,还是全部。
- 操作:
search、get、create、update、delete。
自动化 automation
让工作台替你设一个定时任务,到点让 Agent 去做。比如店主设「每天早上 9 点检查库存,低于 10 件就列出来」,做市场的设「每周一整理上周的数据」。详见自动化。
- 新建和修改分两步:先
preview拿到预览和一个 10 分钟有效的令牌,再用完全相同的内容调create或update。这样能保证真正建出来的,就是用户看过的那一份。 - Floatboat 不会替你弹确认框。 预览结果要由工作台自己显示给用户,用户点了确认再创建。
- 要写明信任级别,也就是允许 Agent 动手到什么程度;执行方式默认只读。
runs能查最近的运行记录,默认 30 天内,最多查 366 天、200 条。- 操作:
preview、create、update、archive、list、get、runs。
空间 workspaces
让工作台帮你管理空间、打开网页。比如产品经理的项目工作台,点一下就为新项目建好空间,再打开项目的需求文档页面。
list、create、activate:列出、新建、切换空间。新建时可以指定文件夹,也可以选用独立的浏览器身份。openUrl:在空间里打开一个网页,只支持http和https。- 切换空间和打开网页要 Floatboat 窗口正开着,否则返回
WORKSPACE_UI_UNAVAILABLE,所以后台自动跑的代码要处理好这个错误。
不经过 Floatboat 的部分
工作台自己的事,比如给页面提供接口、存数据,都在它自己的服务端里完成,不用声明能力:
| 接口 | 用途 |
|---|---|
app.route(method, path, handler) | 给页面提供接口,路径以 /api/ 开头,支持 GET、POST、PUT、PATCH、DELETE |
app.database | 工作台自己的 SQLite(Node 内置的 node:sqlite) |
app.action(name, handler) | 响应清单 contributions 里加的按钮和命令 |
app.trackTask(fn) / app.tasks() | 记录长任务的状态,退出时没做完的会标记为中断 |
app.resources.path(id, rel) | 拿到清单 resources 里文件或文件夹的真实路径,不允许跳出这个范围 |
页面这边用 connectWorkbenchPage() 拿到 page.fetch,它只能请求自己服务端的 /api/*。
数据怎么存
想知道数据会不会丢、换电脑怎么带走,看这一节。
- 数据库在
<Floatboat 数据目录>/workbench/data/<appId>/data.sqlite,和源码分开放。改代码、重新构建都不会丢数据。 - 表结构用
migrations/NNNN_名字.sql管。迁移文件就是一条条「改表」的记录,按编号依次执行。它只能往后加:要改表,就新写一个文件,别去动老的。已经执行过的迁移文件被改了或删了,启动会报错。 - 分享或导出时,可以选择带不带数据。
和对话里的 Agent 怎么配合
- Agent 写代码。 你在空间里让 Agent 改工作台,它会用专门的工具新建工作台、读编写指南、检查构建,并用
workbench_runtime打开、查看日志、停止或重装工作台。 - Agent 知道你的工作台在哪。 每次对话,Floatboat 都会把当前空间里工作台的源码位置和说明文档告诉 Agent,你不用每次重复。
- 你可以让 Agent 打开它。 说一句「在旁边打开网店库存」,它会调用
workbench.open,见 Agent 能调用的客户端动作。 - 方向是单向的。 工作台可以用
agent能力请 Agent 干活;但工作台不能给对话里的 Agent 加新工具。
依赖与资源
npm 依赖
想在工作台里用别人写好的 npm 包时,注意这几条:
- 只支持纯 JavaScript 包。带原生二进制(
.node、.dll、.dylib、.so、.exe)的包会被拒绝,所以一些要编译的包(常见于图片处理、数据库驱动)用不了,挑包时先看一眼。 - 锁文件要用 pnpm 的
pnpm-lock.yaml,每个依赖都要带完整性校验。 - 安装时,Floatboat 用自带的 pnpm 从
registry.npmjs.org下载,不执行任何安装脚本,也不读你电脑上的 npm 配置。所以你本机设的镜像源、私有源在这里不起作用。 - 依赖加起来不超过 100 MiB、1 万个文件;源码不超过 100 MiB。
- 模板预装了 React 19 和 lucide-react 图标库。
依赖的 Workflow 和 Combo
工作台要用到某条 Workflow 或某个 Combo 时,在 dependencies 里声明。每项可以来自三个地方:
bundle:直接打包在工作台里,跟着一起走。binding:安装后让用户从自己已有的里面选一个。registry:指定市场上某个确切版本。
用户可以在工作台的「依赖与资源」里查看和调整。
文件和文件夹
工作台要读写某个文件或文件夹(比如一份素材、一个导出目录),在 resources 里声明:
| 字段 | 取值 |
|---|---|
kind | file 或 directory |
role | asset(只读素材)或 data(读写数据) |
location | project(工作台源码里,只能放素材)、data(数据目录里)、external(用户自己选一个位置) |
access | read 或 read-write |
transfer | 分享或导出时,snapshot 带上一份副本,rebind 让对方重新选位置(只有 external 能用) |
安全边界
运行别人的工作台之前,值得花一分钟看完这几条:
- 工作台的服务端是一个普通的 Node 程序。 它能读写你电脑上的文件,也能联网。这也是第一次运行时要你点「信任并运行」的原因:只运行你信任的工作台。
- 页面被关在框里。 页面只能请求自己服务端的接口,不能直接连外部网站;只有声明了
asr才能用麦克风。 - 拿不到你的账号凭据。 Floatboat 的登录信息、模型密钥、连接服务的令牌都不会交给工作台。
- 每次调用都会重新检查。 退出登录、停用工作台、换账号之后,之前的调用会立刻失效。
上架时「内容详情」里写的是什么
上架时在窗口里展开「内容详情」,会看到几项和本页对应的信息,方便你核对:
| 内容详情里 | 对应 |
|---|---|
| 运行时 | runtime,即 node22-sqlite-v1 |
| SDK 版本 | sdkVersion,即 1.0.0 |
| 能力 | capabilities 声明了几项 |
| 最低客户端版本 | minDesktopVersion,写了才显示 |
上架流程见上架工作台。
不支持的
现在的 SDK 还没有这些:语音合成(把文字念出来)、图片生成、向量和 embedding、给用户发通知、让工作台向对话里的 Agent 提供工具。
真需要的话,有两条路:用 agent 让 Agent 去做,或者在服务端自己接第三方服务。
接下来
- 在自己电脑上用 Claude Code、Codex 写:用外部 Agent 开发工作台
- 写好了想上架:上架工作台
- 先在 Floatboat 里做一个试试:工作台