跳到内容

如何定义图状态

本操作指南将介绍定义图状态的不同方法。

先决条件

  • 状态概念指南 - 关于定义图状态的概念指南。
  • 构建图 - 本操作指南假设您对如何构建图有基本的了解。

设置

本指南需要安装 @langchain/langgraph@langchain/core 软件包

npm install @langchain/langgraph @langchain/core

入门

Annotation 函数是为新的 StateGraph 图定义图状态的推荐方式。Annotation.Root 函数用于创建顶层状态对象,其中每个字段代表图中的一个通道。

以下是一个如何定义一个名为 messages 的简单图状态的示例

import { BaseMessage } from "@langchain/core/messages";
import { Annotation } from "@langchain/langgraph";

const GraphAnnotation = Annotation.Root({
  // Define a 'messages' channel to store an array of BaseMessage objects
  messages: Annotation<BaseMessage[]>({
    // Reducer function: Combines the current state with new messages
    reducer: (currentState, updateValue) => currentState.concat(updateValue),
    // Default function: Initialize the channel with an empty array
    default: () => [],
  })
});

每个通道都可以选择包含 reducerdefault 函数:- reducer 函数定义新值如何与现有状态组合。- default 函数为通道提供初始值。

有关 reducer 的更多信息,请参阅reducer 概念指南

const QuestionAnswerAnnotation = Annotation.Root({
  question: Annotation<string>,
  answer: Annotation<string>,
});

上面,我们所做的只是定义通道,然后将未实例化的 Annotation 函数作为值传递。需要注意的是,我们总是将每个通道的 TypeScript 类型作为 Annotation 的第一个泛型参数传入。这样做可以确保我们的图状态是类型安全的,并且在定义节点时可以获得正确的类型。下面展示了如何从 Annotation 函数中提取类型

type QuestionAnswerAnnotationType = typeof QuestionAnswerAnnotation.State;

这等同于以下类型

type QuestionAnswerAnnotationType = {
  question: string;
  answer: string;
}

合并状态

如果您有两个图状态注释,您可以通过使用 spec 值将两者合并为一个注释

const MergedAnnotation = Annotation.Root({
  ...QuestionAnswerAnnotation.spec,
  ...GraphAnnotation.spec,
})

合并后的注释类型是两个注释的交集

type MergedAnnotation = {
  messages: BaseMessage[];
  question: string;
  answer: string;
}

最后,使用注释实例化您的图就像将注释传递给 StateGraph 构造函数一样简单

import { StateGraph } from "@langchain/langgraph";

const workflow = new StateGraph(MergedAnnotation);

状态通道

Annotation 函数是 LangGraph 中状态定义底层实现的一个便利包装器。虽然不推荐用于大多数情况,但仍可以使用 channels 对象(即 Annotation 的包装对象)定义状态。下面的示例展示了如何使用此模式实现图

import { StateGraph } from "@langchain/langgraph";

interface WorkflowChannelsState {
  messages: BaseMessage[];
  question: string;
  answer: string;
}

const workflowWithChannels = new StateGraph<WorkflowChannelsState>({
  channels: {
    messages: {
      reducer: (currentState, updateValue) => currentState.concat(updateValue),
      default: () => [],
    },
    question: null,
    answer: null,
  }
});

上面,我们将 questionanswer 的值设置为 null,因为它不包含默认值。要设置默认值,通道应该像 messages 键那样实现,default 工厂返回默认值。reducer 函数是可选的,如果需要可以添加到通道对象中。

使用 Zod

如果您想为您的状态添加运行时验证,可以使用 Zod 而不是 Annotation 函数来定义状态。您还可以通过导入 @langchain/langgraph/zod 传入自定义的 reducerdefault 工厂,这将使用 LangGraph 特定的方法扩展 Zod。

import "@langchain/langgraph/zod";
import { z } from "zod";

const AgentState = z.object({
  messages: z
    .array(z.string())
    .default(() => [])
    .langgraph.reducer(
      (a, b) => a.concat(Array.isArray(b) ? b : [b]),
      z.union([z.string(), z.array(z.string())])
    ),
  question: z.string(),
  answer: z.string().min(1),
});

const graph = new StateGraph(AgentState);