从过程确定性到结果确定性:AI 时代的另一种安全感

即使在2026年,把AI从demo做成产品也不是一件容易的事。比如中翻英,大家都觉得早就被LLM解决了,不就是调个API的事情嘛。但我们最近因为要把Superlinear Academy社区加入一个中翻英自动同步的功能,才发现开发体验这么差。

这个问题的核心在 AI 的输出有很多不确定性。比如一个帖子太长了,AI会偷懒,前面正常翻译,后面开始缩写。或者它会脑子短路,开始输出还是英文,中间非要夹几个中文。或者在格式里做一些小手脚,比如丢了个粗体。或者它可能会超时,输出一半就卡在那儿,直到挂掉。

为了克服这些不确定性,我们就要在程序里面做很多细节处理。比如如果帖子太长,就要分几段分别调用API,最后再拼接起来(Wide Research)。但这会带来另一个问题,不同段之间的术语未必统一,所以我们还要进一步设计工作流,来保证同一个中文术语第一段跟第二段之间不至于翻译成两个不同的英文单词。最后我们还得加一个检查,如果输出还有中文字符,就需要再翻译一遍。为了解决超时的同时避免重复翻译,我们还要做断点续传,只把失败的那一小部分翻译,回头再插进去。

用这样的方式,我们确实大幅提升了成功率,保证即使对社区里面很长的帖子,AI也能正常翻译。但整个感觉就是累。我们90%的时间都没有花在怎么让AI翻译得更好,而是用workflow跟orchestration来给AI擦屁股。而且到后来,因为总会出各种意外情况,很多只出现一两次的问题我们就没修了,因为感觉永远修不完。总之完全没有感觉到生产力的提升。还不如调以前的机翻API。

后来我们换了一种完全不同的思路问题反而解决了。但在介绍具体怎么做之前,我想先解释一下我们对这个问题成因的更深一层的思考。

Agent调用的四层结构

像前面提到的,调用AI的API不是调用完了就甩手不管了这么简单。它需要做很多配套的事情。而这些事情从集成的角度来看可以分成四层:

  1. 模型层:我们是用Claude还是GPT?用Opus还是Haiku?用什么Reasoning Effort?
  2. 协议层:用Chat Completion API还是Response API?用MCP还是RESTful API?Rate Limit怎么解决?JSON Mode要不要开启?当我们说调用API的时候,大多数情况下我们指的是协议层。
  3. 运行时层:状态怎么管理?工具怎么调用?文件的内容怎么给AI?权限怎么控制?用多少并发?这一层不是传统意义调用API的开发内容,但是但凡想要把AI稳定用到生产环境,这是绕不过去的一层。
  4. 契约层:到底什么样的标准算成功?比如拿到AI的结果之后做什么检查?Guardrail怎么设?什么时候要引入人工干预?怎么保证不违反社会主义核心价值观?这一层决定了我们能不能信任AI的输出,并且真的把它用于生产。

一说到AI产品开发,大家讨论最多的是协议层。但实际的开发过程中,最花时间的反而是运行时层。这是因为协议层和传统的API调用不一样,LLM引入了太多的不确定性,而这些不确定性都需要运行时层来吸收和处理。问题是,运行时层跟业务逻辑没什么关系。无论是做翻译、代码生成、还是客服机器人,我们都要处理偷懒、拼接、上下文管理、并发控制这些事情。这意味着每个团队都在重复造轮子。所以一个自然的想法就是:能不能把运行时层外包出去?

这件事没那么简单。不同模型的 failure pattern 是不一样的。有些模型遵循指令的能力很强,但容易在长文本上偷懒;有些模型创造力好,但格式控制一塌糊涂。针对不同模型,我们擦屁股的方式也不同,尤其是长尾failure pattern更是如此。因此运行时层很多时候是针对模型高度定制化的,也就很难复用,外包更无从谈起。

但最近有一件事情让这个局面有了改观:Claude Code 本身不是开源项目,但越来越多的模型提供商开始主动兼容。Kimi、DeepSeek、GLM 都提供了官方接口,只要改几个环境变量,就能让 Claude Code 在后台调用这些模型。这件事很有意思。它意味着 Claude Code 已经超越了工具本身,变成了一种可复用的东西。

更重要的是,当模型提供商宣称兼容 Claude Code的时候,他们实际上做的事情是:把自己模型的 failure pattern 适配到 Claude Code 的预期行为上。换句话说,擦屁股这件事没有消失,但擦的人变了——从我们开发者变成了模型提供商。他们为了进入这个生态,必须确保自己的模型在 Claude Code 的运行时里表现稳定。(这个讨论也对其他类似的工具比如Codex/Cursor Agent也适用,因为它和Claude Code的命令行接口也非常类似,适配很简单)

换言之,Claude Code/Codex/Cursor Agent 正在成为一种可以复用的 Agentic Runtime。

这就解决了前面提到的长尾问题。那些零星出现的 edge case,一个团队修不完,但整个生态可以。每一个想兼容 Claude Code 的模型提供商,甚至包括Anthropic本身,都在帮我们填坑。所以一个新的思路就是,对于翻译这个任务,我们完全可以从“调 API然后自己擦屁股”改成“直接交给 Claude Code”。这里我们通过白嫖整个生态为了兼容它而做的适配工作,事实上复用了它的运行时层,或者说,我们从自己造轮子,变成了站在一个正在收敛的标准上面。

实战:把翻译交给 Claude Code

这就是我们决定换一条路的原因:与其继续在运行时层自己处理不确定性,不如直接站到这个正在收敛的标准上。所以我们就试着把社区翻译这个任务交给 Claude Code 去做。最直观的感受就是:之前我们花大量时间处理的问题,现在大部分自动消失了。

先说偷懒的问题。之前调 API,我们需要自己做分段、拼接、校验。但 Claude Code 的工作方式天然就不一样——它操作的基本单位是文件。文件是一个 stateful 的东西,它存在磁盘上,可以序列化、持久化。所以我们可以让 Claude 一章一章地翻译,每翻完一章它自己就写回文件,整个过程不需要我们在外面再包一层 orchestration 来追踪和管理进度。

断点续传也一样。以前调 API 超时了,我们要记录断点、只翻失败的部分、再拼回去。现在不用了。翻译到一半挂掉,文件就在那儿,已经翻好的部分总之不会丢。重启之后让 Claude Code 接着翻就行,它自己会读文件、看到哪里没翻、继续往下做。

术语统一的问题以前需要我们设计专门的流程,用总分或者递进的形式,让第一段的术语传递给第二段。现在 Claude Code 每次修改之前会先读整个文件,它天然能看到前面的上下文。所以术语统一的问题一个简单的 prompt 就能解决,比如先读整个文件,看看之前用的是什么术语,翻译第XX行到第XX行。

输出夹杂中文这个问题,以前我们要做检测、判断、重试。现在可以直接在 prompt 里跟 Claude 说:翻译完成后,从头到尾检查一遍,确保没有残留的中文字符。更进一步,因为 Claude Code 可以调用 Python,我们甚至可以让它写一个简单的脚本来验证最终文件的格式是否符合要求。它自己写检查逻辑,自己跑,自己修。

这些变化的共同点是:以前需要在 workflow 层面解决的问题,现在可以用自然语言在 prompt 里说清楚,让 agent 自己处理,而且还很可靠。我们终于可以把精力放在怎么让翻译效果更好,而不是怎么防止系统脑残出幺蛾子上。

Agentic Loop 与 Evaluation-First Mindset

这些变化让我们终于可以把精力放在翻译效果本身上。但做完以后我开始好奇:为什么 Claude Code 能做到这些?换一个方式调用 API 真的有这么大区别吗?

前面我们说过,一个重要原因是我们在复用整个生态的适配工作。但这只是更高层更表面的原因。如果从四层结构的角度来看,Claude Code 能 work 的直接原因是:它让 AI 能够观察到自己行动的结果。

这听起来像废话,但它是 agentic AI 和传统 API 调用的本质区别。当你用 API 的时候,AI 只能看到喂给它的 prompt,它吐出一个结果,然后就结束了。如果结果有问题,比如 JSON 格式不对、漏了字段、后半段开始偷懒,AI 自己不知道。发现问题的是你,决定要不要重试的也是你,怎么修复的逻辑还是你来写。这是为什么我们觉得AI很傻,我们要跟着收拾的直接原因。

但 Claude Code 不一样。它改完一个文件之后,可以调用 Python 跑一个JSON parser,看到报错说第 9527 行有语法错误。这个报错会反馈给它,它就知道该去修哪里。修完再跑一遍,通过了,继续往下。这个执行 → 观测 → 纠错的循环,就是 agentic loop。

这也是为什么文件这个形态这么重要的原因。文件是状态的载体,状态可见才能让闭环成立。我们把翻译任务从调一次 API 拿结果变成让 agent 在一个工作目录里操作文件,这在事实上给 AI 装上了一双眼睛。它能看见自己上一步做了什么,能看见验证脚本的输出,能根据这些信息决定下一步怎么做。这是运行时层带来的能力。

但 agentic loop 能跑起来,不代表它能跑对。观测到结果是一回事,知道什么结果才算"对"是另一回事。这是契约层要回答的问题。回到翻译这个例子。即使用了 Claude Code,它也不是我们换了个工具,一下就神奇地work了的。

如果只说"把这个文件翻译成英文"。Claude 翻了,结果里还是会有几段夹着中文字符。和之前调 API 遇到的问题一样,只不过这次修起来容易很多:我们可以在 prompt 里加一句:翻译完之后跑一个 Python 脚本检查有没有残留的中文字符,有的话自己修。Claude Code就会可靠地写一个简单的正则检查,跑一遍,发现问题就回去改,改完再跑,直到通过。

但这件事体现了一个更重要的问题:之前出错不是因为 Claude 笨,而是因为它不知道什么叫翻译完了。对它来说,保证对每一章都做了一次中翻英这个操作,任务就结束了。但对我们来说,翻译完了还包括格式正确、没有残留中文、术语统一这些隐含的期望。这些期望在我们脑子里,Claude 看不到。而一旦我们把这些期望显式地写出来,并且告诉它怎么验证,它就能自己判断做没做完。

我喜欢做的一个比喻是:想象你在给一个有健忘症的实习生交代任务。这个实习生没有任何上下文,不知道你之前聊过什么,不知道你的隐含期望,只能看到你这一次给他的指令。你需要把验收标准写到这种程度:只根据这些信息,他就能判断自己做完了没有。如果他觉得没做完,他知道还差什么。我的经验是,写到这种详细程度,基本上可以期待Claude Code/Codex可以可靠地完成任务。如果搞不定,别慌抱怨AI,我们应该首先检查是不是标准没写清楚。

所以现在我们就可以把这两层的关系说清楚了。运行时层给了 agent 观测能力,让它能看见自己做了什么、结果是什么。契约层告诉它什么算成功,让它能判断自己做完了没有。两者缺一不可:只有观测没有标准,agent 会在那里瞎转,给出一个非常漂亮但未必满足我们要求的结果;只有标准没有观测,agent 做完一次就停了,对不对全靠运气。Agentic loop 加上 evaluation-first,才构成一个完整的闭环。

从过程确定性到结果确定性

这个闭环一旦建立起来,会带来一种微妙的对 AI 信任来源的改变。它背后其实是两种不一样的确定性。

传统程序员的安全感来自过程确定性。我写的每一行代码都在我的控制之下,每一个分支、每一个边界条件我都考虑过。程序的行为是我设计出来的,只要它照着这些逻辑做,就一定会得到符合要求的结果。这种确定性是切实可感的,这种把结果翻译成程序行为的能力也是我们长期训练出来的基本功。

但我们刚才看到的 agentic loop 和 evaluation-first mindset,其实是另一种确定性。我们不规定每一步怎么走,而是规定终点长什么样、怎么验证到了终点。过程是不确定的——Claude 可能先翻译再检查,也可能边翻边查,可能用正则也可能用别的方法——但结果是确定的:只要验收标准写对了,最终产物就是对的。这是结果确定性。

这两种确定性背后,其实是两种不同的成本结构。过程确定性的经济学是:代码执行起来几乎不花钱,但写代码的人力很贵。所以我们要精心设计逻辑、追求复用、避免重复,把人力成本摊薄到每一次执行上。结果确定性的经济学正好反过来:intelligence越来越便宜,让AI反复尝试、检查、纠错的成本在快速下降。我们可以挥霍token来换取确定性——不是通过写更多的防御性代码,而是让AI用它的推理能力去对抗不确定性。

这和我之前在《一次性软件与被压缩的现实》中讨论的是同一个逻辑。那篇文章讲的是当写代码的成本趋近于零,一次性软件反而成了最优策略。这里的变化更广:不只是代码,而是整个推理和智能都在变便宜。翻译不是写代码,但它同样是燃烧token所产出的东西。当这个成本足够低,我们就可以让AI每次都现场做检查、现场写验证脚本、反复循环直到结果正确,而不需要像以前那样把所有可能的情况都预先在代码里写成规则。

在成本结构的变化之外,这也带来了天花板的差别。过程确定性的上限是我们的想象力和精力,我们能想到的情况、能写出来的逻辑,就是系统能处理的边界。结果确定性的上限更高:我们不需要穷举所有可能的路径,只需要定义清楚什么是对的,agent 会自己想办法到达那个状态。

但我们不太习惯结果确定性,往往会觉得不踏实。因为我们在职业生涯中引以为豪的一项核心技能,恰恰就是把结果翻译成过程:老板说想要一个能处理十万并发的系统,我们就设计出一套架构来保证这个结果;PM 说用户上传的文件不能超过 10MB,我们就写一个校验逻辑来拦截超限的请求。所以当我们开始用 AI 的时候,这种习惯会很自然地延续——我们本能地想用规则来规定 AI 的行为:输出必须是 JSON 格式,每个字段必须存在,遇到这种情况要这样处理,遇到那种情况要那样处理。

但这条路是有上限的。AI 不是一个确定性的系统,用过程去约束它,你会发现自己在做的事情是用大量的 rule 来控制它的不确定性。规则越写越多,漏洞越补越多,最后你花在防御上的精力比花在解决问题上的还多。这就是我们最开始拿 API 做翻译时遇到的困境。

但如果我们可以接受一点让步呢?如果我们愿意接受过程上的不确定性,转而通过规定结果来约束 AI 的行为,事情会变得不一样。我们不再说"你必须用这个方法处理这种情况",而是说"最终产物必须满足这些条件,怎么满足你自己想办法"。这样一来,AI 的灵活性不再是我们需要控制的风险,而是它完成任务的资源。

当然,以前这条路没那么容易走。如果你想让 AI 能够自己观测结果、自己判断对错、自己决定下一步怎么做,你得自己搓一个 agentic loop 出来。而 agent 套壳比它看上去要更难:你要处理工具调用的格式,要解析 AI 的输出,要管理上下文窗口,还要针对不同模型的特性做适配。这套东西做下来,你会发现自己又在做另一种形式的用过程换确定性。(而且引入 Agentic 框架往往会带来更大的技术债

但现在不用了。Claude Code、Codex、Cursor Agent 这些工具已经把运行时层的脏活干完了。Agentic loop 是现成的,文件系统是现成的,工具调用的封装也是现成的。你要做的,就是想清楚你要什么结果,怎么验证这个结果,然后用自然语言告诉它。

所以我有一个建议:尝试拥抱过程上的不确定性。不要条件反射地去规定 AI 的每一步行为,而是直接描述你对最终结果的期望,把它 codify 成可验证的标准。运行时层的事情交给 Claude Code 这类工具去处理,你专注于契约层:定义什么是对的,定义怎么检验。

这是一种不一样的工作方式,也是一种不一样的安全感来源。

结语

当然,这种工作方式不是没有边界的。

首先是任务本身的性质。结果确定性能 work,前提是你能清晰地定义什么是"对的"。翻译这个任务之所以适合,是因为验收标准可以形式化:格式正确、没有残留中文、术语一致,这些都可以写成脚本让 agent 自己跑。但有些任务的"对"很难定义,或者定义出来的标准本身就有歧义——不过话说回来,这种情况下用 rules 来约束过程只会更难。至少 evaluation-first 还给了一个明确的失败信号。

其次是安全。用 API 的时候,AI 对你的系统没有任何控制权。它只能接收 prompt、返回文本,仅此而已。但 Claude Code 这类工具不一样。它能读写文件,能执行 Python,能跑 bash 命令。这是它强大的原因,也是一个危险因素。这个问题要认真对待。我们的做法是在配置层面收紧权限:用 --allowedTools 参数限制它能调用的工具,把可执行的范围收敛到特定的脚本上。更进一步,可以结合现在比较流行的轻量级 sandbox 方案,让 agent 就算搞砸了也只会把 sandbox 里的文件弄乱,不至于影响宿主系统。

这方面确实还有很多坑。权限模型怎么设计、sandbox 怎么配置、出了问题怎么回滚,这些都是开放的问题,没有标准答案。但我对这个方向还是乐观的。安全问题是工程问题,工程问题是可以解决的。不会因为有这些风险,这条路就走不通。

回到开头的问题:到底是把 AI 作为系统的一部分,用程序去调用它,我们做的是一个带AI功能的翻译产品?还是把 AI 作为系统的核心,让它去调用程序,我们做的是一个完成翻译任务的AI Agent?

我们试了两条路,发现后者的成功率和稳定性意外地高很多。这可能是因为后者让我们可以复用整个生态的适配工作,因为 agentic loop 让 AI 能够自我纠错,因为 evaluation-first 让我们可以用结果而不是过程来约束 AI 的行为。这些因素叠加在一起,构成了一种不同的工作方式。

它需要我们放弃一些东西:对过程的掌控感,对每一步行为的确定性,以及我们花了很多年训练出来的那种把结果翻译成流程的本能。但它也给了我们一些东西:更高的上限,更少的体力活,以及一种新的、基于结果的安全感。

这个模式能推广到多远?我不确定,但至少在翻译这个场景上,它彻底改变了我们的开发体验。我们把这套实践整理成了一份操作指南,你也可以发给自己的AI,让它现在就试试看。

Comments