我的 Prompt & Skill 工程实践

第一部分:Prompt 工程

一、调优哲学(五条优先级原则)

在动手改任何 prompt 之前,先用这五条原则做方向判断:

聚焦优先于全面。 先保证主任务稳定完成,再补充细节。一个能把核心事情做对的 prompt,比一个面面俱到但哪个都不太稳的 prompt 有价值得多。

场景优先于通用。 不同场景的必要字段不同,不能机械套模板。先问自己:这个场景里,什么字段缺了会让结果失真?先保这些,再优化其他。

结构优先于辞藻。 清晰的模块化结构比"高级角色设定""华丽描述"更有用。模型不需要你用精美的语言描述任务,它需要你把任务说清楚。

定性优先于精算。 LLM 更擅长判断和归类,不擅长细碎数值计算。让模型做"优秀/良好/一般/较弱"的定性判断,不要逼它做多层级小数评分和复杂加权计算。如果一定要量化,优先用整数映射。

减法优先于加法。 先删冗余、删重复、删模糊项,再考虑增加约束。如果删掉 30% 的内容主任务反而更稳定,说明那 30% 本来就不该在那里。

二、正面建设:从零搭一个好 Prompt

2.1 用固定骨架,不要堆长说明

经过反复验证,比较稳定的骨架结构是:

Role        → 你是谁,一句话
Constraints → 不可违反的硬约束
Input       → 输入数据的格式和来源说明
Rules       → 处理规则和判断标准
Output Format → 直接可套用的输出模板

这个结构的好处是:模型更容易理解任务分层;后续迭代时容易定位问题在哪一层;不会把"要求""格式""输入说明"混在一起。

2.2 输出格式要直接可执行

不要只说"按以下格式输出",要把格式本身写完整——直接给 Markdown 标题、直接给表格列名、最好给一行占位示例。

反面示例:

输出包括:概述、时间线、原因分析、改进措施

正面示例:

| 时间 | 事件 | 负责人 | 操作 |
|------|------|--------|------|
| [HH:MM] | [发生了什么] | [姓名/-] | [做了什么] |

格式不是"说明",格式本身就应该是"模板"。

2.3 Few-shot 示例要精选边界 Case

给模型的示例不在多,在于"边界清晰"。正例告诉模型"什么样的输出才算对",但更重要的是反例告诉模型"这种看起来像对的其实是错的"。

反例的选取要选那些和正例长得最像、最容易混淆的 case,而不是明显错误的例子。模型不需要你告诉它 2+2≠5。

2.4 批次策略

大量数据需要 LLM 分析时,批次大小直接影响准确率和成本。太大导致模型注意力分散、漏掉细节;太小则上下文不够、无法做跨条目关联分析。实践中 5-8 条/批在准确率和效率之间取得了较好平衡,可根据场景调整。

2.5 规则要聚合,别分散

常见问题是:前面写一组要求,后面又写"注意",最后再补"额外说明",输出格式下面再来"字段说明"。这样会导致约束分散、重复、冲突。

更好的方式:Constraints 放强约束,Rules 放处理规则,Output Format 放模板。能内嵌到模板里的说明就别单独再写一遍。同一个意思只出现一次。

2.6 "可选项"必须写清触发条件

只写"可选",模型不知道什么时候该输出、什么时候不该输出、输出到什么程度。

所有"可选模块"都尽量改成:

  • 仅当……时输出
  • 若……存在,则补充
  • 如未提及,则省略

2.7 警惕上下文焦虑

Prompt 太长时,模型容易出现注意力被稀释、主任务和附属要求权重失衡、后半段规则执行变差的问题。输出看起来更完整,但核心质量下降。

应对手段:删重复说明、删无效话术、删非必要的"专家履历"、删场景里不稳定的可选模块、控制模板层级不要过深。

三、对抗式调优:完整操作手册

对抗式调优不是"改 prompt 的技巧集合",而是一套有严格工程纪律的闭环流程。它的核心思路类似 GAN:你的目标 prompt 是 G(Generator),你自己扮演 D(Discriminator)——不是靠直觉猜 prompt 哪里不好,而是用固定标准扫描 G 的实际输出,从输出中的垃圾反推 prompt 的漏洞,然后精确打补丁。每一轮都有备份、有测试、有对比数据,改不改得靠数字说话。

3.1 单轮操作流程

一轮完整的对抗式调优包含以下七步,顺序不可跳跃:

Step 1  备份当前 prompt(带版本号+时间戳,存入 backup/)
Step 2  用当前 prompt 对全部测试用例跑一遍,结果落盘
Step 3  用判定标准逐条扫描输出,标记 bad case
Step 4  归类 bad case 的误判模式(不是逐条修,是找共性)
Step 5  针对本轮最突出的一种误判模式,修改 prompt(只改一个变量)
Step 6  用修改后的 prompt 对全部用例重跑,结果落盘
Step 7  对比新旧两轮数据,按决策树决定保留哪个版本

为什么每步都不可省略: Step 1 保证任何时候能回滚;Step 2-3 保证"发现问题"基于数据而非直觉;Step 4 保证你改的是模式而非个例;Step 5 的"只改一个变量"保证你能归因;Step 6-7 保证改动确实有效。

3.2 怎样设计判定标准

判定标准是整个流程的锚点——标准设错了,后面所有步骤都在做无用功。

写法: 每条标准必须是一个可二值判定的命题,不能有程度词。"输出不够详细"不是好标准,"缺少时间字段"是好标准。"质量不高"不是好标准,"出现了不在输入数据中的人名"是好标准。

数量: 初期 5-8 条足够。条目太多会导致 bad case 标记混乱,难以归类误判模式。随着迭代推进,可以逐步增加细化标准,但每次增加也要跑全量回归确认新标准不会把原本正确的输出标记为 bad case。

存储: 单独存文件(如 criteria.txt),和 prompt 解耦。标准的变更和 prompt 的变更是两个独立维度,混在一起会丧失可审计性。

3.3 怎样从 bad case 反推误判模式

拿到一批 bad case 后,不要急着逐条修。先做归类:

第一步,标注命中了哪条判定标准。 同一条标准被大量命中,说明 prompt 在这个维度上有系统性缺陷,不是偶发问题。

第二步,在同一标准下找共性。 比如 10 个 bad case 都命中了"出现了不在输入中的信息",进一步看:是模型在编造数据?还是把 A 条目的信息错误归到了 B?还是把"推测"当成了"事实"?这三种是完全不同的误判模式,对应的修复手段也不同。

第三步,只挑本轮最突出的一种模式来修。 最突出 = 数量最多或影响最严重。不要试图一轮修三种模式,因为你分不清最终效果归因于哪个改动。

3.4 怎样写有效的反例

找到误判模式后,最有效的修复手段是在 prompt 里加正例/反例对照。这不是普通的 few-shot 示例,而是一种边界划定策略。

反例选取原则:选和正例长得最像、最容易混淆的 case。 模型不需要你告诉它 2+2≠5,它需要你告诉它"这个看起来像对的,其实是错的,错在哪里"。

写法示例:

✅ 正例:[具体的正确输出片段]
   → 为什么对:[一句话解释关键判断依据]

❌ 反例:[和正例非常相似但错误的输出片段]
   → 为什么错:[一句话指出具体错误点]

关键是"为什么错"这一行。不写它,模型只能猜你在纠正什么;写了它,模型知道你在划哪条边界。

3.5 哪些问题不该靠 prompt 解决:软约束 → 机制约束

对抗式调优到一定阶段,你会发现有些 bad case 怎么加反例都堵不死。这时候要问自己:这个问题是不是根本就不该让 LLM 来解决?

核心原则:不要依赖 LLM 的自觉性。 在 prompt 里写"请不要超过 10 条",模型可能听也可能不听。这种"LLM 软约束"必须转为"代码机制约束"。

三类转化手段:

手段 做法 适用场景
输入端过滤 不满足前置条件的数据直接跳过,根本不送给 LLM 数据质量问题导致的 bad case
输出端截断 代码层面只取前 N 条、只保留特定字段 输出过长、信息冗余类 bad case
枚举收口 把开放分类改为闭合枚举,删除"其他"兜底项 分类不准、乱贴标签类 bad case

判断标准: 如果某类 bad case 在加了反例后仍然出现频率 > 20%,就应该考虑从 prompt 约束转为代码约束。能在代码层面解决的问题,不要寄希望于 prompt。

3.6 版本管理与决策树

每轮改动都涉及"改还是不改"的决策,必须用明确的逻辑而非人工感觉。

备份纪律: 每次改 prompt 之前,先把当前版本复制到备份目录,文件名带版本号和时间戳(如 G_v3_20250504_143000.md)。这是宪法级约束——没有备份就没有回滚能力,没有回滚能力就不敢做实验。

回归测试纪律: 用固定测试集做基准,每轮结果落盘。两个不可违反的原则:测试集在迭代过程中不可修改(否则评估基准漂移,会产生虚假"改进");全量运行不可抽样(抽样会遗漏边界 case)。

版本保留决策树:

if 新版格式合规率 < 100%:
    直接否决,保留旧版(格式是底线,不可妥协)
elif 新版 bad case 数 < 旧版:
    替换
elif 新版 bad case 数 == 旧版 and 新版 prompt 更短或输出更短:
    替换(奥卡姆剃刀:同等正确性下更简洁更好)
else:
    保留旧版

这棵决策树把"什么算更好"从模糊的直觉变成了可执行的代码逻辑。

3.7 约束分层:宪法级 vs 法律级

在对抗式调优过程中,你会不断往 prompt 里加规则。规则多了,必须分层,否则模型会在关键约束上也"灵活处理"。

  • 宪法级约束: 不可违反,保证整个流程可信赖的底线。如"禁止编造数据""必须备份""结果必须来自真实执行""先备份再改"。这些约束在任何迭代中都不可动摇。
  • 法律级规则: 可以随迭代调整的处理规则。如阈值设定、批次大小、输出长度限制、判定标准的具体条目。

在 prompt 中明确标注两层的分界,让模型知道哪些是底线、哪些可以灵活执行。

3.8 元指令与业务指令分离

控制 Agent 行为的规则(如"什么时候停止迭代""什么时候回滚")不应该出现在业务输出的模板里。

做法:在 prompt 中用明确的标记(如 blockquote、注释行、独立章节)隔离元指令,并附注"以下规则仅用于内部行为控制,禁止出现在输出中"。这既告诉模型什么时候该停,又防止它把控制逻辑泄漏到最终输出中。

3.9 什么时候停止迭代

三个停止信号:

  1. 连续 2 轮 bad case = 0 且格式合规 100% → 当前 prompt 已达到测试集的天花板,继续改没有收益。
  2. 连续 3 轮新版均未减少 bad case → 陷入局部最优,当前的改动方向不对。此时应该停下来,重新审视判定标准本身是否合理,或者考虑从结构层面重构 prompt 而非继续打补丁。
  3. 发现剩余 bad case 全部属于"软约束无法解决"的类型 → 说明 prompt 端的优化已到极限,该转向代码层面做机制约束了。

3.10 GAN 风格自优化:让整个流程自动跑

当上述流程跑熟之后,可以把它封装成一个自优化 Agent——自动执行 3.1 的七步循环,自动做 bad case 扫描、归类、修改、回归测试、版本决策。人只需要在停止条件触发时介入。

这就是 GAN 风格的本质:G(目标 prompt)负责生成输出,D(Agent 自身)负责评判输出质量,D 的评判结果反向驱动 G 的迭代,形成自举循环。

先追求稳定输出,再追求丰富输出。 不要一上来就做"最全模板",先做"最稳模板"。

第二部分:Skill 工程

写好 Skill 的本质,是把一个模糊的人机对话意图,翻译成一个可路由、可拆解、可执行、可验证的"微服务协议"。

一、核心认知:Skill 是微服务,不是文档

Skill 不是"一段漂亮的 Prompt",而是一个可复用的能力单元。它有明确的输入输出契约、有触发边界、有失败处理、有版本管理——和微服务的设计思路完全一致。

这个认知决定了后面所有设计决策的方向:不是"怎么写得更好看",而是"怎么让它在生产环境里稳定运行、被准确调用、能和其他 Skill 协作"。

二、路由契约:Skill 的灵魂

一个 Skill 能不能被正确触发,不取决于它内部逻辑写得多好,而取决于路由契约写得是否精准。路由契约回答三个问题:什么时候该调我?什么时候不该调我?调我需要给什么?

2.1 触发条件与排除条件

触发条件之间是 AND 关系(全部满足才触发),排除条件之间是 OR 关系(任一满足就不触发)。两者必须显式写出来,不能让 Agent 自己猜。

为什么排除条件和触发条件同样重要: 实践中,误触发(不该调你的时候调了你)造成的损害往往大于漏触发。用户说"帮我看看这个文件",到底该触发文件阅读 Skill 还是代码审查 Skill?靠触发条件可能两个都命中,靠排除条件才能精确区分。

2.2 description 的写法

description 字段是 Agent 做语义匹配的核心依据。一句话说清四件事:做什么 + 何时用 + 何时不用 + 输出什么

过泛导致误触发(把不该它处理的任务也接了),过窄导致漏触发(用户换个说法就匹配不上)。校准的方法:准备 20 条测试用例(5 条应该触发 + 5 条不该触发 + 5 条边界 + 5 条异常),逐条验证匹配准确率。

2.3 触发词覆盖

同时覆盖中文、英文、口语化表述、正式表述。不要只写一个"生成报告",因为用户可能说"帮我看看今天有什么新内容""跑一下日报""出个报告"。多测试几种用户可能的说法,确保都能命中。

2.4 必需输入声明

每个输入字段要写清三件事:类型、示例值、缺失时的默认行为。"缺失时的默认行为"特别重要——不写它,模型会自己决定是报错还是猜一个值填上去,行为不可控。

三、架构设计:目录结构与加载机制

3.1 标准化目录结构

skill-name/
├── SKILL.md              # 必需:元数据 + 路由契约 + 执行指引
├── scripts/              # 可选:确定性执行脚本
│   ├── main.py
│   └── helper.py
├── references/           # 可选:参考文档(按需加载)
│   └── api_spec.md
└── resources/            # 可选:模板、数据等静态资源
    └── template.json

关键原则:只有 SKILL.md 一开始就加载,其他资源在执行时按需加载。 这不是文件整理的审美问题,而是 Token 效率的工程问题。

3.2 三层渐进式加载

这是 Skill 区别于普通 Prompt 的核心机制:

层级 加载时机 内容 Token 消耗
L1 元数据层 Agent 启动时 name, description 极低
L2 正文层 Skill 被触发时 SKILL.md 主体 中等
L3 资源层 执行特定步骤时 scripts, references 按需

不浪费 Token,不稀释模型注意力。Agent 启动时只加载所有 Skill 的 L1(name + description),用于路由匹配;命中后才加载 L2(完整执行指引);执行过程中需要查 API 文档或跑脚本时才加载 L3。

3.3 SKILL.md 保持精简

主文件控制在 500 行以内。超过则拆分为子文档(如 reference.md),通过主文件按需引用。和 Prompt 工程里"警惕上下文焦虑"完全同理——内容太长,模型注意力被稀释,核心指令执行质量下降。

黄金法则:删掉任何一句后不影响效果的话,就删掉它。 不要解释显而易见的概念(如"什么是 PDF"),只提供模型不知道的特定上下文。

四、SKILL.md 结构模板

---
name: skill-name
description: 做什么 + 何时用 + 何时不用 + 输出什么
---

## 路由契约
- 触发条件:(AND 关系,全部满足才触发)
- 排除条件:(OR 关系,任一满足就不触发)

## 必需输入
- 字段名: 类型 / 示例 / 缺失时的默认行为

## 执行步骤(标注执行者)
1. [步骤名] - 执行者: 模型/脚本 - 具体动作

## 输出格式
[固定模板或 JSON Schema]

## 失败处理
| 场景 | 检测方式 | 处理动作 |

## 示例
- 正例: 输入 → 期望输出
- 反例: 输入 → 应拒绝/降级

这个模板的每个章节都对应一个设计决策,不是可选的装饰。"路由契约"决定什么时候被调用,"必需输入"决定调用时需要什么,"执行步骤"决定怎么做,"输出格式"决定做完给什么,"失败处理"决定出错怎么办,"示例"用正反例划定边界。

五、两种形态,两套设计侧重

Skill 分两种截然不同的形态,设计侧重点完全不同:

定时任务型 Skill: 无人值守,自动执行,跑完推送结果。核心矛盾是"无人值守下的可靠性"——它不能弹对话框问你"要继续吗?"。设计重点:执行闭环、稳定性、幂等性、运维性。SKILL.md 要写清执行前置条件、失败处理策略、输出存储路径。

会话触发型 Skill: 有人在对面等结果,容错性更高。核心矛盾是"触发准确性和响应速度"。设计重点:路由契约精度、触发词覆盖、交互格式、响应速度。

不要混为一谈。 评审定时任务型 Skill 时,分析重点应放在闭环和稳定性上,而非用户交互体验;评审会话触发型 Skill 时,优先关注路由准确率和响应速度。

六、自由度控制:根据任务容错率定"松紧度"

不同类型的任务,应该给模型不同程度的自由度:

自由度 适用场景 做法
高风险操作(数据库迁移、支付、文件删除) 精确脚本 + 严格步骤 + 不留发挥空间
有首选模式但可微调(代码生成、数据转换) 伪代码 + 带参数的脚本
创意类任务(代码审查、写作、分析报告) 大致方向 + 相信模型判断

判断标准:这个步骤如果模型做错了,后果是什么? 后果不可逆(如删数据)→ 低自由度,用脚本锁死;后果可逆且成本低 → 高自由度,交给模型。

这也是"确定性操作剥离给 scripts"这一原则的底层逻辑——不是所有事情都该让 LLM 来做,能用代码确定性完成的,就不要让模型"发挥"。

七、执行闭环:四环设计

一个完整的 Skill 执行链路必须包含四个环节,少一个都可能在生产中出问题:

数据获取环 → 处理计算环 → 输出存储环 → 通知反馈环

数据获取环: 从哪里拿输入数据?怎么鉴权?每个数据源要有独立的超时和重试策略,一个源挂了不能拖垮整条链路。

处理计算环: 拿到数据后怎么处理?每个处理步骤职责单一。处理逻辑和 prompt 解耦——prompt 只管分析,代码管截断/过滤/合并。确定性操作交给 scripts,不确定性判断交给模型。

输出存储环: 结果存到哪里?输出必须有明确的唯一标识(通常是日期),保证幂等性——同一天重跑不会产生重复数据。

通知反馈环: 谁来看结果?怎么知道跑成功了?不只推送成功结果,也要推送失败告警。"今天报告没生成,原因是XXX"比"静默失败"好一万倍。

八、单一职责与组合协作

8.1 单一职责

一个 Skill 只做一件事。"抓取公众号文章并总结要点"——"抓取"和"总结"是两件事,应该拆成两个 Skill。

拆分的依据是"复用",不是"好看"。 问自己:这个步骤单独拿出来,有没有人会单独用?有 → 拆;没有 → 不拆。

8.2 通用接口

输出格式用 Markdown 或 JSON,不要发明只有自己能解析的私有结构。输入假设尽量最小化——对输入的要求越少,能被越多上游 Skill 接住。

8.3 并行化与结果融合

当 Skill 需要从多个数据源获取信息时:并行调度多路请求,而非串行执行;多路结果做权重打分、去重、按置信度排序,只保留 Top-N;数据新鲜度不能只靠 prompt 里的"请只返回最近的内容"(软约束),还需要在结果处理阶段做时间戳检查(机制约束)。

不要靠人的记忆来保证质量,要靠自动化检查。把关键约束写成可执行的 lint 规则——"SKILL.md 必须包含失败处理章节""description 不超过 200 字""每个执行步骤必须标注执行者"。这种只存在于脑子里的品味,应该变成一条自动执行的检查。

项目约束、文档维护规则、执行计划纪律等,通过自动注入规则在 Agent 每次执行时自动读取,比在每次对话里口头提醒"记得遵守XXX规则"可靠得多。

核心理念:Linter 脚本把品味变成代码,Rules 把约束变成基础设施。

第三部分:Prompt × Skill 的统一视角

Prompt 和 Skill 的关系

Prompt 方法论解决的是"怎么让 LLM 做对事",Skill 方法论解决的是"怎么让做对的事稳定地跑在生产环境里"。两者合在一起就是一条从"想法"到"可靠运行的自动化"的完整路径。

贯穿全流程的核心信念

不要信任 LLM 的自觉性,要用数据找漏洞、用反例划边界、用代码做兜底。

先追求稳定,再追求丰富。先做最稳模板,再做最全模板。

Prompt 调优不是"加更多要求",而是"让模型更容易把最重要的事做好"。

#agent开发##prompt工程#
全部评论

相关推荐

不愿透露姓名的神秘牛友
04-30 17:45
本人简历上&nbsp;1&nbsp;个&nbsp;RAG&nbsp;项目&nbsp;+&nbsp;1&nbsp;个&nbsp;Agent&nbsp;demo;这次面的是AI岗一面前我以为:背完八股&nbsp;+&nbsp;把项目讲清楚,应该能稳过。0-5&nbsp;min:自我介绍&nbsp;+&nbsp;项目背景-&nbsp;顺利。讲清楚了我的&nbsp;RAG&nbsp;是给法律咨询场景做的,痛点是大模型不懂行业术语。5-20&nbsp;min:项目深挖(开始崩)-&nbsp;Q1:你的法律文档总共多少?切了多少个&nbsp;chunk?-&nbsp;我:约&nbsp;500&nbsp;份&nbsp;PDF,5&nbsp;万个&nbsp;chunk-&nbsp;Q2:500&nbsp;份&nbsp;PDF&nbsp;加起来才&nbsp;5&nbsp;万&nbsp;chunk?平均每份&nbsp;100&nbsp;个&nbsp;chunk,你切片粒度是多少?-&nbsp;我:512&nbsp;token-&nbsp;Q3:法律文档里"第三条第二款"和"第三条之二"是不同含义,你的切片会不会把它切散?-&nbsp;我:(沉默&nbsp;5&nbsp;秒)……应该会-&nbsp;Q4:那你怎么解决?-&nbsp;我:我可以加一个&nbsp;metadata……(开始编)❌&nbsp;第一次崩:切片粒度没考虑业务语义。20-35&nbsp;min:评测体系(继续崩)-&nbsp;Q:你怎么知道你的&nbsp;RAG&nbsp;有效?-&nbsp;我:我用&nbsp;Recall@5……-&nbsp;Q:评测集多少条?怎么构造的?-&nbsp;我:100&nbsp;条,我手工标注的-&nbsp;Q:100&nbsp;条够吗?分布怎么样?-&nbsp;我:分布……我没分-&nbsp;Q:那你的&nbsp;Recall@5&nbsp;是&nbsp;0.81,你怎么知道这个数字是好是坏?baseline&nbsp;是什么?-&nbsp;我:(沉默&nbsp;10&nbsp;秒)❌&nbsp;第二次崩:没有&nbsp;baseline,没分布分析,纯靠"看起来还行"。35-55&nbsp;min:Agent&nbsp;部分(彻底崩)-&nbsp;Q:你的&nbsp;Agent&nbsp;demo&nbsp;用了几个工具?-&nbsp;我:3&nbsp;个,搜索、计算器、文档查询-&nbsp;Q:当用户问一个问题,你的&nbsp;Agent&nbsp;怎么决定调哪个工具?-&nbsp;我:用&nbsp;ReAct,让模型自己决定-&nbsp;Q:模型决策错了怎么办?-&nbsp;我:我加了个&nbsp;reflection……-&nbsp;Q:reflection&nbsp;失败&nbsp;3&nbsp;次后怎么处理?-&nbsp;我:(沉默&nbsp;15&nbsp;秒)……我没想过❌&nbsp;第三次崩:异常路径完全没设计。55-65&nbsp;min:业务理解&nbsp;+&nbsp;反问-&nbsp;Q:你觉得字节做&nbsp;AI&nbsp;应用最大的瓶颈是什么?-&nbsp;我:算力?数据?-&nbsp;Q:你看过哪些字节最近发的&nbsp;AI&nbsp;产品?-&nbsp;我:豆包、扣子……-&nbsp;Q:扣子是&nbsp;Agent&nbsp;平台还是工作流平台?-&nbsp;我:(再次沉默)❌&nbsp;第四次崩:对面试公司业务一无所知。
面试官拷打AI项目都会问...
点赞 评论 收藏
分享
评论
1
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务