WorkBuddy 资料库安全读写(避坑版)
把 WorkBuddy 内置「资料库」增删改查的正确字段格式与反坑清单固化下来。聚焦纯 API 操作避坑:字段值格式、读取、批量、分页、成功判据、写入前校验、不可逆操作的护栏。不碰 token / 宿主通道那一层。
什么时候用这个 Skill
用户问到这些场景时加载:
- 「往资料库写 url 字段怎么老被清空」
- 「workbuddy 资料库 url / 链接字段格式到底怎么写」
- 「batch_add 一次能写几条?我批量入库怎么分片」
- 「查资料库返回 success 但记录没写进去」
- 「query 资料库怎么翻页,数据好几万条」
- 「资料库 select 选项报 11607 是什么错」
- 用资料库存数据、写自动化同步脚本、做采集入库
核心结论(先读这条)
- 写侧字段值(PropertyValue)和读侧字段值(FieldValue)格式不一样;写错不报错,只是静默失败或清空。
url字段必须写成对象{ "url": { "text": "...", "link": "https://..." } };写成裸字符串"url": "https://..."会被静默清空。- 批量接口单次 1–100 条;超过必须分片。生产环境保守分片 20–50 条更稳。
results[].success: true不等于已落库,接口可能返回成功但没写进去;报送完成前必须回查。- 删除字段 / 批量删除 / 改字段类型 / 删 select 选项是不可逆操作,必须 warn-then-wait。
- 字段名必须与
get_database_schema的实时返回完全一致,不能凭记忆或样例文档构造。
写值正确姿势(PropertyValue 速查)
完整对照表见 @references/value-formats.md。要点:
- 所有写值都是 oneof:
{ "<类型key>": 值 },只能有一个 key,不能混用。 url:{ "url": { "text": "官网", "link": "https://..." } }—— 是对象,不是字符串。select/multi_select:传选项的text或id都可以,但必须是 schema 里已存在的选项,否则报11607(选项不会自动创建)。image:{ "image": { "images": [{ "title": "...", "imageUrl": "https://..." }] } }—— 外层包一层images数组。attachment:数组,每项至少含name/fileType/fileSize/mediaType,确认上传后用attachmentId引用。person:数组,每项{ "id": "uid..." }—— 写只收id,name由服务端回填。phone_number的 key 带下划线,不要写成phone。
读值(FieldValue)与写值的差异
- 读出来的
select只是字符串(选项 text),不是对象。 - 读出来的
url是{ "text", "link" }对象。 - 空字段是
null,不是"";读取时按null判断缺失。 - 读取兜底取值:优先按字段名取;取不到再试
field_id(属踩坑经验,脚本未静态证实,但实践中 server 偶发回 field_id,建议保留兜底取值器)。
批量写入
- 单次最多 100 条、最少 1 条;超过必须分片。
- 生产环境建议保守分片 20–50 条,降低单次失败面。
- 写前必跑
get_database_schema校验字段名与选项;字段名必须和 schema 完全一致。 - 用
scripts/db_write_guard.py做本地 pre-flight(oneof 校验、url 包裹检测、分片尺寸、select 选项预检),确认无误再提交线上接口。
分页查询(游标)
query_database_record返回{ "results": [...], "next_cursor": "...", "has_more": bool }。- 翻页:把上一次返回的
next_cursor作为下一次的--start-cursor,循环直到has_more为false。 - 不要自己估总数或用 offset;用游标才稳,尤其数据量上万时。
false-success 防御
batch_add/batch_update返回results[].success: true不代表已落库,接口可能返回成功但记录没真正写进去。- 报送完成前,必须逐条或抽样用
get_database_record回查确认。 - 既不能用
results,也不能用query_database_record的列表计数当作写入成功的证据。
写入前 schema 校验(必做)
- 字段名:必须与
get_database_schema返回的properties[].name完全一致。 select/multi_select选项:传入的text/id必须已在 schema 的options里;缺失先add_database_field/update_database_field加选项再写,否则11607。person列:只有name时先space.permission.collaborators解析uid,精确命中才写;模糊或多命中绝不自动写。
不可逆操作的护栏(warn-then-wait)
以下操作删除 / 改写数据且不可恢复,绝不自动执行;即使当前消息明确要求,也先回复点名表 + 字段 / 记录集、说明不可恢复、等用户明确许可后才执行:
- 删除字段(
delete_database_field) - 批量删除记录(
batch_delete_database_records) - 改字段类型(
update_database_field改 type)——无法转换的单元格会被清空 - 删除
select/multi_select已有选项——引用该选项的每个单元格被清空
纯改名(rename)无风险,不需要护栏。
本地预检脚本
scripts/db_write_guard.py 不联网、不收 token、不调任何资料库接口:输入 schema(JSON)和待写 records(JSON),输出规范化 / 分片后的 payload 与问题清单,覆盖 oneof 校验、url 包裹层检测、分片尺寸(1–100)与 select / multi_select 选项对 schema 预检。使用方式见脚本内 docstring。
红线(绝不做)
- 不写 token / 凭据相关逻辑(属层 2)。
- 不自动执行删除字段 / 批量删除 / 改字段类型 / 删选项(必须 warn-then-wait)。
- 不因
results[].success: true就断言写入成功。 - 不把
url写成裸字符串。 - 不从记忆 / 样例文档构造字段名,必须现场
get_database_schema。