1. はじめに
1-1. Difyとは?
Difyは、テキスト分割(チャンク化)や埋め込みモデルによる検索を行い、LLMを活用したアプリケーションを効率的に作れるプラットフォームです。ドキュメントをナレッジベースとして登録することで、類似検索やチャットボットへの回答源として利用できます。
1-2. 本記事の目的と想定読者
本記事では、スプレッドシート(Google Sheets)に登録された求人データを、GAS(Google Apps Script)とDifyのAPIを使って一括アップロードし、1レコード=1チャンクとして扱う実装例を紹介します。
想定読者は、GASやAPIを用いた自動化に慣れているエンジニア、またはシステム担当者です。
2. スプレッドシートからDifyナレッジベースへの自動同期
2-1. なぜ「1レコード=1チャンク」がおすすめなのか?
求人情報は、一つひとつが独立した情報として管理されるケースが多いです。「1レコード=1チャンク」にしておくと、検索やLLM回答時に、特定求人のみが抽出されるため誤混入リスクが低減し、ユーザーが絞り込みやすくなります。
2-2. 全求人を1つのファイルにまとめるアプローチ
Difyでは「ファイルごと」に分割ルール(process_rule
)が適用される仕組みです。そのため、全レコードを単一ファイル(all-jobs.txt
)にまとめてアップロードし、「\n\n\n
」などの区切り文字でDify側が分割してくれるように設定するのがシンプルです。
3. 具体的な実装ステップ
3-1. ナレッジベース(Dataset)の作成
- UIからの新規作成
Difyのダッシュボードに入り、「ナレッジベース」を作成。「test」「MyJobsDataset」など任意の名前を指定します。 - APIからの新規作成
GASからPOST https://api.dify.ai/v1/datasets
を呼ぶことで作成可能。返却されるdataset_id
を後続処理で利用します。
// 例: createDataset関数
function createDataset(name, permission) { ... }
3-2. スプレッドシートの構造を整備
以下のように「会社名」「ポジション」「業務内容」「応募条件」「給与」「都道府県」等の列を用意し、1行1求人で管理します。
会社名 | ポジション | 業務内容 | 応募条件 | 給与 | 都道府県 |
---|---|---|---|---|---|
株式会社アクメ | ソフトウェアエンジニア | システム機能の設計・実装, CI/CD保守, … | 3年以上のJavaまたはPython経験 | 月給40万円 | 東京都 |
… | … | … | … | … | … |
3-3. GASでのスクリプト例
以下は「すべての求人を1つのファイル(all-jobs.txt)にまとめる」サンプルコードです。各求人を\n\n\n
で区切りし、Difyへcreate-by-text
APIを呼び出してアップロードします。
/**
* メイン関数:スプレッドシートの求人データを1つの大きなファイル (all-jobs.txt) としてDifyにアップロード。
* 各行は "\n\n\n" 区切りとなり、Dify 側では行ごとにチャンク化される想定。
*/
function syncJobsAsSingleFile() {
Logger.log("=== 開始: syncJobsAsSingleFile ===");
var datasetId = "YOUR_DATASET_ID"; // or createDataset("MyJobsDataset", "only_me");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Jobs");
if (!sheet) throw new Error("シート 'Jobs' が見つかりません。");
var values = sheet.getDataRange().getValues();
if (values.length < 2) return;
var headers = values[0];
var dataRows = values.slice(1);
// カラムインデックスを探す
var companyCol = headers.indexOf("会社名");
var positionCol = headers.indexOf("ポジション");
var dutiesCol = headers.indexOf("業務内容");
var requirementsCol = headers.indexOf("応募条件");
var salaryCol = headers.indexOf("給与");
var prefCol = headers.indexOf("都道府県");
// 行ごとに文字列を構築
var lines = [];
for (var i = 0; i < dataRows.length; i++) {
var row = dataRows[i];
var lineText = "";
lineText += "会社名: " + (row[companyCol] || "") + "\n";
lineText += "ポジション: " + (row[positionCol] || "") + "\n";
if (dutiesCol >= 0) lineText += "業務内容: " + (row[dutiesCol] || "") + "\n";
if (requirementsCol >= 0) lineText += "応募条件: " + (row[requirementsCol] || "") + "\n";
if (salaryCol >= 0) lineText += "給与: " + (row[salaryCol] || "") + "\n";
if (prefCol >= 0) lineText += "都道府県: " + (row[prefCol] || "") + "\n";
lines.push(lineText);
}
// "\n\n\n" で連結
var docText = lines.join("\n\n\n");
// 1ファイルとしてDifyに登録
var docName = "all-jobs.txt";
var documentId = createSingleFileWithOverlap(datasetId, docName, docText);
Logger.log("アップロード完了 => docId=" + documentId);
}
/**
* Difyに 1ファイルを create-by-text でアップロードし、separator="\n\n\n" / overlap=250 などを設定
*/
function createSingleFileWithOverlap(datasetId, docName, docText) {
var apiKey = "Bearer YOUR_DATASET_API_KEY";
var url = "https://api.dify.ai/v1/datasets/" + datasetId + "/document/create-by-text";
var payload = {
name: docName,
text: docText,
indexing_technique: "high_quality",
process_rule: {
mode: "custom",
rules: {
pre_processing_rules: [
{ id: "remove_extra_spaces", enabled: true },
{ id: "remove_urls_emails", enabled: false }
],
segmentation: {
separator: "\n\n\n",
max_tokens: 2000,
overlap: 250
}
}
}
};
var options = {
method: "post",
headers: {
"Authorization": apiKey,
"Content-Type": "application/json"
},
muteHttpExceptions: true,
payload: JSON.stringify(payload)
};
var response = UrlFetchApp.fetch(url, options);
var status = response.getResponseCode();
var text = response.getContentText();
if (status !== 200) {
throw new Error("createSingleFileWithOverlap 失敗: status=" + status + " body=" + text);
}
var json = JSON.parse(text);
return json.document.id;
}
4. Dify側でのチャンク設定
4-1. process_rule の重要パラメータ
separator
: テキスト内を分割する区切り文字列。\n\n\n
などを指定すると「三重改行単位」でチャンク化されます。overlap
: 分割したチャンク同士をどれだけ重複させるか。例:250トークン分を重複させることで、文脈が途切れにくくなります。max_tokens
: 1チャンクの最大トークン数。ここを小さくすると、長いテキストがさらに分割される可能性があるので注意。
4-2. 改行コードや分割ルールでハマりやすいポイント
- Windows系の改行が
\r\n
の場合、単に"\n\n\n"
指定してもヒットしないケースあり。 - GASで
docText.replace(/\r\n/g, "\n")
などして、すべて\n
に統一しておくと安全。
5. トラブルシュート
5-1. 区切り文字が反映されないとき
- Payloadの
separator
が正しく"\n\n\n"
になっているか確認します。 - ログに
docText
を表示し、実際の文字列内に"\n\n\n"
が入っているかを確認しましょう。
5-2. Overlap・max_tokens設定の注意点
- Overlapを大きくしすぎると、実質的にチャンクが重複し、検索に不要なノイズが混入する可能性もあります。
max_tokens
をあまり小さくすると、本来1求人が1チャンクになるはずが追加で分割される原因となります。
5-3. APIキーエラーで401/403が出る場合
Bearer <API_KEY>
の形式が正しいか(”Bearer “の半角スペースを含むか)。- Dataset用のAPIキーとApp用のAPIキーを間違えていないか。
- Dify管理画面でAPIキーが**「revoked」(破棄)**されていないか再チェック。
6. まとめと今後の拡張
6-1. メタデータ(都道府県など)の活用
- Difyではメタデータを追加して、例えば「都道府県=○○」でフィルタリングする検索なども可能です。
metadata API
や GASからの一括割当などを利用し、より柔軟なクエリを実現できます。
6-2. APIリクエスト数の管理
- 大量レコードを細切れで登録する場合、Rate Limit(レート制限)や総リクエスト数に注意してください。
- 今回の「全レコードを1ファイル」とする方針なら、アップロード回数は1回のみで済むので大幅にAPIコール数を削減できます。
6-3. 今後の拡張: 自動タグ付けや外部サービス連携
- ChatGPTプラグイン、Airflowや他のETLツールとの連携、Notion等からの転送など、拡張方法は多彩です。
- 企業独自の業務フローに合わせて、GAS×Dify×外部APIでワンストップなデータパイプラインを組むのも面白いでしょう。
まとめ
本記事では「スプレッドシート上にある求人データを、Difyで1ファイルとしてアップロードし、\n\n\n
区切りでチャンク管理する」という実装手順を紹介しました。GASスクリプト例とDifyのAPIを組み合わせることで、一括登録・チャンク管理・検索最適化をスムーズに自動化できます。
ぜひ今回のコード例を参考にして、Difyとスプレッドシートの連携を試してみてください。