prompt-optimizer skill

把 SkillOpt 包装成一个 Claude Code Skill

从「一个自动优化提示词的算法」到「用一句自然语言就能跑起来的技能」------本文以本项目里的 prompt-optimizer skill 为样本,拆解怎样把 SkillOpt 的 6 步优化循环包装成一个可复用的 Claude Code Skill。

一、先说清楚:SkillOpt 是什么,Skill 又是什么

这两个词长得像,但指的是完全不同的两层东西,先分开。

SkillOpt 是一个算法思想:借鉴强化学习里 rollout → evaluate → reflect 的训练循环,把「优化一段提示词」变成一个可以自动跑、有明确接受/拒绝判据的迭代过程。它的核心信念只有一句:

不猜测一次改动是否有效,而是用真实重跑出来的分数去对比新旧提示词。

Skill 则是 Claude Code 的一种封装形式 :一个目录,里面放一份 SKILL.md(告诉 Claude 什么时候用、怎么用)、若干脚本、若干参考文档。用户不需要记命令,只要说「跑 3 轮优化」,Claude 就会自己找到这个 skill、执行脚本、读回结果、汇报给你。

这篇博客讲的就是中间那一层桥:怎么把前者(一个 Python 算法)包装进后者(一个 Claude 能自主调度的技能)。

二、被包装的内核:6 步优化循环

包装之前,得先有个值得包装的内核。prompt-optimizer 的内核是 optimize_prompt.py 里的一轮循环,六步环环相扣:

vbnet 复制代码
Step 1: ROLLOUT  --- 用当前提示词跑任务 → 得到标注结果
Step 2: EVALUATE --- LLM Judge 评分 → 得到 old_hard_acc, old_soft_acc
Step 3: REFLECT  --- 分析低分样本 → 提出结构化 edits(修改建议)
Step 4: UPDATE   --- 把 edits 应用到提示词 → 生成新提示词
Step 5: VERIFY   --- 用新提示词重跑任务 + 评分 → 得到 new_hard_acc
Step 6: GATE     --- new > old ? 接受新提示词 : 回滚到旧提示词

这里面最值钱的设计是 GATE。它不是「让模型自我感觉良好地说『我改进了』」,而是拿两组真实测量的分数硬碰硬:

python 复制代码
if new_hard_acc > old_hard_acc:
    → ACCEPT,更新 current_prompt.md、best_prompt.md
elif new_hard_acc == old_hard_acc and new_soft_acc > old_soft_acc:
    → ACCEPT(软分提升,硬分不变)
else:
    → REJECT,回滚旧提示词,converge_count += 1

连续 N 轮被 GATE 拒绝,就判定「收敛」,自动停机。整个内核对外只暴露几个动作:跑 N 轮 / 只评分 / 查状态 / 最终验证 / 重置

记住这份「动作清单」------它就是我们要在 Skill 层暴露的接口。

三、包装的四个部件

一个 Skill 目录长这样:

bash 复制代码
.claude/skills/prompt-optimizer/
├── SKILL.md                       # 契约:何时触发 + 怎么执行 + 怎么汇报
├── references/
│   └── optimization_guide.md      # 深层参考:参数、配置、机制细节
└── scripts/
    ├── optimize_prompt.py         # 6 步循环
    └── llm_judge.py               # 评分脚本

再加一个「入口糖」:

bash 复制代码
.claude/commands/prompt-opt.md     # 斜杠命令,/prompt-opt 3

下面逐一说这四个部件各自扛什么责任。

部件 1:SKILL.md ------ 触发契约 + 执行剧本

这是整个包装的灵魂。它做两件事。

第一,写好 description,让 Claude 知道「什么时候该想到我」。 frontmatter 里的描述不是给人看的文案,是给模型做语义匹配的触发器:

yaml 复制代码
---
name: prompt-optimizer
description: |
  Claude Code 驱动的提示词自动优化 --- 用 SkillOpt 6步循环(打标→评分→反思→修改→验证→决策)
  自动改进总结提示词直到收敛。Claude 执行脚本、读取结果、汇报进展,用户只需说意图。
  Use when: 用户想优化提示词、评估标注质量、跑优化循环。
  Triggered by: "优化提示词", "调优prompt", "跑优化", "评估质量", "prompt-opt",
  "继续优化", "评分", "evaluate", 或给一个数字表示优化轮数。
---

关键技巧:把用户会说的原话列进 Triggered by。用户不会说「请调用 prompt-optimizer skill」,他们会说「跑 3 轮」「评个分」「继续优化」。把这些口语直接写进去,命中率立刻上一个台阶。

第二,写一份「执行剧本」,让 Claude 知道拿到意图后一步步做什么。 SkillOpt 内核对外暴露 5 个动作,SKILL.md 就用一张表把「用户口语 → 模式 → 该跑哪个脚本」对齐:

用户说 模式 Claude 执行
"跑3轮优化"/数字 N 轮优化 optimize_prompt.py --rounds N
"评分"/"evaluate" 只评分 llm_judge.py
"优化状态"/"status" 查状态 optimize_prompt.py --status
"重置优化" 重置 删除 optimize/ 目录

剧本里还有一条铁律,值得单独强调:

执行脚本后,Claude 必须 Read 结果文件(prompt_history.jsonbest_prompt.md),再汇报给用户。

这是把「脚本」变成「技能」的分水岭。脚本只管在终端打印;而 Skill 要求 Claude 主动读回结构化产物、翻译成人能看懂的进展表格:

yaml 复制代码
🔄 优化进展
  轮 | 旧hard | 新hard | gate            | edits | 收敛计数
  1  |  0.50  |  0.67  | accept_new_best |  3    | 0
  2  |  0.67  |  0.67  | reject          |  2    | 1
  best_hard: 0.73 | best_soft: 0.80
  ✅ 尚未收敛,可以继续: /prompt-opt 3

执行---读取---汇报,这个三拍子就是 Skill 相较裸脚本的全部增量价值。

部件 2:scripts/ ------ 自包含、能自己找到依赖

内核脚本要跟 SKILL.md 放在一起,让 skill 自包含。但真实项目里脚本之间有依赖------optimize_prompt.py 需要调 vlm-labeler skill 的 run_task.pytask_config.py。包装时踩的第一个坑就是路径:skill 被安装到别处后,硬编码的相对路径会全断。

解法是让脚本自己向上找项目根 ,认准 .claude/skills/ 这个锚点:

python 复制代码
def _find_project_dir():
    """向上遍历,找到包含 .claude/skills/ 的目录当作项目根。"""
    current = os.path.dirname(os.path.abspath(__file__))
    for _ in range(10):
        parent = os.path.dirname(current)
        if os.path.isdir(os.path.join(parent, ".claude", "skills")):
            return parent
        current = parent
    ...

然后跨 skill 找依赖脚本时,按「优先 skill 源头 → 再项目根 scripts/ → 最后当前目录」的优先级兜底:

python 复制代码
run_task_script = _find_script("run_task.py", "vlm-labeler")
judge_script    = _find_script("llm_judge.py", "prompt-optimizer")

包装启示:脚本一旦要进 skill,就不能再假设「当前工作目录」。把「我在哪」「依赖在哪」的问题在脚本内部解决掉,skill 才能被搬到任意项目里即插即用。

部件 3:references/ ------ 分层,别把细节塞进 SKILL.md

SKILL.md 要短、要像剧本,让 Claude 一眼扫完就知道怎么动手。而参数全集、配置格式、GATE 的显著性阈值、Judge 多次采样聚合、过拟合诊断......这些深层细节一股脑塞进去会淹没主线。

于是它们被拆到 references/optimization_guide.md,并在 SKILL.md 里留一句指路:

所有任务特定内容由 task_config.yaml 定义,详见 references/optimization_guide.md

Claude 平时只读 SKILL.md;只有当用户追问「怎么调采样次数」「显著性阈值怎么设」时,才按需去翻 reference。这是一种渐进式披露------上下文预算花在刀刃上。

部件 4:commands/ ------ 给个斜杠入口

Skill 靠语义触发,已经够用。但再包一层斜杠命令,能给用户一个确定性的、可打字的入口:

markdown 复制代码
---
description: 通用提示词自动优化 --- Claude Code 驱动,评分或迭代优化提示词直到收敛
---
用户意图: $ARGUMENTS

根据 `$ARGUMENTS` 判断模式,然后由 Claude 执行脚本并汇报结果。
详细流程见 .claude/skills/prompt-optimizer/SKILL.md。

命令文件本身几乎不装逻辑,只把 $ARGUMENTS 接住,再把 Claude 指回 SKILL.md。于是 /prompt-opt 3/prompt-opt evaluate/loop 10m /prompt-opt 都能用------命令是壳,SKILL.md 是芯。

四、把配置从内核里抽出去

包装成 skill 的一个隐藏收益:通用化 。原本 SkillOpt 只会优化「VLM 图片打标」的提示词,但只要把所有任务特定的东西------输入类型、评分维度、角度提示词------全部外置到 task_config.yaml,同一个 skill 就能优化图片打标、QA 评估、文本分类:

yaml 复制代码
judge:
  pass_threshold: 0.8
  dimensions:            # 评分维度完全由配置决定
    completeness: { name: "信息完整性", weight: 0.3, ... }
    accuracy:     { name: "准确性",     weight: 0.3, ... }
optimize:
  converge_patience: 3   # 连续几轮无提升算收敛
  max_edits_per_round: 5 # 每轮最多几条修改

内核代码里没有一处写死「图片」或某个具体维度;references/ 里还附了三套模板(image_labeling / qa_evaluation / text_classification)。算法归脚本,任务归配置------这条边界划清楚,一个 skill 才能服务一类问题,而不是一个问题。

五、可复用的包装配方

抽掉 SkillOpt 这个具体案例,把「把一个算法包装成 Skill」的通用步骤留下:

  1. 锁定内核的动作清单。先让算法脚本能独立跑通,并想清楚它对外暴露哪几个动作(这里是:优化 N 轮 / 评分 / 查状态 / 验证 / 重置)。动作清单 = Skill 的接口。
  2. 写触发型 description 。把用户会说的原话塞进 Use whenTriggered by,让语义匹配命中口语,而不是命中术语。
  3. 写执行剧本。用一张「用户口语 → 模式 → 脚本命令」的表把意图映射到动作;并强制「执行 → Read 产物 → 汇报」三拍子。
  4. 让脚本自包含、自寻路 。以 .claude/skills/ 为锚点向上找项目根,跨 skill 依赖按优先级兜底,不依赖当前工作目录。
  5. 分层放细节SKILL.md 只留主线剧本,参数/机制/FAQ 下沉到 references/,按需披露。
  6. 外置任务配置 。算法归脚本、任务归 *.yaml,让一个 skill 覆盖一类任务。
  7. (可选)加斜杠命令壳 。给一个确定性入口 /xxx $ARGUMENTS,逻辑仍指回 SKILL.md

尾声

SkillOpt 的算法值钱,但把它包装成 Skill 之后,它才真正好用:不用记参数、不用手动读 JSON、不用自己判断收敛。用户说「跑 3 轮」,Claude 就替他跑循环、读分数、画进展表、给下一步建议。

包装做的事,一句话概括------把「一个人要会用的脚本」变成「一句话就能指挥的技能」 。而这层包装的全部秘密,就藏在那份写得像剧本的 SKILL.md 里。

相关推荐
万法若空1 小时前
【数据结构-哈希表】哈希表原理
数据结构·算法·散列表
退休倒计时1 小时前
【每日一题】LeetCode 437. 路径总和 III TypeScript
算法·leetcode·typescript
学逆向的2 小时前
汇编——内存
开发语言·汇编·算法·网络安全
tachibana22 小时前
hot100 翻转二叉树(226)
java·数据结构·算法·leetcode
生戎马3 小时前
高光谱拼接算法(七)USAC
算法
PhotonixBay3 小时前
共聚焦成像核心原理:针孔、PSF与三维形貌测量技术
人工智能·测试工具·算法
来一碗刘肉面4 小时前
C++中的引用语法
c++·算法
Black蜡笔小新4 小时前
自动化AI算法训练服务器DLTM零代码私有化赋能智慧农业用AI智能视觉守护万亩良田
人工智能·算法·自动化
Tisfy4 小时前
LeetCode 1288.删除被覆盖区间:排序(非二重循环暴力)
算法·leetcode·题解·排序