跳到主要内容

136 篇博文 含有标签「prompt-engineering」

查看所有标签

提示词组合:管理一组提示词,而非单一的最佳提示词

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数生产环境中的 AI 团队谈论提示词(prompt)的方式就像初级交易员谈论股票一样:总觉得存在一个“最好的”,而工作就是把它找出来。于是他们不断迭代——一个 Slack 线程,几行评估数据,产生一个新的赢家,推送到主分支,如此循环。其结果是一个承载了产品全部意图解析覆盖面的单一制品(artifact),针对一个固化的评估集进行了优化,而它距离 P1 级事故往往只差一次令人遗憾的修改。

错误在于“单一”这个词。提示词不是一种证券,而是一种配置(allocation)。同一个用户意图可以由几个变体很好地服务,每个变体都有自己的置信区间、各细分领域的性能以及对模型和语料库偏移的敏感度。正确的心理模型不是“找到最好的提示词”,而是“管理一篮子提示词,其构成本身就是产品”。量化金融在五十年前就弄明白了这一点,而其运营机制几乎可以直接无缝迁移。

Prompt 回滚不像代码:为什么 git revert 是错误的原子操作

· 阅读需 10 分钟
Tian Pan
Software Engineer

一位资深工程师将 Prompt 的更改通过 10% 的灰度发布(canary)推送到生产环境。到了第二天早上,灰度组的有用性评分(helpfulness score)下降了四个点,值班人员发现了这一点,团队做了每个团队都会做的事情——撤回提交(revert commit)并重新部署。仪表板没有恢复。第二天也没有恢复。三天后,一份复盘报告显示,看到糟糕 Prompt 的那一组用户仍然在看到退化的输出,因为他们的对话历史中现在包含了由已撤回的 Prompt 生成的助手回复,而模型正基于这些回复进行预测。提交已经消失了,但损害仍在。

这是 LLMOps 中“像对待代码一样对待 Prompt”这一建议悄悄跳过的部分。代码回滚是文本替换,用于恢复确定性的过去状态。Prompt 回滚必须处理一系列副作用——缓存、历史记录、评估基准、实验分组、下游契约——这些都是糟糕的 Prompt 已经印刻在生产环境中的。git revert 翻转了文本,但它没有翻转后果。

少样本示例造成的租户泄露:当你的提示词库变成跨客户数据存储库

· 阅读需 13 分钟
Tian Pan
Software Engineer

打开一个日益成熟的 AI 产品的生产环境系统提示词(system prompt),向下滑过角色描述,你几乎总能看到一个标有 # Examples## Few-shot demonstrations 的部分。这些示例非常出色——它们很具体,具有领域针对性,且精准地匹配了上季度评估集(eval set)中表现不佳的失败模式。但在仔细观察后,你发现它们其实也是真实的客户数据。来自真实账户的真实工单 ID。从支持会话中原封不动摘录的措辞模式。某个租户使用的内部产品代码,而其他客户群从未听说过。

把这些示例放进去的团队并不是粗心大意。这些示例进入提示词的方式与好示例一贯进入提示词的方式相同:有人从生产环境的追踪(traces)中挖掘出模型处理不佳的案例,挑选出最干净的现成示例,将其粘贴到系统消息中,看着评估分数上升,然后发布。这条从生产环境追踪到系统提示词的流水线,是现代 LLM 工程中最可靠的提示词改进闭环。但这也是团队在不知不觉中构建的一个结构性跨租户数据泄露渠道,而系统提示词已悄然变成了一个数据处理协议(DPA)从未涵盖的多租户数据存储库。

LLM 提示词中 “现在” 的五种定义

· 阅读需 13 分钟
Tian Pan
Software Engineer

一名客服代理告诉用户“根据我们截至今天的最新定价”,并引用了上季度的价格表。系统提示词正确地注入了 today is {current_date}。检索层提取了具有最高时效性评分(freshness score)的文档。模型回答得信心十足。每个组件都严格执行了其规定的任务,但用户得到了一个错误的答案,而值班工程师却无法复现这个问题,因为当他们在晚上 9 点回溯追踪记录(trace)时,“今天”已经变成了不同的一天。

这并不是一个罕见的 Bug。这是一种几乎存在于每一个生产级 LLM 流水线中的失效模式,因为“现在”隐式地存在于提示词的五个不同层级中,而这些层级是由不同的人在不同时间、针对不同的“现在”定义编写的。只要请求是从前端用户会话同步运行的,这些层级通常能达成一致。一旦请求被重放用于调试、在夜间进行批处理、在固定于 3 月的评估框架(eval harness)中运行,或者被排队并在一个小时后消费,各层级就开始产生分歧——模型产生了一个在提示词内部逻辑自洽但在外部环境中错误的答案。

Prompt 卧推:对“快乐路径”之外的提示词进行压力测试

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个在你的评估集(eval set)上得分 92%,但在真实生产流量中得分 60% 的提示词(prompt),并不是一个有 bug 的提示词。它是一个评估集在结构上无法发现 bug 的提示词。这种差距并非噪声,而是针对那些与提示词设计意图共享语域(register)、长度分布、语言和礼貌程度的示例进行优化的结果——而这些示例正是由编写评估案例的同一个意图所创作的。

真实用户不会配合你的设计意图。他们会发送三个词的片段、十二个段落的文章、作为问题粘贴的代码块、省略冠词的非正式语域、添加敬语的正式语域,以及你的 few-shot 示例从未涉及的语言查询。这些都不是攻击性的,这只是输入分布(input distribution)。如果你的评估集是由编写提示词的同一个人策划的,那么它几乎肯定与这种分布毫无相似之处。

缩小这一差距的学科不是“更多评估”,而是一种不同类型的评估——一个压力矩阵(stress matrix),它刻意改变你策划的集合中保持不变的维度,并对退化曲线(degradation curves)进行评分,而不是单一的准确率数字。称之为提示词卧推(prompt bench press):你不是在测试提示词能否完成工作,而是在测试随着输入变得更难,它是如何失败的。

采样漂移:当 Temperature 和 Top-P 变成团队内部的“口头传说”

· 阅读需 10 分钟
Tian Pan
Software Engineer

打开任何上线超过一年的 AI 功能的生产环境配置,你会发现一个考古挖掘现场。temperature: 0.7 是因为有人想让演示看起来不那么机械。top_p: 0.85 是因为一个客户抱怨输出太普通。frequency_penalty: 0.4 是因为 2024 年有那么糟糕的一周,一个现在已经退役的模型一直在重复自己。这些决定都没有记录。它们都没有针对当前的基座模型进行重新测试。它们在每一次请求、每一次评估、每一次 A/B 测试中运行,塑造着自原始工单关闭以来再也没有人会有意识选择的行为。

这就是采样漂移(Sampling Drift)。它是由于权宜之计而进行的采样器微调的缓慢积累,这些微调最初的理由已经消失,而其效果却在不断叠加。你配置中的数值并不是经过“调优”的——它们是过去事故的化石记录,被缩放到了你当前的流量规模。

它之所以不可见,是结构性原因造成的。你运行的每一次评估都是针对当前的采样配置进行评分的,所以头条数据看起来总是没问题。当 Temperature 值比基座模型落后两个版本时,不会触发报警。也没有日历邀请会提醒你“在本季度重新网格化采样参数”。这种衰减是无声的,直到有人运行了一个干净的实验,发现一个质量提升、Token 减少,或者两者兼而有之的机会,就摆在眼前,且不需要任何工程成本。

规范翻译税:当规范、提示词和评估发生漂移时

· 阅读需 12 分钟
Tian Pan
Software Engineer

一名 PM 用英文写了一份功能规范 (feature spec)。一名工程师将其翻译成带有惯用 LLM 模式的系统提示词 (system prompt) —— 思维链 (CoT) 脚手架、输出格式强制,以及一些涵盖规范中从未提到的失败模式的避险条款。一位评估 (eval) 作者打开同一份规范,冷读一遍,并根据自己的理解编写 JSON 测试用例。三周后,这三个产物各不相同,没人能说清楚一个回归到底是提示词的 bug、规范与实现的差异,还是从第一天就写错的评估。

这就是规范翻译税 (specification translation tax)。传统软件也有这种问题 —— PRD 与代码之间、代码与测试之间的差距 —— 但编译器和类型系统缩小了这种差距。AI 功能没有这种兜底保障。提示词是系统实际阅读的文档。评估是没人签署的合同。规范是没人执行的意图描述。每一项都是将同一意图翻译成不同的媒介,如果没有双向的一致性,行为就会通过那个最容易编辑的产物泄露进来。

入职缺口:为什么新工程师需要三个月才能上手 AI 技术栈

· 阅读需 10 分钟
Tian Pan
Software Engineer

一个拥有八年经验的后端工程师加入了你的团队。在一般的代码库中,到第三周他们就能开始交付功能了。但在 AI 层面,他们仍然在私信里问问题,而且你能预测出他们在问哪两位资深工程师。入职三个月后,他们终于被信任可以修改系统提示词(system prompt)了 —— 这并不是因为提示词有多难,而是因为没有人能告诉他们,哪些评估(evals)能捕捉到退化(regression),而哪些会直接放行错误的输出。

这在通常意义上并不是招聘问题或文档问题。AI 代码库带有一种隐藏的领域知识税(domain-knowledge tax),这种税不会出现在代码审查中,不会出现在 README 中,静态分析器也无法察觉。这笔税体现在入职时间、对同一批人的重复提问,以及最终团队悄悄分化为“能动它的人”和“其他所有人”。

冰封提示词:当你的团队不敢修改一个仍然奏效的系统提示词时

· 阅读需 15 分钟
Tian Pan
Software Engineer

每个成熟的 AI 产品最终都会演变成一个当前团队中没人能完全理解的系统提示词(system prompt)。起初它只是 40 个 token 的纯英文,20 个月后,它变成了一堵 4,000 token 的“高墙”,堆满了条件句、拒绝模板、格式规则、角色强化、边缘情况警告,以及一句没人能解释的关于周二的奇特句子。每一行都是为了应对特定的失败:客户投诉、法务发来的 Slack 消息、评估(eval)中发现的回归,或者在投资者演示期间出现的偶发 bug。写下第 37 行的工程师已经转岗到其他团队。写下第 112 行的工程师是一名外包人员,他的 Notion 文档已被归档。评估套件覆盖了提示词所主张行为的大约三分之一,但没人确定是哪三分之一。

于是,这个提示词以一种最糟糕的方式成为了系统的“承重墙”:它能用,团队也知道它能用,但团队已经不再碰它了。本该对提示词进行迭代的工程师,反而绕过它来处理变更——这里加一个后处理过滤器,那里加一个 few-shot 封装,或者做一个并行的“v2 提示词”并用特性标志(feature flag)关闭,以防有人哪天有勇气进行 A/B 测试来替换它。提示词不再是软件,而成了遗迹。一旦发生这种情况,提示词就不再是你用来改进产品的杠杆,而是塑造产品的约束条件。

负面提示词是代码异味:为什么系统提示词中的每个 “不要” 都是技术债

· 阅读需 11 分钟
Tian Pan
Software Engineer

打开任何已经上线超过三个月的生产环境 AI 功能的系统提示词(system prompt)。数一数其中的负向条款——“不要”、“绝不说”、“避免”、“在任何情况下都不”、“你绝不能”。如果计数达到了两位数,你看到的就不是一个系统提示词。你看到的是一个坟场。每一块墓碑都标志着一个特定的用户投诉、一个特定的事故报告,或者一条来自利益相关者的 Slack 消息,因为他们看到模型做出了一些令人尴尬的事情。团队在表面打了补丁后就继续前进了,现在这个提示词读起来就像一份被强行嫁接了人格的法律免责声明。

负向提示词是代码异味(code smells)。并非隐喻意义上的,而是字面意义上的。它们在提示词工程中相当于吞掉异常的 try/except 块、没有文档的配置标志,或者是 2022 年留下的 // TODO: refactor this。它们在某种程度上有效,直到它们失效。而且它们所掩盖的失败模式,几乎总是比它们被添加用来压制的那个失败更有趣。

策略文件:为什么你不应该把拒绝规则写在系统提示词里

· 阅读需 13 分钟
Tian Pan
Software Engineer

上个季度,一家金融科技初创公司的安全审核员在系统提示词(system prompt)中添加了四行内容。这次修改包含一条拒绝规则,旨在防止助手为公司未获得运营许可的司法管辖区提供具体的税务建议。这听起来很合理、范围明确且符合审计要求。该规则在周二上线。到周五时,评估套件显示在与税务完全无关的客户入职流程中出现了 7 个点的下降——模型开始对任何提及国家的提问都模棱两可,甚至包括“这个账户持有哪种货币”。产品团队撤回了修改。安全团队在下周以略有不同的措辞重新发布了它。三周后,同样的退化以不同的形式再次出现,而接下来的安全修改又破坏了另一个无关的流程。

这里的 bug 不在于措辞,而在于拒绝规则放错了位置。它被挤进了一个 2,400 token 的构件中,该构件还包含助手的对话语气、格式契约、任务指令以及其他六项策略条款——对其中任何一项的修改都是对所有内容的行为修改,因为模型无法分辨哪句话是策略,哪句话是风格。生产环境中的系统提示词之所以变成了一坨乱麻的单体,是因为三个正交的关注点伪装成了一个整体。没有将它们解耦的团队在每次修改时都在支付“集成税”。

Prompt 修改不只是措辞变动:将 Prompt 视为软件的代码审查规范

· 阅读需 13 分钟
Tian Pan
Software Engineer

周二下午,一个只有六行代码的系统提示词(system prompt)编辑出现在了一个 Pull Request (PR) 中。Diff 只是普通的英文。两位评审者扫了一眼新的措辞,觉得读起来更自然,于是点击了批准。PR 在不到一分钟内合并。到了周五,客服开始收到关于智能体的工单:它突然拒绝总结超过一定长度的文档,不再引用来源,并莫名其妙地在每句回复开头都加上 “Certainly!” —— 这种行为没人要求过,Diff 中也无法预见。

当一个花了十年时间学习如何评审代码的团队,在面对提示词这一产物时,竟然退化到了第一周的水平,结果就是这样。Diff 看起来 毫无害处,因为它读起来像英语,而人类正是用眼睛来审阅英语的。让代码评审发挥作用的规范 —— 运行测试、检查影响范围、对 “小改动” 保持适当的怀疑 —— 并没有悄然转化。措辞变好了,但行为变差了,直到用户发现之前,没人注意到。