首页 / 文章

循环与图:构建AI智能体的两种架构方式

AI智能体中的循环工程与图工程:两种架构的运作原理、何时选择其中一种而非另一种,以及上线前的检查清单。

发布者:REACTAPP.TOP阅读约 12 分钟2697 词

来源: Loop Engineering vs. Graph Engineering: The Architecture Shift Quietly Reshaping AI Agents

两个热门术语,一个架构问题

那些关注能够自行搜索网络、调用工具、编写代码或运行工作流的AI智能体的人,最近很可能听说过两个术语:循环工程和图结构工程。它们听起来像是营销用语,但实际上关乎重要的架构决策。

试想这样一个智能体:为了解决本应三步就能解决的问题,它却消耗了40,000个标记以及六分钟的实时处理时间。模型和提示语本身并无问题,问题出在设计上——因为该智能体无法意识到自己已经尝试过某些操作,只能不断重复“思考、行动、观察”的循环,却没有任何关于自身处理流程的记录。

这种类型的故障使得智能体设计不再属于提示工程范畴,而属于软件架构领域。一旦从这种角度看待问题,几乎所有已上线的智能体系统都基于两种模式之一:循环和图结构。下文将介绍这两种模式的定义、构建方式、易出问题的地方,以及为何从一种模式转向另一种模式会改变工程师处理这些系统的方式,而不仅仅是工具的更换。

智能体实际在做什么

在任何给定时刻,智能体都会执行以下三种操作之一:思考(调用大型语言模型)、行动(调用工具),或者决定下一步该做什么。智能体工程主要就是将这三者有机结合起来的工作。

在大型语言模型智能体出现的最初几年,几乎所有人都采用相同的实现方式,即循环结构。这是最显而易见的选择。ReAct风格的智能体从一开始就是如此工作,这也与Anthropic对最简单智能体的描述一致:一个在循环中自主使用工具的模型。

后来,智能体需要处理更复杂、更庞大的任务:多个专门的子智能体、重试机制、人工审核以及分支化的业务规则。单一的循环结构已无法应对这些需求。团队希望明确了解流程的架构,而非仅仅依赖循环最终会收敛这一假设,这种需求催生了图论工程,它通过节点和边来清晰呈现流程,而不依赖重复迭代来隐含表达。

这两种模式都没有错。它们解决的是不同的问题,因此都值得仔细研究。

厨房里的类比

想象一下教别人做饭的情景。循环工程就是重复执行同一条指令:尝尝汤的味道,如果需要就加盐,再尝一次,如此反复直到味道合适。这是一个“行动、检查、必要时重复”的循环。简单、可预测,也容易想象。

图结构工程则类似于一份带有分支的完整食谱。有人烧水时,另一个人负责切菜。两者都准备就绪后,再将它们混合在一起。如果品鉴者觉得需要更多调味料,就把锅送回调味步骤,否则就可以上菜了。这里没有单一的步骤流程,存在多条路径,有些步骤可以同时进行,有些则取决于决策结果,只有出现异常时某些步骤才会循环回去。

循环工程的含义

循环是构建智能体的最原始也是最简单的方式。它在一个连续的循环中运行:

  1. 接收输入,即用户的问题或目标。
  2. 思考下一步该做什么,即模型的推理步骤。
  3. 执行动作,通常意味着调用工具、运行代码或查找信息。
  4. 观察该动作产生的结果。
  5. 回到第2步并持续循环,直到智能体判断目标已达成。

人们常将此称为“思考-执行-观察”循环,许多最早的智能体都是基于这一循环构建的。在循环工程中,智能体被视为一个不断重复的单一循环:模型进行推理、选择动作、执行动作、分析结果并将结果写回自身的上下文,然后再次循环,直到认为任务已完成或达到上限。

以下几点解释了其受欢迎的原因:

  • 它构成了姚及其同事提出的著名ReAct模式(推理与行动的结合)的基础,在该模式中,推理过程与行动相互交织,模型无需事先完成所有思考。
  • 大多数智能体框架也是以这种方式定义“智能体”的,即一个具有共享上下文窗口和工具调用接口的控制循环。
  • 编写这样的智能体只需几行代码。其状态即为目前的对话内容,且只有一个退出条件。无论是用于编辑文件的编程助手,还是会持续搜索直至满足条件的研究智能体,都不需要更复杂的结构。
  • Anthropic的指导原则也指出:从最简单的可行方案开始,只有在任务需要时再添加结构。

问题在于,循环需要处理的所有信息——包括它已经尝试过的内容、失败的原因以及目标——都必须存储在不断扩大的上下文窗口中。该流程没有独立的内存,仅能保留一份记录。

循环的构成方式

其运行流程如下所示。

逐步说明如下:

  • 用户任务会带着一个目标启动循环。
  • 在思考阶段,模型会读取系统提示、对话历史以及最新的观察结果,然后决定下一步该做什么。
  • “任务是否完成?”的判断并非由独立的系统负责,模型会根据自身的上下文来判断是否已完成。
  • 如果尚未完成,模型会选择一个工具并调用它。
  • 工具的处理结果或错误会以观察结果的形式返回。
  • 该观察结果会被添加到上下文中,随后循环重新开始,每次迭代时的历史记录都会稍长一些。
  • 当模型完成思考后,循环结束并返回最终答案。
  • 一个简短的 Python 示例展示了实现此功能所需的代码量之少。

    def run_agent(task: str, tools: list, max_iterations: int = 10) -> str:
        context = [{"role": "user", "content": task}]
    
        for step in range(max_iterations):
            response = llm.generate(context, tools=tools)
    
            if response.is_final_answer:
                return response.content
    
            tool_result = execute_tool(response.tool_call)
            context.append({"role": "assistant", "content": response.content})
            context.append({"role": "tool", "content": tool_result})
    
        return "Stopped: max iterations reached"
    

    图工程的含义

    图工程以显式的方式描述了智能体的处理流程。它由一系列节点组成,每个节点代表一项工作单元,如大语言模型调用、工具调用或业务逻辑代码,这些节点通过固定或条件性的边相互连接。所有节点都读取并写入同一个共享的、有类型的状态对象。简而言之,工作流程就变成了由相互连接的步骤构成的映射关系,以及这些步骤之间的路径,从而取代了单一的重复循环。

    节点可以是以下任意一种:

    • 智能体,即具备特定功能的专用人工智能,负责研究、写作或分析等工作
    • 工具调用,例如网络搜索、计算或数据库查询
    • 决策点,用于选择下一条路径
    • 合并点,用于整合多条路径的结果

    多条路径可以并行执行,还有一些路径是条件性的,只有测试通过时才会被执行。循环仍然是可行的,但只会出现在需要的地方,作为一种可控的机制,让流程退回几步重新处理,而不会盲目地重复所有操作。

    为何图结构如此吸引人

    • 并行性。独立的任务可以同时运行,无需排队等待。
    • 分支性。工作流程可以根据具体情况朝不同方向发展,而不会重复相同的步骤。
    • 专业化。每个节点都可以成为专注于单一任务的独立智能体,这更接近人类团队分工的方式。
    • 针对性重试。当质量检查之类的步骤失败时,只有该步骤会将任务退回重新处理,其他部分则不受影响。
  • 可见性。由于工作流以地图的形式呈现,因此更容易发现问题出在何处以及选择了哪条路径。
  • LangGraph正是基于这一理念构建的框架,它将工作流建模为具有状态的全循环有向图。由于节点可能拥有指向 earlier节点的边,因此图表仍可形成循环,从而支持重试和迭代操作。不同的是,这种循环现在只是众多边中的一条,显示在可供查看的地图上,而不再构成整个架构。

    这与Anthropic对工作流的划分十分契合——在那种工作流中,大语言模型和工具遵循开发者控制的预定义代码路径;而在代理型工作流中,模型则能动态地指导自身的处理流程。图表可以用一种结构同时呈现这两种情况:在需要可预测性的场景中使用固定路径,在需要灵活性的场景中使用由模型驱动的分支,二者之间的差异会在图表中显现出来。

    图表的构建方式

    以同一类问题为例,即一个需要自行进行研究、编写代码、审查输出结果并重新构建的代理,我们可以将其转化为图表。

    具体步骤如下:

    • START是图表的入口点。
    • 路由节点会读取当前状态并选择路径。这可以是简单的if/else语句,也可以是用于对请求进行分类的大语言模型调用。
  • 三个专用节点——研究节点、代码节点和审核节点——各自独立运行:它们读取状态、执行相应任务,然后将结果写回。
  • 聚合节点或合并节点会将各分支执行产生的结果整合为一个统一的状态对象。
  • 重试路径用于处理审核失败的情况。当审核节点的验证失败时,会有专门的路径将任务重新路由回路由器,而不会简单地再次提示同一模型。该路径会在图中显示,而不会像“如果这次失败,再试一次”那样隐藏在提示语句中。
  • 一旦状态通过验证,流程即到达结束状态。
  • 以下是大多数图框架所采用的简化伪代码格式。

    from graph_engine import StateGraph, START, END
    
    def router(state: dict) -> str:
        return state["task_type"]  # "research" | "code" | "review"
    
    def research_agent(state: dict) -> dict:
        state["results"]["research"] = do_research(state["query"])
        return state
    
    def code_agent(state: dict) -> dict:
        state["results"]["code"] = write_code(state["query"])
        return state
    
    def review_agent(state: dict) -> dict:
        state["valid"] = validate(state["results"])
        return state
    
    def aggregator(state: dict) -> dict:
        state["final_output"] = merge(state["results"])
        return state
    
    graph = StateGraph(schema=AgentState)
    graph.add_node("router", router)
    graph.add_node("research_agent", research_agent)
    graph.add_node("code_agent", code_agent)
    graph.add_node("review_agent", review_agent)
    graph.add_node("aggregator", aggregator)
    
    graph.add_conditional_edges("router", {
        "research": "research_agent",
        "code": "code_agent",
        "review": "review_agent",
    })
    graph.add_edge(["research_agent", "code_agent", "review_agent"], "aggregator")
    graph.add_conditional_edges("aggregator", {
        "valid": END,
        "invalid": "router",   # explicit retry edge
    })
    
    app = graph.compile()
    app.invoke({"query": "...", "results": {}, "task_type": "code"})
    

    将这两种结构放在一起对比,能更清晰地看出差异。

    为何行业正从循环结构转向图结构

    这一转变并非突然发生。随着人们试图构建更复杂的系统,循环结构因多种原因遇到了实际限制。

    实际工作并非线性进行。研究、客户支持、财务分析以及内容处理流程都会出现分支。如果是账单相关问题,就按一条路径处理;若是技术问题,则走另一条路径。循环结构会将所有这些流程压缩成一条冗长的条件链,很快就会变得混乱不堪。

    多智能体系统需要更合适的结构。当一个系统包含多个智能体,比如研究人员、摘要生成器和事实核查器时,单个循环结构无法表达两个智能体应同时工作,或某个智能体仅在检测失败时才启动这样的逻辑。而图结构则能自然地体现这些关系。

    随着时间推移,可见性与可信度更为重要。当初处理高风险任务时,团队需要逐步了解所发生的一切。可以通过图表直观展示某个流程的优劣,而循环则只是执行相同代码二十次的透明代码块。

    成本与速度。每一次不必要的迭代都会耗费时间,往往还会带来实际成本,因为每一个思考步骤都意味着一次语言模型的调用。并行执行独立步骤,并仅重新处理需要修改的部分,显然效率更高。

    测试与维护更简单。可以逐个节点构建、测试和修改图表。结构复杂的循环通常是全有或全无的逻辑,因此在系统上线后要安全地对其进行修改会更加困难。

    如何选择

    以下是一个大致的决策框架。

    以下情况适合使用循环:

    • 该任务只有一个目标:一个代理、一个目标以及一套工具
    • 在投入任何架构开发资金之前,需要先测试早期原型
    • 步骤数量较少且相对容易预测
    • 由一个人全权负责,无需将任何内容交给其他团队

    以下情况应使用图表:

    • 涉及两个或更多专业代理或角色时
    • 需要对重试、备用方案或人工审批进行审计时
    • 流程的某些部分可以并行运行,之后需要合并结果时
    • 需要追踪执行过程、设置检查点或从特定点继续执行,而单纯的重放记录无法实现这些功能时
    • 不同人员或团队负责不同的环节,图表可作为他们之间的协作依据时

    这里有一个实用的自我检查方法。如果循环代码中出现了诸如“此时我们本应已经尝试过X”这样的注释,那就说明你正在以错误的方式在循环内部构建图结构,或许是时候画出这个图了。

    这并非非此即彼的选择,而是要根据任务特点选择合适的工具。那种一次只回答一个问题的简单聊天机器人,或是不断重复某个明确操作直至完成的小脚本,更适合使用循环;而那些包含多个分支步骤、需要多个协同工作的专用智能体、可并行处理的任务,或是需要清晰了解发生什么及原因的场景,则更适合使用图结构。合理的默认做法是先从循环开始,一旦“如果…那么…”的逻辑变得难以理解,就立即转为使用图结构。

    生产环境检查清单

    在采用这两种结构中的任何一种之前,养成以下习惯会很有帮助:

    • 限制步骤数量。两种模式都需要设置断路器,因为没有限制的智能体会导致费用无上限。
    • 让“完成”状态可检测。退出条件应是测试能够验证的内容,而非模型凭直觉判断的结果。
    • 为每个步骤添加监控。为每次大语言模型调用及工具调用分别创建独立的OpenTelemetry跟踪记录,避免整个运行过程只使用一个跟踪记录。
    • 对流程图进行版本控制。像处理代码一样将定义保存在版本控制系统中,因为细微的边节点变化就可能悄悄改变系统行为。
    • 在节点边界验证状态。检查进入和离开每个节点时的数据结构是否符合类型规范,从而及早阻止格式错误的数据进入后续处理流程。
  • 记录决策过程。需记录模型选择某条路径的原因及其具体操作,因为事后分析正是依赖这些信息。
  • 测试重试机制。故意引发验证失败,确认重试或循环处理是否达到预期效果,包括延迟和资源消耗的变化情况。
  • 衡量每项工作的成本与时间。统计图中的每个节点以及循环中的每次迭代,找出运行缓慢或成本过高的环节所在。
  • 核心要点

    循环是最简的设计形式,包含一个上下文、一个循环周期,由模型控制流程走向,适用于功能单一、任务范围有限的智能体。而图则通过节点、有类型的状态以及边来描述整个流程,便于进行分支处理、并行运行以及有针对性的重试操作。

    更深层次的变革并非图表变得更优秀,而是思维方式从“过程导向”——思考接下来该做什么——转变为“声明导向”——思考系统的形态是怎样的。图表与生产系统所需的追踪和可观测性机制相得益彰,而循环则适合快速迭代。

    大多数生产系统最终都是以循环构建的图表形式存在,本质上属于状态机,其节点为小型ReAct风格的循环。这使得这两种模式成为同一架构的不同层级,而非相互竞争的关系。对于任何当前的智能体项目,一个值得思考的问题是:所有操作是否仍以一个长循环的形式运行,还是已经到了将其绘制成图表的时候。

    相关阅读

  • 记录AI智能体在移动应用中运行的循环 — 每个智能体循环必须回答的四个问题,从点击到数据分割的六个嵌套循环,以及五种在未检测到任何异常时仍能表明任务完成的检查方式。