反思是一种用于提高代理和类似 AI 系统质量与成功率的提示策略。本文概述了如何使用 LangGraph 构建 3 种反思技术,包括 Reflexion 和语言代理树搜索的实现。

关键链接

反思是一种提示策略,用于提升智能体和类似 AI 系统的质量和成功率。它通过提示大语言模型(LLM)反思和批评其过去的行为,有时还会结合外部信息,如工具观察。

人们常提到“系统1”和“系统2”思维,系统1是反应迅速或本能的,而系统2则更为有条理和反思性。正确应用反思,可以帮助 LLM 系统摆脱纯粹的系统1思维模式,表现出更接近系统2的行为。

系统1和2:快速思考?慢下来。

反思需要时间!本文中的方法都用了一些额外的计算换取更好的输出质量。虽然这可能不适合低延迟应用,但对于知识密集型任务,响应质量比速度更重要,确实值得这样做。

以下是三个示例:

基本反思

链接: (PythonYoutube

这个简单示例由两个 LLM 调用组成:一个生成器和一个反思器。生成器尝试直接响应用户请求,反思器则扮演老师角色,对初始响应提供建设性的批评。

循环进行固定次数后,返回最终生成的输出。

简单反思循环

我们可以在 LangGraph 中定义以下循环:

from langgraph.graph import MessageGraph

builder = MessageGraph()
builder.add_node("generate", generation_node)
builder.add_node("reflect", reflection_node)
builder.set_entry_point("generate")


def should_continue(state: List[BaseMessage]):
    if len(state) > 6:
        return END
    return "reflect"


builder.add_conditional_edges("generate", should_continue)
builder.add_edge("reflect", "generate")
graph = builder.compile()

MessageGraph 表示一个有状态图,其中“状态”只是一个消息列表。每次调用生成器或反思节点时,它会将一条消息添加到状态的末尾。最终结果由生成器节点返回。

这种简单的反思方式有时可以通过让 LLM 多次尝试改进输出,并让反思节点在批评输出时扮演不同角色,从而提高性能。

然而,由于反思步骤不依赖于任何外部过程,最终结果可能不会显著优于原始结果。我们来探索一些可以改善这一情况的其他技术。

反思

链接:(PythonYoutube

Shinn 等人的 Reflexion 是一种通过语言反馈和自我反思来学习的架构。在 Reflexion 中,演员智能体会明确批评每个响应,并将其批评基于外部数据。它被要求生成引用,并明确列出生成响应中的多余和缺失部分。这使得反思内容更具建设性,并能更好地引导生成器改进响应。

链接示例中,我们在固定步数后停止,当然你也可以将这个决定交给反思 LLM 来处理。

智能体循环的概述如下所示:

反思演员概述

在每一步中,响应者的任务是生成一个响应,并附带一些搜索查询形式的额外操作。然后提示修订者反思当前状态。逻辑可以在 LangGraph 中定义如下:

from langgraph.graph import END, MessageGraph

MAX_ITERATIONS = 5
builder = MessageGraph()
builder.add_node("draft", first_responder.respond)
builder.add_node("execute_tools", execute_tools)
builder.add_node("revise", revisor.respond)
# draft -> execute_tools
builder.add_edge("draft", "execute_tools")
# execute_tools -> revise
builder.add_edge("execute_tools", "revise")

# Define looping logic:
def event_loop(state: List[BaseMessage]) -> str:
    # in our case, we'll just stop after N plans
    num_iterations = _get_num_iterations(state)
    if num_iterations > MAX_ITERATIONS:
        return END
    return "execute_tools"


# revise -> execute_tools OR end
builder.add_conditional_edges("revise", event_loop)
builder.set_entry_point("draft")
graph = builder.compile()

这个智能体可以有效利用明确的反思和基于网络的引用来提高最终响应的质量。然而,它只采用一条固定的路径,所以一旦出现错误,该错误会影响后续的决策。

语言智能体树搜索

链接:(PythonYoutube

Zhou 等人的语言智能体树搜索(LATS)是一种通用的大语言模型(LLM)智能体搜索算法。它通过结合反思/评估和搜索(特别是蒙特卡洛树搜索),在整体任务性能上优于类似技术如 ReACT、Reflexion 甚至 Tree of Thoughts。LATS 采用标准的强化学习(RL)任务框架,用 LLM 调用来替代 RL 智能体、价值函数和优化器。这有助于智能体适应和解决复杂任务,避免陷入重复循环。

搜索过程如下图所示:

搜索过程主要有四个步骤:

  1. 选择:根据下面步骤(2)的总奖励选择最佳下一步操作。如果找到解决方案或达到最大搜索深度,则停止搜索并给出响应;否则,继续搜索。
  2. 扩展和模拟:生成 N(在本例中为 5)个潜在操作并并行执行它们。
  3. 反思和评估:观察这些操作的结果,并根据反思(以及可能的外部反馈)对决策进行评分。
  4. 反向传播:根据结果更新根轨迹的分数。

如果智能体具有紧密的反馈循环(通过高质量的环境奖励或可靠的反思评分),搜索能够准确区分不同的行动轨迹并选择最佳路径。最终的轨迹可以保存到外部存储中(或用于模型微调),以在未来改进模型。

“选择”步骤选择具有最高上置信界(UCT)的节点,这在平衡预期奖励(第一个项)与探索新路径的激励(第二个项)。

UCT=valuevisits+cln(parent.visits)visits UCT = \frac{\text{value}}{\text{visits}} + c \sqrt{\frac{\ln(\text{parent.visits})}{\text{visits}}}

查看代码了解其实现。在我们的 LangGraph 实现中,我们将生成和反思步骤放在每个节点中,并在每次循环时检查树的状态以确定任务是否解决。简略的图定义如下所示:

from langgraph.graph import END, StateGraph

class Node:
    def __init__(
        self,
        messages: List[BaseMessage],
        reflection: Reflection,
        parent: Optional[Node] = None,
    ):
        self.messages = messages
        self.parent = parent
        self.children = []
        self.value = 0
        self.visits = 0
    # Additional methods are defined here. Check the code for more!

class TreeState(TypedDict):
    # The full tree
    root: Node
    # The original input
    input: str

def should_loop(state: TreeState):
    """Determine whether to continue the tree search."""
    root = state["root"]
    if root.is_solved:
        return END
    if root.height > 5:
        return END
    return "expand"


builder = StateGraph(TreeState)
builder.add_node("start", generate_initial_response)
builder.add_node("expand", expand)
builder.set_entry_point("start")


builder.add_conditional_edges(
    "start",
    # Either expand/rollout or finish
    should_loop,
)
builder.add_conditional_edges(
    "expand",
    # Either continue to rollout or finish
    should_loop,
)

graph = builder.compile()

一旦你创建了基本框架,扩展到其他任务就变得简单了!例如,这种技术非常适用于代码生成任务,在这些任务中,智能体可以编写明确的单元测试,并根据测试质量对执行路径进行评分。

LATS 结合了其他智能体架构的推理、规划和反思功能,如 Reflexion、Tree of Thoughts 和 plan-and-execute 智能体。LATS 还通过反思和基于环境的反馈反向传播来优化搜索过程。尽管它对奖励评分较为敏感,但这种通用算法可以灵活应用于各种任务。

图 1: LATS 与其他智能体架构的比较

视频教程

结论

感谢阅读!所有这些示例都可以在 LangGraph 仓库中找到,我们很快会将这些移植到 LangGraphJS(或许在你读到这篇文章时已经移植完毕)。

上述所有技术都利用了额外的大语言模型 (LLM) 推理来提高生成高质量输出的可能性,或正确回答更复杂的推理任务。尽管这需要额外时间,但当输出质量比响应时间更重要时,这是值得的。如果你将执行路径保存到内存中(或作为 微调数据),可以更新模型以避免将来重复犯错。