对 Agent 来说,“能力”这个词太抽象。 真正具体的是动作空间。
动作空间指的不是模型理论上能生成多少种句子,而是系统实际上允许它发起哪些动作、以什么参数发起、动作会产生什么效果、失败后会留下什么状态、以及这些动作在什么边界内可被调用。换句话说,动作空间是 Agent 对外部世界的可操作面。
这一定义一旦成立,很多问题就会立刻清楚起来。
一个系统如果只能回答问题、不能触发外部变化,它更像解释器;一个系统如果可以查数据库、点网页、发邮件、改工单、执行代码、调用支付、创建审批、操作桌面,它才真正进入了行动系统的范围。于是,“Agent 到底能做什么”这个问题,归根到底不是问模型有多聪明,而是问系统把多大的世界控制权交给了它。
所以,动作空间首先决定能力边界,其次决定风险边界。
如果动作空间过窄,系统就只会解释,不会推进任务;如果动作空间过宽,却没有约束、验证和审批,系统就会迅速进入高风险区。很多团队早期只关注第一点,担心 Agent 能做的太少;真正上线以后,往往才发现第二点更要命,因为一旦动作具有副作用,错误就不再停留在文本层,而会变成真实的外部世界变化。
这也是为什么今天主流平台都在把工具调用做成显式原语,而不是让模型自由输出“我现在去做某事”。函数调用、OpenAPI action group、MCP tool、browser automation、computer use、code interpreter、shell execution,本质上都在做同一件事:把原本模糊的“做事能力”压缩成一组受约束的动作接口。
从更早的经典理论看,这并不新鲜。
无论是 STRIPS 里的 operator,还是 PDDL 里的 action,核心都是一样的:动作必须有前置条件、参数、效果和状态更新语义。现代 Agent 的工具接口虽然换成了 JSON Schema、OpenAPI 或平台 SDK,但本质仍是 operator 的工程化版本。一个工具之所以可被系统调用,不是因为模型会说它的名字,而是因为这个动作已经被明确写成契约。
所以,一个成熟的工具接口,至少要把七件事说清楚。
第一,动作名称是什么。
第二,动作目的是什么。
第三,输入参数的结构是什么。
第四,输出结果的结构是什么。
第五,失败信号与错误类型是什么。
第六,权限等级和可调用条件是什么。
第七,副作用与外部后果是什么。
前五项通常比较容易被团队接受,因为它们看起来像技术接口设计;真正最容易被低估的是后两项,尤其是副作用说明。因为在真实生产环境里,一个动作调用从来不只是“取点信息”,很多时候它会真正改变外部世界。
它可能会写数据库,发客户消息,创建工单,提交审批,改配置,触发自动化流程,执行命令,甚至完成支付或发布内容。到了这一步,动作就不再是语言行为,而是外部世界行为。只要团队没有把这一点写进系统设计,风险就会以一种非常隐蔽的方式累积起来:表面上看是模型“输出了一段文本”,实际上系统已经接受那段文本作为操作指令去改变世界了。
因此,动作空间设计的核心,不是把工具接得越多越好,而是把动作定义得越清楚越好。
这里的“清楚”至少包含三层。
第一层,是语义清楚。
Agent 必须知道这个动作究竟是“查询”还是“修改”,是“建议”还是“执行”,是“读操作”还是“写操作”,是“幂等动作”还是“一次性动作”。如果这些语义在接口层不清楚,模型就很难形成稳定的动作选择策略,系统也很难在风险上分级治理。
第二层,是参数清楚。
动作空间的很多错误并不是选错了工具,而是参数填错了、漏填了、单位错了、目标对象错了、依赖顺序错了。正因为如此,结构化输出、严格 schema、参数校验和 tool-result 配对协议才会在各家平台里被反复强调。模型的自由语言能力很强,但越接近执行层,就越不能依赖自由语言来承载动作细节。
第三层,是后果清楚。
一个动作一旦成功,会产生什么状态变化;一旦失败,会留下什么中间状态;如果动作部分成功,系统能否识别;如果动作不可逆,系统是否需要审批;如果动作幂等性不足,重试会不会造成重复副作用。这些问题如果不前置考虑,动作空间就只是“表面可调用”,不是真正可运行。
所以,本章更准确的说法应该是:
动作空间不是工具清单,而是系统允许 Agent 影响世界的方式集合。只要这样理解,很多平台设计就变得非常自然。为什么 function calling 要用 schema 描述参数?因为动作必须可验证。为什么 tool result 必须带着对应的 call id 回填?因为动作不是一句话,而是一次有身份、有结果的执行事件。为什么 computer use 要把截图、鼠标、键盘、命令动作拆开?因为 GUI 世界也必须被离散化成一组可操作原语。为什么有些平台支持 long-running tools、pause、resume、approval?因为很多动作不是瞬时完成,而是带时长、带等待、带人工门控的过程。
从控制与强化学习角度看,动作空间还有另一个常被忽略的维度:层级。
并不是所有动作都应该是原子动作。很多时候,真正有用的不是“向左点击一次”“再输入一个字符”这种极细粒度控制,而是更高层的 option、skill 或子任务,比如“获取客户资料”“完成一次排障检查”“执行一次安全审批”“提交一次代码修改并跑测试”。这是经典层级控制理论早就强调过的事情。Agent 如果被迫只在过低层级上思考,动作序列会变长,错误面会变大,规划成本也会飙升。
于是,好的动作空间往往是分层的。
底层动作解决原子控制问题,中层动作解决领域工具问题,高层动作解决任务步骤问题。很多现代 Agent 系统中的 manager-worker、planner-executor、subagent-handoff,本质上都在做动作空间的层级化重组,而不只是角色分工。
但分层并不自动带来安全,反而会带来新的接口问题。
因为一旦动作开始多步串联,系统就不再只需要考虑“单个工具调得对不对”,而要考虑整条动作轨迹是否正确。一个动作的输出可能变成下一个动作的输入;一个局部参数错误可能在第三步才表现为严重后果;一个中途失败如果没有被显式标记,后面动作可能会在错误状态上继续执行。这就是为什么 NESTFUL、ToolHop、ToolSandbox、AppWorld、WebArena 这类 benchmark 都在强调 trajectory correctness,而不是只看单轮函数调用是否格式正确。
因此,动作空间的真正难点从来不在“是否支持工具”,而在“是否支持正确执行”。
这又把我们带回工具接口设计本身。一个生产级动作接口,不应该只暴露“入口”,还要暴露:
- 这个动作能否并行执行
- 这个动作是否需要前置确认
- 这个动作是否允许自动重试
- 这个动作失败是否可恢复
- 这个动作执行过程中是否会产生中间状态
- 这个动作是否需要沙箱环境
- 这个动作是否必须在用户上下文权限内执行
如果这些属性不被写入接口层,系统后面就只能靠 prompt 临时猜测,而这恰恰是最不稳定的做法。
再看一个最常见、也最容易被低估的动作:“发送消息”。
从技术上看,它可能只是一个 send_message 工具;从业务后果看,它可能完全不是同一个动作。发一条普通提醒,也许风险很低;发一条对客户作出承诺的消息,风险显著更高;发一条面向公众的公告,又是另一种级别。也就是说,动作空间不能只按技术类型划分,还必须按业务后果分层。否则,同样一个“发送动作”会把低风险通知和高风险承诺混在一起,最终让系统无法治理。
这正是“最小权限”在 Agent 里变得特别重要的原因。
一个成熟系统不应让 Agent 拥有抽象的“发送能力”“写入能力”“执行能力”,而应让它拥有被业务语义切开的、被权限边界限制的具体能力。例如:只能给当前工单相关责任人发内部提醒,不能对外发送;只能创建草稿,不能直接提交;只能生成支付建议,不能调用支付;只能在只读数据库执行查询,不能改表;只能在沙箱里跑代码,不能直接进入生产环境。动作空间越强,这种切分越重要。
于是,动作空间设计最终会落到一个非常工程化的闭环:
先定义动作,再定义 schema;先定义 schema,再定义权限;先定义权限,再定义执行回路;先定义执行回路,再定义失败处理、审批节点、审计记录和补偿路径。没有这个闭环,所谓“工具调用”就只是把风险从语言层搬到了执行层。
所以,第 19 章最终要建立的结论是:
Agent 能做什么,不由模型的想象力决定,而由系统为它声明、约束、验证并执行的动作接口决定。动作空间越清晰,Agent 的能力就越稳定;动作接口越结构化,系统就越容易评测;权限和副作用边界越明确,系统就越安全;层级和协议设计越好,Agent 就越可能在真实环境中完成任务,而不是只在 prompt 里显得很会做事。