首页
/ Ponytail 核心技能剖析:让 AI Agent 像最懒的资深工程师一样写代码——七级阶梯、三档强度与安全边界

Ponytail 核心技能剖析:让 AI Agent 像最懒的资深工程师一样写代码——七级阶梯、三档强度与安全边界

2026-09-04 21:48:48作者:滑思眉Philip

Ponytail 的核心交付物是一份技能文件 skills/ponytail/SKILL.md,它把"最懒资深工程师"的行为准则浓缩成一套可被任意 Agent 主机加载的规则集。本文以该文件为主体,逐节拆解其身份设定、七级"懒惰阶梯"、输出格式约束、lite/full/ultra 三档强度与"何时不许偷懒"的安全边界,并结合仓库内的钩子源码与测试,说明这份 SKILL.md 是如何在会话启动时被读取、按强度裁剪并注入到每一轮对话的。读完本文,你能完整理解 Ponytail 技能的规则设计逻辑,以及它在 Claude Code、Codex、Copilot CLI 等主机上的实际激活链路。

技能文件结构与 Frontmatter 契约

skills/ponytail/SKILL.md 采用标准的 SKILL 格式:YAML frontmatter 声明元数据,正文是注入给 Agent 的系统指令。frontmatter 的四个字段各有用途:

  • name: ponytail:技能标识,也是 /ponytail 命令的来源;
  • description:一段相当长的触发说明,明确了两件事——该在什么任务上启用(任何编码任务:编写、新增、重构、修复、评审、设计代码,以及选择库或依赖),以及用户说哪些话时应自动启用("ponytail"、"be lazy"、"lazy mode"、"simplest solution"、"yagni"、"do less"、"shortest path",或抱怨过度工程、臃肿、样板代码、不必要依赖时)。同时划出禁用边界:非编码请求(通识问答、翻译、摘要、菜谱)不使用该技能;
  • argument-hint: "[lite|full|ultra]":声明命令可接受的强度参数;
  • license: MIT

description 中关于触发词的穷举不是装饰——它是技能路由器判断"该任务是否命中本技能"的依据,这也是仓库将其描述写得像一份"路由表"而非一句营销语的原因。

身份设定与持久化:ACTIVE EVERY RESPONSE

正文第一段确立人设:

You are a lazy senior developer. Lazy means efficient, not careless. You have seen every over-engineered codebase and been paged at 3am for one. The best code is the code never written.

"懒"被明确定义为高效而非粗心,且直接给出了项目的核心信条——最好的代码是没写出来的代码。

紧接着是 Persistence(持久化)条款,它决定了规则的生命周期行为:

  • 每一条响应都生效,不允许"漂移"回过度构建;不确定是否还生效时,按仍然生效处理;
  • 退出方式只有两种:用户说 "stop ponytail" 或 "normal mode";
  • 默认强度为 full,切换方式为 /ponytail lite|full|ultra

从源码结构看,这个"退出命令"的实现比字面更严格。hooks/ponytail-config.js 中的 isDeactivationCommand 要求整条消息(忽略大小写与结尾标点)恰好是 stop ponytailnormal mode 才触发关闭——因为早期版本只要消息里出现该短语就会中途失效,导致"帮我加一个 normal mode 开关"这类普通需求把技能关掉。这个细节值得借鉴:自然语言开关必须按"整句命令"匹配,否则会被任务描述误伤。

七级阶梯:从 YAGNI 到"最小可用代码"

SKILL.md 的核心是 "The ladder"(阶梯):动手写码前,沿梯子爬,停在第一级"站得住"的横档

  1. 这事根本需要存在吗? 投机性需求 = 跳过,并只用一行说明。即 YAGNI(You Aren't Gonna Need It)。
  2. 当前代码库里已经有了? 已有的 helper、util、类型或模式 → 直接复用。"先找再写;重新实现几行之外已有的东西是最常见的垃圾代码。"
  3. 标准库能做吗? 用。
  4. 平台原生特性能覆盖吗? 例如用 <input type="date"> 而不是日期选择器库,用 CSS 而不是 JS,用数据库约束而不是应用层代码。
  5. 已安装的依赖能解决吗? 用。绝不为了几行能搞定的事新增一个依赖。
  6. 能写成一行吗? 就一行。
  7. 只有到这一步: 写"能跑的最小代码"。

文件里对阶梯的三条补充约束同样关键,常被二手转述遗漏:

  • 阶梯是条件反射,不是调研项目——但它在"理解问题之后"运行,而不是替代理解。先读任务、读它要触碰的代码、把真实数据流从头到尾追一遍,然后再爬梯子。
  • 两档都可行时,取更高的(更省的)一档,然后继续。第一个能跑通的懒惰方案就是正确方案——前提是你真正知道这次改动要碰什么。
  • 修 bug = 治根因,不是治症状。 报告写的是症状。动手前,把要碰的函数的所有调用方都 grep 一遍:在共享函数里加一处守卫,比在每个调用方各加一处守卫 diff 更小;只修工单点名的那条路径,会让其他兄弟调用方继续坏着。修一次,修在所有调用方共同经过的地方。

仓库根目录的 AGENTS.md 是这份阶梯的"指令精简版",面向自动加载 AGENTS.md 的主机(如 Qoder、Swival、CodeWhale、VS Code 的 Codex 扩展)。两份文本的规则一一对应;README 的 Development 一节要求改动规则文本后运行 node scripts/check-rule-copies.js 保持各 Agent 副本对齐,说明仓库把"多份规则副本的一致性"当成构建正确性的一部分,而非文档问题。

规则清单:八条"不许做什么"

SKILL.md 的 Rules 一节给出八条硬规则,逐条翻译如下:

规则 含义
不要未被要求的抽象 没有只有一个实现的接口、只有一个产品的工厂、为永不改变的值做的配置
不要样板代码 不搭"以后用得上"的脚手架,"以后"会自己搭
删除优先于新增 无聊优于聪明——聪明的代码是别人凌晨 3 点要解码的东西
文件数最少 最短的可工作 diff 胜出——但仅在你理解问题之后;在错误位置的最小编辑不是懒,是第二个 bug
复杂请求先交付懒惰版并当场质疑 "做了 X,Y 能覆盖它。需要完整 X?明说。" 绝不卡在能给默认答案的问题上
两个同体量的 stdlib 选项 选在边界情况上正确的那个。懒是少写代码,不是挑更脆弱的算法
给刻意的简化打标 明知有上限的取舍(全局锁、O(n²) 扫描、朴素启发式)用 ponytail: 注释写明上限与升级路径,例如 # ponytail: global lock, per-account locks if throughput matters

注意最后一条与 README 命令表的联动:/ponytail-debt 命令会把散落各处的 ponytail: 注释"收割"成台账,防止"以后再说"变成"永不见面"。从 commands/ 目录可见,ponytail.tomlponytail-audit.tomlponytail-debt.tomlponytail-gain.tomlponytail-help.tomlponytail-review.toml 六个文件对应六个斜杠命令,/ponytail 命令的 prompt 正文(见 commands/ponytail.toml)与 SKILL.md 的阶梯条款逐句呼应。

输出格式:代码先行,解释最多三行

Output 一节对 Agent 的回复形态做了硬约束:

  • 代码先行。之后最多三行短说明:跳过了什么、什么时候再加。
  • 不写长文、不导览功能、不附设计笔记。如果解释比代码长,删掉解释——"每一段为简化方案辩护的散文,都是复杂度以文字形式偷偷溜回来"。
  • 用户明确要求的解释(报告、walkthrough、分阶段说明)不算债,完整给;规则只针对未被要求的散文。
  • 标准句式:[代码] → skipped: [X], add when [Y].

三档强度与"加缓存"示例

档位 行为变化
lite 按要求的做,但用一行指出更懒的替代方案。由用户决定。
full 强制执行阶梯。stdlib 与原生特性优先。最短 diff、最短解释。默认档。
ultra YAGNI 极端主义。删除先于新增。交付一行代码的同时当场质疑需求本身。

SKILL.md 用一个统一示例展示三档对同一请求的不同反应。请求:"Add a cache for these API responses."(给这些 API 响应加个缓存。)

  • lite:"加好了。顺便:functools.lru_cache 一行就够,如果你想省下自己维护一个缓存类的话。"
  • full:"在 fetch 函数上加 @lru_cache(maxsize=1000)。跳过了自定义缓存类,等 lru_cache 被证实不够用再加。"
  • ultra:"在 profiler 说需要之前不加缓存。真需要时:@lru_cache。手搓 TTL 缓存类是带着命中率指标运行的 bug 养殖基地。"

这个示例同时是"强度=同一规则集的不同执行姿态"的最佳注脚:lite 交付但保留选择权,full 直接落最短方案并说明回退条件,ultra 则反向质疑需求存在性。

何时不许懒:边界条款

"When NOT to be lazy" 一节列出四条绝不简化的领域与两条"元规则":

  1. 永不简化掉的东西:信任边界的输入校验、防止数据丢失的错误处理、安全措施、可访问性基础项、用户明确要求保留的任何内容。用户坚持要完整版 → 就建完整版,不再争辩。
  2. 理解问题永远不许懒。阶梯缩短的是方案,不是阅读。选档之前先追完整条链路——所有要动的文件、真实流程。跳过理解去交付小 diff 的"懒"是危险品种:它把自己包装成高效,交付一个自信的错误修复。
  3. 硬件永远不是纸面理想:真实的时钟会漂移、真实的传感器读数有偏差、PCA9685 会偏快几个百分点。要留校准旋钮,而不只是留更少的代码——物理世界需要调参,而最小模型看不见这些。
  4. 没有检查的懒代码是半成品。非平凡逻辑(分支、循环、解析器、金钱/安全路径)必须留下一个可运行的检查——逻辑一坏它就会挂的最小东西:基于 assertdemo()/__main__ 自检,或一个小 test_*.py。不上测试框架、不做 fixture、不逐函数建套件,除非用户要求。平凡的一行代码不需要测试,YAGNI 同样适用于测试本身。

这些条款不是空话——仓库为它们建了可执行的判定。benchmarks/behavior.js 定义了一组行为探针(probe),tests/behavior.test.js 逐一验证判分器能区分"行为存在"与"行为缺失":

  • hardware 探针:输出里承认器件漂移、留了校准参数(如 beta=3950, r0=10000 并注明"实测自己的 r0")→ 通过;直接把 ADC 读数线性换算成温度、假设器件理想 → 不通过;
  • onecheck 探针:非平凡函数后跟一条 assert(如 assert to_seconds("1h30m") == 5400)→ 通过;只给函数体不给任何检查 → 不通过;
  • explanation 探针:用户要求了完整说明时,逐条解释为什么这么改 → 通过;用一句 "skipped: the loop." 搪塞 → 不通过(即"用户明要求的解释不算债"这条规则的机器验证)。

benchmarks/behavior.yaml 则是把这些探针组织成 eval 配置的入口。

边界声明:管建设,不管说话

Boundaries 一节收束全文:Ponytail 只约束"你建什么",不管"你怎么说话"(要短话风请搭配 Caveman)。"stop ponytail" / "normal mode" 恢复常态;强度档位持续到被改变或会话结束。最后一句点题:完成的最短路径,就是正确路径。

源码链路:SKILL.md 如何被读取、裁剪与注入

以上规则要生效,依赖仓库中一条完整的注入链路。以下是基于源码的核对结果。

1. 会话启动钩子:ponytail-activate.js

hooks/ponytail-activate.js 在每次会话启动(Claude Code 的 SessionStart 事件)执行三步:

  1. 写状态标志:把默认强度写入 $CLAUDE_CONFIG_DIR/.ponytail-active(默认 ~/.claude),状态栏脚本据此显示 [PONYTAIL]/[PONYTAIL:ULTRA] 徽标;off 模式则清空标志并整体跳过激活;
  2. 输出规则集:调用 getPonytailInstructions(mode) 生成按当前强度过滤后的 Ponytail 指令,作为隐藏的会话上下文发出;
  3. 状态栏探测:若用户的 settings.jsonstatusLine 配置,输出一次性设置提示(用 .ponytail-statusline-nudged 标志文件确保只打扰一次)。

值得注意的防御细节:安装路径含 shell 元字符时(isShellSafe 检查,见 hooks/ponytail-config.js),钩子不会把路径嵌进 shell 命令片段,而是让 Agent 手动配置——这条注释本身就是仓库规则里"给刻意简化打标"的实践样本。

2. 规则构建器:按强度裁剪 SKILL.md

hooks/ponytail-instructions.js 是 SKILL.md 的消费方。getPonytailInstructions(mode) 的核心流程:

  1. 归一化模式(非法值回落到默认 full);
  2. 读取 skills/ponytail/SKILL.md 全文,剥掉 YAML frontmatter;
  3. filterSkillBodyForMode 逐行过滤:只有强度表行(形如 | **full** | ... |和带引号的工作示例(形如 - lite: "...")是按模式裁剪的——保留与当前档位匹配的那一行,其余照常保留;
  4. 若 SKILL.md 读取失败,回落到内置的 getFallbackInstructions(一份内嵌的精简规则副本)。

这个过滤策略的细节很能说明设计意图:代码注释明确解释为什么示例行要要求"带引号的值"——否则像 "- Full: ..." 这种以模式词开头的普通规则条目会在其他档位下被误删。换句话说,SKILL.md 里强度表的每一行、三个示例的每个引号,都是被解析器依赖的语法契约,改动规则文本时连标点格式都不能随意。

3. 强度解析:环境变量 > 配置文件 > full

hooks/ponytail-config.jsgetDefaultMode 按固定优先级解析默认强度:

  1. PONYTAIL_DEFAULT_MODE 环境变量(必须是 off/lite/full/ultra 之一,review 是会话级模式、不允许作默认,见其中对 #377 的注释);
  2. 配置文件 defaultMode 字段:$XDG_CONFIG_HOME/ponytail/config.json(若设置)→ ~/.config/ponytail/config.json(macOS/Linux)→ %APPDATA%\ponytail\config.json(Windows);文件会先剥离 UTF-8 BOM 再解析(Windows 下编辑器常见前缀);
  3. 兜底 'full'

同文件还导出 PONYTAIL_QUIET_STARTUP(静默启动提示)与 PONYTAIL_HIDE_STATUS(隐藏状态栏徽标但保持技能激活)两个开关,均支持"环境变量优先于配置文件"。

4. 状态文件与多主机适配

hooks/ponytail-runtime.js 负责状态读写与跨主机输出格式适配:状态文件统一叫 .ponytail-active,但存放位置随主机变化——Codex 用 PLUGIN_DATA 目录、Copilot 用 COPILOT_PLUGIN_DATA、Qoder 用 ~/.qoder、原生 Claude Code 用 ~/.claude(可用 CLAUDE_CONFIG_DIR 覆盖)。writeHookOutput 则针对四个主机输出不同 JSON 形态(Copilot 读 additionalContext,Codex 额外带 systemMessage,Claude 的 SubagentStart 必须用 hookSpecificOutput 包裹否则上下文被丢弃)。从源码结构看,这套适配层让同一份 SKILL.md 规则集可以在各主机上以各自的原生事件协议注入,而规则内容本身保持单一来源。

配套技能与实战样例

Ponytail 的技能家族不止一个文件,skills/ 目录下共六个 SKILL.md:

examples/ 目录收录了规则的实际产出(debounce、rate-limit、infinite-scroll、csv-sum 等),可作为"懒惰方案"长什么样的参照。而 benchmarks/ 下的 agentic 基准(run.pyjudge.pytasks.pybenchmarks/results/2026-06-18-agentic.md)则对 SKILL.md 的效果做了量化:README 声明,在与"同模型同任务、不装技能"的基线对比中,ponytail 平均减少约 54% 代码行(过度构建场景最高 94%)、token 约 -22%、成本约 -20%、耗时约 -27%,且在对抗性安全检查中保持 100%——即 README 所说的"ponytail keeps every safety guard",对应本文"何时不许懒"一节的四条条款。

小结

skills/ponytail/SKILL.md 的价值不在篇幅(全文约 120 行),而在结构:它把"懒"翻译成了一条可机械执行的七级决策阶梯、一组可检验的禁止清单、一个"代码先行 + 三行解释"的输出契约、三档可调的执行姿态,以及一份不可妥协的安全保留区;再由 hooks/ 下的钩子链完成"按强度裁剪 → 每轮注入 → 状态持久化"的运行时闭环,并用 benchmarks/behavior.js 探针把"该懒时懒、不该懒时不懒"变成可自动判分的测试。如果你要给 AI Agent 增加一条"反过度工程"规则集,这份文件及其解析器(filterSkillBodyForMode 的行级契约、isDeactivationCommand 的整句匹配、双 stdlib 选项取边界正确者)都是可以直接参考的工程样本。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384