AI编程工具不是装好就能用:两套主流系统的工程约束设计对比
很多团队在引入AI编程工具时,会碰到一个规律:刚上手觉得什么都能做,用一段时间后发现系统越来越难控制。长会话里上下文混乱,工具调用顺序出错,改完的代码说不清改了什么,多个代理协作后责任归属模糊。这些问题看起来是模型能力不足,实际上更可能是约束结构没有做好。
约束结构,在工程领域有一个专门的词叫harness。直译是”挽具”,套在马身上用来控制方向的装备。对于AI编程系统来说,harness就是一整套持续生效的控制结构,用来约束模型在终端、文件系统和团队流程中的行为边界。
目前有两套主流的AI编程系统,Claude Code和Codex,都在认真做harness,但它们的做法差异很大。理解这些差异,对团队选型和自行构建系统都有实际帮助。
harness解决的核心问题:让模型别乱来
一个只会输出文本的模型,说错了最多浪费理解成本。但一个能运行命令、写文件、访问网络、修改仓库的模型,出错后留下的是执行结果。目录会变化,进程会中断,配置会损坏,Git历史会变得难以追踪。
到了这一步,核心问题不再是模型够不够聪明,而是系统有没有提供足够约束。没有约束的能力只会扩大事故半径。
Claude Code和Codex都承认一件对产品宣传不太友好的事实:模型不能直接拿来当执行体。它会误判,会忘记上下文,会把语气里的自信和结论里的正确性混为一谈。一个系统要是愿意承认这一点,它就早晚会长出prompt分层、状态持久化、权限控制、上下文治理、失败恢复、验证机制和本地制度这些结构。
这两套系统都越过了”只要模型更强,系统问题就会自动消失”的阶段。它们都把harness当成真正的控制层,把模型当成这层控制之下最不稳定、但也最有生产力的部件。
两种harness设计哲学的根本差异
Claude Code和Codex的差别,不是功能多少的问题,而是秩序安放在哪一层的问题。
Claude Code更接近从运行时事故经验里长出来的系统。它优先解决的是会话如何连续活下去,工具如何别闯祸,恢复如何别把系统拖进死循环,验证如何别沦为形式。它的秩序主要住在运行时。
Codex更接近从显式结构设计里长出来的系统。它优先解决的是控制层命名、策略表达、边界清晰和可组合性。它的秩序主要住在系统外沿的制度层。
打个比方。Claude Code像一个做过很多脏活的现场总工,知道现场会出什么乱子,因此把很多本该由”聪明”解决的问题提前交给控制流和恢复流。Codex像一个新来但制度意识极强的项目经理,先把规则贴出来,把类型定义好,再开始协调人做事。
两种路数都合理,只是合理的地方不一样。
控制面设计:动态装配 vs 结构化片段
控制面指的是系统用什么方式告诉模型”你能做什么、不能做什么”。很多人谈控制面,容易落到文风上,仿佛核心是把一段文字写得更像老工程师。这样理解控制面,多少有点把警察制度理解成说话语气。
Claude Code采用层层拼装的方式。它的system prompt不是一个固定文本,而是一条装配线。默认prompt是底板,append prompt是外加要求,agent prompt是特定角色的补充,CLAUDE.md和memory又带来现场条件。这样做的好处是让同一个主循环适配很多不同场景。代价是必须非常在乎装配顺序,否则一层层拼上去以后,系统很容易出现相互覆盖和语义稀释。
实际操作中,这意味着你在设计prompt时要考虑清楚优先级。Claude Code的做法是把prompt来源排成一条明确的链:覆盖系统prompt优先级最高,然后是协调者prompt、agent prompt、自定义prompt,默认prompt优先级最低。这种设计允许自定义,但不放弃秩序。
Codex的做法更接近结构化片段。它不把指令当成一块随意串接的自然语言,而是把它当成”有开始、有结束、有来源类型”的上下文单元。AGENTS.md不只是读进来,还是一个明确的片段;skill也不只是附录文本,而是一个明确包裹过的上下文单元。
这种差异在本地规则文件上体现得最清楚。Claude Code强调CLAUDE.md,它更接近团队或目录范围内的长期工作约束,有一种工程现场公告板的气质。Codex强调AGENTS.md,而且进一步讨论规则的适用范围和继承关系,它在乎”规则的适用范围和优先级如何被系统明说”。
实际选择时,要看你的系统更怕哪一种失控。怕长会话里指令失真、现场变化太快,就更容易欣赏动态装配。怕规则来源不清、作用域模糊、无法系统化治理,就更容易欣赏结构化片段。
会话连续性:主循环心跳 vs 线程状态基础设施
把代理系统理解成”多轮聊天”,就像把数据库理解成”一个比较耐心的记事本”。代理系统真正的难题是连续性:上一轮做了什么,这一轮怎么接;工具结果怎么回填;中断之后怎么收口;上下文太长以后怎么整理。
Claude Code把连续性压进主循环。在它的query loop里,当前消息序列、工具调用上下文、压缩跟踪、输出token恢复计数、待处理摘要、轮次计数、状态转换这些内容被放在同一个跨迭代状态里。系统的骨架因此更接近一个不断自我校正的会话发动机。
这意味着Claude Code对”代理如何活着”这个问题的回答是运行时性的。连续性主要由循环维护。很多会话里的真实麻烦恰好都发生在循环里:工具返回的顺序、模型输出突然截断、提示词过长、历史裁剪、用户插话。Claude Code不试图回避这些问题,而是把它们作为循环内部的合法状态来处理。
Codex把连续性拆成线程、rollout和状态桥。连续性并不只存在于一个巨大的循环里,它被分摊到线程管理器、rollout、状态桥、状态对象、消息历史这些结构中。thread是外部开发者可以直接理解和操作的一级概念,持有ID,可以流式运行,也可以同步运行;审批策略、工作目录、沙箱模式、网络访问等执行条件,都是和线程运行紧密耦合的显式参数。
这种差异会直接影响恢复和审计。Claude Code的恢复强项在于离现场近,很多问题就在循环内部被发现和修复。Codex的恢复强项更可能体现在状态可追踪性,线程有ID,rollout有记录,状态桥和消息历史提供了更清楚的外部结构。
简单说,Claude Code更接近现场救火队,Codex更接近带档案系统的调度中心。前者更擅长活下来,后者更擅长说清楚自己是怎么活下来的。
工具调度与权限:运行时审批 vs 策略语言
一个模型说错话,通常只是浪费时间。一个模型执行错命令,就可能顺手把目录、仓库、进程和工作流一起带坏。真正把AI编程系统区分开来的,是它调工具之前谁拥有最后解释权。
Claude Code的工具系统有一种很强的现场调度感。并发要看schema和并发安全性判断;上下文修改要保证回放顺序稳定;流式工具执行还要考虑中断、合成结果和界面反馈。它很像给模型装了一个工地监工:工人可以干活,但监工得盯着先做哪一个,哪些能并行,哪些必须串行,做完以后怎么记账,干到一半被叫停怎么办。
尤其是Bash这类高风险工具,Claude Code的态度可以说是唠叨。围绕git和PR写了一整段操作规约:不要乱改git配置,不要跳过hooks,不要随手git add,不要在pre-commit失败后用amend把上一条提交也搭进去。高风险接口通常就需要高密度约束。
Codex则把工具本身先做成类型化接口。以命令执行工具为例,它会显式拥有命令、工作目录、shell类型、终端模式、超时时间、最大输出token、登录信息、审批相关参数这些字段,而不是简单接收一个字符串命令。工具在Codex里首先是规范化的API面,其次才是执行单元。
更进一步,Codex把审批和权限提升抽成显式参数,把请求权限做成单独工具,又把执行策略单独做成模块,包含策略、规则、评估、决策、解析器等概念。这套命名说明执行边界已经形成一门小型政策语言,而不只是几个条件判断。
实际操作中的选择:Claude Code的审批逻辑很适合与具体工具调用现场紧密耦合,长处是灵敏,缺点是规则更容易藏在运行时逻辑里。Codex的策略思路尽量把规则抽离出来,让它成为可以单独解析、单独评估的实体,长处是规则可读性和可迁移性更强,缺点是系统会显得更重。
上下文治理:预算制度而非无限仓库
人一旦可以往上下文里不停塞东西,就很容易相信信息越多系统越聪明。可惜代理系统不是图书馆,上下文不是一个”存进去就算拥有”的仓库,它首先是一笔昂贵、易膨胀、还会自我污染的预算。
Claude Code在上下文治理上做了几件很实际的事。
第一,长期指令分层。它把指令来源分成托管内存、用户内存、项目内存、本地内存,按优先级和目录距离加载。离当前工作目录越近的项目规则,优先级越高。这避免了把长期协作规则和本轮临时对话混成一锅粥。
第二,记忆入口必须短。MEMORY.md被定义为索引而不是正文,保存记忆要两步走:先把具体内容写进独立文件,再在索引里加一个一行指针。入口文件有行数和字节上限,超过了直接截断并追加警告。入口文件一旦既当目录又当正文,最后就既不是目录也不是正文。
第三,短期连续性结构化。会话记忆有固定模板,包含当前状态、任务规格、文件和函数、工作流、错误与修正、代码库文档、学习记录、关键结果、工作日志这些栏目。更重要的是有明确的预算上限,超过预算要求激进取舍,优先保留当前状态和错误修正。
第四,压缩是预算治理。自动压缩会先从有效窗口里扣除摘要输出预算、警告缓冲区、错误缓冲区、手动压缩预留空间。压缩不是等到窗口只剩一口气时才想起求生。而且压缩有失败熔断机制,连续失败超过阈值就直接跳过,防止把API调用浪费在注定失败的尝试上。
第五,压缩的目标是重建可继续工作的上下文。压缩之后不只是保留一条摘要,还要清空旧的文件读取状态,重新生成后压缩文件附件,把计划附件、技能附件、延迟工具等补回来,执行压缩后的hooks,记录压缩边界信息。压缩更像一次受控重启,而不是一次聊天总结。
实际操作中,这些设计传递的原则是:长期规则、长期记忆、会话连续性应该分层,不该混写;入口型记忆必须短小;会话摘要应该服务于”继续工作”而不是”回忆完整”;压缩后的上下文必须保住运行语义,而不是只保住语言表面。
错误恢复:按主路径设计而非例外处理
工程世界最不值得相信的话,就是”正常情况下”。代理系统一旦进入真实运行环境,错误就会成为稳定存在:模型会超token,会触发提示词过长,会撞上最大输出限制,会遇到工具拒绝、用户打断、hook阻塞和API重试。
Claude Code对错误与恢复的理解,核心目标是保护执行叙事的一致性。就是系统还能不能说清楚:刚才试图做什么,为什么没做成,用了什么恢复路径,现在是继续、停止还是换轨。
以提示词过长的处理为例,Claude Code先尝试更便宜、更保守的恢复路径。若启用了上下文折叠,就先提交已暂存的折叠;如果还不行,再进入响应式压缩。恢复是分层的,先排空已知积压,再做更重的全文压缩,不会一上来就重建世界。
对最大输出token的处理,Claude Code先试提升token上限直接重跑同一请求,没有插入元消息,也没有让模型先寒暄,系统先给它一次把原任务做完的机会。如果还不够,再追加一条指令:直接继续,不要道歉,不要回顾,若中断发生在半句就从半句接着写。在长任务里,每一次截断后的回顾都会进一步消耗预算并增加语义漂移,最后系统做的就不再是任务本身,而是一轮轮地回顾自己做任务。
自动压缩的失败熔断也很说明问题。一旦连续失败次数超过阈值,即使判断”按理说该压缩了”,系统也会直接跳过。恢复机制本身也要受治理,否则恢复会从保险丝变成新的起火点。
更极端的情况是压缩请求自己也可能提示词过长。Claude Code的处理方式是把更早的API轮次从头部剥掉再重试压缩。这种修复有损也会丢历史,但它优先保证用户不被完全锁死。
中断也是错误恢复的一部分。如果流式输出时用户打断,系统会先消费剩余结果,为已经发出但尚未完成的工具生成合成结果,确保前面承诺过的工具调用不会变成悬空债务。不能因为用户打断了就假装前面的动作从未发生。
实际操作中,错误恢复应该遵循这些原则:恢复路径按成本和破坏性分层,不要所有问题都打一把重锤;恢复逻辑必须防止自我回环;自动恢复需要计数和熔断;截断后的最佳恢复通常是续写而不是总结;中断也是一种需要语义收尾的失败态。
多代理协作:职责分区而非人海战术
单代理走到一定程度,问题就不再是”会不会做”,而是”怎么分工”。研究、实现、验证都挤在同一条上下文链上,彼此抢预算、抢注意力、抢叙事中心。
Claude Code对子代理的第一原则是缓存安全。分叉时必须共享缓存关键参数确保提示词缓存命中,否则子代理每次都把父上下文重新烧一遍token,看上去像在并行提效,实际只是把浪费并行化。这些参数包括系统提示词、用户上下文、系统上下文、工具调用上下文、分叉上下文消息等。
第二原则是状态隔离。默认情况下所有可变状态都隔离,避免干扰主循环。文件读取状态先克隆,中断控制器生成子控制器而不是直接共享。只有在明确选择加入的情况下才会共享某些回调。共享要靠明确同意,隔离才是默认伦理。
协调者模式强调综合才是稀缺能力。当worker回报研究结果后,协调者必须先读懂,再写出具体提示词,不能说”根据你的发现”然后把理解继续外包给worker。研究可以分布式,但理解必须重新收束。
验证必须独立成阶段。Claude Code把常见任务分成研究、综合、实现、验证,并强调验证的目标是证明代码有效而不只是确认代码存在。实现worker自证一遍,验证worker再作为第二层质量保证。在代理系统里,”我改了代码”和”代码因此正确”之间隔着一条很宽的河,模型尤其擅长在这条河上搭纸桥。
子代理有完整的生命周期管理。启动时可以观测,停止前可以介入,转录路径可追踪。每个异步代理都会注册清理处理器,父中断可以自动传播给子中断控制器,任务结束后要清理输出、更新状态、解除清理注册。
多代理真正有价值的地方,在于把不同种类的不确定性关进不同容器里,再用协调者把它们组织回来。这种做法比单纯追求并发更稳,也更符合工程要求。
Codex在多代理上的做法差异在于,代理委派被更明显地定义成工具接口。委派动作更容易被记录、审计和组合,因为是显式工具调用而不是内部运行时逻辑。代理协作更容易与线程、状态和审批体系对齐。这种设计更适合把多代理做成平台能力。
团队落地:把个人技巧变成可复用制度
个人能用不等于团队能承受。个人技巧之所以有效,往往因为它依赖个人持续盯防、个人背景知识和个人临场判断。团队一旦接手,你不能再假定每个人都知道哪些命令其实危险,哪些记忆已经过期,哪些技能会把任务分叉出去。
团队级CLAUDE.md的价值不在于写满,而在于分层稳定。适合放的是代码库级硬约束、统一验证口径、输出纪律、常见协作约束。不适合放的是会频繁波动、只对少数任务生效、或者本来就该由技能承担的具体流程细节。CLAUDE.md一旦被写成百科全书,很快就会没人愿意维护。
技能应该被视为可复用的制度切片,而不是长一点的提示词模板。 Claude Code的技能执行往往在分叉的子代理上下文里,有自己的token预算、自己的上下文隔离、必要时还带上允许的工具集合。团队落地时,只有把技能视为制度切片,才会认真去明确它的适用边界、允许动哪些工具、应该直接执行还是分叉到子代理、输出物和验证方法。
审批用来替团队划责任边界。谁批准的,为什么可以自动过,哪些场景必须询问,哪些规则能被hook覆盖,哪些绝对不能,这些都必须有制度。审批不要按工具种类粗暴切,而要按后果不可逆性和环境敏感度切。读文件、列目录、纯分析一般可以放宽;改工作区、改配置、推Git、打外网、碰生产资源就该明显收紧。
hook的真正用途是把制度挂到生命周期上。Claude Code预留的事件包括子代理启动和停止、压缩前后、停止失败、指令加载、会话开始和结束、目录变更、文件变更。让规范有机会在正确时机发生,比把所有制度都塞进静态规则强得多。
如何选择适合自己团队的方向
如果你已经有agent原型但长会话经常失控,常见症状是上下文越来越乱、工具调用链断裂、中断后状态说不清、子代理跑完以后没人收口、验证变成随口一说。这时候最该补的是主循环纪律,先把运行时心跳做稳,再谈制度美学。
如果你已经有不少规则但规则来源过散、权限边界不清,常见症状是本地规则四处散落、哪些约束进了提示词哪些进了工具没人说得清、审批逻辑混在代码里难以解释、多种扩展能力接进来以后边界越来越模糊。这时候最需要的是把控制层显式化,先把指令、工具、策略、线程这些概念立住,再让运行时在里面工作。
如果你还没有成型系统准备从零开始,最危险的是同时羡慕两边的优点然后把自己搞成折中失败品。更稳妥的路线是先选一个主矛盾,先围绕主矛盾设计主骨架,另一边只补最低限度。
优先从Claude Code学习的部分:主循环的状态心智、压缩与上下文治理、工具编排与中断处理、子代理生命周期和验证独立、把失败路径当主路径设计。
优先从Codex学习的部分:指令片段化、工具schema化、审批策略的显式表达、线程和状态的基础设施化、hook事件和技能资产管理。
一个常见的危险误区是把”显式”与”灵活”误认为天然对立。显式并不必然僵硬,灵活也不必然混乱。真正的问题在于你有没有清楚地定义哪些东西必须显式、哪些东西可以留给现场判断。
如果要从零开始做一套harness,按事故发生顺序排列的推荐顺序是:先定义高风险动作和最小权限模型,再定义主循环或线程生命周期,再定义上下文治理与恢复路径,再定义技能、本地规则与hook,最后再扩多代理、平台化和复杂生态。这个顺序不性感,但大致符合事故发生顺序。
学Claude Code,主要是学如何让系统在现场活下来;学Codex,主要是学如何让系统在组织里长久维持秩序。谁只学前者,容易变成经验过强、制度不足;谁只学后者,容易变成制度漂亮、现场发虚。真正值得做的,是根据自己的主矛盾决定先长哪根骨头,而不是选边站。
最后可以用六个问题检验你的系统:谁拥有最终控制权,模型还是harness;连续性主要住在循环里还是住在线程和状态里;工具动手前谁来拦最后一道;本地规则怎么进入系统、怎么分层;验证由谁负责、如何独立;出事以后团队靠什么追溯。这六句问下来,系统大概属于哪一派,通常也就清楚了。

