第4章:反思

智能体设计模式:构建智能系统的实战指南 阅读 49 次

第4章:反思

反射模式概述

在前几章中,我们探讨了基本的智能体模式:链式模式用于顺序执行,路由模式用于动态路径选择,并行化模式用于并发任务执行。这些模式使智能体能够更高效、更灵活地执行复杂任务。然而,即使有复杂的流程,智能体的初始输出或计划可能并不最优、准确或完整。这就是反思模式发挥作用的地方。

反射模式涉及智能体对其自身的工作、输出或内部状态进行评估,并利用这种评估来提高其性能或细化其响应。这是一种自我纠正或自我提升的形式,使智能体能够根据反馈、内部批评或与期望标准的比较,迭代地细化其输出或调整其方法。偶尔,可以通过一个专门的角色为智能体提供帮助,该角色的具体职责是分析初始智能体的输出。

与简单的顺序链不同,其中输出直接传递到下一步,或者路由选择路径,反射引入了反馈循环。智能体不仅产生输出;它还会检查该输出(或生成它的过程),识别潜在问题或改进领域,并利用这些洞察来生成更好的版本或修改其未来的行为。

该过程通常包括:

  1. 执行:智能体执行一项任务或生成初始输出。
  2. 评估/批评:智能体(通常使用另一个LLM调用或一组规则)分析前一步的结果。这种评估可能检查事实准确性、连贯性、风格、完整性、遵守指令或其他相关标准。
  3. 反思/精炼: 根据批评,智能体确定如何改进。这可能涉及生成精炼的输出、调整后续步骤的参数,甚至修改整体计划。
  4. 迭代(可选但常见): 精炼后的输出或调整后的方法可以随后执行,反思过程可以重复进行,直到达到满意的结果或满足停止条件。

关键且高效的反射模式实现将过程分为两个不同的逻辑角色:生产者和评论家。这通常被称为“生成器-评论家”或“生产者-审阅者”模型。虽然单个智能体可以执行自我反思,但使用两个专门的智能体(或使用两个具有不同系统提示的独立LLM调用)通常会产生更稳健和无偏见的成果。

  1. 生产者智能体:该智能体的主要职责是执行任务的初始阶段。它专注于生成内容,无论是编写代码、起草博客文章还是制定计划。它接受初始提示并生成输出的第一个版本。

  2. 批判智能体:这个智能体的唯一目的是评估生产者生成的输出。它被赋予不同的指令,通常是一个独特的角色(例如,“你是一位资深软件工程师”,“你是一位严谨的事实核查者”)。批评者的指令指导它根据特定标准分析生产者的工作,例如事实准确性、代码质量、风格要求或完整性。它旨在找出缺陷、提出改进建议并提供结构化反馈。

这种关注点的分离非常强大,因为它防止了智能体在审查自身工作时的“认知偏差”。评论家智能体以全新的视角来审视输出,全身心致力于寻找错误和改进区域。然后,评论家智能体的反馈被传递回生产者智能体,生产者智能体将其作为指南来生成一个新版本、更精炼的输出。提供的LangChain和ADK代码示例都实现了这种双智能体模型:LangChain示例使用特定的“reflector_prompt”来创建评论家角色,而ADK示例明确定义了生产者和审查者智能体。

实现反思通常需要将智能体的工作流程结构化,以包含这些反馈循环。这可以通过代码中的迭代循环或使用支持状态管理和基于评估结果的条件转换的框架来实现。虽然评估和精炼的单个步骤可以在LangChain/LangGraph、ADK或Crew.AI链中的任何一个中实现,但真正的迭代反思通常涉及更复杂的编排。

反射模式对于构建能够产生高质量输出、处理细微任务并展现出一定自我意识和适应能力的智能体至关重要。它使智能体超越了单纯执行指令的层面,向更高级的问题解决和内容生成形式迈进。

反思与目标设定及监控的结合(参见第11章)值得关注。目标为智能体的自我评估提供了最终基准,而监控则追踪其进展。在许多实际案例中,反思可能充当纠正引擎的角色,利用监控反馈来分析偏差并调整其策略。这种协同作用将智能体从被动执行者转变为有目的的系统,能够自适应地工作以实现其目标。

此外,当LLM(大型语言模型)能够记住对话内容时(参见第8章),反射模式的效率会显著提升。这种对话历史为评估阶段提供了关键上下文,使得智能体不仅能够独立评估其输出,还能在先前互动、用户反馈和不断变化的目标背景下进行评估。它使智能体能够从过去的批评中学习,避免重复错误。没有记忆,每次反思都是一个独立的事件;有了记忆,反思变成了一种累积过程,每个循环都建立在上一个循环的基础上,从而实现更智能和更具有情境意识的改进。

实际应用与用例

反射模式在输出质量、准确性或遵循复杂约束至关重要的情况下非常有价值:

  1. 创意写作与内容生成: 精炼生成的文本、故事、诗歌或营销文案。

用例: 一个智能体撰写博客文章。 * 反思: 生成草稿,对其流畅度、语气和清晰度进行批评,然后根据批评意见进行修改。重复此过程,直至文章达到质量标准。 * 优势: 生成更加精致和有效的内容。

  1. 代码生成与调试: 编写代码、识别错误并修复它们。

用例: 一个智能体编写Python函数。 * 反思: 编写初始代码,运行测试或静态分析,识别错误或不效率之处,然后根据发现的结果修改代码。 * 优势: 生成更稳健和功能性的代码。

  1. 复杂问题解决: 评估多步推理任务中的中间步骤或提出的解决方案。

用例: 一个智能体解决逻辑谜题。 * 反思: 提出一个步骤,评估它是否有助于接近解决方案或引入矛盾,如果需要,则回溯或选择不同的步骤。 * 优点: 提升智能体在复杂问题空间中的导航能力。

  1. 摘要和信息综合: 精炼摘要以提高其准确性、完整性和简洁性。

用例: 智能体总结长文档。 * 反思: 生成一个初始摘要,将其与原文中的关键点进行比较,对摘要进行细化,以包含缺失信息或提高准确性。 * 优点: 创建更准确和全面的摘要。

  1. 规划与策略: 评估拟议的计划并识别潜在缺陷或改进之处。

用例: 智能体规划一系列动作以实现目标。 * 反思: 生成一个计划,模拟其执行过程,或根据约束条件评估其可行性,并根据评估结果修订计划。 * 益处: 制定更有效和更现实的计划。

  1. 对话智能体: 回顾对话中的前几个回合,以维持上下文、纠正误解或提高回复质量。

用例: 客户支持聊天机器人。 * 反思: 在用户回复后,回顾对话历史和最后生成的消息,以确保连贯性并准确回应用户的最新输入。 * 好处: 导致更自然和有效的对话。

反思为智能体系统增加了一层元认知,使它们能够从自己的输出和过程中学习,从而实现更智能、更可靠、更高品质的结果。

动手代码示例(LangChain)

实现一个完整、迭代的反思过程需要状态管理和循环执行机制。虽然这些在基于图的框架(如LangGraph)中或通过自定义过程代码中可以原生处理,但单个反思周期的基本原理可以通过LCEL(LangChain表达式语言)的组合语法有效地演示。

本例使用Langchain库和OpenAI的GPT-4o模型实现了一个反射循环,以迭代生成和优化一个计算数字阶乘的Python函数。过程从任务提示开始,生成初始代码,然后根据模拟的高级软件工程师角色的批评,反复对代码进行反思,并在每次迭代中优化代码,直到批评阶段确定代码完美或达到最大迭代次数。最后,它打印出最终优化的代码。

首先,请确保已安装必要的库:

pip install langchain langchain-community langchain-openai

您还需要设置您的环境,并使用您选择的语言模型(例如,OpenAI、Google Gemini、Anthropic)的API密钥。

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import SystemMessage, HumanMessage

# --- Configuration ---
# Load environment variables from .env file (for OPENAI_API_KEY)
load_dotenv()

# Check if the API key is set
if not os.getenv("OPENAI_API_KEY"):
    raise ValueError("OPENAI_API_KEY not found in .env file. Please add it.")

# Initialize the Chat LLM. We use gpt-4o for better reasoning.
# A lower temperature is used for more deterministic outputs.
llm = ChatOpenAI(model="gpt-4o", temperature=0.1)

def run_reflection_loop():
    """Demonstrates a multi-step AI reflection loop to progressively improve a Python function."""
    # --- The Core Task ---
    task_prompt = """
    Your task is to create a Python function named `calculate_factorial`.
    This function should do the following:
    1. Accept a single integer `n` as input.
    2. Calculate its factorial (n!).
    3. Include a clear docstring explaining what the function does.
    4. Handle edge cases: The factorial of 0 is 1.
    5. Handle invalid input: Raise a ValueError if the input is a negative number.
    """

    # --- The Reflection Loop ---
    max_iterations = 3
    current_code = ""

    # We will build a conversation history to provide context in each step.
    message_history = [HumanMessage(content=task_prompt)]

    for i in range(max_iterations):
        print("\n" + "="*25 + f" REFLECTION LOOP: ITERATION {i + 1} " + "="*25)
        # --- 1. GENERATE / REFINE STAGE ---
        # In the first iteration, it generates. In subsequent iterations, it refines.
        if i == 0:
            print("\n>>> STAGE 1: GENERATING initial code...")
            # The first message is just the task prompt.
            response = llm.invoke(message_history)
            current_code = response.content
        else:
            print("\n>>> STAGE 1: REFINING code based on previous critique...")
            # The message history now contains the task, the last code, and the last critique.
            # We instruct the model to apply the critiques.
            message_history.append(HumanMessage(content="Please refine the code using the critiques provided."))
            response = llm.invoke(message_history)
            current_code = response.content
        print("\n--- Generated Code (v" + str(i + 1) + ") ---\n" + current_code)
        message_history.append(response)  # Add the generated code to history
        # --- 2. REFLECT STAGE ---
        print("\n>>> STAGE 2: REFLECTING on the generated code...")
        # Create a specific prompt for the reflector agent.
        # This asks the model to act as a senior code reviewer.
        reflector_prompt = [
            SystemMessage(content="""
                You are a senior software engineer and an expert
                in Python. Your role is to perform a meticulous code review.
                Critically evaluate the provided Python code based
                on the original task requirements. Look for bugs, style issues,
                missing edge cases, and areas for improvement.
                If the code is perfect and meets all requirements,
                respond with the single phrase 'CODE_IS_PERFECT'.
                Otherwise, provide a bulleted list of your critiques.
            """),
            HumanMessage(content=f"Original Task:\n{task_prompt}\n\nCode to Review:\n{current_code}")
        ]
        critique_response = llm.invoke(reflector_prompt)
        critique = critique_response.content
        # --- 3. STOPPING CONDITION ---
        if "CODE_IS_PERFECT" in critique:
            print("\n--- Critique ---\nNo further critiques found. The code is satisfactory.")
            break
        print("\n--- Critique ---\n" + critique)
        # Add the critique to the history for the next refinement loop.
        message_history.append(HumanMessage(content=f"Critique of the previous code:\n{critique}"))

    print("\n" + "="*30 + " FINAL RESULT " + "="*30)
    print("\nFinal refined code after the reflection process:\n")
    print(current_code)

if __name__ == "__main__":
    run_reflection_loop()

代码首先设置环境,加载API密钥,并初始化一个强大的语言模型(如GPT-4o),以低温度参数生成聚焦的输出。核心任务通过一个提示定义,要求编写一个Python函数计算一个数的阶乘,包括对文档字符串、边界情况(0的阶乘)和错误处理(对负输入的处理)的具体要求。run_reflection_loop函数负责协调迭代改进过程。在循环的第一迭代中,语言模型根据任务提示生成初始代码。在后续迭代中,它根据前一步的批评来改进代码。一个独立的“反思者”角色,同样由语言模型扮演,但使用不同的系统提示,充当高级软件工程师,对生成的代码进行批评,以符合原始任务要求。批评以问题列表的形式提供,如果没有发现任何问题,则显示“CODE_IS_PERFECT”。循环将继续,直到批评表明代码完美或达到最大迭代次数。会话历史记录在每一步中保持并传递给语言模型,以提供生成/改进和反思阶段的上下文。最后,在循环结束后,脚本打印最后生成的代码版本。

动手代码示例(ADK)

现在,让我们来看一个使用谷歌ADK(应用开发套件)实现的代码示例。具体来说,这段代码通过采用生成器-评论家结构来展示这一点,其中一个组件(生成器)生成初始结果或计划,另一个组件(评论家)提供批判性反馈或评论,引导生成器朝着更精细或更准确最终输出的方向发展。

from google.adk.agents import SequentialAgent, LlmAgent

# The first agent generates the initial draft.
generator = LlmAgent(
    name="DraftWriter",
    description="Generates initial draft content on a given subject.",
    instruction="Write a short, informative paragraph about the user's subject.",
    output_key="draft_text"  # The output is saved to this state key.
)

# The second agent critiques the draft from the first agent.
reviewer = LlmAgent(
    name="FactChecker",
    description="Reviews a given text for factual accuracy and provides a structured critique.",
    instruction="""
    You are a meticulous fact-checker.
    1. Read the text provided in the state key 'draft_text'.
    2. Carefully verify the factual accuracy of all claims.
    3. Your final output must be a dictionary containing two keys:
       - "status": A string, either "ACCURATE" or "INACCURATE".
       - "reasoning": A string providing a clear explanation for your status, citing specific issues if any are found.
    """,
    output_key="review_output"  # The structured dictionary is saved here.
)

# The SequentialAgent ensures the generator runs before the reviewer.
review_pipeline = SequentialAgent(
    name="WriteAndReview_Pipeline",
    sub_agents=[generator, reviewer]
)

# Execution Flow:
# 1. generator runs -> saves its paragraph to state['draft_text'].
# 2. reviewer runs -> reads state['draft_text'] and saves its dictionary output to state['review_output'].

此代码演示了在Google ADK中利用顺序智能体管道生成和审查文本的方法。它定义了两个LLmAgent实例:生成器和审查员。生成器智能体旨在针对给定主题创建一个初始草稿段落。它被指示撰写一篇简短且信息丰富的文章,并将输出保存到状态键draft_text。审查员智能体充当生成器产生的文本的事实核查者。它被指示从draft_text中读取文本并验证其事实准确性。审查员的输出是一个具有两个键的结构化字典:状态和理由。状态指示文本是“准确”还是“不准确”,而理由提供了状态的解释。该字典被保存到状态键review_output。创建了一个名为review_pipeline的顺序智能体来管理两个智能体的执行顺序。它确保生成器先运行,然后是审查员。整体执行流程是生成器生成文本,然后将其保存到状态。随后,审查员从状态中读取此文本,执行其事实核查,并将其发现(状态和理由)保存回状态。此管道允许使用单独的智能体进行结构化的内容创建和审查过程。

注意: 对于有兴趣的读者,也提供了一个利用ADK的LoopAgent的替代实现。

在得出结论之前,重要的是要考虑,尽管反射模式显著提升了输出质量,但它也伴随着重要的权衡。虽然迭代过程非常强大,但它可能导致更高的成本和延迟,因为每个优化循环可能都需要一个新的LLM调用,这使得它对于时间敏感的应用来说并不理想。此外,该模式对内存消耗较大;随着每次迭代,对话历史都会扩展,包括初始输出、批评和随后的优化。

概览

内容: 智能体的初始输出通常不够理想,可能存在不准确、不完整或无法满足复杂要求的问题。基本的智能体工作流程缺乏智能体识别和修复自身错误内置流程。通过让智能体评估自己的工作或更稳健地引入一个独立的逻辑智能体作为评论者,可以解决这个问题,无论初始响应的质量如何,都不会成为最终结果。

原因: 反射模式通过引入自我纠正和优化的机制提供了一种解决方案。它建立了一个反馈循环,其中“生产者”智能体生成输出,然后“评论者”智能体(或生产者本身)根据预定义的标准对其进行评估。然后,这种评论被用来生成改进版本。这种生成、评估和优化的迭代过程逐渐提高了最终结果的质量,导致更准确、连贯和可靠的结果。

经验法则:当最终输出的质量、准确性和细节比速度和成本更重要时,请使用反射模式。这种方法特别适用于生成精炼的长篇内容、编写和调试代码以及创建详细计划等任务。当任务需要高度客观性或需要专业评估,而一般生产型智能体可能忽略时,请使用一个独立的评论型智能体。

视觉摘要

image1

图1:反射设计模式,自我反射

image2

图2:反射设计模式,生产者和评论智能体

关键要点

反射模式的主要优势在于其能够迭代自我校正和优化输出,从而显著提高质量、准确性和对复杂指令的遵循度。 它涉及执行、评估/批判和改进的反馈循环。反思对于需要高质量、准确或细微输出的任务至关重要。 一个强大的实现是生产者-评论家模型,其中有一个独立的智能体(或提示角色)评估初始输出。这种关注点的分离增强了客观性,并允许提供更多专业化和结构化的反馈。 然而,这些好处是以增加延迟和计算成本为代价的,同时还有超过模型上下文窗口或被API服务限制的风险。 尽管完整的迭代反思通常需要具有状态的工作流程(如LangGraph),但LangChain可以使用LCEL(语言链元素)来实现单个反思步骤,以便传递输出以供批评和后续改进。 谷歌ADK可以通过顺序工作流程促进反思,其中一个智能体的输出被另一个智能体所批判,从而允许后续的改进步骤。 此模式使智能体能够进行自我纠正,并随着时间的推移提升其性能。

结论

反射模式为智能体工作流程中的自我校正提供了关键机制,使迭代改进超越单次执行。这是通过创建一个循环来实现的,在该循环中,系统生成一个输出,将其与特定标准进行比较,然后利用该评估结果产生一个更精细的结果。这种评估可以由智能体本身(自我反思)执行,或者通常更有效地由一个独立的批评智能体执行,这代表了模式中的关键架构选择。

虽然一个完全自主的多步骤反思过程需要一个强大的状态管理架构,但其核心原则在一个单一的生成-批判-精炼循环中得到了有效展示。作为一个控制结构,反思可以与其他基础模式相结合,构建更稳健且功能复杂的智能体系统。

参考文献

以下是一些关于反射模式及相关概念的进一步阅读资源:

  1. 通过强化学习训练语言模型进行自我纠正
  2. LangChain 表达式语言(LCEL)文档
  3. LangGraph 文档
  4. Google智能体开发者工具包(ADK)文档(多智能体系统)