简介:面向希望低门槛搭建业务应用的办公人员与开发者,这是一套将飞书多维表格与 OpenClaw 技能结合的一键安装资源,覆盖日常 CRUD(增删改查)操作,可用于项目管理、客户关系维护、库存跟踪、内容策划等场景。压缩包共 13 个文件,以 Markdown 说明文档和 Python 脚本为主,另有 Shell 安装脚本、配置文件及 JSON 元数据,分别承担技能使用指引、字段映射与自动化流程说明,以及核心 CRUD 操作脚本和安装部署功能,整体约 55KB,结构清晰便于按需调用。目前已有 90 人学习查看。用户拿到后可直接安装模板,借助中文分类的技能库快速搭建个性化流程,无需复杂编程即可将多维表格变成可承载业务流程的应用开发环境;同时资源保留了系统模式、公式参考、权限指南等进阶说明,方便二次调整与团队推广,适合希望提升数据管理效率的非技术用户及团队管理者。
1. 为什么是 OpenClaw 技能而不是脚本:先回答值不值得装
看到标题里"OpenClaw 技能"五个字的时候,多数人的第一反应是:这不就是给飞书多维表格写个脚本吗,跟技能有什么关系?我第一次也是这么想的,直到把一套完整 CRUD 塞进 Skills 目录之后才发现,OpenClaw 的技能机制本质上是一个带描述、带输入输出约定、能让人工智能自主调用的函数包,而多维表格刚好需要一个能被反复调用、随时改参数、还不会把 token 写死在代码里的入口。这个 zip 包做的就是这件事:把鉴权、查询、增删改、结果回落全部封装成技能,让你在对话里直接说"把今天新增的客户记录拿出来"就能触发操作,而不是每次对着 OpenAPI 手搓请求。适合正在搞 Agent 工作流、又不想让程序和电子表格割裂的人;如果你只是偶尔导一次数据,那它确实不值,但如果你每天都要在多维表格里维护几百条记录,这包就是省心神器。
2. OpenClaw 技能机制与多维表格 API:把两套东西接到一起
2.1 技能与工具的区别:Claw 到底管什么
OpenClaw 里有一对容易混淆的概念:技能与工具。工具是底层的原子操作,比如"发 HTTP 请求""读本地文件""执行命令行";而技能是把几个原子操作按业务语义编排起来的东西,它有自己的名字、描述、输入参数和输出约定。你写一个"查询客户"的技能,Claw 看到用户指令后先做意图匹配,再决定调用哪个技能、填什么参数、拿返回值干什么。这个分层有三个直接好处:第一,Claw 不需要知道飞书 API 的地址和鉴权细节,它只面对一个干净的 Python 接口;第二,技能可以独立调试,不依赖对话上下文;第三,换一套 API 时只改技能内部实现,外层描述不变。多维表格的接入就用这个思路,把 OpenAPI 的细节全部包在技能里。
2.2 鉴权与网络:飞书开放平台那点坑
任何涉及飞书 API 的集成,第一步都是鉴权。常见的做法是申请一个企业自建应用,拿到 App ID 和 App Secret,然后用 tenant_access_token 模式换取调用凭证。这里有个新手高频翻车点:多维表格的 app_token 和 table_id 是两个不同的东西,app_token 是那张表格文档的标识,table_id 是表格内某个数据表的标识,在 URL 里长这样:
https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records一旦把 table_id 当 app_token 用,请求直接 400。另外 token 的有效期一般是两小时,但企业自建应用在沙箱环境里的 IP 白名单经常没配,导致 token 换出来了但请求还是 403。我的习惯是先把网络策略改成"不校验",调通之后再加白名单,能少掉一半排查时间。这一节不解决具体代码,先把接口路径和鉴权模型讲清楚,下一节直接看实现。
2.3 用 Python 跑通一张表的读写
先别急着写技能,把最小可用的 Python 脚本跑通,再往 OpenClaw 里搬。下面这段代码负责换 token 和读取记录,是后续所有 CRUD 的基础:
import requests APP_ID = "cli_xxxxxxxx" APP_SECRET = "xxxxxxxxxxxxxxxx" APP_TOKEN = "bascnxxxxxxxxxxxxxxxx" TABLE_ID = "tblxxxxxxxxxxxxxxxx" def get_tenant_access_token(): url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" payload = {"app_id": APP_ID, "app_secret": APP_SECRET} resp = requests.post(url, json=payload) resp.raise_for_status() data = resp.json() if data.get("code") != 0: raise RuntimeError(f"token 换取失败: {data.get('msg')}") return data["tenant_access_token"] def fetch_records(token, page_size=100): url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records" headers = {"Authorization": f"Bearer {token}"} params = {"page_size": page_size} resp = requests.get(url, headers=headers, params=params) resp.raise_for_status() return resp.json().get("data", {}).get("items", []) if __name__ == "__main__": token = get_tenant_access_token() records = fetch_records(token) print(f"拉取到 {len(records)} 条记录")逻辑说明:get_tenant_access_token 用 App ID 和 App Secret 换临时凭证,凭证放在请求头里;fetch_records 走 bitable 的 records 端点,page_size 控制每次拉取条数。参数说明:page_size 最大值是 500,但建议先用 100 验证分页逻辑;返回结构里 items 是记录数组,每条记录里有 fields 字段,字段名就是多维表格里的列名。这一步跑通之后,你已经完成整个技能里最难的环节,剩下的 CRUD 都是同一个 API 模式的不同 HTTP 动词。
3. 把日常 CRUD 封装成技能:查询、写入、改状态一次讲清
3.1 技能目录结构与能力描述怎么写
OpenClaw 的技能目录一般长这样:~/.openclaw/skills/feishu-bitable/,下面至少要有 SKILL.md 和 main.py。SKILL.md 是整个技能的"身份证",Claw 靠它判断什么时候调用这个技能,写得越具体,误调用的概率越低。我拆过的技能里,最容易出问题的就是描述太宽泛,比如"操作飞书多维表格",结果用户说"帮我查天气"它也去匹配。正确的写法是把输入参数和触发场景都写清楚:
--- name: feishu_bitable_crud description: 操作飞书多维表格的增删改查。当用户提到"多维表格""客户表""记录""新增/更新/删除"时使用。 parameters: type: object properties: action: type: string enum: ["query", "create", "update", "delete"] description: 要执行的操作类型 table_id: type: string description: 数据表 ID,缺省时使用配置里的默认表 fields: type: object description: 写入或更新时的字段键值对 required: ["action"] --- # 飞书多维表格 CRUD 技能参数说明:parameters 里的 schema 决定了 Claw 在调用技能时会把哪些对话内容解析成参数,action 用枚举约束,避免 Claw 自由发挥;table_id 设成可选,默认走配置文件里的值,这样在单表场景下用户不需要提表名。这里有个细节:SKILL.md 的 frontmatter 里不要写版本号,OpenClaw 在加载技能时会自己扫描目录,写版本号反而可能在升级时产生重复加载的幻觉。
3.2 查询与写入:先读后写原则
真正实现 CRUD 时,我强烈建议先把查询写透,再做写入。原因有二:一是查询能让你确认字段名是否正确,多维表格里多一个空格或少一个下划线,写入时就会报字段不存在;二是"先读后写"是避免脏数据的底线。比如更新一条记录时,如果不先查一次,很可能把别人刚改过的字段覆盖掉。下面这段是技能 main.py 的核心实现,包含查询和创建两条路径:
import os import requests from typing import Optional APP_ID = os.getenv("FEISHU_APP_ID") APP_SECRET = os.getenv("FEISHU_APP_SECRET") BASE = "https://open.feishu.cn/open-apis/bitable/v1" def get_token(): url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" resp = requests.post(url, json={"app_id": APP_ID, "app_secret": APP_SECRET}, timeout=5) resp.raise_for_status() data = resp.json() if data.get("code") != 0: raise RuntimeError(f"token error: {data.get('msg')}") return data["tenant_access_token"] def run(action: str, table_id: str, fields: Optional[dict] = None, record_id: Optional[str] = None): token = get_token() headers = {"Authorization": f"Bearer {token}"} app_token = os.getenv("FEISHU_APP_TOKEN") if action == "query": url = f"{BASE}/apps/{app_token}/tables/{table_id}/records" resp = requests.get(url, headers=headers, params={"page_size": 200}, timeout=10) items = resp.json().get("data", {}).get("items", []) return [{"record_id": it["record_id"], **it["fields"]} for it in items] if action == "create": url = f"{BASE}/apps/{app_token}/tables/{table_id}/records" payload = {"fields": fields} resp = requests.post(url, headers=headers, json=payload, timeout=10) return resp.json().get("data", {}) if action == "update": url = f"{BASE}/apps/{app_token}/tables/{table_id}/records/{record_id}" payload = {"fields": fields} resp = requests.put(url, headers=headers, json=payload, timeout=10) return resp.json().get("data", {}) raise ValueError(f"unknown action: {action}")逻辑说明:run 函数的参数直接对应 SKILL.md 里的 schema,action 决定走哪条分支;查询时把 record_id 和 fields 打平返回,Claw 读起来更直观;更新操作需要先由调用方传入 record_id,这就是"先读后写"的实践。参数说明:timeout 统一设 10 秒,飞书 API 在业务高峰期偶发慢响应,宁可超时重试也不要无限等;fields 里的值类型要注意,多维表格的"数字"字段对应 JSON number,"日期"字段要传毫秒时间戳,传成字符串会被直接拒绝。
3.3 把结果吐回对话:结构化与超链接
CRUD 本身不难,难的是让 Claw 把结果组织成人话。很多技能翻车就翻在这里:查询返回了一坨 JSON,Claw 只挑出第一条就回答,用户觉得它"偷懒"。我的做法是让 main.py 在返回数据的同时附带一个 summary 字段,把记录条数和关键字段先聚合好,Claw 拿 summary 当事实依据,再决定要不要展开。更实用的一个技巧是:给每条记录拼一个三维表链接,用户可以直接点进去看原文。链接格式是固定的:
def build_record_url(app_token: str, table_id: str, record_id: str) -> str: return (f"https://feishu.cn/base/{app_token}?table={table_id}&view=vew_xxxx" f"&recordId={record_id}")注意:recordId 参数名在网页端是驼峰写法,拼错的话链接能打开但不定位到具体记录,这是我在拆包时确认过的细节。view 参数对应数据表的视图 ID,如果不想硬编码,可以省略,链接落到默认视图。用这个方式,用户每问一条数据,对话里就带着直达链接,比复制粘贴字段值体验好得多。思路不复杂,但加上这层之后,技能从"能调接口"升级到了"能交付结果"。
4. 一键安装与本地调试:装完起不来?先看这五条注意点
4.1 安装 zip 包里到底装了什么
下载下来的是一个 zip 一键安装包,解压之后你会看到三个核心部分:skills 目录、config 目录、install 脚本。skills 目录里就是上一章写的 feishu-bitable 技能;config 目录下面有个 .env 文件,所有飞书凭证都放在这里;install 脚本负责把技能软链到 OpenClaw 的 skills 目录,并且帮你检查 Python 依赖。安装动作本身不复杂,真正决定成败的是环境变量。很多人装上之后技能列表里看不到它,十有八九是 OpenClaw 没找到技能目录。所以安装脚本里我做了一步额外的校验:装完之后主动扫描一遍技能目录,打印出当前 OpenClaw 实际加载的技能名。如果列表里没有 feishu_bitable_crud,说明路径不对,后面所有调试都是白费。
#!/usr/bin/env bash set -euo pipefail SKILL_DIR="${OPENCLAW_SKILLS_DIR:-$HOME/.openclaw/skills}" INSTALL_ROOT="$(cd "$(dirname "$0")" && pwd)" ln -sfn "$INSTALL_ROOT/skills/feishu-bitable" "$SKILL_DIR/feishu-bitable" python3 -m pip install -r "$INSTALL_ROOT/requirements.txt" --quiet echo "当前已加载的技能:" ls -1 "$SKILL_DIR"逻辑说明:OPENCLAW_SKILLS_DIR 可以覆盖默认技能目录,方便多套配置切换;ln -sfn 用了软链接而不是复制,这样你改源码时不用重新安装,适合迭代开发。参数说明:set -euo pipefail 是必须的,任何一步失败立即退出,避免"装了一半还以为成功"的假象。requirements.txt 里通常只有 requests 一个依赖,Python 3.9 以上都带得动。
4.2 首次运行与配置校验
装完先别急着和对话交互,直接用命令行跑一次技能,把配置和网络先验证掉。这一步能把你和"对话里报错但不知道错在哪"隔离开。校验脚本的核心逻辑是这样的:读取 .env 里的五个变量,逐一检查是否为空,然后真实调一次 token 接口,最后再查一条记录。如果 token 接口返回 200 但 records 返回 403,那问题基本在网络策略或权限范围上,和代码无关。
python3 -m scripts.verifyverify 脚本输出三种结果:环境变量缺失、token 换取失败、查询成功。前两种按提示改 .env 就行;第三种说明整个链路已经通了,剩下的就是把技能加载进 OpenClaw 然后重启会话。这里有个容易忽略的点:OpenClaw 的技能列表是在会话启动时扫描的,改完技能文件之后必须开一个新会话才能生效,你在同一个会话里反复问"为什么没有这个技能"纯粹是浪费时间,这是拆包时踩过最实在的坑。
4.3 常见问题与排查
打包之前我特意把几种典型故障场景写在 README 里,这里挑最有价值的几条展开:
现象:技能加载了,但 Claw 说"没有权限调用"。原因:OpenClaw 的技能白名单没有放开。解决:在配置里把 feishu_bitable_crud 加进允许列表,或者把权限模式改成"所有技能可用"。
现象:调用 create 接口返回 code 1254041。原因:字段名和表格实际列名对不上,常见于列名带空格或特殊字符。解决:先跑一次 query,打印返回的 fields 键名,照着键名改写。
现象:更新记录后,过几秒发现值没变。原因:多维表格有缓存延迟,尤其是公式字段和关联字段。解决:更新后主动 sleep 2 秒再查一次,做幂等校验,不要立刻依赖返回值。
现象:token 换出来是空的。原因:App Secret 复制错了,或者企业自建应用还没发布。解决:去飞书开放平台确认应用版本已发布,沙箱环境里的 secret 和正式环境的不是同一个。
现象:查询一次只能拿回 100 条,句柄没走完。原因:page_size 设了但没做分页循环。解决:把自动翻页逻辑写进技能,用 page_token 循环到空为止。
排查思路就一条:永远先分离变量。环境变量、网络、权限、代码,四个层面逐个排除,不要一报错就改代码。这套方法记下来,不仅飞书,接任何外部 API 都适用。
5. 进阶用法:把这张表变成团队的"机器人同事"
到这里,基础 CRUD 已经能跑了,但如果你只把它当"数据库操作快捷键",那有点浪费。我拆完这个包之后,最快见效的用法是把它接到周报流程里:让 Claw 每天定时查一次多维表格里的任务状态,把超期未完成的记录拉出来,直接在对话里生成一份待办提醒。这个场景里技能本身改动极小,只需加一个按截止日期过滤的查询参数。做法是在 query 分支的 params 里拼过滤条件,多维表格支持 filter 表达式:AND(CurrentValue.[状态]="未完成", CurrentValue.[截止日期]<Now())。这个表达式的语法和飞书多维表格的筛选器一致,写错字段名会返回 410301 错误码,调试时拿一条已存在的记录做测试集。
更进一步,你可以把多维表格当成轻量消息队列。我在生产环境里试过一个用法:创建一张"待处理任务"表,A 员工往表里写行,Claw 定时把新行状态改成"处理中",再调用其他技能去执行实际操作,结束后把结果写回同一行的备注字段。整套流程的关键在于加一个 status 字段做状态机,三个值:pending、processing、done。Claw 每次轮询只取 pending 的行,处理完更新状态,这样可以避免并发重复处理。我一般会在技能里追加一个 claim 操作,处理前先把行锁住,最后再 release。这套模式跑起来之后,那张多维表格就不再是"数据存放处",而是团队里一张看得见的任务流转看板。
验证整个技能稳定性的办法是灰度跑七天。前三天只开放查询权限,让群里的人随便问,看字段映射和参数解析是否稳定;第四天开放创建,但每次写入都打一个"来源:Claw"的标记字段;最后两天再放开更新和删除。不要第一天就全量放开,删除操作的不可逆性比想象中猛,虽然多维表格有回收站,但恢复流程麻烦。从那以后我每次接一个外部系统到 OpenClaw,都强制走一遍"只读验证、单操作灰度、全量放开"三步,先让数据说安全,再让技能碰真数据。希望这个思路也能帮你少走弯路。
本文还有配套的精品资源,点击获取