深入浅出 LangGraph —— 第2章:环境搭建与第一个Agent
📖 本章学习目标
- ✅ 掌握 LangGraph TypeScript 开发环境的完整搭建流程
- ✅ 理解项目依赖结构与版本管理策略
- ✅ 学会安全管理 API 密钥与环境变量
- ✅ 构建并运行第一个可对话的 AI Agent
- ✅ 理解 StateGraph 的基本工作模式
- ✅ 能够独立排查常见环境配置问题
一、开发环境准备
1、环境要求
在开始之前,确保你的机器满足以下要求:
| 工具 | 最低版本 | 推荐版本 | 说明 |
|---|---|---|---|
| Node.js | 20.x | 22.x LTS | 支持 ES Modules |
| npm / pnpm | npm 9+ | pnpm 8+ | pnpm 更快更省空间 |
| TypeScript | 5.0+ | 5.4+ | 严格模式支持 |
| VS Code | 任意 | 最新版 | 推荐 IDE |
💡小贴士:推荐使用 nvm(macOS/Linux)或 nvm-windows 管理 Node.js 版本,便于多项目切换。Windows下更推荐使用fnm。
2、检查现有环境
打开终端,运行以下命令验证环境:
node--version# 期望: v20.x.xnpm--version# 期望: 10.x.xnpx tsc--version# 期望: Version 5.x.x如果 Node.js 版本低于 18,请先升级。
⚠️注意:如果你看到类似SyntaxError: Cannot use import statement的错误,通常是因为 Node.js 版本过低或不支持 ES Modules。
二、创建项目
1、初始化项目结构
# 创建项目目录mkdirmy-langgraph-agentcdmy-langgraph-agent# 初始化 npm 项目npminit-y# 安装 TypeScript 和开发工具npminstall-Dtypescript ts-node @types/node dotenv代码解读:
npm init -y:快速创建 package.json,跳过所有交互问询typescript:TypeScript 编译器核心ts-node:直接运行 .ts 文件,无需手动编译@types/node:Node.js 的类型定义文件dotenv:从 .env 文件加载环境变量
生成 TypeScript 配置:
npx tsc--init2、配置 tsconfig.json
用以下内容完整替换自动生成的tsconfig.json:
{"compilerOptions":{"target":"ES2022","module":"CommonJS","lib":["ES2022"],"outDir":"./dist","rootDir":"./src","strict":true,"esModuleInterop":true,"skipLibCheck":true,"resolveJsonModule":true,"forceConsistentCasingInFileNames":true},"include":["src/**/*"],"exclude":["node_modules","dist"]}代码解读:
target: ES2022:使用现代 JS 特性(async/await、可选链等)module: CommonJS:Node.js 默认模块系统strict: true:开启所有严格类型检查,推荐生产项目必开esModuleInterop: true:允许 import xxx from ‘xxx’ 导入 CommonJS 模块skipLibCheck: true:跳过第三方库的类型检查,加快编译速度resolveJsonModule: true:允许直接 import JSON 文件forceConsistentCasingInFileNames: true:强制文件名大小写一致
💡小贴士:如果你是初学者,可以先设置
"strict": false减少类型错误干扰,等项目跑通后再逐步开启严格模式。
3、安装 LangGraph 核心依赖
# LangGraph 核心包npminstall@langchain/langgraph# LangChain 核心(LangGraph 依赖)npminstall@langchain/core# OpenAI 集成(或其他 LLM)npminstall@langchain/openai# Zod:用于结构化输出(后续章节会用到)npminstallzod代码解读:
@langchain/langgraph:LangGraph 框架核心,提供StateGraph、Annotation等@langchain/core:LangChain 基础抽象,LangGraph 内部依赖@langchain/openai:OpenAI 模型的 LangChain 适配器zod:TypeScript 优先的 schema 验证库,用于结构化 LLM 输出
⚠️注意:
@langchain/langgraph会自动安装兼容版本的@langchain/core,但建议手动显式声明依赖,避免版本冲突。
如果你想使用其他模型提供商(如 Anthropic、Google Gemini),可以安装对应的包:
@langchain/anthropic- Claude 系列模型@langchain/google-genai- Gemini 系列模型@langchain/cohere- Cohere 模型
三、配置 API 密钥
1、创建 .env 文件
在项目根目录创建.env文件:
# .env# OpenAI 官网申请,用于调用 GPT 系列模型OPENAI_API_KEY=sk-your-openai-api-key-here# 国内用户常需要配置代理地址OPENAI_BASE_URL=https://api.openai.com/v1# 可选,国内代理时使用# 可选:LangSmith 追踪(调试神器,第16章详细介绍)LANGCHAIN_TRACING_V2=true# LangSmith 平台的 API KeyLANGCHAIN_API_KEY=ls-your-langsmith-key# 项目名称,用于在 LangSmith 中组织追踪记录LANGCHAIN_PROJECT=my-langgraph-project2、添加 .gitignore
⚠️极其重要:绝对不能把 API 密钥提交到代码仓库!
# .gitignorenode_modules/ dist/ .env *.env.local .DS_Store✅验证:运行git status,确认.env文件没有出现在待提交列表中。
3、创建环境验证脚本
创建src/check-env.ts:
// 加载 .env 文件中的变量到 process.envimport*asdotenvfrom'dotenv';dotenv.config();functioncheckEnv(){constrequired=['OPENAI_API_KEY'];// 检查必需变量是否都已设置constmissing=required.filter(key=>!process.env[key]);if(missing.length>0){console.error('❌ 缺少必要的环境变量:',missing.join(', '));process.exit(1);// 非零退出码表示错误,方便 CI/CD 检测}console.log('✅ 环境变量配置正确!');// 只显示 Key 前缀,避免泄露完整密钥console.log('API Key 前缀:',process.env.OPENAI_API_KEY?.slice(0,7)+'...');}checkEnv();运行验证:
npx ts-node src/check-env.ts期望输出:
✅ 环境变量配置正确! API Key 前缀: sk-proj...四、构建第一个 Agent
现在进入最激动人心的部分——构建你的第一个 LangGraph Agent!
步骤 1:理解目标
我们要构建一个简单的对话 Agent,它能够:
- 接收用户输入的问题
- 调用 LLM 生成回复
- 保持对话状态(记住上下文)
步骤 2:定义 State
创建src/first-agent.ts,首先定义状态:
import*asdotenvfrom'dotenv';dotenv.config();import{StateGraph,MessagesAnnotation}from'@langchain/langgraph';import{ChatOpenAI}from'@langchain/openai';import{HumanMessage,AIMessage}from'@langchain/core/messages';// MessagesAnnotation 是 LangGraph 内置的消息状态注解// 它帮我们自动管理对话历史消息列表constmodel=newChatOpenAI({model:'gpt-4o-mini',temperature:0.7,apiKey:process.env.OPENAI_API_KEY,});代码解读:
MessagesAnnotation:LangGraph 预定义的状态注解,内含messages字段,代表消息历史数组,自动处理消息的追加逻辑ChatOpenAI:OpenAI 聊天模型的 LangChain 封装temperature: 0.7:模型温度,用于控制创造性,0=确定性,1=最随机apiKey: process.env.OPENAI_API_KEY:从环境变量读取,避免硬编码
步骤 3:定义节点函数
// 定义 chatbot 节点:接收状态,返回更新后的状态asyncfunctionchatbotNode(state:typeofMessagesAnnotation.State){// 把所有历史消息传给 LLM,实现多轮对话// state.messages包含当前对话的完整消息历史constresponse=awaitmodel.invoke(state.messages);// 返回新增的 AI 消息,LangGraph 会自动追加到 messages 数组return{messages:[response]};}步骤 4:构建图并运行
// 构建 StateGraphconstgraph=newStateGraph(MessagesAnnotation).addNode('chatbot',chatbotNode)// 添加节点.addEdge('__start__','chatbot')// 起点 → chatbot.addEdge('chatbot','__end__')// chatbot → 终点.compile();// 编译成可执行图代码解读:
StateGraph(MessagesAnnotation):用消息注解创建图,自动处理消息合并addNode:注册节点,第一参数是名称,第二参数是处理函数addEdge:添加边,‘start’ 和 ‘end’ 是内置的起止节点compile():将图定义编译成可执行的 Runnable 对象, 这一步非常关键,未编译的图无法执行
主运行函数:
// 主运行函数asyncfunctionmain(){console.log('🤖 AI Agent 已启动,输入问题开始对话\n');// 传入初始状态,触发图的执行constresult=awaitgraph.invoke({messages:[newHumanMessage('你好!请介绍一下你自己,并说明你能做什么。')],});// 打印最后一条 AI 回复constlastMessage=result.messages[result.messages.length-1]asAIMessage;console.log('AI:',lastMessage.content);}// 捕获并打印异步错误,防止未处理的 Promise rejectionmain().catch(console.error);步骤 5:运行 Agent
npx ts-node src/first-agent.ts期望输出:
🤖 AI Agent 已启动,输入问题开始对话 AI: 你好!我是一个AI助手,基于大型语言模型构建。我能够: - 回答各种问题和解释概念 - 协助编写和优化代码 - 进行多轮对话,记住上下文 - 提供分析和建议 ...🎉 恭喜!你已经成功运行了第一个 LangGraph Agent!
五、升级为多轮对话
单次问答并不够有趣,让我们升级成真正的多轮对话:
import*asreadlinefrom'readline';asyncfunctionmultiTurnChat(){constrl=readline.createInterface({input:process.stdin,output:process.stdout,});letmessages:HumanMessage[]=[];console.log('🤖 多轮对话 Agent 启动!输入 exit 退出\n');constaskQuestion=()=>{rl.question('你: ',async(userInput)=>{if(userInput.toLowerCase()==='exit'){console.log('再见!👋');rl.close();return;}messages.push(newHumanMessage(userInput));constresult=awaitgraph.invoke({messages});messages=result.messages;// 保存完整历史constlastMsg=messages[messages.length-1]asAIMessage;console.log('AI:',lastMsg.content,'\n');askQuestion();// 继续对话});};askQuestion();}multiTurnChat().catch(console.error);代码解读:
readline:Node.js 内置模块,用于读取命令行输入messages:在循环中累积对话历史,实现上下文记忆messages = result.messages:每轮对话后更新本地消息历史, 这是实现多轮记忆的关键!- 递归调用
askQuestion():实现持续对话循环
注意:这种方式适合演示,生产环境应使用数据库持久化
运行多轮对话:
npx ts-node src/first-agent.ts示例对话:
🤖 多轮对话 Agent 启动!输入 exit 退出 你: 你好! AI: 你好!很高兴见到你。有什么我可以帮助你的吗? 你: 你能帮我写一段 Python 代码吗? AI: 当然可以!请告诉我你需要什么功能的 Python 代码? 你: 计算斐波那契数列 AI: 这是一个计算斐波那契数列的 Python 函数: def fibonacci(n): if n <= 0: return [] elif n == 1: return [0] ... 你: exit 再见!👋六、最佳实践和踩坑指南
💡 实践 1:环境变量的正确加载方式
❌ 不好的做法:
// 硬编码 API Key —— 绝对禁止!constmodel=newChatOpenAI({apiKey:'sk-abc123...'});✅ 推荐做法:
// 程序入口处立即加载 .envimport*asdotenvfrom'dotenv';dotenv.config();// 必须在其他 import 之前或程序最顶部调用constmodel=newChatOpenAI({apiKey:process.env.OPENAI_API_KEY});原因:硬编码的密钥极易通过代码仓库泄露,造成严重安全风险和经济损失。
💡 实践 2:模型版本的显式声明
❌ 不好的做法:
constmodel=newChatOpenAI();// 使用默认模型,版本不透明✅ 推荐做法:
constmodel=newChatOpenAI({model:'gpt-4o-mini',// 显式声明模型版本temperature:0.7,// 显式声明参数maxTokens:2048,// 控制最大输出长度});原因:默认模型可能随 SDK 版本升级而改变,显式声明确保行为稳定可预期。
💡 实践 3:项目结构的规范化
❌ 不好的做法:
my-project/ ├── agent.ts# 所有代码堆在一个文件├── utils.ts └── .env✅ 推荐做法:
my-project/ ├── src/ │ ├── agent.ts# Agent 定义│ ├── nodes/# 节点函数│ │ ├── chatbot.ts │ │ └── router.ts │ ├── tools/# 自定义工具│ │ └── search.ts │ └── index.ts# 入口文件├── .env ├── .gitignore ├── package.json └── tsconfig.json原因:随着项目复杂度增加,模块化结构能大幅提升可维护性。
⚠️ 常见问题
| 问题 | 现象 | 解决方案 |
|---|---|---|
| dotenv 未在顶部加载 | process.env.OPENAI_API_KEY为undefined | 确保dotenv.config()在所有使用环境变量的代码之前执行 |
| TypeScript 严格模式报错 | 大量类型错误无法运行 | 先设strict: false跑通后逐步修复,或使用类型断言 |
| Node.js 版本过低 | SyntaxError: Cannot use... | 升级至 Node.js 18+,使用nvm use 20 |
| pnpm/npm 混用 | 依赖冲突、幽灵依赖 | 项目统一使用一种包管理器,删除混用的 lock 文件 |
忘记.compile() | 调用invoke()时报错"not a function" | 构建图后必须调用.compile()才能执行 |
| 消息被覆盖 | 对话历史丢失,只保留最后一条 | 使用MessagesAnnotation而非普通数组,它的 reducer 是追加语义 |
📝 本章小结
核心知识点回顾
| 知识点 | 关键要点 | 应用场景 |
|---|---|---|
| 项目初始化 | npm init+tsconfig.json配置 | 所有 TS 项目的起点 |
| 依赖安装 | @langchain/langgraph+@langchain/openai | LangGraph 核心能力 |
| 环境变量 | .env+dotenv+.gitignore | API 密钥安全管理 |
MessagesAnnotation | 内置消息状态注解 | 对话历史管理 |
StateGraph | 图定义 → 编译 → 执行 | 所有 LangGraph 应用的基础 |
| 节点函数 | 接收 State,返回 Partial | 图的处理单元 |
🎯 动手练习
练习 1:更换语言模型
- 目标:将 OpenAI 替换为其他模型(如 Anthropic Claude)
- 要求:
- 安装
@langchain/anthropic - 修改模型初始化代码,使用
ChatAnthropic - 在
.env中添加ANTHROPIC_API_KEY
- 安装
- 验收标准:Agent 能正常对话,返回 Claude 的回复风格
练习 2:添加系统提示词
- 目标:给 Agent 设定一个角色(如"你是一个代码助手")
- 要求:
- 在
messages数组首位插入SystemMessage - SystemMessage 内容描述 Agent 的角色和行为准则
- 测试不同角色的效果差异
- 在
- 验收标准:Agent 的回复风格符合设定的角色,如代码助手会主动提供代码示例
练习 3:多轮对话历史限制
- 目标:防止 Token 超限,只保留最近 10 条消息
- 要求:
- 在调用
graph.invoke前,截取messages数组的最后 10 条 - 添加日志输出当前消息数量
- 测试长对话场景(超过 10 轮)
- 在调用
- 验收标准:长对话不报错,且 Agent 仍能正常回复,Token 消耗可控
练习 4:错误处理增强
- 目标:为 Agent 添加完善的错误处理
- 要求:
- 捕获 API 调用失败(网络错误、配额不足等)
- 提供友好的错误提示
- 实现重试机制(最多 3 次)
- 验收标准:模拟网络故障时,Agent 能优雅降级而非崩溃
📚 延伸阅读
- LangGraph.js 官方文档
- LangGraph.js GitHub 仓库
- OpenAI 模型列表
- dotenv 使用指南
- TypeScript 官方手册
下一章:第3章 —— 图的核心概念:节点、边与状态
