文章目录
-
-
- 前言
- [1 方案一:库封装,开箱直接躺平](#1 方案一:库封装,开箱直接躺平)
-
- [1.1 先装依赖](#1.1 先装依赖)
- [1.2 注册模块](#1.2 注册模块)
- [1.3 服务里注入使用](#1.3 服务里注入使用)
- [1.4 高阶玩法:工具调用一键搞定](#1.4 高阶玩法:工具调用一键搞定)
- [1.5 优缺点唠一唠](#1.5 优缺点唠一唠)
- [2 方案二:手动构建,一切自己说了算](#2 方案二:手动构建,一切自己说了算)
-
- [2.1 装核心依赖](#2.1 装核心依赖)
- [2.2 先建个AI模块](#2.2 先建个AI模块)
- [2.3 核心操作:自己拼一条Chain](#2.3 核心操作:自己拼一条Chain)
- [2.4 模块注册](#2.4 模块注册)
- [2.5 写个接口](#2.5 写个接口)
- [2.6 环境变量配好](#2.6 环境变量配好)
- [2.7 每行代码干啥的,掰碎了说](#2.7 每行代码干啥的,掰碎了说)
- [2.8 手动玩法的好坏处](#2.8 手动玩法的好坏处)
- [3 两种方式横向对比](#3 两种方式横向对比)
- [4 实战到底选哪个?听我一句劝](#4 实战到底选哪个?听我一句劝)
-
- [4.1 选封装库,如果你](#4.1 选封装库,如果你)
- [4.2 选手动构建,如果你](#4.2 选手动构建,如果你)
- [5 想换阵营怎么办?](#5 想换阵营怎么办?)
-

P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/H1727548
前言
不知道你们刚开始把LangChain往NestJS里塞的时候,是不是都犯过选择困难症。
要么搜个第三方库一键装上,图个省事;要么硬着头皮自己手写每一步,图个踏实。
说白了就跟买电脑一个道理,有人买整机插电就用,有人买散件蹲直播间挨个比价自己装。
今天就把这两条路都给你们捋明白,看完你就知道自己该走哪条道。
1 方案一:库封装,开箱直接躺平
1.1 先装依赖
就一行npm命令的事,跟点外卖似的,你只管下单,剩下的交给npm帮你拉包。
装不上也别慌,多半是网络的锅,切个国内镜像再试,都是老演员了。
bash
npm install nestjs-langchain langchain @langchain/openai
1.2 注册模块
在AppModule里注册一下,全局配置一次,项目里到处都能用。
就跟小区录门禁人脸似的,录一回,整个小区的门你都能刷脸进。
配置项也简单,指定用啥模型,填一下API Key,再写个系统提示词,齐活。
typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { LangChainModule } from 'nestjs-langchain';
import { AiModule } from './ai/ai.module';
@Module({
imports: [
LangChainModule.register({
model: {
model: 'openai:gpt-3.5-turbo',
apiKey: process.env.OPENAI_API_KEY,
},
systemPrompt: 'You are a helpful assistant.',
}),
AiModule,
],
})
export class AppModule {}
1.3 服务里注入使用
想在哪用,就在哪个Service的构造函数里注入LangChainService。
跟你平时注入数据库服务没啥区别,NestJS那套依赖注入该咋用咋用。
调用更简单,一个run方法传问题进去,直接拿结果。
新手第一次跑通的时候,多半都得感叹一句:原来AI集成这么简单?
typescript
// ai.service.ts
import { Injectable } from '@nestjs/common';
import { LangChainService } from 'nestjs-langchain';
@Injectable()
export class AiService {
constructor(private readonly langChainService: LangChainService) {}
async askQuestion(question: string) {
return await this.langChainService.run(question);
}
}
1.4 高阶玩法:工具调用一键搞定
这库最香的地方,就是这个@Tool()装饰器。
你想让AI调用啥方法,就在方法头上贴个装饰器,写清楚干啥用的。
就跟公司给员工挂工牌似的,AI扫一眼就知道:哦,这哥们是干加法的,算算术喊他就行。
typescript
// math.service.ts
import { Injectable } from '@nestjs/common';
import { Tool, ToolParam } from 'nestjs-langchain';
@Injectable()
export class MathService {
@Tool({
description: '将两个数字相加。当用户需要进行加法计算时使用此工具。',
})
add(
@ToolParam({ name: 'a', description: '第一个加数', type: 'number' })
a: number,
@ToolParam({ name: 'b', description: '第二个加数', type: 'number' })
b: number,
): number {
return a + b;
}
}
注册也省事,直接把模块丢进tools数组里,里面所有带装饰器的方法自动就被AI识别了。
typescript
@Module({
imports: [
LangChainModule.register({
model: { model: 'openai:gpt-3.5-turbo', apiKey: process.env.OPENAI_API_KEY },
systemPrompt: '你是一个智能助手,可以使用工具来帮助用户。',
tools: [MathModule],
}),
MathModule,
],
})
export class AppModule {}
1.5 优缺点唠一唠
好处不用多说,省事是真省事。配置集中,代码量少,跟NestJS依赖注入体系适配得特别顺。
缺点也明摆着:人家封装好啥你用啥,想搞点花活容易受限制。就跟买品牌整机似的,你想换个显卡超个频,人家壳子都给你焊死了。
而且LangChain官方更得快,第三方库跟进总得慢半拍,追新党可能会难受。
2 方案二:手动构建,一切自己说了算
要是你觉得封装库不够造,想怎么玩就怎么玩,那手动构建绝对适合你。
说白了就是不用中间商,直接跟LangChain核心包打交道。
2.1 装核心依赖
不用装第三方封装库了,就装官方的核心包。
依赖树干干净净,就像你清空了购物车,只留刚需品。
bash
npm install @langchain/openai @langchain/core
2.2 先建个AI模块
用Nest CLI一行命令生成模块、控制器、服务。
老Nest玩家了,这步操作估计闭着眼都能敲。
bash
nest g module ai
nest g controller ai
nest g service ai
2.3 核心操作:自己拼一条Chain
重点来了,在Service里自己把整条链路拼出来。
就跟拼奶茶似的,茶底选啥,糖度多少,加不加珍珠,全你自己定。
第一步先整个Prompt模板,留个占位符等着传问题。
第二步初始化模型,配置从ConfigService里读。别把密钥硬写代码里,不然上线就得被安全部门约谈。
第三步用pipe串起来,模板→模型→输出解析器,一条流水线就搭好了。
最后整个invoke方法对外暴露,完事。
typescript
// ai.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { ChatOpenAI } from '@langchain/openai';
import { PromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';
import type { Runnable } from '@langchain/core/runnables';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AiService {
private readonly chain: Runnable;
constructor(@Inject(ConfigService) 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): Promise<string> {
return this.chain.invoke({ query });
}
}
2.4 模块注册
正常把服务和控制器注册进模块里就行。
没啥花里胡哨的,标准Nest模块写法。
typescript
// ai.module.ts
import { Module } from '@nestjs/common';
import { AiService } from './ai.service';
import { AiController } from './ai.controller';
@Module({
controllers: [AiController],
providers: [AiService],
})
export class AiModule {}
2.5 写个接口
整个Controller,写个GET接口,传query参数就能调用。
前端调一下,就能拿到AI的回答,前后端联调都省事儿。
typescript
// ai.controller.ts
import { Controller, Get, Query } from '@nestjs/common';
import { AiService } from './ai.service';
@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,
};
}
}
2.6 环境变量配好
.env文件里把密钥、地址、模型名都写上。
别把密钥提交到Git!别把密钥提交到Git!别把密钥提交到Git!
重要的话说三遍,踩过坑的都懂。
env
OPENAI_API_KEY=your-api-key-here
OPENAI_BASE_URL=https://api.openai.com/v1
MODEL_NAME=gpt-3.5-turbo
2.7 每行代码干啥的,掰碎了说
PromptTemplate就是出题的卷子,提前把格式印好,到时候填问题就行。
ChatOpenAI就是阅卷老师,负责看题给答案。
pipe方法就是流水线传送带,把上一步的结果传给下一步。
invoke就是按下启动键,整条流水线跑起来。
ConfigService就是后勤,专门管各种配置参数,不用你到处写死。
2.8 手动玩法的好坏处
好处就是自由,极致的自由。Prompt想怎么改怎么改,多模型切换随便搞,复杂Chain随便编排。
官方更啥新特性,你当天就能用上,不用等第三方适配。依赖也干净,没有中间商赚差价。
缺点就是啥都得自己来,代码量肯定多一点。尤其是工具调用,得自己写Agent逻辑,不像封装库一个装饰器就搞定。
新手刚上手可能有点懵,得先搞懂LangChain那几个核心概念。
3 两种方式横向对比
配置复杂度:封装库简单,都集中在模块里;手动构建中等,得自己初始化。
灵活性:封装库受限于API;手动构建完全灵活,可以深度定制。
工具注册:封装库一个装饰器完事;手动构建得自己实现Agent逻辑。
学习曲线:封装库平缓,开箱即用;手动构建较陡,得懂核心概念。
依赖管理:封装库多一层第三方依赖;手动构建只依赖官方核心包。
4 实战到底选哪个?听我一句劝
别纠结,看菜下饭,看需求选方案。
4.1 选封装库,如果你
刚接触这俩玩意,想先跑通流程找找感觉。
需要AI调用各种工具,不想自己写Agent写到头秃。
业务场景不复杂,不用搞啥花里胡哨的定制。
4.2 选手动构建,如果你
对Prompt要求特别高,要动态拼接、搞条件分支各种操作。
喜欢追新,LangChain出啥新特性都想第一时间玩上。
有代码洁癖,不想项目里多一堆没必要的依赖。
以后说不定要换模型底层,想留好扩展空间。
5 想换阵营怎么办?
很简单,双向奔赴都不难,没什么技术壁垒。
从手动转封装:装库,注册模块,删掉自己写的初始化代码,换成注入服务,改调用方法就行。
从封装转手动:反过来操作一遍,把库的东西删掉,自己写初始化逻辑。
说白了就是换个写法,核心逻辑还是那套东西。
最后总结一句:封装库是买整机,插电就用,适合大多数场景;手动构建是自己装机,麻烦点,但每一块配件都合你心意。
学习可以先从封装库入门跑通流程,熟了再转手动摸透原理。真到项目里,甚至可以混着用,哪块适合用哪种就用哪种。
能解决问题的写法,就是最好的写法。
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/H1727548