career-ops SKILL.md 路由器详解:一个路由文件如何驱动多 CLI 下的 30+ 求职模式
本文以 career-ops 仓库中 .cursor/skills/career-ops/SKILL.md 为核心对象,拆解这个"路由器"文件的工作机制:它如何解析项目根目录、如何把用户输入映射到 30 余种执行模式、如何注入输出语言指令、以及如何按模式分级加载上下文文件并委派子代理。读完后,你能理解一个 Agent 技能路由器的完整设计范式,并知道如何在自己的多模式 CLI 项目中复刻类似的路由与上下文加载策略。
路由器的定位与文件结构
career-ops 是一个开源的 AI 求职命令行工作系统:扫描招聘门户、把职位评估为带 A-H 报告块和 1-5 全局分数的结构化结果、定制简历、跟踪申请状态,并本地运行在各种 AI 编码 CLI(Claude Code、Codex、OpenCode、Antigravity 等)中。在这个体系里,SKILL.md 承担"总路由"的角色——文件开头自述为 # career-ops -- Router,并强调"以下路由在各受支持的 Agent CLI 之间共享,即使各 CLI 的调用界面不同"。
文件由两部分构成:一段 YAML frontmatter(技能元数据)+ 一段自然语言路由规则(Agent 遵循的行为指令)。frontmatter 定义了技能的身份与触发方式:
---
name: career-ops
description: >-
AI job search command center -- evaluate offers, generate CVs, scan portals,
track applications. Use when the user pastes a job URL or JD, asks to scan
portals, generate a CV/PDF, track applications, prepare for interviews, draft
outreach/emails, or run any career-ops mode.
arguments: mode
user_invocable: true
user-invocable: true
argument-hint: "[scan | discover | deep | pdf | text | latex | latex-tex | cover | email | add | expand | ... | update]"
license: MIT
---
几个字段各有分工:name 是技能注册名;description 同时服务于人读(说明用途)和 Agent 匹配(列出了触发条件:粘贴职位 URL/JD、要求扫描门户、生成 CV/PDF、跟踪申请、准备面试等);arguments: mode 声明该技能接受一个位置参数 $mode;user_invocable: true 允许用户直接调用;argument-hint 列出全部可选模式名,作为调用时的补全提示。值得注意的是 description 里那句 "Use when..." 的写法——这是让 LLM 在自然语言场景下也能正确命中该技能的关键设计。
PROJECT_ROOT 解析:嵌套检出的路径安全
路由规则的第一节就解决了一个容易被忽视的工程问题:技能文件里的所有仓库相对路径(modes/、config/、data/、脚本与输出路径)应该相对于什么解析?SKILL.md 给出的答案是从技能文件自身向上推导,而不是依赖进程当前工作目录:
从加载的
SKILL.md所在目录开始,向上逐层查找,直到找到同时包含AGENTS.md和modes/这两个哨兵(sentinel)的最近目录,该目录即为PROJECT_ROOT。本路由中解析的所有路径(modes/、config/、data/、脚本、模板、输出路径)必须相对于PROJECT_ROOT,绝不相对于进程当前工作目录。即使用户检出目录本身是嵌套的(例如Development\career-ops),或者命令从某个子目录发起,这条规则也成立。如果找不到这两个哨兵,停止操作,先定位 career-ops 检出目录,再读写文件。
这条规则有三个要点值得学习:
- 双哨兵判定:单个文件(如
AGENTS.md)可能在其他项目里也存在,用"同时存在AGENTS.md和modes/"两个标志共同判定项目根,降低了误判概率。仓库根目录确实同时含有这两个标志,modes/目录下有 50 余个模式文件(oferta.md、scan.md、auto-pipeline.md等),与规则描述一致。 - 显式拒绝 CWD 依赖:很多 CLI 会话的工作目录不可控(用户在子目录启动、检出目录嵌套),以 CWD 解析相对路径是典型的失败来源,这里被明确禁止。
- 失败即停:找不到哨兵时不猜测、不继续,而是要求先定位检出目录——这是一种防数据写错位置的保守策略,对"会写文件"的技能尤为重要,因为 career-ops 的模式会写入
data/applications.md、reports/等用户层文件。
多 CLI 调用面:同一套路由语义
SKILL.md 的 "Invocation Notes" 一节规定了路由在不同调用面上的映射关系:
- 支持斜杠命令注册的 CLI 可以把该路由器暴露为
/career-ops; - 在 Cursor 中,技能位于
.cursor/skills/career-ops/,被自动发现;用户可以用模式名请求,或直接粘贴 JD/URL 触发 auto-pipeline; - 交互式 Codex 会话在仓库根运行
codex。由于 Codex 不保证支持斜杠命令,如果/career-ops不可用,就请 Codex 按名称运行同一模式; - 无头 Codex worker 使用
codex exec "prompt"; - 无论入口是斜杠命令还是自然语言提示,下方路由语义保持不变。
文档给出了 Codex 提示词与路由语义的等价示例:
Evaluate this JD with career-ops auto-pipeline: https://company.com/jobs/123
Run the career-ops scan mode and summarize new matches.
Run the career-ops pipeline mode for data/pipeline.md.
Run the career-ops pdf mode for the latest evaluated role.
Run the career-ops tracker mode and summarize the current statuses.
这与仓库中 docs/SUPPORTED_CLIS.md 的 CLI 清单互相印证:Claude Code 走 CLAUDE.md 入口、交互式 claude 后用 /career-ops、无头 claude -p "prompt";OpenCode 走 OPENCODE.md;Antigravity CLI、Grok Build CLI、Qwen 都走 AGENTS.md 入口;Codex 走 CODEX.md 入口、无头 codex exec "prompt";Kimi 走 KIMI.md;GitHub Copilot CLI 仅无头 copilot -p "prompt";Gemini 的 GEMINI.md 是重定向到 AGENTS.md 的遗留包装。从源码结构看,"核心逻辑共享于 AGENTS.md,CLI 特有细节由各入口包装文件处理"这一分层,正是 SKILL.md 敢于宣称"路由语义不变"的前提——路由器只依赖共享的 modes/ 文件,不依赖任何特定 CLI 的界面。
模式路由表:$mode 到模式文件的映射
路由器核心是一张映射表,Agent 根据 $mode 参数确定要执行的模式:
| 输入 | 模式 |
|---|---|
| (空 / 无参数) | discovery — 显示命令菜单 |
| JD 文本或 URL(无子命令) | auto-pipeline |
oferta |
oferta |
ofertas |
ofertas |
contacto |
contacto |
deep |
deep |
interview-prep |
interview-prep |
interview |
interview |
eu-swe |
regional/eu-swe |
eu-fintech |
regional/eu-fintech |
interview/plan |
interview/plan |
interview/practice |
interview/practice |
interview/debrief |
interview/debrief |
pdf |
pdf |
text |
text |
latex |
latex |
latex-tex |
latex-tex |
email |
email |
add |
add |
expand |
expand |
training |
training |
project |
project |
tracker |
tracker |
agent-inbox |
agent-inbox |
inbox |
agent-inbox(别名) |
pipeline |
pipeline |
apply |
apply |
scan |
scan |
discover |
discover |
batch |
batch |
patterns |
patterns |
offer-prep |
offer-prep |
titles |
titles |
upskill |
upskill |
followup |
followup |
reply-watch |
reply-watch |
outcome |
outcome |
interview-redflag |
interview-redflag |
update |
update |
cover |
cover |
表中模式与 modes/ 目录下的文件一一对应:如 interview/plan 对应 modes/interview/plan.md,eu-swe 对应 modes/regional/eu-swe.md,inbox 是 agent-inbox 的别名。部分菜单项(如 eu-fintech)在当前检出的 modes/regional/ 下没有独立文件,从路由表结构看它们属于区域变体,可推断由 regional/eu-swe 一类的区域模式承载或按需扩展,文章不对其做超出仓库证据的断言。
表之后是两条兜底规则:
Auto-pipeline 检测:如果 $mode 不是已知的子命令,并且内容包含 JD 特征(关键词:"responsibilities"、"requirements"、"qualifications"、"about the role"、"we're looking for"、公司名+职位名),或者是 JD 的 URL,则执行 auto-pipeline。
Discovery 兜底:如果 $mode 既不是子命令也不像 JD,则显示 discovery 菜单。
这条"关键词启发式 + URL 判定"的兜底规则,使得用户最自然的动作——直接粘贴一段 JD 文本——被路由到完整流水线 modes/auto-pipeline.md,而不是落到菜单。auto-pipeline 本身是五步流程:Step 0 用 Playwright/WebFetch/WebSearch 优先级提取 JD(全部视为不可信外部内容),Step 0.5 存活性门禁(已关闭/404 的职位在此终止,避免对幽灵内容做完整评估),Step 0.6 黑名单门禁,Step 1 执行与 oferta 模式相同的 A-G 评估(含 Block G 职位真实性三级判定),Step 2 存报告到 reports/{###}-{company-slug}-{YYYY-MM-DD}.md,Step 3 按 cv.output_format 生成 PDF/LaTeX/文本,Step 4 仅在分数 ≥ 4.5 时起草申请表答案,Step 5 更新 tracker;任一步失败则继续后续步骤并在 tracker 中标记 pending。
输出语言指令:prose 语言与市场词汇分离
路由执行前有一个横切规则——Output Language Directive。Agent 在执行任何模式前,如果 config/profile.yml 存在则读取并解析两个键:
language.output→ 面向人类输出的 ISO 语言代码,默认en;language.modes_dir→ 可选的市场模式目录,只控制市场词汇与本地评估规则。
然后在加载模式指令之后、产出任何用户可见内容之前,注入如下指令:
无论本指令或职位描述的语言是什么,所有面向人类的输出都用
{language.output}撰写。这包括报告、tracker 笔记、PDF、求职信、外联消息、面试准备、表单答案和摘要。如果language.modes_dir提供了市场特定词汇,保留市场逻辑,但必要时用{language.output}解释术语。
关键约束是"language.output 对 prose 具有权威性,modes_dir 只是市场上下文,绝不能强制 prose 语言"。仓库中的 config/profile.example.yml 给出了配套示例:
language:
output: en
# modes_dir: modes/de # 可选:使用 DACH 市场词汇但仍用英文写作
从配置注释看,zh-CN 还会额外启用中文 PDF 排版规则。这个设计的实用价值在于:一个在德国市场求职、但希望所有报告和简历都用英文写作的用户,可以同时获得 DACH 市场的词汇与评估规则(modes/de/ 目录确实存在)而不改变输出语言。
Discovery 菜单:36 个入口的完整清单
无参数调用时,路由器展示如下命令菜单(原文完整保留,作为该系统的"功能总目录"):
career-ops -- Command Center
Available commands:
/career-ops {JD} → AUTO-PIPELINE: evaluate + report + PDF + tracker (paste text or URL)
/career-ops pipeline → Process pending URLs from inbox (data/pipeline.md)
/career-ops oferta → Evaluation only A-F (no auto PDF)
/career-ops ofertas → Compare and rank multiple offers
/career-ops contacto → LinkedIn power move: find contacts + draft message
/career-ops deep → Deep research prompt about company
/career-ops interview-prep → Generate company-specific interview prep doc
/career-ops interview → Interactive profile/CV onboarding interview
/career-ops eu-swe → Calibrate a European SWE application before CV/apply/interview
/career-ops eu-fintech → Scan 21 EU fintech portals for Product Manager roles (zero-token)
/career-ops interview/plan → Time-blocked prep plan for an upcoming interview
/career-ops interview/practice → Practice interview, one question at a time with feedback
/career-ops interview/debrief → Post-interview debrief: close gaps, predict next round
/career-ops pdf → PDF only, ATS-optimized CV
/career-ops text → Tailored markdown CV (mirrors cv.md, no PDF)
/career-ops latex → Export CV as LaTeX/Overleaf .tex
/career-ops latex-tex → Tailor your own resume.tex in place (opt-in; cv.md stays default)
/career-ops cover → Cover letter: standalone JD paste or /career-ops cover {slug}
/career-ops email → Formal application email draft (draft-only; never sends, submits, or clicks)
/career-ops add → Add a project/paper/role to your CV (fetch + preview + confirm)
/career-ops expand → Auto-discover and add missing competencies from profile links
/career-ops training → Evaluate course/cert against North Star
/career-ops project → Evaluate portfolio project idea
/career-ops tracker → Application status overview
/career-ops agent-inbox → Queue/drain requests for the next session (data/agent-inbox.md)
/career-ops apply → Live application assistant (reads form + generates answers)
/career-ops scan → Scan portals and discover new offers
/career-ops discover → Resolve a company list to scannable ATS boards + append to portals.yml (zero-token)
/career-ops batch → Batch processing with parallel workers
/career-ops patterns → Analyze rejection patterns and improve targeting
/career-ops offer-prep → Read a received offer/contract with the candidate: clause walk + lawyer questions (not legal advice)
/career-ops titles → Suggest adjacent job titles from your CV to broaden the search
/career-ops upskill → Aggregate skill-gap analysis from your evaluated reports
/career-ops followup → Follow-up cadence tracker: flag overdue, generate drafts
/career-ops outcome → Record application outcome & archive artifacts
/career-ops update → Update career-ops system files with diff preview + compat check
Inbox: add URLs to data/pipeline.md → /career-ops pipeline
Or paste a JD directly to run the full pipeline.
菜单末尾的 Inbox 提示揭示了另一条常用工作流:把 URL 追加到 data/pipeline.md,再用 /career-ops pipeline 批量处理。对 Codex 这类无斜杠命令的会话,SKILL.md 还给出了一组"斜杠命令 ↔ 自然语言提示词"的等价映射,例如 /career-ops scan ↔ "Run the career-ops scan mode and summarize new matches.",保证同一套语义在两种调用面上都能落地。
上下文加载:三个级别的文件组合
确定模式后,路由器的最后一节规定"执行前先加载必要文件",按模式分成三个加载级别:
级别一:_shared.md + 模式文件(重评估类模式)。读取顺序为 modes/_shared.md + modes/_profile.md(若存在)+ modes/_custom.md(若存在)+ modes/{mode}.md。适用模式:auto-pipeline、oferta、ofertas、pdf、text、contacto、apply、pipeline、scan、batch。这些正是需要评分、事实门控、报告格式的评估型模式。
级别二:独立模式 + profile/custom 上下文。读取 modes/_profile.md(若存在)+ modes/_custom.md(若存在)+ modes/{mode}.md,不需要 _shared.md。适用模式:tracker、agent-inbox、deep、interview-prep、interview、regional/eu-swe、interview/plan、interview/practice、interview/debrief、latex、latex-tex、training、project、patterns、titles、upskill、followup、reply-watch、outcome、cover、email、add、offer-prep、discover。
级别三:委派子代理的模式。scan、apply(带 Playwright)、pipeline(3 个以上 URL)以 worker/subagent 方式启动,把 _shared.md + _profile.md + _custom.md + modes/{mode}.md 的内容注入 worker 提示。SKILL.md 给出了标准调用形状:
Agent(
subagent_type="general-purpose",
prompt="[output language directive]\n\n[content of modes/_shared.md]\n\n[content of modes/_profile.md if exists]\n\n[content of modes/_custom.md if exists]\n\n[content of modes/{mode}.md]\n\n[invocation-specific data]",
description="career-ops {mode}"
)
注意提示词的拼装顺序:输出语言指令在最前,然后依次是共享系统规则、用户画像、用户家规、模式指令,最后才是本次调用的具体数据——这个顺序与级别一/二的文件读取顺序一致,保证子代理看到的主从关系和主会话相同。
三个上下文文件的职责边界在 modes/_shared.md 中有明确定义,这是理解级别一/二差异的钥匙:
modes/_shared.md(系统层,可自动更新):文件头注释声明"THIS FILE IS AUTO-UPDATABLE",存放系统规则、评分逻辑与工具配置,"随每个 career-ops 发布改进"。它定义了唯一事实来源表(cv.md、article-digest.md、config/profile.yml、_profile.md等)、Data Root 解析顺序(环境变量CAREER_OPS_ROOT/CAREER_OPS_DATA_DIR→.career-ops-data标记文件 → 仓库根)、spend_tier 到具体模型的映射表、五维评分体系与全局 1-5 分解释(4.5+ 立即申请、4.0-4.4 值得申请、3.5-3.9 视情况、低于 3.5 不建议申请)、Block G 职位真实性三档判定、公司类型与薪酬可信度分级、六大角色原型检测(AI Platform/LLMOps、Agentic/Automation、Technical AI PM、AI Solutions Architect、AI Forward Deployed、AI Transformation)、NEVER/ALWAYS 全局规则与工具表。modes/_profile.md(用户层,永不自动更新):用户画像、叙事、谈判立场,用户自定义覆盖_shared.md的默认值。仓库提供 modes/_profile.template.md 作为起步模板。modes/_custom.md(用户家规):SKILL.md 特别说明——如果存在,在_profile.md之后、所选模式文件之前读取;它可以覆盖工作流/风格默认值,但"绝不添加关于候选人的事实声明"。仓库同样提供 modes/_custom.template.md。
这套分层的成本工程意图在 _shared.md 的"Subagent delegation (cost guardrail)"一节得到佐证:career-ops 派生的任何子代理都是"单程 worker"——不得再派生子代理、不得调用其他技能(尤其是开放式研究技能),因为"嵌套 Agent 会在一次运行中烧掉数千万 token";公司与薪酬研究必须 inline 完成,限定在模式点名的少量 WebSearch/WebFetch 查询内。这与 AGENTS.md 的 Headless/Batch 一节呼应:并行 fan-out 前先用 node reserve-report-num.mjs --count N 预占报告号段,避免并行 worker 各自计算 max+1 造成编号竞争(原文标注为 #749 race)。
从路由到执行:一次调用的完整链路
把以上各节串起来,一次 /career-ops https://company.com/jobs/123 调用的执行链路是:
- 入口:CLI 把该技能注册为
/career-ops(或 Cursor 自动发现该技能),$mode收到 URL; - 根解析:Agent 从
.cursor/skills/career-ops/向上找到同时含AGENTS.md与modes/的目录,确立PROJECT_ROOT; - 路由判定:URL 不是已知子命令但符合 JD URL 特征 → 命中 auto-pipeline 兜底规则;
- 语言指令:读取
config/profile.yml的language.output(缺省en),拼装输出语言指令; - 上下文加载:级别一组合——
_shared.md→_profile.md→_custom.md→auto-pipeline所需模式文件(含 modes/oferta.md 的 A-G 块定义,auto-pipeline 的 Step 1 明确"执行与oferta模式相同的 A-G 评估"); - 执行:五步流水线(提取 JD → 存活/黑名单门禁 → A-G 评估 → 存报告 + 按分数阈值生成 PDF → 更新 tracker),其中评分口径(五维、全局 1-5、4.0 申请线)由
_shared.md统一约束,保证任何 CLI、任何层级模型产出结构一致的报告——_shared.md的 Output parity 规则明确"模型选择不改变 A-H 报告结构、标题或分节"。
复刻要点:多模式 Agent 路由器的设计清单
从 SKILL.md 这个 200 行不到的文件里,可以提炼出可直接迁移的设计要点:
- 路由器与执行体分离:SKILL.md 不含任何业务逻辑,只做"参数解析 + 文件加载顺序 + 委派决策";业务逻辑全部在
modes/*.md。这让新增一个模式(在路由表加一行 + 新增一个模式文件)成为低耦合操作。 - 双哨兵根解析替代 CWD 假设:对一切会读写文件的多目录 Agent 系统,"从技能/入口文件自身向上找哨兵目录"是比"信任当前工作目录"更稳的路径策略,且必须配"找不到就停"的失败行为。
- 启发式兜底优于报错:未知参数不是错误,而是进入两级判定(JD 特征 → auto-pipeline;都不像 → 菜单),把用户最随意的输入映射到最有价值的路径。
- 语言与市场双轨分离:
output决定 prose 语言、modes_dir决定市场词汇,两个正交配置键解决"目标市场语言 ≠ 用户偏好输出语言"的真实场景。 - 上下文加载分级 + 加载顺序即主从关系:系统规则(可自动更新)→ 用户画像(覆盖默认)→ 用户家规(覆盖流程但不添加事实)→ 模式文件,顺序本身就是权限模型;子代理委派时把同样的顺序序列化进 prompt,保证主/子会话行为一致。
- 成本控制写进路由层:哪些模式走子代理、子代理禁止嵌套、并行前预占编号资源——这些运行时纪律在路由文档中前置声明,而不是散落在各模式里。
对于正在用 Claude Code / Codex / OpenCode 等 CLI 构建"一个入口、多个子模式"的 Agent 技能,SKILL.md 提供了一个可完整参照的范本:frontmatter 声明触发面,正文声明根解析、路由表、兜底规则、横切指令(语言)与上下文加载协议,业务则全部外置到可独立演进的模式文件中。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00