跳到主要内容

📁 应用结构

引言

一个 LangGraph 应用的结构就像一个现代前端项目一样,需要清晰的组织和配置。理解应用结构对于成功部署至关重要,它决定了你的应用如何被构建、配置和运行。

对于前端开发者来说,这就像配置一个 React 或 Vue 项目 - 你需要定义依赖、入口点、环境变量和构建配置。LangGraph 应用结构遵循类似的原则,但专门针对智能代理应用进行了优化。

核心组成要素

一个完整的 LangGraph 应用包含以下关键组件:

项目目录结构

标准项目结构

推荐的项目结构

/**
* LangGraph 应用推荐项目结构
*
* my-langgraph-app/
* ├── src/ # 源代码目录
* │ ├── agent.ts # 主图定义
* │ ├── utils/ # 工具函数
* │ │ ├── config.ts # 配置管理
* │ │ ├── logger.ts # 日志工具
* │ │ └── validation.ts # 数据验证
* │ ├── nodes/ # 节点实现
* │ │ ├── chat.ts # 聊天节点
* │ │ ├── tools.ts # 工具调用节点
* │ │ └── routing.ts # 路由节点
* │ ├── state/ # 状态定义
* │ │ ├── types.ts # 类型定义
* │ │ └── reducers.ts # 状态合并器
* │ └── tools/ # 工具集成
* │ ├── search.ts # 搜索工具
* │ ├── calculator.ts # 计算工具
* │ └── weather.ts # 天气工具
* ├── tests/ # 测试文件
* │ ├── unit/ # 单元测试
* │ └── integration/ # 集成测试
* ├── docs/ # 文档
* ├── .env # 环境变量
* ├── .env.example # 环境变量模板
* ├── .gitignore # Git 忽略文件
* ├── langgraph.json # LangGraph 配置
* ├── package.json # Node.js 依赖
* ├── tsconfig.json # TypeScript 配置
* └── README.md # 项目说明
*/

// 项目结构验证工具
import { existsSync } from 'fs';
import { join } from 'path';

interface ProjectStructure {
required: string[];
recommended: string[];
optional: string[];
}

const LANGGRAPH_PROJECT_STRUCTURE: ProjectStructure = {
required: ['langgraph.json', 'package.json', 'src/', 'src/agent.ts'],
recommended: [
'.env',
'.env.example',
'tsconfig.json',
'src/utils/',
'src/nodes/',
'src/state/',
'src/tools/',
'README.md',
],
optional: [
'tests/',
'docs/',
'.gitignore',
'Dockerfile',
'docker-compose.yml',
],
};

/**
* 验证项目结构
*/
export function validateProjectStructure(projectRoot: string = process.cwd()) {
const results = {
required: [] as string[],
recommended: [] as string[],
optional: [] as string[],
missing: [] as string[],
};

// 检查必需文件
for (const path of LANGGRAPH_PROJECT_STRUCTURE.required) {
const fullPath = join(projectRoot, path);
if (existsSync(fullPath)) {
results.required.push(path);
} else {
results.missing.push(path);
}
}

// 检查推荐文件
for (const path of LANGGRAPH_PROJECT_STRUCTURE.recommended) {
const fullPath = join(projectRoot, path);
if (existsSync(fullPath)) {
results.recommended.push(path);
}
}

// 检查可选文件
for (const path of LANGGRAPH_PROJECT_STRUCTURE.optional) {
const fullPath = join(projectRoot, path);
if (existsSync(fullPath)) {
results.optional.push(path);
}
}

return results;
}

/**
* 生成项目结构报告
*/
export function generateStructureReport(projectRoot?: string) {
const results = validateProjectStructure(projectRoot);

console.log('📁 LangGraph 项目结构检查报告');
console.log('================================');

console.log('\n✅ 必需文件:');
results.required.forEach((file) => console.log(`${file}`));

if (results.missing.length > 0) {
console.log('\n❌ 缺失的必需文件:');
results.missing.forEach((file) => console.log(`${file}`));
}

console.log('\n📋 推荐文件:');
results.recommended.forEach((file) => console.log(`${file}`));

if (results.optional.length > 0) {
console.log('\n📦 可选文件:');
results.optional.forEach((file) => console.log(`${file}`));
}

const score =
(results.required.length / LANGGRAPH_PROJECT_STRUCTURE.required.length) *
100;
console.log(`\n📊 结构完整度: ${score.toFixed(1)}%`);

return results;
}

/**
* 创建项目模板
*/
export function createProjectTemplate() {
const template = {
'package.json': {
name: 'my-langgraph-app',
version: '1.0.0',
type: 'module',
scripts: {
dev: 'tsx src/agent.ts',
build: 'tsc',
test: 'jest',
lint: 'eslint src/',
},
dependencies: {
'@langchain/langgraph': '^0.2.0',
'@langchain/openai': '^0.3.0',
'@langchain/core': '^0.3.0',
},
devDependencies: {
typescript: '^5.0.0',
'@types/node': '^20.0.0',
tsx: '^4.0.0',
jest: '^29.0.0',
eslint: '^8.0.0',
},
},
'langgraph.json': {
dependencies: ['.'],
graphs: {
agent: './src/agent.ts:graph',
},
env: './.env',
},
'tsconfig.json': {
compilerOptions: {
target: 'ES2022',
module: 'ESNext',
moduleResolution: 'bundler',
strict: true,
esModuleInterop: true,
skipLibCheck: true,
forceConsistentCasingInFileNames: true,
outDir: './dist',
rootDir: './src',
},
include: ['src/**/*'],
exclude: ['node_modules', 'dist'],
},
'.env.example': `# LangGraph 应用环境变量模板
# 复制此文件为 .env 并填入实际值

# OpenAI API 配置
OPENAI_API_KEY=your_openai_api_key_here
OPENAI_MODEL=gpt-4o-mini

# 应用配置
NODE_ENV=development
LOG_LEVEL=info
PORT=3000

# 其他 API 密钥
TAVILY_API_KEY=your_tavily_api_key_here
ANTHROPIC_API_KEY=your_anthropic_api_key_here`,
'.gitignore': `# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Build outputs
dist/
build/
*.tsbuildinfo

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
logs/
*.log`,
};

return template;
}

// 导出项目结构常量
export { LANGGRAPH_PROJECT_STRUCTURE };

目录说明

  • src/:所有项目代码的根目录
  • utils/:可重用的工具函数和辅助模块
  • nodes/:图节点的具体实现
  • state/:状态定义和相关类型
  • tools/:外部工具和 API 集成
  • langgraph.json:LangGraph Platform 配置文件
  • package.json:Node.js 项目依赖和脚本
  • .env:环境变量配置

配置文件详解

langgraph.json 配置

langgraph.json 是 LangGraph Platform 的核心配置文件,类似于前端项目中的 webpack.config.jsvite.config.ts

配置文件示例

/**
* LangGraph 配置文件示例
* 文件名:langgraph.json
*/

// 基础配置示例
export const basicConfig = {
// 必需:项目依赖
dependencies: ['.'],

// 必需:图定义映射
graphs: {
// 图名称: 文件路径:导出名称
'my-agent': './src/agent.ts:graph',
},

// 可选:环境变量配置
env: './.env',
};

// 完整配置示例
export const fullConfig = {
// 依赖配置 - 支持多种格式
dependencies: [
'.', // 本地包
'@langchain/openai', // NPM 包
'@langchain/community', // 社区包
'./custom-tools', // 本地自定义包
],

// 图定义 - 支持多个图
graphs: {
// 主聊天代理
'chat-agent': './src/agents/chat.ts:createChatAgent',

// 工具调用代理
'tool-agent': './src/agents/tools.ts:createToolAgent',

// 多代理系统
'multi-agent': './src/agents/multi.ts:createMultiAgentSystem',
},

// 环境变量 - 支持文件路径或直接配置
env: {
// 直接配置(不推荐用于敏感信息)
NODE_ENV: 'production',
LOG_LEVEL: 'info',

// 或者使用文件路径
// env: './.env'
},

// Node.js 版本
node_version: '20',

// 额外的 Docker 配置
dockerfile_lines: [
'RUN apt-get update && apt-get install -y curl',
'RUN npm ci --only=production',
'EXPOSE 8000',
],
};

// 开发环境配置
export const developmentConfig = {
dependencies: ['.'],
graphs: {
'dev-agent': './src/agent.ts:graph',
},
env: './.env.development',
node_version: '20',
dockerfile_lines: [
'RUN npm install', // 开发环境安装所有依赖
'EXPOSE 3000',
],
};

// 生产环境配置
export const productionConfig = {
dependencies: ['.', '@langchain/openai', '@langchain/community'],
graphs: {
'prod-agent': './dist/agent.js:graph', // 使用编译后的文件
},
env: {
NODE_ENV: 'production',
LOG_LEVEL: 'warn',
},
node_version: '20',
dockerfile_lines: [
'RUN npm ci --only=production',
'RUN npm run build',
'EXPOSE 8000',
'HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD curl -f http://localhost:8000/health || exit 1',
],
};

// 多环境配置管理
export const configs = {
development: developmentConfig,
production: productionConfig,
testing: {
dependencies: ['.'],
graphs: {
'test-agent': './src/agent.ts:graph',
},
env: './.env.test',
node_version: '20',
},
};

/**
* 配置验证函数
*/
export function validateConfig(config: any): {
valid: boolean;
errors: string[];
} {
const errors: string[] = [];

// 检查必需字段
if (!config.dependencies) {
errors.push('缺少必需字段: dependencies');
}

if (!config.graphs) {
errors.push('缺少必需字段: graphs');
}

// 检查依赖格式
if (config.dependencies && !Array.isArray(config.dependencies)) {
errors.push('dependencies 必须是数组');
}

// 检查图定义格式
if (config.graphs && typeof config.graphs !== 'object') {
errors.push('graphs 必须是对象');
}

// 检查图路径格式
if (config.graphs) {
for (const [name, path] of Object.entries(config.graphs)) {
if (typeof path !== 'string') {
errors.push(`图 "${name}" 的路径必须是字符串`);
continue;
}

if (!path.includes(':')) {
errors.push(`图 "${name}" 的路径格式错误,应为 "文件路径:导出名称"`);
}
}
}

// 检查 Node.js 版本
if (config.node_version && typeof config.node_version !== 'string') {
errors.push('node_version 必须是字符串');
}

return {
valid: errors.length === 0,
errors,
};
}

/**
* 生成配置文件
*/
export function generateConfig(options: {
environment?: 'development' | 'production' | 'testing';
graphs?: Record<string, string>;
dependencies?: string[];
envFile?: string;
}) {
const {
environment = 'development',
graphs,
dependencies,
envFile,
} = options;

const baseConfig = configs[environment];

return {
...baseConfig,
...(graphs && { graphs }),
...(dependencies && { dependencies }),
...(envFile && { env: envFile }),
};
}

// 使用示例
export function example() {
console.log('🔧 LangGraph 配置示例');

// 验证配置
const validation = validateConfig(fullConfig);
if (validation.valid) {
console.log('✅ 配置验证通过');
} else {
console.log('❌ 配置验证失败:');
validation.errors.forEach((error) => console.log(` - ${error}`));
}

// 生成自定义配置
const customConfig = generateConfig({
environment: 'production',
graphs: {
'my-custom-agent': './src/custom-agent.ts:createAgent',
},
dependencies: ['.', '@langchain/openai'],
envFile: './.env.prod',
});

console.log('📝 生成的配置:', JSON.stringify(customConfig, null, 2));
}

配置字段说明

字段必需说明示例
dependencies项目依赖数组["."]["@langchain/openai"]
graphs图定义映射{"agent": "./src/agent.ts:graph"}
env环境变量配置"./.env"{"API_KEY": "value"}
node_versionNode.js 版本"20" (默认)
dockerfile_lines额外的 Docker 配置["RUN apt-get update"]

图定义格式

图定义支持两种格式:

// 1. 直接导出编译后的图
export const graph = workflow.compile();

// 2. 导出创建图的函数(推荐)
export function createGraph(config?: RunnableConfig) {
// 根据配置创建图
return workflow.compile({ checkpointer: config?.checkpointer });
}

依赖管理

package.json 配置

依赖配置示例

/**
* package.json 配置示例
*/

export const packageConfig = {
name: 'my-langgraph-app',
version: '1.0.0',
description: 'LangGraph 智能代理应用',
type: 'module',
main: 'dist/index.js',
scripts: {
// 开发脚本
dev: 'tsx src/agent.ts',
'dev:watch': 'tsx watch src/agent.ts',

// 构建脚本
build: 'tsc',
'build:clean': 'rm -rf dist && npm run build',

// 测试脚本
test: 'jest',
'test:watch': 'jest --watch',
'test:coverage': 'jest --coverage',

// 代码质量
lint: 'eslint src/ --ext .ts,.tsx',
'lint:fix': 'eslint src/ --ext .ts,.tsx --fix',
format: 'prettier --write src/',

// 部署脚本
start: 'node dist/index.js',
'docker:build': 'docker build -t my-langgraph-app .',
'docker:run': 'docker run -p 3000:3000 my-langgraph-app',
},
dependencies: {
// LangGraph 核心
'@langchain/langgraph': '^0.2.0',
'@langchain/core': '^0.3.0',

// LLM 提供商
'@langchain/openai': '^0.3.0',
'@langchain/anthropic': '^0.3.0',

// 工具和集成
'@langchain/community': '^0.3.0',
'tavily-search': '^0.1.0',

// 实用工具
dotenv: '^16.0.0',
zod: '^3.22.0',
},
devDependencies: {
// TypeScript
typescript: '^5.0.0',
'@types/node': '^20.0.0',
tsx: '^4.0.0',

// 测试
jest: '^29.0.0',
'@types/jest': '^29.0.0',
'ts-jest': '^29.0.0',

// 代码质量
eslint: '^8.0.0',
'@typescript-eslint/parser': '^6.0.0',
'@typescript-eslint/eslint-plugin': '^6.0.0',
prettier: '^3.0.0',
},
engines: {
node: '>=18.0.0',
npm: '>=8.0.0',
},
keywords: ['langgraph', 'langchain', 'ai', 'agent', 'llm', 'typescript'],
};

依赖类型

  1. 核心依赖:LangGraph 和 LangChain 相关包
  2. 模型依赖:特定 LLM 提供商的包
  3. 工具依赖:外部 API 和服务集成
  4. 开发依赖:TypeScript、测试工具等

环境变量管理

.env 文件结构

环境变量示例

/**
* 环境变量配置示例
*/

// .env 文件内容示例
export const envExample = `# LangGraph 应用环境变量

# ===== 基础配置 =====
NODE_ENV=development
PORT=3000
LOG_LEVEL=info

# ===== OpenAI 配置 =====
OPENAI_API_KEY=sk-your-openai-api-key-here
OPENAI_MODEL=gpt-4o-mini
OPENAI_TEMPERATURE=0.7
OPENAI_MAX_TOKENS=1000

# ===== 其他 LLM 提供商 =====
ANTHROPIC_API_KEY=your-anthropic-api-key
GOOGLE_API_KEY=your-google-api-key

# ===== 工具 API 密钥 =====
TAVILY_API_KEY=your-tavily-search-api-key
SERP_API_KEY=your-serp-api-key

# ===== 数据库配置 =====
DATABASE_URL=postgresql://user:password@localhost:5432/langgraph
REDIS_URL=redis://localhost:6379

# ===== 应用特定配置 =====
MAX_ITERATIONS=10
TIMEOUT_SECONDS=30
ENABLE_DEBUG=false`;

// 类型安全的环境变量配置
export interface EnvironmentConfig {
// 基础配置
NODE_ENV: 'development' | 'production' | 'test';
PORT: number;
LOG_LEVEL: 'debug' | 'info' | 'warn' | 'error';

// OpenAI 配置
OPENAI_API_KEY: string;
OPENAI_MODEL?: string;
OPENAI_TEMPERATURE?: number;
OPENAI_MAX_TOKENS?: number;

// 其他配置
TAVILY_API_KEY?: string;
DATABASE_URL?: string;
REDIS_URL?: string;

// 应用配置
MAX_ITERATIONS?: number;
TIMEOUT_SECONDS?: number;
ENABLE_DEBUG?: boolean;
}

/**
* 加载和验证环境变量
*/
export function loadEnvironmentConfig(): EnvironmentConfig {
const config: EnvironmentConfig = {
NODE_ENV: (process.env.NODE_ENV as any) || 'development',
PORT: parseInt(process.env.PORT || '3000'),
LOG_LEVEL: (process.env.LOG_LEVEL as any) || 'info',
OPENAI_API_KEY: process.env.OPENAI_API_KEY!,
OPENAI_MODEL: process.env.OPENAI_MODEL || process.env.OPENAI_MODEL_NAME,
OPENAI_TEMPERATURE: parseFloat(process.env.OPENAI_TEMPERATURE || '0.7'),
OPENAI_MAX_TOKENS: parseInt(process.env.OPENAI_MAX_TOKENS || '1000'),
TAVILY_API_KEY: process.env.TAVILY_API_KEY,
DATABASE_URL: process.env.DATABASE_URL,
REDIS_URL: process.env.REDIS_URL,
MAX_ITERATIONS: parseInt(process.env.MAX_ITERATIONS || '10'),
TIMEOUT_SECONDS: parseInt(process.env.TIMEOUT_SECONDS || '30'),
ENABLE_DEBUG: process.env.ENABLE_DEBUG === 'true',
};

// 验证必需的环境变量
if (!config.OPENAI_API_KEY) {
throw new Error('OPENAI_API_KEY 环境变量是必需的');
}

return config;
}

环境变量最佳实践

环境变量管理建议
  1. 分层配置:开发、测试、生产环境分别配置
  2. 敏感信息:API 密钥等敏感信息不要提交到代码库
  3. 默认值:为非敏感配置提供合理的默认值
  4. 类型安全:使用 TypeScript 定义环境变量类型
类型安全的环境变量
interface EnvironmentConfig {
OPENAI_API_KEY: string;
MODEL_NAME?: string;
TEMPERATURE?: number;
MAX_TOKENS?: number;
}

function getConfig(): EnvironmentConfig {
return {
OPENAI_API_KEY: process.env.OPENAI_API_KEY!,
MODEL_NAME: process.env.MODEL_NAME || process.env.OPENAI_MODEL_NAME,
TEMPERATURE: Number(process.env.TEMPERATURE) || 0.7,
MAX_TOKENS: Number(process.env.MAX_TOKENS) || 1000,
};
}

代码组织模式

状态定义模块

状态定义示例

import { Annotation } from '@langchain/langgraph';
import { BaseMessage } from '@langchain/core/messages';

// 基础状态定义
export const BasicStateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
});

// 扩展状态定义
export const ExtendedStateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
currentTool: Annotation<string | null>({
reducer: (x, y) => y ?? x,
default: () => null,
}),
iterations: Annotation<number>({
reducer: (x, y) => x + y,
default: () => 0,
}),
});

节点实现模块

节点实现示例

import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';
import { ExtendedStateAnnotation } from './state-definition.js';

const model = new ChatOpenAI({ model: process.env.OPENAI_MODEL_NAME });

// 聊天节点实现
export async function chatNode(state: typeof ExtendedStateAnnotation.State) {
const response = await model.invoke(state.messages);
return {
messages: [response],
iterations: 1,
};
}

// 工具调用节点实现
export async function toolNode(state: typeof ExtendedStateAnnotation.State) {
// 模拟工具调用
const toolResult = `工具 ${state.currentTool} 执行完成`;
return {
messages: [new HumanMessage(toolResult)],
currentTool: null,
iterations: 1,
};
}

工具集成模块

工具集成示例

import { tool } from '@langchain/core/tools';
import { z } from 'zod';

// 搜索工具
export const searchTool = tool(
async ({ query }: { query: string }) => {
// 模拟搜索 API 调用
return `搜索结果: ${query}`;
},
{
name: 'search',
description: '搜索互联网信息',
schema: z.object({
query: z.string().describe('搜索查询'),
}),
}
);

// 计算工具
export const calculatorTool = tool(
async ({ expression }: { expression: string }) => {
try {
// 简单的数学表达式计算
const result = eval(expression);
return `计算结果: ${result}`;
} catch (error) {
return '计算错误: 无效的表达式';
}
},
{
name: 'calculator',
description: '执行数学计算',
schema: z.object({
expression: z.string().describe('数学表达式'),
}),
}
);

export const tools = [searchTool, calculatorTool];

部署配置

开发环境配置

生产环境配置

生产环境配置
// langgraph.json (生产环境)
{
"dependencies": ["."],
"graphs": {
"production-agent": "./src/agent.ts:createGraph"
},
"env": {
"NODE_ENV": "production",
"LOG_LEVEL": "info"
},
"dockerfile_lines": [
"RUN npm ci --only=production",
"RUN npm run build"
]
}

实践指导

项目初始化步骤

  1. 创建项目结构

    mkdir my-langgraph-app
    cd my-langgraph-app
    npm init -y
  2. 安装依赖

    npm install @langchain/langgraph @langchain/openai
    npm install -D typescript @types/node
  3. 创建配置文件

    touch langgraph.json .env tsconfig.json
  4. 组织代码结构

    mkdir -p src/{utils,nodes,state,tools}

配置验证

配置验证工具

import { z } from 'zod';

// 配置验证 Schema
const ConfigSchema = z.object({
dependencies: z.array(z.string()),
graphs: z.record(z.string()),
env: z.union([z.string(), z.record(z.string())]).optional(),
node_version: z.string().optional(),
dockerfile_lines: z.array(z.string()).optional(),
});

/**
* 验证 LangGraph 配置
*/
export function validateLangGraphConfig(config: unknown) {
try {
const validatedConfig = ConfigSchema.parse(config);

// 额外验证图路径格式
for (const [name, path] of Object.entries(validatedConfig.graphs)) {
if (!path.includes(':')) {
throw new Error(
`图 "${name}" 的路径格式错误,应为 "文件路径:导出名称"`
);
}
}

return {
success: true,
config: validatedConfig,
errors: [],
};
} catch (error) {
return {
success: false,
config: null,
errors: error instanceof z.ZodError ? error.errors : [error.message],
};
}
}

/**
* 生成配置验证报告
*/
export function generateValidationReport(config: unknown) {
const result = validateLangGraphConfig(config);

console.log('🔍 LangGraph 配置验证报告');
console.log('============================');

if (result.success) {
console.log('✅ 配置验证通过');
console.log('📋 验证的配置项:');
console.log(` - 依赖数量: ${result.config.dependencies.length}`);
console.log(` - 图数量: ${Object.keys(result.config.graphs).length}`);
console.log(` - Node.js 版本: ${result.config.node_version || '默认'}`);
} else {
console.log('❌ 配置验证失败');
console.log('🐛 错误详情:');
result.errors.forEach((error, index) => {
console.log(` ${index + 1}. ${error}`);
});
}

return result;
}

常见问题和解决方案

依赖冲突

问题:不同包版本冲突 解决方案

  • 使用 npm ls 检查依赖树
  • 固定关键包的版本
  • 使用 resolutions 字段强制版本

路径解析问题

问题:图定义路径无法找到 解决方案

  • 使用相对路径:"./src/agent.ts:graph"
  • 确保导出名称正确
  • 检查 TypeScript 编译输出

环境变量缺失

问题:部署时环境变量未加载 解决方案

  • langgraph.json 中明确指定 .env 路径
  • 使用平台环境变量管理
  • 添加环境变量验证

小结与延伸

良好的应用结构是成功部署的基础。通过合理组织代码、正确配置依赖和环境变量,你可以确保应用在不同环境中的一致性和可维护性。

关键要点:

  • 遵循标准的项目结构模式
  • 正确配置 langgraph.json 文件
  • 合理管理依赖和环境变量
  • 使用类型安全的配置管理

接下来我们将学习 LangGraph Studio,了解如何使用这个强大的可视化开发工具来调试和优化你的应用。