说实话我一开始对“文档自由”这四个字没什么感觉。直到某天我数了一下自己一天里到底干了多少件复制粘贴的活儿把AI生成的周报从网页里粘到飞书文档把多维表格里的数据截图贴到群里把项目进展从聊天记录里扒出来再整理成文档。一天下来真正用来思考的时间没多少搬运活儿反倒占了大半。后来我索性写了个小工具把“AI生成内容上传飞书”这个流程压缩成一个命令一键生成、一键上传、一键通知。这篇文章就记录一下这个工具的完整搭建过程包括飞书API的选型、机器人Webhook的调用方式、多维表格的写入技巧以及我在Windows上用Claude Code辅助开发时踩过的一堆坑。适合被文档搬运折磨的运营、研发、产品以及刚上手飞书开放平台的开发者。1. 项目整体设计与思路拆解1.1 痛点AI生成的内容卡在最后一公里很多人用AI的姿势是网页上等结果复制切到飞书粘贴调格式。这套流程看起来没什么但一旦内容高频更新痛点就出来了。我写过日报、周报、竞品分析、会议纪要几乎每天都要经历三四次完整搬运。最烦的不是复制粘贴本身而是飞书文档的排版总要手动调——标题层级、加粗、表格、代码块AI在网页端生成得很规整粘到飞书后全部打回原形。更夸张的是群里的同步。文档写好了还得把链接手动甩到群里附带一句“这是今天的报告大家看下”。如果哪天忘了发就有人来问“报告呢”。这类高频率、低技术含量的操作机械执行太浪费所以我一开始就把它定义成一个“自动化脚本”项目输入一个需求描述输出一个已上传飞书且已经通知到群的文档。整个过程不需要打开浏览器不需要复制粘贴也不需要手动排版。我接触到很多人的第一反应是“用飞书机器人就能发吧”。没错机器人只是发送消息的出口但完整的链路远不止这一环。要生成文档内容需要接大模型要创建飞书文档需要走云文档API要把文档写进知识库或合作空间需要指定folder_token要让群里的人直接看到需要Webhook。把这些串起来才叫完整方案。1.2 整体架构三层协作脚本只做编排这个项目我最终分成三层来看。最底层是大模型API。我用的DeepSeek和通义千问的OpenAI兼容接口因为两者在国内可直接调用、价格便宜而且能处理结构化输出。换模型的话只要保持messages格式不变替换base_url和api_key就能切换。中间层是飞书开放平台的三类接口第一类是用tenant_access_token调用的云文档API负责创建文档和执行导入任务第二类是群机器人Webhook负责把消息或卡片发到群里第三类是多维表格API负责把结构化记录写入表格。最上层就是编排层一个Python脚本。它负责读取配置、调大模型生成内容、调用飞书API上传、最后发通知。脚本本身不包含业务逻辑逻辑全在“先做什么再做什么”这条主流程里。这样设计的理由很直接一旦某个环节出问题可以单独重试不会影响其他步骤。为什么用Python而不是Node或者Go说实话Python写这类胶水脚本是最快的requests库一把梭JSON处理也方便。而且大模型SDK对Python的支持最成熟。我身边也有人用Node做但从零到能用Python能少花一半时间。1.3 方案取舍飞书 vs 本地文件 vs 其他协作平台做“文档自由”的时候身边同事问过我两个问题为什么不直接存本地为什么不用其他协作平台本地文件当然是最简单的AI生成完保存成Markdown发群里就行。但问题是团队协作时你的本地文件等于不存在。飞书的价值在于三点文档天然带链接分享成本为零权限体系可以让不同人看到不同内容文档可以挂在知识库下实现长期沉淀。我需要的正是“生成—传达—沉淀”闭环本地文件完全覆盖不了。至于钉钉和企业微信Webhook能力和API开放程度都不差。但我个人最终选了飞书原因有三云文档的导入API支持Markdown这省了我大量排版工作多维表格对API写入的兼容度很高常见的字段类型都能直接落库飞书的卡片交互样式更适合做“文档已生成”这类通知。每个团队选型维度不同但对我这个场景飞书确实是最省事的。这里顺便说明一个选型教训开始不要追求大而全的Agent框架先做一条“生成—上传—通知”的最小闭环跑通之后再加事件订阅、多机器人协作这些能力。这个项目的前身就是一堆散落的代码片段后来才整理成结构化脚本经验就是——先有闭环再谈架构。2. 核心细节解析与实操要点2.1 飞书文档导入API一条命令把Markdown变成在线文档很多第一次接触飞书开放平台的人会以为创建文档得用docx接口然后一块一块地建block。我一开始也这么想看到block文档时头皮发麻——一篇带标题、表格、代码块的报告可能要建几十个block不仅代码量大顺序错一个整篇内容就乱了。后来我发现了drive/v1/import_tasks这个导入接口思路一下子通了。它本质上是一个“文件转换任务”支持把Markdown内容直接转成飞书云文档。你只需要传file_extension为mdfile_name给个标题file_content放上整篇Markdown正文再把point里的mount_key指定成某个文件夹的token就会在后台异步执行导入然后返回一个ticket。用ticket轮询导入结果拿到最终文档的URL和token。这个接口最大的好处是免去了逐行建block的问题。我实测标题、加粗、引用、代码块、表格这些Markdown元素都能转到飞书文档基本不用二次排版。但需要注意的是它要求file_content必须有完整的Markdown正文不能只给一句话另外要注意异步轮询导入不是立刻返回结果需要间隔几秒查一次ticket状态。我在设计脚本时把这个循环写成了30秒超时超过就报错实测90%的导入都在10秒内完成。2.2 群机器人Webhook把文档卡片送进群聊群机器人是飞书里最简单的接入点创建方式很顺手在群里打开设置添加自定义机器人拿到Webhook地址。飞书支持文本、富文本、交互卡片三种消息我实际使用下来最合适的是交互卡片。因为发卡片不只是通知“文档传好了”还能直接在卡片里放文档标题、摘要、链接点击就能打开视觉上比纯文本清爽得多。调用方式就是一个POST请求把JSON发到Webhook地址。卡片的字段结构稍微有点绕——header里放标题elements里放正文内容正文可以用lark_md标记语言支持加粗、超链接、人。我习惯把文档链接做成“ 文档标题 ”格式这样群里的人点一下就能进去不用再复制链接。安全设置这里必须多说一句。飞书自定义机器人支持三种安全设置关键词、签名校验、IP白名单。我强烈建议至少开启签名校验因为Webhook地址一旦泄露任何人都能往你的群里发消息。签名算法不复杂把timestamp和密钥拼接做HMAC-SHA256再Base64编码具体字段官方文档写得很清楚。我第一次做的时候没开签名结果一次测试时地址被无关脚本扫描到群里涌入了一堆垃圾消息后来老老实实把签名校验加上世界清净了。2.3 多维表格写入结构化数据的落库路径文档适合叙事但有些数据天然是结构化的。比如我每天用AI跑出来的项目“风险清单”每条记录包含风险等级、描述、负责人、状态这种内容放文档里检索起来很痛苦适合放进多维表格。多维表格的API模型是app_token加table_id。一个多维表格是一个app里面可以有多张表。写入记录走的是POST /open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/recordsbody里直接传fields对象键是字段名值是字段值。听起来很简单但细节坑不少。最典型的是日期字段。飞书多维表格的日期字段接收的是毫秒时间戳不是“2025-04-03”这种字符串。我第一次写入时传了字符串API返回200但表格里那列全是空的。排查半天发现是我把日期写成字符串了改成时间戳后立刻正常。还有单选字段传入的值必须和字段的选项完全一致多一个字就写入失败。这些知识点接口文档里都有但文档分散不看够一定数量根本想不到。我把自己的约定放在config里所有日期都统一由脚本转时间戳单选枚举都做一层映射这才彻底解决。2.4 权限、Token与安全边界先想清楚再动手飞书开放平台的权限体系比想象中严格也是最容易在一开始劝退人的地方。创建应用后开发阶段可以用“测试企业”模式但真正要让脚本跑得顺畅必须给应用开通一堆权限且需要管理员审核。我用的核心权限包括创建文档docx:document、导入云文档drive:import、发送机器人消息im:message:send_as_bot、读写多维表格记录bitable:app。在开放平台的权限管理里搜索对应的英文标识并开通即可。这里建议一次性把所有要用的权限都开好因为每改一次权限都要重新发布应用版本审核走流程也要时间反复改真的很烦。Token方面要分清tenant_access_token和user_access_token。前者是应用身份适合我这个脚本场景因为它不需要用户登录态每次调用接口前带上即可。它的有效期是2小时所以我做了缓存全局变量记录获取时间和token过期才重新请求避免每次调用都刷一次token。调用量小的时候无所谓但批量导入10个文档时每篇都去重新获取token就完全没有必要了。安全边界上还要注意不要把app_secret硬编码在代码里我放在config.yaml里而且这个配置文件加了权限限制不提交到Git仓库。飞书的权限最小化原则也适用只申请脚本必需的那些权限别图省事申请一堆用不到的审核过不了还是小事权限面扩大才是风险。3. 实操过程与核心环节实现3.1 环境准备开放平台建应用 本地依赖整个环境准备我只做了三件事。第一件是在飞书开放平台创建企业自建应用。登录后进入开发者后台点创建应用填名称和描述就有了app_id和app_secret。然后进权限管理把前面说的docx、import、bot、bitable几类权限搜出来开通。最后在版本管理里创建版本并发布等管理员审核通过。这一步其实不复杂但很多人会卡在这里因为不发布应用拿不到正式权限调用接口时会报“权限不足”。第二件是群里加机器人。打开目标飞书群在设置里添加自定义机器人复制Webhook地址打开签名校验保存密钥。如果你不想签名也可以选关键词校验但我不推荐。第三件是本地Python环境。我用的Python 3.11装三个库就够了requests、pyyaml、openai。openai这个库其实只用到了最基础的chat.completions接口你完全可以用requests直接调但它封装好了省事。装完依赖后我把config.yaml按以下结构写好feishu: app_id: cli_xxxxxxxx app_secret: your-secret-here webhook: https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx webhook_sign_secret: your-sign-secret import_folder_token: fldxxxxxxxx bitable_app_token: bascnxxxxx bitable_table_id: tblxxxxx llm: base_url: https://api.deepseek.com/v1 api_key: sk-xxx model: deepseek-chat temperature: 0.3说明一下这些token去哪里找应用凭证页有app_id和app_secret群机器人设置页有webhook和签名密钥云空间里想存放文档的文件夹在浏览器地址栏URL中能找到folder_token多维表格的链接里则能解析出bascn开头的app_token和tbl开头的table_id。第一次接触的人会觉得头大按这个对应关系去填就好。3.2 核心代码AI生成内容并导入飞书文档先实现第一步拿用户给的prompt让大模型生成Markdown正文。from openai import OpenAI client OpenAI(base_urlcfg[llm][base_url], api_keycfg[llm][api_key]) def generate_markdown(prompt: str) - str: resp client.chat.completions.create( modelcfg[llm][model], messages[ {role: system, content: 你是一个严谨的文档助手只输出规范的Markdown正文不要输出解释性语言。}, {role: user, content: prompt}, ], temperaturecfg[llm][temperature], streamFalse, ) return resp.choices[0].message.content这里有个我把控得很死的地方system prompt里要求模型只输出Markdown正文。如果不加这个约束模型经常会在文档前后加“好的以下是生成的文档”这类废话导入飞书后这些废话全得手工删。加一句“不要输出解释性语言”能明显减少返工。接下来是获取tenant_access_token并调用导入接口import requests, time, base64, hashlib _token_cache {token: None, expire_at: 0} def get_tenant_token() - str: if _token_cache[token] and time.time() _token_cache[expire_at]: return _token_cache[token] resp requests.post( https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal, json{app_id: cfg[feishu][app_id], app_secret: cfg[feishu][app_secret]}, timeout10, ).json() _token_cache[token] resp[tenant_access_token] _token_cache[expire_at] time.time() int(resp[expire]) - 120 return _token_cache[token] def import_markdown(title: str, markdown: str) - dict: token get_tenant_token() payload { file_extension: md, file_name: title, file_content: markdown, point: {mount_type: 1, mount_key: cfg[feishu][import_folder_token]}, } headers {Authorization: fBearer {token}, Content-Type: application/json} resp requests.post( https://open.feishu.cn/open-apis/drive/v1/import_tasks, headersheaders, jsonpayload, timeout15, ).json() ticket resp[data][ticket] for _ in range(10): time.sleep(2) r requests.get( https://open.feishu.cn/open-apis/drive/v1/import_tasks/ ticket, headersheaders, timeout10, ).json() if r[data][result][job_status] 0: return r[data][result][tokens] raise TimeoutError(import task timeout)解释一下两个细节。expire减去120秒是给token留了2分钟的余量防止正好卡在服务端过期时间附近。导入任务返回的是tokens字段里面一般是一个数组通常会包含原始token和转换后的token我取最后一项作为正式的文档token再拼接成文档URL。生成标题也顺便讲一下我让AI在文档正文之前额外输出一行“title: 某某报告”脚本里解析这个字段来决定文件名。比起传死标题这样更灵活每天生成的报告标题都会自动带上日期。3.3 核心代码机器人发文档卡片到群文档创建完成后下一步是把文档卡片发到群里。我封装了一个函数def send_doc_card(title: str, doc_url: str, summary: str): sign gen_sign(cfg[feishu][webhook_sign_secret]) card { config: {wide_screen_mode: True}, header: { title: {tag: plain_text, content: f文档已生成{title}}, template: blue }, elements: [ {tag: div, text: {tag: lark_md, content: f**[查看文档]({doc_url})**}}, {tag: hr}, {tag: div, text: {tag: lark_md, content: summary}}, ], } requests.post( cfg[feishu][webhook], json{timestamp: sign[ts], sign: sign[sign], msg_type: interactive, card: card}, timeout10, )签名函数是官方算法我直接按文档实现def gen_sign(secret: str) - dict: ts str(int(time.time())) string_to_sign f{ts}\n{secret} h hashlib.sha256(string_to_sign.encode(utf-8)).digest() sign base64.b64encode(h).decode(utf-8) return {ts: ts, sign: sign}这里有一个容易踩的坑开启签名校验后timestamp和sign要放在请求体顶层和msg_type、card平级而不是塞进card里。我一开始放错位置飞书一直报签名错误排查了十几分钟才反应过来。摘要summary怎么来我让大模型在生成正文之前先输出一段百字以内的摘要脚本提取后传给卡片。这样群成员不用打开文档就知道大概内容体验接近人工发消息。实际效果比我预期好很多群里的反馈是“至少能先瞄一眼再决定要不要点进去”。3.4 一键封装与定时任务让流程真正自动化所有模块都完成后就是“一键”了。我用click写了一个命令行入口python feishu_uploader.py --prompt 生成今天的数据日报重点突出异常指标 --title-prefix 日报 python feishu_uploader.py --from-file weekly_template.md在脚本里主函数分了五步解析参数、调generate_markdown生成正文、解析标题和摘要、调import_markdown导入文档、调send_doc_card发卡片。每一步都有try/except出错时会在本地日志里写下当前步骤和错误信息而不是直接崩溃。这样排障时能直接知道是哪一环挂了。做完命令入口后我又加了一步定时任务。在Windows上我通过任务计划程序让脚本每天早上9点自动跑一次日报。命令行长这样schtasks /create /tn feishu-daily-report /tr C:\Users\me\.venvs\feishu\Scripts\python.exe C:\scripts\feishu_uploader.py --prompt-file daily_report.txt /sc daily /st 09:00 /f这里有个大坑任务计划程序里如果直接用python不加全路径很可能因为找不到解释器而失败尤其是用了虚拟环境的时候。我一开始用任务计划界面配置一直提示“操作成功但任务未运行”查日志才发现是指向了全局Python而不是虚拟环境里的Python。后来改成虚拟环境里的绝对路径并设置“起始于”目录为脚本所在目录就稳定了。还有一步值得提的是AI辅助开发。这个脚本从零到能跑通大部分代码其实是Claude Code帮我写的。我给它描述清楚需求它直接输出带注释的版本我再根据飞书API文档校对关键字段。开发过程中遇到Webhook签名问题时我把官方文档丢给它它也能给出修正代码。顺带说一句身边也有用Codex接入飞书场景做插件尝试的朋友思路大同小异都是让AI理解飞书API结构减少人工翻文档的时间。用AI编程工具的意义不是完全不用人而是把“查文档、写样板代码”这部分时间砍掉人把精力留给业务逻辑和排障。4. 常见问题与排查技巧实录4.1 创建文档时报权限不足这是最常见的拦路虎。表现为调用导入接口返回错误码99991672提示“权限不足”或“操作无权限”。我排查的思路分三步。第一步确认应用是否已经发布并通过审核开发态下很多权限不生效第二步检查权限管理里是否真的开通了对应权限比如导入文档要开通“导入云文档”消息要用“获取与发送单聊、群组消息”的机器人权限第三步如果应用是刚加权限的必须在版本管理里再走一次发布流程让新权限生效。值得一提的还有一个隐蔽问题即使应用有权限调用导入接口时指定的folder_token必须在应用可见范围内。如果文件夹没授权给应用接口照样报权限错误。解决办法是在云空间里把文件夹分享给这个应用的“管理员”或者直接把应用加为文件夹协作者。4.2 导入API一直返回400或解析失败导入接口返回400时八成是file_content本身有问题。我遇到过两类情况。一类是中英文混合的非法JSON字符比如AI生成内容里带了控制字符或异常换行导致整个请求体无法被服务端解析。解决办法是发送前做一次清洗把连续空白字符压缩、把异常换行替换成正常换行。另一类情况是Markdown内容包含不支持的语法比如某些扩展表格写法导入后个别块会渲染失败但通常不影响整体文档生成。我的策略是在脚本里加了一个简单的“后处理”把AI输出中的多余空行删掉把英文引号统一成中文引号实测能减少大半解析错误。说到底飞书导入接口的核心限制是它把Markdown当作文本内容去解析所以对内容格式的容错不算强。遇到解析失败最直接的排查方式就是先用postman发一段最简单的Markdown测试排除接口和权限问题后再逐步增加内容复杂度定位到具体是哪段语法出了问题。4.3 Webhook消息发不出去或签名报错Webhook的问题通常集中在三类。第一类是网络层请求超时或者返回403。这种情况先确认webhook URL是否完整是否多了空格或换行。第二类是签名错误报错信息里会出现“invalid sign”。如果你的机器人开启了签名校验记住我前面说的timestamp和sign要放在顶层。我发现很多人是把签名放在了card对象里飞书根本找不到自然校验失败。第三类是内容格式错误卡片里文本标签写错也会发不出去。还有一个小概率的情况同一个Webhook被多个脚本共用一个脚本的消息被另一个脚本的签名覆盖了但这种情况很少见。我后来把Webhook按用途拆分日报群、告警群、文档归档群各用各的机器人排障更清晰。4.4 多维表格写入失败字段类型与格式坑多维表格写入报错最常见的是字段类型不匹配。比如多选字段传了字符串日期字段传了日期字符串而不是毫秒时间戳数字字段传了带千分位的字符串。我的建议是写一个字段类型映射表文本字段传字符串、数字字段传数字、日期字段传毫秒时间戳、单选/多选字段传选项值所有布尔值传true/false。另外如果要写入的字段是非必填的干脆不要出现在fields里省得为它拼一个空值。初期写完记录后多去表格里看一眼实际渲染效果比看接口返回更直观。API返回200不代表数据落对了只有表格里显示符合预期才算真成功——这句话是我的血泪经验。4.5 我的踩坑总结整理一下我累计修复过的问题做成一个速查问题现象常见原因快速处理办法导入返回权限不足应用未发布或文件夹无权限发布版本并给应用授权文件夹导入返回400Markdown含非法控制字符发送前做内容清洗文档导入成功但内容乱扩展表格语法不兼容简化Markdown表格结构Webhook签名报错sign放错位置放在请求体顶层消息发送403校验未通过检查签名算法和密钥多维表格日期为空传了日期字符串转毫秒时间戳后再写入日报任务没跑计划任务用了错误Python使用虚拟环境绝对路径这个表格不是标准答案但每一条都是真实线上跑出来的问题。技术文档会教你怎么做对但很少教你做错之后怎么找原因这也是我坚持记录的原因。5. 扩展玩法与个人体会5.1 从单点到Agent让飞书机器人自己干活目前这个脚本还是“被动执行”我给它一个prompt它跑完整条链路。要做到真正的“对话即操作”就要把它升级成飞书Agent。飞书开放平台支持事件订阅机器人可以接收群里的消息当有人它时说“生成日报”它就把任务丢给脚本完成后把结果和链接发回群里。这意味着要把脚本包成一个可被事件回调的服务加一层消息解析逻辑。听起来很复杂但它本质上是给现有脚本加一个入口把收到的文本当prompt走原有生成和上传流程。我在测试环境里跑过一版体验很爽——完全不用自己打开终端群里说一句话文档自己就出现在协同空间里链接也自动弹出。还有一个方向是接入知识库。飞书的知识库本质上是文件夹权限和文档结构的组合。你可以按项目建文件夹把导入的文档按规则自动分流到对应目录配合多维表格做索引就有一个简易的“AI知识库”了。不用买昂贵的知识管理软件一套脚本加上飞书开放API就能支撑小团队使用。5.2 写在最后AI替我实现文档自由的含义“文档自由”这个词很多人以为是自动写文档。其实真正自由的是不再被工具流程绑住。我不用再记着“写完了要粘贴”“发完了要通知”这些杂事脚本接管之后我能把时间花在真正需要判断的地方内容是否准确、逻辑是否严密。最后分享一个我至今受用的小习惯所有自动化脚本无论多简单一定要在开头打印一行当前步骤和时间失败时把上下文带出来。一开始我觉得多余日志文件也懒得看后来跑了三周准时任务发现排障时有这一行能少掉九成的猜测时间。AI生成的代码再聪明跑在真实环境里总会碰见权限、格式、网络这类的意外人还是要掌握最基本的排障能力。一个项目真正落地靠的不是某次灵光一现而是把每个环节的不确定都变成可控这大概也是“文档自由”背后最快的路径。