首页/新闻资讯/正文详情

【AI】Codex 执行完成后自动发送飞书通知:TaoToken 统一 Key 配置与 Hook 验证

发布时间:2026/9/26 7:26:16 来源:云帆数科 栏目:资讯中心
【AI】Codex 执行完成后自动发送飞书通知:TaoToken 统一 Key 配置与 Hook 验证
1. 为什么要在 Codex 执行完成后自动发飞书通知在 VSCode 里用 Codex 跑任务最难受的不是写代码而是等它跑完。你切到浏览器看两眼回来发现它早就停了你去倒杯水回来发现它卡在最后一步等你确认。尤其是让 Codex 做批量重构、跑测试、生成文档这类耗时任务时人就被绑在窗口前效率反而更低。我想要的链路其实很朴素Codex 在 VSCode 里执行结束本机自动触发一个 Python 脚本脚本调用飞书应用机器人把「任务完成时间、项目路径、模型名、最后一条回复」推送到我的飞书单聊。这样我就可以把 VSCode 最小化去干别的事飞书响了再回来看结果。这套方案适合三类人一是经常用 Codex 做长任务的开发者二是想把 Codex 接入自己自动化流程的人三是已经在用飞书做团队协作、希望把本地开发动作同步到飞书的人。核心检索词就是 Codex、飞书、VSCode、Hook、Python下面我会把整条链路拆成可复制的配置和脚本。整条链路的关键在于 Codex 的 Hook 机制。Codex 提供了生命周期扩展点其中 Stop 事件会在当前回合停止时触发正好对应「任务执行完成」这个时机。我们只需要在 Hook 里挂一个本地命令让它去跑 Python 脚本脚本再走飞书的开放接口发消息。听起来步骤不少但真正需要你手写的只有两个文件一个hooks.json一个feishu_codex_done.py。另外Codex 本身需要调用模型能力如果你同时还在用别的 AI 编码工具Key 管理会变得很乱。我这边统一用 TaoToken 做 Key 接入Codex、其他 CLI 工具、脚本都走同一个 Key省得每个工具配一遍。下面会先讲 TaoToken 的前置配置再进入 Hook 和飞书部分。2. TaoToken 统一 Key 前置配置TaoToken 是一个面向开发者的模型接入平台官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。它的作用是让你用一个 Key 接入多种模型能力Codex、Claude Code、自己写的脚本都能复用同一套凭证不用在多个平台之间来回切换。对本文场景来说TaoToken 解决的是「Codex 调用模型」这一环。Hook 和飞书通知是本地逻辑但 Codex 执行任务本身需要模型服务把这块统一到 TaoToken 之后你只需要维护一个 Key后面换工具、加脚本都不用重新配。2.1 获取 API Key登录 TaoToken 后进入控制台找到 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。在这里创建一个新的 Key建议按用途命名比如codex-local方便以后区分。创建完成后立刻复制保存页面刷新后就看不到完整 Key 了。这个 Key 后面会写进 Codex 的配置文件也会被本地脚本读取所以不要提交到 Git也不要贴在公开聊天里。2.2 在 Codex 中配置 TaoTokenCodex 的模型接入配置一般放在用户目录下的config.toml。Windows 路径是C:\Users\YourName\.codex\config.tomlmacOS/Linux 是~/.codex/config.toml。下面是一个可复制的骨架把YOUR_TAOTOKEN_KEY换成你刚创建的 Key# ~/.codex/config.toml model gpt-5.5 model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat然后在系统环境变量里写入 Key。Windows PowerShellsetx TAOTOKEN_API_KEY YOUR_TAOTOKEN_KEYmacOS/Linux 写入~/.zshrc或~/.bashrcexport TAOTOKEN_API_KEYYOUR_TAOTOKEN_KEY改完环境变量后关闭当前终端重新打开再重启 VSCode让 Codex 扩展读到新变量。如果你不确定配置是否生效可以在 Codex 对话里发一句简单指令看它是否能正常返回能返回就说明模型接入没问题。2.3 为什么建议统一 Key我试过同时维护三四个平台的 Key结果就是每次换工具都要翻笔记还容易把旧 Key 填到新工具里。统一到 TaoToken 之后Codex、Claude Code、自己写的 Python 脚本都读同一个环境变量换机器时也只需要配一次。对于本文这种「Codex 本地脚本 飞书」的组合Key 越少排障路径越短。3. 飞书应用机器人与 open_id 配置飞书这边有两种机器人自定义机器人 Webhook 和应用机器人。Webhook 更适合往群里发消息配置简单但只能发群聊应用机器人可以给指定用户发单聊更适合「任务完成通知我本人」这个场景。本文用的是应用机器人。3.1 创建自建应用并开通权限进入飞书开放平台创建一个企业自建应用记下 App ID 和 App Secret。然后在「添加应用能力」里启用机器人。接着进入「权限管理」搜索并开通im:message:send_as_bot这个权限允许应用以机器人身份发消息。权限变更后必须重新发布应用版本否则权限不生效。路径是「应用发布 → 版本管理与发布 → 创建版本」版本说明随便写提交后如果提示免审核就直接生效。3.2 获取当前应用下的 open_idopen_id 是用户在当前应用下的身份标识不同应用之间不通用。很多人第一次失败就是因为复制了示例里的ou_xxx结果返回{ code: 99992351, msg: The request you send is not a valid {open_id} or not exists, field_violations: [ { field: receive_id, description: id not exist } ] }正确做法是在飞书开放平台的 API 调试台里选择你刚创建的应用选择成员为自己调用获取 open_id 的接口复制返回的ou_开头的值。这个值才是当前应用下真实存在的接收人 ID。3.3 用 API 调试台先验证发送在调试台里调用发送消息接口POST /open-apis/im/v1/messages查询参数receive_id_typeopen_id请求头带Authorization: Bearer tenant_access_token请求体{ receive_id: ou_xxxxxxxxxxxxxxxxxxxxx, msg_type: text, content: {\text\:\Codex 单聊通知测试成功\}, uuid: codex-test-001 }注意content必须是字符串不是 JSON 对象。写成对象会报 content invalid。调试台能收到消息说明应用配置和 open_id 都没问题接下来才进入本地脚本环节。4. 可复制的 Hook 与 Python 脚本配置这一节是全文的核心两个文件hooks.json负责告诉 Codex 什么时候跑脚本feishu_codex_done.py负责发飞书消息。4.1 写入环境变量脚本需要四个环境变量App ID、App Secret、接收人 ID、接收人 ID 类型。Windows PowerShellsetx FEISHU_APP_ID cli_xxxxxxxxxxxxxxxxx setx FEISHU_APP_SECRET 你的 App Secret setx FEISHU_RECEIVE_ID ou_xxxxxxxxxxxxxxxxxxxxx setx FEISHU_RECEIVE_ID_TYPE open_idsetx只对之后启动的进程生效所以执行完要关闭当前 PowerShell重新打开并重启 VSCode。否则 Codex 和脚本都读不到变量。4.2 编写 Python 通知脚本创建目录和脚本文件mkdir $env:USERPROFILE\.codex\hooks -Force notepad $env:USERPROFILE\.codex\hooks\feishu_codex_done.py写入下面这份脚本它做了四件事读取 Hook 传入的 stdin JSON、获取 tenant_access_token、发送飞书消息、把过程写进日志文件方便排障。import datetime import json import os import sys import traceback import urllib.error import urllib.request APP_ID os.environ.get(FEISHU_APP_ID, ).strip() APP_SECRET os.environ.get(FEISHU_APP_SECRET, ).strip() RECEIVE_ID os.environ.get(FEISHU_RECEIVE_ID, ).strip() RECEIVE_ID_TYPE os.environ.get(FEISHU_RECEIVE_ID_TYPE, open_id).strip() LOG_PATH os.path.join(os.path.expanduser(~), .codex, hooks, feishu_codex_done.log) def safe_unicode(text): if text is None: return if not isinstance(text, str): text str(text) return text.encode(utf-8, replace).decode(utf-8, replace) def cut(text, max_len2000): text safe_unicode(text) if not text: return return text if len(text) max_len else text[:max_len] ... def log(message): try: os.makedirs(os.path.dirname(LOG_PATH), exist_okTrue) now datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S) with open(LOG_PATH, a, encodingutf-8, errorsreplace) as f: f.write(f[{now}] {safe_unicode(message)}\n) except Exception: pass def post_json(url, payload, headersNone): data json.dumps(payload, ensure_asciiFalse).encode(utf-8, replace) req urllib.request.Request( url, datadata, headers{Content-Type: application/json; charsetutf-8, **(headers or {})}, methodPOST, ) try: with urllib.request.urlopen(req, timeout20) as resp: raw resp.read().decode(utf-8, errorsreplace) log(fPOST OK url{url} status{resp.status} body{cut(raw, 3000)}) return json.loads(raw) except urllib.error.HTTPError as e: raw e.read().decode(utf-8, errorsreplace) log(fPOST HTTP ERROR url{url} status{e.code} body{cut(raw, 3000)}) raise except Exception: log(POST EXCEPTION:\n traceback.format_exc()) raise def require_feishu_ok(name, result): if not isinstance(result, dict): raise RuntimeError(f{name} 失败返回不是 JSON{result}) if result.get(code) ! 0: raise RuntimeError(f{name} 失败{result}) def get_tenant_access_token(): result post_json( https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal, {app_id: APP_ID, app_secret: APP_SECRET}, ) require_feishu_ok(获取 tenant_access_token, result) return result[tenant_access_token] def send_feishu_message(token, text): result post_json( fhttps://open.feishu.cn/open-apis/im/v1/messages?receive_id_type{RECEIVE_ID_TYPE}, { receive_id: RECEIVE_ID, msg_type: text, content: json.dumps({text: safe_unicode(text)}, ensure_asciiFalse), }, headers{Authorization: fBearer {token}}, ) require_feishu_ok(发送飞书消息, result) return result def read_hook_stdin(): try: raw_stdin sys.stdin.read() log(fHOOK RAW STDIN: {cut(raw_stdin, 3000)}) if not raw_stdin.strip(): return {} return json.loads(raw_stdin) except Exception: log(HOOK STDIN JSON PARSE FAILED:\n traceback.format_exc()) return {} def main(): try: hook_data read_hook_stdin() if not APP_ID or not APP_SECRET or not RECEIVE_ID: log(MISSING ENV: APP_ID%s APP_SECRET%s RECEIVE_ID%s % ( bool(APP_ID), bool(APP_SECRET), bool(RECEIVE_ID))) print(json.dumps({continue: True}, ensure_asciiFalse)) return cwd safe_unicode(hook_data.get(cwd, )) model safe_unicode(hook_data.get(model, )) event safe_unicode(hook_data.get(hook_event_name, )) last_msg cut(hook_data.get(last_assistant_message, ), 500) now datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S) text ( Codex 执行完成\n\n f时间{now}\n f项目{cwd if cwd else 未知}\n f模型{model if model else 未知}\n f事件{event if event else Stop}\n f最后回复{last_msg if last_msg else 无} ) token get_tenant_access_token() send_feishu_message(token, text) log(FEISHU SEND SUCCESS) except Exception: log(FEISHU NOTIFY FAILED:\n traceback.format_exc()) finally: print(json.dumps({continue: True}, ensure_asciiFalse)) if __name__ __main__: main()脚本最后输出{continue: true}是给 Codex Hook 的约定返回表示不阻断后续流程。4.3 配置 hooks.json创建 Hook 配置文件notepad $env:USERPROFILE\.codex\hooks.json写入下面内容把YourName换成你的 Windows 用户名{ hooks: { Stop: [ { hooks: [ { type: command, command: python \C:\\Users\\YourName\\.codex\\hooks\\feishu_codex_done.py\, timeout: 15, statusMessage: Sending Feishu notification } ] } ] } }这里建议写绝对路径。我一开始用%USERPROFILE%变量在 Codex Hook 里不一定会展开改成绝对路径后稳定触发。5. 验证请求与成功结果配置写完后先手动验证脚本再验证 Hook。5.1 手动测试脚本不要直接python feishu_codex_done.py脚本会等 stdin看起来像卡住。正确方式是传一个空 JSONecho {} | python $env:USERPROFILE\.codex\hooks\feishu_codex_done.py如果飞书收到消息说明脚本、环境变量、飞书应用配置都通了。再模拟一次 Codex 传入的数据{cwd:D:\\TestProject,model:gpt-5.5,hook_event_name:Stop,last_assistant_message:Test from local PowerShell} | python $env:USERPROFILE\.codex\hooks\feishu_codex_done.py这次飞书通知里应该能看到项目路径、模型名和最后回复。5.2 在 VSCode 中确认 Hook重启 VSCode在 Codex 输入框里输入/hooks如果配置被读取会显示 Stop 事件对应的命令、超时和状态消息。如果提示未信任在这个界面里启用信任。5.3 触发一次真实任务让 Codex 执行一个小任务比如「请回复一句 hello不要修改文件」。等它结束后飞书应该自动收到通知。收到就说明整条链路跑通了Codex Stop Hook → Python 脚本 → 飞书应用机器人 → 单聊通知。6. 本篇常见错误排查下面这些坑是我实际踩过的按出现频率排序。现象原因解决方式飞书返回 id not existopen_id 不是当前应用下的真实 ID在 API 调试台重新获取权限已添加但发不出消息权限变更后未重新发布应用创建版本并发布content invalidcontent 写成了 JSON 对象改成 JSON 字符串python 命令不可用未安装 Python 或被 Store 别名拦截安装 Python 并检查执行别名手动运行脚本没反应脚本在等 stdin用 echo {}Hook 不触发未信任或路径未展开/hooks确认改用绝对路径环境变量读不到setx 后未重启终端和 VSCode关闭重开重启 VSCode中文乱码编码或引号问题用脚本里的 safe_unicode 处理如果脚本没发消息先看日志文件C:\Users\YourName\.codex\hooks\feishu_codex_done.log里面会记录 stdin 内容、HTTP 状态码和飞书返回体比盲猜快很多。另外提醒一句App Secret 如果出现在截图或草稿里建议立刻去飞书开放平台重置然后重新setx并重启 VSCode。真实 App Secret、open_id、项目路径都不要提交到 Git。7. 继续扩展与接入入口跑通之后可以按需扩展通知里加任务耗时、区分成功失败、列出修改过的文件、只显示项目名而不是完整路径、不同项目发到不同飞书会话、换成消息卡片让通知更好看。这些都是在 Python 脚本里改文本和字段Hook 配置不用动。如果你还没配 TaoToken 的 Key可以从 API Keys 页面开始https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。想先验证模型是否正常返回可以用模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 。如果你打算长期用 Codex 做编码和 Agent 任务Coding Plan 会更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。接入过程中遇到配置问题可以对照接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。用 Claude Code 的话Anthropic 接入说明在这里https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 。整套方案里最容易卡住的不是代码而是飞书 open_id、权限发布、Python 环境和 Hook 路径这四件事。把这四点处理好剩下的就是复制粘贴。

相关推荐

Java代码热更新全解析:原理、实战与踩坑指南
Java代码热更新全解析:原理、实战与踩坑指南

1. 热更新解决的痛点:从“改一行重启三分钟”说起代码热更新这件事,我最早被它“救命”是在做 Java Web 维护的时候。线上一个老项目出了个小 bug,按传统流程走:改代码、打包、传包、重启容器,前后折腾十几分钟&#x… · 2026/9/26 7:26:10

Zotero翻译插件选型与配置指南:从划词翻译到DeepSeek大模型接入
Zotero翻译插件选型与配置指南:从划词翻译到DeepSeek大模型接入

1. 学术文献阅读的痛点与Zotero翻译方案选型1.1 为什么我们需要在Zotero里直接翻译PDF读外文文献这件事,最折磨人的从来不是看不懂单词,而是在阅读器和翻译工具之间反复横跳。我早期读英文论文的流程是这样的:Zotero里打开PDF,遇到… · 2026/9/26 7:26:10

Thread Dump 实战:从 jstack 抓取到锁分析,快速定位 Java 线上性能问题
Thread Dump 实战:从 jstack 抓取到锁分析,快速定位 Java 线上性能问题

简介:这是一款面向Java开发者和运维人员的线程转储分析工具,主要用于诊断应用响应慢、无响应等并发问题,通过Web界面上传并解析线程转储文件,快速识别死锁、线程阻塞及堆栈异常。压缩包内含81个文件,包体仅1.49MB&… · 2026/9/26 7:26:10

ThinkPHP+Laravel+Vue二手车销售平台开发实战
ThinkPHP+Laravel+Vue二手车销售平台开发实战

做二手汽车销售平台,一开始摆在面前的两条路就挺有意思。项目标题里同时挂了ThinkPHP和Laravel,很多同行看到第一反应是“这俩框架选一个不就完了吗”。实际做下来你会发现,真正落地的项目里,这个选择题背后牵扯的是团队技术栈、服… · 2026/9/26 7:56:47

UE5内置建模工具链:Modeling Mode与Geometry Script实战指南
UE5内置建模工具链:Modeling Mode与Geometry Script实战指南

1. 从“37”说起:为什么 UE5 的建模工具链值得单独拎出来聊 如果你最近在 UE5 里折腾过场景搭建,大概率会遇到一个尴尬的瞬间:美术给的模型还没到位,但你想先摆个白模看看比例;或者从商城买来的资产面数爆炸&#xff0… · 2026/9/26 7:56:47

无畏契约Vanguard启动报错全解析:从服务到驱动的排查与修复指南
无畏契约Vanguard启动报错全解析:从服务到驱动的排查与修复指南

1. 先搞清楚Vanguard到底在干什么很多人一看到无畏契约启动报错,第一反应就是“游戏坏了”,然后开始重装游戏、重装系统,折腾一整天问题还在。实际上,无畏契约的启动链路比大多数游戏复杂得多,它不是一个单纯的游戏客户… · 2026/9/26 7:56:35

iOS国密改造实战:OpenSSL集成SM2/SM4与避坑指南
iOS国密改造实战:OpenSSL集成SM2/SM4与避坑指南

简介:面向iOS平台国密算法开发者的实践参考,内容围绕SM2加密在iOS侧的落地展开,基于GmSSL改造整理,弥补了网上iOS端缺少可直接参考国密示例的空白。作者在C语言基础较弱、现有实现代码杂乱且缺少注释的条件下反复踩坑,… · 2026/9/26 7:56:35

手写SQL解析器:词法分析、AST与生产级选型实践
手写SQL解析器:词法分析、AST与生产级选型实践

简介:基于Flex与Bison这两款开源编译器工具构建的SQL解析器完整工程,面向数据库内核研发和编译器技术学习者,提供从SQL语句输入到词法切分、语法检查、抽象语法树构建再到中间表示输出的完整实现参考。压缩包共包含11个文件,以四个… · 2026/9/26 7:56:29

金融技术服务项目启动前提与内容规范
金融技术服务项目启动前提与内容规范

我无法根据当前输入生成符合要求的博文。原因如下:项目标题为"financial-services",这是一个高度泛化的行业术语,本身不构成具体可操作、可拆解的项目或技术主题;项目正文为空,未提供任何实质性描述、功能定… · 2026/9/26 7:56:29

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码