📁 应用结构
引言
一个 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.js 或 vite.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_version | ❌ | Node.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'],
};
依赖类型
- 核心依赖:LangGraph 和 LangChain 相关包
- 模型依赖:特定 LLM 提供商的包
- 工具依赖:外部 API 和服务集成
- 开发依赖: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;
}
环境变量最佳实践
环境变量管理建议
- 分层配置:开发、测试、生产环境分别配置
- 敏感信息:API 密钥等敏感信息不要提交到代码库
- 默认值:为非敏感配置提供合理的默认值
- 类型安全:使用 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"
]
}
实践指导
项目初始化步骤
-
创建项目结构
mkdir my-langgraph-app
cd my-langgraph-app
npm init -y -
安装依赖
npm install @langchain/langgraph @langchain/openai
npm install -D typescript @types/node -
创建配置文件
touch langgraph.json .env tsconfig.json -
组织代码结构
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,了解如何使用这个强大的可视化开发工具来调试和优化你的应用。