跳到主要内容

30 天 Prompt 见习计划:当“阅读代码”失效时,如何入职工程师

· 阅读需 13 分钟
Tian Pan
Software Engineer

一位资深工程师在周一加入你的团队。到了周五,他们已经交付了一个涉及 11 个文件的 TypeScript 重构,并在通过评审时仅有两个小细节(nits)需要修改。两周后,这位工程师打开了路由智能体(routing agent)的系统提示词——240 行指令、三个编号的示例块、四个“绝不允许”子句,以及底部一段读起来像道歉的段落——然后盯着它看了一个小时。他们无法告诉你如果删除第 87–94 行会发生什么。六个月前写下这些内容的工程师也无法告诉你。

这是没人会写在入职文档里的鸿沟。一个重度依赖提示词的代码库看起来像是个代码库:它存在于同一个仓库中,运行相同的 CI,并在相同的 PR 中接受评审。但它的语义却存在于别处:存在于一个团队中没人构建的模型观察到的行为中,针对的是一个没人能完全枚举的输入分布,其失败模式表现为添加一句话的 PR,而不是错误报告。传统的代码阅读工具——类型、签名、测试、命名——几乎不起作用。一个试图“阅读代码”的新员工无法了解为什么每一行都在那里,而一个只交给他们 Notion 文档和 Slack 频道的团队,实际上是在默认将入职培训“外包”给提示词的最初作者。

解决办法不是提供更好的文档,而是一个课程:一个结构化的 30 天学徒制,教会工程师像推理函数一样推理提示词——通过询问其行为,而不是解析其文本。以下是这样一套课程的样子,团队为了使其运行必须维护哪些产出物,以及当这些产出物不存在时每个团队最终都会陷入的失败模式。

为什么“阅读代码”对提示词失效

一个 200 行的 TypeScript 模块拥有类型签名、公开 API、一套单元测试,以及包含详尽描述性提交消息的 Git 历史记录。读者可以从命名推断意图,从类型缩小范围,并通过运行测试来重现行为。在系统提示词中,这些信号都不存在。文本就是整个产出物。在“提示词做什么”和“它怎么做”之间没有界限。

更糟糕的是,提示词的行为是整体性的。为了“语气”插入的一句话可能会悄悄削弱十行前设置的约束。一个新的少样本示例(few-shot example)可能会使输出偏离一个罕见但重要的边缘情况。在顶部添加的“保持简练”可能会与底部的“务必引用来源”产生互动,这种互动只有在团队没想过测试的输入下才会显现。最近的从业者文章为此起了一个名字——指令遵循偏移(instruction adherence drift),即随着提示词变得越来越复杂,模型会逐渐降低关键约束的优先级——而这对于阅读静态文本的人来说是不可见的。

差异历史(diff history)也救不了你。典型的提示词修改提交消息通常写着“修复路由边缘情况”,只有一行描述,且没有指向触发该修复的输入的链接。今天阅读第 142 行的工程师无法知道这一行的加入是因为三个月前,一个企业客户的税务报表上传导致智能体静默截断了其响应。语义存在于失败中,而不是代码行中。

因此,如果向一名新工程师展示提示词并告诉他们“读一下”,就是在要求他们去做一件连提示词最初作者在不重新运行评估(evals)的情况下都做不到的事:预测每条指令换回了什么价值。静态文本不是规范,它是过去失败的残留物。要针对它进行入职培训,你必须教授这些“残留物”,而不是文本本身。

第一周:阅读失败案例库,而不是提示词

学徒期的第一天不是打开提示词文件,而是打开“失败案例库(failure gallery)”——这是一个经过整理的记录,包含了提示词表现糟糕到必须进行修改的每一个生产环境输出,并配有修复它的修改方案。每个条目由四个部分组成:触发失败的输入、模型生成的错误输出、解决该问题的提示词差异(diff),以及现在用于防止回归的评估案例。

按顺序阅读,这个案例库会教会工程师提示词“对手”的轮廓。他们会了解到,这个路由智能体在历史上一直会被混合语言输入搞混。他们会了解到,当之前的指令被放宽时,模型开始过度引用。他们会了解到,团队曾经发布过一个“更具对话性”的调整,结果破坏了工具调用(tool-call)格式,且花了一周时间才检测到。到本周末,工程师没有阅读提示词的任何一行文本。他们将其阅读为一系列它必须抵抗的力量(forces it had to resist),这与它的实际语义更加吻合。

这不是大多数团队的入职方式。更常见的模式是给工程师一个描述提示词预期功能的 Notion 页面,将文件链接给他们,并指派一名资深工程师回答问题。Notion 页面总是过时的,而且描述的是意图而非行为。资深工程师变成了机构记忆的单点故障。新工程师将提示词视为一个静态产出物而非控制系统来学习,并且无法继承对哪些行是“承重行”的直觉。

建立失败案例库并非毫无成本。它要求在修复落地之前,每一个提示词事故都要产生记录,且记录是可搜索和带标签的,还需要有人进行整理,以免案例库沦为一堆半记不清的错误报告。投入建立这一产出物的团队将其描述为防止重复错误的机构知识——这种东西你只需要建立一次,但无法廉价地事后补救。

第二周:消解 Prompt 并观察评估套件

第二周是学徒期的技术核心。工程师会得到一个可以运行的评估套件——运行速度快到可以在几秒钟内完成,确定性足以进行运行对比——并被要求进行消融实验。挑选一个段落。删除它。重新运行套件。阅读分数差异。恢复该段落。再选另一个。

这是学习 Prompt 中哪些行是“承重”的唯一可靠方法。如果一行代码在被删除后,导致三个评估类别的分数各下降了 8 分,那么这一行就在发挥实际作用。如果删除一行没有产生可衡量的变化,那么它要么是多余的,要么是它的保护作用已被后来添加的另一条指令悄悄吸收了。工程师通过尝试来学会区分这两者。

这种技术反映了成熟的 Prompt 团队在回归测试中已经做的事情——衡量单个指令的贡献,而不是将 Prompt 作为一个整体来衡量。将其应用于入职培训的新颖之处在于,消融是一种 学习 工具,而不不仅仅是维护工具。一名花了 5 天时间删除段落并观察分数变化的工程师,会对 Prompt 的解剖结构产生一种直觉,这是任何阅读都无法产生的。他们会了解到,两条看起来相似的指令可能有截然不同的影响,指令的位置很重要,示例有时比规则更有效,而且 Prompt 很少像其作者认为的那样写得严丝合缝。

这里的评估基础设施成本不容小觑。评估套件必须在几秒钟而非几分钟内运行——缓慢的评估会破坏课程所依赖的迭代循环。套件必须密集地覆盖 Prompt 已知的失败点,以便消融承重指令时确实能引起数值变动。而且评估案例必须足够便宜,以便工程师一天内可以运行几十次,而不会烧掉团队的 API 预算。已经构建了集成 CI/CD 的 Prompt 回归套件的团队已经拥有了大部分此类基础设施;没有构建的团队会在第二周发现,在学徒期可以运行之前,他们还有数月的平台工作要做。

第三周:观摩 Prompt PR 并重构推理过程

到第三周,工程师已经将 Prompt 内化为一种行为,而不仅仅是文本。接下来的技能是阅读和编写 变更——这正是以 Prompt 为核心的代码库的社交和评审层面所在。

练习结构如下:拿一个来自其他工程师的公开 Prompt PR,阅读标题和描述,但 不要 看 diff 或评论列表。写下你认为该变更的作用、你认为它存在的原因,以及你认为它正在修正的失败模式是什么样的。然后阅读 diff。再阅读评论。将你的重构过程与实际的推理过程进行对比。

这种方法之所以有效,是因为一个好的 Prompt PR 有特定的格式。标题命名失败模式。描述链接到该变更针对的评估案例,并展示变更前后的数值变化。Diff 是微小且精准的。评审 PR 的人正在做的正是学徒正在接受训练的重构工作。将其作为一种练习——随后可以看到答案——将原本需要 6 个月的接触时间压缩成了一周的刻意练习。

这种做法所依赖的产物同样至关重要:PR 必须真实地承载推理过程。在一个 Prompt PR 只有一行描述且没有评估差异的团队中,学徒将无法进行重构对比。“每次 Prompt 变更都要引用它所防止的失败”这种纪律不是可选的;它是代码库能够携带足够信息供新工程师从过去的变更中学习的唯一方式。

第四周:通过评估门禁交付真实的修改

学徒期以工程师交付一个真实的变更而结束。从失败案例库中挑选一个小的失败案例——理想情况下是那个一直在等待有人处理的案例。提出一个 Prompt 修改方案。在本地运行评估套件。将行为差异(behavioral diff)提交代码评审。

关键部分是 行为差异。PR 描述不会说“我让 Prompt 变得更清晰了”。它会说“我添加了以下指令。评估套件显示多语言类别的分数提升了 4 分,其他 12 个类别没有变动,以及我正作为后续任务跟踪的一个长上下文案例中出现了一个新的失败。”评审人不需要阅读 Prompt 并判断其文笔。他们被要求批准一个效果已经被衡量的变更。

这就是“Prompt 即代码”的真实含义。不是指 Prompt 存在于仓库中(它们一直都在)。也不是指它们有版本历史(它们一直都有)。而是指对 Prompt 的变更受到可衡量的行为信号的限制,就像对函数的变更受到测试的限制一样。完成第四周学习的工程师已经证明,他们可以在不依赖原作者提供上下文的情况下完成 Prompt 变更,这正是传统代码库对自主性设定的门槛。

当这些产物缺失时,什么会崩溃

该课程的质量取决于它所依赖的四个产物:一个精心整理的失败案例库、一个快速的评估套件、一个将指令与所防止的失败联系起来的决策日志,以及一个将行为差异视为主要评审信号的 PR 文化。去掉其中任何一个,学徒期就会崩溃。

一个没有失败案例库的团队通过一对一的口头故事会来进行入职培训。原作者带着新工程师进行口头讲解。六个月后,作者离开了团队。下一位工程师由听过故事的人通过二代知识进行引导。即使文件本身没有变化,Prompt 的语义也会随着人员轮换而衰退。

一个没有快速评估套件的团队无法将消融作为学习工具。工程师猜测哪些行重要,交付一个删除了其中一行的变更,两周后生产环境中出现了回归。消融建立的本能——你可以通过扰动来探查 Prompt 的结构——被另一种本能所取代,即 Prompt 是充满神秘感的,除非万不得已,否则你必须心怀敬畏,不要触碰。

一个没有决策日志的团队最终会得到单调增长的 Prompt。每一条指令都是神圣不可侵犯的,因为没有人能说出它是在防范什么。新工程师防御性地添加自己的指令,从不删除别人的指令,随着累积的覆盖和反覆盖以任何单次编辑都无法预见的方式相互作用,Prompt 随着时间的推移逐渐变得语无伦次。

架构方面的启示是那些无法在幻灯片上一一列出的:以 Prompt 为核心的代码库需要传统代码库不需要的 学习界面,因为 Prompt 的语义存在于观察到的行为中,而不是静态文本中。你可以拥有业界最整洁的 Prompt 文件,但如果你没有构建周围的基础设施来使其行为可教,你仍然无法引导任何人入职。学徒期是可见的部分。失败案例库、评估套件、决策日志和评审文化是不可见的部分——正是这些部分决定了你的团队是否能够超越创始人而持续成长。

在你需要它之前,先构建学习基面

建立学习基面(Learning Surface)最困难的时刻,正是当你发现自己需要它的那一刻——当 Prompt 的原作者递交辞呈,而团队意识到他们拥有的那个 240 行的文件,在功能层面上,其实并不属于他们。最合适的开始时机,是在你发布了第一个在模型升级中幸存下来的 Prompt 的第二天。在这一天,该 Prompt 已经赢得了拥有失败案例库、评估套件和决策日志的资格,因为它已经积累了足够的组织价值,以至于失去其背后的上下文信息将代价高昂。

如果你的团队现在正盯着一个 Prompt,却无法回答“为什么第 87 行在这里”,那么你遇到的并不是文档问题。你面临的是学徒传承问题,而解决这一问题的课程,将从那些你尚未构建的产物开始。

References:Let's stay in touch and Follow me for more thoughts and updates