LangGraphJs 核心概念、工作流程理解及应用

了解LangGraphJs:核心概念、工作流程及应用

LangGraph 简介

LangGraph 是一个由 LangChain 团队开发的低层级 Agent 编排框架,旨在构建有状态且长时间运行的 AI 工作流。与传统的线性 LLM 调用链不同,它将工作流建模为有向图(Directed Graph)。这种架构支持执行复杂操作和条件分支,非常适合处理复杂的流程管理任务。

核心组件

  • 节点 (Node):每个节点代表一个具体的函数调用,可以是调用 LLM、执行工具或数据处理操作。

  • 边 (Edge):定义了节点之间的流转路径,支持多种类型的边缘连接:

    • 普通边 (Fixed Path):指定明确的从源节点到目标节点的路径。
    • 条件边 (Conditional Edge):根据状态动态路由流转方向。
    • 起始边(Start Node):定义工作流的起始节点。
    • 结束边(End Node):标识工作流结束时的状态。
  • 状态 (State):在整个工作流中共享并传递的数据,可以用来记录流程中的各种信息和结果。

工作原理概述

为了更好地理解 LangGraph 的核心功能,我们将使用一个简化的类实现 MiniStateGraph。该简化版本涵盖了基本的 API 功能,并展示了如何构建和执行有向图工作流。

type NodeFunction
<S> = (state: S) => Promise<S> | S;
type NextEdgeResolver
<S> = (state: S) => string;

interface GraphDefinition
<S> {
  nodes: Record<string, NodeFunction<S>>;
  edges: Record<string, string[]>; // 普通边
  condEdges: Record<string, NextEdgeResolver<S>>; // 条件边
  entry?: string; // 入口节点
}

export class MiniStateGraph<S extends Record<string, any>> {
  private nodes: GraphDefinition
<S>["nodes"] = {};
  private edges: GraphDefinition
<S>["edges"] = {};
  private condEdges: GraphDefinition
<S>["condEdges"] = {};
  private entry?: string;

  addNode(name: string, fn: NodeFunction
<S>) {
    this.nodes[name] = fn;
    return this;
  }

  addEdge(from: string, to: string) {
    if (!this.edges[from]) this.edges[from] = [];
    this.edges[from].push(to);
    return this;
  }

  addConditionalEdges(from: string, resolver: NextEdgeResolver
<S>) {
    this.condEdges[from] = resolver;
    return this;
  }

  setEntryPoint(name: string) {
    this.entry = name;
    return this;
  }

  compile(options?: { maxSteps?: number; onStep?: (info: { step: number; node: string; state: S; next?: string }) => void }) {
    const def: GraphDefinition
<S> = {
      nodes: this.nodes,
      edges: this.edges,
      condEdges: this.condEdges,
      entry: this.entry
    };

    if (!def.entry) {
      throw new Error("Entry point is not set");
    }

    const END = "end";
    const maxSteps = options?.maxSteps ?? 100;

    async function runNode(name: string, state: S): Promise
<S> {
      const fn = def.nodes[name];
      if (!fn) throw new Error(`Node ${name} not found`);
      return await fn(state);
    }

    function resolveNextEdge(name: string, state: S): string {
      // 条件边优先处理
      if (def.condEdges[name]) {
        const next = def.condEdges[name](state);
        if (!next) throw new Error(`Conditional edge from ${name} returned empty`);
        return next;
      }
      // 普通路径
      const outs = def.edges[name];
      if (!outs || outs.length === 0) return END;
      return outs[0] ?? "";
    }

    async function execute(input: S): Promise
<S> {
      let state: S = input;
      let current: string = def.entry as string;

      for (let step = 0; step < maxSteps; step++) {
        state = await runNode(current, state);
        const next = resolveNextEdge(current, state);

        options?.onStep?.({ step, node: current, state, next });

        if (next === END) return state;
        current = next;
      }
      throw new Error(`Max steps (${maxSteps}) exceeded - possible infinite loop`);
    }

    async function* executeStream(input: S) {
      let state: S = input;
      let current: string = def.entry as string;

      for (let step = 0; step < maxSteps; step++) {
        state = await runNode(current, state);
        const next = resolveNextEdge(current, state);

        yield { step, node: current, state, next };

        if (next === END) return;
        current = next;
      }
      throw new Error(`Max steps (${maxSteps}) exceeded - possible infinite loop`);
    }

    return { execute, executeStream };
  }
}

示例应用

const graph = new MiniStateGraph<{ a: string; b: number }>()
  .addNode("nodeA", (state) => ({ ...state, a: "hello" }))
  .addConditionalEdges("nodeA", (state) => state.b > 10 ? "nodeB" : "nodeC")
  .setEntryPoint("nodeA");

const { execute } = graph.compile();
execute({ b: 5 }).then((finalState) => console.log(finalState));

上述代码展示了如何使用 MiniStateGraph 创建一个简单的有向图,并执行该图的工作流。通过定义节点和边缘,我们能够构建复杂的逻辑流程。

通过对LangGraph核心概念的理解以及实际的应用示例,我们可以看到其在处理复杂 AI 工作流中的强大功能。接下来我们将进一步探讨 LangGraph 的更多高级特性和最佳实践。

五、构建复杂Agent的图结构(重点)

在构建复杂的多工具智能客服系统时,设计清晰且可扩展的图结构是关键步骤之一。我们利用LangGraphJS提供的MiniStateGraph类来实现这一目标。

import { MiniStateGraph } from "./mini-langgraph-0.3.x";

const graph = new MiniStateGraph
<State>();

graph.addNode("router", routerNode);
graph.addNode("order", orderNode);
graph.addNode("rag", ragNode);
graph.addEdge("llm", "review"); // 审核节点在LLM生成之后
graph.addConditionalEdges("review", (state) => state.needRetry ? 'llm' : 'end');  // 循环检测并重试

// 构建完成的图结构可以进行编译,形成应用实例。
const app = graph.compile();

通过上述代码片段,我们构建了Agent执行的关键节点之间的连接,并配置了条件分支以支持循环修正逻辑。这种灵活且模块化的架构能够简化复杂任务流程的设计和维护。

六、结论与展望

本文详细介绍了使用LangGraphJS实现一个复杂的多工具智能客服系统的过程,从概念到具体实践全面覆盖。通过构建状态管理、意图识别、工具调用及LLM生成等核心节点,并巧妙地利用条件分支来支持循环修正逻辑,我们成功创建了一个能够解决用户复杂请求的智能Agent。

未来的工作可以探索更多高级功能与应用场景,比如增加语音或图像处理能力以实现多模态交互;或者结合外部API服务(如天气预报、新闻资讯)为用户提供更丰富的信息源。此外,还可以研究如何将这种架构应用于其他领域,如机器人流程自动化(RPA)中的任务流设计等。

通过深入理解LangGraphJS的工作原理及其在复杂场景下的应用实践,开发者们可以构建出更加智能且高效的自动化服务解决方案。


> 🔗 相关阅读向量数据库