让飞书多维表格CRUD一句话跑通:OpenClaw技能包实战
简介这套飞书多维表格 OpenClaw 技能包面向希望用低代码方式搭建业务应用的非技术用户与团队管理者将创建、读取、更新、删除等 CRUD 操作封装为可一键安装的模板解决从零搭表、重复录入与协作管理效率低的问题。包体共 13 个文件、压缩后仅 55KB以 md 文档为主体涵盖字段类型映射、自动化工作流、权限指南与公式参考配合 Python 脚本及 JSON 配置完成核心操作另含安装脚本与辅助文件便于快速部署。目前已有 90 人学习下载。实际使用中无需复杂编程即可按项目管理、客户维护、库存跟踪等场景灵活调整模板既适合刚接触 OpenClaw 技能的入门读者也能为需要扩展自定义流程的进阶用户提供可复用脚本与排错思路是一份轻量实用的技能模板组合。1. 从「打开表格手动改」到「一句话跑通 CRUD」飞书多维表格 OpenClaw 技能到底解决什么团队把客户、任务、库存都收进飞书多维表格之后真正的麻烦才刚开始每天大量的「查一下、改一下、删一条」操作要么打开表格手动找行要么写个用完就扔的临时脚本。这个技能包要做的是把飞书多维表格的日常 CRUD 封装成 OpenClaw 能调用的技能——你直接用自然语言说「把预算表里超支的三条标记一下」「查一下本周到期的合同」OpenClaw 解析意图后调用脚本脚本直连多维表格 API 执行增删改查再把结构化结果翻译回人能读的话。适合手里有三五个表格天天要维护、又不想给每个人都开编辑权限的运营、项目与售前团队。2. 动手前先摸清两头OpenClaw 技能触发机制与多维表格 API 关键参数2.1 OpenClaw 的「技能」不是机器人插件而是一个带描述的脚本目录很多人把 OpenClaw 的技能当成黑匣子其实拆开看就是一个「带说明书的脚本目录」。常见做法是一个技能目录里放一个描述文件SKILL.md 或 skill.yaml、一个可执行脚本、一份依赖声明。描述文件决定了框架在什么场景下调用它脚本负责干实事。它和你熟悉的「插件」有个明显差异插件的边界是功能技能的边界是意图。比如同样一个「查合同」技能你问「这个月到期的合同有哪些」和「把张三那份合同金额调成 20 万」框架会根据描述文件里的语义指引把请求路由到同一个技能、但不同 action 上。这个机制对飞书多维表格特别合适因为表格操作天然是「动词 对象 条件」结构查、建、改、删是动词表名是对象筛选条件是修饰语。把这一层拆成参数技能就活了。我一般会让描述文件把参数写得尽量细比如注明「table 必须是 config.json 里 datasources 中已配置的表名」避免模型猜出一个不存在的表。另外一个容易被忽略的点OpenClaw 跑技能脚本时走的是 HTTP API和你用云端模型还是本地模型无关。就算你切了本地小模型技能照样能跑因为模型只负责把自然语言翻译成参数真正干活的是脚本。2.2 多维表格 API 五件套tenant_access_token、app_token、table_id、record_id 与字段类型写代码之前先记住飞书多维表格 API 的四个核心标识少一个都调不通。第一个是 tenant_access_token应用级凭证所有业务请求都要带它做鉴权第二个是 app_token定位到具体某一个多维表格第三个是 table_id定位到表格里的某一张数据表第四个是 record_id定位到表里的某一行记录。前三个找起来其实不用查文档打开任意一个多维表格地址栏长这样https://xxx.feishu.cn/base/Abc123DEF?tableTbl456viewvew789/base/后面那段就是 app_token?table后面那段就是 table_id。record_id 则要靠查询接口返回。第五个概念是字段类型。多维表格的列为文本、数字、单选、多选、日期、人员等类型API 传值时格式各不相同数字字段直接传数值单选字段传选项文本多选字段传字符串数组日期字段传毫秒时间戳或「2024-01-01」这类格式化字符串。这个设计看似简单实际最容易翻车后面避坑章节会专门展开。创建或更新记录时payload 是一个fields对象key 是字段名value 按字段类型决定例如{客户名称: 某某科技, 合同金额: 120000, 标签: [重点, 新签]}。2.3 技能包目录结构zip 解压之后你该看到哪些文件既然标题带「一键安装.zip」技能包的结构就得从一开始就定好不然后面交付时手忙脚乱。我一般会按下面的目录组织feishu-bitable-skill/ ├── SKILL.md # 技能描述给 OpenClaw 读 ├── skill.py # 主脚本CRUD 全在这里 ├── config.example.json # 配置模板复制成 config.json 后填 ├── requirements.txt # 依赖声明目前只需要 requests └── install.sh # 一键安装脚本做依赖检查和目录部署SKILL.md 是 OpenClaw 判断「什么时候该用这个技能」的依据内容质量直接影响触发准确率。skill.py 是核心实现建议把所有动作收敛成query / create / update / delete四个子命令方便框架按参数调用。config.example.json 放 app_id、app_secret 和表名映射这样用户安装时不需要改任何代码。install.sh 做三件事检查 Python 版本、安装 requests、把整个目录链接到 OpenClaw 的技能目录。在写技能代码之前先把这个结构立起来后面每写一段代码都知道该放哪。3. 从零跑通「查询记录」拿 token、翻页、注册成技能的完整链路3.1 在飞书开发者后台创建自建应用并开通多维表格读写权限第一步是去飞书开发者后台创建一个企业自建应用拿到app_id和app_secret。路径是「开发者后台 → 创建应用 → 企业自建应用」创建完成后在「凭证与基础信息」页面能看到这两个值。注意app_secret 只在创建时完整展示一次后面再进页面只能重置建议拿到就先存进自己的密码管理器。接着去「权限管理」里开通多维表格相关权限。搜索框里搜「多维表格」或「bitable」把读写相关权限勾上然后一定要在「版本管理与发布」里创建一个版本并发布。很多第一次做的人在这里卡住权限勾了但没发布版本token 能拿到但接口一直报权限不足。发布后回到多维表格页面点右上角「...」→「添加文档协作者」在搜索框里输入你刚创建的应用名称把它加为表格协作者。这一步最容易被漏应用有 API 权限不等于它能访问某张具体表格你必须把它加到协作者列表里。提示自建应用本质是一个「机器人身份」不是某个用户的身份。所以它的权限 后台勾选的权限 被添加为协作者的文档范围两者缺一不可。准备工作做完写一个最小的 token 获取脚本验证链路# get_token.py import os import requests APP_ID os.getenv(FEISHU_APP_ID, ) APP_SECRET os.getenv(FEISHU_APP_SECRET, ) BASE https://open.feishu.cn/open-apis def get_tenant_token(): 获取 tenant_access_token有效期约 2 小时 resp requests.post( f{BASE}/auth/v3/tenant_access_token/internal, json{app_id: APP_ID, app_secret: APP_SECRET}, timeout10, ) data resp.json() if data.get(code) ! 0: raise RuntimeError(ftoken 获取失败: {data}) return data[tenant_access_token] if __name__ __main__: print(get_tenant_token())这段代码里唯一需要解释的是接口路径/auth/v3/tenant_access_token/internal是「企业自建应用」专用拿到的是应用身份令牌不是用户身份令牌。响应里的code为 0 表示成功非 0 时不要只看 code把整个 data 打出来里面会明确提示是 app_id 错误还是 secret 错误。token 默认两小时过期技能脚本里每次执行都重新获取不要存缓存省得处理过期逻辑。3.2 第一个能跑的查询用 Python 列出一条记录token 能拿到接下来写查询。查询是 CRUD 里最安全的动作也最适合用来验证 app_token 和 table_id 是否正确。先写一个只查一页数据的函数# query_records.py import os import requests BASE https://open.feishu.cn/open-apis def get_tenant_token(): # ... 同上文的 token 获取逻辑 ... def query_records(app_token, table_id, page_size100, page_tokenNone): 查询多维表格记录返回 (records, has_more, next_page_token) token get_tenant_token() url f{BASE}/bitable/v1/apps/{app_token}/tables/{table_id}/records headers {Authorization: fBearer {token}} params {page_size: page_size} if page_token: params[page_token] page_token resp requests.get(url, headersheaders, paramsparams, timeout10) data resp.json() if data.get(code) ! 0: raise RuntimeError(f查询失败: {data}) records data[data] return ( records.get(items, []), records.get(has_more, False), records.get(page_token, None), ) if __name__ __main__: app_token input(app_token: ).strip() table_id input(table_id: ).strip() items, has_more, _ query_records(app_token, table_id, page_size10) print(f返回 {len(items)} 条还有更多: {has_more}) for item in items[:3]: print(item[record_id], item[fields])注意headers里Authorization的值必须是Bearer加 token中间有一个空格拼错会报鉴权失败。page_size最大可设到 500示例里先用 10 验证链路。返回体里items是记录数组每条记录包含record_id和fields两个字段——record_id是后续更新和删除的唯一依据fields就是这行数据的列值映射。跑通这一步说明你的应用身份、表格访问权限、接口路径全部正确。如果返回空列表先别怀疑代码回到表格里确认有没有数据或者看看 app_token 是不是从分享链接里复制的——分享链接里的串经常不是真正的 app_token这个坑第 5 章会细说。3.3 用 SKILL.md 声明触发条件让 OpenClaw 在对话中认出这个技能脚本能跑了但 OpenClaw 怎么知道该在什么时候调用它这就靠 SKILL.md。我一般用 Markdown 带 front-matter 的写法把技能名、用途描述、参数说明放在最前面让框架做意图匹配时能快速命中--- name: feishu-bitable description: 查询、创建、更新、删除飞书多维表格中的记录。当用户提到查数据、看任务、改状态、新增一行、删记录、表格里有什么时使用。 arguments: action: type: string enum: [query, create, update, delete] description: 要执行的操作 table: type: string description: 表名必须是 config.json 里 datasources 中已配置的名称 record_id: type: string description: 记录 IDupdate 和 delete 时使用 fields: type: object description: 字段名到值的映射create 和 update 时使用 --- # 飞书多维表格 CRUD 技能 操作飞书多维表格的查询、创建、更新、删除。 执行方式python skill.py action --table 表名 [--record record_id] [--json fields]description 里要把触发场景写具体尤其是「当用户提到……时使用」这一段。写得太泛模型什么请求都往这里发写得太窄该触发时不触发。我自己的习惯是覆盖高频说法查数据、看任务、改状态、新增一行、删掉这条记录。称呼上「表格」「多维表格」「数据表」都提一句模型对同义词的把握没你想的那么稳。arguments 部分声明了四个参数其中action用枚举限定防止模型编造出不存在的操作。到这里最小链路已经成立用户在 OpenClaw 里说一句话 → 框架匹配到 feishu-bitable 技能 → 按 arguments 提取参数 → 执行python skill.py query --table 项目表→ 脚本连飞书 API 取数 → 结果返回给模型组织成自然语言回答。先把这个闭环跑通再往下补完整的增删改。4. 把日常 CRUD 补全创建、更新、删除的接口与命令行参数设计4.1 创建记录单条 POST 与批量 batch_create创建记录是 CRUD 里最常用的写操作。单条创建的接口是POST /open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records请求体就是{fields: {...}}。实现起来非常简单# skill.py 中的创建函数 import json import requests def create_record(app_token, table_id, fields): 创建一条记录返回新记录的 record_id token get_tenant_token() url f{BASE}/bitable/v1/apps/{app_token}/tables/{table_id}/records headers {Authorization: fBearer {token}} resp requests.post(url, headersheaders, json{fields: fields}, timeout10) data resp.json() if data.get(code) ! 0: raise RuntimeError(f创建失败: {data}) return data[data][record]调用时传入的fields必须和表结构对齐。文本字段传字符串数字字段传数值单选字段传选项文本多选字段传字符串数组。例如往「任务表」加一行new_record create_record( app_token, table_id, { 标题: 部署飞书技能到生产环境, 优先级: 高, # 单选 预估工时: 4, # 数字 标签: [运维, 自动化] # 多选 } ) print(new_record[record_id])一次性创建多条时用批量接口POST .../records/batch_create请求体是{records: [{fields: {...}}, {fields: {...}}]}。批量接口的意义不只是少写几个请求而是保证一批数据要么都成功、要么都失败避免数据半落不落。批量创建单次上限 100 条超过要分批。真要导入几千条旧数据建议分批加间隔否则容易触发接口限流。4.2 更新记录先查 record_id 再精准更新更新要先拿到 record_id。最直接的方式是用户在对话里把某条记录的特征告诉你但模型通常只能给出「把标题为 X 的记录改成 Y」这种条件给不出 record_id。所以技能里要有一个「先查后改」的流程先用查询接口按条件找到 record_id再调更新接口。def update_by_condition(app_token, table_id, condition_field, condition_value, new_fields): 按条件匹配第一条记录并更新 items, has_more, _ query_records(app_token, table_id, page_size100) target None for item in items: if str(item[fields].get(condition_field)) str(condition_value): target item break if not target: raise RuntimeError(f没有找到 {condition_field} {condition_value} 的记录) return update_record(app_token, table_id, target[record_id], new_fields)这段逻辑里有两个实用细节。第一匹配时用str()包裹两边再比较因为同一列里可能混着数字和字符串比如「状态」列有的行是文本「已完成」有的行因为历史原因存成了数字 1直接比较会漏匹配。第二这个实现只匹配第一条如果表格里有重复记录建议在提示词层面让模型补充更多条件或者改用 view_id 过滤而不是在代码里做复杂去重。更新接口本身很简单PUT .../records/{record_id}请求体同创建传{fields: {...}}只写要改的字段即可未传字段保持不变。这个「部分更新」语义很关键不用担心覆盖掉其他列。4.3 删除记录DELETE 接口与批量删除的安全闸门删除是 CRUD 里唯一不可逆的操作代码实现简单但必须在两个层面加防护。第一层在我的技能脚本里加确认参数第二层在 OpenClaw 的提示词层面要求模型必须拿到用户明确确认后才能删。def delete_record(app_token, table_id, record_id, confirmFalse): 删除单条记录confirmFalse 时拒绝执行 if not confirm: raise RuntimeError(删除操作需要 confirm 参数请先让用户确认) token get_tenant_token() url f{BASE}/bitable/v1/apps/{app_token}/tables/{table_id}/records/{record_id} headers {Authorization: fBearer {token}} resp requests.delete(url, headersheaders, timeout10) data resp.json() if data.get(code) ! 0: raise RuntimeError(f删除失败: {data}) return data.get(data, {})批量删除走POST .../records/batch_delete请求体是{records: [rec_id1, rec_id2]}同样有 100 条上限。我建议批量删除函数强制要求传入至少一个confirm_reason字段不填就不执行。这个字段会被记录在日志里出问题的时候能追溯是谁、为什么删了这一批。实际经验是删除相关的翻车事故九成不是接口调不通而是没确认就删了、或者删错了筛选条件命中的行。这类事故在项目周报里非常难看多一道确认成本极低。4.4 把四个动作收进一个入口action 参数与表名映射设计四个函数都齐了需要把它们收进同一个命令行入口方便 OpenClaw 用统一方式调用也方便你手动调试。用 argparse 实现一个带子命令风格的主入口# skill.py 的主入口 import argparse import json import sys def main(): parser argparse.ArgumentParser(description飞书多维表格 CRUD 技能) parser.add_argument(action, choices[query, create, update, delete]) parser.add_argument(--table, requiredTrue, help数据表名称对应 config.json 中的 datasources) parser.add_argument(--record, helprecord_idupdate/delete 时使用) parser.add_argument(--json, destfields_json, help字段 JSON如 {\标题\: \新任务\}) parser.add_argument(--confirm, actionstore_true, help删除操作必须携带) parser.add_argument(--condition, help按条件更新/删除格式 字段名值) args parser.parse_args() if args.fields_json: fields json.loads(args.fields_json) else: fields {} # 根据 action 分发到对应的 CRUD 函数 if args.action query: print(query_records(...)) elif args.action create: print(create_record(...)) elif args.action update: if args.record: print(update_record(..., args.record, fields)) elif args.condition: field, value args.condition.split(, 1) print(update_by_condition(..., field, value, fields)) elif args.action delete: if not args.confirm: sys.exit(删除需要 --confirm 参数) print(delete_record(..., args.record, confirmTrue)) if __name__ __main__: main()这个入口设计有三个值得留意的点。第一--table接收的是表名而不是 table_id真正的映射关系放在 config.json 里这样模型的参数提取压力小很多。第二--condition支持「字段名值」这种极简格式让模型在不知道 record_id 时也能完成「把状态为进行中的任务改成已完成」这类操作。第三所有输出建议用json.dumps序列化保证 OpenClaw 解析结果时不会因为中文字符或引号问题翻车。config.json 的表名映射长这样{ app_id: cli_xxxxx, app_secret: xxxxx, datasources: { 项目表: {app_token: Abc123DEF, table_id: tblAAA}, 任务表: {app_token: Abc123DEF, table_id: tblBBB} } }5. 避坑与排查飞书多维表格技能最容易翻车的 5 个细节5.1 从分享链接复制 token接口一直报「app not found」现象查询接口返回code: 1256之类错误提示找不到应用或表格但 URL 看着没问题。原因很多人习惯点表格右上角的「分享」从分享链接里复制一串字符当作 app_token。分享链接通常带的是短链标识或带额外参数和开放平台 API 用的 app_token 完全不是一回事。解决回到浏览器地址栏从完整的页面 URL 里取。格式是https://xxx.feishu.cn/base/真实app_token?table真实table_idviewvewxxx/base/后到第一个?之间的串才是 app_token。我现在的习惯是拿到 URL 先复制到记事本按?切分成三段再分别填进 config.json从不在代码里临时粘贴。5.2 往数字字段塞了字符串写入被字段校验拦下现象创建记录时接口返回字段校验错误比如「字段类型不匹配」或「无法识别字段类型」。原因模型从自然语言里提取参数时把「金额 120000」提取成了字符串120000而多维表格里这一列是数字类型类型对不上直接拒绝写入。更隐蔽的是日期字段传2024-01-01可能侥幸通过传2024/01/01就会报错因为接口对日期格式有严格约定。解决在create和update的分发逻辑里加一个normalize_fields函数根据每个表已知的字段类型做转换。简单做法是维护一份「字段类型映射」放 config.json 里偷懒做法是在脚本里用isinstance判断外部传入值数字字符串能转 int 就转 int。字段多的时候别偷懒类型映射表值得维护。5.3 token 有效但查不到数据应用没被加为表格协作者现象token 获取成功http 状态码也正常但返回的 items 是空数组或者直接提示「没有权限访问该文档」。原因自建应用的 API 权限是「能力」能不能访问某张具体表格是「范围」。后台开了多维表格读写权限只代表应用有这个能力不代表它自动能读你所有的表。每张表都得手动把应用添加为协作者。解决打开目标多维表格点右上角「...」→「添加文档协作者」搜应用名称不是应用描述授予「可编辑」权限。如果是新创建的测试表检查一下协作者列表里有没有这个应用。这个问题特征明显但也迷惑性强尤其当你有多个多维表格时容易误判是代码问题。5.4 查询只回 20 条就以为数据丢了忘翻页现象接口返回成功但记录只有一页表格里明明有几百行query 结果却缺了一大半造成「数据丢了」的误判。原因多维表格查询接口默认page_size是 20 或 100取决于接口版本且单页返回后has_more为 true 时需要用page_token继续拉下一页。脚本里只取了第一页没有翻页逻辑。解决把page_size显式设为 100 或 500然后循环拉取直到has_more为 false。我一般封装一个query_all_records函数内部用while has_more:累加结果这样技能在对话里回答「一共有多少条」时不会给出偏小的数字。5.5 批量操作一次超过 100 条被拒分批重试现象批量创建或删除时接口返回code: 99991400之类的参数错误或者提示超出数量限制。原因多维表格批量接口单次限制 100 条超过上限请求直接被拒。写成循环逐条调又太慢1000 条数据要跑 10 次请求还容易中途触发限流。解决写一个chunked_batch包装函数把记录列表按 100 条切片逐批调用后再汇总结果。每批之间加 200~500 毫秒延时避免触发频率限制。给技能做数据迁移时这个切片逻辑几乎是必备的不加的话数据一多就翻车。6. 交付成「一键安装.zip」目录规范、安装脚本与自测清单6.1 zip 包里的标准目录与安装脚本做了什么交付时把整个feishu-bitable-skill/目录打成一个 zip命名带上版本号比如feishu-bitable-skill-v1.0.0.zip。解压后用户只会做三件事复制config.example.json为config.json、填入自己的 app_id 和 app_secret、运行bash install.sh。install.sh 的核心逻辑是往 OpenClaw 技能目录里放一个指向本项目的链接或直接拷贝并检查 Python 版本和 requests 依赖#!/usr/bin/env bash # install.sh 一键安装脚本 set -e # 1. 检查 Python 版本 python3 -c import sys; assert sys.version_info (3,8), 需要 Python 3.8 # 2. 安装依赖 pip3 install -r requirements.txt # 3. 检查配置文件 if [ ! -f config.json ]; then cp config.example.json config.json echo 请编辑 config.json填入 app_id 和 app_secret exit 1 fi # 4. 复制到 OpenClaw 技能目录并做安装后验证 SKILL_DIR$HOME/.openclaw/skills/feishu-bitable mkdir -p $SKILL_DIR cp skill.py SKILL.md config.json $SKILL_DIR/ echo 安装完成可用下面命令自测 echo python3 skill.py query --table 任务表这段脚本的逻辑按顺序走先确认 Python 版本避免用户用 2.x 或太旧的 3.x 跑出诡异错误再装依赖然后检查 config.json 是否存在不存在就复制模板并退出让用户先填配置最后把三个关键文件复制到 OpenClaw 的默认技能目录。set -e表示任何一步失败就立即终止不带着半残的环境继续。如果你在安卓端跑 OpenClaw目录路径会不太一样把$HOME/.openclaw/skills改成你实际挂载的技能目录即可脚本结构不用变。6.2 三步端到端自测清单装完之后我习惯按三步验收一个技能包任何一步没过都不算交付完成。第一步是命令行自测python3 skill.py query --table 任务表能打印出记录就算脚本没问题。第二步是自然语言自测在 OpenClaw 对话里问「任务表里现在有几条记录」看模型能否正确路由到技能并返回数字。第三步是错误注入自测故意传一个不存在的表名看脚本报错是否足够清晰而不是抛一个 Python traceback 给用户。三步全过后这个 zip 才敢往外发。我自己每次改完 skill.py都会先跑一遍命令行再交给对话层这个习惯帮我挡掉了至少一半的线上事故。希望帮到你。本文还有配套的精品资源点击获取