项目实战
🏗️ 项目实战:做一个可用的 LangGraph.js 应用
把教程里的概念串起来:从图、工具、记忆到部署,完成一个能长期运行的 AI 应用
📚 学习目标
学完这篇文章后,你将能够:
- 把 LangGraph.js 的核心概念落到一个真实项目的目录结构里
- 设计一个“用户输入 -> Graph 编排 -> 工具/模型 -> 输出”的端到端链路
- 为应用加入可恢复的会话(thread_id)和持久化(checkpointer)
- 为上线准备最基本的测试、可观测性与回归验证清单
前置知识
在开始学习之前,建议先阅读:
你需要了解:
- Next.js 基础(路由/接口/环境变量)
- TypeScript 基础
1️⃣ 选一个“可交付”的项目题目
为了让实战更像真实工作,我们先明确“交付物”。建议从这三类里选一个:
| 项目类型 | 你要解决的问题 | LangGraph.js 的价值 |
|---|---|---|
| 聊天机器人 | 多轮对话 + 工具调用 + 可恢复会话 | Loop + 状态 + ToolNode |
| RAG 问答 | 文档检索 + 引用 + 追问 | 分支/子图 + 结构化状态 |
| 编码助手 | 规划 -> 生成 -> 评审 -> 返工 | 多节点闭环 + 多 Agent 协作 |
本教程的推荐落地路径是:先做“聊天机器人”跑通全链路,再按需升级到 RAG 或编码助手。
2️⃣ 目录结构:把 Graph 当成“业务编排层”
一个好用的项目通常会把“编排(Graph)”和“Web 接口(API)”解耦。
app/
api/
chat/
route.ts # HTTP 入口:把请求交给 graph 执行
lib/
graphs/
chat.graph.ts # Graph 构建与 compile()
tools/
weather.tool.ts # Tool 定义(zod schema + 实现)
llm/
model.ts # 统一的模型创建与配置这样做的好处:
- Graph 可以独立测试(不依赖 HTTP)
- API 只做“参数校验/鉴权/日志/流式输出”,不耦合业务编排
3️⃣ 最小可用:一个能跑的 Chat Graph
下面是一个“能跑”的最小版本:一个 LLM 节点 + 记忆(messages state)。
import { StateGraph, START, END } from '@langchain/langgraph';
import { MessagesAnnotation } from '@langchain/langgraph';
import { ChatOpenAI } from '@langchain/openai';
const model = new ChatOpenAI({
model: 'gpt-4o-mini',
temperature: 0,
});
const callModel = async (state: typeof MessagesAnnotation.State) => {
const reply = await model.invoke(state.messages);
return { messages: [reply] };
};
export const chatGraph = new StateGraph(MessagesAnnotation)
.addNode('model', callModel)
.addEdge(START, 'model')
.addEdge('model', END)
.compile();代码解析:
MessagesAnnotation提供了“消息列表”的默认 reducer(用于追加历史)。- 节点返回
{ messages: [reply] },LangGraph 会把它合并回状态。 - 这就是一个最简单的“状态机”:每次调用都执行一次模型,然后结束。
4️⃣ 加上工具调用:让它能“行动”
当你需要查询天气、查数据库、跑脚本……就需要 Tool Calling。
高层结构通常是 ReAct Loop:
你可以直接参考本教程的“工具调用”章节,把 ToolNode 接进来:
5️⃣ 加上记忆与持久化:让它“可恢复”
做成应用后,你会立刻遇到一个现实问题:
- 页面刷新/服务重启后,对话就没了
LangGraph 的解决方案是 Checkpointer + thread_id。
import { MemorySaver } from '@langchain/langgraph';
const checkpointer = new MemorySaver();
export const appGraph = chatGraph; // 这里假设你的 chatGraph 已经构建好
// 注意:compile 时需要传 checkpointer
// const appGraph = new StateGraph(...).compile({ checkpointer })
const config = {
configurable: {
thread_id: 'user-123:session-456',
},
};
const result = await appGraph.invoke(
{ messages: [{ role: 'user', content: '我叫小明' }] },
config
);更完整的解释与生产环境替换方案见:
6️⃣ 交付前检查:你至少要能回答这 6 个问题
这不是“模板化流程”,而是为了避免你把 Demo 当产品。
- 输入边界:最长输入多少?超长时怎么处理?
- 失败路径:模型超时/工具报错/JSON 不合法时,用户看到什么?
- 并发与隔离:多个用户同时聊天,thread_id 是否会串?
- 成本控制:一次请求最多跑几轮循环?是否设置了
recursionLimit? - 可观测性:你能定位一次糟糕回复是“模型问题”还是“工具问题”吗?
- 回归验证:你改了一个节点逻辑,怎么确保没把别的用例弄坏?
💡 练习题
- 实现题:基于“工具调用”章节,新增一个
searchDocs工具,并把它接进你的 ReAct Loop。 - 调试题:人为让工具抛错(throw Error),你要在最终回复中给出“可理解”的降级提示。
- 工程题:设计一个最小的回归用例:固定输入,断言 Graph 输出里必须包含某个关键字段(比如引用列表)。
📚 参考资源
官方文档
本项目相关内容
✅ 总结
你现在应该能做到:
- 以“Graph”为中心组织项目,把编排层和接口层分离
- 在项目中落地 Tool Calling、循环、记忆与持久化
- 用一套最小的交付检查清单把 Demo 推到可上线的方向
下一步:去把你的项目跑起来,然后回到对应章节逐个补齐能力(人机交互、时间旅行、并行处理等)。
登录以继续阅读
解锁完整文档、代码示例及更多高级功能。