news 2026/7/22 0:00:59

Agent 终态判定:何时该停止思考、给出最终回复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复

一、你的 Agent 在"再想想"的循环里绕了 12 轮,用户已经关窗口了

Agent 与人最大的区别是:人知道什么时候该停下来给答案,Agent 会一直"想"下去。你给 Agent 接了搜索引擎 API,它第一轮搜了一下,觉得信息不够全,第二轮换了关键词再搜,第三轮觉得 "还有 3 篇相关文章没读",第四轮读了之后发现"这篇文章引用了另一篇——我再搜一下"……十二轮后用户早就不在了。

Agent 的终止判定是一个比想象中更复杂的问题。不是因为怎么停止很难,而是"什么情况下应该停止"需要几个维度的判断:信息完备度够不够、用户是否有明确的时间预期、本轮回答的质量是否已经"够好"。

当前大多数 Agent 框架的终止逻辑极其简单。要么是固定步数限制(最多调用 5 步就停),要么是 LLM 自己说了算(它觉得该停了就停了)。固定步数太硬——复杂任务 5 步不够,简单任务 1 步就够了。LLM 自己判断太软——它可能永远不觉得"信息够了"。

二、底层机制与原理剖析

Agent 终态判定的多维度决策模型:

四个判定维度:

信息增益(Information Gain):每一步工具调用都会带回新的信息。衡量这一步带来的信息增量——如果新信息与已收集信息的语义重复度超过 95%,说明再搜下去也不会获得新信息了(边际收益递减)。计算方法:新信息的嵌入向量 vs 已收集信息的嵌入向量之间的余弦相似度。

步数预算:不是固定上限(如 5 步),而是动态预算。简单问题(如"今天天气怎么样")1-2 步足够,复杂问题(如"分析 A 公司财报并做竞品对比")给 8-10 步。LLM 在初始阶段评估任务复杂度,生成预算值。

置信度阈值:每一步后让 LLM 自评当前答案的置信度(0-100%)。超过阈值(如 85%)就输出答案;未超过就继续。这比固定步数更弹性——有时 1 步就能高置信度回答,有时 8 步才能。

用户显式信号:最高优先级的停止信号。用户的任何"结束意图"的表达("可以了""够了""先这样吧""明白了")都应该立即停止推理并输出最终回答。

三、生产级代码实现

""" Agent 终态判定器 四个维度:信息增益、步数预算、置信度阈值、用户显式信号 """ from dataclasses import dataclass, field from typing import List, Dict, Optional, Tuple import numpy as np from enum import Enum class StopReason(Enum): INFORMATION_SATURATED = "info_saturated" # 信息饱和 BUDGET_EXCEEDED = "budget_exceeded" # 步数预算耗尽 CONFIDENCE_REACHED = "confidence_reached" # 置信度达标 USER_SIGNAL = "user_signal" # 用户显式终止 MAX_STEPS_FORCED = "max_steps_forced" # 硬上限强制终止 @dataclass class StopDecision: """终态判定结果""" should_stop: bool reason: StopReason confidence: float # 当前置信度 information_gain: float # 最后一步的信息增益 steps_used: int # 已用步数 steps_budget: int # 总步数预算 detail: str # 人类可读的决策说明 @dataclass class AgentContext: """Agent 当前步骤的上下文""" steps_taken: int steps_budget: int collected_info: List[str] # 已收集的信息片段 last_action_result: str # 最后一步动作的结果 current_confidence: float # 当前置信度 (0-100) user_last_message: str # 用户最后一条消息 class TerminationJudge: """Agent 终态判定器""" def __init__( self, embed_fn, confidence_threshold: float = 85.0, information_gain_threshold: float = 0.05, max_steps_fallback: int = 15, # 绝对硬上限 ): self.embed_fn = embed_fn self.confidence_threshold = confidence_threshold self.information_gain_threshold = information_gain_threshold self.max_steps_fallback = max_steps_fallback # 用户终止信号词 self.stop_signals = [ "可以了", "够了", "先这样", "明白了", "清楚了", "不用了", "够了谢谢", "就这样吧", "好的谢谢", "ok", "got it", "that's enough", "stop", ] def judge(self, context: AgentContext) -> StopDecision: """ 判定是否应该停止 判定优先级:用户信号 > 信息饱和 > 置信度达标 > 步数预算 > 硬上限 """ # === 优先级 1: 用户显式信号 === for signal in self.stop_signals: if signal in context.user_last_message.lower(): return StopDecision( should_stop=True, reason=StopReason.USER_SIGNAL, confidence=context.current_confidence, information_gain=0, steps_used=context.steps_taken, steps_budget=context.steps_budget, detail=f"用户发出了终止信号: '{signal}'", ) # === 优先级 2: 信息饱和(最后一步的信息增益太低)=== info_gain = self._calculate_information_gain( context.last_action_result, context.collected_info, ) if context.steps_taken >= 2 and info_gain < self.information_gain_threshold: return StopDecision( should_stop=True, reason=StopReason.INFORMATION_SATURATED, confidence=context.current_confidence, information_gain=info_gain, steps_used=context.steps_taken, steps_budget=context.steps_budget, detail=f"信息增益 {info_gain:.3f} 低于阈值 {self.information_gain_threshold},继续搜索收益递减", ) # === 优先级 3: 置信度达标 === if context.current_confidence >= self.confidence_threshold: return StopDecision( should_stop=True, reason=StopReason.CONFIDENCE_REACHED, confidence=context.current_confidence, information_gain=info_gain, steps_used=context.steps_taken, steps_budget=context.steps_budget, detail=f"置信度 {context.current_confidence}% >= 阈值 {self.confidence_threshold}%", ) # === 优先级 4: 步数预算耗尽 === if context.steps_taken >= context.steps_budget: return StopDecision( should_stop=True, reason=StopReason.BUDGET_EXCEEDED, confidence=context.current_confidence, information_gain=info_gain, steps_used=context.steps_taken, steps_budget=context.steps_budget, detail=f"已用 {context.steps_taken}/{context.steps_budget} 步,预算耗尽", ) # === 优先级 5: 绝对硬上限 === if context.steps_taken >= self.max_steps_fallback: return StopDecision( should_stop=True, reason=StopReason.MAX_STEPS_FORCED, confidence=context.current_confidence, information_gain=info_gain, steps_used=context.steps_taken, steps_budget=self.max_steps_fallback, detail=f"达到绝对上限 {self.max_steps_fallback} 步,强制终止", ) # === 继续 === return StopDecision( should_stop=False, reason=StopReason.INFORMATION_SATURATED, # 实际未触发 confidence=context.current_confidence, information_gain=info_gain, steps_used=context.steps_taken, steps_budget=context.steps_budget, detail="继续下一步推理", ) def _calculate_information_gain( self, new_info: str, existing_info: List[str] ) -> float: """ 计算新信息相对于已有信息的信息增益 方法:计算新信息向量与已有信息向量的最大余弦相似度。 1 - 最大相似度 = 信息增益。 新信息与已有信息越相似,增益越低。 """ if not existing_info or not new_info: return 1.0 # 没有旧信息,新信息的增益是 100% try: new_embed = self.embed_fn(new_info) # 计算与每条已有信息的相似度,取最大值 max_similarity = 0.0 for info in existing_info[-5:]: # 只比较最近 5 条,降低计算量 try: info_embed = self.embed_fn(info) sim = self._cosine_sim(new_embed, info_embed) max_similarity = max(max_similarity, sim) except Exception: continue # 增益 = 1 - 最大相似度 gain = 1.0 - max_similarity return max(0.0, gain) except Exception: return 0.5 # 计算失败时返回中性值 @staticmethod def _cosine_sim(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-8) def estimate_budget(self, user_query: str) -> int: """ 根据用户问题复杂度估算步数预算 简单启发式(生产环境应用 LLM 估计复杂度): - 短问题(< 20 字):2-3 步 - 中等问题(20-50 字):4-6 步 - 长问题(> 50 字):7-10 步 """ length = len(user_query) if length < 20: return 3 elif length < 50: return 6 elif length < 100: return 8 else: return 10 def build_stop_message(self, decision: StopDecision) -> str: """根据终止原因构建给用户的说明""" if decision.reason == StopReason.BUDGET_EXCEEDED: return ( f"(已进行 {decision.steps_used} 步分析," f"当前置信度:{decision.confidence:.0f}%。" f"如需更深入的分析,可以提出更具体的问题。)" ) elif decision.reason == StopReason.CONFIDENCE_REACHED: return "" elif decision.reason == StopReason.INFORMATION_SATURATED: return ( f"(搜索到的信息开始重复," f"当前置信度:{decision.confidence:.0f}%。" f"如需更多信息源,可以指定方向。)" ) else: return ""

四、边界分析与架构权衡

信息增益的准确性

用 embedding 向量计算语义相似度作为信息增益的代理变量,会产生误判。两段文字在语义上相似但包含了关键的差异信息(如两个不同城市的人口数据——结构相同但数据不同)——语义相似度高但信息增益应该高。这是当前方法的盲区。

置信度评分的可靠性

让 LLM 自己评估自己的置信度,存在"过度自信"或"不自信"的偏差。模型的置信度评估本身就是不可靠的——它可能对错误答案给出 90% 的置信度。可以用 Self-Consistency 方法(让模型多次回答同一问题,统计答案的一致程度)来增加置信度评分的可信度。

适用边界

最适合多步推理的 Agent(如研究助手、数据分析 Agent、代码调试 Agent)。任务步数在 3-15 步之间、需要多轮信息检索或分析的场景。

禁用场景

不适合单轮问答的 Agent——不需要多步推理的场景不存在终止判定的问题。也不适合实时对话——用户应该在任意时刻都能打断 Agent。

五、总结

Agent 终态判定不是简单的最大步数限制。四个维度的综合决策——用户显式信号(优先级最高)、信息增益(边际收益判断)、置信度阈值(答案质量判断)、步数预算(资源边界)。当任一维度触发时,Agent 应在回答中附上置信度和停止理由,让用户知道"这个回答是经过几轮推理得出的,可信度如何"。关键是让 Agent 能在"过度推理"和"草率给出答案"之间找到平衡。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/21 23:56:01

Spring Boot3整合MyBatis-Plus实战避坑指南

1. Spring Boot3与MyBatis-Plus整合概述在Java企业级开发领域&#xff0c;Spring Boot3作为最新一代的微服务框架&#xff0c;与MyBatis-Plus这一强大的ORM工具的结合&#xff0c;已经成为现代Java后端开发的黄金组合。这套技术栈能够显著提升开发效率&#xff0c;但在实际整合…

作者头像 李华
网站建设 2026/7/21 23:51:49

深入解析TI C2000 eCAP模块:从捕获到APWM的嵌入式时序控制

1. eCAP模块&#xff1a;嵌入式时序控制的瑞士军刀在电机控制、数字电源或者任何需要与外部世界进行精确“对话”的嵌入式系统中&#xff0c;时间就是一切。你是否曾为测量一个高速编码器的脉冲间隔而绞尽脑汁&#xff1f;或者为生成多路严格同步且相位可调的PWM波而调试到深夜…

作者头像 李华
网站建设 2026/7/21 23:46:51

DNF美服狄瑞吉困难模式机制与攻略详解

1. 狄瑞吉困难模式更新解析&#xff1a;DNF美服6月9日版本核心内容 作为一名从60版本玩到现在的老玩家&#xff0c;这次美服更新的狄瑞吉困难模式&#xff08;Diregie Hard Mode&#xff09;确实带来了不少值得研究的机制变化。相比普通模式&#xff0c;困难模式不仅提升了怪物…

作者头像 李华
网站建设 2026/7/21 23:45:55

分布式系统中的配置中心架构:推送、拉取与长轮询的工程选择

分布式系统中的配置中心架构&#xff1a;推送、拉取与长轮询的工程选择 一、配置变更的"最后一公里"&#xff1a;从手工改文件到毫秒级热更新 绝大多数系统故障源于配置变更。这件事在单体时代还算可控&#xff0c;一个配置文件、一次重启。到了微服务时代&#xff0…

作者头像 李华