LangChain.jsでベクトルストアを作成する。

# 概要

色々なソースから、RAG用のベクトルストアを作成する方法を書きます。
1. テキストファイル
1. PDF
1. Webページ

## 基本的な流れ
ファイルからテキストを読み込む。
↓
テキストをチャンクに分ける。
↓
ベクトルストアを作成する。

### 埋め込みモデル
埋め込みモデルには、`multilingual-e5-large`を使います。
Webページからベクトルストアを作成するときだけ、`multilingual-e5-large`だと上手くいかなかったのでOpenAIの`text-embedding-3-small`を使います。（お金がかかる）

トークンが多すぎて`multilingual-e5-large`だと使えないのかもしれないです。

LangChain.jsでは埋め込みモデルは以下のようにインスタンスします。
apikeyは.envファイルに書き込んでおきます。

```js
// embeddings =========================================================================

const emb_e5 = new HuggingFaceInferenceEmbeddings({
    apiKey: process.env.HUGGINGFACEHUB_API_KEY,
    model: "intfloat/multilingual-e5-large"
});

const emb_openai = new OpenAIEmbeddings({
    openAIApiKey: process.env.OPEN_AI_API_KEY, 
});

```

### 近似最近傍探索ライブラリ
近似最近傍探索ライブラリにはFaissを使います。

## 1. テキストファイルからベクトルストアを作成する
テキストファイルはファイルを読み込むだけです。
```js
// make index =========================================================================
const make_index_text = async () => {
    // textファイルを読み込み
    const text = fs.readFileSync("data.txt", "utf8");

    // textをチャンクに分ける
    const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 400, chunkOverlap: 50 });
    const docs = await textSplitter.createDocuments([text]);

    // ベクトルストアを作成
    const vectorStore = await FaissStore.fromDocuments(
        docs,
        emb_e5    // or emb_openai
    );

    // indexTEXTというフォルダに保存
    await vectorStore.save("indexTEXT");
};
```

### 全体
```js
// import ============================================================================

import * as fs from "fs";

import { OpenAIEmbeddings } from "@langchain/openai";
import { HuggingFaceInferenceEmbeddings } from "@langchain/community/embeddings/hf";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { FaissStore } from "@langchain/community/vectorstores/faiss";

// embeddings =========================================================================

const emb_e5 = new HuggingFaceInferenceEmbeddings({
  apiKey: process.env.HUGGINGFACEHUB_API_KEY,
  model: "intfloat/multilingual-e5-large"
});

const emb_openai = new OpenAIEmbeddings({
    openAIApiKey: process.env.OPEN_AI_API_KEY, 
});

// make index =========================================================================

const make_index_text = async () => {
    const text = fs.readFileSync("data.txt", "utf8");
      
    const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 400, chunkOverlap: 50 });
    const docs = await textSplitter.createDocuments([text]);
  
    const vectorStore = await FaissStore.fromDocuments(
        docs,
        emb_e5    // or emb_openai
    );
  
    await vectorStore.save("indexTEXT");    // indexフォルダ
};

// do function =====================================================================

make_index_text();

```

## 2. PDFからベクトルストアを作成する
PDFは`PDFLoader`を使ってテキストを読み込みます。
```js
// make index =========================================================================

const make_index_pdf = async () => {
    // PDFを読み込み＆チャンクに分ける
    const loader = new PDFLoader("data.pdf");
    const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 400, chunkOverlap: 50 });
    const docs = await loader.loadAndSplit(textSplitter);

    // ベクトルストアを作成
    const vectorStore = await FaissStore.fromDocuments(
        docs,
        emb_e5    // or emb_openai
    );

    // indexPDFに保存
    await vectorStore.save("indexPDF");    // indexフォルダ
};
```

### 全体
```js
// import ============================================================================

import { PDFLoader } from "langchain/document_loaders/fs/pdf";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { FaissStore } from "@langchain/community/vectorstores/faiss";
import { HuggingFaceInferenceEmbeddings } from "@langchain/community/embeddings/hf";
import { OpenAIEmbeddings } from "@langchain/openai";

// embeddings =========================================================================


const emb_e5 = new HuggingFaceInferenceEmbeddings({
  apiKey: process.env.HUGGINGFACEHUB_API_KEY,
  model: "intfloat/multilingual-e5-large"
});

const emb_openai = new OpenAIEmbeddings({
    openAIApiKey: process.env.OPEN_AI_API_KEY, 
});


// make index =========================================================================

const make_index_pdf = async () => {
    // PDFを読み込み＆チャンクに分ける
    const loader = new PDFLoader("data.pdf");
    const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 400, chunkOverlap: 50 });
    const docs = await loader.loadAndSplit(textSplitter);

    // ベクトルストアを作成
    const vectorStore = await FaissStore.fromDocuments(
        docs,
        emb_e5    // or emb_openai
    );

    // indexPDFに保存
    await vectorStore.save("indexPDF");    // indexフォルダ
};

// do function =====================================================================

make_index_pdf();

```

## 3. Webページからベクトルストアを作成する
Webページは`RecursiveUrlLoader`を使って読み込みます。
設定したURLの子ディレクトリも全部読み込まれます。

埋め込みモデルには`multilingual-e5-large`が使えなかったので、OpenAIのモデルを使ってます。
```js
// make index =========================================================================
const make_index_web = async () => {
    // Webページからテキスト読み込み＆チャンクに分ける
    const url = "https://www.toraichi.com/"
    const compiledConvert = compile({ wordwrap: 130 });
    const loader = new RecursiveUrlLoader(url, {
        extractor: compiledConvert,
        maxDepth: 1,
    });
    const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 500, chunkOverlap: 100 });
    const docs = await loader.loadAndSplit(textSplitter);

    // ベクトルストアを作成
    const vectorStore = await FaissStore.fromDocuments(
        docs, 
        emb_openai
    );

    // indexWEBフォルダに保存
    await vectorStore.save("indexWEB");
};

```

### 全体
```js
// import ============================================================================

import { compile } from "html-to-text";
import { RecursiveUrlLoader } from "langchain/document_loaders/web/recursive_url";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { OpenAIEmbeddings } from "@langchain/openai";
import { FaissStore } from "@langchain/community/vectorstores/faiss";

// embeddings =========================================================================

const emb_openai = new OpenAIEmbeddings({
    openAIApiKey: process.env.OPEN_AI_API_KEY, 
});

// make index =========================================================================
const make_index_web = async () => {
    // Webページからテキスト読み込み＆チャンクに分ける
    const url = "https://www.なんかのサイト.com/"
    const compiledConvert = compile({ wordwrap: 130 });
    const loader = new RecursiveUrlLoader(url, {
        extractor: compiledConvert,
        maxDepth: 1,
    });
    const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 500, chunkOverlap: 100 });
    const docs = await loader.loadAndSplit(textSplitter);

    // ベクトルストアを作成
    const vectorStore = await FaissStore.fromDocuments(
        docs, 
        emb_openai
    );

    // indexWEBフォルダに保存
    await vectorStore.save("indexWEB");
};

// do function =====================================================================

make_index_web();
```

おわり