大家好。起初我打算做一场非常技术性的演讲,但看到自己被列入领导力轨道,我不太确定那意味着什么,心想那就一半讲高层理念、一半讲技术吧。所以这更像是一个关于我在今年一月所做之事的讲述,因为大约在十二月左右,AI Agent 的能力出现了一个质的飞跃。我想那是 Opus 4.6(或类似版本)带来的变化,那一刻我意识到这件事真的发生了。我在 AI 行业工作,当时我想:“好吧,我要请两周假。”我是 Text 公司的 CEO,这是一家15人的公司。我告诉我的 CTO:“我要离开两周,深入钻研这件事,看看我们能从中得到什么,以及它究竟有多好。”
这个故事其实是我在两周时间里解决自己的痛点,试图弄清楚如何真正使用 Agent,以及构建 Agent 的良好原语是什么,或者这些是否已经存在。这个标题虽然有点耸人听闻,但事实证明它对这次演讲也很合适。大家可以在左边看到那座城堡是我的办公室,这是真的。我租了一间位于城堡里的办公室。那个带箭头的小东西是一个机器人割草机,它每天全天候无人值守地工作。它在后台默默运行,不需要任何人使用遥控器或操心任何事情。
我希望能让我的早晨也实现同样的自动化,因为我的前几个小时总是重复着相同的事情:浏览市场新闻、回顾 Linear 或 Jira 等项目管理工具,以及我的 CRM。我喜欢在早晨散步大约一个小时,然后花一小时处理那些我在散步时录制的超长语音笔记。我只想要一份伴着咖啡的晨间简报,这也是过去几年人们向我们描绘的未来图景。
然而,当你真正开始使用它时,即使不写代码,今天你得到的只是一个文本用户界面(TUI)。这很神奇,你可以编码。我实际上开始在其中做一些非编码的事情,这对这类任务非常有用。Agent 擅长于此,但这就像开着一辆自动驾驶的拖拉机割草机,你却必须坐在上面。这很令人沮丧,因为它虽然能做很多事情,但你仍然需要全程参与。
当然,实验室并没有止步于此,他们推出了应用。我基本上称之为“带有氛围感的 SSH”,这很棒。但当这种情况出现时,我想:“哇,太棒了!我不再需要在手机上使用像 SSH 这样的终端了。”CodeEx 很棒。然而,我注意到我开始在散步时指挥 Agent 做事,那时我已经无法清晰地思考了。我只是开始在晨间散步时在手机上运行 Agent。
这并不好,因为这相当于你虽然不需要紧挨着拖拉机,但它仍然需要一个遥控器让你时不时地改变轨迹。这很有用,但回想起来有点荒谬。当你看到人们一直盯着手机做这些事时,这显然是一种过渡状态。我们肯定不会止步于此。于是我做了一件很蠢的事:作为 CEO,我开始偷偷编码(别告诉董事会),构建了一个尽可能简单可行的东西。当然,这变成了一个疯狂的兔子洞。
仓库在那里供你查看。代码并不完美,但能运行。首先,我开始使用框架。有很多优秀的框架,我不会点名具体是哪些,因为它们各有千秋,流程也各不相同,这没问题。但我把所有时间都花在了编辑代码中的提示词上,我觉得这样其实没什么用。在座各位可能都像其他人一样讨厌 YAML,但我发现这实际上更容易实现无代码 Agent 的开发。
你可以版本控制它、进行差异比较,并在 PR(拉取请求)中审查。而且非常简单:你只需编写文件,将其放入文件夹,一旦有了运行时环境,它就会神奇地出现并自动工作。然后我需要我的市场监控功能在我早晨散步时运行。为此,我们使用了一些早已存在的东西:定时任务(cron jobs)。
调度器指定 Agent 何时需要运行,稍后我们会看到这一点。它们还发布事件。Markdown 和 Cron 显然在底层非常复杂,但接口是这样的:你不写代码,这就是目前为止整个产品的核心。老实说,大部分时候它都能工作,我稍后会再回来讨论“大部分时间”这个问题。
这是一张真实的照片,展示了我的一次晨间散步。我在走路时录制语音笔记。人们会使用 Cron 任务来做这件事,因为这是 CodeEx 今天提供的功能,但它们并不理想。因为它们只覆盖“何时”,而不覆盖“因为”。例如,“当某事发生时”或“系统中的某些事情自动触发时”,比如当你把语音笔记放入系统时,它会发出一个事件,Agent 会对该事件做出反应。
这与收到新邮件、CRM 中新增条目、打开新的 PR(拉取请求)或合并了新的 PR 等情况是一样的。它只是对事件做出反应,而不仅仅是 Cron 任务。这意味着语音笔记处理 Agent 不是一个简单的定时任务,而是声明它接受什么以及返回什么作为事件。这里它接受语音笔记,将其转录为右侧的持久化笔记,并发出一个新的事件。为此使用了结构化输出,我们稍后会回到这一点。
现在我们终于得到了承诺的未来:语音笔记 Agent 发出“语音笔记已处理”的事件。然后我有我的每日简报 Agent,它将获取定时任务的输出……
注意智能体,我们创建每日简报,并通过 Slack 消息发布。因此,“Slack 消息发送”事件实际上是一个订阅该事件并发出 Slack 消息给我的进程。这确实是真实存在的,而且正在运行。我稍后可以在手机上展示给你看。你知道的,有些框架会向你兜售“为此你需要代码中的图结构”这一观点。但在这种情况下,你根本不需要图。你所需要的只是事件。你没有需要维护的边(edges)。智能体只需订阅事件即可。任何人都可以进来编辑它,你不需要这样做。是的,你无需懂得编程知识。你只需要了解系统中存在哪些事件即可。“扇入”和“扇出”都是免费的,无需代码。只需放入一个文件,拓扑结构便会根据日志记录的发生情况自动浮现出来。
当然,我尝试运行了它。所以第一个版本大约花了我……你知道的,我作弊了,我使用了 CodeEx,大概用了一天时间写出了第一版。但这只是我从一周的工作中挤出来的成果。当然,我尝试运行它时它就崩溃了。所以这些是真实的案例,虽然日期可能记不清了,但确实是真实例子。比如第一天,简报被发布了两次;周三时我的一条语音笔记完全消失了;到了周末快结束时,我一整周都在调整提示词(prompts),结果市场简报变得一团糟。但我没有对更改进行版本控制,所以我实际上不记得到底改了提示词的哪一部分才导致整个系统完全失效。
现在,如果房间里有人是分布式系统的工程师,你们可能已经熟悉这份“购物清单”了。太阳底下无新事。你知道的,每一次失败——也就是你发现的每一种故障模式——都促使我构建了最终成为运行时(runtime)的一部分。丢失的笔记实际上变成了日志。我只是想让一切都被永久保存,以便我可以回溯查看当时发生了什么。至于重复项,那是因为我未遵循流程:它尝试了多次,而我没有跟进这些重试。我没有合适的队列机制,也没有计算重试次数等等。
而最有趣的部分可能是最后那个提示词的问题。我在那里陷入了一个非常深的“兔子洞”,最终构建了一个内容寻址系统(content-addressed system)。你可以将其类比为 Git,或者任何其他的构建系统。而且我这样做并不是因为我想设计一个运行时。在那一点上,我只是希望我的智能体能正常工作,同时也觉得这种折腾挺有意思的。我是随着错误出现而逐步偿还技术债务的。希望我的董事会不会看到这场演讲。
日志是系统的记忆,没有信息丢失,一切都被观察到了。你只有一个追加事件表(append-only events table),就像命令行中的 events 一样,你可以获取所有的事件。它们也是因果关联的,比如哪个事件触发了哪个事件,这在调试时非常有用。即使只有三四个智能体,你也会开始遇到重大的调试头痛问题,所以这非常有帮助。而且所有内容都是可查询的,这对调试再次起到了关键作用。
第二点是,既然我们有日志,就可以回溯事情等。但仍然很难知道究竟什么进入了模型——即发送了什么提示词给模型,因为当你使用 CodeEx 时看到的是一种“谎言”。你感觉像是在与模型进行实时聊天会话,因此你会认为那就是模型所看到的内容。但事实并非完全如此。原因有很多:压缩(compaction)显然是其中重要的一部分,但也有一些怪癖。比如 OpenAI 或 Anthropic 都不会与你分享思考过程(thinking traces),所以你完全不知道什么进去了,或者说只知道大概。
因此你需要不同的东西。这就是那个大“兔子洞”:试图找到一种方法或构建一个系统,以便能够回溯到模型内部实际看到的内容。所以我所做的基本上没有什么新意,这其实就是构建系统的工作原理。提示词由不同部分组成:你有系统提示词、第一技能或第二技能的描述、工具的描述,以及作为对模型提问的用户消息。每一部分都被存储并寻址,以哈希值(hash)作为标识符存储在某个地方。
因此,当我们构建提示词时,在渲染文本之前,我们实际上将提示词表示为这些哈希值的列表。这意味着当后续收到模型回答时(顺便说一句,它也是以相同方式存储的),我们可以非常轻松地回溯到提示词。然后从该提示词中,我们可以确切地知道什么进入了模型的上下文,这非常重要。这不仅对调试至关重要,也让压缩变得容易得多:你只是在操作图结构,而不是字符串,这要简单得多。它也间接地使 KV 缓存管理变得更容易。
但我认为,当你规模化使用这种方法时,主要的优势其实是可审计性(auditability)。你可以确切地知道智能体发生了什么以及为什么返回了那样的结果。所以我快速过一下这部分:一旦拥有这种图结构,你就能获得差异(diffs)。你可以说“看看这两次运行之间有什么变化”,比如哪些组件发生了变化?仅仅是我的消息变了吗?我给了模型不同的技能或工具吗?你可以运行这个函数,它会显示两次运行之间的差异。这里有三个组件是相同的;有一个变了,即用户消息发生了变化。其余的消息则是延续的,属于同一个会话的后续部分。
此外,还有一个免费获得的功能是重放(replay)。重播放对我非常有用,因为一段时间后——当你看到成本飙升时,也就是拥有可观测性后你会意识到的那样——你会发现成本增加得非常快。我想尝试使用开源模型……
模型,所以我想要重建旧的请求来进行评估。看看我是否得到了相同的结果,或者令人满意的结果,以及是否需要做出任何更改。结果发现,一旦你拥有了这种基于内容寻址的系统,你就可以从图中重建请求,并可以完全精确地重放它。你可以重新发送,或者使用不同的模型。如果你愿意的话,你也可以使用不同的请求,对其进行更改等等。是的,实际上你免费获得了很多东西。你需要实现一些东西。
这与我在几个月前开始做这件事时发现的有所不同,尽管现在可能已经不同了:当时市面上有很多库和框架。这些框架只是调用代码,而你的代理则隐藏在它们的抽象之中。我不喜欢与操作系统的类比——好吧,每个人都知道这个比喻:内核运行进程,而你的代理某种程度上就像一个进程。它实际上做什么并不重要。系统可以调度它、隔离它,并通过日志和代理定义对其进行记录(journaling)。
因此,Markdown 处于用户空间。如果你不想使用带有前端的系统,你实际上不需要使用该 Markdown 格式。这是一个非常重要的观点,也是我的一个心得,同时也证明了我致力于此工作的合理性:结构化输出是我们的专长。我们为此已经工作了三年,这最终变成了一个大规模的“吃狗粮”(内部试用)项目。
我最初这样做的原因并不是因为我绝对想使用我们的软件,也不是为了强制其他软件采用它。而是因为 Infropic 在结构化输出方面表现极差,导致我大约 20% 的事件是错误的并被系统拒绝。这就是我做这件事的原因。内核的工作实际上是使错误的操作不可能发生,而不仅仅是降低其可能性。
因此,在代理与外部世界之间存在两个边界。第一个是类型化的工具调用:你不想调用不存在的工具等。另一个与其他代理的边界则是类型化的事件,这是不可协商的。我发现仅凭这一点就能避免很多错误。我写了一篇很长的博客文章讨论这个问题,扫描二维码即可找到。
结果是,我在构建该系统后在公司内部部署了它。如今,在部署一个月后的今天,左侧有 20 个代理,值得一提的是,它们不仅仅是由技术人员贡献的。右侧则是我们部署的环境(互联网),包括简报等内容。
作为结论,有几个经验教训:首先,执行良好的后台代理真的很有魔力。它们就像我最初拥有的那种机器人割草机一样:你坐下来,回来时就会收到一份早报。这份简报可能比你自己手动做的还要好,它每天出现在我的收件箱中,处理我随机的想法。
做这类事情遇到的困难只是老式的工程问题。在编排这些事情方面,太阳底下没有新鲜事,这只是传统的软件编排工作。开源模型已经存在且足够好。我已经替换掉了第三方 API,现在我只使用开源模型,甚至在笔记本电脑上也使用本地模型。对于我做的这些事来说已经足够了(虽然编程可能还差点)。
Infropic 领域肯定尚未定型。在开始自己构建之前,我尝试过几件事。我建议今天一定要“先建后买”。如果你是小型公司或科技公司的 CEO,这其实是一种优势,因为你可以在不占用工程师精力、不让它们偏离正轨的情况下直接做这件事。但一定要先尝试构建,以确切了解你需要什么以及现有方案的局限性。
另外,我想对正在为此构建框架的人说:请吃自己的狗粮(内部试用)。有时很明显人们在构建代理编排框架等,但并没有自己使用。所以请这样做。
我很高兴我抽出这两周时间来实地体验,因为这完全改变了公司的轨迹。我知道我们是家 AI 公司应该投身其中,但商业环境总是让你思考下一个、再下一个。我敦促大家停下来,真正沉浸其中,看看它对公司有多大用处。
你可以偷取代码。这不是我们要销售的产品,我们也无意出售。你也可以阅读我们的博客——我还没解释这一点,但我将发布相关内容。感谢大家的聆听。