Agentic系统目标设定与监控:让智能体不跑偏的基础设施
发布时间:2026/10/22 23:14:26 锦皓数字建站

写完前面几章 agentic 设计模式到了“目标设定与监控”这一章我反而觉得有必要先泼一盆冷水大部分团队做 agentic 系统一开始花的精力都在 prompt、工具调用、工作流编排上真正上线跑起来才发现最难的不是让智能体“会干活”而是让它“记得自己该干什么、干到哪了、有没有跑偏”。这一章讲的就是这两件事怎么把目标从一句含糊的自然语言变成一个系统能理解、能跟踪、能校验的结构化对象以及怎么在智能体自主执行的过程里持续感知它的状态、进度和偏差在它跑偏之前介入纠偏。适合正在做 agentic 应用的开发者、架构师也适合那些已经跑通 demo 但发现效果不稳定的团队参考——你们缺的往往不是更好的模型而是目标和监控这一层基础设施。1. 目标设定不是“写一段任务描述”那么简单1.1 目标设定在智能体架构里的真实位置很多人把 agentic 系统理解成一个“能自己调用工具的机器人”于是把所有精力都放在工具封装和模型提示词上。但如果你拆开看一个成熟的 agentic 系统它的核心其实是三层目标层、执行层、监控层。执行层负责干活——拆任务、调工具、生成结果目标层负责定义“活是什么”监控层负责回答“活干得怎么样”。执行层的自由度越高目标层和监控层就要越重否则系统就是一个没有方向感的执行器。目标设定之所以容易被低估是因为它看起来“太简单了”。用户说“帮我整理一份本周的竞品动态”你把它填到系统提示词里不就完事了实际上这只是把用户意图转发给了模型并没有把目标变成系统能够判断、校验、跟踪的东西。目标设定的本质是把自然语言意图“翻译”成一组可计算的状态和规则让目标和执行不在同一个层级上互相搅浑。我见过不少项目智能体跑着跑着突然去做某件跟主任务无关的事原因不是模型“变笨了”而是系统从头到尾就没有一个叫“目标边界”的东西。目标模糊后面所有规划、执行、评估都是建立在流沙上。1.2 三个最常见的目标定义错误第一个错误是目标太模糊。比如“提高报告质量”“更好地理解用户需求”这种说法模型拿到之后只能靠猜。什么叫“更好”什么标准算“提高”没有量化或至少没有可操作的判定标准目标就是不完整的。第二个错误是目标没有可验证的产出标准。目标本身可能是清楚的——“写一份关于某行业的市场分析报告”但什么叫“写完”需要包含哪些章节数据要更新到什么时候格式有没有要求是否有字数下限如果这些没有预先定义执行结果就不可评估“完成”就变成了一句空话。第三个错误是缺少目标边界。边界指的不是“最终要交付什么”而是“过程中不能做什么”。比如一个做数据分析的智能体你说“帮我找一下销量下滑的原因”它可能会自己去抓取外部舆情、自己做一些没有依据的推测、甚至自己去联系某个数据源——这些行为可能是有价值的也可能是越权的。没有边界的目标会让智能体在“自由发挥”和“越界操作”之间无法自检。1.3 好的目标长什么样一个结构化目标示例我们内部做一个“某市场调研助手”时最初的目标描述就是一句“帮我分析一下最近三个月的市场趋势”。后来重构之后目标变成了这样{ goal_id: mkt_trend_q3, intent: 生成最近三个月的市场趋势分析重点覆盖品类销量与竞品动态, scope: { time_range: 过去三个月, regions: [华东, 华南, 华北], categories: [智能家居, 便携音频], data_sources: [内部销售系统, 公开行业报告] }, outcome: { deliverables: [ { type: report, format: markdown, sections: [摘要, 品类趋势, 竞品动态, 数据附录] } ], min_quality: { data_freshness: 数据时间不超过7天, citation_required: true } }, constraints: { forbidden_actions: [访问收费数据源, 抓取社交媒体评论], budget_limit: 30 }, success_criteria: [所有结论均有数据来源, 覆盖全部指定品类, 报告字数不少于2000字] }这么一改好处立刻就能看出来目标定义层和执行层分离了规划器拿到的是一个结构化目标而不是一段需要自己二次解读的自然语言监控层也有了判断依据——比如某次操作触碰了constraints监控器就能主动报警执行完成后评估环节也有了明确的验收标准。设计结构化目标时有四个字段必填intent意图、deliverables产出物、constraints约束、success_criteria成功标准。其余的字段比如scope、priority、stakeholders根据业务场景自行扩展。核心原则是任何一个字段都要能被监控层“看懂”也就是说都要转化成可判定的规则而不是又一段给人看的描述。2. 目标状态显式建模让目标在整个任务生命周期里“活”起来2.1 为什么要给目标建一个状态机我见过不少 agentic 系统的实现方式把目标当成了一个“只读参数”启动的时候塞给智能体然后就再也不碰它了。问题是一旦任务执行超过几分钟中间经过规划、执行、暂停、补充信息、重新规划这些步骤之后系统的“当前目标状态”就只剩模型自己还记得了。模型上下文窗口有限一旦被中间结果冲掉很多系统就直接从“执行任务”滑向“随机输出”。给目标建立显式状态机本质上是把目标的“当前状态”变成系统的第一等公民任何模块都可以随时读取和修改它而不是依赖模型“记得”。状态机的价值有三个可观察、可审计、可恢复。出问题的时候你能回答“这个目标现在处于什么阶段”“它经历过哪些状态”“它是在哪个节点开始偏离的”。这三个问题的答案比任何一帧对话日志都更有诊断价值。2.2 一套可落地的目标状态设计我们跑过几个项目之后沉淀出一套比较稳的目标状态集合不一定适合所有场景但可以作为起点状态含义进入条件可流转目标状态pending目标已创建尚未开始执行目标创建成功active, abandonedactive正在执行中规划器产出了首个执行计划paused, diverged, succeeded, failedpaused暂停执行等待外部输入用户中断、缺少关键数据、成本超限active, abandoneddiverged检测到行为或结果偏离目标监控层判定偏离超阈值active, failed, pending(重新规划)succeeded目标验收通过所有 success_criteria 校验通过终态failed目标确认失败不可恢复错误或重复失败终态abandoned目标被人工取消用户主动放弃终态几个容易踩坑的点diverged和failed一定不要合并。跑偏往往不是一次性确认的可能是“疑似跑偏 → 部分回退 → 继续执行”这样一个反复的过程。把跑偏合并进失败会导致一偏离就终止系统会变得非常脆弱。paused不是失败。暂停常常是因为缺少外部输入比如用户没有回复、某个数据接口不可用。把这类状态归类为失败会让监控系统误报。状态流转一定要记录原因和触发者trigger不能只记录“状态从A变成了B”。没有原因的流转日志在复盘时几乎没有价值。2.3 状态流转的触发点由谁来控制目标状态不是自己变的它是被三类角色触发的执行器、监控器、用户。执行器在开始干活、完成某个产物时会触发流转监控器在做偏差判定时会触发流转用户在界面上点了“暂停”“取消”也会触发流转。这三类触发者的优先级要明确用户 监控器 执行器。否则会出现“执行器刚把目标标成 succeeded监控器后脚就发现产出不达标”的矛盾局面。可以把状态流转的方法收敛成一个统一的入口所有模块都走这一个口而不是各写各的def transition(goal_id, new_state, trigger, reason): goal store.get(goal_id) if new_state not in state_machine[goal.state]: raise InvalidTransition(goal.state, new_state) goal.state new_state goal.history.append({ from: goal.state, to: new_state, trigger: trigger, reason: reason, ts: now() }) store.save(goal)3. 目标的生命周期从主目标到子目标树再回到主目标3.1 目标分解的三个层次一个完整的 agentic 任务目标不会只有一个。它通常是一个树状结构根节点是主目标中间节点是子目标叶子节点是具体执行任务task。我用“目标”和“任务”两个词刻意做了区分目标是“要达成的结果”任务是“要做的事情”。比如“生成一份竞品分析报告”是目标“调用搜索接口收集竞品A的公开定价信息”是任务。目标分解的原则有三个每个子目标必须能映射到主目标的一个或多个success_criteria。如果某个子目标跟任何验收标准都对不上它大概率是多余的。子目标之间要尽量正交也就是一个子目标的执行结果不依赖另一个子目标的中间产物否则会产生隐性耦合排查问题时会很痛苦。叶子任务必须可执行。也就是说一个任务对应一个明确的操作调某个API、查某张表、生成某段文本而不是“进一步分析市场”。3.2 子目标失败如何向父目标传导这个传导机制是我见过做得最差的一部分。很多系统的做法是子目标失败了父目标也标记失败任务结束。这其实把层级关系做成了简单的“与逻辑”。现实中子目标失败通常有三种情况处理方式完全不一样子目标失败情况父目标应做的反应子目标的验收标准无法满足但不影响主目标记录失败父目标继续总目标视为“部分完成”子目标失败但存在替代方案触发 replan补充一个替代子目标子目标失败且是主目标的核心依赖父目标转入 diverged由监控器决定是否升级为 failed看这个表就明白了子目标失败的传导不是一道“失败冒泡”的问题而是一个决策逻辑。我建议在实现里给子目标加一个字段叫criticality取值是critical或optional。critical的子目标失败了父目标才需要进入偏离状态optional的子目标失败了记录日志继续跑就行。这个字段虽然简单却能把层级目标之间的传导逻辑讲清楚避免“一处失败全盘推翻”。3.3 目标完成并不等于执行结束目标生命周期的最后一段很多人会忽略目标进入succeeded之后还需要做一次“完成核验”。核验跟执行不是一回事执行是“造出产出物”核验是“检查产出物是否满足success_criteria”。最好由独立的评估器来做而不是让执行智能体自己给自己打分。就像考试不能让学生自己判卷一样。核验方式通常有两种基于规则的核验和基于模型的核验。规则核验用来检查硬性条件比如“是否包含所有必填章节”“数据是否在 7 天内”“字数是否达标”模型核验用来检查软性条件比如“结论是否都有数据支撑”“报告逻辑是否连贯”。两者结合完成核验的准确率会明显高于单用其中一种。4. 监控的本质不是看日志而是看“偏差”4.1 监控的双重职能状态感知与偏差发现监控这个词在传统后端系统里很常见但 agentic 系统的监控跟普通后端监控有本质区别。普通后端监控关心的是“服务还活着吗、响应快不快、错误率高不高”agentic 系统监控关心的是“智能体还在往正确的方向走吗、执行的每一步是否在目标边界内、有没有产生不可预期的副作用”。所以 agentic 监控要做两层事情。第一层是状态感知把智能体的执行过程记录下来让各个模块能实时看到“现在进行到哪一步了”。第二层才是偏差发现在一堆状态感知数据里识别出“偏离目标”的信号。状态感知是地基偏差发现是建立在它上面的判断逻辑。很多系统直接跳过第一层去做第二层结果就是凭空判断没有数据和事件支撑偏差判定只能靠猜。4.2 三层监控透镜我们把监控的数据来源分成三层这也是内部项目实践之后沉淀下来的视角输入层监控用户或上游系统传入的信息有没有变化。最常见的场景是用户中途补充了一句“哦对了时间范围改成最近半年”如果目标还是按“最近三个月”跑那所有中间产物都会和目标脱节。输入层的信号源是用户消息、对话上下文。推理层监控智能体在中间过程产出的推理、规划、工具调用决策。比如它规划出的子目标列表是否包含主目标的success_criteria它是否触发了constraints里禁止的动作它是否对某个数据做出了无法溯源的假设。推理层是最难监控的因为中间推理往往是自然语言不好做自动化判定。我们目前的经验是至少在工具调用层面做强校验也就是对“智能体要调哪个工具、传什么参数”做白名单规则而中间思考过程则做采样式的抽查。输出层监控最终和阶段性产物。产物是否按预期格式输出、是否满足硬性校验、是否包含幻觉内容。输出层的信号源是各阶段的 deliverables以及验收器的校验结果。4.3 监控什么、不监控什么刚开始做监控的时候我们犯过一个错误想把智能体的每一步都记录下来包括每一步的思考、每一个中间变量。结果数据量爆炸真正有用的信号反而淹没在里面。后来收敛出一张核心监控指标清单这里分享给大家做参考指标类型异常信号当前目标状态状态长时间停留在 active超过预估时长无流转子目标完成率进度执行时间过半完成率低于 30%工具调用次数/频率成本短时间内高频调用同一工具可能陷入循环无效动作率质量连续 N 步没有产生任何中间产物或状态变化约束命中次数合规任何一次命中 constraints 都需告警产物校验通过率质量阶段性产物校验失败率超过阈值目标变化次数稳定性用户在短时间内反复修改目标监控不是越多越好每个指标都要回答一个问题它能不能帮我发现一次“值得介入的偏离”如果不能就别接进来。5. 偏离检测与纠偏机制跑偏不只有一种跑法5.1 什么算“跑偏”偏离检测的第一步是定义清楚“偏离”有哪些形态。根据我们线上观察到的案例跑偏至少有三种行为偏离智能体做出了目标边界之外的动作。比如目标明确禁止抓取社交媒体它还是去调了相关接口或者它开始反复尝试同一个已经失败的方案陷入了“行动循环”。结果偏离智能体产出的东西和success_criteria对不上。最常见的是“报告写完了但缺少必填章节”或“结论和数据不一致”。目标漂移执行过程中智能体自己“修改”了目标。这个最隐蔽比如原目标是“分析销量下滑原因”执行到一半它开始专注于“优化数据采集流程”这就是典型的在局部问题上过度深入忘记了自己在回答什么。针对目标漂移有一个比较有效的做法在系统提示词和目标上下文里定期注入“当前主目标”和“当前完成进度”让智能体每执行几步就重新对焦一次。这不是临时补救而是一个接近“强制性”的设计——把目标回显放在上下文窗口的醒目位置让模型每次决策时都能看到它。5.2 纠偏的三种策略检测到偏离之后不是所有情况都要终止任务也不是所有情况都重来一遍。我们内部把纠偏策略分成三级按干预力度从小到大排序策略触发条件操作内容干预力度重规划replan偏离不严重主目标仍可达重新规划剩余子目标保留已完成部分低局部回退rollback偏离影响已产出的部分回退到最后一个通过验收的阶段从那里重新执行中升级到人escalate偏离严重或自动纠偏连续失败暂停任务把目标状态、偏离报告、已有产物推送给用户高关键在于触发条件必须写成可判定的规则不能靠“感觉偏离了”就随意触发。比如“replan”的触发条件定义为“已完成子目标占比 ≥ 50% 且偏离类型为行为偏离”“escalate”的触发条件是“自动纠偏尝试超过 2 次仍失败”或“偏离直接导致某个 critical 子目标无法满足”。5.3 阈值怎么设为什么不能用成功/失败二值判断很多初版监控系统的问题是对每个指标只设了一个“成功/失败”的线比如“工具调用次数超过 10 次就算失败”。但实际执行中同样调用 10 次工具可能是正常的探索也可能是死循环。所以我们把阈值设计成了分段式的并且配合时间窗口。举个例子工具调用频率的监控如果 1 分钟内调用超过 20 次触发 warning如果 5 分钟内调用超过 50 次触发 replan如果触发 replan 后仍然出现同样频率则 escalate。这里的精髓是“连续窗口 逐级升级”而不是一上来就拉响最高级别警报。阈值参数建议上线初期调宽松一点观察一周真实数据后再收紧否则你会发现系统频繁误报把正常执行都打断了。6. 一个可复用的目标-监控实现骨架6.1 最小实现目标存储与状态流转下面这个简化实现可以直接嵌入到已有的 agentic 系统里不需要改动原有的执行逻辑只需要在关键节点插入状态更新和事件上报。# goal_manager.py from dataclasses import dataclass, field from enum import Enum from datetime import datetime import json class GoalState(str, Enum): PENDING pending ACTIVE active PAUSED paused DIVERGED diverged SUCCEEDED succeeded FAILED failed ABANDONED abandoned STATE_MACHINE { GoalState.PENDING: {GoalState.ACTIVE, GoalState.ABANDONED}, GoalState.ACTIVE: {GoalState.PAUSED, GoalState.DIVERGED, GoalState.SUCCEEDED, GoalState.FAILED}, GoalState.PAUSED: {GoalState.ACTIVE, GoalState.ABANDONED}, GoalState.DIVERGED: {GoalState.ACTIVE, GoalState.FAILED, GoalState.PENDING}, GoalState.SUCCEEDED: set(), GoalState.FAILED: set(), GoalState.ABANDONED: set(), } dataclass class Goal: goal_id: str intent: str scope: dict outcomes: dict constraints: dict success_criteria: list state: GoalState GoalState.PENDING history: list field(default_factorylist) def transition(self, new_state, trigger, reason): if new_state not in STATE_MACHINE[self.state]: raise ValueError(fInvalid transition {self.state} - {new_state}) self.state new_state self.history.append({ to: new_state.value, trigger: trigger, reason: reason, ts: datetime.utcnow().isoformat() })这样一个类就能满足三件事把目标描述结构化存储、用状态机约束流转合法性、记录完整的历史事件供复盘。我建议在目标创建的入口就调用它而不是在执行中才临时造一个对象。6.2 监控事件结构统一的上报格式监控层要接收各方上报的事件事件结构如果不统一后面的判定逻辑会很混乱。我们统一用下面这个格式{ event_id: evt_00123, ts: 2025-01-15T10:22:31Z, goal_id: mkt_trend_q3, type: tool_call.start, payload: { tool_name: fetch_sales_data, params: { start: 2024-10-01, end: 2025-01-01 } }, context: { step_id: step_42, parent_goal_id: sub_goal_07 } }type字段建议统一枚举goal.created、goal.transition、plan.generated、tool_call.start、tool_call.finish、artifact.ready、validation.pass、validation.fail、divergence.detected。事件上报之后监控器按照goal_id聚合再做指标计算和阈值判定。这样做的好处是监控逻辑跟智能体的执行逻辑完全解耦你可以在不改执行代码的情况下随时加一个新的监控规则。6.3 接入现有系统的最短路径如果已经有一套跑起来的 agentic 系统想尽快把目标-监控补上建议按下面这几步走先定义目标结构把用户输入改造成结构化目标对象至少包含intent、deliverables、constraints、success_criteria。在系统提示词和上下文里注入目标的当前状态确保模型每次决策时能看到“主目标是什么、完成到什么程度”。在关键节点上报监控事件优先覆盖“目标创建”“每个 step 完成”“每次工具调用”“每次产物校验”。先只配置状态感知和基础指标不要一上来就接自动纠偏跑通 1~2 天。观察指标分布再逐步加偏离检测和 replan 逻辑。这套最短路径的核心思路是“先可观测再可控制”。跳过可观测直接做自动纠偏就是在不知道发生了什么的情况下瞎操作线上环境会很危险。7. 实践中的几个坑以及我们最后留下的经验7.1 三个让我印象深刻的翻车案例第一个坑把监控做成了“一次性快照”。项目初期我们把监控逻辑写在了一个单独的接口里只在任务结束后调用一次。结果任务执行到一半出问题除了最终产物中间过程完全不可见排查成本极高。后来改成事件流处理每个阶段实时上报才能做到“任务还在跑监控已经看到它跑偏了”。第二个坑目标状态和执行记录分家。有段时间我们同时维护了目标状态库和对话日志库但两者没有关联复盘时想找“这个目标在什么时候第一次出现偏离”要同时查两套系统拼时间线效率极低。后来把goal_id和step_id作为公共字段贯穿所有事件和日志这条时间线才变得可查可复现。第三个坑对模糊目标做硬编码校验。有一个子目标当初写的是“尽量覆盖所有重要竞品”我们在监控层把“所有重要竞品”硬编码成了一个固定列表结果每次有新的竞品出现校验就误报失败。正确的做法是把模糊词换成可配置的规则或让校验逻辑定期从数据源同步更新列表而不是写死。7.2 一个低成本但回报很高的小技巧最后分享一个小技巧把目标的当前状态回写到系统提示词里。我们做法是在执行器每次决策前拼一段“当前目标快照”放进上下文的固定位置格式大致是【当前目标】 goal_id: mkt_trend_q3 intent: 生成最近三个月的市场趋势分析重点覆盖品类销量与竞品动态 当前进度: 已完成 2/5 个子目标 最近完成产物: report.md 初步框架 最近偏离情况: 无 剩余约束: 禁止抓取社交媒体评论这段快照看起来只是“多打了几行字”实际效果非常明显。它给模型提供了一个持续的锚点让它在长任务中反复回来对照自己的进度而不是凭上下文窗口里残留的印象往下走。我们对比过加入快照之后长任务的目标漂移问题下降了至少一半。7.3 目标和监控是一体的不是两个模块如果你在建 agentic 系统我把这一章最核心的一句话放在这里目标设定和监控不要当成两个独立模块去做它们在数据层面必须共享同一套对象和事件流。目标定义得再漂亮如果监控层读不到这些定义它就是死的监控做得再精密如果目标本身是模糊的它就是无源之水。现在回看这一章你会发现我们讨论的本质上不是“怎么写 prompt”也不是“怎么调模型”而是怎么给一个高度自主的执行系统装上“方向盘和仪表盘”。这层基础设施决定了智能体是“看起来聪明但不可控”还是“真的能在生产环境里稳定交付”。这套思路在我们自己的系统里经过了多次重构才逐渐稳定下来希望对正在做同类系统的你有一些参考价值。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。