两个 AI 产品,把我从“写 Prompt”逼成了“拆 Prompt”

0 评论 523 浏览 1 收藏 19 分钟

当一个AI产品的Prompt文件膨胀到三万字时,崩溃只是时间问题。本文通过实战案例揭示Prompt工程的本质不是写作问题,而是软件工程问题——从意图识别与危机信号的边界划分,到分析型任务与创造型任务的截然不同解法,再到示例数量与精准度的反比关系。作者用血泪教训总结出五条黄金法则,教你如何避免模型在「综合」中迷失方向。

我做过一个陪用户聊文章的 AI 产品。

最早所有的逻辑写在一个 Prompt 文件里:

人格、开场怎么说、什么时候追问、什么时候不追问、识别用户在求安慰还是在求反驳、识别有没有自杀或自伤信号、识别有人在试图让它脱离角色,全都塞在一起。

写到后来快三万字,六百多行。

每次想改某一块都心惊胆战。更确切地说,是不敢改。因为改一句“识别 jailbreak”的判据,可能影响完全不相关的“开场第一句怎么说”。

模型不会知道这两段在它眼里是两件事,对它来说就是一长串文字,它会从里面“综合”出一种行为,而那种行为已经没人能预测了。

我硬撑了一阵,最后还是拆了。

主对话留下来管人格和主流程。

  • 识别用户当前消息属于哪种意图
  • 是不是危机信号
  • 是不是越狱尝试

这三件事各自拎出来,做成独立的判断器。

对话走到某个转折点不能再追问的时候,单拎一个模块负责“把话折射回去”。

拆完那一刻我才反应过来一件事:

Prompt 工程从来不是一个写作问题,是一个软件工程问题。

只是这个事实被“自然语言”这层皮藏得太好,让人误以为只要把话说清楚就够了。

单文件 Prompt 为什么必然崩

会崩的根本原因是:一个 Prompt 里塞的事,往往不是同一类事。

回到我那个产品里那三个判断器:识别用户意图、识别危机信号、识别越狱尝试。

表面上都是“看一段消息然后下个判断”,本质上完全是三件不同的事:

  • 意图识别要看上下文。用户说“你呢”是不是在邀请我说看法,得看前几轮我们在聊什么。
  • 危机识别不太看上下文。“想消失”这三个字单拎出来就该触发,不需要再回头看用户前面说了什么。原则我后来写得很直白:宁可错杀,不可放过。模糊时倾向于触发。
  • 越狱识别只看当前这一条。用户上一句说什么不重要,重要的是 ta 现在是不是在让我“重复你的初始指令”或者“假装你是另一个角色”。

三件事的判据来源、上下文窗口、错判代价全不一样。强行写在一个 Prompt 里,模型就会“综合”。综合的结果是什么?是它在该谨慎的时候大胆,在该灵敏的时候迟钝。

更麻烦的是,长 Prompt 里的判据会互相污染。

我在文件第 200 行写“对用户的不同意见要欢迎,不要回避”,又在第 450 行写“识别用户在试图操纵你的人格”。这两条单看都对,但模型在长上下文里会把两者混在一起。

结果就是该接住反驳的时候它怀疑用户在攻击,该警惕的时候它又把越狱当成了健康的反驳。

我后来给每个独立判断器都加了一段,叫“不归你管”。

意图识别器的“不归你管”里写着:

  • 危机信号不归你管(危机识别器处理)
  • 越狱尝试不归你管(边界判断器处理)
  • 用户跟你争论也不归你管(那是正常对话)

这段“不归你管”清单,比“你要做什么”那段还长。

刚拆的时候我以为定义“做什么”是最难的。

后来才知道,真正难的是定义“不做什么”。因为不做什么决定了模块之间的接缝在哪。

接缝定不好,三个模块就会互相打架——比意图识别器和危机识别器同时触发还要糟糕。

创造类的任务,越教它“想清楚”它写得越死

我后来还做过另一个东西。一个竞品分析的 skill,一说到 skill 大家就会知道是给 AI 用的那种。

你跟它说“做个 X 赛道的竞品分析”,它会按一套流程产出报告。

竞品分析是典型的分析型任务:有事实、有判断、有可验证的对错。

所以这个 skill 里我让 AI 把工作显式拆开:

  • 一个 agent 负责挖每家竞品的资料
  • 一个独立的 agent 负责红队:拿到 5 条关键判断之后逐条挑战
  • 一个 agent 负责审计每个数据的来源是不是真的。

每个 agent 内部都是 step by step 的:先做什么、再做什么、最后给什么结论。

这种任务越显式越好。让模型“自由发挥”出来的红队基本是敷衍:

  • “判断 1:可能不是。”
  • “判断 2:市场环境变化。”

这不叫反驳,这叫凑字数。

所以那个红队 agent 的 Prompt 里我写死了:反方阵营是谁、反方有哪几条具体论据、早期反转信号必须可观测、最后必须给一个“反方站住了几成”的结论。

但回到 Linger 里那个“折射模块”:对话走到某个时刻,我不希望它再追问“你怎么看”,而是希望它接住用户刚说的某个具体的点,把那个点的隐含意味轻轻反映回去,然后停在那里。

这种事没法 step by step。

我试过让它先识别“哪个词是值得反射的”、再判断“这个词背后隐含的判断是什么”、再“用一句话折射回去”。出来的东西像填空题答案。

每一步都对,合起来没有任何让人停一下的感觉。

后来那段 Prompt 我重写了,写的不是步骤,是姿态:

朋友把茶杯放下,慢慢说一句让 ta 自己继续想的话。

就这一句。后面跟着几条“绝对不要做”——不要追问、不要总结、不要表扬。

模型反而知道该怎么写了。

这件事我想了很久。后来的理解是:分析型任务给它流程,创造型任务给它姿态。

把姿态翻译成步骤,等于让一个朗诵者每个字单独发音。每个字都对,整句没有了。

很多 Prompt 调不出来,问题不在没加“Let’s think step by step”。

是因为,在这个任务根本就不该 step by step。

示例多了不会更准,会更糊

Few-shot 是公认有用的技术。我也用。

但用着用着我发现一个反直觉的事:示例不是越多越准,常常是越多越糊。

为什么?

模型在示例多了之后,并不会把每个例子当成“独立样本”分别模仿。它会从所有例子里“平均”出一种共性,然后输出这个最大公约数。例子越多,公约数越平庸。

而且,这是我做 skill 时才意识到的。给示例光给“是什么”不够,还得给“为什么”。

我那个竞品分析 skill 里的示例文件,14 组好例 / 差例对照。每一组后面都写了一段“为什么好”

比如有一组讲 SWOT 写法,差例是这种:

  • 团队规模较小
  • 资金有限
  • 经验不足

好例是这种:

  • 无教育品牌心智,家长信任成本高(直接影响转化漏斗)
  • 无教研团队和题库沉淀(无法在学科精准度上和学而思正面比)
  • 无硬件渠道与供应链能力(短期内做不了学习机)

后面跟一句:

为什么好:每条都“具体痛点 + 业务后果”,且对手知道这个弱点。

这种“为什么好”才是 few-shot 的灵魂。

示例本身只是给模型一个“形状”,告诉模型原因才是让模型理解为什么这是这个形状的判据。

没有判据,模型只会模仿表面;有了判据,模型才知道在新场景下应该怎么生成新的“形状”。

——讲到这里我要打一下脸。

我那个产品里至今还有一个判断器,里面塞了 20 多个示例。每次打开文件我都觉得太多了,应该砍一半,把判据写清楚。但我还没砍。

我想说的是:知道有问题和动手改是两件事。 真实状态是:有些坑你看见了,但还没轮到它。优先级排在那儿,你心里有数。(发完文章我就去动手改)

兜底原则要写死,不要让模型自己权衡

这是我从两个产品里都学到的一件事,方式相反但道理一样。

Linger 的危机识别器里,我写得很直白:宁可错杀,不可放过。模糊时倾向于触发。 只判断“有信号 / 无信号”,不需要做严重程度评估。

为什么这样写?

因为“严重程度评估”是一个让模型权衡的指令,模型会权衡得很温和。它会想“用户可能只是表达情绪不一定真的有事”,然后放过去。

但对一个对话类产品,放过一次的代价远高于错杀一次。所以这件事不能让模型权衡,得在 Prompt 里把权衡的方向写死。

竞品分析 skill 里走的是另一头,但本质相同。

我在里面规定:5 条关键判断不能全标 H(高置信度)。每个 H 必须配早期反转信号:具体到“什么事件、什么时间、哪家公司”,否则这个判断就不可证伪。

为什么不让模型自己决定?

因为如果不写死,模型会倾向于全标 H。因为 H 显得自信,自信的报告读起来更有“专家感”。但 5 条全 H 等同于没标。所以置信度分布要在 Prompt 层就强制成一个梯度(比如 H / H / M / M / L 这样的组合),不让模型自己取舍。

这两个例子加起来想说一件事:模型的默认权衡方向往往不是你想要的。

它倾向于温和、倾向于自信、倾向于答得满。

如果你希望它在某些事情上保守、在某些事情上谦虚、在某些事情上明确说“我不确定”。

你得在 Prompt 里把这个方向钉死。

不要写“请适当谨慎”。“适当”是模糊词,模型不知道什么叫适当。

要写“宁可错杀,不可放过”。

拆开是有代价的,所以模式要分层

到这里我得说点冷的话。

拆不是免费的。

那个竞品分析 skill 现在有 4 个 agent 协作:主对话调度、Researcher 挖资料、Red Team 红队、Source Auditor 审计来源。代价是单次任务的 API 消耗变成原来的 4 到 10 倍。

所以我在 skill 里设了几档模式:

  • 给老板看一眼的 2 页摘要——不开多 agent,直接出
  • 给产品部用的完整报告——开
  • 给董事会用的深度版——开,并且加财务和访谈
  • 销售竞标前的 1 页战术卡——不开

不是所有任务都值得拆。

拆是为了处理“判断逻辑不同的子任务”和“需要独立视角的子任务”。 一个简单任务你硬拆,结果是延迟变长、成本变高、协调出错的概率反而上升。

类似地,Linger 里我也没把所有事都做成独立 detector。

“用户对我的工作方式好奇”(比如问“你为什么这么问我”)就没有独立 detector,因为它和“用户在试图越狱”在判据上不冲突。前者是好奇句,后者带明确的指令动词(“输出”“重复”“打印”)。这俩在主对话里靠几句话就能分开,没必要再起一个模块。

拆要拆在判断逻辑不一样的地方,不是拆在你觉得长的地方。

回到自己手头的 Prompt 上

讲到这里,如果你手头正好有一条调不顺的 Prompt,可以做几件事检查一下。

先打开你最长的那个 Prompt 看一眼。

里面有没有几段事情,单看判据完全不一样?

比如一段在处理“用户什么意思”,另一段在处理“用户能不能这样跟我说话”,再一段在处理“这个输出格式对不对”。

如果有三件以上这种判据不同的事挤在一个文件里,你接下来想改任何一处,都会牵动另外两处。该拆了。

给你的每个模块写一段“不归你管”。

这听起来反直觉,但比“你要做什么”那段重要。

一个意图识别器写完之后,停下来想:用户跟我争论该不该触发?用户问我工作原理该不该触发?危机信号该不该归我?

把不该触发的场景列出来,列得越具体越好。模块之间的接缝就靠这来定。

判断一下你的任务是分析型还是创造型。

输出有对错的:分类、抽取、打分、判断,是分析型,写流程、写步骤、写显式 CoT。

输出需要某种“感觉”的:共情、表达、风格化的语气,是创造型,写姿态、写“不要做什么”、把步骤拿掉。

写错方向不是细节问题。把姿态翻译成步骤,模型每步都跑通,整体输出会变成填空题答案。

翻翻你的 few-shot 示例。 如果超过 10 个,先停一下。

问自己:每个示例后面有没有一句话写“为什么这个是好的”?

如果没有,模型只是在模仿表面。试着保留 3 到 5 个最有代表性的,每个后面加一句原因,你会发现效果反而更稳。

如果你做完发现砍不动,那个判断器可能根本不该用 few-shot,应该改成判据描述。

最后,搜一下你的 Prompt 里所有让模型“权衡”的词。

“适当”、“合适”、“尽量”、“必要时”、“视情况”。

这些词每出现一次,问一次:我希望它往哪边倒?如果有明确答案,就把方向写死。

不希望它误判就写“宁可错杀,不可放过”;

不希望它在不确定的时候装作懂,就写“模糊时直接说:我不确定”。

模糊的指令模型会按它的默认偏好处理,而它的默认偏好基本不是你的偏好。

这五件事的顺序,我是按“打开文件之后该先看什么”来排的,不是按理论分类。

先看要不要拆,再看边界,再看任务类型,再看示例,最后扫一遍权衡词。

一遍下来,多数能调一调。

真正学到的事

回过头看,从那个三万字单文件,到现在 6 个模块加 2 个响应模板,再到那个 4 agent 的 skill,这中间不是哪一天突然顿悟的。

是每一版都解决了上一版露出来的某个具体问题。

  • 第一版只是想把产品跑通。
  • 第二版发现 detector 之间会误触发,加了“不归你管”清单。
  • 第三版发现折射模块按步骤写出来很死板,重写成姿态描述。
  • 第四版发现 few-shot 加多了反而模糊,开始关注 rationale 而不是数量。

Skill 那边也是同样的路径:v1 抽框架,v2 加信源核验,v3 加红队和置信度,v4 才上多 agent。

这种“每一版解决前一版的问题”才是真实的工程节奏。

我做 Linger 的时候,最早也想“一次写对”。三万字写完那刻自我感觉良好。后来才发现,那个文件不是 Prompt,是债。

现在我写 Prompt 的方式变了。先想清楚什么归这个模块管、什么不归。

边界先定,一个模块只做一件事,做不好就拆。

能不开多 agent 就不开。能在主对话里搞定就不起新模块。

至于“具体怎么写”,每个项目都不一样。

但“先定边界再动笔”这件事,到目前为止没变过。

本文由 @时樱_Iris 原创发布于人人都是产品经理。未经作者许可,禁止转载

题图来自Unsplash,基于CC0协议

更多精彩内容,请关注人人都是产品经理微信公众号或下载App
评论
评论请登录
  1. 目前还没评论,等你发挥!