NestJS集成LangChain两条路:封装库躺平 vs 手动硬撸

文章目录

      • 前言
      • [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

相关推荐
markvivv1 小时前
【译】适合在RTX 5090、DGX Spark或类似机器上可运行的最佳新模型是什么?
大数据·人工智能·spark
caimouse1 小时前
ReactOS 窗口系统分析(31):TextOutW 文本输出全链路 — 从用户函数到显示缓冲区的旅程
网络·人工智能·计算机视觉
sunneo1 小时前
每周GitCode开源项目精选
人工智能
zhangfeng11331 小时前
HiDevLab vCANNLab(昇腾两个云端WebIDE)安装codebuddy
人工智能·ai编程·算子开发
leeyi1 小时前
Agent 间 Transfer 交接:用户在不同 Agent 间无缝切换(第93篇-E79)
人工智能·aigc·agent
很楠爱上1 小时前
从“AI 看合同”到可举证的合同决策链:CounterClause(对薄) 的架构设计与工程实践
人工智能·经验分享·python·学习·agent
Java后端的Ai之路1 小时前
02、Python普通工厂模式
开发语言·人工智能·python·设计模式·普通工厂模式
爱奥尼欧1 小时前
14.输出解析器-Pydantic与JSON
人工智能·学习·langchain·json
ReleaseU1 小时前
Claude Code 两天三版本、Cursor 把仓库搬进编辑器:AI 编程工具在卷什么?
人工智能·大模型