ponytail爆火背后:为什么AI Agent写的代码总是太啰嗦?
一个Skill让Claude Code少写54%的代码,GitHub 53K星。开发者苦AI冗余代码久矣。
一个现象
如果你用过AI写代码,应该见过这种输出:
def get_user(user_id: int) -> User | None:
"""
Retrieve a user from the database by their unique identifier.
Args:
user_id: The unique identifier of the user to retrieve.
Returns:
The User object if found, None otherwise.
Raises:
DatabaseError: If there is an error connecting to the database.
"""
try:
session = SessionLocal()
user = session.query(User).filter(User.id == user_id).first()
session.close()
return user
except Exception as e:
logger.error(f"Failed to retrieve user {user_id}: {e}")
raise DatabaseError(f"Database error: {e}")
有经验的开发者一眼就能看出问题:这段代码做了太多不该做的事。
AI生成了什么:
-
docstring写了4行
-
异常处理覆盖了数据库错误
-
手动管理session生命周期
-
日志记录
-
自定义异常包装
实际要什么:
-
一个简单的查询函数
-
项目里其他地方怎么写的,这里就怎么写
多余代码是AI的安全策略——它宁可多写,也不敢漏写。但这就是AI代码和人类代码之间的核心矛盾。
ponytail的技术思路
ponytail是一个Skill文件,安装到Claude Code后自动生效。它的核心是改变AI生成代码时的判断标准。
安装方式:
npm install @dietrichgebert/ponytail
或者手动安装Skill:
curl -o ~/.claude/skills/ponytail.md https://raw.githubusercontent.com/DietrichGebert/ponytail/main/ponytail.md
核心规则
ponytail的规则文件只有不到200行,核心规则可以概括为几条:
不写不需要的代码: 不添加当前任务不需要的依赖、工具函数、配置项。一个函数里不需要的import不写。
不写防御性代码: 如果参数类型已经通过TypeScript或Pydantic校验,函数内部不需要再重复校验。如果数据库层已经处理了异常,上层不需要再包一层try-except。
优先用现有的: 项目里有现成的工具函数、装饰器、中间件,优先复用而不是自己写。检查现有代码的模式,保持一致。
减少注释: 函数名和参数类型能表达清楚逻辑时,不写docstring。不写显而易见的注释。
效果数据
根据ponytail作者在12个任务上的测试(Haiku 4.5,n=4):
| 指标 | 普通Agent | 加ponytail | 变化 |
|---|---|---|---|
| 代码量 | 基准 | -54% | 减少一半以上 |
| token消耗 | 基准 | -20% | 节省费用 |
| 执行时间 | 基准 | -27% | 速度提升 |
| 安全性 | 基准 | 不变 | 不影响安全 |
| 功能正确性 | 基准 | 不变 | 功能不受影响 |
极端情况下(生成日期选择器这类简单组件),代码量减少达到94%。
为什么普通Agent会写多余代码
理解ponytail的效果,需要先理解AI生成冗余代码的原因。
原因1:安全策略
LLM的训练数据中,带完整异常处理、日志记录、参数校验的代码被认为是“好代码”。模型被训练成倾向于多写而不是少写,因为多写的风险低于少写。
从LLM的角度看:生成一段完整但冗余的代码,用户最多说“啰嗦”;生成一段缺少异常处理的代码,用户会说“有Bug”。
原因2:没有项目上下文
Agent不知道当前项目中其他代码是怎么写的。没有参照系,它只能按通用的“最佳实践”来写,而通用的最佳实践往往是冗余的。
同一个项目里,所有CRUD函数都用了同一个装饰器处理异常,但Agent不知道这个约定,所以每个函数都自己写try-catch。
原因3:指令模糊
用户说“写一个用户查询接口”,Agent的理解是“实现一个完整的查询功能”,而不是“按项目现有模式写一个查询接口”。
解决思路:不是少写,是写对的
ponytail的做法不是简单粗暴地让AI“少写代码”,而是改变AI的判断标准。
思路1:用项目模式替代通用模式
普通Agent的生成逻辑:
看到“写一个查询函数” → 调用通用模板(try-catch + 日志 + docstring) → 输出冗余代码
ponytail的生成逻辑:
看到“写一个查询函数” → 检查项目里其他查询函数怎么写 → 发现项目里用了装饰器处理异常 → 按项目模式生成 → 输出精简代码
思路2:优先级排序
ponytail的prompt里给Agent设定了代码生成的优先级:
-
功能正确 — 代码必须能工作
-
一致性 — 和项目现有代码风格一致
-
必要性 — 每行代码都有理由存在
-
可读性 — 代码本身表达意图,注释是补充
很多Agent把“可读性”放在“必要性”前面,导致docstring和注释比逻辑代码还多。
思路3:明确什么是不需要的
普通Agent需要被告知“要做什么”,ponytail额外告诉Agent“不做什么”:
-
不需要手动管理session(项目已有中间件处理)
-
不需要重复写类型校验(Pydantic已经处理)
-
不需要写显而易见的注释(get_user_by_id不需要注释说是查询用户的)
-
不需要处理当前任务范围外的异常
实际效果对比
安装ponytail前后的同一个需求
需求:“给用户模型加一个update_last_login方法”
安装前:
def update_last_login(self, user_id: int) -> User:
"""
Update the last login timestamp for a user.
Args:
user_id: The ID of the user whose login time to update.
Returns:
The updated User object.
Raises:
ValueError: If user_id is invalid.
DatabaseError: If the database operation fails.
"""
if not isinstance(user_id, int) or user_id <= 0:
raise ValueError("Invalid user_id")
try:
user = self.session.query(User).filter(User.id == user_id).first()
if not user:
raise ValueError(f"User {user_id} not found")
user.last_login = datetime.utcnow()
self.session.commit()
self.session.refresh(user)
return user
except SQLAlchemyError as e:
self.session.rollback()
raise DatabaseError(f"Failed to update login: {e}")
安装后:
def update_last_login(self, user_id: int) -> User: user = self.session.get(User, user_id) user.last_login = datetime.utcnow() self.session.commit() return user
差异:前者65行,后者7行。功能等价。
成本变化
假设一个项目每天生成100个函数:
| 指标 | 安装前 | 安装后 | 节省 |
|---|---|---|---|
| 每个函数平均token | 350 | 160 | 54% |
| 每日token消耗 | 35K | 16K | 19K |
| 月token消耗 | 1.05M | 0.48M | 0.57M |
| 月费用(按$5/M计算) | $5.25 | $2.40 | $2.85 |
对于大规模使用AI编程的团队,这个节省是实际的成本优化。
局限
ponytail对简单到中等复杂度的代码效果最好。对于复杂业务逻辑、安全敏感代码、需要详细注释给新人看的项目,过度精简可能会降低可读性。
另外,ponytail的效果依赖项目本身的代码质量。如果项目现有代码风格不统一或质量不高,Agent按“项目现有模式”生成的结果也可能不好。
总结
-
ponytail(⭐53K)让Agent代码量平均减少54%,token消耗减少20%,执行时间减少27%
-
AI生成冗余代码的原因是安全策略、缺乏项目上下文、指令模糊
-
不是简单让AI少写代码,而是让AI按项目现有模式写代码
-
对中等复杂度代码效果最好,复杂场景需要权衡精简和可读性
-
安装只需一行命令,可以按项目决定是否启用
2026年6月 | Vincent #ponytail #Agent #AI编程 #代码精简 #ClaudeCode

29

被折叠的 条评论
为什么被折叠?



