写作归档

LangChain/LangGraph

类型
文章
日期
2026/10/15
阅读
8 分钟
字数
2,146

1. 概述

通过上一章的学习,我们知道,优秀的 AI Agent 需要一个强大的内存调度架构。而 langChain 和 langGraph 则为此提供了具体的抽象能力,是构建 AI 伴侣 Agent 的得力助手

当然,langChain/langGraph* 并非构建 Agent 的必备项,当我们的 Agent 比较简单时,我们可以自己轻松处理任务节点的编排,之所以选择 langChain/langGraph 是因为我们预设了我们的 Agent 在未来要面对复杂的编排和复杂的调度逻辑*

假设用户向 AI 伴侣发送了一句话:

你还记得我最喜欢什么蛋糕吗?

系统要给出自然且可信的回答,通常需要完成几项工作:理解用户正在询问过去的偏好,查询长期记忆,把检索结果和最近对话整理成上下文,调用模型生成回复,并把这一轮产生的新状态保存下来。

我们可以从需求的发展历程来理解 LangChain 和 LangGraph 的定位

刚开始开发时,需求非常简单,这些步骤可以 仅仅写在一个请求函数里

但是,当越来越多类似的需求出现时,我们可能就会考虑将这些步骤进行抽象,并形成一个一个可复用的功能模块,并在此基础之上快速组装出新的 Agent,我们需要统一接入模型、定义工具,组织功能节点之间的先后执行顺序,这个时候,我们就可以用到 langChain

继续发展下去,系统还会出现条件路由、跨请求状态、节点中断和恢复执行等需求。这时问题已经从 怎样组合能力 变成 怎样控制流程 ,LangGraph 则为这种显式调度提供了具体实现

2. 链式调用

链式调用是 langChain 提供的一种非常基础的能力,它允许我们通过 pipe() 方法将多个 Runnable 连接在一起,形成一个链式调用。

chain.ts

import { StringOutputParser } from '@langchain/core/output_parsers'
import { createDemoChatModel } from '../_shared/create-demo-chat-model'
import { companionPrompt } from './prompt'

interface RunCompanionChainOptions {
  question: string
  settings: any
  abortSignal: AbortSignal
}

export async function runCompanionChain({ question, settings, abortSignal }: RunCompanionChainOptions) {
  const model = await createDemoChatModel(settings)
  const parser = new StringOutputParser()
  // !mark
  const chain = companionPrompt.pipe(model).pipe(parser)

  const content = await chain.invoke(
    { question }, // 为用户的输入,由 UI 组件传入
    { signal: abortSignal },
  )

  return { content, model: settings.modelId }
}

prompt.ts

import { ChatPromptTemplate } from '@langchain/core/prompts'

export const companionPrompt = ChatPromptTemplate.fromMessages([
  ['system', '你是一名温和、自然的陪伴型助手。'],
  ['human', '{question}'],
])

pipe() 连接的是三个节点。前一个节点的输出会成为后一个节点的输入,因此调用方只需要向整条链提供初始参数。

这里所说的固定,指的是执行节点流程的固定,而不是模型每次都会返回完全相同的文字。无论用户输入什么,数据都会按照 Prompt → Model → Parser 的顺序向后流动;链本身不会临时决定跳过 Parser,也不会自行插入一个工具节点

这种结构适合 Prompt 格式化、文本改写、摘要、分类和结构化解析等步骤明确的任务。链也可以携带历史消息和使用流式输出,但这些能力不会改变它的固定执行关系。

下面的案例把最近对话放入 MessagesPlaceholder,再通过 prompt.pipe(model).pipe(parser) 生成回复。你可以通过这个案例观察链式节点的固定调用

连续发送两条消息可以观察历史如何进入下一轮;模型选择器右侧的重置按钮可以清空这个案例的对话历史

3、createAgent

当我们无法预先固定执行节点流程时,单向链式调用就无法满足需求了。比如,当用户的输入是:

你还记得我最喜欢什么蛋糕吗?

我们需要去查询历史记忆,并根据查询到的结果生成回复。但是如果用户输入的是:

睡前给我一句简短的晚安问候。

那我们在实现上,就不应该去查询历史记忆,而是应该直接生成回复

在 langChain 中,我们可以通过 createAgent() 来决定是否调用工具。如下案例所示,观察 2 次不同的输入,节点执行的流程是否相同

agent.ts

import { createAgent } from 'langchain'
import { searchMemory } from './search-memory-tool'

const agent = createAgent({
  model: 'deepseek-v4-flash',
  tools: [searchMemory],
  systemPrompt: `你是一名陪伴型助手。
当用户询问过去的偏好或共同经历时,先调用记忆工具核实,不要猜测。`,
})

const result = await agent.invoke({
  messages: [{
    role: 'user',
    content: '你还记得我最喜欢什么蛋糕吗?',
  }],
})

search-memory-tool.ts

import { tool } from 'langchain'
import { z } from 'zod'

export const searchMemory = tool(
  async ({ query }) => {
    if (query.includes('蛋糕')) {
      return '用户最喜欢巧克力榛子蛋糕,不喜欢草莓奶油。'
    }

    return '没有找到相关记忆。'
  },
  {
    name: 'search_memory',
    description: '查询与当前用户有关的长期记忆',
    schema: z.object({
      query: z.string().describe('需要检索的记忆主题'),
    }),
  },
)

tool() 把普通函数转换成模型能够理解的工具。名称和描述帮助模型判断工具用途,Zod Schema 则约束调用参数。

这段循环是 createAgent() 的行为,不是所有 langChain 程序的默认形态。langChain* 的 createAgent() 底层使用 langGraph 的运行时,但开发标准工具型 Agent 时,通常只需要使用 langChain 提供的高层 API。只有当业务需要显式控制节点、条件分支、中断和恢复时,才需要直接进入 langGraph*。

4. LangGraph

注意观察上面的代码,你会发现,是否调用 searchMemory 工具,是由模型通过提示词来决定的,而不是基于我们的代码逻辑决定的

但是更多时候,我们做的 Agent 产品要求某些步骤和分支必须由业务规则控制,那这个时候,就需要把处理过程显式建模出来,这就是 langGraph 的用武之地了。

angGraph 使用三个核心概念描述这类流程:

  • State 是当前工作流的数据快照,节点通过它交换消息和业务状态
  • Node 是执行单元,可以调用模型、数据库、工具或普通业务函数
  • Edge 描述节点之间的流转关系,可以是固定连接,也可以根据 State 动态选择分支

利用 langGraph,我们可以对流程进行更精细的控制,例如随时可以中断正在执行的节点,中断之后,还可以从当前中断的节点继续执行,而不是从头开始重新执行

下面这个案例是一个可以被中断的案例,当用户输入

你还记得我最喜欢什么蛋糕吗?

执行流程我们拆成四个明确节点:安全检测、意图识别、长期记忆查询和回复。长期记忆工具由 langChain tool() 定义,当前返回一条写死的模拟记忆;langGraph 负责节点顺序、条件路由和 checkpoint

案例默认不会预设固定的中断节点。用户不做额外操作时,工作流会依次执行并自然到达最终回复;执行过程中主动点击输入框最右侧的停止按钮,案例会终止当前 graph run,并从 Checkpointer 读取仍在 next 中的当前节点。时间线把这个节点标记为“执行中断”,

中断之后,当用户输入 继续执行 时,langGraph 会携带前序节点已经写入 State 的安全结果、意图和记忆,从被中断的节点重新执行,而不是创建一张新图

完整案例代码逻辑非常复杂,这里就不再一一展示代码,后续章节会详细介绍 langGraph 的用法

5. 总结

LangChain 和 LangGraph 处在模型应用技术栈的不同层级。LangChain 通过 Runnable 与 LCEL 组合固定的数据处理链,也通过 createAgent() 提供预构建的工具调用循环;LangGraph 提供状态与工作流运行时,让开发者精确控制节点、分支、循环、持久化和恢复过程。

步骤固定的模型管线可以从 Runnable 与 LCEL 开始,标准工具调用可以从 createAgent() 开始。当记忆检索、情绪更新、安全检查或人工确认需要按照明确的业务顺序执行时,再使用 StateGraph 描述完整流程。外层图与节点内 Agent 也可以组合,从而兼顾整体流程的确定性与局部任务的灵活性

0 条

还没有评论。

发表评论