LangChain.jsのFile Loadersで任意の拡張子に対応する
はじめに
とうとう社会人2年生になってしまったwinnieです 🍯
少し前からPM見習いとして実装と離れた生活を送っていましたが、最近プライベートで携わっているプロジェクトで本格的にLLMまわりの技術を触り始めたので、久しぶりに実装関連の知見をアウトプットしていこうと思います!
困ったこと
RAGなどを実装するときにファイルの中身を読み込むべく、LangChainのFile Loadersを利用するシチュエーションは多々あるかと思います。
その中でも DirectoryLoader
や MultiFileLoader
は複数のファイルをまとめて読み込むことができてとても便利です[1] [2]。しかし、任意の拡張子に対応しようとしたときに少し悩んだため解決方法を記載していきます。
例えばこちらの DirectoryLoader
の実装は、一般的なテキストファイルやCSVファイルのほかにJavaScriptなどの拡張子にも対応しています。ただ、ほかにも色々なファイルに対応しようと拡張子を列挙していくとキリがありません。
const loader = new DirectoryLoader(directoryPath, {
'.txt': (path) => new TextLoader(path),
'.csv': (path) => new CSVLoader(path, 'text'),
'.json': (path) => new JSONLoader(path, '/texts'),
'.js': (path) => new TextLoader(path),
'.ts': (path) => new TextLoader(path),
'.jsx': (path) => new TextLoader(path),
'.tsx': (path) => new TextLoader(path),
// 対応したい拡張子が無数に存在する…
})
またLangChainの実装を見ても[3]、任意の拡張子を処理できるようにはなっていないようです。
ちなみに上記の例について、「 .js
や .ts
には TextLoader
じゃなくて専用のLoaderを使った方が良くない?」と思われるかもしれませんが、おおむねLLMが各種文法を理解してくれるので、テキストファイルとして読み込んでもそこまで問題はないです。
解決方法
まずは、形式を指定して読み込みたいファイルをオブジェクトに定義します。
const loaders = {
'.txt': (path) => new TextLoader(path),
'.csv': (path) => new CSVLoader(path, 'text'),
'.json': (path) => new JSONLoader(path, '/texts'),
}
次に、定義されていない拡張子をテキストファイルとして読み込むよう定義します。
import { extname } from 'path'
const unknownLoaders = filePaths.reduce((object, path) => {
const key = extname(path) // 拡張子を取得
if (key in loaders) {
return object // 拡張子が定義されている場合、スキップ
}
// 拡張子ごとの読み込みをオブジェクトに追加
return { ...object, [key]: (path: string) => new TextLoader(path) }
}, {})
最後に、それぞれのオブジェクトを展開してLoaderを作成します。
const loader = new DirectoryLoader(directoryPath, { ...loaders, ...unknownLoaders })
const documents = await loader.load() // [Document { pageContent: ...
解説
特に難しいことはしていないので、コメントを読んで頂ければ分かるかと思います。 reduce()
や [key]
などはあまり使わない気がするので[4] [5]、引っかかるとすればその辺りかもしれません。この部分の参考記事は脚注に記載しています。
例えば、 .txt
, .yml
, .py
の3ファイルが与えられた場合、最終的にLoaderに展開されるオブジェクトは以下のようになります。 .txt
, .csv
, .json
の3つに加え、対応していなかった .yml
, .py
のLoaderが追加されています。
{
'.txt': (path) => new TextLoader(path),
'.csv': (path) => new CSVLoader(path, 'text'),
'.json': (path) => new JSONLoader(path, '/texts'),
'.yml': (path) => new TextLoader(path),
'.py': (path) => new TextLoader(path),
}
おわりに
最近の(というほどでもないですが)生成AIまわりの技術はアップデートが早くて楽しいですね!DifyやOpenAI Assistants APIもいいですが、個人的に最近はVerel AI SDKとLangSmithの2つが好きです 🔥
Discussion