跳到正文

AGENT PROJECT INTERVIEW · Q04 / 25

Agent Planner 生成的计划为什么不能直接执行?怎么校验和动态重规划

拆解 Agent Planner 从结构化计划、依赖检查、工具与权限校验,到局部重规划、状态回滚和版本回放的完整工程链路。

作者与复核:吴师兄内容依据:公开 Agent 教程、项目题资料与工程推导查看关联教程 →查看吴师兄公开作品 →

25 道题目Q04
本页内容21 节

30 秒先说结论

Planner 输出的计划可能漏步、依赖冲突、引用不存在的工具、缺少参数或包含越权动作,不能交给执行器。应先把计划约束为结构化 Schema,再依次检查完整性、依赖图、工具与参数、权限、预算和完成条件。执行中若 Observation 让某个前提失效,就冻结已验证结果,只重规划受影响节点;状态不可信或目标改变时才全量重建。计划版本、校验错误和重规划原因应进入 Trace。

面试官问:“Planner 已经把任务拆成步骤了,为什么还要做计划校验?”

一个很危险的回答是:“因为大模型偶尔会幻觉,所以再让模型检查一遍。”

问题不只在于模型可能写错,而在于“计划”连接了自然语言目标和真实工具。一份看起来顺畅的计划,可能引用不存在的工具、漏掉必要前置步骤、使用无来源参数、超过预算,甚至包含当前用户无权执行的动作。它如果直接进入 Executor,模型的一次语言错误就会变成系统行为。

因此,LLM Planner 输出的是候选计划,不是执行授权。工程系统要把候选计划变成可检查对象,逐层验证,再一次释放一个可以安全执行的节点。

先给出 30 秒回答

Planner 输出的计划可能漏步、依赖冲突、引用不存在的工具、缺少参数来源或包含越权动作,所以不能直接交给执行器。我会先约束它输出结构化计划,再依次检查字段完整性、依赖图、工具与参数、权限、预算和完成条件。执行中如果新的 Observation 让某个前提失效,就冻结已验证结果,只重规划受影响节点;用户目标改变、关键状态不可信或工具能力整体变化时,才全量重建。每份计划、状态和重规划原因都带版本进入 Trace,写操作另外记录幂等键与副作用状态,避免计划变化后重复执行。

这段回答把四个层次说清了:计划是数据、校验分层、重规划有范围、执行有历史。

为什么“步骤写得合理”还不够

人阅读计划时容易被语言流畅度影响。模型写出“先收集资料,再做比较,最后生成报告”,逻辑似乎没有问题,但执行器仍然不知道:收集哪些资料、使用哪个工具、输入从哪里来、怎样判断收集完成、资料缺失时怎么办。

可执行计划必须回答机器问题,而不是只回答人类的阅读问题。

计划可能引用不存在的能力

模型根据训练知识写出一个工具名,并不代表当前系统真的注册了它。即使名字存在,版本和参数也可能不同。工具注册表才是能力事实来源。

计划可能漏掉必要步骤

教学场景中,模型计划直接“读取记录并发送结果”,但业务要求先做身份和数据权限检查。漏掉的不是优化步骤,而是不能绕过的边界。

计划中的参数可能没有来源

模型写出日期、对象 ID 或过滤条件,却无法指出来自用户输入、当前状态还是工具结果。参数看起来合法,也可能是猜出来的。

计划依赖可能无法执行

步骤 B 依赖步骤 A 的输出,步骤 A 又要求先获得 B 的结论;或者两个节点都等待对方完成。自然语言列表不容易发现这种环,依赖图可以检查。

完成条件可能只是主观描述

“分析充分”“结果合理”“资料完整”无法由系统直接验证。需要改成可观察条件,例如必需字段已获得、指定测试通过、每条结论有来源或缺失项已明确标记。

计划先要成为一个有契约的数据对象

具体字段可以按项目调整,但一份教学计划至少应包含:

  • 全局目标和边界;
  • 子任务 ID;
  • 子任务目标;
  • 前置依赖;
  • 候选工具或动作类型;
  • 参数以及参数来源;
  • 成功条件;
  • 可预期失败;
  • 风险等级与是否需要确认;
  • 预算或截止条件。

下面是为说明结构而写的教学示例,不代表现有项目代码:

{
  "plan_id": "teaching-plan",
  "goal": "比较资料 A 与资料 B 的适用条件",
  "steps": [
    {
      "id": "extract_a",
      "depends_on": [],
      "tool": "read_document",
      "args_from": ["user.document_a"],
      "success": "返回条件列表及原文位置",
      "risk": "read_only"
    },
    {
      "id": "compare",
      "depends_on": ["extract_a", "extract_b"],
      "tool": "compare_evidence",
      "success": "输出相同项、差异项与缺失项",
      "risk": "read_only"
    }
  ]
}

结构化并不自动保证计划正确,但它让错误可以被定位。字段缺失是结构错误;工具不存在是能力错误;用户无权访问是权限错误;资料变更导致前提失效是运行时错误。不同错误走不同处理路径。

候选计划到安全执行要过七道关

计划越接近真实执行,越不能只靠自然语言“看起来合理”。

计划校验应该分成哪几层

第一层:Schema 与字段完整性

检查输出是否能解析,必填字段是否存在,类型和枚举是否正确,子任务 ID 是否唯一。结构错误不必调用工具,更不应进入权限系统。

Schema 校验适合确定性代码。模型可以根据错误摘要修复一次,但如果反复生成相同错误,应停止或切换到受控模板,不能无限修复。

第二层:依赖和图结构

检查前置节点是否存在、是否有非法环、是否出现永远不可达节点、多个节点是否争用同一不可并发资源。计划如果允许并行,还要明确汇合条件。

这层也主要由代码完成。图算法能判断结构是否合法,模型没有必要用自然语言猜。

第三层:工具与参数

工具名必须来自当前用户可见的注册表;参数类型、范围和枚举符合工具 Schema;参数来源必须存在。一个日期字段通过类型校验,但若来源只是模型自行补全,仍不能执行。

工具版本也要进入计划。工具升级后字段变化,旧计划不能默认兼容。

第四层:业务完整性

固定规则要求的节点是否都在,例如身份、权限、审批、结果验证。这类规则不应仅依赖 Planner 自觉生成,应由模板或 Policy 检查器强制验证。

第五层:权限与副作用

用户能否访问数据,Agent 是否有权调用工具,动作是只读还是写入,是否需要人工确认,全部由后端判断。模型写在计划里的 risk=read_only 只是请求声明,系统要按真实工具元数据重新计算。

第六层:预算与可完成性

估计节点数量、串并行关系、必需工具和剩余预算。如果计划明显超出任务允许的总耗时或工具次数,应要求缩减、拆分或转异步,而不是执行到一半才发现无法完成。

预算估计只能作为控制依据,不应被包装成精确成本预测。实际值仍从执行日志获得。

第七层:完成条件

每个节点必须能通过工具结果、测试、规则或人工确认判断是否完成。“模型觉得够了”可以触发检查,但不是最终证据。

哪些校验交给代码,哪些可以交给模型

可以确定判断的项目优先交给代码:字段类型、枚举、唯一性、依赖环、工具存在、参数边界、用户权限、预算硬限制和必须节点。

需要理解语义的项目可以使用模型辅助,例如子任务是否偏离用户目标、两个步骤是否语义重复、完成条件是否覆盖用户要求。但模型判断应输出理由和引用的计划字段,并接受规则或人工抽检。

不要让同一个 Planner 在没有新证据的情况下回答“我生成的计划是否正确”。这容易产生自我确认。可以使用独立规则、不同提示的 Critic、工具模拟、测试或人工审核,但仍要承认它们各自的边界。

计划失败要定位到具体校验层

校验错误必须保留原始类别,统一让 Planner 重写会让根因消失。

执行时为什么一次只释放一个或一组安全节点

通过校验的整份计划也可能在运行中失效。外部数据会变化,工具会失败,用户会补充约束。一次性把整份计划转换成不可中断脚本,会让后续步骤继续基于旧前提执行。

更稳妥的方式是保存计划和任务状态,只释放当前前置依赖已经满足的节点。工具完成后,验证结果并更新状态,再决定后继节点是否仍然有效。

允许并行时,也要确认并行节点没有共享写冲突,结果汇合有确定规则。并行不是 Planner 写了“同时执行”就能安全发生。

什么情况下只做局部重规划

局部重规划的前提是:用户目标没变;已完成节点的结果仍然可验证;失效范围可以从依赖关系确定。

例如教学资料比较中,资料 B 的读取工具临时不可用,但资料 A 的解析结果仍然有效。系统可以冻结 A,只替换读取 B 的方案以及依赖 B 的比较节点,不必重新读取 A。

局部重规划通常经历:标记失败节点;找到所有依赖它的未完成节点;暂时失效这些节点;保留不受影响的完成证据;在剩余预算和工具范围内生成替代子图;重新校验后接回计划。

这里不能简单按节点编号删除。一个失败结果可能已被后续节点使用,必须依据真实依赖传播失效范围。

什么情况下必须全量重建

用户改变目标,原计划的整体方向已不成立;关键输入被证明错误,已完成结果无法信任;工具能力或权限发生大范围变化;计划 Schema 版本变化,旧节点无法安全解释。这些情况下继续局部修补会保留错误根基。

全量重建也不等于丢掉全部历史。原始输入、用户确认、工具证据和失败记录仍应保留,只是不能把它们继续标记为当前计划的有效完成节点。

局部重规划与全量重建不是一回事

重规划范围由目标与状态可信度决定,不由模型一句“重新规划”决定。

计划变化后,怎样避免重复副作用

这是动态重规划最危险的一环。

假设旧计划已执行“发送通知”,但在写回完成状态前进程中断。恢复后 Planner 看不到成功记录,又生成一次相同动作。没有幂等或外部状态核验,就会重复发送。

写操作至少需要:稳定的业务幂等键;请求与外部结果记录;副作用状态;恢复前查询真实业务结果;无法确认时转人工。计划节点 ID 可以帮助追踪,但不要把随机生成的节点 ID 直接当作业务幂等键,因为重规划后 ID 可能变化。

计划版本和状态版本也要分开。计划描述“准备做什么”,状态描述“实际上发生了什么”。修改计划不能覆盖已经发生的事实。

计划、状态和事件日志要分成三类数据

如果把三者都塞进一个 JSON,每次模型重写计划都可能覆盖已完成事实,恢复时也无法判断某个字段是模型建议还是工具确认。

计划保存目标、节点、依赖和候选动作;当前状态保存哪些节点待执行、运行中、完成、失败或等待确认;事件日志追加记录每次计划生成、校验、工具请求、结果、审批和状态迁移。事件日志不应被模型修改,它是回放与审计依据。

例如 Planner 把节点名称从“读取资料”改为“提取元数据”,计划变化不代表旧工具调用从未发生。状态仍要引用已经完成的事件和证据,再判断新节点能否复用它。

状态迁移要使用比较条件

并发执行或进程恢复时,两个执行器可能同时读取旧状态并写回。仅靠最后写入覆盖,容易丢失已完成结果或重复释放后继节点。

可以为状态保存版本号,更新时要求“当前版本仍等于我读取的版本”。若不一致,重新读取最新状态并判断本次结果是否仍然有效。具体数据库实现可不同,但核心是不能把并发一致性交给模型。

计划节点需要稳定的业务身份

重规划后节点 ID 可能变化。若系统仅按模型生成的字符串判断是否执行过,就无法识别“发送同一通知”实际上是同一业务动作。

只读动作可以按工具、规范化参数和输入版本做去重;写操作应使用由业务语义生成的幂等键,例如任务、对象和动作类型的组合。幂等键设计必须基于真实业务,不能简单复制教学格式。

结果复用要验证来源与有效期

局部重规划希望复用旧结果,但旧结果不一定仍然有效。系统要检查它来自哪个工具版本、查询参数、数据时间和权限上下文。用户权限变化后,即使文本内容没有变,旧结果也可能不能继续使用。

因此“冻结已验证结果”不是无条件缓存。每个结果要带来源、状态版本和必要的有效期,重规划时由规则判断复用、刷新或作废。

并行节点和汇合节点怎么校验

计划允许并行时,需要明确节点之间没有数据依赖和写冲突。两个只读查询通常可以并行;两个同时修改同一对象的动作则要串行或使用业务并发控制。

汇合节点要写清等待策略:必须全部成功、至少一个成功,还是达到某个证据集合。若一路失败,其他成功结果是否可形成部分答案,也要进入完成条件。

Planner 只写“并行处理”远远不够。校验器要检查共享资源、汇合条件、失败传播和取消策略,Executor 才能安全调度。

计划差异也应该进入评审

同一个任务在更换模型、Prompt 或工具描述后,可能生成结构不同但都能通过 Schema 的计划。发布前应比较新旧计划:必要节点是否丢失;新增工具是否扩大权限;串行是否变成并行;完成条件是否变松;调用范围是否无故扩大。

差异评审不要求计划文字完全一致。重点是行为边界有没有变化。若只是节点名称和解释改写,可以通过;若审批节点消失或写操作提前,则必须拒绝并查清来源。

对于无法自动判断的语义差异,可以抽样人工审查,并把结论沉淀为新的规则或回归样本。这样计划质量会随着真实坏例积累,而不是只靠不断加长 Prompt。

常见失败模式与诊断

失败一:自动修复掩盖原始错误

解析器发现工具名不存在,直接换成名字相似的工具继续执行。最终即使成功,也无法知道模型原本为何选错,更可能调用语义不同的能力。

正确做法是保留原始计划、校验错误和修复版本。确定性的小格式错误可规范化,涉及语义的替换必须重新确认。

失败二:每次 Observation 都整份重规划

这会让计划抖动,已完成步骤不断改名,难以追踪,也增加模型调用。只有前提或目标变化时才重规划;正常成功结果只更新状态并释放后继节点。

失败三:完成条件由 Planner 自己解释

Planner 生成“拿到足够信息”,执行后又说“信息已经足够”。没有独立证据,计划形成自我闭环。

完成条件应尽量绑定工具字段、测试、规则或人工确认。

失败四:状态与计划混在一个长 Prompt

每次修改都让模型重写整段文本,无法知道哪些是已发生事实、哪些是待执行建议。应分离 immutable Trace、当前状态和候选计划。

失败五:全量重建后重复写操作

没有副作用记录、幂等键和外部核验,新的计划把旧动作当作未完成。此问题不能靠 Prompt 提醒解决。

怎样验证计划校验和重规划

准备四类计划样本

第一类结构错误:缺字段、重复 ID、类型错误。第二类依赖错误:前置缺失、非法环、不可达节点。第三类业务错误:漏权限、超预算、越权工具。第四类运行时变化:工具失败、结果冲突、用户修改目标。

对每层定义可观察结果

结构错误应在执行前拒绝;依赖错误不能释放节点;越权动作不能通过换工具绕过;局部变化应只失效受影响子图;目标变化应生成新计划版本;已完成副作用不能重复。

比较逐题明细而不是单一总分

记录每个样本在哪一层被发现、错误是否解释清楚、是否错误放行、重规划范围是否正确、是否重复执行。一个“计划通过率”无法区分严格校验和漏检。

回放版本变化

更换模型、Prompt、工具 Schema 或规则后,用相同状态重放。对比生成计划、校验错误、节点释放和最终状态。必须保存版本,否则无法解释差异来源。

面试官会怎样连续追问

追问一:计划至少有哪些字段

目标、子任务 ID、依赖、候选工具、参数及来源、完成条件、失败条件、风险和预算。字段名可变,但必须能校验与回放。

追问二:为什么不再让模型检查一遍

模型可辅助语义检查,但字段、依赖、工具、权限和预算都有确定事实,应由代码验证。同一个模型自评不能成为唯一安全门。

追问三:什么时候局部重规划

目标不变、已有结果可信、失效范围可以沿依赖图定位时。只替换失败节点和受影响下游。

追问四:什么时候全量重建

用户目标改变、关键输入不可信、整体权限或工具能力变化、旧计划版本无法安全解释时。

追问五:重规划如何避免重复执行

计划和执行状态分离;写操作使用业务幂等键;记录副作用结果;恢复前核验外部状态;不确定时转人工。

追问六:怎么证明校验有效

用结构、依赖、业务和运行时四类错误集逐题回放,检查错误发现层、误放行、失效范围和重复副作用,不虚构总分。

计划与重规划验收清单

计划、状态和副作用必须可对齐,才谈得上安全重规划。

怎样向面试官解释计划校验与重规划

LLM Planner 生成的是候选计划,不是执行授权,因为它可能漏掉必要步骤、依赖成环、引用不存在的工具、使用无来源参数或包含越权动作。我会先让计划符合结构化 Schema,再分层校验:字段与类型、依赖图、工具和参数来源、业务必需步骤、用户权限、风险与预算、每个节点的完成条件。通过后也不整份脚本一次跑完,而是只释放前置依赖已满足的节点,工具结果验证后更新状态。运行中单个工具失败、而已有结果仍可信时,沿依赖图只重规划受影响子图;用户目标改变、关键状态不可信或工具能力整体变化时,才全量重建。计划版本、状态版本和副作用记录分开保存,写操作用业务幂等键并在恢复前核验外部结果。验证时用结构错误、依赖错误、越权计划和运行时变化做回放,检查是否误放行、是否错误扩大失效范围,以及是否重复执行。

谈重规划前,先准备这些项目证据

如果简历写了“支持动态重规划”,下面这些材料应当能被当场展开:

  1. 一份真实但脱敏的计划 Schema。
  2. 结构、依赖、工具、权限和预算各层校验规则。
  3. 计划版本与状态版本的存储方式。
  4. 一次局部重规划的失效范围说明。
  5. 一次全量重建的判断依据。
  6. 写操作的幂等与副作用核验方案。
  7. 一组非法计划与环境变化回归样本。

如果你的项目没有实际 Checkpoint、执行记录或故障回放,就明确说这是设计方案。能让模型列出漂亮步骤并不难,难的是证明每一步可执行、变化后不重复、失败时知道该保留什么、推翻什么。

INTERVIEW FOLLOW-UPS

面试官会怎样继续追问?

这些追问会继续检查方案取舍、验证方法和项目事实。
  1. Q01

    一份可执行的 Agent 计划至少要包含哪些字段?

  2. Q02

    哪些校验适合规则完成,哪些需要模型或人工判断?

  3. Q03

    什么时候局部重规划,什么时候必须整份计划重建?

  4. Q04

    计划版本变化后,如何避免重复执行已经完成的副作用?

PROJECT CHECK

换成你的项目,这几件事能否说清楚?

  • 准备计划 Schema、依赖和完成条件定义
  • 区分结构、语义、权限和预算四层校验
  • 记录每次重规划的触发原因与状态差异
  • 用漏步、非法工具、状态变化和重复执行做回归
用自己的项目经历练一次