随着大模型应用的发展,越来越多的 AI 功能开始融入到 Web 应用中。例如:
- AI 聊天助手
- AI 文档总结
- AI 代码生成
- AI 搜索 / RAG
在这些场景中,一个非常常见的技术架构是:
Frontend
↓
Backend API(NestJS)
↓
LangChain
↓
LLM(OpenAI / DeepSeek / Ollama)
也就是说,大多数 AI Agent 实际上是运行在后端服务中的。前端只负责界面和交互,而真正的 AI 调用、Prompt 处理、数据访问都在后端完成。
在 Node.js 技术栈中,NestJS + LangChain 是一个非常常见的组合。本文将通过一个简单示例,介绍如何使用 NestJS 构建 AI API 服务,同时梳理其中涉及到的后端架构设计思想。
NestJS:Node.js 的企业级框架
NestJS 是一个基于 Node.js 和 TypeScript 的后端框架,它的设计灵感来自 Angular,强调 模块化架构和依赖注入。
从技术实现上来说,NestJS 的底层其实是:
NestJS
↓
Express / Fastify
也就是说,NestJS 本质上是对 Express 的封装,但它在上层提供了完整的工程化能力,例如:
- MVC 架构
- 模块化系统
- 依赖注入(DI)
- 装饰器
- RESTful API 设计
这些能力使得 NestJS 非常适合构建 企业级后端服务。
NestJS 的模块化架构
NestJS 项目通常采用模块化结构。一个典型项目结构如下:
src
├── app.module.ts
├── ai
│ ├── ai.module.ts
│ ├── ai.controller.ts
│ └── ai.service.ts
└── book
├── book.module.ts
├── book.controller.ts
└── book.service.ts
在 NestJS 中,每个业务功能通常会被拆分成一个 模块(Module) 。
模块中通常包含三个核心部分:
Controller
Service
Module
Controller 负责处理请求,Service 负责业务逻辑,而 Module 则负责组织这些组件。
这种设计可以让系统结构更加清晰,也更适合大型项目的扩展。
MVC 架构在 NestJS 中的体现
MVC 是后端开发中非常经典的设计模式:
Model
View
Controller
在现代 Web 开发中,大多数项目都采用 前后端分离架构,因此 View 通常由前端框架(React / Vue)实现,而 NestJS 主要负责 Model 和 Controller 部分。
在 NestJS 中:
| MVC | NestJS |
|---|---|
| Model | Service |
| View | 前端应用 |
| Controller | Controller |
Controller 负责处理 HTTP 请求,例如:
@Controller('ai')
export class AiController {
constructor(private readonly aiService: AiService) {}
@Get('chat')
async chat(@Query('query') query: string) {
const answer = await this.aiService.runChain(query)
return {
answer
}
}
}
这里的 Controller 做了两件事:
- 接收请求参数
- 调用 Service 处理业务逻辑
真正的 AI 调用逻辑则放在 Service 中。
依赖注入(DI)与 IoC 容器
NestJS 的一个核心特性是 依赖注入(Dependency Injection) 。
在传统开发中,如果 Controller 需要使用 Service,通常需要手动创建实例:
const service = new AiService()
但在 NestJS 中,这个过程由 IoC 容器自动完成。
例如:
constructor(private readonly aiService: AiService) {}
Nest 会自动:
创建 AiService 实例
管理生命周期
注入 Controller
这种设计的好处是:
- 降低耦合
- 更容易测试
- 更方便扩展
依赖注入是很多现代框架(Spring、Angular、Nest)都会使用的重要设计思想。
装饰器:NestJS 的核心语法
NestJS 大量使用 装饰器(Decorator) 来描述系统结构。
装饰器是一种设计模式,可以在不修改原有代码的情况下,对类或方法进行增强。
例如:
Controller 装饰器
@Controller('ai')
表示这个类是一个控制器,并且路由前缀是 /ai。
HTTP 方法装饰器
@Get('chat')
表示该方法处理:
GET /ai/chat
参数装饰器
@Query('query')
用于获取 URL 查询参数:
/ai/chat?query=hello
Service 装饰器
@Injectable()
表示该类可以被 Nest 的 依赖注入系统管理。
装饰器的存在让 NestJS 的代码结构非常清晰,也非常符合面向对象设计思想。
RESTful API 设计
NestJS 默认推荐使用 RESTful API 风格。
REST 的核心思想是:
一切皆资源
资源通常使用 名词 表示,例如:
user
book
article
而操作则通过 HTTP Method 表达:
| Method | 作用 |
|---|---|
| GET | 查询 |
| POST | 创建 |
| PUT | 更新 |
| DELETE | 删除 |
例如:
GET /book
POST /book
PUT /book/1
DELETE /book/1
这种设计方式可以让 API 结构更加清晰,也更符合 Web 标准。
使用 Nest CLI 快速生成资源
NestJS 提供了一个非常方便的 CLI 工具,可以快速生成模块代码。
例如:
nest g res book
这条命令会自动生成:
book.module.ts
book.controller.ts
book.service.ts
book.dto.ts
如果不需要测试文件,可以使用:
nest g res book --no-spec
这样可以避免生成 .spec.ts 文件,让项目结构更加简洁。
使用 LangChain 实现 AI 接口
接下来,我们可以在 Service 中接入 LangChain,实现 AI 能力。
示例代码如下:
@Injectable()
export class AiService {
private readonly chain: Runnable
constructor(configService: ConfigService) {
const prompt = PromptTemplate.fromTemplate(
`请回答以下问题:\n\n{query}`
)
const model = new ChatOpenAI({
temperature: 0.7,
modelName: configService.get('MODEL_NAME'),
apiKey: configService.get('OPENAI_API_KEY'),
configuration: {
baseURL: configService.get('OPENAI_BASE_URL')
}
})
this.chain = prompt
.pipe(model)
.pipe(new StringOutputParser())
}
async runChain(query: string) {
return this.chain.invoke({ query })
}
}
LangChain 的执行流程实际上是一条 处理链(Chain) :
用户输入
↓
PromptTemplate
↓
LLM Model
↓
OutputParser
↓
返回结果
通过这种方式,可以非常方便地构建 AI 接口服务。
总结
在 AI 应用开发中,后端往往承担着非常重要的角色。通过 NestJS 可以构建结构清晰、可扩展的 API 服务,而 LangChain 则可以帮助我们快速接入大模型能力。
整个系统的调用流程可以总结为:
Frontend
↓
NestJS Controller
↓
Service
↓
LangChain
↓
LLM API
NestJS 提供的 模块化架构、依赖注入、装饰器机制以及 RESTful 设计,让 Node.js 后端开发具备了接近传统企业级框架(如 Spring)的工程能力。
在实际 AI 项目中,这样的架构已经成为一种非常常见的实践方式。