📖 本章学习目标

  • ✅ 掌握 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 框架核心,提供 StateGraphAnnotation
  • @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 生成回复
  • 保持对话状态(记住上下文)

👤 用户输入

chatbot 节点
(调用 LLM)

AI 回复

继续对话?

结束

步骤 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_KEYundefined 确保 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)
  • 要求:
    1. 安装 @langchain/anthropic
    2. 修改模型初始化代码,使用 ChatAnthropic
    3. .env 中添加 ANTHROPIC_API_KEY
  • 验收标准:Agent 能正常对话,返回 Claude 的回复风格

练习 2:添加系统提示词

  • 目标:给 Agent 设定一个角色(如"你是一个代码助手")
  • 要求:
    1. messages 数组首位插入 SystemMessage
    2. SystemMessage 内容描述 Agent 的角色和行为准则
    3. 测试不同角色的效果差异
  • 验收标准:Agent 的回复风格符合设定的角色,如代码助手会主动提供代码示例

练习 3:多轮对话历史限制

  • 目标:防止 Token 超限,只保留最近 10 条消息
  • 要求:
    1. 在调用 graph.invoke 前,截取 messages 数组的最后 10 条
    2. 添加日志输出当前消息数量
    3. 测试长对话场景(超过 10 轮)
  • 验收标准:长对话不报错,且 Agent 仍能正常回复,Token 消耗可控

练习 4:错误处理增强

  • 目标:为 Agent 添加完善的错误处理
  • 要求:
    1. 捕获 API 调用失败(网络错误、配额不足等)
    2. 提供友好的错误提示
    3. 实现重试机制(最多 3 次)
  • 验收标准:模拟网络故障时,Agent 能优雅降级而非崩溃

📚 延伸阅读


下一章:第3章 —— 图的核心概念:节点、边与状态

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐