一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

AI Agent 开发进阶:用 NestJS 接入 LangChain 与 Ollama

时间:2026-09-17 10:14:01 编辑:袖梨 来源:一聚教程网

在 AI Agent 服务端开发中,将本地大模型封装成稳定、可复用的业务接口是常见需求。借助 NestJS 的模块化结构、LangChain 的模型调用能力和 Ollama 的本地运行环境,可以快速搭建一套基础对话服务。下面从项目初始化开始,逐步完成配置、接口实现、参数校验与故障排查。

NestJS 集成 LangChain + Ollama 实现大模型调用教程

一、环境准备

1. 启动 Ollama 并拉取模型

bash

# 启动服务(默认 http://127.0.0.1:11434)
ollama serve

# 拉取一个模型
ollama pull qwen2.5:7b

# 验证模型可用
curl http://127.0.0.1:11434/api/tags

2. 创建 NestJS 项目(已有项目可跳过)

bash

npm i -g @nestjs/cli
nest new nest-langchain-demo
cd nest-langchain-demo

二、安装依赖

bash

npm install @langchain/ollama @langchain/core
npm install dotenv

注意:@langchain/ollama 要求 Node.js >= 18,不需要安装 langchain 大杂烩包,按需引入即可。


三、配置管理

1. 创建 .env 文件

env

# Ollama 配置
OLLAMA_HOST=http://127.0.0.1:11434
OLLAMA_CHAT_MODEL=qwen2.5:7b
OLLAMA_TEMPERATURE=0.7

2. 创建配置文件 src/config.ts

TypeScript

import 'dotenv/config';

export const config = {
  ollama: {
    host: process.env.OLLAMA_HOST || 'http://127.0.0.1:11434',
    ch@tModel: process.env.OLLAMA_CHAT_MODEL || 'qwen2.5:7b',
    temperature: Number(process.env.OLLAMA_TEMPERATURE ?? 0.7),
  },
};

四、创建模块

1. 生成模块文件

bash

nest g module models
nest g service models
nest g controller models

2. Service 层 src/models/models.service.ts

TypeScript

import { Injectable, Logger } from '@nestjs/common';
import { config } from '../config';
import { ChatOllama } from '@langchain/ollama';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';

@Injectable()
export class ModelsService {
  private readonly logger = new Logger(ModelsService.name);

  // 创建 LLM 实例(单例,全局复用)
  private llm = new ChatOllama({
    model: config.ollama.ch@tModel,
    temperature: config.ollama.temperature,
    baseUrl: config.ollama.host,
    think: false, // 关闭思考模式,减少 token 消耗
  });

  /**
   * 普通对话
   */
  async baseChat(message: string) {
    const response = await this.llm.invoke([new HumanMessage(message)]);

    return {
      question: message,
      answer: response.content,
      usageToken: response.usage_metadata, // token 用量统计
    };
  }

  /**
   * 角色扮演对话
   */
  async ch@tRole(role: string, message: string) {
    const response = await this.llm.invoke([
      new SystemMessage(role),
      new HumanMessage(message),
    ]);

    return {
      role,
      question: message,
      answer: response.content,
      usageToken: response.usage_metadata,
    };
  }
}

3. DTO 校验 src/models/dto/[email protected]

TypeScript

import { IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';

export class BaseChatDto {
  @IsString()
  @IsNotEmpty({ message: 'message 不能为空' })
  @MaxLength(4000)
  message: string;
}

export class RoleChatDto extends BaseChatDto {
  @IsOptional()
  @IsString()
  @MaxLength(500)
  role?: string = '你是一个乐于助人的中文助手';
}

4. Controller 层 src/models/models.controller.ts

TypeScript

import { Body, Controller, Post } from '@nestjs/common';
import { ModelsService } from './models.service';
import { BaseChatDto, RoleChatDto } from './dto/[email protected]';

@Controller('models')
export class ModelsController {
  constructor(private readonly modelsService: ModelsService) {}

  // 普通对话
  @Post('ch@t')
  baseChat(@Body() dto: BaseChatDto) {
    return this.modelsService.baseChat(dto.message);
  }

  // 角色对话
  @Post('ch@t/role')
  ch@tRole(@Body() dto: RoleChatDto) {
    return this.modelsService.ch@tRole(dto.role, dto.message);
  }
}

5. 注册模块 src/models/models.module.ts

TypeScript

import { Module } from '@nestjs/common';
import { ModelsService } from './models.service';
import { ModelsController } from './models.controller';

@Module({
  controllers: [ModelsController],
  providers: [ModelsService],
  exports: [ModelsService], // 导出后其他模块也能用
})
export class ModelsModule {}

6. 根模块引入 src/app.module.ts

TypeScript

import { Module } from '@nestjs/common';
import { ModelsModule } from './models/models.module';

@Module({
  imports: [ModelsModule],
})
export class AppModule {}

7. 开启全局参数校验 src/main.ts

TypeScript

import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(
    new ValidationPipe({ whitelist: true, transform: true }),
  );

  app.setGlobalPrefix('api'); // 统一前缀
  await app.listen(3000);
  console.log('? Server: http://localhost:3000/api');
}
bootstrap();

需要 npm i class-validator class-transformer


五、启动测试

bash

npm run start:dev

调用示例

普通对话:

bash

curl -X POST http://localhost:3000/api/models/ch@t 
  -H "Content-Type: application/json" 
  -d '{"message": "用一句话介绍 NestJS"}'

返回:

JSON

{
  "question": "用一句话介绍 NestJS",
  "answer": "NestJS 是一个基于 Node.js 的渐进式框架,使用 TypeScript 构建,结合了 OOP、FP 和 FRP 范式。",
  "usageToken": {
    "input_tokens": 15,
    "output_tokens": 28,
    "total_tokens": 43
  }
}

角色对话:

bash

curl -X POST http://localhost:3000/api/models/ch@t/role 
  -H "Content-Type: application/json" 
  -d '{"role": "你是一名资深的 Java 架构师", "message": "讲讲微服务拆分的原则"}'

六、常见问题

1. 连接报错 fetch failed

检查 Ollama 是否启动、端口是否正确:

bash

curl http://127.0.0.1:11434   # 应返回 "Ollama is running"

如果是 Docker 部署的 Ollama,baseUrl 不能用 127.0.0.1(容器内回环地址),要用宿主机 IP 或 host.docker.internal

2. 模型不存在 model 'xxx' not found

bash

ollama pull qwen2.5:7b   # 名称必须和 .env 中完全一致

3. 首次调用特别慢

模型第一次加载到内存需要数秒到数十秒,属于正常现象。可在服务启动时预热一次:

TypeScript

async onModuleInit() {
  await this.llm.invoke([new HumanMessage('hi')]); // 预热
}

4. think 参数说明

  • think: false:关闭模型的推理过程输出,只返回最终答案,省 token、速度快
  • think: true:返回思考过程(部分模型支持,如 qwen3、deepseek-r1),response.content 中可能包含 <think>...</think> 标签,需要自行剥离

5. 超时设置

大模型生成较慢时,可在 ChatOllama 配置中增加:

TypeScript

new ChatOllama({
  // ...其他配置
  timeout: 120000, // 2 分钟
  numPredict: 512, // 限制最大生成 token 数,防止无限输出
})

七、目录结构总览

plain

src/
├── config.ts                  # 配置
├── main.ts                    # 入口
├── app.module.ts
└── models/
    ├── dto/
    │   └── [email protected]        # 参数校验
    ├── models.module.ts
    ├── models.controller.ts   # 接口层
    └── models.service.ts      # 业务层(调用 Ollama)

热门栏目