ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ai-job-search /gmail-sync 命令全解析:让 Gmail 成为求职申请的自动状态源

ai-job-search /gmail-sync 命令全解析:让 Gmail 成为求职申请的自动状态源 AI 应用AI 技能【免费下载链接】ai-job-searchThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.项目地址https://gitcode.com/GitHub_Trending/ai/ai-job-search点击查看免费下载/gmail-sync是 ai-job-search 框架中一个读取邮件、提议更新、获批后才落盘的 Claude Code 命令它扫描 Gmail 中与已追踪申请相关的邮件面试邀请、在线测评、Offer、拒信自动分类后以批量表格的形式提议写入job_search_tracker.csv与documents/applications/company_role/outcome.md经用户批准后才真正写入。读完本文你将掌握该命令的完整调用约定、Gmail 查询语义、信号分类表、CSV 写入安全规则与幂等状态机并能直接复刻这套机器分类 人类批准的应用状态同步流程。本文以仓库中的命令规范 .claude/commands/gmail-sync.md 为主体骨架并结合其配套测试与相关源码展开实现细节。一、命令定位与 /outcome 的分工在 ai-job-search 的申请管线中job_search_tracker.csv的status列与每个申请的归档outcome.md是两处框架已经读取、但过去没有系统写入的数据落点参见 .claude/commands/outcome.md 的定位说明。/outcome直接询问用户发生了什么由用户口述进展与结果后写入同一两个位置使用同一套 schema。/gmail-sync不去问用户而是自己阅读真实邮件并分类——但它从不自主写入。每一条被分类出的变更都会在触碰 tracker 或outcome.md之前以整批提议的形式呈现只有用户批准后才继续整批一次批准可以先写后标记绝对不行。两者写的是同一批文件、同一套 schema区别在于信息的来源/outcome的来源是用户的记忆/gmail-sync的来源是雇主的真实邮件。正因如此/gmail-sync有一条铁律错误的写入会静默污染/setup后续用于校准的应用历史所以每条提议的变更都必须引用其来源邮件每个不确定的案例都必须浮出水面而不是猜测。命令规范开篇将其定位概括为不是在收件箱里发现点什么而是为一条永久记录提出一行有据可依的正确数据并且只在用户说是之后才写入。在 README.md 的命令清单中/gmail-sync也被明确描述为通过 Gmail 连接器读取邮件中的状态信号以整批提议让用户在写 tracker /outcome.md之前批准每条变更都引用来源邮件Offer 只推进到提出offer绝不猜测hired/offer_declined冲突或无法匹配的信号交给手动/outcome处理而不是猜。整个命令按 10 个步骤严格按顺序执行下文逐一展开。二、Step 0前置条件运行前必须确认 Gmail MCP 工具mcp__claude_ai_Gmail__*可用。如果不可用告诉用户在 claude.ai 的 Settings → Connectors → Gmail 中连接 Gmail 集成然后停止。严禁通过 Bash、IMAP 或其他任何渠道绕过该集成去读邮件——这不仅是为了简化实现更是安全边界邮件的读取必须发生在受控的官方连接器内而不是任意的 shell 命令通道。这一条与仓库整体的安全姿态一致tools/security_guards.py及其测试 tests/test_security_guards.py 对.claude/settings.json中的权限允许列表进行白名单审查拒绝Bash(*)、Bash(curl:*)等通配型权限/gmail-sync的只用 MCP 读 Gmail、不走 Bash正是这一思想的延伸。三、Step 1解析输入参数$ARGUMENTS支持三种形态且三种形态互不影响、不会改写持久化状态输入行为无参数使用默认回溯窗口见 Step 3 的 lookback 规则公司名如/gmail-sync acme将搜索范围限定到这一条已追踪的申请since YYYY-MM-DD仅本次运行覆盖回溯起始日期不修改持久化的 state 文件注意since的语义它是本次运行有效的一次性覆盖而 state 文件中持久化的last_sync才是跨运行的默认基准。四、Step 2加载状态本步分四件事读job_search_tracker.csv。如果文件不存在告诉用户还没有可同步的对象建议先运行/outcome或/apply然后停止。不要在这里创建它——/gmail-sync永远不会发起新申请它只更新已存在的申请。读gmail_sync/state.json缺失则创建初始形状为{last_sync: null, processed_message_ids: []}。这是该命令的幂等锚点详见 Step 8。构建开放申请集合tracker 行中status不是Final按 .claude/commands/outcome.md 中Tracker status vocabulary的定义Final hired、rejected、no_response、offer_declined、withdrawn的行。对每一条按 documents/README.md 的Subfolder naming规则推导归档目录documents/applications/company_role/并检查其中是否存在outcome.md。Step 7a 的任何写入都要复用这个推导出的精确路径。关键点drafted行留在开放集合中这正是值得去搜邮件的理由。/apply写出行时从不提交用户手动提交后可能想不起来运行/outcome。一条针对仍标记为drafted的行的回复正是这种情况——而这行正好持有搜索所需的公司名。公司过滤如果$ARGUMENTS指定了公司对开放集合做大小写不敏感的过滤。找不到匹配 → 告知用户并停止不猜测。从测试可以看到这一层的约束很严格tests/test_tracker_status_vocab.py 中的test_gmail_sync_references_vocabulary_block断言/gmail-syncStep 2必须引用/outcome的词汇表块Tracker status vocabulary而不得在本地方重申 Final 状态集合尤其禁止本地再出现no response这种空格拼写——因为历史上 #298 正是本地第二份拼写清单漂移导致的 bug。五、Step 3构建 Gmail 搜索查询回溯窗口lookback三选一优先级从高到低since date参数若给state.last_sync若已设置默认newer_than:30d。随后按以下顺序组装查询调用list_labels寻找名称暗示求职邮件的用户标签如名称大小写不敏感地包含 job、application、career。找到则记下其id。规范化公司名供后续匹配转为小写去掉inc、inc.、llc、ltd、a/s、corp、corporation、group等法律后缀去掉标点折叠空白。组合 Gmail 查询用OR分组{}语法拼接若找到求职标签label:id开放申请公司名的引号 OR 组如{Acme Corp BigCo}常见 ATS 平台发件人域名 OR 组{from:greenhouse.io from:lever.co from:myworkday.com from:ashbyhq.com from:smartrecruiters.com from:icims.com from:bamboohr.com}回溯上界如newer_than:30d或after:2026/06/15-in:sent -in:drafts。-in:sent -in:drafts这两个负运算符是整个查询语义的精髓。为什么不是in:inbox因为in:inbox只匹配当前仍在收件箱的邮件会静默排除所有已归档邮件——而 Step 3.1 找到的求职标签所命中的邮件恰好通常已被标准过滤器归档skip the inbox限制到收件箱会把这些目标邮件一起丢掉。负运算符则既排除了你发出去的又保留了归档邮件与标签过滤邮件的范围。示例newer_than:30d -in:sent -in:drafts ({Acme Corp BigCo} OR {from:greenhouse.io from:lever.co from:myworkday.com from:ashbyhq.com})调用search_threads使用view: THREAD_VIEW_MINIMAL、pageSize: 50通过pageToken翻页直到耗尽或结果明显超出相关窗口。这一查询语义被仓库测试 tests/test_gmail_sync_command.py 用两条断言钉死test_query_excludes_sent_and_drafts_explicitly规范文本中必须包含-in:sent -in:draftstest_query_never_restricts_to_the_inbox规范文本中不得出现in:inbox剔除负运算符字面后。该测试文件的 docstring 还点明了失败模式in:inbox的失败是静默的欠检出——漏掉一条拒绝或面试邀请看起来只是没有更新。这正是从源码结构看查询语义的正确性被当作安全不变量来守护的证据。六、Step 4过滤到新邮件对每条返回的线程用其消息 ID 与state.processed_message_ids比对如果线程中的每条消息都已被处理整条线程跳过存在未处理消息的线程调用get_thread并使用messageFormat: FULL_CONTENT取回完整正文——Step 5 的分类绝不能只依赖 snippet/主题行因为 snippet 会截断那个区分我们想约个电话聊聊与感谢你申请的关键短语。七、Step 5分类每条未处理消息分类分两阶段先匹配申请再匹配信号。申请匹配对每条新消息先将规范化后的发件人域名 / 显示名 / 主题 / 正文与 Step 3 的规范化公司名比对。没有可信匹配公司确实缺席或在两家已追踪公司之间模棱两可→不提任何写入在 Step 6 摘要中记为 unmatched 并处理下一条。信号分类对匹配上的消息按内容分类且要求信号短语出现在主题或开头几行——只出现在深层转发的线程或新闻通讯页脚的某公司名不算信号。完整的信号表如下这正是命令规范的核心表格须逐行完整保留信号示例措辞trackerstatusoutcome.md动作申请确认ackweve received your applicationdrafted→applied其他状态(无变更)对drafted行这是唯一一条能证明用户手动提交了的邮件且通常在提交后一天内到达——提议状态迁移date设为邮件日期。任何其他状态上它都是噪音。OA / 在线测评online assessment、coding challenge、complete your assessment、HackerRank/Codility 链接interview勾选最近的匹配阶段复选框若没有合适的复选框则在 Notes 中加一行——测评并不总是一个列出的阶段面试邀请 / 已排期schedule a call、phone screen、technical interview、next round、onsite、final roundinterview用邮件日期勾选匹配的阶段复选框Offer 发出pleased to offer、extend an offer、offer letteroffer勾选 Offer received 复选框。绝不从邮件提议hired或offer_declined——接受还是拒绝是用户的现实决策不是可推断的事。在 Step 6 摘要中显著标记为需要用户决策与普通的批准/跳过表分开。拒绝moving forward with other candidates、not selected、unable to proceed、decided not to continuerejected设置Status: rejected、Date resolved:为邮件日期冲突规则如果分类出的信号与申请当前的终结性相矛盾例如一家 tracker 行刚有了一条rejected邻接写入的公司又收到一封 moving forward 邮件或本次运行已提议了 Offer 后又收到拒绝——不要提议覆盖它。在 Step 6 中记为冲突留给手动/outcome解决。八、Step 6呈现提议更新此时什么都没写这是批准前置的核心环节尚未写入任何东西。Step 5 的全部分类结果以单一批次呈现让用户在触碰 tracker 或outcome.md之前看到全貌。规范中的标准输出模板如下## Gmail Sync - Proposed Updates - YYYY-MM-DD Scanned N threads (M new messages) since lookback date. ### Proposed Changes (reply approve all, or list which to skip, e.g. skip 2) | # | Company | Role | Signal | Current - Proposed Status | Source Email (date) | |---|---|---|---|---|---| | 1 | ... | ... | Interview invite | applied - interview | Subject line (2026-07-10) | | 2 | ... | ... | Offer extended | interview - offer | Subject line (2026-07-12) | | 3 | ... | ... | Application ack | drafted - applied, date - 2026-07-02 | Subject line (2026-07-02) | ### Needs Manual Review (conflicting signal - not proposed, use /outcome) - **Company** - what conflicted and why it wasnt proposed ### Unmatched Emails (no change proposed) - subject from sender - looked job-related but couldnt be confidently linked to a tracked application. ### Stale Applications (30 days, no activity) - **Company** - last activity YYYY-MM-DD, still status.模板中有几个值得注意的语义细节行 3 的日期变更drafted行的当前状态单元里同时显示日期变更drafted - applied, date - 2026-07-02。因为该行从未被记录为已提交Step 7a 即将替换起草日期。命令要向用户说明日期取自邮件并询问用户是否知道真实提交日期——批准状态迁移不应静默批准一个用户可以纠正的日期。若 Proposed Changes 表为空简短说明后直接跳到 Step 8更新状态——没有可批准的东西。Offer 仍然进入提议表tracker 移到offer只有hired/offer_declined永不被提议。Needs Manual Review、Unmatched Emails、Stale Applications 三个分区与提议表分开保证冲突与未匹配项不会被误当作可批准项。九、Step 7等待批准然后才写入在此停下等待用户回复。本次运行的任何分类结果在显式响应到来之前不得写入approve all / yes 或等价说法 → Proposed Changes 表中每一行都进入 Step 7a部分响应如 approve 1, skip 2 或 just the interview one → 只有指定行进入no / 拒绝 / 不做变更 → 没有行进入直接走 Step 8更新状态。一次回复批准整批是预期的 UX——要求的是回复先发生而不是用户逐行批准。Step 7a写入已批准的更新对每一条用户批准的行Trackerjob_search_tracker.csv按 Step 5 表格更新匹配行的status列向notes追加date gmail-sync: signal (email subject)但先把主题中的每个逗号、双引号和换行符全部删除。为什么这条净化规则如此重要仓库中没有任何写入器会产出带引号的 tracker 字段也没有任何读取器会做反引号解析所以一个未转义的逗号会同时让朴素 split 和真正被使用的csv.DictReadertools/rank_state.py 中的读取方式把行劈开——cv_file、cover_letter_file、source各向左错一列换行更糟——直接结束当前行并开启第二行双引号删除则是廉价的保险。主题在此只是给人看的痕迹而下面的第 2 项会把它原样保留在outcome.mdMarkdown 无此约束。这条规则重要到 tests/test_tracker_notes_csv_safe.py 用一整组用例把规则必须写在追加指令所在行与净化后行仍可被csv.DictReader正确解析全部钉死其 docstring 特别指出/gmail-sync是唯一一个把第三方文本拷进 tracker 的写入器也是唯一一个无人值守运行的写入器。绝不重构 CSV、重排行或触碰无关行与/outcome同规则重写只动status、notes以及触发 drafted 规则时的date保留行内其余每个字段无论是否解析确保/applyStep 6b 写入的deadline列、以及未来新增的任何列都不会被一次状态同步清空。如果匹配行仍是drafted同时把date设为邮件日期。雇主回复即证明用户手动提交了却没运行/outcome此时该列中的起草日期是错的。邮件日期是真实提交日期的上界——对 ack 邮件紧、对数周后的拒绝邮件松——这正是 Step 6 展示它并让用户提供真实日期的原因。outcome.md按表格勾选相关阶段复选框在括号中加上日期或更新Status/Date resolved。向## Notes追加一条带日期的条目绝不覆盖既有 Notes 历史YYYY-MM-DD (via /gmail-sync): one-line summary of what the email said. Source: subject from sender, email date.归档目录不存在时如果匹配申请还没有归档目录 /outcome.md创建目录并写入一个最小outcome.md格式严格遵循 documents/README.md 中定义的格式与/outcome会做的一样。这是drafted行的常态/applyStep 6b 写 tracker 行而只有/outcomeStep 3 会创建归档所以目录不存在是合法状态手工加行的情况同理。用户跳过的行原样不动——无 tracker 写入、无outcome.md写入——但它们的消息 ID 仍在 Step 8 被标记为已处理这样同一封邮件不会在每次运行时被反复提议。outcome.md的归档格式来自 documents/README.md/gmail-sync新建时必须完全遵守/setupPath A 才能无特殊分支地解析# Outcome: Company — Role **Status:** in_progress | hired | offer_declined | rejected | no_response | interview_only **Date resolved:** YYYY-MM-DD ## Interview stages reached - [ ] Phone screen - [ ] Technical interview - [ ] Case interview - [ ] Final round - [ ] Offer received ## Notes十、Step 8更新状态幂等性把本次运行处理过的每一条消息 ID——已批准、已跳过、未匹配、或过滤为噪音的——都加入gmail_sync/state.json的processed_message_ids并把last_sync设为今天。这让重跑幂等同一封邮件永远不会产生重复提议、重复 tracker 记录或重复 Notes 条目。processed_message_ids与last_sync构成了这个无人值守命令的完整记忆前者避免重复提议后者提供下一次运行的默认回溯起点。十一、Step 9陈旧检查对本次运行没有匹配到任何活动的开放申请检查 tracker 的date列与它们outcome.md中最近的带日期 Notes 条目若其中较新的那个距今30 天以上在收尾摘要中标记为 needs follow-up。这仅仅是提示——绝不为陈旧写任何东西。一个必须跳过的子集drafted行——什么都没发出所以没有人在迟复。这条在 tests/test_tracker_status_vocab.py 的ReaderCases中也有专门断言/gmail-sync Step 9 must skip drafted rows。值得注意的是 30 天这个数字与/outcome的 10 天默认跟进的对照tests/test_outcome_followup.py 的 docstring 与/outcomeStep 2b 都明确记录了这个设计对比——10 天跟进是回复仍有可能时的主动提示30 天陈旧标记是整行被遗忘的只读警报两个数字服务不同时刻因此刻意不同。十二、Step 10呈现收尾摘要确认实际发生的事区别于 Step 6 的提议。规范模板## Gmail Sync - Done - YYYY-MM-DD ### Written | Company | Role | Signal | Tracker Status | Source Email | |---|---|---|---|---| | ... | ... | Interview invite | applied - interview | Subject line, 2026-07-10 | ### Skipped (not written) - **Company** - signal declined by user. ### Offers Requiring Your Decision - **Company** - offer written 2026-07-12 (subject). Tracker set to offer; run /outcome company to record accept/decline once you decide. ### Stale Applications (30 days, no activity) - **Company** - last activity YYYY-MM-DD, still status.若本次运行什么都没提议用一句简短说明代替空摘要即可。校准交接若本次运行让outcome.md状态为 final 的申请数达到3 个以上或解决了共享同一模式的第二个申请建议与/outcome相同的/setupPath A 校准交接——不重复其逻辑只把用户指过去。十三、重要规则汇总命令规范以 9 条规则收束这是整个命令的安全底线逐条列出如下这也是被配套测试守护的核心不变量从完整邮件正文分类绝不只凭 snippet。一条改变状态的提议必须真的通过get_thread/get_message拉取并阅读过该消息。在用户批准 Step 6 批次之前不写入任何东西。一次回复批准全部是合理的 UX先写后标记则不行。绝不提议hired或offer_declined。那需要用户的现实决策/gmail-sync停在提议offer并标记之。对已终结或已写入状态相冲突的信号是人工复核标记不是提议的覆盖。拿不准就不提议浮出水面。outcome.md的 Notes 只追加同/outcome。绝不重写或删除既有历史。按消息 ID 幂等。重跑绝不为同一封邮件重复提议、重复 tracker 记录或 Notes 条目。绝不捏造匹配。若无法从邮件中置信地识别公司就进 Unmatched而不是猜。对 Gmail 本身只读。本命令只读和分类不打标签、不归档、不删除邮箱中的任何东西。所有状态都是个人数据。gmail_sync/state.json、job_search_tracker.csv、documents/applications/**均被 gitignore——绝不建议提交它们。第 9 条与仓库的安全防护一致tools/security_guards.py及 tests/test_security_guards.py 中的GitignoreGuardTests要求这些个人数据规则必须存在且禁止通过否定规则negation把它们重新纳入版本控制。十四、测试如何守护这套不变量规范即实现是这个仓库的鲜明风格——/gmail-sync的行为规范就是它的实现因此配套测试直接对 .claude/commands/gmail-sync.md 的文本做断言把最容易在编辑中悄悄漂移的关键语义钉死测试文件守护的不变量tests/test_gmail_sync_command.py查询必须显式含-in:sent -in:drafts必须不含in:inbox否则静默丢失已归档、被标签过滤命中的邮件tests/test_tracker_notes_csv_safe.py净化规则去逗号、双引号、换行必须写在 Step 7a 的notes追加指令所在行净化后的行经csv.DictReader解析列不漂移、行不劈裂同法守护/outcomeStep 4tests/test_tracker_status_vocab.pyStep 2 必须引用/outcome的词汇表块而非本地重申 Final 集合Step 9 必须跳过drafted行tests/test_outcome_followup.py10 天跟进提示与 30 天陈旧标记的刻意对比必须保留在规范中此外tracker 的实际读取端 tools/rank_state.py 用csv.DictReader解析行tracker_pairs函数这正是 Step 7a 净化规则要防的读者——测试 tests/test_tracker_notes_csv_safe.py 的 docstring 明确点名了它。十五、实战编排建议把/gmail-sync放进完整管线时有几个从命令规范与相关命令交叉得到的推荐节奏提交后立即用/gmail-sync companydrafted→applied的 ack 确认是它的独有能力——它补上了用户手动提交但忘了记录这个漏洞且date上界语义让日期可被用户纠正。Offer 阶段交给/outcome收尾/gmail-sync停在offer并显著标记接受或拒绝由你决定后运行/outcome company记录hired/offer_declined。冲突与未匹配交给/outcomeNeeds Manual Review 与 Unmatched 两个分区天然指向手动解决绝不猜测。每轮结束后保持幂等Step 8 的processed_message_ids保证你随时可以重跑同一命令同一封邮件不会被二次提议。结合陈旧检查规划跟进30 天无活动 → 运行/outcome followup进入 10 天阈值的主动跟进分支该分支只起草、绝不代发且最多两次跟进。这套邮件分类提议 → 整批人工批准 → 幂等落盘 → 只读邮箱的设计把 AI 求职框架中最容易出错、最怕污染的一环把第三方文本写入永久记录变成了可审计、可纠正、可重跑的安全流程——这正是/gmail-sync之于 ai-job-search 的核心价值。赞分享AI 应用AI 技能【免费下载链接】ai-job-searchThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.项目地址https://gitcode.com/GitHub_Trending/ai/ai-job-search点击查看免费下载相关推荐BiSheng 前端 Client 工程化规范实战终端用户 Chat UI 的技术栈、硬性规则与品牌主题体系BiSheng 前端 Client 工程化规范实战终端用户 Chat UI 的技术栈、硬性规则与品牌主题体系 导读 BiSheng毕昇是一个开源的企业级AI 应用AI 技能ai-job-search 的 /apply 命令深度解析Drafter-Reviewer 双 Agent 求职申请工作流ai job search 的 /apply 命令深度解析Drafter Reviewer 双 Agent 求职申请工作流 导读 /apply 是 ai joAI 应用AI 技能ai-job-search基于 Claude Code 的 AI 求职申请框架全解析——从 /setup 画像到 /apply 投递的完整工作流ai job search基于 Claude Code 的 AI 求职申请框架全解析——从 /setup 画像到 /apply 投递的完整工作流 本文以 REAI 应用AI 技能上一篇Deep-Live-Cam终极指南3步实现实时人脸替换的完整教程下一篇DataSphereStudio 数据应用开发平台从零到企业级部署终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表