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 能在"过度推理"和"草率给出答案"之间找到平衡。
更多推荐


所有评论(0)