上一讲讨论了 DeepSeek Harness(dsh):一个简洁的 Agent 循环,怎样被真实世界的需求包围,逐渐长成能够承载扩展生态的系统。这一讲给它加上时间维度。今天看到的架构、规则和测试,是怎样一步一步形成的?当 AI 能完成越来越多的修改,人又应该怎样决定软件往哪里走?
开场依然是“日常震惊瘫坐”。2026 年 10 月 26 日,OpenAI 发布研究消息,声称其模型得到了否定指数时间假说(Exponential Time Hypothesis,ETH)的结果。这里保留“声称”:发布方也把模型产出的文稿视为需要学界进一步理解与独立检验的研究材料,而不是把发布消息等同于验证已经结束。具体说明见 OpenAI 的研究公告。
这类消息引出的更大问题是:人类用于理解世界的大脑、数学语言与程序,是否也会遇到失效的边界?我们习惯寻找优美的对称、简洁的规律,并用自己能够驾驭的分类整理问题。例如,多项式时间类 P 有方便的归约与封闭性质,适合建立理论,但它把 与 放在同一个抽屉里。这个抽屉很好用,却没有穷尽计算问题的结构。如果追问“一台图灵机在给定的 步内,究竟能完成这个问题的哪些部分”,熟悉的粗粒度分类未必能直接给出有用答案。
于是可以提出一个带有挑衅意味的猜想:也许世界本来的结构很不规整,我们只是恰好生活在一个足够“平坦”、能够被现有工具理解的局部。又或者,只有存在可复用规律的环境,经验才有机会被压缩成预测和行动,智能才有机会产生。对称是可复用结构的一种,但可压缩的结构不一定都长得对称。这些是关于理解与智能的思想实验,不是已经证明的结论。AI 也许会找到人类表示习惯之外的结构;届时,得到一个结果和理解这个结果之间,可能仍然隔着很长一段路。
这种变化也会碰到我们的日常生活。“家庭投资教育,大学培养人才,企业购买劳动”是一个熟悉的循环。如果多年训练得到的能力变成随时可以调用的服务,能力的价格会变;如果一份漂亮成品很容易由 AI 生成,用成品证明个人能力的方法也会变。
一个当下就值得尝试的方法,是用 AI trajectory,也就是人与 AI 协作的过程轨迹,展示自己的能力。除了最后做出了什么,还可以看:怎样提出问题,怎样拆解任务,何时不接受模型的回答,拿什么证据纠正它,以及第二次遇到同类问题时是否做得更好。这些过程可能比一份精修后的结果更能体现判断力、成长力和创造力。两个人交换轨迹,也许很快就能发现是否“英雄所见略同”——甚至可以拿来匹配交友。
“上学也是期货投资”是一个刻意尖锐的比喻:今天付出学费、时间和放弃的工作收入,押注几年后市场仍然需要自己学到的技能,但没有企业提前承诺收购。这不是说教育只有就业价值,而是提醒我们关注投入与回报之间的时间差。沿着这个思路推下去,就有了“贷款上班”“证明你比 AI 强,否则交罚款”的暴论:为了继续卖出劳动,人是否还得不断自费购买下一轮训练?它不是对就业制度的事实描述,而是在追问,技能贬值与再训练的成本最后由谁承担。
上一讲把 dsh 看成一个“操作系统”。也可以把它看成航母:平台已经开始搭建,飞机还在试验,围绕它的完整协作方式也在探索。值得期待的是 Agent 生态,最终成功的平台未必就是今天的 dsh。
看到停泊在圣迭戈的 USS Midway,很容易产生一个疑问:1945 年的人怎么能造出如此复杂的系统?如果只看最后的成品,复杂性像是凭空出现的;把时间轴展开,就能看到能力怎样积累,任务怎样改变,又怎样反过来改变结构。
课堂用几种船的故事展示这条线索。帆船依靠帆、索具与船员配合,把外部的风转化为推进力;汽船把燃料、锅炉和机器带上船,获得更主动安排航行的能力,同时也背上了供水、补给和维护的要求。到 USS Langley,原来的运煤船改装成了组织飞机起降的平台。加一层飞行甲板只是开始:飞机怎样停下,怎样腾出甲板,怎样从存放空间送上来,都要靠设备和操作流程一起解决。
其中一个很有工程味的细节是升降机。即使装上了升降机,它降到底的位置与原有甲板之间仍可能有高度差,还需要滑道与人员配合才能完成转运。“有这个部件”与“整个流程能工作”之间,总会冒出新的接口问题。到 Midway 这样的航母,飞行甲板、机库、动力、防护和保障空间已经需要在整舰设计中共同安排。这里比较的是能力与组织方式的变化,并不是说这些船构成一条直接的船型谱系。
软件也有这样的时间轴。一个平台的能力,既在代码里,也在团队的工作方法和周围的生态里。课程中说“我们也在造航母了”,说的正是这种实践:起初连老师在内都做全栈,随着工作扩大,分工、接口和协作方式还会继续变化。今天的组织形式,也只是演化中的一个状态。
软件早期通常只有一个精简的 idea。随着问题本身的要求增加,本质复杂性也会增加。有些结构经受住了时间的考验,有些局部被替换,还有些最初看起来很漂亮的设计,后来成了阻碍。
过早引入复杂架构,可能反而约束未来。每个提前设计的扩展点,都暗含着一个预测:“未来会沿这个方向变化。”一旦预测错误,扩展机制本身就变成了需要维护的负担。因此,值得追问的不是有没有足够多的层,而是哪些决定容易变化,以及变化时需要牵动多少地方。
Linux 的调度器给出了一个例子。CFS 在 Linux 2.6.23 中进入内核,替代此前的 O(1) 调度器;调度类机制则把不同策略放在相应边界之后。算法可以被淘汰,边界仍然能帮助系统容纳下一次变化。这个例子说明,局部重构也可以成为整理职责的机会。相关机制见 Linux 的 CFS 设计文档。
Python 2 到 Python 3 展示的是另一种阻力。Python 3.0有意打破向后兼容,重新整理了文本与二进制数据等语义。但已经依赖旧行为的程序不会自动迁移,库与应用也需要彼此等待。Python 2 原定在 2015 年停止支持,后来延长到 2020 年,原因之一正是大量用户尚未完成迁移。架构变更的成本,必须把所有依赖旧行为的代码算进去,而不只是看解释器内部改了多少行。Python 官方回顾记录了这一过程。
Kubernetes 则适合用来理解“核心轻、生态重”。它的核心想法可以说得很短:描述期望状态,控制器反复观察实际状态并采取动作,让两者趋于一致。例如,期望某项服务有若干副本,就持续发现偏差、补足副本。这个循环是理解系统的入口,见 Kubernetes 的控制器说明。不同领域的扩展可以围绕类似机制组织自己的知识,而不必让核心承担所有具体业务。
这种边界也需要持续调整。Kubernetes 1.24 移除了内置 dockershim,把与 Docker Engine 的适配移到核心之外;这不意味着 Docker 构建的镜像不能继续使用。“轻”在这里体现为责任边界:专用适配可以独立维护,核心通过统一接口连接运行时。相关区别见 dockershim 移除说明。
把时间维度加进来以后,软件仍然可以用状态机理解。某个版本是一个状态;修 bug、加 feature、做 chore(日常维护)、进行 refactor(重构),都是让项目进入下一个状态的动作。一次修改能否成立,要看它如何改变已有行为,以及是否仍满足必要约束。
我们学习时却常常从简化版本入手。研究 Linux,可以从 Linux 0.11 看起,再看 2.4、2.6;老师是在 2.6 时期入局的。学习操作系统、编译器和数据库,要先抓住核心机制;学习大模型系统,也可以借助 nano-gpt、nano-vllm 这样的精简实现。它们让复杂系统变得可以理解,但真实项目里的例外、兼容层和特殊处理,还需要从版本历史中寻找来由。
SWE-bench把这种“从一个状态走到下一个状态”的工作变成了评测任务:给出真实仓库及 GitHub issue,让模型修改代码,并检查是否解决问题。它把注意力从孤立的小函数移到了现有项目中的修改。课堂的判断是,语言模型已经能完成大量这样的工作;官方记录中,2025 年 7 月的 mini-SWE-agent 已在 Verified 子集取得 65% 的成绩。不同子集和运行设置需要分别看待,不能把这个数字当作所有工程任务的成功率。
由此产生一个关于 scaling 的直觉:只要某个 benchmark 已经“有一点点希望够到”,持续投入模型能力、训练和工具,最终很可能把它攻下来。这是一种趋势判断,不是保证任何任务都会被解决的定律。它更有意思的推论是:当通过测试越来越容易,我们需要怎样的新评测?
也许需要一个 taste-bench,评价做软件的品味。两份实现都能通过测试,哪份更贴合问题、更容易理解、给下一次修改留下的负担更少?这不容易压成一个 Yes/No 或总分。可以先在同一需求下做多维度的相对比较,再加入后续需求,看看原来的优劣是否反转。软件的“好”,本来就包含它将来怎样继续变化。
dsh 对教学很慷慨的一点,是能够看到从第一天起的提交历史。阅读这样的项目,不必一上来就吞下整个当前版本。挑一条修改,追踪它为什么出现、怎样划分、如何证明有效,往往就能学到很多东西。可以从课堂整理的提交历史开始。
首先是设计理由怎样留下来。RFC(Request for Comments,征求意见的提案)用于讨论方案,ADR(Architecture Decision Record,架构决策记录)保存最后的选择及理由,Agent Notes 则保留对后续工作有价值的上下文。它们帮助后来者理解问题、备选方案、取舍与验收标准。普通局部修改未必需要一篇设计文档;记录的价值在于帮助下一次决策。
其次是工作怎样划分。原子提交让一个逻辑变化能够单独理解和检查;一个 PR(Pull Request,合并请求)对应一个 worktree(同一 Git 仓库的独立工作树),让不同任务各自修改、安装和测试;有依赖的工作可以组织成堆叠 PR,让上层建立在下层修改之上。不过,隔离也有边界。课堂材料中,dsh 曾遇到在一个 worktree 安装 Git 钩子管理工具 Lefthook,却影响其他 worktree 的 hooks(提交、推送等操作触发的脚本)的问题。这提醒我们,目录分开了,不代表所有底层状态都分开了。工程规则也要通过实际故障检验。
第三是验收怎样分层。逐文件 100% 覆盖率可以推动开发者处理错误路径与清理逻辑,但执行到一行代码,不等于它的行为已经正确。Session 录制与回放固定模型响应,可以检查组装后的事件、持久化结果和用户输出,也避免每次回归都依赖真实模型服务。真实产品入口与发布产物还需要另外验证:从源码运行正常,不代表安装包带齐了 worker、资源和依赖;在错误端口上打开一个页面,也不能证明刚修改的 GUI 已经生效。
最后是经验怎样进入下一轮工作。一次事故可以留下复盘、修复提交和回归测试;被采纳的评审意见可以整理为候选规则,再由维护者审阅并更新 Skill。如此一来,下一位开发者或 Agent 不必完全依赖记忆。但规则也会积累负担,所以 Agent Notes 要归档,重复要求要清理,检查要考虑运行时间。流程本身也是软件演化的一部分。
AI 在很大的设计空间里采样,仍然容易迷失方向。项目总体上仍由人驾驭:决定哪些变化值得做,哪些行为必须保持,以及什么证据足以支持一次修改。
上一讲的双语文档就是一个具体例子。修改一侧后,同步另一侧并更新配对记录,可以防止两组读者拿到不同的操作方法。配对检查能发现漏同步,却不能独自判断译文是否准确。为了约束 Agent,仓库里也会多出 YAML 文件、同步记录和各种规则,读目录的人反而更难看懂全貌。IDE 可以把这些信息重新组织起来,缓解这种负担。这也是“软件项目不该只用目录存储”这个观点的含义:我们真正关心的是需求、实现、文档、验证之间的关系,目录树只能表达其中很有限的一部分。
生产“与人类对齐”的好代码,一种可执行的方法是:找出不好代码的特征,把它们变成检查条件,失败就修正或重新采样。接口改了而调用方没改,可以用类型检查发现;发布包漏了文件,可以通过安装真正的包发现;文档仍然推荐已经删除的配置,可以结合生成内容检查与评审发现。自动检查、人工评审和专项验证各自覆盖不同问题,不能因为某个持续集成(CI)任务变绿,就认为所有层面都得到了证明。课堂的检查规则整理给出了各类规则的动机、例子和适用条件。
课堂整理 dsh 的检查规则时,还发生了一个小插曲:因为工作发生在 dsh 项目中,Agent 自动触发了 dsh-prose-standard skill,生成的说明也受到该仓库文风规范的影响,之后又加入了一些引导提示词。同一个写作任务,仅仅换一个工作上下文,就可能被另一套规则塑形。规则怎样进入 Agent 的视野、适用于什么范围,本身就影响产物。
困难在于,表达“坏”通常比表达“好”容易。我们很容易指出一个具体错误,却未必能把自己真正想要的东西完整说出来。模型能力很强,但有时过分自信,有时过度谨慎,还常常分不清轻重。
“番茄炒蛋(不放蒜版)”是一个很贴切的比喻。用户纠正“不要放蒜”,本意可能只是让模型自然地给出合适做法;模型却把这次纠正变成了标题里最显眼的内容。接着纠正香菜、韭菜、辣椒,就得到“番茄炒蛋(不放蒜、不放香菜、不放韭菜、不放辣椒……)”。每条要求似乎都被遵守了,成品却越来越奇怪。可以把它理解为,出现在上下文中的词被突出强调,而无数同样不该出现、却从未被提到的词没有得到同等对待。这是对行为的解释性比喻,不是对模型内部权重的测量。
反例引导的归纳合成(Counterexample-Guided Inductive Synthesis,CEGIS)提供了一个理解这种失败的角度:先提出候选,再寻找反例,把反例加入约束,然后提出新候选。用简写表示,就是从 开始,逐渐累加为 、,其中 表示要排除的一类坏情况。这个公式描述的是约束不断增加的过程,并不自动保证最终能得到想要的答案。
一个极端例子是:在无界整数里找一个 ,使所有整数 都满足 。每次验证者拿出反例 ,生成者就换一个尚未出现过的 。它总能通过此前积累的有限反例,却永远完成不了目标,因为目标本来就无解:对任何 ,都可以取 。这类例子见 Solar-Lezama 的程序合成课程。它说明需要检查搜索空间和目标,而不是说反例引导的方法总会失败。
更常见的困境是,答案并非不存在,只是我们一直困在错误的表达方式里。比如“别写这么长”可能是在批评篇幅,也可能是在批评答非所问;模型若只新增一条“回答要简短”,下次可能删掉真正需要的解释,却继续回答错误的问题。某套去除套话的规则在一种语言中有效,换到中文也未必仍然奏效。
这时需要设计空间的跳跃。与其一直要求“把这份答案改好一点”,可以重新考虑读者要做什么决定、真正需要哪些信息,以及是否应当换一种组织方式。对于代码也是如此:不断给原方案增加分支,可能永远比不上重新划分状态或接口。局部反馈未必包含逃出当前陷阱的线索。
老师尝试过一个非常贴身的任务:手里有自己回复过的邮件,能不能把这些材料用好,让模型学会自己的判断和表达方式?到这次课为止,这个尝试仍然失败,没有收敛。
尝试的循环很直接:让模型从邮件中增加 guideline,生成回复,再评价是否更像本人,然后继续增加 guideline。这是一种递归改进的尝试,但规则越来越多,并不意味着模型越来越理解这个人。“回复要简短”和“解释要清楚”都像好规则,真正困难的是结合来信、关系和情境,判断此时哪一条更重要。
一种可能的原因是,邮件保存了“最后怎么说”,未必保存了“为什么这样说”,更没有完整保留那些被放弃的选项。同样一句简短回复,可能来自事情很简单,也可能来自双方已经有充分共识;只模仿表面形式,会丢掉决定形式的条件。这仍是失败原因的推测。
一个值得验证的方向,是让同一封来信对应几种不同回复,比较各自的取舍理由,再用没有参与增加规则的新邮件检验这些取舍能否迁移。但它还不能作为已经成功的方法来介绍。目前模型的“悟性”仍然不够:我们能够越来越精细地排除已知错误,却还没有因此获得一个知道何时应该打破惯例、重新选择方向的自己。