🕌

LangChain で外部からデータを参照 前編(Node.js)

2024/06/02に公開

はじめに

この記事では、公式のドキュメントを使いながら LangChain で外部から入力された情報を参照する方法を紹介します。こちらが記事です。チャットモデル以外に知識をあたえるために外部データを読み込ませて応答を拡張できます。本記事ではその方法について記述します。

https://js.langchain.com/v0.2/docs/how_to/chatbots_retrieval/

TypeScript / JavaScript での GitHub リポジトリーを公開している実装例はすくないので記事化しました。作業リポジトリはこちらです。

https://github.com/hayato94087/langchain-chatbots_retrieval-sample

記事が長いため、前編後編 に分けて記述しています。本記事は 前編 です。

LangChain x TypeScript での実装例を以下の記事で紹介しています。

LangChain とは

LangChain は、大規模言語モデル(LLM)を活用したアプリケーションの開発を支援するフレームワークです。

https://js.langchain.com/v0.2/docs/introduction/

作業プロジェクトの準備

TypeScript の簡易プロジェクトを作成します。

長いので折りたたんでおきます。

package.json を作成

package.json を作成します。

$ mkdir -p langchain-chatbots_retrieval-sample
$ cd langchain-chatbots_retrieval-sample
$ pnpm init

下記で package.json を上書きします。

package.json
{
  "name": "langchain-chatbots_retrieval-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "scripts": {
    "typecheck": "tsc --noEmit",
    "dev": "vite-node index.ts",
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
}

TypeScript & vite-node をインストール

TypeScript と vite-node をインストールします。補足としてこちらの理由のため ts-node ではなく vite-node を利用します。

$ pnpm install -D typescript vite-node @types/node

TypeScriptの設定ファイルを作成

tsconfig.json を作成します。

$ npx tsc --init

tsconfig.json を上書きします。

tsconfig.json
{
  "compilerOptions": {
    /* Base Options: */
    "esModuleInterop": true,
    "skipLibCheck": true,
    "target": "ES2022",
    "allowJs": true,
    "resolveJsonModule": true,
    "moduleDetection": "force",
    "isolatedModules": true,

    /* Strictness */
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "checkJs": true,

    /* Bundled projects */
    "noEmit": true,
    "outDir": "dist",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "jsx": "preserve",
    "incremental": true,
    "sourceMap": true,
  },
  "include": ["**/*.ts", "**/*.js"],
  "exclude": ["node_modules", "dist"]
}

git を初期化します。

$ git init

.gitignore を作成します。

$ touch .gitignore
.gitignore
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build
dist/

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local
.env

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

動作確認コードを作成

動作を確認するためのコードを作成します。

$ touch index.ts
index.ts
console.log('Hello, World');

型チェック

型チェックします。

$ pnpm run typecheck

動作確認

動作確認を実施します。

$ pnpm run dev

Hello, World

コミットします。

$ git add .
$ git commit -m "初回コミット"

LangChain をインストール

LangChain をインストールします。

$ pnpm add langchain @langchain/core

コミットします。

$ git add .
$ git commit -m "LangChainをインストール"

言語モデルの選択

LangChain は、多くの異なる言語モデルをサポートしており、それらを自由に選んで使用できます。

例えば、以下のような言語モデルを選択できます。

  • OpenAI
  • Anthropic
  • FireworksAI
  • MistralAI
  • Groq
  • VertexAI

ここでは OpenAI を利用します。OpenAI を LangChain で利用するためのパッケージをインストールします。

$ pnpm add @langchain/openai

コミットします。

$ git add .
$ git commit -m "LangChainでOpenAIを利用するためのパッケージをインストール"

OpenAI API キーを取得

OpenAI API キーの取得方法はこちらを参照してください。

https://zenn.dev/hayato94087/articles/85378e1f7bc0e5#openai-の-apiキーの取得

環境変数の設定

環境変数に OpenAI キーを追加します。<your-api-key> に自身の API キーを設定してください。

$ touch .env
.env
# OPENAI_API_KEY は OpenAI の API キーです。
OPENAI_API_KEY='<your-api-key>'

Node.js で環境変数を利用するために dotenv をインストールします。

$ pnpm i -D dotenv

コミットします。

$ touch .env.example
.env
# OPENAI_API_KEY は OpenAI の API キーです。
OPENAI_API_KEY='<your-api-key>'
$ git add .
$ git commit -m "環境変数を設定"

基礎編

まず、シンプルに Tools を使わない方法で LLM を使ってみます。

コードの作成

コードを作成します。

$ touch demo01.ts
demo01.ts
import { ChatOpenAI } from "@langchain/openai";
import 'dotenv/config'

const model = new ChatOpenAI({
  model: "gpt-3.5-turbo",
  temperature: 0
});

const result = await model.invoke("猫についてジョークを言ってください");
console.log(result)

ローカルで実行します。

$ pnpm vite-node demo01.ts

コミットします。

$ git add .
$ git commit -m "簡易的にLLMに問い合わせて答えをもらう"

コードの解説

OpenAI の言語モデルを利用するために ChatOpenAI をインポートします。

import { ChatOpenAI } from "@langchain/openai";

gpt-3.5-turbo のモデルを選択します。temperature は 0 に設定します。temperature が低いほど、モデルの出力はより予測可能になります。

const model = new ChatOpenAI({
  model: "gpt-3.5-turbo",
  temperature: 0
});

invoke メソッドを実行し、LLM にリクエストを送信します。AIMessage のオブジェクトが返されます。

const result = await model.invoke("猫についてジョークを言ってください");
console.log(result)

コンソール出力

AIMessage {
  lc_serializable: true,
  lc_kwargs: {
    content: 'Q: 猫がパソコンを使うときに使うプログラミング言語は何でしょう?\nA: プロクラスミング言語!',
    tool_calls: [],
    invalid_tool_calls: [],
    additional_kwargs: { function_call: undefined, tool_calls: undefined },
    response_metadata: {}
  },
  lc_namespace: [ 'langchain_core', 'messages' ],
  content: 'Q: 猫がパソコンを使うときに使うプログラミング言語は何でしょう?\nA: プロクラスミング言語!',
  name: undefined,
  additional_kwargs: { function_call: undefined, tool_calls: undefined },
  response_metadata: {
    tokenUsage: { completionTokens: 51, promptTokens: 22, totalTokens: 73 },
    finish_reason: 'stop'
  },
  tool_calls: [],
  invalid_tool_calls: [],
  usage_metadata: undefined
}

Retriever の作成

ここではウェブからデータを取得、ベクターストアにデータをストア、ベクターとストアからデータを取得する Retriever を作成します。

インストール

cheerioをインストールします。

$ pnpm add @langchain/openai cheerio @langchain/community

コードの作成

コードを作成します。

$ touch demo02.ts
demo02.ts
import "cheerio";
import { CheerioWebBaseLoader } from "@langchain/community/document_loaders/web/cheerio";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";

const loader = new CheerioWebBaseLoader(
  "https://ja.wikipedia.org/wiki/LangChain"
);
const rawDocs = await loader.load();
console.log(rawDocs);

const textSplitter = new RecursiveCharacterTextSplitter({
  chunkSize: 500,
  chunkOverlap: 0,
});
const allSplits = await textSplitter.splitDocuments(rawDocs);
console.log(allSplits);

const vectorstore = await MemoryVectorStore.fromDocuments(
  allSplits,
  new OpenAIEmbeddings()
);

const retriever = vectorstore.asRetriever(2);
const docs = await retriever.invoke("LangChainのライセンス形式は?");
console.log(docs);
$ pnpm vite-node demo02.ts

コミットします。

$ git add .
$ git commit -m "Retrieverの作成"

コードの解説

cheerioを利用して、指定した URL の HTML を取得します。

import "cheerio";
import { CheerioWebBaseLoader } from "@langchain/community/document_loaders/web/cheerio";

const loader = new CheerioWebBaseLoader(
  "https://ja.wikipedia.org/wiki/LangChain"
);
const rawDocs = await loader.load();
console.log(rawDocs);

コンソール出力

[
  Document {
    pageContent: 'コンテンツにスキップ\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      'メインメニュー\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\n' +
      '\t\t\n' +
      '\n' +
      '\t\n' +
      '\tメインメニュー\n' +
      '\tサイドバーに移動\n' +
      '\t非表示\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\t案内\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\tメインページコミュニティ・ポータル最近の出来事新しいページ最近の更新おまかせ表示練習用ページアップロード (ウィキメディア・コモンズ)\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\tヘルプ\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\tヘルプ井戸端お知らせバグの報告寄付ウィキペディアに関するお問い合わせ\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '検索\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t検索\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\tアカウント作成\n' +
      '\n' +
      'ログイン\n' +
      '\n' +
      '\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '個人用ツール\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t アカウント作成 ログイン\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\tログアウトした編集者のページ もっと詳しく\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t投稿記録トーク\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\n' +
      '\t目次\n' +
      '\tサイドバーに移動\n' +
      '\t非表示\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\tページ先頭\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t1歴史\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t2統合化\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t3脚注\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '目次の表示・非表示を切り替え\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\tLangChain\n' +
      '\t\t\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '7の言語版\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\tEnglishفارسیहिन्दी한국어PortuguêsTürkçe中文\n' +
      '\t\t\t\n' +
      '\t\t\tリンクを編集\n' +
      '\t\t\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\tページノート\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t日本語\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t閲覧編集履歴表示\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\tツール\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\tツール\n' +
      '\tサイドバーに移動\n' +
      '\t非表示\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\t操作\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t閲覧編集履歴表示\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\t全般\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\tリンク元関連ページの更新状況ファイルをアップロード特別ページこの版への固定リンクページ情報このページを引用短縮URLを取得するQRコードをダウンロードウィキデータ項目\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\t印刷/書き出し\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\tブックの新規作成PDF 形式でダウンロード印刷用バージョン\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\t\n' +
      '\t\t\n' +
      '\n' +
      '\t\t\t\t\t\t出典: フリー百科事典『ウィキペディア(Wikipedia)』\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\tLangChain\n' +
      '開発元\n' +
      'Harrison Chase初版\n' +
      '2022年10月 (1年前) (2022-10)\n' +
      '最新版\n' +
      '0.1.16[1]\n' +
      '   リポジトリ\n' +
      'github.com/langchain-ai/langchainプログラミング言語\n' +
      'PythonとJavaScript種別\n' +
      '大規模言語モデルアプリケーションソフトウェア開発のためのソフトウェアフレームワークライセンス\n' +
      'MITライセンス公式サイト\n' +
      'LangChain.comテンプレートを表示\n' +
      'LangChain(ラングチェイン)は、大規模言語モデル(LLM)を使ったアプリケーションソフトウェアの作成を簡素化するように設計されたフレームワークである。言語モデル統合フレームワークとして、LangChainのユースケースは、文書解析や要約、チャットボット、コード解析など、一般的な言語モデルのユースケースとほぼ重なっている[2]。\n' +
      '\n' +
      '歴史[編集]\n' +
      'LangChainは、機械学習スタートアップ企業Robust Intelligenceに勤務していたハリソン・チェイス(Harrison Chase)によって、2022年10月にオープンソースプロジェクトとして立ち上げられた。このプロジェクトはすぐに人気を博し、GitHubでは数百名のコントリビューターによる改善、Twitterではトレンドの議論、プロジェクトのDiscordサーバ上では活発な活動、多くのYouTubeチュートリアル、サンフランシスコとロンドンでのミートアップなどが行われた。2023年4月、LangChainは法人化し、新しいスタートアップは、ベンチャーキャピタルBenchmark(英語版)から1,000万ドルの資金投資を発表した1週間後、セコイア・キャピタルから少なくとも2億ドルの評価額で2,000万ドル以上の資金を調達した[3][4]。\n' +
      '2023年10月、LangChainは、LCEL(LangChain Expression Language)プロトタイプから本番運用可能なアプリケーションへの移行を促進するために設計された展開ツールLangServeを発表した[5]。\n' +
      '\n' +
      '統合化[編集]\n' +
      '2023年3月現在、LangChainはAmazon、Google、Microsoft Azureなどのクラウドストレージを含むシステムとの統合を含め、次のようなソフトウェアやサービスとの連携や、機能を有している。\n' +
      '\n' +
      'ニュース、映画情報、天気などのAPIラッパー\n' +
      'Bashによる要約、構文とセマンティクスのチェック、およびシェルスクリプトの実行\n' +
      '複数のウェブスクレイピング・サブシステムとテンプレート\n' +
      '少数ショット学習用のプロンプト生成\n' +
      'コード内の「todo」タスクの検索と要約\n' +
      'Google Driveドキュメント、スプレッドシート、プレゼンテーションの要約、抽出、作成\n' +
      'Google検索とMicrosoft Bingウェブ検索\n' +
      'OpenAI、Anthropic、Hugging Face言語モデル\n' +
      'iFixitの修理ガイドとWikiの検索と要約\n' +
      'MapReduceによる質問回答、ドキュメントの結合、質問生成\n' +
      'n-gram(英語版)重複スコアリング\n' +
      'PDFファイルのテキスト抽出と操作のためのPyPDF、pdfminer、fitz、pymupdf\n' +
      'PythonおよびJavaScriptのコード生成、分析、デバッグ\n' +
      'ベクトル埋め込みを保存・取得するMilvusベクトルデータベース[6]\n' +
      '埋め込みとデータオブジェクトをキャッシュするWeaviateベクトルデータベース[7]\n' +
      'Redisキャッシュデータベースストレージ\n' +
      'APIリクエストのためのPython RequestsWrapperとその他のメソッド\n' +
      'JSONサポートを含む、SQLおよびNoSQLデータベース\n' +
      'ロギングを含むStreamlit\n' +
      'k-最近傍探索のためのテキストマッピング\n' +
      'タイムゾーン変換とカレンダー操作\n' +
      'スレッドおよび非同期サブプロセス(英語版)の実行におけるスタックシンボルの追跡と記録\n' +
      'Wolfram AlphaのWebサイトとSDK[8]\n' +
      '2023年4月現在、50種類を超えるドキュメント種類とデータソースを読み取ることができる[9]。\n' +
      '\n' +
      '脚注[編集]\n' +
      '\n' +
      '\n' +
      '^ “Release 0.1.16” (11 4月 2024). 23 4月 2024閲覧。\n' +
      '\n' +
      '^ Buniatyan, Davit (2023年). “Code Understanding Using LangChain”. Activeloop. 2023年5月13日閲覧。\n' +
      '\n' +
      '^ Palazzolo, Stephanie (2023年4月13日). “AI startup LangChain taps Sequoia to lead funding round at a valuation of at least $200 million” (英語).  オリジナルの2023年4月18日時点におけるアーカイブ。. https://web.archive.org/web/20230418041335/https://www.businessinsider.com/sequoia-leads-funding-round-generative-artificial-intelligence-startup-langchain-2023-4 2023年4月18日閲覧。 \n' +
      '\n' +
      `^ Griffith, Erin; Metz, Cade (2023年3月14日). “‘Let 1,000 Flowers Bloom’: A.I. Funding Frenzy Escalates” (英語). The New York Times. .mw-parser-output cite.citation{font-style:inherit;word-wrap:break-word}.mw-parser-output .citation q{quotes:"\\"""\\"""'""'"}.mw-parser-output .citation.cs-ja1 q,.mw-parser-output .citation.cs-ja2 q{quotes:""""""""}.mw-parser-output .citation:target{background-color:rgba(0,127,255,0.133)}.mw-parser-output .id-lock-free a,.mw-parser-output .citation .cs1-lock-free a{background:url("//upload.wikimedia.org/wikipedia/commons/6/65/Lock-green.svg")right 0.1em center/9px no-repeat}.mw-parser-output .id-lock-limited a,.mw-parser-output .id-lock-registration a,.mw-parser-output .citation .cs1-lock-limited a,.mw-parser-output .citation .cs1-lock-registration a{background:url("//upload.wikimedia.org/wikipedia/commons/d/d6/Lock-gray-alt-2.svg")right 0.1em center/9px no-repeat}.mw-parser-output .id-lock-subscription a,.mw-parser-output .citation .cs1-lock-subscription a{background:url("//upload.wikimedia.org/wikipedia/commons/a/aa/Lock-red-alt-2.svg")right 0.1em center/9px no-repeat}.mw-parser-output .cs1-ws-icon a{background:url("//upload.wikimedia.org/wikipedia/commons/4/4c/Wikisource-logo.svg")right 0.1em center/12px no-repeat}.mw-parser-output .cs1-code{color:inherit;background:inherit;border:none;padding:inherit}.mw-parser-output .cs1-hidden-error{display:none;color:#d33}.mw-parser-output .cs1-visible-error{color:#d33}.mw-parser-output .cs1-maint{display:none;color:#3a3;margin-left:0.3em}.mw-parser-output .cs1-format{font-size:95%}.mw-parser-output .cs1-kern-left{padding-left:0.2em}.mw-parser-output .cs1-kern-right{padding-right:0.2em}.mw-parser-output .citation .mw-selflink{font-weight:inherit}ISSN 0362-4331.  オリジナルの2023年4月18日時点におけるアーカイブ。. https://web.archive.org/web/20230418041335/https://www.nytimes.com/2023/03/14/technology/ai-funding-boom.html 2023年4月18日閲覧。 \n` +
      '\n' +
      '^ “Introducing LangServe, the best way to deploy your LangChains” (英語). LangChain Blog (2023年10月12日). 2023年10月17日閲覧。\n' +
      '\n' +
      '^ “Milvus — LangChain”. python.langchain.com. 2023年10月29日閲覧。\n' +
      '\n' +
      '^ “Weaviate — LangChain 0.0.184”. python.langchain.com. 2023年5月29日閲覧。[リンク切れ]\n' +
      '\n' +
      "^ Hug, Daniel Patrick (2023年3月8日). “Hierarchical topic tree of LangChain's integrations”. GitHub. 2023年4月29日時点のオリジナルよりアーカイブ。2023年4月18日閲覧。\n" +
      '\n' +
      '^ “Document Loaders — LangChain 0.0.142”. python.langchain.com. 2023年4月18日時点のオリジナルよりアーカイブ。2023年4月18日閲覧。\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '<img src="https://login.wikimedia.org/wiki/Special:CentralAutoLogin/start?type=1x1" alt="" width="1" height="1" style="border: none; position: absolute;">\n' +
      '「https://ja.wikipedia.org/w/index.php?title=LangChain&oldid=98654458」から取得\n' +
      '\t\t\t\t\tカテゴリ: 人工知能大規模言語モデルソフトウェアフレームワーク2022年のソフトウェア隠しカテゴリ: 外部リンクがリンク切れになっている記事/2023年8月\n' +
      '\t\t\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t 最終更新 2023年12月30日 (土) 07:25 (日時は個人設定で未設定ならばUTC)。\n' +
      '\tテキストはクリエイティブ・コモンズ 表示-継承ライセンスのもとで利用できます。追加の条件が適用される場合があります。詳細については利用規約を参照してください。\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\tプライバシー・ポリシー\n' +
      '\tウィキペディアについて\n' +
      '\t免責事項\n' +
      '\t行動規範\n' +
      '\t開発者\n' +
      '\t統計\n' +
      '\tCookieに関する声明\n' +
      '\tモバイルビュー\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\t \n' +
      ' \n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\n' +
      '本文の横幅制限を有効化/無効化\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '(RLQ=window.RLQ||[]).push(function(){mw.config.set({"wgHostname":"mw-web.codfw.main-f665c695c-hrg8s","wgBackendResponseTime":121,"wgPageParseReport":{"limitreport":{"cputime":"0.320","walltime":"0.697","ppvisitednodes":{"value":6956,"limit":1000000},"postexpandincludesize":{"value":51442,"limit":2097152},"templateargumentsize":{"value":21311,"limit":2097152},"expansiondepth":{"value":39,"limit":100},"expensivefunctioncount":{"value":6,"limit":500},"unstrip-depth":{"value":1,"limit":20},"unstrip-size":{"value":10721,"limit":5000000},"entityaccesscount":{"value":1,"limit":400},"timingprofile":["100.00%  440.308      1 -total"," 64.52%  284.091      1 Template:Infobox_software"," 64.08%  282.137      2 Template:Infobox"," 53.07%  233.658      1 Template:Infobox_software/simple"," 51.09%  224.961      1 Template:Wikidata"," 30.78%  135.548      1 Template:Reflist"," 17.94%   78.980      2 Template:Cite_news"," 16.90%   74.401      2 Template:Citation/core"," 10.17%   44.799      1 Template:Citation/identifier","  9.74%   42.888      1 Template:ISSN"]},"scribunto":{"limitreport-timeusage":{"value":"0.150","limit":"10.000"},"limitreport-memusage":{"value":6754214,"limit":52428800}},"cachereport":{"origin":"mw-web.codfw.main-5fd47cfb8c-ttdrx","timestamp":"20240519102114","ttl":1085927,"transientcontent":true}}});});\n' +
      '{"@context":"https:\\/\\/schema.org","@type":"Article","name":"LangChain","url":"https:\\/\\/ja.wikipedia.org\\/wiki\\/LangChain","sameAs":"http:\\/\\/www.wikidata.org\\/entity\\/Q117340550","mainEntity":"http:\\/\\/www.wikidata.org\\/entity\\/Q117340550","author":{"@type":"Organization","name":"Contributors to Wikimedia projects"},"publisher":{"@type":"Organization","name":"\\u30a6\\u30a3\\u30ad\\u30e1\\u30c7\\u30a3\\u30a2\\u8ca1\\u56e3","logo":{"@type":"ImageObject","url":"https:\\/\\/www.wikimedia.org\\/static\\/images\\/wmf-hor-googpub.png"}},"datePublished":"2023-11-19T04:56:18Z","dateModified":"2023-12-30T07:25:18Z","image":"https:\\/\\/upload.wikimedia.org\\/wikipedia\\/commons\\/3\\/3f\\/LangChain_logo.png"}\n' +
      '\n',
    metadata: { source: 'https://ja.wikipedia.org/wiki/LangChain' }
  }
]

LLM のコンテキストウィンドウが処理できるより小さなチャンクに分割します。

import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";

const textSplitter = new RecursiveCharacterTextSplitter({
  chunkSize: 500,
  chunkOverlap: 0,
});
const allSplits = await textSplitter.splitDocuments(rawDocs);
console.log(allSplits);

コンソール出力

[
  Document {
    pageContent: 'コンテンツにスキップ\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      'メインメニュー\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\n' +
      '\t\t\n' +
      '\n' +
      '\t\n' +
      '\tメインメニュー\n' +
      '\tサイドバーに移動\n' +
      '\t非表示\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\t案内\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\tメインページコミュニティ・ポータル最近の出来事新しいページ最近の更新おまかせ表示練習用ページアップロード (ウィキメディア・コモンズ)\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\tヘルプ\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\tヘルプ井戸端お知らせバグの報告寄付ウィキペディアに関するお問い合わせ\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\n' +
      '検索\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t検索',
    metadata: {
      source: 'https://ja.wikipedia.org/wiki/LangChain',
      loc: [Object]
    }
  },
  Document {
    pageContent: 'アカウント作成\n' +
      '\n' +
      'ログイン\n' +
      '\n' +
      '\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '個人用ツール\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t アカウント作成 ログイン\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\tログアウトした編集者のページ もっと詳しく\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t投稿記録トーク\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\n' +
      '\t目次\n' +
      '\tサイドバーに移動\n' +
      '\t非表示\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\tページ先頭\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t1歴史\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t2統合化\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t3脚注\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\n' +
      '\n' +
      '\n' +
      '\n' +
      '\t\t\t\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\n' +
      '\t\t\n' +
      '\t\t\t\n' +
      '\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\t\n' +
      '\n' +
      '\t\n' +
      '\t\n' +
      '\n' +
      '目次の表示・非表示を切り替え',
    metadata: {
      source: 'https://ja.wikipedia.org/wiki/LangChain',
      loc: [Object]
    }
  },
  ...
]

ベクターストアを作成します。先程、特徴量化したチャンクをベクターストアに保存詩ます。ベクターストアはメモリー上に展開します。

import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";

const vectorstore = await MemoryVectorStore.fromDocuments(
  allSplits,
  new OpenAIEmbeddings()
);

ベクターストアから情報を取得する Retriever を作成します。

const retriever = vectorstore.asRetriever(2);
const docs = await retriever.invoke("LangChainのライセンス形式は?");
console.log(docs);

コンソール出力

[
  Document {
    pageContent: '出典: フリー百科事典『ウィキペディア(Wikipedia)』\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\tLangChain\n' +
      '開発元\n' +
      'Harrison Chase初版\n' +
      '2022年10月 (1年前) (2022-10)\n' +
      '最新版\n' +
      '0.1.16[1]\n' +
      '   リポジトリ\n' +
      'github.com/langchain-ai/langchainプログラミング言語\n' +
      'PythonとJavaScript種別\n' +
      '大規模言語モデルアプリケーションソフトウェア開発のためのソフトウェアフレームワークライセンス\n' +
      'MITライセンス公式サイト\n' +
      'LangChain.comテンプレートを表示\n' +
      'LangChain(ラングチェイン)は、大規模言語モデル(LLM)を使ったアプリケーションソフトウェアの作成を簡素化するように設計されたフレームワークである。言語モデル統合フレームワークとして、LangChainのユースケースは、文書解析や要約、チャットボット、コード解析など、一般的な言語モデルのユースケースとほぼ重なっている[2]。',
    metadata: {
      source: 'https://ja.wikipedia.org/wiki/LangChain',
      loc: [Object]
    }
  },
  Document {
    pageContent: '歴史[編集]\n' +
      'LangChainは、機械学習スタートアップ企業Robust Intelligenceに勤務していたハリソン・チェイス(Harrison Chase)によって、2022年10月にオープンソースプロジェクトとして立ち上げられた。このプロジェクトはすぐに人気を博し、GitHubでは数百名のコントリビューターによる改善、Twitterではトレンドの議論、プロジェクトのDiscordサーバ上では活発な活動、多くのYouTubeチュートリアル、サンフランシスコとロンドンでのミートアップなどが行われた。2023年4月、LangChainは法人化し、新しいスタートアップは、ベンチャーキャピタルBenchmark(英語版)から1,000万ドルの資金投資を発表した1週間後、セコイア・キャピタルから少なくとも2億ドルの評価額で2,000万ドル以上の資金を調達した[3][4]。',
    metadata: {
      source: 'https://ja.wikipedia.org/wiki/LangChain',
      loc: [Object]
    }
  }
]
const docs2 = await retriever.invoke("LangChainがサポートするプログラミング言語は?");
console.log(docs2);

コンソール出力

[
  Document {
    pageContent: '出典: フリー百科事典『ウィキペディア(Wikipedia)』\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\t\n' +
      '\t\t\t\t\tLangChain\n' +
      '開発元\n' +
      'Harrison Chase初版\n' +
      '2022年10月 (1年前) (2022-10)\n' +
      '最新版\n' +
      '0.1.16[1]\n' +
      '   リポジトリ\n' +
      'github.com/langchain-ai/langchainプログラミング言語\n' +
      'PythonとJavaScript種別\n' +
      '大規模言語モデルアプリケーションソフトウェア開発のためのソフトウェアフレームワークライセンス\n' +
      'MITライセンス公式サイト\n' +
      'LangChain.comテンプレートを表示\n' +
      'LangChain(ラングチェイン)は、大規模言語モデル(LLM)を使ったアプリケーションソフトウェアの作成を簡素化するように設計されたフレームワークである。言語モデル統合フレームワークとして、LangChainのユースケースは、文書解析や要約、チャットボット、コード解析など、一般的な言語モデルのユースケースとほぼ重なっている[2]。',
    metadata: {
      source: 'https://ja.wikipedia.org/wiki/LangChain',
      loc: [Object]
    }
  },
  Document {
    pageContent: '歴史[編集]\n' +
      'LangChainは、機械学習スタートアップ企業Robust Intelligenceに勤務していたハリソン・チェイス(Harrison Chase)によって、2022年10月にオープンソースプロジェクトとして立ち上げられた。このプロジェクトはすぐに人気を博し、GitHubでは数百名のコントリビューターによる改善、Twitterではトレンドの議論、プロジェクトのDiscordサーバ上では活発な活動、多くのYouTubeチュートリアル、サンフランシスコとロンドンでのミートアップなどが行われた。2023年4月、LangChainは法人化し、新しいスタートアップは、ベンチャーキャピタルBenchmark(英語版)から1,000万ドルの資金投資を発表した1週間後、セコイア・キャピタルから少なくとも2億ドルの評価額で2,000万ドル以上の資金を調達した[3][4]。',
    metadata: {
      source: 'https://ja.wikipedia.org/wiki/LangChain',
      loc: [Object]
    }
  }
]

Document chains

理想は、先ほど作成した Retriever を利用し、LLM にユーザーから質問があった場合、ベクターストアを検索し、検索結果から回答を作成することです。

が、今回は、Retriever には固定の質問「ライセンス形式は?」にしておきベクターストアから情報を取得します。その情報をもとに、ユーザーからの質問に回答するプロンプトを作成します。LLM にはハルシネーションがあるため、そのハルシネーションが抑えられているかこの例では確かめます。

コードの作成

コードを作成します。

$ touch demo03.ts
demo03.ts
import "cheerio";
import { CheerioWebBaseLoader } from "@langchain/community/document_loaders/web/cheerio";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";

const llm = new ChatOpenAI({
  model: "gpt-3.5-turbo",
  temperature: 0
});

const loader = new CheerioWebBaseLoader(
  "https://ja.wikipedia.org/wiki/LangChain"
);
const rawDocs = await loader.load();

const textSplitter = new RecursiveCharacterTextSplitter({
  chunkSize: 500,
  chunkOverlap: 0,
});
const allSplits = await textSplitter.splitDocuments(rawDocs);

const vectorstore = await MemoryVectorStore.fromDocuments(
  allSplits,
  new OpenAIEmbeddings()
);

const retriever = vectorstore.asRetriever(3);
const docs = await retriever.invoke("ライセンス形式は?");

const SYSTEM_TEMPLATE = `# 指示
以下の質問に回答してください。質問に対する情報がコンテキストによって提供されない場合、または明確な情報源が存在しない場合は、『わかりません』とだけ回答してください。推測や創作はしないでください。

質問に対する情報が見つからない場合、必ず『わかりません』と回答してください。例えば、以下の質問に対してコンテキストに情報が含まれない場合です。

質問:「少年ジャンプで掲載されていた『ナルト』について教えて

# コンテキスト
{context}

<context>
{context}
</context>
`;

const questionAnsweringPrompt = ChatPromptTemplate.fromMessages([
  ["system", SYSTEM_TEMPLATE],
  new MessagesPlaceholder("messages"),
]);

const documentChain = await createStuffDocumentsChain({
  llm,
  prompt: questionAnsweringPrompt,
});

const result = await documentChain.invoke({
  messages: [new HumanMessage("LangChainのライセンス形式は?")],
  context: docs,
});
console.log(result);

const result2 = await documentChain.invoke({
  messages: [new HumanMessage("LangChainのライセンス形式は?")],
  context: [],
});
console.log(result2);

ローカルで実行します。

$ pnpm vite-node demo03.ts

コミットします。

$ git add .
$ git commit -m "Document chains の作成"

コードの解説

先ほどの説明通り、Retriever には固定の質問「ライセンス形式は?」にしておきベクターストアから情報を取得しています。

const docs = await retriever.invoke("ライセンス形式は?");

LLM に送信するプロンプトのテンプレートを作成します。ユーザーからの質問は {context} に指定した内容から回答するように指示を出します。{context} には先ほど取得したウェブから取得したドキュメントが入ります。

const SYSTEM_TEMPLATE = `# 指示
以下の質問に回答してください。質問に対する情報がコンテキストによって提供されない場合、または明確な情報源が存在しない場合は、『わかりません』とだけ回答してください。推測や創作はしないでください。

質問に対する情報が見つからない場合、必ず『わかりません』と回答してください。例えば、以下の質問に対してコンテキストに情報が含まれない場合です。

質問:「少年ジャンプで掲載されていた『ナルト』について教えて

# コンテキスト
{context}

<context>
{context}
</context>
`;

Prompt Template の system にはプロンプトのテンプレートが入り、messages にはユーザーからの質問が入ります。messages は配列になっていますが、要素は 1 つだけです。

createStuffDocumentsChain ヘルパー関数を使用して、すべての入力ドキュメントをプロンプトに詰め込みます。

import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";

const questionAnsweringPrompt = ChatPromptTemplate.fromMessages([
  ["system", SYSTEM_TEMPLATE],
  new MessagesPlaceholder("messages"),
]);

const documentChain = await createStuffDocumentsChain({
  llm,
  prompt: questionAnsweringPrompt,
});

.invoke() して、ユーザーからの質問に対して回答を取得します。ここでは、先程取得したドキュメントを context として渡します。すると先程取得したドキュメントに基づいて回答が返ってきます。

const result = await documentChain.invoke({
  messages: [new HumanMessage("LangChainのライセンス形式は?")],
  context: docs,
});
console.log(result);
LangChainのライセンス形式はMITライセンスです。

.invoke() して、ユーザーからの質問に対して回答を取得します。ここでは、context には空の情報を渡します。プロンプトで指示した通り、「わかりません」と返してきます。

const result2 = await documentChain.invoke({
  messages: [new HumanMessage("LangChainのライセンス形式は?")],
  context: [],
});
console.log(result2);
わかりません。

Retrieval chains

Retriever を利用し、LLM にユーザーから質問があった場合、柔軟にベクターストアを検索し、検索結果に基づいた回答を返却します。

コードの作成

$ touch demo04.ts
demo04.ts
import "cheerio";
import { CheerioWebBaseLoader } from "@langchain/community/document_loaders/web/cheerio";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
import type { BaseMessage } from "@langchain/core/messages";
import {
  RunnablePassthrough,
  RunnableSequence,
} from "@langchain/core/runnables";

const llm = new ChatOpenAI({
  model: "gpt-3.5-turbo",
  temperature: 0
});

const loader = new CheerioWebBaseLoader(
  "https://ja.wikipedia.org/wiki/LangChain"
);
const rawDocs = await loader.load();

const textSplitter = new RecursiveCharacterTextSplitter({
  chunkSize: 500,
  chunkOverlap: 0,
});
const allSplits = await textSplitter.splitDocuments(rawDocs);

const vectorstore = await MemoryVectorStore.fromDocuments(
  allSplits,
  new OpenAIEmbeddings()
);

const retriever = vectorstore.asRetriever(3);

const SYSTEM_TEMPLATE = `# 指示
以下の質問に回答してください。質問に対する情報がコンテキストによって提供されない場合、または明確な情報源が存在しない場合は、『わかりません』とだけ回答してください。推測や創作はしないでください。

質問に対する情報が見つからない場合、必ず『わかりません』と回答してください。例えば、以下の質問に対してコンテキストに情報が含まれない場合です。

質問:「少年ジャンプで掲載されていた『ナルト』について教えて

# コンテキスト
{context}
`;

const questionAnsweringPrompt = ChatPromptTemplate.fromMessages([
  ["system", SYSTEM_TEMPLATE],
  new MessagesPlaceholder("messages"),
]);

const documentChain = await createStuffDocumentsChain({
  llm,
  prompt: questionAnsweringPrompt,
});

const parseRetrieverInput = (params: { messages: BaseMessage[] }) => {
  const lastMessage = params.messages[params.messages.length - 1];
  if (lastMessage) {
    return lastMessage.content;
  }
  return "";
};

const retrievalChain = RunnablePassthrough.assign({
  context: RunnableSequence.from([parseRetrieverInput, retriever]),
}).assign({
  answer: documentChain,
});

const result = await retrievalChain.invoke({
  messages: [new HumanMessage("LangChainのライセンス形式は?")],
});
console.log(result)

const result2 = await retrievalChain.invoke({
  messages: [new HumanMessage("転生したらスライムだった件の作者は誰?")],
});
console.log(result2)

ローカルで実行します。

$ pnpm vite-node demo04.ts

コミットします。

$ git add .
$ git commit -m "Retrieval chainsの作成"

コードの解説

parseRetrieverInput は最後のメッセージを取得し Retrieval Chains に渡します。parseRetrieverInput はユーザーからの直近の問い合わせを取得するのに利用されます。

import type { BaseMessage } from "@langchain/core/messages";
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";

const parseRetrieverInput = (params: { messages: BaseMessage[] }) => {
  const lastMessage = params.messages[params.messages.length - 1];
  if (lastMessage) {
    return lastMessage.content;
  }
  return "";
};

const retrievalChain = RunnablePassthrough.assign({
  context: RunnableSequence.from([parseRetrieverInput, retriever]),
}).assign({
  answer: documentChain,
});

LLM に問い合わせ回答を得ます。質問ごとにベクターストアへアクセスし、その結果をもとに回答を作成します。

const result = await retrievalChain.invoke({
  messages: [new HumanMessage("LangChainのライセンス形式は?")],
});
console.log(result)

コンソール出力

{
  messages: [
    HumanMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'LangChainのライセンス形式は?',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {}
    }
  ],
  context: [
    Document {
      pageContent: '出典: フリー百科事典『ウィキペディア(Wikipedia)』\n' +
        '\t\t\t\t\t\n' +
        '\t\t\t\t\t\n' +
        '\t\t\t\t\t\n' +
        '\t\t\t\t\t\n' +
        '\t\t\t\t\tLangChain\n' +
        '開発元\n' +
        'Harrison Chase初版\n' +
        '2022年10月 (1年前) (2022-10)\n' +
        '最新版\n' +
        '0.1.16[1]\n' +
        '   リポジトリ\n' +
        'github.com/langchain-ai/langchainプログラミング言語\n' +
        'PythonとJavaScript種別\n' +
        '大規模言語モデルアプリケーションソフトウェア開発のためのソフトウェアフレームワークライセンス\n' +
        'MITライセンス公式サイト\n' +
        'LangChain.comテンプレートを表示\n' +
        'LangChain(ラングチェイン)は、大規模言語モデル(LLM)を使ったアプリケーションソフトウェアの作成を簡素化するように設計されたフレームワークである。言語モデル統合フレームワークとして、LangChainのユースケースは、文書解析や要約、チャットボット、コード解析など、一般的な言語モデルのユースケースとほぼ重なっている[2]。',
      metadata: [Object]
    },
    Document {
      pageContent: '歴史[編集]\n' +
        'LangChainは、機械学習スタートアップ企業Robust Intelligenceに勤務していたハリソン・チェイス(Harrison Chase)によって、2022年10月にオープンソースプロジェクトとして立ち上げられた。このプロジェクトはすぐに人気を博し、GitHubでは数百名のコントリビューターによる改善、Twitterではトレンドの議論、プロジェクトのDiscordサーバ上では活発な活動、多くのYouTubeチュートリアル、サンフランシスコとロンドンでのミートアップなどが行われた。2023年4月、LangChainは法人化し、新しいスタートアップは、ベンチャーキャピタルBenchmark(英語版)から1,000万ドルの資金投資を発表した1週間後、セコイア・キャピタルから少なくとも2億ドルの評価額で2,000万ドル以上の資金を調達した[3][4]。',
      metadata: [Object]
    },
    Document {
      pageContent: '2023年10月、LangChainは、LCEL(LangChain Expression Language)プロトタイプから本番運用可能なアプリケーションへの移行を促進するために設計された展開ツールLangServeを発表した[5]。',
      metadata: [Object]
    }
  ],
  answer: 'LangChainのライセンス形式はMITライセンスです。'
}

LLM に Retriever で取得できない質問を投げると、Retriever が取得できない質問に対しては、わかりません と返します。

const result2 = await retrievalChain.invoke({
  messages: [new HumanMessage("転生したらスライムだった件の作者は誰?")],
});
console.log(result2)

コンソール出力

{
  messages: [
    HumanMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: '転生したらスライムだった件の作者は誰?',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {}
    }
  ],
  context: [
    Document {
      pageContent: 'アカウント作成\n' +
        '\n' +
        'ログイン\n' +
        '\n' +
        '\n' +
        '\t\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\n' +
        '\n' +
        '\n' +
        '\t\n' +
        '\t\n' +
        '\n' +
        '\t\n' +
        '\t\n' +
        '\n' +
        '個人用ツール\n' +
        '\t\n' +
        '\t\n' +
        '\n' +
        '\n' +
        '\t\t\n' +
        '\n' +
        '\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\t アカウント作成 ログイン\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\n' +
        '\n' +
        '\n' +
        '\n' +
        '\t\n' +
        '\t\tログアウトした編集者のページ もっと詳しく\n' +
        '\t\n' +
        '\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\t投稿記録トーク\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\n' +
        '\n' +
        '\n' +
        '\t\n' +
        '\t\n' +
        '\n' +
        '\n' +
        '\n' +
        '\n' +
        '\t\t\n' +
        '\t\n' +
        '\n' +
        '\n' +
        '\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\t\t\n' +
        '\t\t\t\t\n' +
        '\t\t\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\n' +
        '\t\n' +
        '\t\t\t\t\n' +
        '\t\t\t\t\t\n' +
        '\t\t\t\t\t\n' +
        '\t\n' +
        '\t目次\n' +
        '\tサイドバーに移動\n' +
        '\t非表示\n' +
        '\n' +
        '\n' +
        '\n' +
        '\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\t\tページ先頭\n' +
        '\t\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\t1歴史\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\n' +
        '\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\t2統合化\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\n' +
        '\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\t3脚注\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\n' +
        '\n' +
        '\n' +
        '\n' +
        '\t\t\t\t\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\n' +
        '\t\t\n' +
        '\t\t\t\n' +
        '\t\t\t\t\n' +
        '\t\t\t\t\t\n' +
        '\t\t\t\t\t\t\n' +
        '\n' +
        '\t\n' +
        '\t\n' +
        '\n' +
        '目次の表示・非表示を切り替え',
      metadata: [Object]
    },
    Document {
      pageContent: 'ニュース、映画情報、天気などのAPIラッパー\n' +
        'Bashによる要約、構文とセマンティクスのチェック、およびシェルスクリプトの実行\n' +
        '複数のウェブスクレイピング・サブシステムとテンプレート\n' +
        '少数ショット学習用のプロンプト生成\n' +
        'コード内の「todo」タスクの検索と要約\n' +
        'Google Driveドキュメント、スプレッドシート、プレゼンテーションの要約、抽出、作成\n' +
        'Google検索とMicrosoft Bingウェブ検索\n' +
        'OpenAI、Anthropic、Hugging Face言語モデル\n' +
        'iFixitの修理ガイドとWikiの検索と要約\n' +
        'MapReduceによる質問回答、ドキュメントの結合、質問生成\n' +
        'n-gram(英語版)重複スコアリング\n' +
        'PDFファイルのテキスト抽出と操作のためのPyPDF、pdfminer、fitz、pymupdf\n' +
        'PythonおよびJavaScriptのコード生成、分析、デバッグ\n' +
        'ベクトル埋め込みを保存・取得するMilvusベクトルデータベース[6]\n' +
        '埋め込みとデータオブジェクトをキャッシュするWeaviateベクトルデータベース[7]',
      metadata: [Object]
    },
    Document {
      pageContent: '.cs1-visible-error{color:#d33}.mw-parser-output .cs1-maint{display:none;color:#3a3;margin-left:0.3em}.mw-parser-output .cs1-format{font-size:95%}.mw-parser-output .cs1-kern-left{padding-left:0.2em}.mw-parser-output .cs1-kern-right{padding-right:0.2em}.mw-parser-output .citation .mw-selflink{font-weight:inherit}ISSN 0362-4331.  オリジナルの2023年4月18日時点におけるアーカイブ。. https://web.archive.org/web/20230418041335/https://www.nytimes.com/2023/03/14/technology/ai-funding-boom.html 2023年4月18日閲覧。',
      metadata: [Object]
    }
  ],
  answer: 'わかりません。'
}

さいごに

この記事では、公式のドキュメントを使いながら LangChain で外部から入力された情報を参照する方法を紹介しました。長いのでここで切って、続きは後編になります。

作業リポジトリ

こちらが作業リポジトリです。

https://github.com/hayato94087/langchain-chatbots_retrieval-sample

Discussion