🕌

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 での実装例を以下の記事で紹介しています。

Query transformation

現状のチャットボットはウェブから取得したコンテンツの内容に合わせた回答はできますが、ユーザーと普通に対話できない問題があります。この問題を解消し、ドキュメント以外も回答できるようにしていきます。

コードの作成

$ touch demo05.ts
demo05.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 { AIMessage, HumanMessage } from "@langchain/core/messages";
import type { BaseMessage } from "@langchain/core/messages";
import {
  RunnablePassthrough,
  RunnableSequence,
} from "@langchain/core/runnables";
import { RunnableBranch } from "@langchain/core/runnables";
import { StringOutputParser } from "@langchain/core/output_parsers";

// LLM の対話モデル
const llm = new ChatOpenAI({
  model: "gpt-3.5-turbo",
  temperature: 0,
});

// Webドキュメントをダウンロード
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()
);

// ベクターストアから情報を取得するRetrieverを作成
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 "";
};

// Retrieverとドキュメントチェーンを組み合わせたチェーンを作成
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);

const queryTransformPrompt = ChatPromptTemplate.fromMessages([
  new MessagesPlaceholder("messages"),
  [
    "user",
    "上記の会話を踏まえ、会話に関連する情報を得るための検索クエリを生成してください。クエリのみを回答し、それ以外のことは書かないでください。",
  ],
]);
const queryTransformationChain = queryTransformPrompt.pipe(llm);
const result3 = await queryTransformationChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
  ],
});
console.log(result3)

const queryTransformingRetrieverChain = RunnableBranch.from([
  [
    (params: { messages: BaseMessage[] }) => params.messages.length === 1,
    RunnableSequence.from([parseRetrieverInput, retriever]),
  ],
  queryTransformPrompt.pipe(llm).pipe(new StringOutputParser()).pipe(retriever),
]).withConfig({ runName: "chat_retriever_chain" });

const conversationalRetrievalChain = RunnablePassthrough.assign({
  context: queryTransformingRetrieverChain,
}).assign({
  answer: documentChain,
});

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

const result5 = await conversationalRetrievalChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
  ],
});
console.log(result5)

const result6 = await conversationalRetrievalChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
    new HumanMessage("転生したらスライムだった件の作者は誰?")
  ],
});
console.log(result6)

ローカルで実行します。

$ pnpm vite-node demo05.ts

コミットします。

$ git add .
$ git commit -m "Query transformationの作成"

コードの解説

「もっと教えて」と LLM に問い合わせると、LLM は「もっと教えて」という質問に対して、ベクターストアから関連ドキュメントを取得し、取得したドキュメントをもとに回答を返します。LLM は以前のやり取りを記憶していないため、愚直に「もっと教えて」というテキストをベクターストアに問い合わせます。

ように制約をかけているため、「申し訳ありませんが、どのような情報を求めているのか具体的に教えていただけますか?具体的な質問やテーマがあればお答えできるかもしれません。」と回答されます。

LLM には知らない情報については回答できない。

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: 'LangChain\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閲覧編集履歴表示',
      metadata: [Object]
    }
  ],
  answer: '申し訳ありませんが、どのような情報を求めているのか具体的に教えていただけますか?具体的な質問やテーマがあればお答えできるかもしれません。'
}

この問題を解決するには、過去の会話履歴を含めて、LLM に問い合わせる必要があります。例えば、Retrieval Chain には連結詩ないで、過去の会話を LLM にわたすと、過去の会話をもとに回答を返すことができます。

const queryTransformPrompt = ChatPromptTemplate.fromMessages([
  new MessagesPlaceholder("messages"),
  [
    "user",
    "上記の会話を踏まえ、会話に関連する情報を得るための検索クエリを生成してください。クエリのみを回答し、それ以外のことは書かないでください。",
  ],
]);
const queryTransformationChain = queryTransformPrompt.pipe(llm);
const result3 = await queryTransformationChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
  ],
});
console.log(result3)

コンソール出力

AIMessage {
  lc_serializable: true,
  lc_kwargs: {
    content: 'LangChainのMITライセンスに関する詳細情報',
    tool_calls: [],
    invalid_tool_calls: [],
    additional_kwargs: { function_call: undefined, tool_calls: undefined },
    response_metadata: {}
  },
  lc_namespace: [ 'langchain_core', 'messages' ],
  content: 'LangChainのMITライセンスに関する詳細情報',
  name: undefined,
  additional_kwargs: { function_call: undefined, tool_calls: undefined },
  response_metadata: {
    tokenUsage: { completionTokens: 18, promptTokens: 121, totalTokens: 139 },
    finish_reason: 'stop'
  },
  tool_calls: [],
  invalid_tool_calls: [],
  usage_metadata: undefined
}

では、documentChain, retrievalChain に連結させます。これで、過去の会話をもとに回答を返すことができます。

const queryTransformingRetrieverChain = RunnableBranch.from([
  [
    (params: { messages: BaseMessage[] }) => params.messages.length === 1,
    RunnableSequence.from([parseRetrieverInput, retriever]),
  ],
  queryTransformPrompt.pipe(llm).pipe(new StringOutputParser()).pipe(retriever),
]).withConfig({ runName: "chat_retriever_chain" });

const conversationalRetrievalChain = RunnablePassthrough.assign({
  context: queryTransformingRetrieverChain,
}).assign({
  answer: documentChain,
});

まず、シンプルとに問い合わせます。LangChain について教えてくれます。

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

コンソール出力

{
  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ライセンスです。'
}

さらに追加で「もっと教えて」と質問すると、「LangChain」の詳細な情報を教えてくれます。

const result5 = await conversationalRetrievalChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
  ],
});
console.log(result5)

コンソール出力

{
  messages: [
    HumanMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'LangChainのライセンス形式は?',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {}
    },
    AIMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'LangChainのライセンス形式はMITライセンスです。',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {},
      tool_calls: [],
      invalid_tool_calls: [],
      usage_metadata: undefined
    },
    HumanMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'もっと教えて',
      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: '脚注[編集]\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日閲覧。',
      metadata: [Object]
    }
  ],
  answer: 'LangChainは、大規模言語モデル(LLM)を使用したアプリケーションソフトウェアの作成を簡素化するために設計されたフレームワークです。言語モデル統合フレームワークとして、LangChainのユースケースは、文書解析、要約、チャットボット、コード解析など、一般的な言語モデルのユースケースとほぼ重なっています。LangChainは、2022年10月に機械学習スタートアップ企業Robust Intelligenceに勤務していたHarrison Chaseによってオープンソースプロジェクトとして立ち上げられました。その後、人気を博し、多くのコントリビューターによる改善や活発なコミュニティ活動が行われました。2023年4月には法人化し、ベンチャーキャピタルから資金調達を行いました。'
}

今度は別の質問をすると、LLM が持っている汎用的な知識から回答を返します。

const result6 = await conversationalRetrievalChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
    new HumanMessage("転生したらスライムだった件の作者は誰?")
  ],
});
console.log(result6)

コンソール出力

{
  messages: [
    HumanMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'LangChainのライセンス形式は?',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {}
    },
    AIMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'LangChainのライセンス形式はMITライセンスです。',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {},
      tool_calls: [],
      invalid_tool_calls: [],
      usage_metadata: undefined
    },
    HumanMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'もっと教えて',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {}
    },
    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: '全般\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 形式でダウンロード印刷用バージョン',
      metadata: [Object]
    }
  ],
  answer: '『転生したらスライムだった件』の作者は川上泰樹(かわかみ やすき)です。'
}

Streaming

ここでは LLM の出力結果をストリーミング配信できるように変更詩ます。

コードの作成

$ touch demo06.ts
demo06.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 { AIMessage, HumanMessage } from "@langchain/core/messages";
import type { BaseMessage } from "@langchain/core/messages";
import {
  RunnablePassthrough,
  RunnableSequence,
} from "@langchain/core/runnables";
import { RunnableBranch } from "@langchain/core/runnables";
import { StringOutputParser } from "@langchain/core/output_parsers";

// LLM の対話モデル
const llm = new ChatOpenAI({
  model: "gpt-3.5-turbo",
  temperature: 0,
});

// Webドキュメントをダウンロード
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()
);

// ベクターストアから情報を取得するRetrieverを作成
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 "";
};

// Retrieverとドキュメントチェーンを組み合わせたチェーンを作成
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);

const queryTransformPrompt = ChatPromptTemplate.fromMessages([
  new MessagesPlaceholder("messages"),
  [
    "user",
    "上記の会話を踏まえ、会話に関連する情報を得るための検索クエリを生成してください。クエリのみを回答し、それ以外のことは書かないでください。",
  ],
]);
const queryTransformationChain = queryTransformPrompt.pipe(llm);
const result3 = await queryTransformationChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
  ],
});
// console.log(result3)

const queryTransformingRetrieverChain = RunnableBranch.from([
  [
    (params: { messages: BaseMessage[] }) => params.messages.length === 1,
    RunnableSequence.from([parseRetrieverInput, retriever]),
  ],
  queryTransformPrompt.pipe(llm).pipe(new StringOutputParser()).pipe(retriever),
]).withConfig({ runName: "chat_retriever_chain" });

const conversationalRetrievalChain = RunnablePassthrough.assign({
  context: queryTransformingRetrieverChain,
}).assign({
  answer: documentChain,
});

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

const result5 = await conversationalRetrievalChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
  ],
});
// console.log(result5)

const result6 = await conversationalRetrievalChain.invoke({
  messages: [
    new HumanMessage("LangChainのライセンス形式は?"),
    new AIMessage(
      "LangChainのライセンス形式はMITライセンスです。"
    ),
    new HumanMessage("もっと教えて"),
    new HumanMessage("転生したらスライムだった件の作者は誰?")
  ],
});
// console.log(result6)

const stream = await conversationalRetrievalChain.stream({
  messages: [
    new HumanMessage("Can LangSmith help test my LLM applications?"),
    new AIMessage(
      "Yes, LangSmith can help test and evaluate your LLM applications. It allows you to quickly edit examples and add them to datasets to expand the surface area of your evaluation sets or to fine-tune a model for improved quality or reduced costs. Additionally, LangSmith can be used to monitor your application, log all traces, visualize latency and token usage statistics, and troubleshoot specific issues as they arise."
    ),
    new HumanMessage("Tell me more!"),
  ],
});

for await (const chunk of stream) {
  console.log(chunk);
}

ローカルで実行します。

$ pnpm vite-node demo06.ts

コミットします。

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

コードの解説

LCEL で構成された chain は .stream を利用し出力結果をストリーミングできます。

const stream = await conversationalRetrievalChain.stream({
  messages: [
    new HumanMessage("Can LangSmith help test my LLM applications?"),
    new AIMessage(
      "Yes, LangSmith can help test and evaluate your LLM applications. It allows you to quickly edit examples and add them to datasets to expand the surface area of your evaluation sets or to fine-tune a model for improved quality or reduced costs. Additionally, LangSmith can be used to monitor your application, log all traces, visualize latency and token usage statistics, and troubleshoot specific issues as they arise."
    ),
    new HumanMessage("Tell me more!"),
  ],
});

for await (const chunk of stream) {
  console.log(chunk);
}

コンソール出力

{
  messages: [
    HumanMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'LangChainのライセンス形式は?',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {}
    },
    AIMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'LangChainのライセンス形式はMITライセンスです。',
      name: undefined,
      additional_kwargs: {},
      response_metadata: {},
      tool_calls: [],
      invalid_tool_calls: [],
      usage_metadata: undefined
    },
    HumanMessage {
      lc_serializable: true,
      lc_kwargs: [Object],
      lc_namespace: [Array],
      content: 'もっと教えて',
      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: '脚注[編集]\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日閲覧。',
      metadata: [Object]
    }
  ]
}
{ answer: '' }
{ answer: 'Lang' }
{ answer: 'Chain' }
{ answer: 'は' }
{ answer: '、' }
{ answer: '大' }
{ answer: '規' }
{ answer: '模' }
{ answer: '言' }
{ answer: '語' }
{ answer: 'モ' }
{ answer: 'デ' }
{ answer: 'ル' }
{ answer: '(' }
{ answer: 'LL' }
{ answer: 'M' }
{ answer: ')' }
{ answer: 'を' }
{ answer: '使' }
{ answer: 'っ' }
{ answer: 'た' }
{ answer: 'ア' }
{ answer: 'プ' }
{ answer: 'リ' }
{ answer: 'ケ' }
{ answer: 'ーシ' }
{ answer: 'ョ' }
{ answer: 'ン' }
{ answer: 'ソ' }
{ answer: 'フ' }
{ answer: 'ト' }
{ answer: 'ウ' }
{ answer: 'ェ' }
{ answer: 'ア' }
{ answer: 'の' }
{ answer: '作' }
{ answer: '成' }
{ answer: 'を' }
{ answer: '簡' }
{ answer: '素' }
{ answer: '化' }
{ answer: 'する' }
{ answer: 'た' }
{ answer: 'め' }
{ answer: 'に' }
{ answer: '設' }
{ answer: '計' }
{ answer: 'され' }
{ answer: 'た' }
{ answer: 'フ' }
{ answer: 'レ' }
{ answer: 'ーム' }
{ answer: 'ワ' }
{ answer: 'ー' }
{ answer: 'ク' }
{ answer: 'です' }
{ answer: '。' }
{ answer: '言' }
{ answer: '語' }
{ answer: 'モ' }
{ answer: 'デ' }
{ answer: 'ル' }
{ answer: '統' }
{ answer: '合' }
{ answer: 'フ' }
{ answer: 'レ' }
{ answer: 'ーム' }
{ answer: 'ワ' }
{ answer: 'ー' }
{ answer: 'ク' }
{ answer: 'と' }
{ answer: 'して' }
{ answer: '、' }
{ answer: 'Lang' }
{ answer: 'Chain' }
{ answer: 'の' }
{ answer: 'ユ' }
{ answer: 'ース' }
{ answer: 'ケ' }
{ answer: 'ース' }
{ answer: 'は' }
{ answer: '、' }
{ answer: '文' }
{ answer: '書' }
{ answer: '解' }
{ answer: '析' }
{ answer: 'や' }
{ answer: '要' }
{ answer: '約' }
{ answer: '、' }
{ answer: 'チ' }
{ answer: 'ャ' }
{ answer: 'ット' }
{ answer: 'ボ' }
{ answer: 'ット' }
{ answer: '、' }
{ answer: 'コ' }
{ answer: 'ード' }
{ answer: '解' }
{ answer: '析' }
{ answer: 'な' }
{ answer: 'ど' }
{ answer: '、' }
{ answer: '一' }
{ answer: '般' }
{ answer: '的' }
{ answer: 'な' }
{ answer: '言' }
{ answer: '語' }
{ answer: 'モ' }
{ answer: 'デ' }
{ answer: 'ル' }
{ answer: 'の' }
{ answer: 'ユ' }
{ answer: 'ース' }
{ answer: 'ケ' }
{ answer: 'ース' }
{ answer: 'と' }
{ answer: 'ほ' }
{ answer: 'ぼ' }
{ answer: '重' }
{ answer: 'な' }
{ answer: 'って' }
{ answer: 'います' }
{ answer: '。' }
{ answer: 'Lang' }
{ answer: 'Chain' }
{ answer: 'は' }
{ answer: '、' }
{ answer: '202' }
{ answer: '2' }
{ answer: '年' }
{ answer: '10' }
{ answer: '月' }
{ answer: 'に' }
{ answer: '機' }
{ answer: '械' }
{ answer: '学' }
{ answer: '習' }
{ answer: 'ス' }
{ answer: 'タ' }
{ answer: 'ート' }
{ answer: 'ア' }
{ answer: 'ッ' }
{ answer: 'プ' }
{ answer: '企' }
{ answer: '業' }
{ answer: 'Rob' }
{ answer: 'ust' }
{ answer: ' Intelligence' }
{ answer: 'に' }
{ answer: '勤' }
{ answer: '務' }
{ answer: 'して' }
{ answer: 'い' }
{ answer: 'た' }
{ answer: 'H' }
{ answer: 'arrison' }
{ answer: ' Chase' }
{ answer: 'に' }
{ answer: 'よ' }
{ answer: 'って' }
{ answer: 'オ' }
{ answer: 'ープ' }
{ answer: 'ン' }
{ answer: 'ソ' }
{ answer: 'ース' }
{ answer: 'プ' }
{ answer: 'ロ' }
{ answer: 'ジ' }
{ answer: 'ェ' }
{ answer: 'ク' }
{ answer: 'ト' }
{ answer: 'と' }
{ answer: 'して' }
{ answer: '立' }
{ answer: 'ち' }
{ answer: '上' }
{ answer: 'げ' }
{ answer: 'ら' }
{ answer: 'れ' }
{ answer: 'ました' }
{ answer: '。' }
{ answer: 'その' }
{ answer: '後' }
{ answer: '、' }
{ answer: '人' }
{ answer: '気' }
{ answer: 'を' }
{ answer: '博' }
{ answer: 'し' }
{ answer: '、' }
{ answer: '多' }
{ answer: 'く' }
{ answer: 'の' }
{ answer: 'コ' }
{ answer: 'ント' }
{ answer: 'リ' }
{ answer: 'ビ' }
{ answer: 'ュ' }
{ answer: 'ータ' }
{ answer: 'ー' }
{ answer: 'に' }
{ answer: 'よ' }
{ answer: 'る' }
{ answer: '改' }
{ answer: '善' }
{ answer: 'や' }
{ answer: '活' }
{ answer: '発' }
{ answer: 'な' }
{ answer: 'コ' }
{ answer: 'ミ' }
{ answer: 'ュ' }
{ answer: 'ニ' }
{ answer: 'テ' }
{ answer: 'ィ' }
{ answer: '活' }
{ answer: '動' }
{ answer: 'が' }
{ answer: '行' }
{ answer: 'わ' }
{ answer: 'れ' }
{ answer: 'て' }
{ answer: 'います' }
{ answer: '。' }
{ answer: '202' }
{ answer: '3' }
{ answer: '年' }
{ answer: '4' }
{ answer: '月' }
{ answer: 'に' }
{ answer: 'は' }
{ answer: '法' }
{ answer: '人' }
{ answer: '化' }
{ answer: 'し' }
{ answer: '、' }
{ answer: 'ベ' }
{ answer: 'ン' }
{ answer: 'チ' }
{ answer: 'ャ' }
{ answer: 'ーキ' }
{ answer: 'ャ' }
{ answer: 'ピ' }
{ answer: 'タ' }
{ answer: 'ル' }
{ answer: 'から' }
{ answer: '資' }
{ answer: '金' }
{ answer: '調' }
{ answer: '達' }
{ answer: 'を' }
{ answer: '行' }
{ answer: 'って' }
{ answer: 'います' }
{ answer: '。' }
{ answer: '' }

さいごに

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

作業リポジトリ

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

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

Discussion