ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

模型越强Prompt越该瘦身:Skill与AGENTS.md的减法设计指南

模型越强Prompt越该瘦身:Skill与AGENTS.md的减法设计指南

1. 当模型自己变聪明,堆料式提示词开始反噬

过去一年我经手过十几个基于大模型的自动化项目,从文档整理、代码生成到数据分析流水线都有。有个现象越来越明显:模型每升级一次,我手里那套精心调教的 Prompt 和 Skill 就"过时"一次。不是失效,而是变得多余——原本需要三段角色设定、五条格式约束、两个示例才能让模型稳定输出的任务,现在一句直白的需求描述就能拿到更好的结果。

这个变化不是错觉。早期模型的理解能力有限,我们必须用大量上下文去"框住"它的行为边界,就像教一个新手做事,得把每一步都写清楚。但当模型本身具备了更强的指令遵循能力和推理能力之后,那些冗余的约束反而成了噪音。我见过最典型的案例是一个代码审查 Skill,里面塞了将近两千字的规则说明,结果模型在审查时频繁"卡壳",输出的建议要么过于保守,要么直接跑偏。后来我把规则砍到三百字,只保留核心判断标准,输出质量反而上了一个台阶。

这就是我想聊的核心问题:模型能力提升之后,Skill 和 Prompt 的设计逻辑需要从"加法"转向"减法"。关键词里的 Skill、Prompt、Coding Agent、AGENTS.md、SKILL.md 这些概念,本质上都是在解决同一个问题——怎么让模型按照我们期望的方式工作。但解法在变。以前靠"写得多",现在靠"写得准"。

这篇文章适合两类人看:一是正在维护大量 Prompt 模板或 Skill 配置的开发者,你可能已经感觉到维护成本越来越高但收益在递减;二是刚接触 Agent 开发的新手,你可以少走一些"堆料"的弯路,直接从更简洁的设计思路入手。我会结合自己在 Coding Agent、文档处理、数据分析等场景下的实际经验,把"做减法"这件事拆开讲清楚——减什么、为什么减、怎么减、减完之后怎么验证。

2. 为什么模型越强,Prompt 反而越该瘦身

2.1 指令遵循能力的提升让"过度约束"变成干扰

早期做 Prompt Engineering 的时候,有个默认共识:模型不够聪明,你得把话说死。比如要求输出 JSON 格式,不仅要写"请输出 JSON",还要给示例、加"不要输出其他内容"的强调、甚至用分隔符把指令和输入隔开。这套做法在当时确实有效,因为模型确实容易"跑偏"。

但现在情况变了。主流模型对格式指令的遵循能力已经相当可靠,你写"以 JSON 格式返回,包含 name、type、description 三个字段",它基本不会给你多一个字段或者少一个字段。这时候如果你还在 Prompt 里塞三段格式说明加两个示例,模型反而可能因为信息过载而在某些边界情况下做出奇怪的选择。

我做过一个对比测试,同一个数据提取任务,A 版本 Prompt 有 800 字,包含详细的字段说明、格式示例、异常处理规则;B 版本只有 120 字,直接说清楚要提取什么、输出什么格式。在同一个模型上跑 50 条测试数据,B 版本的准确率比 A 版本高了 7 个百分点,而且输出格式的一致性更好。原因很简单:A 版本里那些"异常处理规则"和"边界情况说明"之间存在微妙的冲突,模型在遇到模糊情况时不知道该听哪一条。

这里有个判断标准:如果你的 Prompt 里出现了"如果……则……否则……"的嵌套逻辑超过两层,大概率是你在替模型做它自己能做的决策。

2.2 上下文窗口的"注意力稀释"效应

大模型的注意力机制有个特点:输入越长,每个 token 分到的注意力权重就越分散。这不是什么秘密,但在实际写 Prompt 的时候很多人会忽略。你往 System Prompt 里塞了 3000 字的角色设定、行为准则、输出规范,模型确实"看到了"这些内容,但它在生成每一个 token 的时候,真正"关注"到的可能只是其中一小部分。

我自己的经验是,当 System Prompt 超过 1500 字之后,靠后的指令对输出的实际影响会明显下降。这就像你跟一个人交代事情,说了半小时,他记住的往往是开头和结尾,中间的大段细节容易被忽略。模型也类似,只不过它的"遗忘曲线"更陡。

这就解释了一个常见现象:很多人发现自己在 Prompt 末尾加的"重要提醒"往往不起作用。不是模型不听话,而是这条提醒被前面的大量信息淹没了。解决办法不是把提醒加粗或者重复三遍,而是把前面那些不必要的内容删掉,让真正重要的指令获得足够的注意力权重。

2.3 Skill 的维护成本随复杂度指数上升

Skill 和 Prompt 的区别在于,Skill 通常是一个可复用的能力单元,可能包含多个 Prompt、工具调用逻辑、后处理步骤。当你往一个 Skill 里不断添加规则和分支的时候,维护成本不是线性增长的,而是指数级的。

我维护过一个用于技术文档翻译的 Skill,最初只有一条翻译规则加一个术语表,运行得很稳定。后来为了处理各种边缘情况,陆续加了"代码块不翻译""链接文本翻译但 URL 不变""表格对齐方式保持""特定缩写不翻译"等十几条规则。结果每次模型升级或者术语表更新,我都要花大量时间检查这些规则之间有没有冲突,有没有哪条规则在新模型上产生了意料之外的行为。

更麻烦的是,这些规则里有相当一部分是为了"修补"旧模型的能力缺陷而加的。新模型可能已经原生支持了这些行为,你加的规则反而在制造问题。比如"代码块不翻译"这条,新模型本身就能识别代码块并保持原样,你再加一条显式规则,有时候会让模型在遇到行内代码时也触发"不翻译"逻辑,导致正常文本被跳过。

3. 拆解一个 Skill 里哪些内容是"该砍的"

3.1 角色设定的边际效益已经很低

"你是一位拥有二十年经验的资深软件架构师,精通分布式系统、微服务、云原生……"这种角色设定在早期确实有用,它能帮模型快速定位到相关的知识领域和表达风格。但现在模型的通用能力已经很强,你直接问架构问题,它不会因为缺少角色设定就给出外行答案。

我现在的做法是:只在角色设定能带来明确的风格差异时才保留。比如你需要模型用"给小学生讲课"的方式解释技术概念,那"你是一位擅长科普的技术博主"这个角色设定是有价值的,因为它约束了输出的语言风格。但如果你只是需要一个准确的代码审查结果,"你是一位资深工程师"这句话删掉完全不影响输出质量。

实测下来,删掉冗余角色设定后,Prompt 长度能减少 20% 到 30%,而输出质量在大多数任务上没有可感知的下降。省下来的 token 空间可以用来放真正重要的东西:具体的任务描述、输入数据的上下文、期望的输出格式。

3.2 重复强调的规则只留一条最清晰的

很多人写 Prompt 有个习惯:重要的规则说三遍。开头说"请务必按照 JSON 格式输出",中间说"注意输出必须是合法的 JSON",结尾说"记住,只输出 JSON,不要有其他内容"。这种做法在旧模型上可能有效,但在新模型上,重复的指令不仅浪费 token,还可能让模型觉得这个要求特别复杂,从而在输出时过度谨慎。

我现在的原则是:一条规则只说一次,放在最靠近它生效位置的地方。格式要求就放在输出描述那一节,不要在开头和结尾重复。如果模型偶尔不遵守,先检查是不是规则本身有歧义,而不是加重复强调。

有个例外情况:当你的 Prompt 很长(超过 2000 字)且任务复杂时,在结尾用一句话总结核心要求是合理的。但这不是"重复",而是"收束",帮助模型在生成前最后确认一次重点。这种收束性总结控制在 50 字以内就够了。

3.3 示例不是越多越好,一个精准的胜过三个模糊的

Few-shot 示例是 Prompt 里的重头戏,但也是最容易膨胀的部分。我见过一个信息抽取的 Prompt 里放了 8 个示例,覆盖各种输入格式和输出情况。作者的本意是"让模型见多识广",但实际效果是模型在遇到新输入时,会试图在 8 个示例里找最相似的那个来模仿,而不是真正理解抽取规则。

示例的作用是锚定输出格式和粒度,不是穷举所有情况。一个设计良好的示例,应该能让模型清楚地看到"输入长什么样、输出长什么样、粒度和边界在哪里"。如果一条规则用文字说不清楚,加一个示例;如果文字已经说清楚了,示例就是多余的。

我通常的做法是:核心规则用文字描述,边界情况用一个示例说明,格式要求用一个示例锚定。总共不超过两个示例。如果发现模型在某些情况下输出不稳定,先想想是不是规则本身没写清楚,而不是加第三个示例。

3.4 异常处理逻辑交给模型自己判断

这是最反直觉的一条。早期我们习惯在 Prompt 里写大量"如果遇到 X 情况,则做 Y 处理"的逻辑,因为模型自己判断不可靠。但现在模型的推理能力已经足够处理大多数常见的边界情况,你写的异常处理规则反而可能和模型自己的判断产生冲突。

举个例子:一个从合同文本里提取关键条款的 Skill,早期版本里我写了"如果某条款表述模糊,则标注为'需人工确认'"。后来发现模型在没有这条规则的时候,会自己判断哪些条款模糊并给出说明,而且判断得比我预设的规则更细致。我那条规则反而限制了它的发挥,让它把所有"稍微有点模糊"的条款都统一标注为"需人工确认",丢失了有价值的信息。

现在的做法是:只写"正常路径"的处理逻辑,异常情况让模型自己判断并说明。如果发现模型在某些异常情况下的处理不符合预期,再针对性地加规则,而不是提前预设所有可能的异常。

4. 从 AGENTS.md 到 SKILL.md:配置文件该怎么精简

4.1 AGENTS.md 里真正需要保留的信息

AGENTS.md 是 Coding Agent 场景下常用的配置文件,用来告诉 Agent 这个项目的结构、约定、注意事项。我见过很多项目的 AGENTS.md 写得像一本开发手册,从项目背景到代码规范到部署流程无所不包。但实际上,Agent 在执行编码任务时,真正需要从 AGENTS.md 里获取的信息非常有限。

我的经验是,AGENTS.md 里保留三类信息就够了:项目结构的关键路径(告诉 Agent 代码在哪里)、不可违反的硬性约定(比如"不要修改 migrations 目录下的文件")、验证方式(比如"运行 npm test 来验证修改")。其他内容,比如代码风格偏好、架构设计理念、历史决策背景,Agent 要么不需要,要么可以从代码本身推断出来。

我做过一个对比:一个 500 行的 AGENTS.md 和一个 80 行的 AGENTS.md,在同一个 Coding Agent 上执行相同的任务集。精简版的完成率更高,而且 Agent 更少出现"过度设计"的情况——它不会因为读了大量架构说明就试图在简单任务上套用复杂模式。

4.2 SKILL.md 的"最小可用结构"

SKILL.md 通常用来定义一个可复用的技能单元。一个常见的误区是把 SKILL.md 写成 Prompt 的容器,把所有指令都塞进去。但实际上,SKILL.md 的核心作用是声明这个 Skill 能做什么、需要什么输入、产出什么输出,具体的执行逻辑应该尽量简洁。

我目前用的 SKILL.md 结构大概是这样的:一段话描述 Skill 的功能和适用场景,一个输入参数的说明表,一个输出格式的说明,然后是核心执行指令(控制在 300 字以内)。如果 Skill 涉及工具调用,再附上工具列表和调用约定。整个文件通常不超过 100 行。

这个结构的关键在于:把"是什么"和"怎么做"分开。"是什么"部分可以详细一些,帮助使用者判断这个 Skill 是否适合当前任务;"怎么做"部分要尽量精简,因为这部分内容会直接进入模型的上下文,每多一个字都在消耗注意力预算。

4.3 用表格替代段落描述参数和约束

这是一个很实用的技巧:当你要描述多个参数、多个约束条件、多个输出字段的时候,用表格比用段落文字更紧凑,模型也更容易准确理解。

比如描述一个文档处理 Skill 的输入参数,用段落写是这样的:

输入包含三个参数:file_path 是待处理文件的路径,支持绝对路径和相对路径;output_format 指定输出格式,可选值为 markdown、plain text 和 json;max_length 控制输出最大长度,单位为字符,默认值为 5000。

用表格写是这样的:

参数类型说明默认值
file_pathstring待处理文件路径,支持绝对和相对路径必填
output_formatstring输出格式:markdown / plain text / jsonmarkdown
max_lengthint输出最大字符数5000

两种写法传达的信息完全一样,但表格版本更紧凑,模型解析起来也更不容易出错。我实测下来,用表格替代段落描述参数,Prompt 长度能减少 30% 到 40%,而且模型对参数的理解准确率更高。

5. 做减法的具体操作:从现有 Prompt 里砍掉什么

5.1 第一步:标记所有"防御性指令"

防御性指令是指那些为了"防止模型犯错"而加的规则,比如"不要编造信息""不要输出无关内容""确保输出准确"。这些指令在旧模型上确实有必要,但新模型在大多数任务上已经不会犯这些低级错误了。

我的做法是:把所有以"不要""禁止""避免""确保不"开头的句子标记出来,然后逐条问自己——如果删掉这条,模型真的会犯这个错吗?如果答案是"大概率不会",就删掉。如果答案是"偶尔会",就先删掉,观察一段时间,真的出问题了再加回来。

实测下来,一个典型的 Prompt 里,防御性指令能占到总长度的 20% 到 30%。删掉之后,输出质量在大多数情况下没有下降,偶尔在边界情况上会有波动,但通过针对性地补充一两条精准的规则就能解决,比保留一大堆防御性指令更高效。

5.2 第二步:合并同义规则,消除隐性冲突

Prompt 里的规则冲突往往不是显性的,而是隐性的。比如一条规则说"输出要简洁",另一条规则说"要给出详细的解释",这两条规则在不同段落里,写的时候不觉得冲突,但模型在执行时会困惑。

我的做法是:把所有规则列出来,按主题分组,看看同一主题下有没有多条规则。如果有,尝试合并成一条最清晰的表述。合并的过程中经常会发现,有些规则之所以存在,是因为另一条规则表述不够清楚,导致模型理解偏了,于是加了一条"补丁规则"。这种情况下,正确的做法是修改原始规则,而不是保留补丁。

举个例子:一个代码生成 Skill 里有"生成的代码要包含注释"和"注释要简洁,不要写显而易见的注释"两条规则。这两条规则其实在说同一件事:注释要有价值。合并成"只为不直观的逻辑添加简洁注释"就够了,比两条分开写更清晰。

5.3 第三步:把"怎么做"换成"做什么"

这是做减法里最关键的一步。很多 Prompt 之所以长,是因为写作者在教模型"怎么做",而不是告诉模型"做什么"。比如:

首先分析输入文本的语言,然后判断文本类型,如果是技术文档则使用专业术语翻译,如果是营销文案则使用流畅自然的表达,翻译时注意保持原文的语气……

这种写法是在描述一个决策流程,模型需要先理解这个流程,再执行。但如果你直接说"将以下文本翻译成中文,技术内容保持术语准确,营销内容保持表达流畅",模型自己就能判断该用什么策略。

把流程描述换成结果描述,是精简 Prompt 最有效的手段之一。你不需要告诉模型先做什么后做什么,只需要告诉它最终输出应该满足什么条件。模型自己会找到达成路径。

5.4 第四步:用"输出示例"替代"格式说明"

格式说明往往占 Prompt 的很大篇幅,因为要描述字段名、字段类型、嵌套结构、可选值等等。但如果直接给一个输出示例,模型看一眼就明白了,不需要那么多文字。

比如你要模型输出一个任务列表的 JSON,用文字描述可能要写:

输出一个 JSON 数组,每个元素包含 id(整数)、title(字符串)、status(枚举值:pending/in_progress/done)、assignee(字符串或 null)四个字段……

用示例只需要:

[ {"id": 1, "title": "完成文档初稿", "status": "in_progress", "assignee": "张三"}, {"id": 2, "title": "代码审查", "status": "pending", "assignee": null} ]

示例不仅更紧凑,而且消除了所有歧义——模型能直接看到 null 是怎么表示的、枚举值长什么样、字段顺序是什么。我现在的习惯是:能用示例说清楚的格式,绝不用文字描述。

6. 减法之后,怎么验证没有砍过头

6.1 建立最小测试集,覆盖核心场景和边界情况

精简 Prompt 之后,你需要一套测试集来验证效果。这套测试集不需要很大,但必须覆盖两类情况:核心场景(这个 Prompt 最常处理的任务类型)和边界情况(容易出错或输出不稳定的输入)。

我的做法是:核心场景准备 5 到 10 条测试数据,边界情况准备 3 到 5 条。每次修改 Prompt 后跑一遍,对比修改前后的输出。如果核心场景的通过率没有下降,边界情况的通过率在可接受范围内波动,就说明精简是成功的。

这里有个经验:不要追求 100% 的通过率。过度追求完美会导致你不断加规则,最终又回到堆料的老路。我的标准是核心场景 90% 以上通过,边界情况 70% 以上通过,剩下的问题通过人工兜底或者后续针对性优化来解决。

6.2 观察"失败模式"的变化,而不是只看通过率

精简 Prompt 之后,失败模式往往会发生变化。以前可能因为规则太多导致模型"选择困难",输出模棱两可;精简之后可能变成偶尔漏掉某个字段,或者格式上有一点偏差。通过率可能差不多,但失败的性质不同。

漏字段、格式偏差这类失败,比"输出模棱两可"更容易修复,因为它们的原因更明确。你只需要针对性地补一条规则或者调整一下示例就能解决。而"选择困难"型的失败往往需要重新设计整个 Prompt 结构,成本高得多。

所以我在验证精简效果时,不仅看通过率,还会看失败案例的具体表现。如果失败模式从"复杂错误"变成了"简单错误",即使通过率暂时下降了一点,我也认为精简是值得的。

6.3 用不同模型交叉验证,避免过拟合

精简后的 Prompt 有一个好处:它更不容易过拟合到某个特定模型上。堆料式 Prompt 往往包含大量针对特定模型行为特点的"补丁",换一个模型就可能失效。而精简后的 Prompt 只描述任务本身,模型换了,任务定义没变,效果通常更稳定。

我习惯在精简完成后,用至少两个不同模型跑一遍测试集。如果两个模型上的表现差距不大,说明这个 Prompt 是"模型无关"的,质量比较高。如果某个模型上表现明显差,可能需要检查是不是精简过程中删掉了某个对该模型特别重要的指令。

7. 几个容易踩的坑和我的应对方式

7.1 减过头导致输出不稳定

做减法最容易犯的错误是减过头。把角色设定删了、示例删了、格式说明也删了,结果模型输出变得很不稳定,有时候格式对有时候格式错。

我的应对方式是:保留一个"锚点示例"。不管怎么精简,至少保留一个完整的输入输出示例。这个示例的作用不是教模型怎么做,而是锚定输出的格式和粒度。有了这个锚点,即使其他指令很简洁,模型的输出也能保持基本的一致性。

另外,格式要求不要省。任务描述可以精简,但输出格式的定义要保留,因为这是模型最容易出错的地方。格式定义用示例加简短说明的方式,通常两三行就够了。

7.2 把"精简"变成"晦涩"

另一个坑是精简之后 Prompt 变得太抽象,模型理解不了。比如把"请用简洁的语言回答,避免冗长的解释"精简成"简洁回答",意思虽然一样,但模型可能理解成"只回答关键词"。

精简的原则是删冗余,不删信息。每一条保留下来的指令都应该是清晰、无歧义的。如果一个指令删掉几个字之后变得模糊了,那就不该删。我通常会在精简后自己读一遍,问自己:如果我是一个不了解背景的人,看到这句话能准确理解要做什么吗?如果答案是否定的,就补回去。

7.3 忽略了 Skill 之间的依赖关系

如果你维护的是一个 Skill 集合,精简某个 Skill 的时候要注意它和其他 Skill 的依赖关系。比如 Skill A 的输出是 Skill B 的输入,你把 Skill A 的输出格式精简了,可能导致 Skill B 解析不了。

我的做法是:在精简之前先画出 Skill 之间的数据流图,明确哪些 Skill 之间有输入输出依赖。精简涉及接口的部分时,要么保持接口不变,要么同步更新下游 Skill。这个工作看起来麻烦,但比出了问题再排查要省时间得多。

7.4 没有版本管理,精简后无法回滚

精简 Prompt 的时候,一定要做版本管理。我见过太多人直接在原文件上改,改完之后发现效果不如以前,想回滚却找不到旧版本了。

我的习惯是:每次精简前先 commit 一版,精简后如果测试通过就保留,不通过就回滚。如果用的是 Git,可以给每次精简打一个 tag,方便对比。如果不用 Git,至少把旧版本复制到一个 backup 目录里。这个习惯看起来简单,但在关键时刻能省很多事。

8. 我在实际项目中的几点体会

做减法这件事,说起来简单,做起来需要克服一种心理惯性——总觉得写得多才安心。我刚开始精简 Prompt 的时候,每次删掉一段内容都会犹豫:万一模型在这个情况下出错了怎么办?后来我发现,这种担心大多数时候是多余的。模型的能力已经足够强,你不需要替它考虑所有情况。

另一个体会是:精简不是一次性的工作,而是持续的过程。模型在升级,你的任务在变化,Prompt 也需要不断调整。我现在养成了一个习惯,每个月回顾一次自己维护的 Prompt 和 Skill,看看有没有可以删掉的内容。通常每次都能找到一些"历史遗留"的冗余指令,删掉之后效果反而更好。

还有一点:精简后的 Prompt 更容易调试。当输出出问题的时候,你面对的不是一个几百行的复杂指令集,而是十几条清晰的规则。你可以快速定位是哪条规则导致了问题,修改起来也更有针对性。这个好处在长期维护中体现得特别明显。

最后分享一个实用技巧:如果你不确定某条指令是否必要,可以做一个 A/B 测试——保留这条指令跑一遍测试集,删掉再跑一遍,对比结果。如果两次结果没有显著差异,就删掉。这个方法比凭感觉判断靠谱得多,而且随着测试次数增加,你会对自己维护的 Prompt 里哪些内容是真正重要的越来越有感觉。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进