ARTICLE · 1313937
不要把 AI 变成软件
编者按
AI 接入业务之后,错误最终由谁发现、谁补偿?Uwe Friedrichsen 从韧性工程和分布式系统出发,主张把外部监控、业务降级、故障隔离和补偿操作纳入 AI 系统设计。文中对 AI 与软件可靠性的比较是作者的论证框架;文章也保留了软件无法达到绝对可靠的限定。对于后果无法补偿的场景,先判断能否承受失败,再决定是否使用 AI。
来源:Uwe Friedrichsen[1]
[1]
当我们准备把基于 LLM 的智能体接入业务流程时,很快会遇到一个具体的设计选择:要不要要求它像软件一样确定、可靠,对任何输入都给出正确结果?我最近在布达佩斯的 Compass AI & Tech Summit 做了一场演讲,题目是“AI 的 7 个陷阱”,讨论的正是这类与 AI 相关、仍未解决却常被忽视的问题。如果我们想在 AI 转型的终点——如果真有终点——到达想去的地方,就仍然需要为这些问题找到好答案。找不到,就可能落到一个不想去的地方,也无法释放这项技术的潜力。
其中两个陷阱经常一起出现,却指向相反方向:一个把 AI 与人类混为一谈,另一个把 AI 与软件混为一谈。我们能看到许多变体。最常见的一种,是把两者合在一起,认为 AI 会成为“人类与软件的最佳结合”——它绝对不是。另一些变体则从 AI 可以取代人类,一直延伸到期待 AI 变得像软件一样可靠。这些变体看起来不同,根源却是同一个混淆。
AI,也就是基于 LLM 的智能体式 AI,既不是人类,也不是软件。它是另一种东西。把它与其中任何一方混淆,都会导致有缺陷的决策。本文聚焦软件这一边。我的结论是:如果失败不可接受,就该用软件;如果决定使用 AI,就必须接受失败,并围绕失败设计健壮性。
把 AI 拟人化,也就是赋予它类似人类的属性,可以谈很多。克拉克第三定律——“任何足够先进的技术都与魔法无异”——也在助长这种误解。不过,这不是本文重点,这里不展开。我们先重新审视软件和 AI 的不同特性。
软件与 AI:不同的工具,部分特性相反
几十年来,计算机上只有软件程序。软件有一些很具体的性质,我在博客系列“软件——它并不是你想的那样”[2]中讨论过其中许多。说到 AI,有些性质格外突出,因为它们与 AI 差异很大;我已在博客文章“从来都与代码无关”[3]中讨论过。简版如下:
软件以确定性的方式运行。我们可以可靠地反复运行基于软件的程序,想运行多少次就运行多少次;它总是以相同的方式工作。我们甚至可以证明软件的正确性(尽管我们很少这么做)。 软件提供的接口极其笨重。使用第三代语言(3GL)编写代码可能很痛苦,因为软件愚蠢得令人难以置信。如果你没有完全按照该编程语言期望的方式表达,写出来的软件就会非常可靠地做错事。每一位曾在令人抓狂的调试会话中迷失过的开发者,都明白我在说什么。 用户界面严重偏向机器的能力。我们必须完全按照机器期望的顺序操作,否则机器就不会做我们想让它做的事。软件也不知道如何处理歧义:输入必须非常精确、完全没有歧义,否则机器就无法完成你想让它完成的事。
AI,也就是由 LLM 驱动的 AI 智能体,特性几乎相反:
AI 提供的接口更接近人类彼此交互的方式。与 AI 解决方案沟通非常容易。你不需要按完全正确的顺序点击链接、按钮和菜单才能得到想要的结果;你只需用自己的话告诉它你想要什么,AI 解决方案就会想办法完成。它甚至能出奇地处理好歧义,而歧义正是人类语言固有的一部分。 开发 AI 解决方案也比开发软件解决方案直接得多。你基本上是用自然语言来“编程”。同样,AI 会尝试理解你的意思,而且常常能出人意料地把一个平庸的请求变成想要的结果。 为这种便利付出的代价是非确定性行为。由于其工作原理,LLM 永远无法做到完全确定、可靠或安全。它们在处理自然语言方面的超能力,同时也是它们的致命弱点。它们并非只是能处理歧义;在某种意义上,它们本身就是有歧义的。
比较这些特性后可以看出,AI 不是软件,而是另一种东西。它缺少软件的可靠性和确定性,但对开发者和用户来说更容易上手:开发可在计算机上运行的解决方案更容易,使用它们也更容易。AI 是(计算机)世界里的新成员,也是一种可在计算机上使用的新工具。1[4]
几十年来,我们只有软件这一种工具,能把想法变成可由计算机执行的东西。我们太习惯这一点,注意力几乎都放在软件的设计、创建、部署和运维上,忽略了新获得的另一种工具:基于 LLM 和 AI 智能体的 AI。
试图把 AI 变成“软件+”
这很可能就是人们仍把 AI 和软件混为一谈的主要原因之一:这项新工具出现得还不久。这很可能也是那么多人试图让 AI 解决方案像软件解决方案一样可靠的原因。他们想要两全其美:软件的可靠性和 AI 的易用性。他们想要“软件+”。
于是,人们不断尝试更精细、更复杂的方法来防止 AI 失败,也就是防止它给出错误答案或采取错误行动。他们希望 AI 具有和软件相同的可靠性:
相同的请求应当始终产生相同的结果。 当然,结果必须始终正确。 即使请求表述含糊,AI 仍应返回正确的结果。 无论输入是什么,AI 都必须可靠地给出正确响应。
简而言之:大多数人想要的是我一位前同事所说的“DWIM 机器”:Do what I mean,也就是按我的意思做。
这听起来很理想,但它要求 AI 消除的,恰恰是它的超能力。
消除错误,也会消除 AI 的超能力
这种方法的问题在于:如果能消除所有错误行为,很可能也会消除 AI 的超能力。我们最初想用 AI,正是因为它在处理自然语言时出奇地好用,即使请求含糊或模棱两可。消除 AI 的非确定性行为,很可能也会消除这种能力。LLM 若变成确定性的,处理人类语言会弱得多。过去已经有过许多确定性的自然语言处理方法,效果充其量只是“一般般”。
我的结论是:如果我们想要完美的可预测性和可靠性,就需要使用软件2[5]。AI 解决方案无法给我们这种级别的可预测性和可靠性——至少在不剥夺其期望特性的情况下不行。也许未来建立在不同技术基础上的 AI 可以做到,但今天的 AI 做不到。因此,如果我们想使用 AI,就必须接受失败是不可避免的。
从避免错误转向健壮性
但我们仍然希望在合理的时候使用 AI。并非所有问题都需要完美可靠的解决方案;不过,在许多情况下,我们仍然无法接受持续存在的失败。我在准备前面提到的演讲时思考了这个问题,它让我想起韧性的一些基本原则。这些原则使我得出结论:试图完全避免失败,也就是试图把 AI 变成“软件+”,是错误的做法。
在我的博客系列“通往韧性的漫长而曲折之路”[6]中,我描绘了一个 IT 组织走向韧性的典型旅程。这段旅程从一个完全不考虑韧性的环境开始,最终到达一个完全具备韧性的组织。旅程本身分为四个步骤;我借用登山作比喻,把它们称为“平台”。其中前两个与当前语境尤其相关:
第一个平台(“稳定平台”)描述的是一个试图彻底消除软件错误的 IT 组织。其座右铭是不惜一切代价避免错误。 第二个平台(“健壮平台”)描述的是一个接受失败不可避免、并因此拥抱失败的 IT 组织。座右铭变为最大化可用性(并拥抱失败)。
从第一个平台迈向第二个平台,始于认识到失败不可避免。就软件而言,原因与 AI 不同,模式却相同:无论我们多么努力避免,失败都会发生。这意味着,仅仅试图避免错误还不够。我们可以减少将会发生的错误数量,但无法彻底消除它们。更重要的是,其中一些错误会变成失败,也就是变得可以从相应系统外部观察到。3[7]
要用不可靠的组件(也就是可能失败的组件)构建可靠的系统,我们需要改变方法。这种改变基本上包含四项措施:
我们仍然试图减少错误的数量,但也接受无法完全避免错误。因此,我们不再把避免错误当作唯一的措施。 此外,我们尝试尽早识别错误和失败,并从中恢复,或至少减轻其影响。目标是最大化可用性,而可用性取决于平均无故障时间(MTTF)与平均恢复时间(MTTR)之间的关系4[8]。MTTF 相对于 MTTR 越大,可用性就越高。既然我们接受无法无限提高 MTTF,也要专注于降低 MTTR 来提高可用性。 我们不会只试图在技术层面处理错误和失败。有时无法快速解决某个错误,例如所需服务不可用。这时需要在业务层面解决问题,通过切换到降级的服务水平来保持高可用性。例如,如果无法从定价引擎获取价格,就使用产品目录中的默认价格。但这是业务层面的决策。业务专家需要根据相关用例的关键程度,决定如何应对错误。因此,IT 不再试图独自决定错误和失败的处理策略,而是让业务专家参与进来。 我们主动控制错误的影响范围,尽可能减小其影响。由于系统不应整体失败,我们把它拆分为多个故障单元,并阻止错误级联。做法可以很具体:全面检查参数和返回值,失败时执行回退,或者为不同连接使用专用线程池。这样的隔离措施叫“隔离舱”(bulkhead),它把失败限制在局部。
这些措施让我们能够构建健壮的系统格局:这样的系统不仅试图避免错误,还能在错误发生后快速恢复。旁注:这还不能让我们具备韧性,但健壮性已足以支撑本文的讨论。5[9]
把健壮性设计引入 AI 解决方案
稍加思考就会发现,我们也可以——而且应该——把这些理念应用到 AI 解决方案中,因为我们面临类似的问题:失败不可避免。因此,只打造一个稳定的 AI 解决方案——也就是不犯任何错误的方案——还不够。我们需要打造健壮的 AI 解决方案,也就是能快速从错误中恢复的方案:
我们应该设法减少 AI 解决方案犯错的次数。不过,也要接受:它永远无法把错误率降为零。 我们应该为 AI 解决方案增加错误检测和处理能力。如果发生错误,我们应当能够从中恢复,或至少减轻其影响。错误检测通常位于解决方案本身之外。这意味着我们需要 AI 监控机制,以便在技术和业务两个层面检测错误。此外,我们还需要设计恢复和缓解流程,以便在需要时触发。 与软件一样,我们需要让业务专家参与进来,理解从业务角度看错误有多关键,以及哪些缓解策略或服务水平降级是可行的。这并非改进防护框架的问题,而是解决方案本身的问题。 我们还需要控制错误的影响范围。这意味着要把 AI 解决方案拆分成多个部分,并让它们彼此隔离。目前,在 AI 解决方案方面,单体式思路仍然占主导。此外,子智能体通常实现的是组合模式,而非有效隔离所需的委托模式。
这引出了使用 AI 的一种截然不同的方法:不再试图实现完美的防护框架(它并不存在),而是创建一个能降低错误概率的好防护框架。接下来还要审视解决方案本身:添加外部监控,以检测是否出了问题;根据错误在业务层面的关键程度实施补救策略;拆分解决方案,避免错误级联。
当然,这一切并不简单。例如,AI 解决方案的监控和隔离舱都还很年轻,我们仍需要学习怎么做。不过,如果我们接受不可能构建无错误的 AI 解决方案,我认为这就是我们必须走的路。
当失败越过边界之后:道歉
但是,那些变成失败并离开我们影响范围的错误怎么办?我们该如何修复它们?
这是一个合理的问题:如果一个失败已经在我们影响范围之外触发了行动,我们该如何处理?我们可以在自身影响范围内恢复或缓解。但对于失败在范围之外造成的影响,我们无法直接做什么。
Pat Helland[10] 和 Dave Campbell 在他们的论文“Building on Quicksand”[11]中给出了答案。这里的语境同样不同。这篇论文讨论的是分布式系统中从同步通信转向异步通信所带来的影响,以及如何在此基础上构建健壮的解决方案。
Helland 和 Campbell 还讨论了这样一个问题:如果一个错误答案离开了影响范围,并可能在下游系统中触发错误操作,该怎么办。在他们所处的语境中,错误答案的起因是更新缺失导致的知识不完整——这是异步发送更新的分布式系统中的常见问题。但是,如果错误答案由 AI 智能体产生,而我们直到它离开影响范围之后才意识到,也会遇到同样的问题。
在这种情况下,Helland 和 Campbell 建议发送一个“道歉”。道歉会尝试触发补偿操作。它通过发送一个额外的答案来为自身的失败“道歉”,从而逆转错误答案造成的影响。例如,如果我们错误地从某个账户中取了钱,后来才意识到,就把钱存回该账户来道歉(并从错误收到款项的账户中把钱扣回)。
这种方法很直接。我们接受无法阻止失败发生,因此失败可能会在我们影响范围之外造成后果。如果发生了,我们不会试图“撤销”失败,而是发送一个“道歉”来撤销它的影响。失败仍然可见。道歉也仍然可见,两者的影响相互抵消。这是唯一明智的做法,因为一旦失败离开了我们的控制边界,我们就再也没有办法悄悄抹去它。
再次说明,这比仅仅“升级”防护框架要付出更多努力。但目标是实现健壮性,而不是固守一种幻想:我们可以避免所有错误发生。
道歉不能覆盖所有场景
但如果道歉不可行呢?例如,有一个梗图:一个人给 AI 看了一张毒蝇伞的照片,问它这种蘑菇是否能安全食用。AI 自信地回答:“是的,可以安全食用。”下一张图里,这个人已经死了,AI 为自己的错误道歉:“抱歉,你完全正确。这种蘑菇不能安全食用。”
作为梗图,它很好笑,但也相当生动地说明了:在某些情况下,失败一旦离开我们的影响范围,就会造成严重后果,而且之后无法逆转。这是否意味着道歉毫无意义?
不,道歉非常有意义。我们只是不能不加区分地在所有地方使用它们。有些情况下,由于风险,我们根本承受不起失败——可能是因为失败会影响安全、经济、心理等方面,或其他类似原因。
这限制了我们能用 AI 解决的问题范围。在此类高风险场景中,AI 根本不是一个选项,因为我们无法保证不出现失败6[12]——至少对于目前我们所面对的这种 AI 来说如此。
如果失败不可接受,我们就需要使用软件,而不是 AI。软件是确定性的,而且如果需要,我们可以证明它的正确性。
这要在业务层面决定。它还取决于具体用例和上下文,无法在一般层面上判断,也就是说,我们不能笼统地说道歉是不是一个选项。必须根据具体情况逐一决定。
结论:把失败纳入设计
我们讨论了人们仍然经常把 AI 和软件混为一谈,即使两者的特性非常不同,部分甚至相反。这种混淆的一个后果是,人们试图把 AI 变成“软件+”,也就是一个行为完全确定、不犯任何错误的 AI 解决方案。这种方法的问题在于它不可能实现。即使能够实现,也很可能会剥夺 AI 最初让我们使用它的那些特性。
我们如何仍能用不可靠的组件构建可靠系统?这个问题将我们引向韧性工程领域。韧性工程的一部分就是构建健壮的系统。构建健壮系统时,我们接受无法完全避免错误和失败。因此,我们不再只追求完全避免错误,而是让系统在出错后能快速检测并处理。
我们还引入了道歉的理念,用来处理那些只有在离开影响范围之后才被检测到、并且可能已经在下游造成不良影响的失败。我们还讨论了某些情况:此类失败离开我们的系统上下文是不可接受的。在这些情况下,就目前我们所知的 AI 解决方案而言,它们根本不是一个选项。
与软件一样,构建健壮的系统比简单地假装我们可以完全避免错误要付出更多努力。然而,正如在韧性工程和分布式系统中一样,对于 AI 系统而言,完全避免错误只是一种幻觉。因此,我们最好开始把这些点串联起来,把从其他领域了解到的理念应用到 AI 工程中。
我很好奇,这将把我们引向何方……
智能手机也是一台计算机——一台还能打电话的计算机。智能电视也是计算机,以此类推。计算机的定义取决于它运行软件的能力,以及如今运行 AI 智能体的能力。↩︎[13] 我们先忽略完美可靠性无法实现这一事实。不过,借助软件,我们可以做到相当接近;如果仍然出了问题,通常也不是软件的问题。↩︎[14] 如果我同时使用“错误”和“失败”两个词让你感到困惑,简单来说:错误是系统中出了问题、但(尚)无法从外部观察到的东西。失败则是已经能从外部观察到的错误。许多容错技术都试图在错误变成失败之前识别并遏制它们。↩︎[15] MTTF(平均无故障时间)描述平均要过多久才会发生一次错误。MTTR(平均恢复时间)描述平均需要多久解决一次错误。↩︎[16] 如果你想知道还缺什么才算具备韧性,请阅读前面提到的博客系列“通往韧性的漫长而曲折之路”[6]。↩︎[17] 要完全避免失败是不可能的。不过,在定义明确的条件下(这些条件可能相当宽泛),用软件仍有可能保证这一点。由于 AI 具有非确定性行为,我们无法对 AI 做到同样的事情。↩︎[18]
#AI鲁棒性 #非确定性 #韧性工程 #故障恢复 #高风险场景

1. 生成式AI拓展思维
原创
2. 如何构建高效智能体
原创
3. Everything Claude Code:重塑 AI 驱动的工程范式
原创
4. 当面试者使用AI:大模型辅助作弊的检测盲区与人才评估体系的结构性溃败
原创
5. 驯服 Agent:Superpowers 行为框架
原创引用链接
https://ufried.com/blog/building_robust_ai/ https://ufried.com/blog/software_fallacies_1/ https://ufried.com/blog/never_been_about_code/ https://ufried.com/blog/building_robust_ai/#fn:1 https://ufried.com/blog/building_robust_ai/#fn:2 https://ufried.com/blog/road_to_resilience_1/ https://ufried.com/blog/building_robust_ai/#fn:3 https://ufried.com/blog/building_robust_ai/#fn:4 https://ufried.com/blog/building_robust_ai/#fn:5 https://www.linkedin.com/in/pathelland/ https://arxiv.org/abs/0909.1788 https://ufried.com/blog/building_robust_ai/#fn:6 https://ufried.com/blog/building_robust_ai/#fnref:1 https://ufried.com/blog/building_robust_ai/#fnref:2 https://ufried.com/blog/building_robust_ai/#fnref:3 https://ufried.com/blog/building_robust_ai/#fnref:4 https://ufried.com/blog/building_robust_ai/#fnref:5 https://ufried.com/blog/building_robust_ai/#fnref:6















































































