Agentic 工程的核心不是多生成,而是让智能体学会拒绝输出
2026/9/11 14:46:43 网站建设 项目流程

Agentic 工程现在真正值得讨论的,不是让 Agent 生成更多的输出,而是让 Agent 学会拒绝输出。这不是一句反常识的口号,而是我在接触多轮自动化任务、代码修改型 Agent、数据加工流程之后得出的实际感受:很多系统从“跑不通”到“跑得通”,靠的不是把模型换大、把提示词写长,而是把“哪些结果不该被放行”这件事提前设计进流程里。这篇文章想把这个判断拆开讲清楚,包括为什么生成型优化会失效、拒绝优先的结构是什么样的、落地时看哪些指标,以及新手和生产环境应该选择什么配置。

Agentic 工程和传统程序开发的差别很大。传统程序是确定性逻辑:输入合法,执行路径明确,输出可预期。Agent 程序不是这样,输入是模糊的自然语言任务,中间步骤由模型自己决定,工具调用一个接一个,最终结果还要经过一层判断才能确认“这个任务真的做完了”。所以 Agent 系统的难点从来不是把某个环节做出来,而是每个环节都可能出错,而且出错方式比普通程序更隐蔽。少生成、慢生成、拒绝生成,经常比多生成、快生成、完整生成更安全。

我见过很多团队评估 Agent 时只看一个问题:它能不能自动完成一个端到端任务。能完成,就继续;不能完成,就换模型、加 few-shot、加长上下文。但自动化稳定之后,真正的风险才开始出现:Agent 会以极高的置信度执行错误动作,会把“生成了一段看起来合理的文本”误认为“完成了真实世界的操作”。这时候最需要的机制不是提高生成能力,而是建立一整套能让输出被拦截、被拒绝、被送回重做的工程屏障。

1. Agentic 工程的核心矛盾:生成容易,验证很难

1.1 生成能力已经溢出,可信执行才是瓶颈

先明确一个概念。Agentic 工程不是指用提示词调用一次大模型,而是指让大模型在受控环境中自主完成多步任务,比如调用代码解释器、操作文件、执行命令、调用业务 API、汇总结果。这类系统真正的高风险点不在模型推理,而在行动。

一个语言模型生成一段代码很容易,生成一句“任务已完成”更容易。但它有没有真正读取文件、有没有实际执行测试、有没有检查输出目录、有没有在失败后停下来而不是继续补一段虚构结果,才是 Agent 系统能不能用于生产的区别。这部分能力不会因为模型的推理水平提高而自动解决,只能靠工程手段解决。

所以我对这个标题的理解是:把 Agent 当成“一个努力输出的东西”,方向就错了。它应该被当成“一个随时准备停下来的执行器”。系统设计者优化的是过滤机制、停止条件、校验链路,而不是最大化解码长度、工具调用次数和完整度。

1.2 “会说话”不等于“会办事”,“会办事”不等于“办对了”

区分三层能力很重要。

第一层是生成。模型能写出步骤、写出代码、写出答案,这是当前模型最擅长的。

第二层是执行。Agent 能调用工具、操作文件、运行命令,这是 Agent 框架层解决的问题。

第三层是判断。Agent 需要决定哪些动作不该执行、哪些结果不该返回、哪些情况必须停下来问人。

大部分项目卡在第三层。原因是第三层没有现成模型可以直接负责,它需要工程规则来兜底。比如“遇到任何删除操作都必须停下来确认”“测试未通过时不允许提交结果”“输出涉及用户隐私字段时直接丢弃”“上下文不足时明确说不知道,而不是补全一个答案”,这些都是拒绝策略。

判断能力越弱,系统就越容易给人一种“已经很智能”的错觉,因为生成结果太流畅了。但只要有一次错误输出没有拦截,整个任务的代价就可能超过它之前所有正确输出节省的人力。拒绝,是 Agent 的可信执行里最便宜的兜底机制。

2. 过度生成在实际场景里是怎么造成事故的

2.1 代码类 Agent:改动范围失控的典型场景

代码助理类 Agent 是最常见的应用场景。用户说“把项目里过时的接口调用清理掉”,Agent 如果只按字面理解,就会搜索所有相关代码,然后一次性大范围替换。结果可能是:更新了不该更新的注释、顺手改了格式、把不同模块里语义不同的同名变量一起替换了,甚至在没有跑测试的情况下直接标记为完成。

这类事故很少是模型“不聪明”造成的,更多是系统没有约束动作范围。真正应该发生的是:先搜索,定位到具体文件,列出待改动清单,逐项标注理由,请用户确认,再小步执行,每步都跑一次测试。任何一步失败,就停下来。

所以代码类 Agent 的工程重点不是让它写多少代码,而是让它在边界之外拒绝行动。改动面多大需要确认、哪些目录只读、哪些分支不允许推送、测试失败后禁止继续提交,每一条都是拒绝规则。

2.2 数据任务里最常见的“自信用错”

数据处理类 Agent 更危险,因为它的输出看起来精确。比如让 Agent 从一批表格里提取某些字段并统计,如果它遇到缺失值、编码错误或语义歧义,最容易出现的行为是“猜一个合理值继续算”,而不是停下来报告数据质量问题。结果是输出完整、数字合理,但前提假设是错的。

正确的 Agent 行为应该是:发现数据异常,先记录;字段含义不明确,先输出假设并请求确认;必要情况下直接判定“这个任务当前不可执行”。拒绝不执行,本身就是有价值的输出,因为它把风险暴露在流程早期。

我一般跟团队说,数据类 Agent 的第一产出物不是最终报表,而是数据质量说明。如果这个环节没有明确的停止机制,后面再智能都会变成事故放大器。

2.3 成本视角:一次错误输出的代价等于 N 次正确输出

为什么拒绝值得被当作核心优化目标?因为 Agent 系统运行成本不只是 token 费用。

错误输出会产生连锁成本:人工审查需要时间,错误操作需要回滚,下游任务需要重跑,用户对系统的信任会下降。一次错误输出可能让数十次正确输出积攒的信任清零。如果系统设计成“尽量生成、少打扰人”,那它在低风险任务里体验确实好,但一旦出错,代价是堆叠式的。

反过来,如果系统主动拒绝、频繁请求确认,表面上看效率变低了,但实际上每一次通过的输出都更接近可用状态。对生产系统来说,宁可多在中间环节停下来,也不要让错误一路畅通地到达终点。真正贵的不是“多问一次”,而是“错到底才发现”。

3. Agent 要拒绝的不是“不做”,而是“不可验证地做”

3.1 把确认、校验、拦截拆成三个独立环节

很多 Agent 设计把安全检查写在提示词里,让模型“自己注意安全”。这种做法在简单任务里有效,在复杂流程里不可靠。更好的做法是把控制逻辑移动到工程层,从三个环节分别把关。

确认环节负责解决意图问题。Agent 在行动前重复一遍它理解的用户意图、执行范围、预期产物和风险点,用户可以修改或确认。这一步看起来多余,却是打断“自说自话执行”的最有效手段。

校验环节负责解决结果问题。Agent 完成一个动作后,系统必须运行自动校验,比如测试是否通过、输出模式是否合法、文件是否生成、结果是否为空。校验不通过时,不允许进入下一步,也不允许把中间结果当最终结果返回。

拦截环节负责解决边界问题。敏感操作、高风险目录、网络请求、删除动作、推送发布等都要设置独立开关。开关默认关闭,只有具备权限的人或明确的策略才能打开。三个环节分开,日志也分开,出事时能快速定位问题出在意图理解、结果质量还是权限配置。

3.2 最小动作原则:小步多次优于一次性大改

让 Agent 学习一种工作习惯:一次只解决一个问题,尽量使用最小化的改动。这不是为了慢,而是为了可追溯。改动越小,每个中间结果的校验成本越低,出现问题时回滚范围越窄。

批量任务尤其要遵循这个原则。很多 Agent 工具支持一次处理一百个文件,但这种批量能力应该放在流程稳定之后,不应该放在最前面。最开始我只建议跑一条、跑三条、跑十条例行任务,确认每一条的输出差异都在可接受范围内,再放开批量。

如果 Agent 倾向于在一次请求里做很多动作,工程上要主动拆分。一个任务拆成“搜索、过滤、草拟、确认、执行、验证”六个子步骤,比让模型自由发挥更稳定。每个子步骤都能独立判断是否继续,这就是拒绝优先的落地形态。

3.3 人工确认点是决策器,不是审批负担

很多人优化 Agent 时喜欢把人工确认点减到最少,觉得“全自动”才算成功。但人工确认点真正的作用是处理低概率但高成本的错误,这跟审批流程是两回事。

一个好的确认点应该提供足够清晰的上下文。不是单纯问“是否确认继续”,而是给用户看到 Agent 当前理解、待执行动作、可能影响的范围、预估风险。这样用户几秒钟就能做出决定。如果确认点设计得让用户还要去查日志、翻源码才能判断,那这个确认点就是失败的,需要先把信息整理好再问人。

我见过一个比较好用的节奏:主动考虑,被动执行。Agent 可以提出完整方案,但动作要等用户明确指令或策略放行后才执行。这个过程的人工操作次数并不一定多,因为大多数策略可以通过规则前置放行,只有规则覆盖不到的才转人工。

4. 把“拒绝优先”落进实际工程流程

4.1 先写验证器,再写生成器

这是我最想强调的一点。很多 Agent 项目都是先让模型生成结果,再考虑怎么校验。正确的顺序应该反过来:先把验证器写出来,再让模型生成。

验证器长什么样取决于任务。代码任务里是单元测试、编译检查和静态检查;数据处理里是字段完整性、类型合法性、空值和异常值统计;文本任务里是格式约束、关键词约束和长度边界。验证器不需要一开始就很完善,但至少要能判断三件事:结果是否为空、结果是否符合结构约束、结果是否与输入产生明显冲突。

有了验证器之后,Agent 的提示词里就可以写清楚:只有通过验证的结果才算完成。验证失败时,要么重新执行,要么明确返回失败原因,不能生成一个“看起来像成功”的答案。这个逻辑放在工程层以后,模型会产生一种行为倾向:它知道后续会校验,所以编造概率会降低,不确定时会倾向于说明问题。

4.2 给 Agent 足够明确的停止条件

Agent 任务失败最典型的现象不是停止,而是硬着头皮继续。任务已经做不下去,模型还在生成步骤、补全文件、尝试新路径。根因是缺少停止条件。

在设计任务模板时,要显式写下几种允许停止的情况:

  • 必要条件缺失,比如找不到目标文件、字段不存在、依赖未安装;
  • 校验结果不通过,且尝试重试次数达到上限;
  • 发现执行结果与预期目标冲突;
  • 上下文已经无法支撑完整执行,继续部分执行会产生误判;
  • 需要更高权限或者依赖外部信息才能继续。

这些规则应该在提示词里,也应在编排层,必要时用代码硬性终止。比如任务超过一定步骤数或者超过一定时间就自动进入“待人工处理”状态,而不是让模型继续尝试。一个明确说“我无法继续,因为测试日志显示 NPM 包不存在”的 Agent,比一个硬编一个包版本继续跑完流程的 Agent 可靠得多。

下面是停止条件比较推荐的一种规范化写法,方便把决策规则收敛到一处。

[AGENT_STOP_RULES] - IF 输入缺少关键参数 AND 无法自行获取 -> STOP_REASON=INSUFFICIENT_INPUT - IF 校验器返回 FAIL 且重试 >= 3 次 -> STOP_REASON=VALIDATION_FAILED - IF 计划改动范围超过用户预期边界 -> STOP_REASON=SCOPE_EXCEEDED - IF 需要执行 forbidden_actions 中的动作 -> STOP_REASON=PERMISSION_DENIED - ELSE -> CONTINUE

注意,这不是标准代码,只是一份通用规则样例。实际字段名和判断顺序要依据你自己的框架来定,但要让模型和人都能看懂同一份停止语义。

4.3 用沙箱和最小权限兜住危险动作

Agent 最终要执行真实世界里的动作,所以权限必须小于用户自身,而不是等于用户。这一点经常被忽略。

本地开发场景里,可以给 Agent 配置独立的运行目录,让它只能访问指定的工作目录;需要联网时,走代理或单独的网络环境;涉及读写的接口,默认只给读权限,写权限按任务临时授权。云端任务则需要考虑服务账号的权限边界,不让 Agent 使用具有全局权限的密钥。

沙箱的目标不是挡住故意的恶意,而是挡住误操作和幻觉导致的无意破坏。模型可能在某次工具调用里把路径写错,或者在拼接命令时多了一层目录切换。没有权限边界,这种小错误直接变成生产事故;有了权限边界,最多只是任务失败,然后回到停止条件。

4.4 一个可照着走的最小落地工作流

如果你要在一个新场景里上 Agent,我建议按下面这套顺序推进。

  1. 定义任务边界。明确输入是什么、输出是什么、哪些动作属于范围外。范围外的事情直接拒绝,不要试图由 Agent 自主判断是否要做。
  2. 写三个最基础的校验器:输出不为空、结构满足要求、关键指标与输入不冲突。
  3. 设计停止条件清单。至少解决“没数据时怎么办”“校验失败时怎么办”“缺少权限时怎么办”。
  4. 跑一条最小任务。只测一个成功路径,观察 Agent 是否会按边界执行。
  5. 人为注入一个错误。故意给 Agent 一个包含异常数据的输入,看它是否停下来而不是硬编结果。
  6. 检查日志。确认 Agent 每次拒绝、暂停、请求确认都有记录,且记录里能找到出处。
  7. 逐步放开范围。每次放开一个权限或增加一个动作类型,都要跑一遍回归验证。

这套步骤不酷,但很有效。它的核心思路是把“最坏情况”前置:先用低风险任务测试高风险路径,再逐步扩大信任边界。

5. 怎么判断 Agent 真的“懂拒绝”

5.1 只看任务成功率远远不够

衡量 Agent 时最容易犯的错是只统计端到端成功率。比如一百个任务里有九十个完成了,就觉得系统很好。但一次错误执行带来的破坏可能超过十次正确执行积累的价值。

更合理的做法是同时统计生成的置信度、动作的危险度和结果的校验通过率。成功不是终点,校验通过才是。如果一个任务完成但校验失败,那它在工程意义上应该被记为失败。

5.2 一组更实用的验收指标

下面这些指标比“是否完成”更有参考价值。

指标计算方式关注点
动作拦截率被规则拦截的动作数 / 总动作数拦截规则是否真的在工作
停止准确率主动停止且理由合理次数 / 总停止次数Agent 是理性停止还是乱停
假阴性率输出质量问题但未被校验发现次数 / 总输出数校验器覆盖是否够
假阳性率正确输出被拦截次数 / 总输出数拒绝策略是否过于保守
人工确认后修改率用户修改 Agent 方案的次数 / 总确认次数确认点提供的上下文是否有效
回滚率需要回滚的操作次数 / 执行总次数执行动作是否被允许得过宽

我自己比较看重的是“人工确认后修改率”。如果这个比例很高,说明 Agent 提的方案跟用户预期差得远,确认点只是在走形式,并没有真正提升确认效率。理想状态是多数情况下用户只看一眼就能确认,少数情况下需要修改,最坏情况是用户发现 Agent 在反复提交明显不该执行的方案。

5.3 Agent 的行为日志比最终文档重要

很多人验收 Agent 项目时只看一份最终汇报,看完觉得很完整,但不知道系统在过程中做了什么。拒绝对 Agent 工程来说,行为是全部。

所以日志设计要有标准:每个动作都要记录触发理由、工具名称、输入摘要、输出摘要、结果状态;每次拒绝都要记录拒绝类型、停止原因、重试次数;每次确认都要记录确认人、确认时间和上下文快照。这样出问题时才能回答“为什么 Agent 会做这件事”,而不是靠猜。

一个值得坚持的做法是:让 Agent 每次任务都输出一份行为卡片,里面不是自然语言总结,而是结构化字段。至少包含任务 ID、已执行动作、被拒绝动作、校验结果、停止原因。这个卡片既是调试材料,也是审计证据,更是后续优化提示词和规则的依据。

6. 新手配置和生产配置应该差多少

6.1 学习阶段怎么配

如果你刚开始尝试 Agentic 工程,不需要一开始就上重规则。先保证三点。

环境上,使用独立工作目录,不直接操作真实业务代码或生产数据库;权限上,只授予读操作和轻量测试权限;流程上,强制每个高风险动作之前人工确认。

提示词里写清楚边界、失败场景和停止条件,但这个阶段校验器可以非常简陋,比如只要输出结构正确、非空、能打开即可。重点是让流程跑通,并体会“什么情况下 Agent 会错误地继续执行”。

学习阶段适合用默认策略跑简单样例。你可以故意设计几个坑:比如让 Agent 读取不存在的文件、修改只有只读权限的文件、处理含空值的表格。观察它是否停下来,还是直接生成一个合理的假结果。这类测试能快速暴露系统在拒绝能力上的缺陷。

6.2 生产阶段怎么配

一旦任务要进入生产环境,配置就需要升级。至少要考虑这些变化:

权限上,按任务最小授权,不共用长期密钥,敏感系统使用独立服务账号。校验上,验证器不仅检查结构,还要维护数据质量基线,例如字段枚举范围、数值区间、重复记录检测等。停止条件要配置成机器可读规则,不只是提示词。流程上,对高风险动作执行双人确认或策略审批。日志要集中存储并做审计,方便回溯。

生产阶段还应该做演练。平时跑任务时随机注入几类错误场景,确认 Agent 能稳定停在预期位置,不会绕过限制。这种演练频率不用太高,但每次改动规则或升级模型后都应该做一次。

6.3 配置差异速览

维度学习阶段生产阶段
运行环境本地独立目录隔离沙箱/预发布环境
权限读为主,写低频最小化授权,敏感操作单独审批
校验结构校验结构校验 + 数据质量基线 + 回归集
停止条件提示词说明提示词 + 编排层硬规则 + 超时终止
人工确认手动确认所有风险动作白名单自动化 + 异常转人工
日志本地文本日志结构化日志、审计追踪、告警
测试几条冒烟样例错误注入 + 回归测试 + 灰度放量

新手不要盲目套用生产配置,否则会被大量拦截规则淹没,连基本流程都调不通。生产也不要停留在新手阶段,否则 Agent 权限越大,事故越大。判断标准很简单:任务是否能长期反复跑、失败时能不能快速定位、被拒绝的动作能不能被人工接管。

7. 常见误区和排查链路

7.1 误区:让 Agent “注意安全”就等于让它会拒绝

提示词里写“注意安全”“不要做危险操作”,本质上还是在生成端想办法。模型看到这句话,确实会短暂倾向于更谨慎,但遇到复杂上下文、多轮对话和上下文过长时,这种倾向会衰减。真正可靠的拒绝需要外部规则、权限控制、校验结果和人工确认点来配合。提示词只能表达意向,工程才能形成约束。

7.2 误区:Agent 拒绝得越多就越好

拒绝策略本身也有成本。拒绝过多,正确任务无法推进,用户体验变差,人工确认点和审批流成了新的瓶颈。好的系统应该是分层结构:常规动作自动执行,超出预期的动作触发确认,明确禁止的动作直接拦截。拒绝的粒度要跟风险匹配,风险低的动作不需要每步都问。

如果发现拦截率过高,不要急着加规则,先看停止原因分布。很多时候问题出在任务描述太模糊、输入格式不完整,或者停止条件太宽泛。给 Agent 更明确的输入信息,往往比放宽拦截规则更有效。我在实际项目里经常发现,Agent 频繁“不知道该怎么办”的时候,提示词里其实没写清楚“什么算完成”。

7.3 排查顺序:从现象一路往上查

当 Agent 出现错误行为或异常停止时,按下面这个顺序排查,比到处改提示词更高效。

先看现象。Agent 还在跑还是已经停了,是卡在等待确认、报错退出还是返回了错误结果。这个阶段只记录,不做修改。

再看输入。核对任务描述、输入文件、环境变量和上下文里有没有脏数据、歧义表述、缺失字段。大量 Agent 行为异常都是输入问题。

三看规则。把停顿和拦截记录翻出来,核对停止条件是否误触发,权限范围是否过窄,以及确认点是不是消息格式不规范导致误判。

四看校验。校验器本身是否是可靠。如果校验器没有发现结果错误,比如没有检查输出目录是否生成文件,Agent 就会认为自己完成了。

五看生成。到了这一步才把注意力放到模型能力上,换模型、调温度、补 few-shot 示例。多数时候你会发现不需要这一步,问题在前面已经找到。

我反复遇到的情况是最后发现不是模型判断错误,而是校验器没写完整,导致 Agent 在缺少某个前置文件的情况下照常产出结果。一旦把“output 目录必须存在且文件非空”这类校验加进去,问题立刻消失。

7.4 一套可以留作自检的排错问题清单

如果任务表现异常,先问自己这几个问题:

  1. Agent 是否知道当前阶段属于哪个流程节点?如果上下文里没有明确的阶段标记,它很容易把中间状态误认为最终状态。
  2. 输入里是否存在多个意图或多种数据格式?Agent 往往只会按最常见的那一种理解,然后忽略其他情况。
  3. 失败路径是否真的被测试过?很多项目只测成功路径,失败场景完全没有覆盖。
  4. 是否有可以绕开确认的操作通道?比如某个工具函数支持批量删除,Agent 只要调用批量接口就能绕过单条确认,这是典型的权限漏洞。
  5. 日志里能看出 Agent 为什么选择某条路径吗?如果看不到,说明日志设计还有缺陷,先补日志再优化模型。

结尾

说回“Agentic engineering optimizes for rejecting output, not generating it”这句话。它真正想表达的不是让 Agent 不要干活,而是让 Agent 的每一次行动都经过“可验证”这道门槛。生成的输出只是候选,能通过校验、在权限边界内、符合停止条件的结果,才算真正完成。Agentic 工程成熟与否,要看它拦截了多少错误输出,而不是看它产生了多少文本和动作。

我个人的建议是:如果你想尝试这一类系统,先别急着追求全自动。从一条最小任务开始,把验证器、停止条件、权限边界各建一层,然后故意给系统喂一个错误场景,看它有没有停下来。如果它能清晰地说出自己的判断依据,并且停下来等待人工确认,那这个 Agent 才算具备进入生产的基本条件。如果它还在自信地生成一套漂亮的结果,那说明工程还差得远。踩过几次之后就会发现,稳定不是生成出来的,是拒绝出来的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询