深入浅出 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.x
npm --version # 期望: 10.x.x
npx tsc --version # 期望: Version 5.x.x
如果 Node.js 版本低于 18,请先升级。
⚠️ 注意:如果你看到类似 SyntaxError: Cannot use import statement 的错误,通常是因为 Node.js 版本过低或不支持 ES Modules。
二、创建项目
1、初始化项目结构
# 创建项目目录
mkdir my-langgraph-agent
cd my-langgraph-agent
# 初始化 npm 项目
npm init -y
# 安装 TypeScript 和开发工具
npm install -D typescript ts-node @types/node dotenv
代码解读:
npm init -y:快速创建 package.json,跳过所有交互问询typescript:TypeScript 编译器核心ts-node:直接运行 .ts 文件,无需手动编译@types/node:Node.js 的类型定义文件dotenv:从 .env 文件加载环境变量
生成 TypeScript 配置:
npx tsc --init
2、配置 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 核心包
npm install @langchain/langgraph
# LangChain 核心(LangGraph 依赖)
npm install @langchain/core
# OpenAI 集成(或其他 LLM)
npm install @langchain/openai
# Zod:用于结构化输出(后续章节会用到)
npm install zod
代码解读:
@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 Key
LANGCHAIN_API_KEY=ls-your-langsmith-key
# 项目名称,用于在 LangSmith 中组织追踪记录
LANGCHAIN_PROJECT=my-langgraph-project
2、添加 .gitignore
⚠️ 极其重要:绝对不能把 API 密钥提交到代码仓库!
# .gitignore
node_modules/
dist/
.env
*.env.local
.DS_Store
✅ 验证:运行 git status,确认 .env 文件没有出现在待提交列表中。
3、创建环境验证脚本
创建 src/check-env.ts:
// 加载 .env 文件中的变量到 process.env
import * as dotenv from 'dotenv';
dotenv.config();
function checkEnv() {
const required = ['OPENAI_API_KEY'];
// 检查必需变量是否都已设置
const missing = 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 * as dotenv from 'dotenv';
dotenv.config();
import { StateGraph, MessagesAnnotation } from '@langchain/langgraph';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, AIMessage } from '@langchain/core/messages';
// MessagesAnnotation 是 LangGraph 内置的消息状态注解
// 它帮我们自动管理对话历史消息列表
const model = new ChatOpenAI({
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 节点:接收状态,返回更新后的状态
async function chatbotNode(state: typeof MessagesAnnotation.State) {
// 把所有历史消息传给 LLM,实现多轮对话
// state.messages包含当前对话的完整消息历史
const response = await model.invoke(state.messages);
// 返回新增的 AI 消息,LangGraph 会自动追加到 messages 数组
return { messages: [response] };
}
步骤 4:构建图并运行
// 构建 StateGraph
const graph = new StateGraph(MessagesAnnotation)
.addNode('chatbot', chatbotNode) // 添加节点
.addEdge('__start__', 'chatbot') // 起点 → chatbot
.addEdge('chatbot', '__end__') // chatbot → 终点
.compile(); // 编译成可执行图
代码解读:
StateGraph(MessagesAnnotation):用消息注解创建图,自动处理消息合并addNode:注册节点,第一参数是名称,第二参数是处理函数addEdge:添加边,‘start’ 和 ‘end’ 是内置的起止节点compile():将图定义编译成可执行的 Runnable 对象, 这一步非常关键,未编译的图无法执行
主运行函数:
// 主运行函数
async function main() {
console.log('🤖 AI Agent 已启动,输入问题开始对话\n');
// 传入初始状态,触发图的执行
const result = await graph.invoke({
messages: [new HumanMessage('你好!请介绍一下你自己,并说明你能做什么。')],
});
// 打印最后一条 AI 回复
const lastMessage = result.messages[result.messages.length - 1] as AIMessage;
console.log('AI:', lastMessage.content);
}
// 捕获并打印异步错误,防止未处理的 Promise rejection
main().catch(console.error);
步骤 5:运行 Agent
npx ts-node src/first-agent.ts
期望输出:
🤖 AI Agent 已启动,输入问题开始对话
AI: 你好!我是一个AI助手,基于大型语言模型构建。我能够:
- 回答各种问题和解释概念
- 协助编写和优化代码
- 进行多轮对话,记住上下文
- 提供分析和建议
...
🎉 恭喜!你已经成功运行了第一个 LangGraph Agent!
五、升级为多轮对话
单次问答并不够有趣,让我们升级成真正的多轮对话:
import * as readline from 'readline';
async function multiTurnChat() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
let messages: HumanMessage[] = [];
console.log('🤖 多轮对话 Agent 启动!输入 exit 退出\n');
const askQuestion = () => {
rl.question('你: ', async (userInput) => {
if (userInput.toLowerCase() === 'exit') {
console.log('再见!👋');
rl.close();
return;
}
messages.push(new HumanMessage(userInput));
const result = await graph.invoke({ messages });
messages = result.messages; // 保存完整历史
const lastMsg = messages[messages.length - 1] as AIMessage;
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 —— 绝对禁止!
const model = new ChatOpenAI({ apiKey: 'sk-abc123...' });
✅ 推荐做法:
// 程序入口处立即加载 .env
import * as dotenv from 'dotenv';
dotenv.config(); // 必须在其他 import 之前或程序最顶部调用
const model = new ChatOpenAI({
apiKey: process.env.OPENAI_API_KEY
});
原因:硬编码的密钥极易通过代码仓库泄露,造成严重安全风险和经济损失。
💡 实践 2:模型版本的显式声明
❌ 不好的做法:
const model = new ChatOpenAI(); // 使用默认模型,版本不透明
✅ 推荐做法:
const model = new ChatOpenAI({
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 能优雅降级而非崩溃
📚 延伸阅读
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)