Agent 犯了错还继续错?反思机制的设计与工程实现

cover

一、Agent 的"一根筋"问题:没有纠错的自主决策是危险的

当前大多数 Agent 系统采用"感知-规划-执行"的直线流程:接收用户输入,调用 LLM 生成行动计划,按计划执行工具调用,返回结果。这个流程在简单任务上表现良好,但在复杂多步任务中,一旦中间步骤出错,后续所有步骤都会基于错误前提继续推进,错误被逐级放大。

典型场景:Agent 被要求"查询 Q3 财报并计算毛利率"。第一步,Agent 调用搜索工具,但关键词不够精确,召回了 Q2 的财报数据;第二步,基于 Q2 数据计算毛利率;第三步,将结果返回给用户。整个过程没有任何环节质疑"这个数据是 Q3 的吗?"。Agent 不是不知道 Q3 和 Q2 的区别,而是缺乏"回头看"的机制。

反思机制(Reflection)的核心思想是:在 Agent 执行链路中插入"自我评估"环节,让 Agent 在关键节点检查当前状态是否偏离目标,如果偏离则修正计划。这不是让模型"更聪明",而是给 Agent 加一个"刹车"——在错误积累到不可挽回之前停下来纠偏。

二、反思循环的架构设计:从执行-评估到执行-评估-修正

反思机制不是简单地在执行后加一步"检查结果",而是一个结构化的循环:执行、评估、修正、再执行。理解这个循环的控制流,才能设计出可靠的反思系统。

graph TD
    INPUT[用户输入] --> PLAN[规划: 生成行动计划]
    PLAN --> EXEC[执行: 调用工具/生成中间结果]
    EXEC --> EVAL[评估: 检查执行结果是否满足约束]

    EVAL --> |通过| NEXT[进入下一步]
    EVAL --> |未通过| REFLECT[反思: 分析失败原因]

    REFLECT --> |可修正| REVISE[修正: 调整计划/参数]
    REVISE --> EXEC

    REFLECT --> |不可修正| ESCALATE[升级: 请求人工介入<br/>或告知用户限制]

    NEXT --> |还有步骤| PLAN
    NEXT --> |全部完成| OUTPUT[输出最终结果]

    style EVAL fill:#fff3e0
    style REFLECT fill:#e1f5fe
    style ESCALATE fill:#ffcdd2

评估(Evaluation):判断当前步骤的执行结果是否满足预期约束。评估标准需要明确——不是模糊的"好不好",而是可量化的检查项。例如:数据时效性(是否是 Q3 数据)、格式正确性(是否是数值)、逻辑一致性(毛利率是否在合理范围内)。

反思(Reflection):当评估未通过时,分析失败原因并生成修正方案。反思的关键是"归因"——准确识别错误发生在哪个环节。是搜索关键词不够精确?是工具返回了错误数据?还是计算逻辑有误?不同的归因对应不同的修正策略。

修正(Revision):基于反思结论调整行动计划。可能是修改工具调用参数、换一个工具、调整中间步骤的顺序,或者放弃当前路径选择替代方案。

升级(Escalation):当反思次数超过阈值仍无法修正时,不再盲目重试,而是升级处理——请求人工介入或向用户说明限制。这是防止无限循环的安全阀。

三、反思机制的生产级实现

3.1 核心数据结构

from dataclasses import dataclass, field
from typing import Optional
from enum import Enum
import time

class StepStatus(Enum):
    PENDING = "pending"
    EXECUTING = "executing"
    EVALUATING = "evaluating"
    REFLECTING = "reflecting"
    REVISING = "revising"
    COMPLETED = "completed"
    FAILED = "failed"
    ESCALATED = "escalated"

@dataclass
class ExecutionStep:
    """Agent 执行步骤"""
    step_id: str
    description: str              # 步骤描述
    tool_name: str = ""           # 调用的工具名称
    tool_input: dict = field(default_factory=dict)  # 工具输入参数
    tool_output: str = ""         # 工具输出
    status: StepStatus = StepStatus.PENDING
    reflection_count: int = 0     # 本步骤已反思次数

@dataclass
class EvaluationResult:
    """评估结果"""
    passed: bool
    checks: list[dict]            # 各检查项的结果
    failure_reason: str = ""      # 失败原因(如果未通过)

@dataclass
class ReflectionResult:
    """反思结果"""
    root_cause: str               # 根因分析
    revision_type: str            # 修正类型: reparam / retool / replan
    revised_input: dict = field(default_factory=dict)  # 修正后的参数
    confidence: float = 0.0       # 修正的信心度 [0, 1]

3.2 反思循环引擎

class ReflectiveAgent:
    """带反思机制的 Agent 引擎"""

    def __init__(
        self,
        llm_client,
        tool_registry: dict,
        max_reflections: int = 3,      # 每步最大反思次数
        max_total_reflections: int = 10, # 全局最大反思次数
    ):
        self.llm = llm_client
        self.tools = tool_registry
        self.max_reflections = max_reflections
        self.max_total_reflections = max_total_reflections
        self.total_reflections = 0

    def run(self, user_input: str) -> str:
        """执行带反思的 Agent 循环"""
        # 1. 生成初始计划
        plan = self._generate_plan(user_input)
        steps = [ExecutionStep(step_id=f"s{i}", description=desc)
                 for i, desc in enumerate(plan)]

        # 2. 逐步执行,每步带反思
        for i, step in enumerate(steps):
            step.status = StepStatus.EXECUTING

            # 执行工具调用
            step = self._execute_step(step)

            # 评估执行结果
            step.status = StepStatus.EVALUATING
            eval_result = self._evaluate_step(step, user_input)

            if eval_result.passed:
                step.status = StepStatus.COMPLETED
                continue

            # 评估未通过,进入反思循环
            step = self._reflection_loop(step, eval_result, user_input)

            if step.status == StepStatus.ESCALATED:
                return self._format_escalation(step, user_input)

        # 3. 所有步骤完成,生成最终答案
        return self._generate_final_answer(steps, user_input)

    def _reflection_loop(
        self,
        step: ExecutionStep,
        eval_result: EvaluationResult,
        user_input: str,
    ) -> ExecutionStep:
        """反思循环:评估 → 反思 → 修正 → 重新执行"""
        while step.reflection_count < self.max_reflections:
            if self.total_reflections >= self.max_total_reflections:
                step.status = StepStatus.ESCALATED
                return step

            # 反思:分析失败原因
            step.status = StepStatus.REFLECTING
            reflection = self._reflect(step, eval_result, user_input)
            step.reflection_count += 1
            self.total_reflections += 1

            if reflection.confidence < 0.3:
                # 信心度过低,继续反思无意义,升级处理
                step.status = StepStatus.ESCALATED
                return step

            # 修正:调整参数或工具
            step.status = StepStatus.REVISING
            step = self._apply_revision(step, reflection)

            # 重新执行
            step.status = StepStatus.EXECUTING
            step = self._execute_step(step)

            # 重新评估
            step.status = StepStatus.EVALUATING
            eval_result = self._evaluate_step(step, user_input)

            if eval_result.passed:
                step.status = StepStatus.COMPLETED
                return step

        # 反思次数耗尽,升级处理
        step.status = StepStatus.ESCALATED
        return step

    def _evaluate_step(self, step: ExecutionStep, user_input: str) -> EvaluationResult:
        """评估步骤执行结果"""
        eval_prompt = f"""
请评估以下步骤的执行结果是否满足要求。

用户目标:{user_input}
步骤描述:{step.description}
工具名称:{step.tool_name}
执行结果:{step.tool_output}

请逐项检查:
1. 结果是否与用户目标相关
2. 结果格式是否正确
3. 结果内容是否完整(非空、非错误信息)
4. 结果是否满足时效性要求(如需要最新数据)

以 JSON 格式返回:
{{"passed": true/false, "checks": [{{"name": "检查项", "passed": true/false}}], "failure_reason": "失败原因"}}
"""
        response = self.llm.chat(eval_prompt)
        # 解析 LLM 返回的评估结果
        return self._parse_evaluation(response)

    def _reflect(
        self,
        step: ExecutionStep,
        eval_result: EvaluationResult,
        user_input: str,
    ) -> ReflectionResult:
        """反思:分析失败根因并生成修正方案"""
        reflect_prompt = f"""
步骤执行失败,请分析原因并提出修正方案。

用户目标:{user_input}
步骤描述:{step.description}
工具名称:{step.tool_name}
工具输入:{step.tool_input}
执行结果:{step.tool_output}
失败原因:{eval_result.failure_reason}
已反思次数:{step.reflection_count}

请分析:
1. 根因是什么?(参数不当/工具选错/计划有误)
2. 如何修正?
3. 修正的信心度(0-1)

以 JSON 格式返回:
{{"root_cause": "根因", "revision_type": "reparam/retool/replan", "revised_input": {{}}, "confidence": 0.8}}
"""
        response = self.llm.chat(reflect_prompt)
        return self._parse_reflection(response)

    def _apply_revision(self, step: ExecutionStep, reflection: ReflectionResult) -> ExecutionStep:
        """应用修正方案"""
        if reflection.revision_type == "reparam":
            # 修改工具参数,保持工具不变
            step.tool_input.update(reflection.revised_input)
        elif reflection.revision_type == "retool":
            # 更换工具
            step.tool_name = reflection.revised_input.get("tool_name", step.tool_name)
            step.tool_input = reflection.revised_input.get("tool_input", step.tool_input)
        elif reflection.revision_type == "replan":
            # 重新规划后续步骤(最重的修正)
            new_plan = reflection.revised_input.get("new_steps", [])
            # 生产环境中需要更新全局步骤列表
        return step

    def _execute_step(self, step: ExecutionStep) -> ExecutionStep:
        """执行工具调用"""
        tool = self.tools.get(step.tool_name)
        if tool is None:
            step.tool_output = f"错误:工具 {step.tool_name} 不存在"
            return step

        try:
            step.tool_output = tool.execute(step.tool_input)
        except Exception as e:
            step.tool_output = f"工具执行异常: {str(e)}"
        return step

3.3 领域约束注入:结构化评估

class FinancialAgent(ReflectiveAgent):
    """财务场景 Agent:注入领域约束评估"""

    def _evaluate_step(self, step: ExecutionStep, user_input: str) -> EvaluationResult:
        """在通用评估基础上增加领域约束检查"""
        # 先执行通用评估
        result = super()._evaluate_step(step, user_input)

        if not result.passed:
            return result

        # 领域约束:财务数据时效性检查
        if "财报" in user_input or "季度" in user_input:
            # 检查返回数据是否包含正确的季度标识
            quarter = self._extract_quarter(user_input)
            if quarter and quarter not in step.tool_output:
                result.passed = False
                result.failure_reason = f"数据时效性不匹配:期望 {quarter},结果中未找到"
                result.checks.append({
                    "name": "季度匹配",
                    "passed": False,
                    "detail": f"期望 {quarter},实际数据未包含"
                })

        # 领域约束:数值合理性检查
        if "毛利率" in user_input or "利润率" in user_input:
            value = self._extract_percentage(step.tool_output)
            if value is not None and (value < -50 or value > 100):
                result.passed = False
                result.failure_reason = f"数值异常:毛利率 {value}% 超出合理范围 [-50, 100]"
                result.checks.append({
                    "name": "数值合理性",
                    "passed": False,
                    "detail": f"毛利率 {value}% 不在合理范围"
                })

        return result

四、反思机制的代价:Token 消耗、延迟膨胀与过度反思

反思机制在提升 Agent 准确率的同时,引入了显著的资源开销和新的工程风险。

Token 消耗倍增。每次反思需要调用 LLM 做评估和根因分析,至少额外消耗 2 次 LLM 调用。3 次反思意味着 6 次额外调用,Token 成本增加 3-6 倍。对于成本敏感的场景,需要严格控制反思触发条件——不是每一步都反思,只在关键步骤(数据查询、数值计算)上启用反思。

延迟线性增长。反思循环是串行的——评估、反思、修正、重新执行,每轮反思增加 2-5 秒延迟。3 轮反思可能让单步执行时间从 2 秒膨胀到 15 秒。对于实时交互场景,这个延迟不可接受。解决方案是:设定反思超时上限,超时后直接升级处理。

过度反思的陷阱。LLM 生成的评估和反思本身可能有误。评估可能误判正确结果为错误(假阳性),导致不必要的反思循环;反思可能给出错误的根因分析,导致修正方向偏离,越改越错。解决方案是:评估标准尽量用确定性规则(正则匹配、数值范围检查),减少对 LLM 评估的依赖。

反思与执行的耦合。当前实现中,反思逻辑和执行逻辑紧耦合。如果工具接口变更,反思逻辑也需要同步修改。建议将评估标准抽象为声明式规则(如 JSON Schema 校验),与执行逻辑解耦。

适用边界:简单单步任务不需要反思,直接执行即可;多步推理任务在关键步骤启用反思;高风险决策任务(财务、法律、医疗)必须启用反思 + 人工升级;实时对话场景限制反思次数为 1 次,避免延迟过大。

五、总结

反思机制的核心价值是:在 Agent 执行链路中插入"自我纠错"环节,防止错误在多步推理中被逐级放大。评估、反思、修正三步循环构成了 Agent 的"免疫系统",让 Agent 具备了从错误中恢复的能力。

落地路线建议:第一步,在 Agent 的关键步骤(数据查询、数值计算、外部 API 调用)后插入评估环节,评估标准优先使用确定性规则;第二步,评估未通过时触发反思,调用 LLM 分析根因并生成修正方案;第三步,设定反思次数上限(每步 3 次、全局 10 次),防止无限循环;第四步,反思次数耗尽时升级处理,请求人工介入或向用户说明限制;第五步,监控反思触发率和修正成功率,持续优化评估标准和反思 Prompt。

反思机制不是让 Agent 变得更聪明,而是让 Agent 变得更可靠。一个会纠错的 Agent,比一个从不犯错但也不知纠错的 Agent 更适合生产环境。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐