Boxのコンテンツ管理プラットフォームの力を直接LangChain.jsアプリケーションに取り入れる新しいドキュメントローダー、langchainjs-boxをリリースしました。このパッケージにより、RAGアプリケーションの構築でBoxのファイルやフォルダをシームレスに統合できます。
Boxのマークダウンレプリゼンテーションに対応
このローダーの最も魅力的な機能の1つは、Boxの新しいマークダウンレプリゼンテーションの組み込みサポートです。Boxは最近、複雑なファイル形式を理解しやすい構造化されたマークダウン形式に変換する機能を導入しました。これはLLM搭載のアプリケーションに最適です。
当社のlangchainjs-boxローダーは、以下の場合に自動的にマークダウンレプリゼンテーションを活用します:
- Microsoft Office: .docx、.pptx、 .xls、.xlsx、.xlsm
- Google Workspace: .gdoc、.gslide、.gslides、.gsheet
- PDF: .pdf
これは、LangChain.jsアプリケーションが、埋め込みや検索に最適化された、理解しやすく構造化されたコンテンツを受け取るということを意味します。他のテキストベースのファイルの場合、ローダーは代わりとしてシームレスにBoxのテキストレプリゼンテーションを利用します。
はじめに
構築を始める前に、いくつかの前提条件があります。以下があることを確認してください:
- Node.jsバージョン20以上
- langchain/coreパッケージ: ≥0.3.78 <1.0.0
- Boxアカウント (無料の開発者アカウントに登録)
- Box開発者コンソールで作成したBoxアプリ:
- langchainjs-boxローダーは利用可能なすべての認証方法をサポートしています。
- JWTまたはCCG (クライアント資格情報許可) を使用するサーバー認証アプリケーションは、使用前にBox管理者の承認が必要です。詳細については、こちらの開発者向けガイドをご確認ください。
ローダーのインストールは簡単で、npmからパッケージを取得するだけです:
認証オプション
langchainjs-boxローダーはBoxAuthヘルパークラスを通じて複数の認証方法をサポートしています:
- 開発者トークン (迅速なプロトタイプ作成に最適):
import { BoxLoader, BoxAuth, BoxAuthType } from 'langchainjs-box';
const auth = new BoxAuth({
authType: BoxAuthType.TOKEN,
boxDeveloperToken: 'DEVELOPER_TOKEN'
});
- JWT(サービスアカウントまたは指定ユーザーの場合):
import { BoxLoader, BoxAuth, BoxAuthType } from 'langchainjs-box';
const auth = new BoxAuth({
authType: BoxAuthType.JWT,
boxJwtPath: './path/to/jwt-config.json',
boxUserId: 'USER_ID'
});
- CCG(こちらもサービスアカウントまたは指定ユーザーの場合):
import { BoxLoader, BoxAuth, BoxAuthType } from 'langchainjs-box';
const auth = new BoxAuth({
authType: BoxAuthType.CCG,
boxClientId: 'CLIENT_ID',
boxClientSecret: 'CLIENT_SECRET',
boxEnterpriseId: 'ENTERPRISE_ID'
});
残りの認証コードの例については、プロジェクトドキュメント (英語) を確認してください。
ファイルまたはフォルダ全体を読み込む
認証部分が完成したら、次はコンテンツを読み込みます。langchainjs-boxローダーには次のような柔軟性があります:
- ファイルIDの配列を指定することで、特定のファイルを読み込むことができます。
import { BoxLoader, BoxAuth, BoxAuthType } from 'langchainjs-box';
const auth = new BoxAuth({
authType: BoxAuthType.TOKEN,
boxDeveloperToken: 'DEVELOPER_TOKEN'
});
const loader = new BoxLoader({
boxAuth: auth,
boxFileIds: ['FILE_ID_1', 'FILE_ID_2'],
characterLimit: 10000 // Optional, defaults to no limit
});
const docs = await loader.load();
- 単一のフォルダIDでフォルダ全体を読み込みます。
const loader = new BoxLoader({
boxAuth: auth,
boxFolderId: 'FOLDER_ID'
});
const docs = await loader.load();
- 再帰的にフォルダコンテンツを読み込み、すべてのサブフォルダやファイルを含めます。
const loader = new BoxLoader({
boxAuth: auth,
boxFolderId: 'FOLDER_ID',
recursive: true, // Optional, defaults to false
characterLimit: 10000 // Optional, defaults to no limit
});
const docs = await loader.load();
- または、コンテンツの遅延読み込みにより、ドキュメントを1つずつ処理できるようにする方法もあります。
const loader = new BoxLoader({
boxAuth: auth,
boxFolderId: 'FOLDER_ID'
});
for await (const doc of loader.lazyLoad()) {
// Process each document
}
RAGアプリケーションの構築
LangChain.jsとlangchainjs-boxを使用してRAGアプリケーションを構築する、全体的な例を見てみましょう。まず、環境変数を作成します:
BOX_FILE_IDS=FILE_ID
CHAR_LIMIT=1000
BOX_DEVELOPER_TOKEN=YOUR_TOKEN
この例では、Boxからドキュメントを読み込んでチャンク化し、埋め込みを作成し、OpenAIモデルと検索拡張生成を用いて質問に回答します。
import 'dotenv/config';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const { BoxLoader, BoxAuth, BoxAuthType } = require('langchainjs-box');
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { RunnableSequence } from '@langchain/core/runnables';
import { StringOutputParser } from '@langchain/core/output_parsers';
// You can use other authentication methods
async function loadBoxDocuments() {
const developerToken = process.env.BOX_DEVELOPER_TOKEN;
const boxFileIds = (process.env.BOX_FILE_IDS || '').split(',').map(s => s.trim()).filter(Boolean);
const boxFolderId = process.env.BOX_FOLDER_ID?.trim();
let auth;
if (developerToken) {
auth = new BoxAuth({
authType: BoxAuthType.TOKEN,
boxDeveloperToken: developerToken
});
}
const loader = new BoxLoader({
boxAuth: auth,
boxFileIds: boxFileIds.length ? boxFileIds : undefined,
characterLimit: process.env.CHAR_LIMIT ? Number(process.env.CHAR_LIMIT) : undefined
});
return loader.load();
}
async function main() {
const openaiApiKey = process.env.OPENAI_API_KEY;
if (!openaiApiKey) {
throw new Error('Missing OPENAI_API_KEY in environment');
}
if (!process.env.BOX_DEVELOPER_TOKEN && !process.env.BOX_FILE_IDS && !process.env.BOX_FOLDER_ID) {
throw new Error('Provide BOX_DEVELOPER_TOKEN and either BOX_FILE_IDS (comma-separated) or BOX_FOLDER_ID');
}
console.log('Loading documents from Box...');
const docs = await loadBoxDocuments();
console.log(`Loaded ${docs.length} documents`);
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200
});
const splits = await splitter.splitDocuments(docs);
const cleanSplits = splits.filter(d => d.pageContent && d.pageContent.trim().length > 0 && !d.pageContent.trim().startsWith('[Error'));
console.log(`Created ${cleanSplits.length} clean chunks (from ${splits.length})`);
const embeddings = new OpenAIEmbeddings({ apiKey: openaiApiKey, model: 'text-embedding-3-small' });
const vectorstore = await MemoryVectorStore.fromDocuments(cleanSplits, embeddings);
const retriever = vectorstore.asRetriever({ searchType: 'mmr', searchKwargs: { fetchK: 20 }, k: 8 });
const question = process.env.QUESTION || 'What are the key points in these documents?';
const prompt = (docsText, q) => `You are a helpful assistant.
Use the provided context to answer the question.
If the answer is not in the context, say you don't know.
Context:
${docsText}
Question: ${q}
Answer:`;
const model = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0, apiKey: openaiApiKey });
const chain = RunnableSequence.from([
async (input) => {
const retrieved = await retriever.getRelevantDocuments(input.question);
const context = retrieved.map(d => d.pageContent).join('\n\n');
console.log('Context:', context);
return { promptText: prompt(context, input.question) };
},
(x) => model.invoke(x.promptText),
new StringOutputParser()
]);
console.log('Asking question:', question);
const answer = await chain.invoke({ question });
console.log('\nAnswer:\n', answer);
}
main().catch(err => {
console.error(err);
process.exit(1);
});
皆さんがLangChain.js用のBox loaderをどのように活用するのか、フィードバックを楽しみにしています。npmのlangchainjs-boxパッケージと詳細なドキュメント (英語) もご確認ください。LangChainが最近発表した新しいメジャーバージョンをサポートするため、まもなくパッケージの更新が予定されています。
追加のリクエストやご提案、またはこのパッケージを活用して作成したアプリの例を共有したい場合は、Box Developer Communityフォーラムに英語で投稿してください。🚀
- トピックス:
- 開発者