Codex Skills 实战:自动采集当日 Git 提交,一键排版精美工作日报并发送邮件到 TaoToken 1. 为什么我又把日报自动化捡起来了每天写工作日报这件事说大不大说小也真能耗掉半小时。我所在的团队要求当天提交、当天汇总格式还得统一今天做了什么、改了哪些模块、有没有风险。问题是白天写代码已经切了七八个分支晚上再靠回忆去拼日报写出来基本是流水账领导看着也累。后来我换了个思路既然 Git 提交本身就是最真实的工作痕迹那日报的原料其实早就躺在仓库里了。缺的只是把git log里那些fix: xxx、feat: xxx的机器语言翻译成人类能读的职场表达再排版成一封像样的邮件。这就是 Codex Skills 派上用场的地方。Codex 的 Skills 机制允许你把「执行 shell 命令」「调用模型」「渲染模板」「发邮件」这些动作编排成一个可复用的技能而不是每次手写一堆胶水脚本。我把它落地成了一个叫daily-reporter的 Skill触发后自动采集当天 Git 提交交给模型做语义扩写和分类渲染成 HTML 邮件最后通过 SMTP 发出去。这篇文章面向的是想自己搭一条日报流水线的开发者尤其是团队里有固定汇报要求、又不想每天手动整理的人。整套流程我实测跑通了下面把配置片段、脚本和踩过的坑都摊开讲。核心检索词就三个Codex Skills、Git 提交采集、工作日报邮件自动化。你跟着做大概四十分钟能跑通第一封。需要说明的是模型调用这一环我用的是 TaoToken 的 API 来承接因为它同时提供对话模型和编码模型配置上比较省事。下面第二节会讲怎么拿 Key 和配 Base URL。2. 前置准备TaoToken 的 Key、Base URL 与模型选择在写 Skill 之前先把模型这一侧的接入搞定。Codex Skills 本身负责编排真正做「语义润色」的是大模型所以你需要一个稳定的 API 入口。第一步是拿 Key。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后在控制台里创建 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 进去之后左侧找 API Keys 菜单新建一个复制出来先存到环境变量里别直接写进脚本。第二步是确认 Base URL。TaoToken 的 API 入口是 https://taotoken.net/api 注意这个地址后面不加任何 UTM 参数直接用它作为 OpenAI 兼容的 base_url 就行。也就是说你原来用 OpenAI SDK 的地方把base_url换成它api_key换成刚拿到的 Key代码基本不用动。第三步是选模型。日报润色这个任务对模型的语义理解要求中等偏上但不需要顶配。我一般用对话模型来做扩写和分类因为它的指令跟随比较稳。如果你后面想把日报生成接到更长的编码 Agent 流程里可以考虑 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合长期、高频的编码类调用。这里有个关键点Codex Skills 在调用模型时需要三件套齐全——Base URL、API Key、Model ID。少一个都会报错。Model ID 就是你在控制台或文档里看到的模型名称填到配置里即可。文档入口在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的模型列表和参数说明配之前扫一眼能省不少事。如果你只是想先验证模型通不通可以用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 手动发一条消息试试确认 Key 有效、余额正常再去写 Skill。这一步别跳过我见过太多人脚本报 401最后发现是 Key 复制时带了空格。环境变量建议这样设Linux/macOS 下写进~/.bashrc或~/.zshrcexport TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_MODEL你的模型ID export SMTP_SERVERsmtp.example.com export SMTP_PORT587 export SENDER_EMAILyouexample.com export SENDER_PASSWORD你的邮箱授权码 export RECIPIENT_EMAILmanagerexample.comWindows 用户可以在系统环境变量里逐条添加或者用.env文件配合python-dotenv读取。注意SENDER_PASSWORD一般不是邮箱登录密码而是邮箱服务商提供的「授权码」或「应用专用密码」这个坑后面第五节会细说。3. 可复制配置Skill 定义与三件套 JSON 片段这一节是整篇的核心给你可以直接抄的配置。Codex Skills 的目录结构一般是每个技能一个文件夹里面放一个描述元数据的配置文件加执行脚本。我把它组织成这样skills/ daily-reporter/ skill.yaml config.json main.py git_fetcher.py report_generator.py email_sender.py先看skill.yaml它告诉 Codex 这个技能叫什么、什么时候触发、入口在哪name: daily-reporter description: 采集当日 Git 提交生成结构化工作日报并发送邮件 version: 1.0.0 entrypoint: main.py triggers: - 生成今日日报 - 发送工作日报 - daily report permissions: - shell:git - network:smtp - network:https env: - TAOTOKEN_API_KEY - TAOTOKEN_BASE_URL - TAOTOKEN_MODEL - SMTP_SERVER - SMTP_PORT - SENDER_EMAIL - SENDER_PASSWORD - RECIPIENT_EMAIL然后是config.json这里放模型三件套和邮件参数。注意 Base URL 和 Model ID 要和你在 TaoToken 控制台看到的一致{ llm: { base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: 你的模型ID, temperature: 0.4, max_tokens: 2000 }, git: { days: 1, exclude_keywords: [Merge, Revert, Merge branch, Merge pull request], repo_path: . }, email: { smtp_server_env: SMTP_SERVER, smtp_port_env: SMTP_PORT, sender_env: SENDER_EMAIL, password_env: SENDER_PASSWORD, recipient_env: RECIPIENT_EMAIL, subject_template: 工作日报 - {date} } }如果你更习惯 TOML等价写法是这样放在config.toml里脚本里用tomllib读[llm] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model 你的模型ID temperature 0.4 max_tokens 2000 [git] days 1 exclude_keywords [Merge, Revert, Merge branch, Merge pull request] repo_path . [email] smtp_server_env SMTP_SERVER smtp_port_env SMTP_PORT sender_env SENDER_EMAIL password_env SENDER_PASSWORD recipient_env RECIPIENT_EMAIL subject_template 工作日报 - {date}这里要强调三件套的完整性Base URL 是https://taotoken.net/apiAPI Key 从环境变量读Model ID 填你选的模型。三者缺一调用就会失败。我建议把config.json里的api_key永远写成api_key_env这种间接引用避免 Key 泄露到版本库。Git 采集部分核心命令是这一条它决定了原始数据的格式git log --since1 days ago --no-merges \ --prettyformat:commit %H%nAuthor: %an %ae%nDate: %ad%nSubject: %s%n--no-merges直接过滤掉合并提交--prettyformat把每条提交压成固定几行方便后面用 Python 解析。--since1 days ago控制时间窗口想生成周报就改成7 days ago。邮件模板我用的是内联 CSS 的 HTML因为很多邮件客户端会剥离style标签内联更保险。模板里留了几个占位符{date}、{summary}、{total_commits}、{files_changed}以及四个分类区块。渲染时用 Python 的str.format填充即可。配置写完后先别急着发邮件。把main.py里的发送步骤注释掉只跑到「生成 HTML」这一步把 HTML 存成本地文件用浏览器打开看看排版。确认没问题了再打开发送。这个习惯能帮你省掉很多「发出去才发现格式乱」的尴尬。4. 验证请求从一次真实提交到邮件送达配置就绪后来跑一次完整验证。我拿一个真实的小仓库做演示当天有两条提交一条新增登录模块一条修复登录超时。先手动确认 Git 能采到数据cd /path/to/your/repo git log --since1 days ago --no-merges --oneline如果这条命令有输出说明采集窗口没问题。如果为空要么当天真没提交要么时间窗口设错了。接着跑 Skill 的入口脚本python skills/daily-reporter/main.py脚本会依次打印四个阶段。第一阶段采集输出类似[1/4] 正在采集 Git 提交记录... 发现 2 条有效提交。第二阶段调用模型。这里我用 TaoToken 的 API 做语义扩写核心调用片段是这样import os from openai import OpenAI client OpenAI( base_urlos.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api), api_keyos.getenv(TAOTOKEN_API_KEY) ) def polish_commit(subject: str, files: list) - str: prompt f你是一名资深工程师请把下面的 Git 提交信息扩写成一句专业的日报描述。 要求说明做了什么、影响范围、业务价值控制在 80 字以内不要用第一人称。 提交信息{subject} 涉及文件{, .join(files[:5])} resp client.chat.completions.create( modelos.getenv(TAOTOKEN_MODEL), messages[{role: user, content: prompt}], temperature0.4, max_tokens300 ) return resp.choices[0].message.content.strip()注意resp.choices[0].message.content这个取值路径如果你用的是别的 SDK 或封装字段名可能不同报reading choices之类的错多半是这里对不上。模型返回后脚本把结果分类进「新功能」「Bug 修复」「重构与优化」「其他」四个桶再渲染 HTML。第三阶段发送邮件成功时输出[3/4] 正在发送邮件... 邮件发送成功第四阶段收尾。整个过程我实测下来从触发到收件箱收到邮件大概十几秒。收到的邮件里顶部是当日总结比如「今日工作进展顺利。完成了 1 项新功能开发。修复了 1 个已知 Bug。」下面按分类列出每条提交的润色描述底部有提交总数和变更文件数。如果你想定时触发不用手动跑加一条 cron 就行。比如每个工作日晚上七点自动生成0 19 * * 1-5 cd /path/to/repo /usr/bin/python3 skills/daily-reporter/main.py /var/log/daily-reporter.log 21Windows 用户可以用任务计划程序触发条件设为每天固定时间操作里填python.exe的完整路径和脚本路径。日志一定要重定向不然出错了你都不知道。验证成功的标志有三个Git 采集条数和你预期一致、模型返回的描述读起来通顺、邮件在收件箱里排版正常。三个都满足这条流水线就算立住了。5. 常见报错排查401、local proxy failed 与 OAuth搭这套东西报错基本集中在模型调用和邮件发送两块。我把踩过的坑按现象列出来你对照着查。401 Unauthorized。这是最常见的。原因通常是 API Key 没读到、Key 失效、或者 Base URL 写错了。先确认环境变量真的生效了echo $TAOTOKEN_API_KEY echo $TAOTOKEN_BASE_URL如果输出为空说明 shell 没加载到检查~/.bashrc有没有 source。如果 Key 有值但还是 401去 TaoToken 控制台 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 确认这个 Key 还在、没被删、余额够。还有一种情况是 Base URL 末尾多写了斜杠或路径正确写法就是https://taotoken.net/api别自己加/v1之类。local proxy failed / connection error。这个报错说明请求根本没出去或者被本地网络环境拦了。先检查你的机器能不能正常访问外网再确认没有奇怪的本地代理配置干扰。如果你在公司内网可能需要让运维放行对应域名。注意这里不要试图用任何非正规的网络工具去绕过合规的网络环境才是前提。排查时可以先写个最小请求测试import os from openai import OpenAI client OpenAI(base_urlos.getenv(TAOTOKEN_BASE_URL), api_keyos.getenv(TAOTOKEN_API_KEY)) print(client.models.list())能列出模型说明链路通报连接错误就是网络或地址问题。reading choices of undefined。这是解析响应时字段对不上。常见于你把非 OpenAI 兼容的返回结构当成标准结构来读。确认你用的 SDK 版本和调用方式匹配resp.choices[0].message.content是标准 OpenAI 格式。如果模型返回被截断或报错choices可能为空数组取值前先判空if not resp.choices: raise RuntimeError(f模型返回异常: {resp})OAuth / 认证相关报错。如果你在 Codex 或某些客户端里配置时看到 OAuth 字样通常是因为客户端默认走了账号授权流程而你要用的是 API Key 模式。这时候需要在设置里切换到 API Key 认证把 Base URL、Key、Model ID 三件套填全。以 Claude Code 这类工具为例配置里要明确写ANTHROPIC_BASE_URL或对应的base_url再配api_key缺一个都会回落到 OAuth 流程然后失败。SMTP 认证失败。邮件发不出去先看是不是用了登录密码而不是授权码。QQ 邮箱、163 邮箱、Gmail 都需要单独开 SMTP 并生成授权码。端口也要对587 走 STARTTLS465 走 SSL别混用。如果报SMTPAuthenticationError九成是授权码错了。采集到 0 条提交。先手动跑git log --since1 days ago确认有数据。如果手动有、脚本没有检查脚本执行时的工作目录是不是仓库根目录repo_path配对了没。cron 场景下尤其容易踩这个因为 cron 的默认目录不是你的项目目录所以命令里要显式cd。邮件发出去了但排版乱。多半是邮件客户端剥离了style。把关键样式改成内联或者用表格布局。测试时多换几个客户端看网页版、手机端都试一遍。排查顺序建议固定成先测模型链路最小请求再测 Git 采集手动命令最后测邮件单独发一封测试邮件。三段分开验证比一上来跑全流程然后对着一个报错猜要快得多。6. 把日报流水线用起来从今天开始整套东西搭完你手里就有了一条能自己跑的日报流水线。它不依赖你当天记不记得做了什么Git 提交就是事实来源也不依赖你文笔好不好模型负责把技术语言翻译成汇报语言。我自己的用法是把它挂在每天下班前半小时触发邮件先发给自己扫一眼没问题再转发给领导。这样既保留了人工把关又省掉了从零组织语言的时间。如果你团队对日报格式有硬性要求把 HTML 模板改一改就能对齐。想继续往下走的话有两个方向。一是把时间窗口从 1 天扩到 7 天同一套脚本就能出周报只要改config.json里的days。二是把模型调用换成更适合长期编码场景的方案如果你后面想把日报生成接到更大的 Agent 工作流里可以看看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它在高频调用下更稳。配置过程中如果卡在 Key 或模型 ID 上直接去 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 核对或者翻文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 找对应模型的准确名称。先把最小请求跑通再回来跑全流程这条路我走过最省时间。