Harness就是后端:三大原语降低Agent复杂度

AI系统复杂度的根源在于把Agent和后端拆成两套东西。统一成Worker、Trigger、Function三件套后,复杂度直接下降,调试从抓鬼变成看一条线。

你现在做AI系统,脑子里默认有个设定:Agent是一层,Backend后端是一层。这就像你非要把炒菜分成锅的世界和菜的世界,结果每次炒个鸡蛋都要开两场会,锅说等等菜没准备好,菜说等等锅还没热。你就在中间当传话筒,累得半死。

但是,系统统一成Worker 、Trigger、 Function三件套会降低复杂度并提升可观测性。

你只要抓住这个因果关系,结构统一产生简单系统,结构分裂制造调试地狱。

行业争论跑偏了:大家一直在吵壳厚还是壳薄

现在行业主流玩法都在吵一个问题:Agent外壳要厚一点还是薄一点。有人说简单点靠模型自己想,有人说复杂点多加规则,两边吵得跟甜咸豆浆似的。Anthropic觉得壳要薄,让模型自由发挥,LangGraph觉得壳要厚,多套几层保险。

这个争论本身没错,但问题问偏了。你在问壳厚不厚,其实更大的问题是壳和身体是不是同一个东西。好比你在讨论外套要不要加口袋,但根本问题是外套和身体是不是长在一起的。如果本来就是连体衣,你还讨论什么厚薄。

传统架构的真实样子:Agent和后端是两家人

你现在的系统,大概率是这样跑的。Agent在Python或者TypeScript里跑着,看起来挺智能。调用工具的时候就发HTTP请求,像个传令兵骑着马去送信。后端那边等着接请求,再去处理数据库、队列、状态这些脏活累活。

看起来挺合理对不对?各干各的,分工明确。但这套东西在实际跑起来的时候,悄悄在搞事情。因为Agent和后端之间永远隔着一层网线,这层网线就是你debug时候的噩梦源头。我说几个真实场景你就懂了。

传统架构的隐藏坑:调试像在抓鬼

来,我们直接说最痛的地方:调试。你有一个Agent,它调用一个工具,这个工具触发一个HTTP请求,请求进了消息队列,队列唤醒一个处理程序,程序再写数据库,数据库返回结果,结果再一层层传回去给Agent。听着很顺,像流水线一样。

但当它挂了的时候,你要干啥?你要查Agent的日志看它发了什么请求,你要查HTTP服务器的日志看请求到没到,你要查队列的日志看消息有没有被消费,你要查数据库的状态看数据写进去没有。然后你开始拼图,像刑侦现场一样推理时间线。

以前这种系统还能忍,因为逻辑是确定的。输入A输出B,最多就是代码写错,改一行就能修好。但现在不一样了,Agent是概率型的,也就是同样输入,它可能今天心情好给你答案A,明天给你答案B。这不是bug,这是feature,但debug的时候这个feature能让你疯。

来个简单算数你就知道多恐怖。1个Agent加上5个后端服务,就是5条不确定路径,每条路径上Agent都可能抽风。4个Agent加上5个后端服务,路径数量就变成80条。你没看错,是乘法不是加法,指数级炸裂。

这时候你再去debug,基本就进入玄学阶段。你会开始怀疑人生,到底是模型疯了还是系统炸了还是我写代码的时候咖啡喝少了。有时候重启一下就好了,有时候重启三次才好,有时候你啥也没改它自己就好了。这不是开玩笑,这是真实发生的惨案。

为什么Agent天生就不听话

很多人第一反应是,能不能让Agent更稳定一点,像普通函数那样每次输出都一样。答案很直接,不行,因为Agent设计的目的就是有变化。如果它每次都输出一模一样,那它和普通函数没有区别,你直接用if else就行了,要什么大模型。

Agent之所以有用,就是它可以在模糊场景里给出不同答案。你问它怎么写代码,它给你几个版本。你问它怎么解决问题,它给你意想不到的角度。换句话说,随机性带来能力,随机性也带来混乱,这俩是绑定销售的,不能拆,就像买手机必须买充电器一样。

所以真正的问题变成了,你怎么在一个不稳定的大脑上搭一个稳定的系统。很多人选择的方法是加更多框架、更多规则、更多中间层,用确定性去压住随机性。听起来像加安全带,其实像在车上装迷宫,系统越来越复杂,但该挂还是挂。

真正的突破点:重新理解后端到底是啥

作者做了一件很关键的事,把后端拆了重看。以前大家理解后端就是一堆服务、一堆API、一堆数据库、一堆架构图,越看越复杂,像在看电路板,每个电容电阻都要记住。后来换了个思路,从最底层看后端到底在干什么。

后端其实只有三样东西。Worker就是干活的人,不管你是程序还是脚本还是Agent,你就是个干活的。Trigger就是触发干活的按钮,可以是时间、可以是消息、可以是HTTP请求。Function就是具体干的活,比如查数据库、发邮件、算数学题。就这么简单,没有第四样。

你别小看这个抽象,这一步直接把问题砍掉一大半。因为你突然发现,不管是多复杂的系统,不管你画多么花哨的架构图,底层跑起来就是这三样东西在动。一个数据库连接池是个Worker,一个定时任务是个Trigger,一段SQL查询是个Function,看穿之后系统变得透明。

关键转折:Agent其实也只是个打工人

现在最骚的地方来了。一旦你接受这个三件套模型,你会突然发现Agent根本不特殊,它也是一个Worker。也就是说,它和API服务一样,和Python脚本一样,和Rust微服务一样,和Cron定时任务一样,地位完全平等。

这一下就打通了任督二脉。以前是Agent调用Backend,Agent在外面,Backend在里面,中间隔着一堵墙。现在变成Agent就是Backend的一部分,大家坐在同一个办公室里干活。就像你从外包员工转正成为公司正式员工,权限和流程完全不同。

这个转变带来的后果非常猛。你不再需要为Agent单独设计一套工具调用协议,不再需要在Agent和后端之间架设API网关,不再需要为Agent的状态单独建一张表。它就是个Worker,别的Worker怎么干活它就怎么干活,一视同仁,公平公正。

看代码就懂:三行核心操作

直接看核心结构,这段代码你一看就明白。registerFunction是告诉系统我能干什么,比如我能搜网页、能算数学、能发邮件。registerTrigger是告诉系统啥时候让我干,比如每天凌晨三点、或者收到某个消息、或者HTTP请求来了。

trigger是叫别人干活,也就是你作为一个Worker,可以让别的Worker去做事情。你看,没有HTTP,没有队列配置,没有复杂的状态管理,就这三招,registerFunction、registerTrigger、trigger,搞定全场。

具体跑起来是这样子的:Agent要做研究,它先调用搜索Worker去搜关键词,搜索Worker搜完了trigger一个结果回来。Agent拿到结果再trigger一个抓网页的Worker去抓内容,抓完再trigger一个总结的Worker做摘要。

总结完再trigger一个存状态的Worker把结果写进数据库,最后再trigger另一个Agent继续处理。全是一个trigger串一个trigger,没有HTTP层的复杂跳转,没有额外工具协议,全在一个系统里跑,一条线看下来清清楚楚。

系统统一后的好处:从拼乐高变成搭积木

当所有东西都变成Worker之后,会发生三件特别爽的事情。

第一件,系统会自动发现能力。一个新Worker上线的时候,它会跟系统说,我能干这些活儿,函数签名是这样的。全系统立刻知道它能干啥,不需要你手动更新任何配置。

Agent不需要你手动更新工具列表,它自己就能看到系统里所有注册的Function。这就像你公司来了个新同事,大家自动知道他会写代码,而不用发邮件通知全公司说新同事会Python。自动发现干掉了一大堆手工维护的工作量。

第二件,系统可以不停机扩展。你加新功能的时候,不需要停掉整个系统,不需要重启,不需要搞什么发布窗口。你直接加个Worker,注册进去,系统立马就能用。这对Agent系统太关键了,因为你永远在试新能力,永远在加新工具,停机发布会把你逼疯。

第三件,也是最有价值的,调试变得极其简单。所有调用都有一条完整链路,从最开始的Agent触发,到中间经过的所有Worker,到最后写数据库,全是一条trace。你不用再拼日志了,不用再对时间戳了,一条线看完整个执行过程。这点价值巨大,谁debug过分布式系统谁懂那种拼图拼到想砸电脑的感觉。

更狠的一招:Agent可以自己造工具

这里开始有点科幻味了,但它是真实发生在生产环境里的。系统支持sandbox worker,也就是隔离环境,你可以在这个环境里动态创建新Worker,跑完就销毁。重点是,Agent也可以干这件事,不是只有程序员能写代码。

也就是说,Agent可以自己创建一个新的隔离环境,在里面写一段代码,把这个代码注册成一个新的Function,然后让系统里的其他Worker去调用它。干完再把这个环境删掉,干干净净不留痕迹。这就像你雇了个员工,他发现工具不够用,自己动手造了个新工具,还自动接入了公司系统。

这个能力一旦放开,系统进化的速度会非常夸张。因为Agent可以在运行时自我扩展,遇到没见过的任务类型,它自己写个新工具来处理,下次再遇到同样类型就不需要重复造轮子了。你想想这意味着什么,系统自己长出了新能力,不是程序员提前写好的。

所谓厚壳薄壳,其实只是函数多少的问题

回到最开始的争论,Anthropic说壳要薄,LangGraph说壳要厚,两边吵得不可开交。现在你再看这套三件套模型,你会发现这其实就是函数多少的问题,跟架构没关系。函数少的时候,Agent只能靠模型自己的推理能力去完成任务,这就是薄壳。

函数多的时候,你用大量细粒度的Function把任务切成小块,Agent只要按顺序调用就行,这就是厚壳。本质没变的,都是Worker调Function,都是Trigger在驱动。所以这个争论其实是配置问题,不是架构问题,就像吵架说早饭应该吃两片面包还是三片面包。

一旦底层统一成Worker Trigger Function三件套,你选择自由度就大了。今天想薄壳就少注册几个Function,明天想厚壳就多注册几个,后天想中间状态就注册中等的数量。不用重写架构,不用换框架,改改配置就行。

为什么这套模型更接近未来

软件历史有个规律,真正的大突破都在减少概念。Unix搞出一句话叫一切皆文件,把设备、网络、硬盘全压成一个抽象,程序员不用记十几种API。React搞出一句话叫组件就是函数,把UI拆成纯函数组合,心智负担直接降下来。

现在这个模型搞出一句话,一切皆Worker。不管是Agent、API、定时任务、消息消费者,全都当成Worker对待。这类抽象的威力在于,你不用记一堆规则和例外,你只用记一个原则,剩下都是组合问题。就像玩积木你只需要知道积木怎么拼,不需要知道工厂怎么生产积木。

这套模型更接近未来的另一个原因是,AI系统还在快速变化,今天流行ReAct,明天流行Plan-and-Solve,后天不知道又冒出什么新框架。如果你的架构把Agent当作特殊公民单独对待,每次新范式出来你都要改架构。但如果所有东西都是Worker,新范式只是重新组合Worker而已。

最后的落地理解:你该怎么用这套思路

你现在做系统,可以直接套这个思路。第一步,把所有能力抽成Function,想清楚系统里到底能干什么活,列一张清单。第二步,用Trigger定义触发条件,什么时候谁应该干活,是定时跑还是被叫醒跑。第三步,把所有执行单元当Worker,不管它是大模型Agent还是简单的脚本。

然后做一件最关键的事,停止区分Agent系统和后端系统。一旦你脑子里还在分这俩东西,你的复杂度一定会上升,因为你在设计两套不同的抽象,然后想办法把它们粘在一起。粘合层永远是bug最密集的地方,这是软件工程的铁律。

当你统一之后,Agent调用工具就是调用Function,Agent的记忆就是Worker的状态,Agent的流程就是一堆Trigger串起来的执行链。整个系统会变得异常清爽,你再看原来的架构图会觉得那是故意画复杂来骗甲方的。

复杂度下降来自统一而不是增加

很多人面对AI系统复杂度的本能反应是再加一层。系统不稳定就加缓存,逻辑混乱就加编排层,调用链太长就加代理。多加框架、多加规则、多加中间件,听起来很努力很专业,其实方向偏了。复杂度不会因为你加的东西多就消失,它只会转移和隐藏。

真正有效的路径是反过来做,减少概念、统一模型、压缩抽象。你把系统压到Worker Trigger Function这三块,复杂度自然掉下来,因为你能看得懂了。看得懂的东西你就能改,能改的东西你就能修,能修的东西就不复杂。

然后你会发现一件挺离谱的事,你原来以为很复杂的问题,其实只是你把它讲复杂了。系统从来不怕复杂逻辑,它只是按你写的代码跑。它怕的是混乱结构,是Agent一个待遇后端另一个待遇,是这里用HTTP那里用RPC,是每个服务都有自己的日志格式。把这些统一了,复杂逻辑跑起来反而很稳。