1. 最小状态机
1.1. 从就绪到提议
控制器把目标、约束、已提交事实和可用动作构造成模型输入。模型输出只是候选动作。解析失败应返回结构错误,参数不满足 schema 应返回校验错误,两者都不应触发环境副作用。
候选动作进入授权阶段后,系统结合主体身份、资源范围和风险等级决定执行、拒绝或等待审批。这个阶段独立于模型,让同一策略可以在不同权限边界运行。
可把状态空间写成 。 保存目标、约束和完成谓词; 保存已经确认的环境事实及版本; 保存当前阶段、待处理动作和最近错误; 保存剩余 token、工具次数、时间与费用。模型通常只看到这些状态经过筛选后的投影 ,而不是能够直接修改权威状态。候选动作由策略 产生,控制器再决定它是否属于当前状态允许的动作集合 。
这一区分处理了模型幻觉和系统事实之间的冲突。模型可以在推理中假设某文件存在,但在读取动作成功前,文件内容不会进入 ;模型可以计划发送邮件,但在审批与发送工具完成前,运行状态只能记录“待审批动作”。候选动作被解析失败时,控制器保存结构错误及 schema 摘要,再把运行送回推理阶段。权限拒绝则进入等待或失败,不能伪装成普通工具错误让模型重复改参数。
1.2. 执行与提交
执行器调用真实环境并获得结果。写操作需要明确提交点:提交前失败可以安全放弃,提交后超时则必须查询环境状态,不能直接重试。幂等键、版本号和事务状态用于区分“没有发生”与“已经发生但响应丢失”。
观察写回状态后才开始下一轮。若模型上下文与权威状态分离,控制器应从权威源重新构造必要字段,避免把未经确认的计划当成事实。
状态转移可以分为纯转移和带副作用转移。纯转移只改变运行记录,例如 READY → THINKING 或 VALIDATING → COMPLETED;带副作用转移会触及文件、网络、数据库或外部账户。后者至少经历“动作已接受、执行已开始、环境已提交、观察已记录”几个事件。若把它们压成一次布尔返回,进程在任意两点中断时都无法判断下一步应查询、重试还是停止。
写操作的幂等键通常由运行标识、逻辑动作标识和资源版本组成。执行器收到重复请求时,返回第一次提交的结果,而不是再次产生副作用。幂等并不等于所有动作都能安全重试:若外部服务不支持幂等,Runtime 必须保存远端事务标识,并在网络超时后先查询事务状态。文件补丁则可用基线内容哈希做乐观并发控制;哈希变化意味着环境已被其他主体修改,旧补丁需要重新生成。
具体例子是发送一封带附件的邮件。模型提出 send_mail 后,控制器先校验收件人、附件路径和审批状态,随后生成操作标识 op-42。邮件服务已经接收请求却在响应前断线时,本地事件停在“执行已开始”。恢复逻辑用 op-42 查询远端:若已发送,则补写提交事件;若明确不存在,才允许重新执行。直接依据超时重试会产生两封邮件,依据模型记忆判断又没有环境证据。
2. 完成判定
2.1. 目标谓词
完成条件应尽量写成环境可检查的谓词。例如代码任务要求指定测试通过且工作树包含预期变更,资料任务要求结论带可访问来源。模型生成“完成”只是一条消息,不能代替这些谓词。
复杂任务可有多个局部谓词,但局部成功不等于全局完成。验证器需要检查依赖关系,例如文件生成后仍要确认链接有效,部署后还要检查健康状态。
目标谓词分为状态谓词与轨迹谓词。状态谓词检查终局,例如测试全部通过、订单状态为已支付;轨迹谓词检查过程约束,例如未访问禁用目录、付款前获得了用户批准。只看终局会漏掉通过越权或破坏性捷径得到的结果。若任务目标为 、安全约束为 ,成功条件应写成 ,其中 是完整事件轨迹。
验证器应尽量和策略使用不同证据路径。代码模型可以建议修复,但完成判定读取测试进程退出码和工作树差异;网页模型可以点击提交,但验证器重新读取账户订单;研究模型可以撰写结论,但引用检查器逐项打开来源。若验证器只是让同一模型回答“任务是否完成”,它容易继承生成阶段的错误前提,形成一致却无法核查的自评。
2.2. 失败与暂停
参数错误、权限拒绝、外部服务故障、验证失败和预算耗尽应使用不同状态。区分原因后,策略才能决定修正参数、请求授权、等待恢复或停止。
暂停意味着执行上下文仍可恢复,失败则表示当前尝试已经结束。若进程重启导致执行体丢失,应如实标记中断;在未知副作用状态下自动重放动作会扩大风险。
失败还要区分可修正与不可修正。schema 校验失败可以把字段错误反馈给策略;资源版本冲突需要重新读取环境;权限不足需要新的授权;外部服务永久拒绝或预算耗尽则结束当前尝试。控制器根据错误类别提供有限的后继转移,避免模型把所有失败都解释为“再试一次”。同一动作在相同状态连续得到相同错误时,进展检测器可以触发重规划或终止。
暂停状态保存恢复所需的确定信息:待审批动作、参数摘要、资源版本、剩余预算和恢复条件。正在运行的进程、内存句柄和未提交推理不能假装被持久化。服务重启后,如果外部副作用状态未知,运行进入中断而不是自动恢复;用户或恢复器查询环境后,再决定补写提交、生成补偿动作或开启新尝试。
3. 轨迹与重放
3.1. 事件记录
轨迹至少记录输入状态版本、候选动作、授权结果、工具参数、工具结果、验证结果和终止原因。每条事件使用稳定标识与时间顺序,敏感字段另行脱敏。
仅保存对话文本无法恢复真实执行顺序,也无法判断某段文本来自模型还是工具。结构化事件让离线评测、故障定位和训练数据筛选共享同一事实源。
一条事件可包含 run_id、event_id、parent_id、state_version、kind、payload_ref、created_at 和 actor。state_version 用于并发比较交换,parent_id 表示因果来源,payload_ref 指向可能很大的原始日志或页面快照。模型输出、工具请求、审批决定、环境结果与验证结论使用不同 kind,消费方不必解析自然语言猜来源。
对于前述订单修复,事件序列可以是:读取任务约束;搜索金额函数;读取实现;运行复现测试并失败;生成补丁;补丁提交;目标测试通过;回归测试通过;类型检查通过;差异范围检查通过;完成。若回归测试在补丁后失败,首次偏离点可能是补丁本身,也可能是初始诊断遗漏。保留每一步的输入版本与观察后,诊断者可以在补丁前状态重放另一个候选动作,而不必重建整段聊天。
3.2. 确定与非确定部分
模型采样和外部网页可能非确定,但控制器的状态转移、schema 校验与权限判断应尽量确定。重放时固定环境快照和模型配置,仍需标注无法复现的外部响应。
因此,轨迹重放不是简单把消息再次送给模型。它需要恢复动作前状态,选择重用已记录观察或重新执行环境,并明确两种模式产生的结论差异。
观察重放把历史 observation 原样交给新策略,适合比较模型在相同信息条件下怎样决策。环境重放则重新执行工具,适合验证完整系统,但外部网页、时间和随机服务可能已经变化。两种模式回答的问题不同:前者隔离策略差异,后者测量端到端可用性。评测报告应写明使用哪一种,不能把历史缓存结果当作实时环境成功。
非确定性还来自并发和采样。模型配置要记录模型版本、温度、随机种子与解码参数;环境要记录镜像、数据快照、时区和外部依赖;Runtime 要记录工具 schema、超时与预算。即使这些条件全部固定,托管模型与网络服务仍可能变化,因此结果需要多次运行与方差,而非把一条成功轨迹视作确定能力。
4. 从状态机到可靠实现
4.1. 并发、版本与背压
同一运行可能同时收到工具结果、用户取消和审批回复。若三个回调都能直接改状态,最终结果取决于调度顺序。更稳妥的实现把它们转换为事件,单一 reducer 根据当前状态与版本接受或拒绝。例如运行已进入 CANCELLED 后,迟到的工具成功事件可以记录环境结果,却不能把运行重新转成 RUNNING;若工具产生了副作用,还需进入补偿或人工检查队列。
长工具调用还会产生流式日志。日志片段属于观察流,不等于动作完成事件。Runtime 可以把片段写入环形缓冲供界面查看,只有工具结束并生成结构化结果后才提交下一状态。消费速度跟不上时要实施背压或截断,并保留完整产物地址;把每个日志 token 都送回模型会挤占上下文,也会让模型在动作尚未结束时提前决策。
4.2. 测试状态不变量
状态机测试需要覆盖合法转移和非法转移。给定 WAITING_APPROVAL,工具执行事件必须被拒绝;给定 EXECUTING,第二个普通动作不能并发开始;给定 COMPLETED,任何模型输出都不能重新打开运行。属性测试可以随机生成事件序列,检查“完成状态不可逆”“每个已提交写动作至多执行一次”“状态版本单调递增”等不变量。
集成测试随后注入故障:模型输出无效 JSON、工具超时但远端已提交、审批在取消后到达、服务在提交与记录之间重启、验证器自身崩溃。成功路径测试很难暴露这些边界。可靠性来自控制器对异常顺序的明确语义,不来自模型在提示中承诺遵守流程。
状态机还需要检查活性。安全性不变量说明坏事不会发生,例如同一付款不会提交两次;活性说明在环境持续可用且预算充足时,等待中的合法动作最终会得到处理。队列饥饿、丢失唤醒和永久占用锁不会直接违反“至多一次”,却会让运行永远停在 EXECUTING。测试 Harness 可以给每个非终止状态设置最大驻留时间,超时后生成诊断事件并转入明确的中断状态。
取消操作尤其能检验实现是否严谨。用户在模型推理阶段取消,控制器可以终止请求并直接提交 CANCELLED;用户在只读工具阶段取消,可以停止消费结果;用户在外部写操作阶段取消,则只能表达“停止后续步骤”,不能保证已经发出的副作用撤回。事件记录应同时保存取消请求时间和环境提交时间。若提交先发生,系统进入“已取消但需核查结果”,而不是把环境事实改写为未执行。
4.3. 补偿而非虚假回滚
文件事务可以通过临时文件与原子替换获得接近原子提交的语义,跨服务动作通常做不到。订单已创建、库存已锁定、通知已发送后,其中一个服务失败,Runtime 无法让时间倒流。可行做法是定义补偿动作:取消订单、释放库存、发送更正通知。补偿本身也是有副作用的动作,需要权限、幂等标识和验证。
Saga 一类长事务思想适合解释这类流程:每个局部步骤 对应补偿 ,失败时按已提交步骤的逆序尝试补偿。对 Agent 而言,策略可以选择业务上的替代方案,但哪些补偿允许自动执行必须在工具契约中预先规定。模型临时编造一个“反向动作”无法保证恢复原状态,尤其当邮件已被阅读或外部价格已经变化时。
4.4. 状态可见性与用户界面
权威状态在服务端,界面展示它的投影。运行进入等待审批、工具完成或验证失败时,状态写点应产生可订阅事件;页面首次打开或刷新时,再从持久状态水合。只做推送会在断线后丢状态,只做拉取会让打开的页面长期陈旧。推拉结合不会改变 Agent 的决策,却决定用户能否看到真实阶段并安全介入。
界面不得自行发明“正在完成”或“已成功”。流式 token 表示模型正在输出,工具执行事件表示环境动作正在进行,完成事件表示验证谓词已经满足。三个状态的视觉效果可以相似,语义必须分开。用户点击取消或批准时,命令携带当前状态版本;若后台已经推进,服务端拒绝过期命令并返回最新状态,避免旧页面覆盖新事实。
4.5. 与马尔可夫建模的关系
经典决策过程常以 表示状态、动作、转移、奖励与折扣。LLM Agent 的工程状态未必满足马尔可夫性:上下文压缩可能丢失历史,外部服务有隐藏状态,用户意图也无法完全观测。因此 更接近控制器维护的 belief state,即根据历史得到的可操作状态估计。
这个差异解释了为什么“把聊天记录都传回去”仍然不保证正确。历史文本并不自动形成充分统计量,关键约束可能被埋在中间,工具返回也可能缺少环境版本。状态构造需要主动提取任务变量、资源标识与未决不确定性。训练或评测若只保存最终对话,便无法判断失败来自策略、部分可观测性还是状态构造器。
5. 两类完整故障轨迹
5.1. 超时之后的重复写入
考虑创建云端实例的动作。模型给出区域、规格和名称,参数校验通过,执行器以请求键 run7-node3 调用云 API。云端已经创建实例,网络在响应返回前中断。本地收到超时,如果状态机把超时等同于“执行失败”,策略会再次提出创建,最终得到两个实例。正确转移是 EXECUTING → UNKNOWN_COMMIT,此时可用动作集合只包含按请求键查询、等待和人工处理。
查询返回实例标识后,Runtime 记录原动作已提交,将环境事实更新为实例存在,再运行健康检查。健康检查失败可以选择修复或删除实例,但不能否认创建已经发生。若查询明确返回不存在,状态才回到可执行,并沿用同一幂等键重试。整条轨迹显示,错误恢复取决于外部事务究竟处于未提交、已提交还是未知状态;模型生成的解释不具备同等效力。
5.2. 迟到观察与用户取消
另一个例子是长时间代码测试。运行在 EXECUTING(test-9) 时用户取消,控制器提交 CANCEL_REQUESTED 并向进程发送终止信号。测试进程恰好在信号到达前结束并返回成功。事件总线可能先收到取消确认,也可能先收到测试结果。若回调直接赋值,最终状态会随机变成取消或运行中。
reducer 依据版本和转移表处理两条事件:取消请求一旦被接受,测试成功只作为迟到观察写入,不再触发下一项动作;进程终止确认使运行进入 CANCELLED。界面可以显示“已取消;取消请求生效前,测试已经通过”。保留迟到观察能呈现完整环境事实,同时阻止成功事件把用户已经取消的运行复活。
5.3. 何时无需持久状态机
一次只读查询、无外部副作用、失败后可以整体重试且不需要用户介入的短任务,可以用进程内循环实现。即使如此,动作与 observation 仍应分角色记录,终止条件仍应明确。持久状态机的收益会随着运行时间、外部副作用、并发参与者和恢复要求增加。
不要把所有函数调用包装成长事务。过度细分状态会增加事件数量和迁移成本,使业务语义被框架噪声淹没。拆分点应落在需要恢复、授权、并发协调或外部确认的边界。纯计算内部步骤可以保持局部实现,只在输入、输出和失败处留下事件。
5.4. 状态机解决不了什么
状态机能保证流程不变量,不能保证模型提出好动作,也不能自动判断用户真正想要什么。错误的目标谓词会让系统稳定地完成错误任务;过宽权限会让每次转移都合法却危险;贫乏 observation 会让策略在正确流程中持续做出低质量决策。
因此可靠 Agent 需要三类工作同时成立:策略具备任务能力,接口提供可学习的动作与有信息量的观察,Runtime 强制状态和权限不变量。本篇集中在第三类,后续章节分别研究前两类。将失败定位到正确层,是显式状态机带来的首要工程价值。
这套边界也适用于短循环。即使运行只包含一次读取和一次回答,控制器仍可用统一事件记录输入版本、工具结果和完成原因。任务日后增加写操作或审批时,状态语义无需重新发明;实现只扩展动作与转移,而不把旧的聊天字段勉强解释为事务状态。 行动循环还需要区分“模型轮次”和“环境事务”。一个 assistant turn 可以提出多个只读动作,也可能只提出一个需要审批的写动作;一次环境事务又可能跨越多个模型轮次完成。若系统把聊天轮次直接当作事务边界,模型重新生成或网络重试就可能重复副作用。Runtime 应为每个逻辑动作生成稳定 ID,将模型输出解析为意图,再由执行器决定事务何时提交。
状态转移可以写成带前置版本的函数 。 是当前环境证据, 是候选动作, 是可供下一轮使用的观察。执行前验证 和资源版本;不匹配时动作进入冲突,而非在旧状态上继续。该约束让文件、网页、数据库和审批都共享同一种乐观并发语义。
观察也有生命周期。工具在状态 上返回的搜索结果,不能自动解释 ;测试结果只覆盖对应补丁哈希,页面元素引用只对对应 DOM 版本有效。上下文层为 observation 保留来源版本,规划器在使用前检查是否过期。这样可以防止长任务把“曾经正确”误当“现在仍正确”。
一次转移的成功有三层:执行器接受请求、环境产生效果、完成谓词得到满足。HTTP 200 只证明第一或第二层,测试命令退出零只证明被执行的检查通过。状态机分别记录 accepted、committed 与 verified,避免一个布尔 success 混淆协议回执和任务完成。
并行动作需要显式依赖图。若两个读取都依赖相同快照,可以并发;若第二个写入读取第一个输出,就必须等待;两个写动作指向同一资源时,除非工具提供可交换操作,否则串行或冲突。调度器从动作声明的读集、写集和外部副作用判断,而不是让模型用自然语言保证“互不影响”。
设动作 的读写集合为 。当 且 时,两动作在当前抽象下可以交换。真实工具若有隐藏全局状态,例如共享速率限制或当前工作目录,还要把它纳入集合。遗漏隐藏状态会让形式上可并行的调用产生不稳定轨迹。
取消不是删除运行记录。用户提出取消时,状态机保存 cancel_requested,并通知执行器停止尚未越过提交点的动作。已提交效果继续存在,后继计划被阻断。最终状态可以是“已取消,且取消前完成了两个读取和一次写入”,比简单显示 cancelled 更符合事实。
预算耗尽也属于可解释终局。模型调用、工具次数、墙钟、外部费用和风险额度分别计量;预留验证与清理预算,不能让探索阶段消耗全部资源后跳过 verifier。达到上限时保存已完成效果、未满足谓词和恢复入口,使后续运行能够复用成果。
循环检测以状态—动作指纹为基础。若资源版本、错误类型和参数均未变化,重复动作不会产生新信息;若查询范围逐步缩小或 observation 增加,则可能是合理迭代。Runtime 可以拒绝无进展重复,但不应只按工具名和次数截断所有重试。
错误分类直接决定转移。schema 错误回到动作构造,权限拒绝进入等待授权,暂时限流进入带上限退避,未知提交进入状态查询,验证失败回到相关计划节点。把它们都映射成 generic failure,会迫使模型从错误文本猜恢复策略,并让训练数据失去稳定标签。
事件日志让状态机可重放。每个事件保存动作 ID、父事件、预期版本、结果分类和产物引用,物化状态只是事件投影。恢复时从快照和后续事件重建,比较状态哈希;历史修正通过追加更正事件完成,不原地修改已经被消费的事实。
事件溯源并不意味着保存所有敏感原文。控制事件长期保留,模型输出、截图和命令日志放入有权限与保留期的产物存储,事件只引用哈希。敏感产物删除后留下墓碑与原因,既能解释轨迹缺口,也避免日志成为新的泄露面。
完整案例可以从“更新一条客户记录”观察这些边界。模型读取记录版本 7,提出修改地址,Runtime 保存动作 op-31 并请求用户审批。审批绑定记录 ID、旧版本与新地址。等待期间另一进程把记录更新为版本 8,执行前检查失败,原批准随参数与前置状态失效。
系统重新读取版本 8,发现另一进程只修改了联系电话,地址仍可独立更新。模型生成新动作与新审批摘要;执行器使用幂等键提交。响应在服务端提交后丢失,运行进入 unknown_commit。状态查询按 op-32 找到版本 9 和变更记录,补写 committed 事件,再由 verifier 核对地址和联系电话均正确。
若系统在超时后直接重试,会产生重复审计或覆盖新版本;若把超时当失败结束,界面又与环境不一致。动作 ID、版本检查与查询路径共同解决问题,没有任何一个单独机制足够。这个案例也说明行动循环的中心是状态一致性,不是让模型多思考几轮。
反例是把所有逻辑塞进一个 try/finally:成功时清状态,失败时重试,结束时重新拉取。回调到达顺序稍变,迟到结果就可能复活终止运行;拉取与外部提交竞态,又产生空白或重复。正确做法是在 reducer 中拒绝非法版本与终止态转移,编排层只派发事件。
另一个反例是把完成状态存在前端。页面显示任务正在执行,刷新后本地状态丢失;后台实际仍在写。权威状态应在服务端内存与持久层,变化通过推送通知,页面进入时再拉取水合。推负责开着页面实时变化,拉负责刷新后恢复,两者缺一都会暴露假状态。
行动循环的评测需要在每个转移点注入故障:意图保存前、外部提交后、结果事件写入前、验证期间和 UI 通知时终止进程。检查外部资源数量、终局状态、重复事件与恢复动作。服务重新启动并不代表通过,环境终局一致且没有重复副作用才是恢复成功。
性质测试随机排列批准、取消、工具成功、超时和迟到结果,验证终止态不可复活、状态版本单调、同一动作至多提交一次、未审批动作不能执行。对并行分支再随机化完成顺序,确保汇合结果不依赖墙钟偶然先后。
指标除任务成功率外,还包括首次非法转移、未知提交停留时间、重复副作用率、恢复动作数、无进展循环、事件投影延迟和人工介入。成功率相同的两个 Runtime,若一个经常靠人工清理重复资源,可靠性显然不同。
公平性与优先级也进入调度状态。短任务持续插队会让长任务饥饿,单个昂贵运行又可能占满全部工具槽。调度器按用户优先级、等待时间、资源配额和风险设置队列,不让模型通过拆分动作绕过额度。等待状态对用户可见,并可安全取消尚未执行的意图。
背压要沿链路传播。工具池已满时,模型继续生成更多动作只会扩大过期队列;Runtime 暂停规划或限制候选数,待资源释放后从最新状态继续。无界队列会把短暂拥塞转成长时间状态陈旧,随后大量动作因版本冲突失败。
租约用于防止多个工作器同时推进同一运行。工作器取得带期限的 fencing token,提交结果时必须仍是当前持有者。旧工作器网络恢复后携带过期 token,其结果进入审计但不能修改权威状态。租约控制推进资格,动作幂等控制重复副作用,两者作用不同。
状态机升级需要兼容事件语义。新 reducer 若改变旧事件解释,同一历史会重放出不同终局。事件带 schema 与 reducer 版本,升级前用历史样本离线重放比较状态哈希;需要迁移时生成显式新事件或一次性转换,不在读取路径永久猜测旧格式。
用户界面是状态投影,不是权威源。审批页、任务列表和聊天窗口订阅相同运行事件,各自可以有缓存,但不能独立发明 running、failed 或 completed。打开页面通过拉取水合,状态变化通过推送更新;推送丢失后,从事件游标补齐。
可观测性记录每次转移的前态版本、触发事件、判定结果与后态版本。非法转移不能静默消失,开发环境输出诊断,生产环境计数并保护敏感载荷。调查者能够回答某个迟到工具结果为何被拒绝,而不是只看到运行停住。
安全策略同样参与转移。策略服务不可用时,高风险动作停在等待或失败状态,不降级为允许;低风险只读是否继续由预设规则决定。审批过期、能力撤销和预算变化都是显式事件,使运行在外部条件变化后及时收敛。
行动循环的工程目标是让任何时刻都存在可解释、可恢复的权威状态。模型可以提出错误动作,网络可以重复消息,进程可以崩溃,用户可以中途取消;只要转移、提交语义和事件证据守住不变量,系统就能安全暴露错误并继续演进。
清晰边界也降低训练与评测歧义:每个动作都有前态、观察和终局,失败可归到策略、执行器或环境。缺少状态边界时,同一日志能被解释成模型错误或工具迟到,分数很难指导修复。
权威状态因此成为模型、工具和用户之间的共同语言。