创建日期:2026-09-14 | 最近更新:2026-09-14 版本基线:LangChain.js 1.5.11 / @langchain/core 1.2.11 / @langchain/langgraph 1.4.15(2026-09 npm 核对)。本系列全部代码本机真实运行,走 DeepSeek 的 Anthropic 兼容端点(
api.deepseek.com/anthropic,模型deepseek-v4-flash)。
LangChain.js 与 LangGraph.js 入门 0:它俩是什么关系
一句话:LangChain.js 是「组件库」(模型、提示、解析、工具、检索……每样给你一个标准件,再用 LCEL 管起来);LangGraph.js 是「编排引擎」(把多步流程画成带状态、可循环、可持久化的图)。
两者同一团队,常一起用,但解决的问题不同:LangChain 让「一次模型调用/一条链」变简单;LangGraph 让「多轮、带状态、要分支和记忆的 Agent」变得可控。如果你已经跟着本站的 frontend-agent 系列 手写过工具循环,这个系列会让你看清:框架到底把你手写的哪部分接了过去。
1. 先分清两者的边界
| LangChain.js | LangGraph.js | |
|---|---|---|
| 定位 | 组件 + 组合(LCEL) | 有状态图编排 |
| 核心抽象 | Runnable(可 invoke/batch/stream) | StateGraph(状态 + 节点 + 边) |
| 擅长 | 单次调用、提示模板、结构化输出、工具调用 | 多步循环、条件分支、记忆/持久化、人工介入 |
| 能循环吗 | 不建议(链是 DAG) | 能,这是它的主业 |
| 典型用途 | 「问一句、抽个 JSON、调个工具」 | 「像 Agent 那样多轮干一件事」 |
一个记忆锚点:LangChain 的「链」是有向无环的(DAG);Agent 需要环(loop),所以要有 LangGraph。 你手写过的 while (有 tool_use) { 执行; 回填 } ——LangGraph 把这个 while 变成图里的一条回边。
2. 安装与环境
npm i langchain @langchain/core @langchain/langgraph @langchain/anthropic zod
@langchain/core:核心抽象(消息、Runnable、工具)——很多包都依赖它;langchain:更高层的整合包(预置 agent、工具集等);@langchain/langgraph:图引擎;@langchain/anthropic:模型接入(本系列用它连「Anthropic 兼容端点」);zod:给工具/结构化输出定义 schema(呼应本站 Zod 入门)。- Node:
langchain要 Node ≥ 20,langgraph要 ≥ 18——按 20+ 准备。
3. 接入模型:一个实例,处处可用
// model.mjs —— 全系列共用
import { ChatAnthropic } from '@langchain/anthropic';
const BASE = (process.env.ANTHROPIC_BASE_URL || 'https://api.deepseek.com/anthropic').replace(/\/+$/, '');
export const model = new ChatAnthropic({
model: process.env.ANTHROPIC_MODEL || 'deepseek-v4-flash',
apiKey: process.env.ANTHROPIC_AUTH_TOKEN,
anthropicApiUrl: BASE, // ← 换成你的兼容端点即可(Claude 官方则不用传)
temperature: 0,
maxTokens: 1024, // ← 见下面的坑
});
最小验证:
import { model } from './model.mjs';
const r = await model.invoke('请只回复两个字:pong');
console.log('text =', JSON.stringify(r.text));
实测输出:
text = "pong"
⚠️ 两个真实踩到的坑(都是推理模型带来的)
maxTokens太小时,token 全被thinking吃掉、拿不到正文。 实测把maxTokens设 64,返回的content里只有 thinking 块,text是空串;设 1024 就有正常文本。content是「块数组」,不是字符串。 推理模型的回复形如:
content 块类型: thinking,text
r.text = "pong"
所以别直接打印 r.content(你会看到一坨 thinking),用 r.text 取正文(LangChain 已帮你把 text 块拼好)。
4. 看一眼 LangGraph:最小状态图
先建立「图」的直觉——两个节点,一条流水线:
import { StateGraph, Annotation, START, END } from '@langchain/langgraph';
const S = Annotation.Root({ n: Annotation({ reducer: (a, b) => b, default: () => 0 }) });
const g = new StateGraph(S)
.addNode('inc', (s) => ({ n: s.n + 1 })) // 节点:接收状态,返回状态的“增量”
.addNode('double', (s) => ({ n: s.n * 2 }))
.addEdge(START, 'inc') // 边:START → inc → double → END
.addEdge('inc', 'double')
.addEdge('double', END)
.compile();
console.log(await g.invoke({ n: 5 }));
实测输出:
{"n":12} // (5+1)*2 = 12
看懂两个词就够了:节点(node)是函数,边(edge)是顺序;Annotation 定义「状态长什么样」。剩下的(条件边、循环、记忆)全都建立在这三件东西上。
5. 本系列路线
| 篇 | 你会得到 |
|---|---|
| 1 LangChain 核心 | 消息类型、提示模板、LCEL 管道、结构化输出(含推理模型的限制与替代方案) |
| 2 LangGraph 入门 | 状态/reducer、条件边、循环、MemorySaver 记忆 |
| 3 实战 ReAct Agent | 用 createReactAgent 搭一个真会调工具的 Agent,并与手写循环对照 |
| 4 LangGraph 图解 | 如果 2 没看懂看这篇:mermaid 画图 + 逐步执行轨迹,把「状态怎么合并、循环怎么转」摊开讲 |
关联:手写版循环看 frontend-agent 2/3;模型协议看 llm-format;工具协议看 MCP。
动手
- 装好依赖,跑通 §3 的 pong;
- 把
maxTokens改成 64 再跑一次,亲眼看看「只有 thinking、没有 text」; - 跑通 §4 的最小图,改成
(n+2)*3看结果。
自测
- LangChain 和 LangGraph 各解决什么问题?
- 为什么「链」不适合做 Agent 循环?
- 推理模型的回复里,
thinking和text怎么区分? maxTokens太小会怎样?- LangGraph 里状态是由什么定义的?