软件系统最危险的敌人,往往不是性能,不是技术选型,也不是某一个没有修掉的 bug。
更危险的东西是复杂度。
复杂度很少以“我要毁掉这个系统”的样子出现。它通常披着合理性的外衣:
- 为了可用性,我们加一个兜底流程;
- 为了稳定性,我们加一个确认握手;
- 为了可靠性,我们加一个回调补偿;
- 为了兼容更多场景,我们加一个配置项;
- 为了避免极端错误,我们再加一个状态。
每一步看起来都对。
每一个局部决策都有理由。
但系统就是这样一点点变重的。
在 AI 时代,这个问题会更严重。因为 AI 让“继续加一点东西”变得更便宜了。写一个分支、补一个状态、生成一个配置面板、加一层 fallback、再写一段兼容逻辑,都比过去更快。
于是,新的瓶颈不再只是能不能做出来,而是:
人能不能审得过 AI 生成和推动出来的复杂度。
复杂度通常是合理地积累出来的
复杂度不是工程师无能的结果。
很多时候,它恰恰来自工程师的责任心。
一个功能上线后,系统会遇到异常输入、网络抖动、用户取消、第三方超时、权限变化、状态不同步、重复请求和并发冲突。
为了让系统更稳,我们开始补流程:
- 请求失败了,要不要重试?
- 用户取消了,要不要回滚?
- 对方没响应,要不要超时?
- 超时后又成功了,要不要补偿?
- 补偿失败了,要不要告警?
- 告警太多了,要不要分级?
- 分级以后,要不要再做自动恢复?
于是,一个原本简单的流程开始长出分支。
分支之间再长出状态。
状态之间再长出同步协议。
最后,系统从“做一件事”变成“管理一堆可能发生的事”。
复杂度最隐蔽的地方在于,它往往不是被一次性设计出来的,而是被合理地积累出来的。
复杂度的代价不是代码多
复杂度最直接的代价,是理解成本。
一个简单系统,工程师可以在脑子里模拟它的运行。
输入是什么,状态怎么变,失败会怎样,基本能想清楚。
一个复杂系统则不行。你必须依赖日志、链路追踪、状态表、回调记录、重试队列和人工排查,才能拼出当时发生了什么。而且经常拼不完整。
这会进一步带来几个问题。
第一,失败路径爆炸。
成功路径通常只有一条,但失败路径可能有十几条。每增加一个状态,失败路径都可能成倍增长。
第二,测试成本上升。
复杂流程不能只测 happy path。它必须测试状态组合、超时组合、取消组合、恢复组合和重复执行组合。组合一多,测试就会失控。
第三,修改变得危险。
一个超时策略、重试机制、状态字段或兼容分支,都可能影响隐藏路径。团队会逐渐不敢动系统,只能继续在外围打补丁。
第四,系统出现多个局部真相。
这在涉及双方通信、握手、确认、取消和补偿的系统里尤其常见:
A 认为任务已经取消
B 认为任务还在等待确认
C 收到了成功回调
用户界面显示处理中
后台任务已经进入重试队列
这不是单个 bug,而是系统存在多个局部真相。
当系统无法稳定回答“现在到底发生了什么”,复杂度就已经从实现问题变成了治理问题。
成熟软件不可能真正简单
当然,不能天真地说所有复杂度都不好。
成熟软件很难保持真正的简单。因为成熟软件要面对真实世界。
真实世界有不同用户、不同设备、不同网络、不同权限、不同历史数据和不同业务规则。
一个玩具项目可以简单,因为它可以拒绝大多数场景。一个成熟产品不能这样做。
支付系统需要幂等。
权限系统需要审计。
文件系统需要恢复机制。
Agent 产品需要记录工具调用、权限边界、上下文变化、执行状态、用户确认和失败恢复。
这些复杂度很昂贵,但它们守住了稳定性和用户信任。
问题不在于系统能不能复杂。
问题在于:
复杂度有没有被管理,能不能被解释,是否真的换来了用户价值。
抽象不是免费的
更好的抽象可以减少复杂度。
把散落的回调变成事件流,把多方状态同步变成单一事实源,把临时判断变成明确的领域模型,这些抽象都可能让系统更清楚。
但抽象本身也是复杂度。
一个抽象需要命名,需要边界,需要学习成本,也需要维护。
如果一个抽象没有降低总体复杂度,只是把复杂度换了一个更高级的名字,它就是新的负担。
很多架构问题并不是“有没有抽象”,而是“抽象到底替谁降低了复杂度”。
- 如果它降低了用户理解成本,它是产品价值。
- 如果它降低了工程定位成本,它是工程价值。
- 如果它降低了系统恢复成本,它是可靠性价值。
- 如果它只是让架构图更漂亮,它可能只是复杂度转移。
这也是 AI 产品里很重要的一条判断标准。
不要因为一个概念听起来高级,就把它引入系统。
概念必须有治理对象。
复杂度是一种预算
我们通常会把 CPU、内存、磁盘、网络、LLM 上下文和用户注意力看成资源。
资源是有限的,使用它就有成本。
复杂度也应该被看成一种资源。
系统能承载的复杂度是有限的。
团队能理解的复杂度是有限的。
用户能感知的复杂度更是有限的。
每增加一个状态、一个配置、一个分支、一个异步回调、一个补偿流程,都在消耗系统的复杂度预算。
所以每个复杂设计都应该回答三个问题:
- 它解决的是高频问题,还是低频焦虑?
- 它降低了用户复杂度,还是只把复杂度转移给了系统?
- 它会让系统更可解释,还是制造更多失败路径?
如果一个复杂设计消耗了大量预算,却没有明显提升用户满意度、稳定性、可恢复性或业务能力,它就是亏的。
Agent 产品会天然放大复杂度
Agent 产品比普通应用更容易复杂。
因为 Agent 不是一次请求一次响应。
它通常会经历一个长链路:
用户目标
-> 任务理解
-> 上下文组装
-> 计划生成
-> 工具调用
-> 文件或数据变更
-> 中间结果观察
-> 失败恢复
-> 用户确认
-> 最终交付
这里每一段都可能产生状态。
每一个状态都可能失败。
每一次失败都可能需要解释、回滚、重试或人工接管。
Agent 产品还会额外面对几个难题:
- 用户目标经常是模糊的;
- 模型输出并不总是稳定的;
- 工具执行会受到权限、环境和外部服务影响;
- 长任务会积累大量上下文;
- 用户需要知道 AI 做了什么,但又不想阅读所有细节;
- 系统要让用户有掌控感,同时不能把所有复杂度都暴露出来。
这意味着 Agent 产品经理不能只关心“模型能不能完成任务”。
还要关心:
- 用户如何提出任务;
- 系统如何约束执行;
- 中间过程如何展示;
- 风险如何被阻断;
- 失败如何恢复;
- 结果如何被审查;
- 信任如何被建立。
Agent 的核心挑战,不是把自动化做满,而是在自动化、控制感和可理解性之间找到平衡。
控制面和信任面不是为了增加概念
在 Plato 里,我把 Agent 产品拆成三个平面:
启发面
帮助用户发现 AI 能做什么,并把模糊意图转化为任务包
控制面
把任务变成可执行、可约束、可验证的流程
信任面
让执行过程可见、可审计、可恢复、可解释
这三个平面不是为了堆概念。
它们的目标恰恰是收敛复杂度。
启发面降低用户表达复杂度。
用户不需要在空白输入框前独自想清楚所有上下文、目标、边界和验收标准。
控制面降低执行复杂度。
系统把任务拆成可追踪、可暂停、可确认、可恢复的步骤,而不是让 AI 在一个黑箱里自由漂移。
信任面降低信任复杂度。
用户不需要猜 AI 做了什么、为什么这么做、哪里可能有风险、失败后还能不能恢复。
所以,控制面和信任面并不是“让产品看起来更复杂”。
它们应该把必要复杂度封装在系统内部,并把用户真正需要理解的部分呈现出来。
如果一个 Agent 产品的控制面只是增加按钮,信任面只是增加日志,那它没有真正降低复杂度。
真正有效的治理平面,应该让用户更容易完成判断。
复杂度必须换来用户价值
一个实用判断标准是:
复杂度的提升,必须换来用户体验、稳定性、可信度或需求满足的提升。
这类提升可以是很多形式。
它可以让用户少犯错。
可以让失败更容易恢复。
可以让团队更快定位问题。
可以让系统状态更一致。
可以让用户更敢把重要任务交给 AI。
可以让复杂任务从“完全不可用”变成“可控地推进”。
这些复杂度是值得的。
但也有很多复杂度只是工程焦虑的产物。
我们害怕某个边界情况,于是设计一个完整机制。
害怕机制失败,再设计一个补偿机制。
害怕补偿机制不够,再设计一个监控机制。
最后,系统确实考虑得很全面,但已经没人能真正理解它。
这类复杂度不会增加用户信任,反而会降低系统可信度。
克制也是产品能力
克制不是少做事。
克制是知道什么复杂度值得保留,什么复杂度应该被砍掉。
在 AI 产品里,这种能力会越来越重要。
因为 AI 很擅长扩展方案。
它会补充边界、增加异常处理、设计更多状态、生成更多配置、提出更多二级能力。
这些能力有时有价值。
但产品负责人必须判断:
- 当前版本真的需要吗?
- 用户是否能理解?
- 团队是否能维护?
- 失败后是否能解释?
- 这个复杂度是否服务主路径?
- 有没有更简单的退化方案?
用户喜欢的产品,往往不是功能最多的产品,而是让人感觉轻松、稳定、可信的产品。
软件最终不是给架构图使用的,是给人使用的。
对 Plato 的启发
这篇文章对 Plato 的意义很直接。
Plato 不是要把 AI 工作台做成一个“功能越多越好”的系统。
它更应该成为一个复杂度治理产品。
启发面要减少用户不知道如何使用 AI 的复杂度。
控制面要减少 AI 执行过程不可控的复杂度。
信任面要减少用户无法判断结果是否可靠的复杂度。
文章、项目、任务、上下文、执行记录、用户确认、失败恢复,都不应该只是功能列表。
它们应该共同回答一个问题:
用户如何更稳定地把一个模糊目标,推进为可信结果?
如果一个设计不能帮助这个问题,它就应该被延后、简化或删除。
这不是保守。
这是让系统长期可迭代的前提。
结论
复杂度不是绝对的坏东西。
没有复杂度的软件,通常也没有足够的能力。
但复杂度必须被管理,必须被审计,必须被证明值得。
每一个复杂设计都应该回答一个问题:
它消耗了系统的理解力、测试力和维护力,换来了什么?
如果答案不清楚,就应该停下来。
软件工程最难的地方,不是把系统做复杂。
复杂很容易。
真实世界会推着我们走向复杂。
难的是在复杂不可避免的时候,仍然保持清醒。
少一点握手,多一点收敛。
少一点局部真相,多一点事实源。
少一点完美机制,多一点可理解的退化。
少一点架构炫技,多一点用户价值。
复杂度是软件的死敌。
而克制,是工程系统最稀缺的美德。