智能体技术正以前所未有的速度改变着我们的生活和工作方式。从智能家居设备到智能办公系统,从智能客服到智能决策辅助工具,智能体的应用场景日益广泛。
Nest.js作为一种高效、可扩展的Node.js框架,凭借其强大的功能和灵活的架构,成为开发智能体应用的理想选择之一。
无论你是初入Nest.js领域的开发者,还是希望在智能体开发中进一步提升技能的资深工程师,本系列文章都将为你提供清晰、实用的指导。
环境搭建
安装 Volta
Volta是一个无忧的JavaScript工具管理器
curl https://get.volta.sh | bash
安装 Node
volta install node@v22.12.0
安装 pnpm
volta install pnpm@9.15.1
安装 pm2
volta install pm2@5.4.3
检查环境
$ volta list
⚡️ Currently active tools:
Node: v22.12.0 (default)
Tool binaries available:
pm2, pm2-dev, pm2-docker, pm2-runtime (default)
pnpm, pnpx (default)
See options for more detailed reports by running `volta list --help`.
创建Nest工程
创建一个文件夹 nest-agent 并使用 pnpm 初始化
mkdir nest-agent
cd nest-agent
pnpm init
在 nest-agent 创建源码目录和入口文件
mkdir src
cd src
touch main.ts
// src/main.ts
async function main() {}
main();
安装和配置 TypeScript
pnpm add typescript -D
pnpm add @types/node -D
touch tsconfig.json
touch tsconfig.build.json
// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "Node",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"incremental": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false,
"jsx": "react-jsx",
"outDir": "dist",
"rootDir": "src",
"target": "ES2021",
"lib": ["DOM", "DOM.Iterable", "ES2021", "ES2022.Error"],
"noErrorTruncation": true,
"strict": true,
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*"]
}
// tsconfig.build.json
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
}
安装和配置 Nest
pnpm add @nestjs/schematics -D
touch nest-cli.json
// nest-cli.json
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src", // 源码目录
"entryFile": "main", // 入口文件
"compilerOptions": {
"deleteOutDir": true
}
}
使用SWC优化编译
pnpm add @swc/cli -D
pnpm add @swc/core -D
// nest-cli.json
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"entryFile": "main",
"compilerOptions": {
"builder": "swc", // 使用 SWC 编译
"typeCheck": true, // 强制类型检查
"deleteOutDir": true
}
}
配置代码格式化
pnpm add @biomejs/biome -D
// biome.json
{
"$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": ["node_modules/**", "dist/**", "build/**"]
},
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "off",
"noAsyncPromiseExecutor": "off",
"noAssignInExpressions": "off"
},
"style": {
"noUselessElse": "off",
"useImportType": "off",
"noParameterAssign": "off",
"noInferrableTypes": "off",
"noNonNullAssertion": "off",
"useNodejsImportProtocol": "off"
},
"performance": {
"noAccumulatingSpread": "off"
},
"complexity": {
"noThisInStatic": "off",
"noBannedTypes": "off",
"noForEach": "off",
"noStaticOnlyClass": "off"
},
"correctness": {
"useExhaustiveDependencies": "off",
"noConstructorReturn": "off"
},
"a11y": {
"useKeyWithClickEvents": "off",
"noSvgWithoutTitle": "off"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always"
},
"parser": {
"unsafeParameterDecoratorsEnabled": true
}
}
}
配置启动脚本
// package.json
{
"name": "nest-agent",
"version": "1.0.0",
"scripts": {
"dev": "nest start --watch",
"start": "node dist/main",
"build": "nest build",
"format": "pnpm biome format --write ."
},
...
}
安装核心依赖
pnpm add @nestjs/core
pnpm add @nestjs/common
pnpm add @nestjs/platform-express
pnpm add rxjs
pnpm add reflect-metadata
创建根模块
// src/app.module.ts
import { Module } from "@nestjs/common";
@Module()
export class AppModule {}
启动Nest框架
// src/main.ts
import { NestFactory } from "@nestjs/core";
import { NestExpressApplication } from "@nestjs/platform-express";
import { AppModule } from "./app.module";
async function main() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
await app.listen(3000);
}
main()
源码结构
src/
├── config/ # 配置文件
├── domain/ # 领域层
│ ├── entities/ # 实体类
│ ├── value-objects/ # 值对象
│ ├── interfaces/ # 领域接口
│ ├── aggregates/ # 聚合根
│ ├── repositories/ # 仓库接口
│ └── domain-events/ # 领域事件
├── application/ # 应用层
│ ├── web/ # Web 模块
│ │ ├── services/ # Web 应用服务
│ │ ├── dtos/ # Web 数据传输对象 (DTOs)
│ │ └── web.module.ts # Web 模块定义
│ ├── admin/ # Admin 模块
│ │ ├── services/ # Admin 应用服务
│ │ ├── dtos/ # Admin 数据传输对象 (DTOs)
│ │ └── admin.module.ts # Admin 模块定义
├── infrastructure/ # 基础设施层
│ ├── databases/ # 数据库相关
│ ├── repositories/ # 仓库实现
│ ├── gateways/ # 外部服务网关
│ ├── controllers/ # 控制器
│ │ ├── web/ # Web 控制器
│ │ ├── admin/ # Admin 控制器
│ │ └── common/ # 公共控制器
│ ├── pipes/ # 管道
│ ├── guards/ # 守卫
│ ├── filters/ # 过滤器
│ ├── interceptors/ # 拦截器
│ └── middlewares/ # 中间件
├── shared/ # 共享层
│ ├── utils/ # 自定义工具
│ ├── constants/ # 自定义常量
│ ├── exceptions/ # 自定义异常
│ └── modules/ # 共享模块(如Kafka、Manticore等)
├── app.module.ts # 根模块
└── main.ts # 入口文件
以下是对上述文件夹结构的详细介绍,包括每个部分的职责和作用:
1. 领域层(Domain Layer)
领域层是整个应用的核心,包含了业务逻辑和业务规则。它独立于任何外部系统,专注于定义业务实体、值对象、聚合根、仓库接口和领域事件等。
entities/ :实体类
- 职责:定义业务实体,这些实体具有唯一标识符(ID),并且其生命周期需要被跟踪。
- 示例:
User、Order等实体类。
value-objects/ :值对象
- 职责:定义值对象,这些对象没有唯一标识符,其价值在于它们的属性值。
- 示例:
Money、Address等值对象。
interfaces/ :领域接口
- 职责:定义领域层需要的接口,这些接口通常由基础设施层实现。
- 示例:
IUserRepository(用户仓库接口)。
aggregates/ :聚合根
- 职责:定义聚合根,聚合根是领域模型中的一组相关实体和值对象的根。
- 示例:
OrderAggregate(订单聚合根)。
repositories/ :仓库接口
- 职责:定义数据访问的接口,这些接口由基础设施层实现。
- 示例:
IUserRepository(用户仓库接口)。
domain-events/ :领域事件
- 职责:定义领域事件,这些事件在业务逻辑中被触发,用于通知其他部分业务逻辑的完成。
- 示例:
UserCreatedEvent(用户创建事件)。
2. 应用层(Application Layer)
应用层是领域层和基础设施层之间的桥梁,负责协调领域层的业务逻辑和基础设施层的资源访问。它封装了领域层的业务逻辑,并通过领域层的接口与基础设施层进行交互。
web/ :Web 模块
-
职责:处理与 Web 相关的业务逻辑。
-
内容:
services/:Web 应用服务,封装了 Web 相关的业务逻辑。dtos/:Web 数据传输对象(DTOs),用于在应用层和基础设施层之间传递数据。web.module.ts:Web 模块的定义文件,用于组织和管理 Web 模块的依赖和服务。
admin/ :Admin 模块
-
职责:处理与 Admin 相关的业务逻辑。
-
内容:
services/:Admin 应用服务,封装了 Admin 相关的业务逻辑。dtos/:Admin 数据传输对象(DTOs),用于在应用层和基础设施层之间传递数据。admin.module.ts:Admin 模块的定义文件,用于组织和管理 Admin 模块的依赖和服务。
3. 基础设施层(Infrastructure Layer)
基础设施层负责处理与外部系统的交互,如数据库操作、外部服务调用、文件存储等。它实现了领域层定义的接口,并为应用层提供具体的实现。
databases/ :数据库相关
- 职责:包含数据库相关的实现,如数据库连接、迁移脚本等。
repositories/ :仓库实现
- 职责:实现领域层定义的仓库接口,提供数据访问的具体实现。
- 示例:
UserRepository(用户仓库实现)。
gateways/ :外部服务网关
- 职责:实现外部服务的调用,如调用第三方 API、消息队列等。
controllers/ :控制器
-
职责:处理 HTTP 请求和响应,是应用的入口点。
-
内容:
web/:Web 控制器,处理 Web 相关的 HTTP 请求。admin/:Admin 控制器,处理 Admin 相关的 HTTP 请求。common/:公共控制器,处理通用的 HTTP 请求。
pipes/ :管道
- 职责:实现数据验证和转换的管道。
guards/ :守卫
- 职责:实现权限验证和访问控制的守卫。
filters/ :过滤器
- 职责:实现异常处理和请求过滤的过滤器。
interceptors/ :拦截器
- 职责:实现请求和响应的拦截器,用于日志记录、性能监控等。
middlewares/ :中间件
- 职责:实现中间件,用于处理 HTTP 请求的生命周期。
4. 共享层(Shared Layer)
共享层包含项目中通用的工具函数、常量、异常处理等,以及一些可以被多个模块共享的基础设施模块。
utils/ :自定义工具
- 职责:提供通用的工具函数。
constants/ :自定义常量
- 职责:定义项目中使用的常量。
exceptions/ :自定义异常
- 职责:定义项目中使用的自定义异常。
modules/ :共享模块
- 职责:包含可以被多个模块共享的基础设施模块,如 Kafka、Manticore 等。
5. 根模块(App Module)
- 职责:定义应用的根模块,组织和管理整个应用的依赖关系。
- 文件:
app.module.ts
6. 入口文件(Main File)
- 职责:应用的入口文件,用于启动应用。
- 文件:
main.ts
PM2部署
创建pm2部署脚本
touch ecosystem.config.js
// ecosystem.config.js
module.exports = {
apps: [
{
name: "agent",
script: "./dist/main.js",
exec_mode: "cluster_mode", // 集群模式
instances: 4, // 4个进程
env: {
NODE_ENV: "production"
}
}
]
}
编译Nest项目
pnpm build
pm2启动项目
pm2 start
远程部署
// ecosystem.config.js
module.exports = {
apps: [
{
name: "agent",
script: "./dist/main.js",
exec_mode: "cluster_mode",
instances: 4, // 根据你的服务器CPU核心数设置启动的进程数
env: {
NODE_ENV: "production",
},
},
],
deploy: {
agent: {
user: "root",
host: [“你的服务器IP”],
ref: "origin/main",
repo: "你的仓库地址",
path: "/root/code/nest-agent",
// 确保你的服务上已经安装了相关环境(如: `git`、`node`、 `pnpm`、 `pm2`)
"post-deploy": "/root/.volta/bin/pnpm install && /root/.volta/bin/pnpm build",
},
},
};
配置服务器
ssh-copy-id root@你的服务器IP
配置pm2服务器环境
pm2 deploy agent setup
执行远程部署
pm2 deploy agent && pm2 deploy agent exec \"/root/.volta/bin/pm2 start --only agent\"
Docker部署
创建Dockerfile
# 使用 Node.js 22 Alpine 镜像作为构建阶段的基础镜像
FROM node:22-alpine AS builder
# 全局安装 pnpm 作为包管理器
RUN npm install -g pnpm
# 设置工作目录为 /app
WORKDIR /app
# 将 package.json 和 pnpm-lock.yaml 复制到工作目录
COPY package.json pnpm-lock.yaml ./
# 安装项目依赖,确保依赖版本固定
RUN pnpm install --frozen-lockfile
# 将项目的所有文件复制到工作目录
COPY . .
# 运行构建命令
RUN pnpm build
# 使用 Node.js 22 Alpine 镜像作为运行阶段的基础镜像
FROM node:22-alpine
# 全局安装 pnpm
RUN npm install -g pnpm
# 设置工作目录为 /app
WORKDIR /app
# 从构建阶段复制构建输出
COPY --from=builder /app/dist ./dist
# 复制 package.json 和 pnpm-lock.yaml
COPY --from=builder /app/package.json /app/pnpm-lock.yaml ./
# 安装生产环境依赖,确保只安装生产环境所需的包
RUN pnpm install --frozen-lockfile --prod
# 设置环境变量 NODE_ENV 为 production
ENV NODE_ENV=production
# 暴露容器的 3000 端口
EXPOSE 3000
# 启动应用的命令
CMD ["pnpm", "start"]
构建镜像
docker build -t nest-agent .
启动容器
docker run -d -p 3000:3000 -e NODE_ENV=development --name my-agent nest-agent
查看日志
docker logs my-agent
Kubernetes部署
安装Tilt
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
有关Tilt的更多介绍可查看服务编排即代码
在k8s文件夹下创建deployment-dev.yaml编排文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nest-agent
spec:
replicas: 1
selector:
matchLabels:
app: nest-agent
template:
metadata:
labels:
app: nest-agent
spec:
containers:
- name: nest-agent
image: my-agent:dev
imagePullPolicy: Always
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: nest-agent-service
spec:
selector:
app: nest-agent
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
创建Tiltfile
# -*- mode: Python -*-
env = os.getenv('ENV', 'dev')
print("ENV:{}".format(env))
# 根据环境变量调整 Docker 镜像名称和标签
image_name = "my-agent:{}".format(env)
# 构建 Docker 镜像
docker_build(image_name, '.')
# 根据环境变量加载不同的 Kubernetes 部署文件
k8s_yaml("./k8s/deployment-{}.yaml".format(env))
# 定义 Kubernetes 资源
k8s_resource('nest-agent')
启动服务
tilt up --context orbstack # 由于笔者使用 orbstack 在本机启动 k8s 所以这里传递 --context orbstack
访问http://localhost:10350查看部署情况
每次代码更新后Tilt会自动构建镜像并重新部署服务