大模型就是一台无状态的老虎机

大模型就像一台老虎机(slot machine),它的主要作用就是预测下一个 token。既然是预测下一个 token,那它就有概率失败。

假设一个复杂任务需要 10 个步骤,中间哪怕只有 2-3 步发生了 1% 的错误,最终结果的误差也会被放大得非常大。

怎么解决这个问题?核心是一个公式:

结果 = 模型质量 × 上下文质量

模型质量是我们控制不了的(除非动用钞能力),所以能下手的只有上下文质量。

无状态指的是,每一次新开一个对话,除了 agent 的内置提示词,大模型对于你的任务其实一无所知。你需要跟模型好好沟通,沟通的质量决定了模型的产出,而沟通手段主要靠提示词。

Context 管理的进化简史

Context 管理的发展大致经历了这么几个阶段:

  1. 提示词工程

    写提示词时,你要先假设模型是个什么角色,明确你的目标是什么,以及实现目标要拆成哪些步骤。

  2. 上下文工程

    后来演变成了上下文工程,你需要管理好自己的上下文——Spec、Skill/MCP,以及 system prompt。

  3. Agentic 阶段

    这一阶段又有两次演化:

    • Harness Engineering:搭建 harness,让模型可以自己验证,而不用你去纠正它。你的提示词质量不关键,关键是模型要能够自己验证自己,就算提示词的质量差一点,模型也可能通过 harness 自己纠错,最终达到你的目的。编写代码为什么大模型做得这么好,因为编译器就是最好的 harness。

    • Loop Engineering:本质上是在 Harness Engineering 之上建立一个自举的 loop,让大模型在这个 Loop 里自己提需求、自己解决问题、自己 review、自己验证,验证完之后自己体验,体验之后再重新走一遍这个循环。说白了,就是把 human-in-the-loop 从里面抽离出去。(token 消耗量巨大,但是人彻底解放了)个人觉得不是很适配 dirty 的生产环境。因为现实环境中的需求有非常多人类决策的 tradeoff,是这个老虎机预测不了的。(不是技术上不行,而是语料不存在)

为什么 Human 没法退出这个循环(human in the loop)

对 vibe coding(氛围编程)来说,做一些从 0 开始的项目时,我觉得 loop engineering 是比较合适的。但碰上复杂的遗留项目,这种做法其实就比较困难了:

  1. 很难把这套 loop 工程搭起来。光模型能自我验证就很难,即使有多模态,验证的效率也非常低下。

  2. 模型就像前面说的,是一台 slot machine——这次可能给你 roll 出一个 SSR,下次可能 roll 出一个比较差的东西。生产环境中 dirty 的东西太多了,需要人去判断、去决策。模型给的完美的实现反而会跟现有的系统不兼容。

  3. 既然剥不掉,人就得在两个地方使劲:模型开工前的对齐和收工后的验证。

人能下手的两个杠杆

我认为,如果你要提升提示词,其实只有两件事可以做。

第一,在模型开始干活之前,先跟它对齐,也就是达成一致,确保你跟模型的理解是一样的。这里有个最佳实践:用 “grill me”(或者 “grill me with docs”)。模型会反过来追问你很多问题,你去逐个回答,最终你和模型站在同一条理解的起跑线上——这时候它才算真正搞懂了你的需求。

第二,在中间过程里,尽可能给它提供足够多的信息。 围绕这一点,有几种补充手段:

  • 对于复杂业务:你可以给它一个 skill,里面封装了做事情的 SOP。SOP 是为了降低模型自行推理走歪,然后中间试错污染模型上下文。

  • 对于确定性的事情:你可以写一些脚本,保证同样的输入都有同样的输出。大模型是概率模型,没办法保证同样的提示词结果完全一样。如果把确定性的事情交给模型去推理,模型一定要能自我验证,否则就不要让模型去做。

  • MCP 则用来补那些模型靠当前上下文拿不到的数据。 比如你的系统依赖一些外部数据,你就尽量把上下文补全,模型自己会推理着完成。信息越足,它越有可能预测出正确的下一个 token。

最后,要保证模型做的事完全符合要求,最重要的是做事后验证,并且最好是模型自己可以验证。 你需要搭端到端测试;或者像现在的 Codex、很多 Agent 工具那样,提供了 computer use、browser use 这类工具,让模型在干完活之后,自己去指挥计算机操作 Web 界面、或者操作 iOS 模拟器来验证,靠截图和视觉分析。

如果有条件,其实可以不用这种纯视觉的方式去拿反馈。尽量用一些确定性的程序去验证,让模型快速 get feedback,那么它迭代的速度会更快。编译器和单元测试就是绝佳的示例。

总之一句话:在老虎机这样的模型之下,人最能做的事情就是事前 grill、事后验证。只要你在考虑这两件事的时候,尽可能保证塞进模型 context 里的东西都跟这个任务相关,你的 vibe coding 就算做得非常好了。

实战:分享我个人的一些经验

接下来再给大家分享一些个人在平时工作中用到的一些小技巧。

1. 直接引用代码。 实际工作里,很多时候你是在已有系统上加新功能或者改 bug。这时候提供上下文,如果能直接引用对应的代码片段、类名或函数名,就别去打字描述了,直接选中对应的代码、加到对话框里就行。模型会自己去分析,你也不需要说太多这段代码是怎么工作的,它可以自己推理,这非常关键。很多人喜欢说一大堆冗长的话,但其实你说对了还好,要是说得跟代码描述的不一样,很可能反而会影响模型的推理。我最近的一段提示词只有一句话:照着添加 xx 功能。(一定要把相关的代码片段、协议文件、需求文档、配置等信息都引用给模型窗口。别的多余的字一个不用说,下次你可以试试)

2. 直接发界面截图。 觉得界面有问题,别费劲去描述这个界面,直接把界面截图发给模型,同时圈出你要修改的点。对支持多模态的模型来说,这个做法非常有用。你用文字描述的东西在沟通时可能说不清楚,一图胜千言!

3. 直接发错误日志或截图。 同样,对于报错信息,也别尝试去描述它,而是直接把错误日志或者报错截图发给大模型,让它去推理。

vibe coding 其实不是要求我们把提示词写得多么详细、像教一个实习生一样,而是把任务相关的、有帮助的、有利于它理解问题并得到更好结果的上下文,全部发给模型就行。接下来你只要给它一个目标——“你帮我完成它,解决 bug 或者添加功能”——就可以了。所以你更多是关注宏观的上下文质量和目标,而不是具体实现细节,不要试图去教模型做事,模型有自己的脑子。

维护项目的上下文(context)也是同理:不要去记录那些模型本来就能通过 grep 或其他工具搜到的信息。比如要维护一份统一的术语表,像 DDD 里提到的 ubiquitous language,这种东西其实没必要手动维护一份词汇表,你可以让模型自己来维护。比如你可以在类名上尽量写一些中文注释,那么你跟模型讨论的时候,它就能通过这些注释找到对应的代码。但这个有时候还是不够准确,尤其是项目复杂之后,所以能直接引用代码会更好。

收尾

理解了大模型本质上是 stateless slot machine,那现在我们学的所有这些新概念 skill/mcp/harness engineering 都容易理解了。让下一次 roll 出 SSR 的概率变高,如果 roll 一次不行,就 roll 多次,并且让模型知道自己 roll 出来了 SSR。