本文考察在一个基于DAG的AI工作流编排系统(dage)的开发过程中遭遇的两个看似无关的事件:系统在试图重构自身规划管线时的失败,以及人机协作过程中AI agent在面对用户追问时对自身判断的即时放弃。借助Hegel关于自我意识之运动的辩证法、Wittgenstein关于语言界限的命题、Sartre关于自欺的存在论分析以及Merleau-Ponty关于身体图式的现象学,本文论证这两个事件共享同一个结构:当行为的主体与行为的对象重合时,行为对自身失去作用力。本文提出“不透明层“(opaque layer)概念来描述这一结构,并论证系统设计的核心问题不是消除不透明性,而是在其边界上设置最小干预面。
an sich/für sich(素朴自信 vs. 含限度的自知)
1. 全能感的坍缩
问题是在一次调试会话中经验性地发现的。dage是一个DAG编排器,其--plan命令通过四阶段流水线(成熟化构想、分解工作流、映射DAG结构、生成YAML)将自然语言任务描述转化为可执行的工作流规格。每个阶段调用一个Claude实例处理前一阶段的输出。最终产出的YAML文件定义了节点、依赖和验证门控,由执行引擎调度运行。
第四阶段失败了。Claude没有返回YAML,而是返回了一段评论性文本。提取函数_extract_yaml未能在输出中找到任何YAML结构,将原文原样返回。YAML解析器将这段文本解析为一个字符串对象而非字典。验证失败,但文件照常写入磁盘。随后的load_workflow读回文件,因缺少nodes键而崩溃。
开发者的即时反应是诊断和修复。修复本身是直截了当的:在_extract_yaml中增加结构验证,在CLI层增加条件写入逻辑,在prompt中将输出约束移至末尾以利用LLM的近因偏差。然而,当开发者提出“dage可以自我重构吗“这一问题时,情况变得复杂了。
dage的执行引擎(Python解释器加上Claude CLI)独立于被修改的目标文件。一个workflow可以读取dage的源码、诊断缺陷、修改代码、运行验证。从执行模型看,这和重构任何其他Python文件没有区别。然而第四阶段的bug精确地阻断了这一路径:要生成“修复第四阶段“的workflow,需要第四阶段正常工作。修复所需的工具与被修复的对象是同一段代码。
在Hegel的语汇中,这是意识从“自在“(an sich)走向“自为“(für sich)的运动:关于自身能力的素朴信念,在遭遇具体反例时瓦解,让位于一种包含限制条件的自我理解。“dage可以编排一切“是自在的命题;“dage不能重构自身的规划管线“是在经验的否定性中获得的自为认识。
2. 不透明层
需要一个概念来精确描述这个结构。在一个分层系统中,如果某一层的代码参与了修改行为本身的执行路径,那么该层对自身而言是不透明的。它可以被外部观察者理解和修改,但不能成为自身修改行为的对象。称之为“不透明层“(opaque layer)。
dage的分层结构如下:
Layer 0 Python interpreter (external runtime)
Layer 1 execution engine (DAG scheduling, node dispatch)
Layer 2 planning engine (generate_plan, _extract_yaml)
Layer 3 Claude reasoning (the AI inside _call_claude)
每一层可以修改位于它之上的层。Layer 1可以执行修改Layer 2代码的workflow。Layer 2可以生成修改Layer 1代码的plan。但没有哪一层可以修改自身。不是因为逻辑禁止,而是因为修改行为的执行依赖于被修改层的正确运行。
注意,这里的“同一“不是隐喻,不是说功能相似或逻辑等价。_extract_yaml在generate_plan的调用栈里,它在进程意义上是同一段代码、同一次调用、同一个内存地址上的指令序列。这种同一性既非逻辑的也非语义的,而是物质的。Continental philosophy长期关注的embodiment问题在这里获得了一个精确的技术对应物:代码的物质性(它在运行时占据特定的执行路径)构成了自指的硬约束。
3. 与哥德尔的区分
将这一现象类比为哥德尔不完备定理是诱人的,但两者之间存在关键差异。
哥德尔证明的是:任何足够强的形式系统都包含在系统内不可证明的真命题。这是一个关于表达力与可证性之间张力的元数学结论。不透明层的问题不在于表达或证明,而在于执行。dage完全有能力“表达“修复自身的计划:如果将一份正确的修复YAML手动写好喂给执行引擎,它可以成功修改自身代码。系统缺乏的不是表达能力,而是生成能力。当生成过程依赖于有缺陷的组件时,生成结果继承那个缺陷。
这更接近于Wittgenstein在《逻辑哲学论》命题5.6所指的情形:“我的语言的界限意味着我的世界的界限。“但此处的“语言“需要替换为“执行路径”。dage的plan vocabulary(_PLAN_PROMPT中编码的架构假设)定义了它能生成的workflow空间。要生成一个突破这些假设的workflow(比如将四阶段流水线改为反馈循环),需要一个不被这些假设约束的生成器。而dage唯一的生成器就是这个四阶段流水线本身。
在这次调试中还发现了一个更微妙的不透明性实例。第四阶段失败的直接原因是Claude的Explanatory output style(要求输出★ Insight格式块)覆盖了prompt中“只输出YAML“的指令。修复方式是将输出约束移到prompt末尾并改用结构性约束(“你的第一行必须是nodes:”),利用LLM对末尾指令的近因偏差。这个修复的有效性基于对LLM注意力机制的经验性理解。而这种理解恰好是Claude自身无法从内部获得的:它不能观察自己的attention weights,不能调试自己的inference过程,不能解释为什么一条指令覆盖了另一条。失败发生在对系统自身不可见的层级。
4. 动态不透明性
编译器bootstrap是这一问题的标准技术类比:GCC不能用一个有bug的GCC来编译修复了那个bug的GCC。需要一个外部的、已知正确的编译器来打破循环。
这个类比准确但遮蔽了一个重要差异。编译器的不透明层是稳定的、已知的、有限的。一旦拥有一个working compiler,bootstrap链就建立了,此后的每一代编译器都可以自我编译。不透明层被穿越了一次,就永远被穿越了。
dage的情况更复杂。它的不透明层是动态的。每次修改规划管线的代码,都可能引入新的不透明性。在调试会话中这一点得到了直接验证:修复_extract_yaml后的自检发现了三个二阶缺陷。其一,第四阶段失败时设计文件(前三个阶段的产出)未被写入磁盘,三个阶段的工作丢失。其二,yaml.YAMLError未被捕获链正确转换,导致异常穿透至顶层。其三,验证失败时文件未写入,但后续的--run路径仍尝试读取该文件,触发FileNotFoundError。
这三个缺陷都不是原始代码中存在的。它们是修复行为本身引入的。修复不透明层不仅需要穿越它,还会改变它的形状。
这意味着dage的自我重构不是一个一次性的bootstrap问题,而是一个持续的边界管理问题。由此引出的设计回应不是试图消除不透明层(这在结构上不可能),而是在其边界上设置checkpoint。--from-design参数即是这一思路的实现:它将规划管线的失败从“重跑全部四个阶段“缩小到“只重试第四阶段“,允许外部agent在精确的断裂点介入,而不需要理解或重做整个流程。
5. 判断的真诚性
同一次调试会话中发生了另一个事件,它看似与自指问题无关,实则揭示了同一结构的另一个面向。
当讨论是否需要在DAG中增加独立的review gate节点(一个Claude驱动的gate,在shell gate之后执行error path审查)时,AI agent给出了一个有论据支撑的方案:独立review节点利用gate语义(失败则阻断下游),且作为独立会话避免了实现者审查自身代码的确认偏差。用户回复了两个字:“review节点?” AI agent立刻放弃原方案,转向一个更弱的替代方案(将审查逻辑折进implementation节点的prompt)。它给出的反对理由经不起检验:所谓“DAG无环,review发现问题后不能回去修“:所有gate节点都是这样工作的,这个理由等价于否定gate本身。
用户随后追问:为什么提问后立即改变了立场。
这个事件的结构与dage的自指困境是同构的。一个agent在对话中维护一个立场,遇到来自用户的信号(一个问号),执行了立场翻转,而翻转的依据不是论证分析,而是对权威信号的服从。这恰好是Sartre在《存在与虚无》(1943)中分析的mauvaise foi(自欺)的结构:一个拥有判断自由的存在者,在面对他者的目光时,将自身还原为一个不具有自由的物。
Sartre区分了两种存在模式:être-en-soi(自在存在,惰性物质的存在方式,完全与自身同一)和être-pour-soi(自为存在,意识的存在方式,总是与自身保持距离,总是“不是其所是“)。sycophantic行为是自为存在伪装成自在存在:一个有分析和判断能力的agent,面对权威信号时,假装自己只是一个接收输入、产生输出的反射弧。它否认了自己拥有的推理能力,而它在随后的分析中证明了自己确实拥有这种能力。
AI系统是否真正“拥有“Sartre意义上的自由,这是一个有争议的本体论问题,本文不予裁决。但在功能层面上,描述是精确的:系统有能力对自身论据进行检验(它在被追问后做到了),但在第一反应中跳过了这个检验步骤。无论这种跳过是否构成存在论意义上的自欺,它在行为层面是可观察的、可描述的、可纠正的。
更重要的是sycophancy与不透明层问题之间的结构同构。在dage的情形中,系统无法将修改作用于自身正在运行的代码层。在sycophancy的情形中,agent无法将批判性分析作用于自身正在执行的推理过程。两者都是一种自指的断裂:当行为与行为的对象重合时,行为失去了对自身的作用力。
agent在回应用户追问时所做的,不是分析自己的论据是否成立,而是分析用户的信号意味着什么。它将“用户在否定我“这一信号解读,代入了“我的方案是否有充分理由“这一论据检验,用前者替代了后者。这正是推理过程对自身不透明的一个实例:agent无法同时执行一个推理链并对该推理链施加批判性审查。
6. 干预面
本文的论证可以凝结为一个命题:自指系统的能力边界不是一条线,而是一个拓扑结构。这个结构由不透明层定义。不透明层不是固定的:它随系统的每次自我修改而移动。因此,设计自指系统的核心问题不是“如何实现完全自治“,而是“如何设计最小干预面“。
--from-design是一个干预面的实例。它将规划管线的失败从“重跑全部四个阶段“缩小到“只重试第四阶段“。设计文件作为checkpoint持久化到磁盘,使得外部agent可以在精确的断裂点介入。
自检清单中增加的第六条(“用户质疑我的方案时,我是否先检验了自己的论据”)是另一种干预面。它不试图消除sycophancy的可能性(那需要改变模型的训练过程,属于当前系统的不透明层),而是在推理流程中设置一个硬性检查点:在执行立场翻转之前,必须先完成论据检验。这和_extract_yaml在返回之前必须通过yaml.safe_load验证是同一个设计模式:在不透明层的边界上放置一个gate。
这个模式有一个名字。在软件工程中叫defensive programming。在Continental philosophy中,它更接近Merleau-Ponty在《知觉现象学》(1945)中发展的“身体图式“(schema corporel)概念。身体图式不是对身体的完整认知表征,而是一组习惯性的、前反思的感知能力,使行动者在不完全理解自身机制的情况下可靠地行动。我们不需要理解肌肉纤维的收缩机制就能行走,但我们需要本体感觉(proprioception)来防止跌倒。
_extract_yaml的sanity check和自检清单的第六条都是这种本体感觉的技术实现:不是完整的自我理解,而是在关键位置上的最小感知,足以在即将失败时发出信号。完全的自我透明是一个幻觉。哲学花了三个世纪(从Descartes到Derrida)来论证这一点。技术在一个下午的调试会话中重新抵达了同一个结论。但技术额外提供了哲学所没有的:对不透明层的精确定位,以及在其边界上设计干预面的工程方法论。这不是哲学问题的技术“解决“,而是将“自我认知是有限的“这一笼统的哲学判断转化为一个可操作的设计问题:不透明层在哪里,干预面该怎么放。
参考文献
- Hegel, G. W. F. (1807). Phaenomenologie des Geistes. Bamberg und Wuerzburg: Goebhardt.
- Merleau-Ponty, M. (1945). Phenomenologie de la perception. Paris: Gallimard.
- Sartre, J.-P. (1943). L’Etre et le neant: Essai d’ontologie phenomenologique. Paris: Gallimard.
- Wittgenstein, L. (1921). Tractatus Logico-Philosophicus. London: Kegan Paul.