许多大语言模型智能体的设计,仍然默认这样一个简单流程:模型提出工具调用,工具执行完毕,返回结果,模型再继续推理。这个流程本质上采用的是有明确返回点的 call/return 模型。对于短暂、边界清楚的操作,它很好用;问题出现在工具执行和外部环境开始持续变化以后。

真实工具的执行过程往往没有这么整齐。有些工具会持续输出日志,有些会逐步生成中间结果,还有些只是启动一个外部任务,然后等它自己推进。用户也可能在任务运行中继续操作界面、修改要求或触发新的服务。与此同时,智能体产生的也不只是文字,它还可能创建任务、调用外部服务,甚至控制设备。

到了这种场景,“模型调用工具,工具返回结果”已经不足以描述整个系统。更合适的做法是把运行时按事件驱动的方式来建模:工具、日志、用户操作、定时任务和传感器都可以产生事件,runtime 负责接收和整理这些变化,需要高层判断时再调用大语言模型。模型在这里更像按需工作的 planner,而不是整个控制流唯一的中心。困难不在于多调用几次工具,而在于把连续、异步、带噪声的变化整理成可用于决策的状态。

有些工具调用不能只按一次函数返回理解

查询、格式转换、一次性计算这类短操作,本来就很适合 call/return。问题出现在执行会持续推进的时候:工具可能长时间运行,不断产生中间状态,或者只是等待另一个系统完成工作。如果接口只暴露“开始”和“最终返回”两个时刻,runtime 就看不到中间发生了什么,也很难在过程里做判断。

代码执行会持续打印输出。网页自动化会经历加载、等待、失败和重试。模型训练、数据处理、文献总结、服务部署,都可能需要很长时间。机器人执行动作时也不会一次性给出结果,而是在环境中持续变化,并不断产生反馈。

对于长任务,如果 planner 只能等最终结果,就很难在中途发现卡死、错误、偏离目标或新的决策机会。反过来,如果每出现一行输出都重新调用模型,系统又会被噪声拖住:上下文很快被日志占满,推理资源也浪费在没有决策价值的变化上。

这类执行更适合被看成一个持续推进、可以观察状态变化的活动。运行过程中可以出现开始、进度、阶段完成、错误、等待、完成或取消等事件。Planner 不需要读取全部原始事件,只需要接收经过观察层筛选和压缩、并且可能影响决策的状态变化。

实现上可以把三件事配在一起。工具有新状态时,优先通过事件让 runtime 及时感知;高频输出先节流和压缩,不让每个片段都进入高层决策;同时保留低频 reconciliation,周期性检查关键状态,用来发现回调丢失、连接中断、日志延迟或状态漂移。

因此,事件推送和周期性检查并不是非此即彼。Push 可以低延迟地告诉系统“有变化了”;周期性查询则适合确认关键状态仍然存在,并发现回调丢失或状态漂移。前者偏向及时感知,后者承担 liveness 检查和 reconciliation。

事件唤醒运行时,Observation 才唤醒模型

事件到达,并不意味着应该立即调用大语言模型。底层信号还需要经过几层语义转换:

raw signal
    ↓
runtime event
    ↓
filter / aggregate / correlate
    ↓
observation
    ↓
does this change a decision?
    ↓
planner invocation

例如,一行新的 CI 日志可以先更新 runtime state,却没有必要触发一次完整推理。等几条日志合在一起已经能说明“测试失败”,它们才形成一个值得 planner 处理的 observation。这个边界可以概括为:events wake the runtime; observations wake the model.

这个区分还有一个直接后果:runtime 的职责之一就是避免不必要的模型调用。事件系统持续接收变化,观察层把它们压缩成有限的语义差异,只有当这些差异可能改变目标、计划或下一步行动时,planner 才需要介入。

观察器(observer)不是缩小版规划器(planner)

观察层可以由规则、小模型,或者在必要时由语言模型实现,但职责应该保持很窄:判断任务是否仍在运行,是否出现错误,是否到达阶段性节点,以及这些变化是否值得交给 planner。它还负责把连续输出压缩成结构化状态。

两者的边界可以说得很简单:observer 负责说明“发生了什么变化”,planner 负责判断“这个变化意味着什么”。Observer 可以决定是否需要升级,但不应该因为自己也能调用模型,就顺手承担目标解释和自由规划。

因此,observer 不应把大量日志原样交给 planner。它更适合输出一个小的状态包:当前在哪个阶段,自上次上报后发生了什么关键变化,有没有异常,是否需要决策,以及支撑这些判断的最小证据是什么。

原始流仍然需要保留,便于审计和回溯。但规划器平时不应直接消费原始流,而应消费结构化状态和语义摘要。只有在需要深入排查时,才回看原始记录。

可以借用人的选择性注意作为直觉:高层判断不需要逐帧处理所有输入,只需要在重要变化出现时介入。这个类比只借用了“分层筛选信息”这一点,并不意味着智能体运行时与人的感知系统具有相同结构。

工程上仍然需要显式定义什么算异常、什么算进展、什么情况值得升级给 planner。越靠近底层,越适合使用清晰规则、状态机、格式校验和阈值判断;只有当信息需要结合目标和上下文解释时,才值得使用更开放的模型推理。

长任务需要决策节点,而不是盲等

长任务真正难处理的不是“等多久”,而是“什么时候需要重新决策”。如果按运行时应该如何处理来划分,常见状态可以粗略分成四类;它们不是严格互斥的事件类型。

第一种是正常推进。任务有输出、有进展,也没有异常。这时规划器不应频繁介入,只需要观察器维护心跳和阶段摘要。

第二种是局部更新。工具产生了新的日志、中间图像或部分结果,但这些信息不足以改变计划。此时系统只更新任务状态,不进入完整推理。

第三种是决策点。任务到达了一个分支,例如出现多种结果、需要额外参数、发现新的约束、进入检查点,或者当前证据不足以继续自动推进。此时观察器应把相关上下文压缩后交给规划器,由规划器决定下一步。

第四种是异常点。任务卡死、超时、反复失败、资源消耗异常、结果明显偏离目标,或者出现安全风险。这时系统需要中断、重试、回滚、切换工具、请求用户确认,或者终止任务。

因此,管理长任务的关键是从连续执行里找出少数真正值得决策的节点。没有这层筛选,planner 要么一直盲等,要么被低价值信号反复打断。

对于仍需要周期性采样或 reconciliation 的状态,检查频率也不必固定。任务刚启动或接近关键节点时可以更密;进入稳定运行期以后可以降低;出现异常征兆或接近超时时再提高频率。这样可以把轮询成本集中在信息变化更快、风险更高的阶段。

持续服务的日志要按用户动作切片

另一类重要场景是启动一个持续运行的服务。服务不断产生日志,其中一部分对应用户的操作。规划器需要在用户行动后理解相关日志,形成判断,并规划后续动作。

关键不在于“让模型读日志”,而在于让用户动作、服务状态、日志片段和后续规划之间具有可追踪的关联。请求编号、会话编号、trace ID 和时间范围可以帮助缩小证据范围,但它们本身并不能自动证明因果关系。缺少这些关联时,规划器会看到很多日志,却很难判断哪些信息与本次操作有关,哪些只是背景噪声。

因此,每次用户操作最好先留下一个动作记录。它至少要说明用户做了什么、针对哪个服务、预期影响什么,并尽量保留请求编号、会话编号或 trace ID。随后,系统再围绕这个动作建立一个有边界的观察回合。

观察回合需要有明确边界。它不是无限期盯住整个日志流,而是在用户动作发生后,根据关联 ID、相关组件和时间范围收集证据。例如,用户点击“部署”后,系统重点观察部署服务、调度器、依赖拉取和健康检查,而不是读取所有后台心跳。观察回合可以在获得足够的成功或失败证据后结束,也可以因为超时或长时间没有相关新信息而结束;具体结束条件应由任务语义决定。

这样,持续日志就被切成一个个与用户动作相关的小片段。规划器面对的不再是无边界的日志海洋,而是一个有起点、有证据、有当前状态的局部回合。

observer 主要做四件事:挑出相关日志,判断什么时候结束观察,压缩证据,再决定是否升级给 planner。它输出的不是一大段日志,而是一个有限结论:这次操作是否生效,当前是成功、失败、进行中还是不确定,异常可能在哪个阶段,以及还缺什么证据。

日志本身也不能被当成事实全集。它可能缺失、延迟、乱序,甚至显示“成功”而实际状态并没有变化。因此,关键判断还需要用指标、状态查询、界面观察、数据库读回或接口结果交叉验证。更可靠的顺序是:先围绕用户动作收集证据,再形成状态判断,验证其中关键部分,最后才让 planner 决定下一步。

除动作触发的观察外,还需要独立的背景健康监视。有些问题并不对应某次用户操作,而是逐渐积累出来的,例如队列堆积、资源耗尽、会话失效或后台任务失败。动作观察器围绕一次操作收集和解释证据,背景监视则负责发现与具体操作无关的系统异常。两者处理的是不同来源的状态变化。

回应用户不只是输出文本

智能体的回应也不能只被理解为“回复一句话”。文本只是行动的一种。系统可能需要向用户解释、播放语音、生成报告、调用外部服务、创建定时任务、下单、控制设备或指挥机器人。

在运行时调度层,可以把这些输出统一表示为 action:向用户发送消息、调用接口、创建任务或控制设备,都需要被规划、执行并返回结果。但这种统一只适用于控制流,不意味着它们具有相同的风险和授权语义。向用户展示信息,与修改数据库、支付或控制设备造成的外部副作用,应继续保持明确区分。

信息型回应主要向用户传递内容,例如文本、表格、报告或进度说明;语音可以看成其中一种呈现方式。另一类 action 会直接改变外部系统状态,例如发送邮件、修改文档、调用服务或执行脚本;控制设备和机器人则进一步改变物理环境。后两类 action 通常需要更严格的权限和验证。

当任务需要同时产生多种结果时,让 planner 输出结构化行动计划通常比只生成一段自由文本更容易执行和审计。例如,同一个目标可以包含生成文献总结、把总结发给用户,以及创建阅读提醒;这些 action 属于同一计划,却由不同执行器完成。

系统还必须根据 action 的副作用和风险执行权限控制。只读取资料并生成总结,与创建持久任务、发送外部消息、支付、删除数据或控制设备,不应共享同一套默认权限。哪些操作可以自动执行、哪些需要确认,应由产品策略、用户授权和资源范围共同决定。

一个更清楚的分工是:planner 提出要做什么,策略层决定这次是否允许,执行层把 action 落到具体操作,观察层再把结果反馈回来。

运行时更接近事件驱动的并发系统

从实现上看,这套 runtime 更接近一个事件驱动的并发系统,而不是一条从头走到尾的同步调用链。用户、工具、observer、scheduler 和 executor 都可能产生或消费事件。大语言模型位于较高的决策层,只在关键变化到来时解释状态、调整目标并选择下一步。

“事件驱动并发系统”在本文中只是对运行时结构的一种概括:工具、观察器、调度器和执行器不必共享一条线性的调用链。本文关注的是事件怎样进入系统、怎样被整理成 observation,以及什么时候值得重新调用 planner。至于不同组件之间谁等待、谁恢复、谁拥有控制权,则需要从通信过程和协程的角度进一步讨论。

统一的运行时结构

把前面的关系放在一起,可以得到一个比较清楚的 runtime 结构。

底层是环境和工具。它们包括外部服务、日志系统、文档系统、网页、机器人、数据库和各种自动化接口。它们不断产生事件,也接收行动请求。

中间层是执行器和观察器。执行器负责把行动计划转化为具体操作。观察器负责从日志、工具输出、状态查询和环境反馈中提取有意义的变化。调度器负责一次性或周期性任务。它们共同维持系统的运行状态。

上层是规划器。它不直接盯住所有原始输出,也不承担低层监控。它接收经过压缩的观察结果,维护对目标和环境的判断,决定下一步行动。它只在目标受到影响、计划出现分支、异常需要处理或信息价值明显升高时介入。

用户既是输入来源,也是被行动影响的对象。智能体可以向用户解释当前状态,也可以请求确认、报告进度、展示结果、播放语音,或根据授权执行外部操作。用户不是系统之外的旁观者,而是整个交互环境的一部分。

如果需要进一步形成直觉,也可以把它看成两个不同速度的反馈环:较快的一层负责感知、状态维护和低风险反应,较慢的一层负责目标解释、策略调整和复杂决策。这里区分的是决策频率和职责,不意味着实现上必须存在两个固定循环。

本文主要回答两个问题:事件怎样进入 runtime,以及什么变化值得重新调用 planner。它讨论的是 communication 和 wakeup,不负责给活动划分生命周期。短调用、scoped 并发,以及能够独立于 caller 存在的活动,需要用另一组生命周期概念区分;这部分我在 《Structured LLM Design:按生命周期组织 Agent Runtime》 中单独展开。

结论

大语言模型智能体的关键,不只是让模型更会调用工具,而是重新设计工具、观察、行动和规划之间的关系。

短工具调用仍然适合普通的 call/return;持续执行的活动则需要暴露可观察状态,并把高频变化压缩成少数值得决策的 observation。日志不应不加筛选地塞进模型上下文,而应围绕具体操作和系统状态形成可追踪的证据。大语言模型也不必承担所有控制权,它更适合在语义发生变化、计划需要调整时介入。

这套架构面对的是一个简单事实:真实环境会持续变化,信息也不会一次给全。Runtime 的价值在于接住这些连续变化,维护可验证的状态,再把有限而重要的变化交给 planner。Planner 作出决定以后,执行层负责把 action 落地,观察层再把结果带回系统。

事件驱动运行时回答的是智能体系统应该如何组织。进一步的问题是:在这样的系统里,究竟是谁在调用谁?这个问题需要从通信过程和协程的角度重新理解。