Paper-Essence — チュートリアル:自動化された論文ダイジェストワークフローの構築

Paper-Essence — チュートリアル: 自動論文要約ワークフローの構築

:open_book: プロジェクト概要

Paper-Essenceは、Difyプラットフォーム上に構築された自動論文要約ワークフローです。このワークフローは以下のことができます。

  • :one_o_clock: 毎日、指定された研究分野の最新論文をarXivから取得する
  • :robot: 大規模言語モデルを使用して、最も価値のある論文をフィルタリングし、選択する
  • :page_facing_up: OCRでPDF論文を解析し、技術的な詳細を抽出する
  • :e_mail: 構造化された日次要約を生成し、メールで送信する

GitHubリポジトリ: github.com/LiaoYFBH/PaperFlowprj/Paper-Essence-CN.ymlまたはprj/Paper-Essence-EN.ymlを直接インポートできます。


:hammer_and_wrench: 前提条件

1. プラットフォームとアカウント

  • Difyアカウント: Difyに登録してログインします
  • メールアカウント: SMTP対応のメール(このチュートリアルでは163 Mailを使用)
  • LLM API: Xinghe Community APIを設定します

2. 必要なプラグインのインストール

Difyプラグインマーケットプレイスから以下のプラグインをインストールします。

プラグイン 目的
paddle-aistudio/ernie-paddle-aistudio Xinghe Community API
langgenius/paddleocr PDFおよび画像のOCR
wjdsg/163-smtp-send-mail 163 SMTPメール送信
langgenius/supabase プッシュされたレコードのデータベースストレージ

3. Supabaseデータベースの準備

重複を避けるため、すでにプッシュされた論文を記録するためにクラウドデータベース(Supabase)を使用します。

ステップ1: ログインとプロジェクト作成

supabase.comにアクセスしてアカウントを作成し、新しいプロジェクトを開始します。

ステップ2: テーブルの作成

SQLエディタで、以下のSQLステートメントを実行して、プッシュされた論文IDを記録し、重複がないことを確認するテーブルを作成します。

create table pushed_papers (
  arxiv_id text not null,
  pushed_at timestamp default now(),
  primary key (arxiv_id)
);

ステップ3: APIキーの取得

プロジェクト設定 → APIセクションに移動して、資格情報を確認します。

以下の情報を記録します。

  • NEXT_PUBLIC_SUPABASE_URL → Difyプラグイン用のSupabase URL
  • NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY → Difyプラグイン用のSupabaseキー

ステップ4: DifyでのSupabaseプラグインの設定

Difyで、プラグインマーケットプレイスに移動し、Supabaseプラグインを見つけて、前のステップで取得したURLとキーを入力します。

(オプション) DockerでDifyをデプロイする

環境設定

このチュートリアルではWSL + Dockerを使用します。WSLとDockerの設定については、Microsoftのドキュメントを参照してください。

Difyリポジトリのクローン

まず、Difyリポジトリをクローンします。Gitを設定していない場合は、リポジトリページからZIPファイルを直接ダウンロードして解凍できます。

Gitを設定している場合は、ターミナルで以下のコマンドを実行します。

# Clone Dify repository
git clone https://github.com/langgenius/dify.git

GitとDockerが設定されている必要があります。

# Navigate to docker deployment directory
cd dify/docker

# Copy environment configuration file
cp .env.example .env

まず、Docker Desktopを開き、ターミナルで以下を入力します。

# Start Dify (this will automatically pull images and start all services)
docker compose up -d

ステータスを確認します。

docker compose ps

アプリケーションにアクセス: http://localhost/


:bar_chart: ワークフローアーキテクチャ

ワークフローは以下の主要な流れに従います。

スケジュールトリガー設定 (コード) → LLM翻訳 (LLM - トピック翻訳) → 行の取得 (ツール - プッシュされた論文の確認) → 検索前処理 (コード) → HTTPリクエスト (http-request) → 検索後処理 (コード) → LLM初期レビュー (LLM) → JSON解析 (コード) → イテレーション (各論文について: データアンパック (コード) → 行の作成 (ツール) → ドキュメント解析 (ツール - OCR) → get_footnote_text (コード) → truncated_text (コード) → 分析 (LLM) → データアセンブリ (コード)) → テンプレート変換 (template-transform) → 163 SMTPメール送信者 (ツール) → 出力 (終了)

このワークフローの全体像:


:wrench: ステップバイステップ設定

ステップ1 — ワークフローの作成

  1. Difyにログインします
  2. スタジオで「アプリを作成」をクリック → 「ワークフロー」を選択します
  3. アプリケーション名を入力します
  4. ワークフローのトリガータイプを選択します

ステップ2 — 環境変数の設定

右上隅の「設定」ボタンをクリックし、「変数を追加」を選択します。

これらの環境変数は、下流ノードのために処理および出力を行うConfigurationノード(コードノード)によって読み取られます。

主要な変数:

名前 タイプ 説明
table_name string Supabaseテーブル名 pushed_papers
SMTP_PORT string SMTPポート 465
SMTP_SERVER string SMTPサーバー smtp.163.com
SMTP_PASSWORD secret SMTP認証コード (あなたの認証コード)
SMTP_USER secret SMTPユーザー/メール your_email@163.com
MY_RAW_TOPIC string 研究トピック agent memory
メール認証コードを取得するには、メールプロバイダーの設定にログインし、サードパーティアプリケーション用のSMTP認証コードを生成してください。

ステップ3 — スケジュールトリガー

ノード名: Scheduled Trigger

設定:

  • トリガー頻度: 毎日
  • トリガー時間: 8:59 AM (または必要に応じて調整)

ステップ4 — 設定 (コードノード)

ノード名: Configuration (タイプ: code) — このコードノードは、環境変数を読み込み、処理し、ダウンストリームノード用の設定値を出力します。

入力変数:

  • 環境変数から: SMTP_PORT, SMTP_SERVER, SMTP_USER, SMTP_PASSWORD, MY_RAW_TOPIC, table_name

出力変数:

  • raw_topic: 研究トピック
  • user_email: 受信者メール
  • fetch_count: 取得する論文数 (デフォルト: 50)
  • push_limit: プッシュ制限 (デフォルト: 3)
  • days_lookback: 遡る日数 (デフォルト: 30)
  • およびSMTP設定

ステップ5 — LLM翻訳 (LLMノード)

ノード名: LLM trans (タイプ: llm) — 研究トピックをarXiv用の最適化された英語のブールクエリに変換します。

モデル設定:

  • モデル: ernie-4.5-turbo-128k または ernie-5.0-thinking-preview。深層思考モデル (ernie-5.0-thinking-preview) を選択する場合、推論形式の分離を有効にする必要があります。
  • 温度: 0.7

プロンプトルール: 中核となる概念を抽出し、用語を翻訳し (必要に応じて)、AND/OR を使用してブール論理を構築し、フレーズを引用符で囲み、クエリ文字列のみを出力します。


ステップ6 — プッシュ済みレコードのクエリ (Supabaseノード)

ノード名: Get Rows (タイプ: tool - Supabaseプラグイン) — 重複を避けるために、既存のプッシュ済みarXiv IDを取得します。

設定:

  • テーブル名: {{table_name}} (設定ノードから)

ステップ7 — 論文検索 (3ノード)

安定性と保守性を向上させるため、検索機能は「前処理」→「HTTPリクエスト」→「後処理」に分割されています。

7.1 検索前処理 (コードノード)

ノード名: Search Pre-process (タイプ: code) — arXiv APIリクエストを構築し、検索パラメーターを準備します。

入力変数:

  • topic: 翻訳された英語の検索語
  • days_lookback: 遡る日数
  • count: 取得する論文数
  • supabase_output: すでにプッシュされたレコード (重複排除用)

コードロジック:

  1. カットオフ日を計算 (cutoff_date)
  2. Supabaseが返したプッシュ済み論文IDリストを解析
  3. トピックに基づいてブールクエリ文字列を構築 (AND/ORロジックをサポート)
  4. トピックキーワードに基づいてarXivカテゴリ制限を追加 (例: cs.CV, cs.CL)
  5. 後続のフィルタリングのために検索キーワードを抽出

出力変数:

  • base_query: 構築されたクエリ文字列
  • pushed_ids: すでにプッシュされたIDのリスト
  • cutoff_str: カットオフ日文字列
  • search_keywords: 検索キーワードのリスト
  • fetch_limit: APIフェッチ制限

7.2 HTTPリクエスト (HTTPノード)

ノード名: HTTP Request (タイプ: http-request) — arXiv APIを呼び出して生のXMLデータを取得します。

設定:

  • API URL: http://export.arxiv.org/api/query
  • メソッド: GET

7.3 検索後処理 (コードノード)

ノード名: Search Post-process (タイプ: code) — XMLレスポンスを解析し、論文をフィルタリングします。

入力変数:

  • http_response_body: HTTPノードのレスポンスボディ
  • および前処理ノードからのすべての出力変数

コードロジック:

  1. XMLレスポンスを解析
  2. 重複排除フィルタリング: pushed_ids 内の論文を削除
  3. 日付フィルタリング: cutoff_date より古い論文を削除
  4. キーワードフィルタリング: タイトルまたは要約に少なくとも1つの検索キーワードが含まれていることを確認
  5. 出力をJSONオブジェクトリストとしてフォーマット

出力変数:

  • result: 最終的にフィルタリングされた論文リスト (JSON文字列)
  • count: 最終的な論文数
  • debug: デバッグ情報 (フィルタリング統計を含む)

ステップ8 — LLMによる初期レビュー

ノード名: LLM Initial Review (タイプ: llm) — LLMを使用して、上位論文 (トップ3) をスコアリングおよび選択します。

出力要件:

  • 整形されたJSON配列形式
  • すべての元のフィールドを保持
  • 上位3論文を出力

ステップ9 — JSON解析 (コードノード)

ノード名: JSON Parse (タイプ: code) — LLM出力を正規化された論文リストに寛容に解析します。

コアロジック:

  • ネストされたJSONを処理
  • papers または top_papers フィールドをサポート
  • エラー耐性のある処理

ステップ10 — イテレーションノード

ノード名: Iteration — 選択された各論文を順次処理します (アンパック、Supabaseへの記録、PDFのOCR処理、LLMによる分析、最終オブジェクトの組み立て)。

設定:

  • 入力: top_papers (論文配列)
  • 出力: merged_paper (処理済み論文オブジェクト)
  • 並列モード: オフ (順次実行)
  • エラー処理: エラー時に停止

イテレーション内部フロー

ノード名 タイプ 機能
1 DataUnpack code イテレーション項目を個々の変数にアンパック
2 Create a Row tool 重複を防ぐためにarxiv_idをSupabaseに記録
3 Document Parsing tool PaddleOCRがPDFを解析してテキストを抽出
4 get_footnote_text code 脚注情報を抽出 (所属認識用)
5 truncated_text code OCRテキストを切り詰める (LLM入力長を制御)
6 Analysis llm 主要情報を抽出するための詳細分析
7 Data Assembly code 最終的な論文オブジェクトを組み立てる
#### 10.1 DataUnpack (コードノード)

イテレーションアイテムを個別の変数にアンパックします。

出力:

  • title_str: 論文タイトル
  • pdf_url: PDFリンク
  • summary_str: 要旨
  • published: 公開日
  • authors: 著者
  • arxiv_id: ArXiv ID

10.2 行を作成 (Supabaseノード)

重複プッシュを防ぐため、論文のArXiv IDをデータベースに記録します。

設定:

  • テーブル名: Configurationノードから
  • データ: {\"arxiv_id\": \"{{arxiv_id}}\"}

10.3 ドキュメント解析 (PaddleOCRノード)

ノード名: Document Parsing (タイプ: tool - PaddleOCR)

PaddleOCR (LLMではない) を使用して論文PDFを解析し、OCRを介してテキストコンテンツを抽出します。

設定:

  • file: PDF URL
  • fileType: 0 (PDFファイル)
  • useLayoutDetection: true (レイアウト検出を有効にする)
  • prettifyMarkdown: true (出力を整形する)

10.4 get_footnote_text (コードノード)

OCRテキストから脚注情報を抽出し、その後の所属認識に利用します。

10.5 truncated_text (コードノード)

LLMの入力長を制御し、トークン制限を超えないようにOCRテキストを切り詰めます。

10.6 分析 (LLMノード)

ノード名: Analysis (タイプ: llm)

論文の深層分析を実行し、主要な情報を抽出します。

抽出フィールド:

  1. One_Liner: 1文での課題と解決策
  2. Architecture: モデルアーキテクチャと主要なイノベーション
  3. Dataset: データソースと規模
  4. Metrics: 主要なパフォーマンス指標
  5. Affiliation: 著者の所属
  6. Code_Url: コードリポジトリリンク

コア原則:

  • 無駄を省く: 特定の方法を直接記述する
  • 詳細に深く掘り下げる: アルゴリズムロジック、損失関数設計を要約する
  • データ優先: SOTAに対する改善を示す
  • N/Aなし: 合理的な推論を行う

出力形式: 純粋なJSONオブジェクト

10.7 データアセンブリ (コードノード)

ノード名: Data Assembly (タイプ: code)

すべての情報を構造化された論文オブジェクトに組み立てます。

コア機能:

  1. 公開ステータスの解析 (トップカンファレンス論文を特定する)
  2. LLM出力JSONの解析
  3. コードリンクの抽出
  4. 最終的な論文オブジェクトの組み立て

出力フィールド:

  • title: タイトル
  • authors: 著者
  • affiliation: 所属
  • pdf_url: PDFリンク
  • summary: 英語要旨
  • published: 公開ステータス
  • github_stats: コードステータス
  • code_url: コードリンク
  • ai_evaluation: AI分析結果

ステップ 11 — テンプレート変換

ノード名: Template Transform (タイプ: template-transform)

Jinja2テンプレートを使用して、論文データを整形されたメールコンテンツに変換します。

テンプレート構造:

📅 PaperEssence Daily
Based on your specified research topic "{{ raw_topic }}", here are the top 3 papers selected from ArXiv updates in recent times.
--------------------------------------------------
--------------------------------------------------
⚠️ Note: Content is AI-generated and for academic reference only. Please verify by checking the original PDF before citing or conducting in-depth research.
Date: {{ items.target_date | default('Today') }}
==================================================

{# Automatically adapt data structure #}
{% set final_list = items.paper | default(items) %}

{% for item in final_list %}
📄 [{{ loop.index }}] {{ item.title }}
--------------------------------------------------
👤 Authors: {{ item.authors }}
🏢 Affiliation: {{ item.affiliation }}
🔗 PDF: {{ item.pdf_url }}
📅 Status: {{ item.published }}
{% if item.code_url and item.code_url != 'N/A' %}
📦 Code: {{ item.github_stats }}
   🔗 {{ item.code_url }}
{% else %}
📦 Code: {{ item.github_stats }}
{% endif %}
Abstract:
{{ item.summary | replace('\n', ' ') }}

🚀 Core Innovation:
{{ item.ai_evaluation.One_Liner }}

📊 Summary:
{# Use newlines and indentation for hierarchy, not relying on HTML tags #}
--------------------------------------------------
🏗️ Architecture:
{{ item.ai_evaluation.Architecture | replace('\n- ', '\n\n   🔹 ') | replace('- ', '   🔹 ') }}

💾 Dataset:
{{ item.ai_evaluation.Dataset | replace('\n- ', '\n\n   🔹 ') | replace('- ', '   🔹 ') }}

📈 Metrics:
{{ item.ai_evaluation.Metrics | replace('\n- ', '\n\n   🔹 ') | replace('- ', '   🔹 ') }}

==================================================
{% else %}
⚠️ No new papers today.
{% endfor %}

ステップ 12 — メール送信 (163 SMTP)

ノード名: 163 SMTP Email Sender (タイプ: tool - 163-smtp-send-mail)

設定:

  • username_send: 送信者メールアドレス (環境変数 SMTP_USER 経由)
  • authorization_code: メール認証コード (環境変数 SMTP_PASSWORD 経由)
  • username_recv: 受信者メールアドレス (Configurationノードから)
  • subject: PaperEssence-{{cutoff_str}}-{{today_str}}
  • content: テンプレート変換からのコンテンツ

注: これらのパラメータ名 (username_sendauthorization_codeusername_recv) は163 SMTPプラグインに固有のものです。


ステップ 13 — 出力ノード

ノード名: Output (タイプ: end)

デバッグと検証のために最終結果を出力します。


:outbox_tray: ワークフローAPIの公開と取得

ワークフローが正しく動作することを確認し、テストした後、右上隅の「公開」ボタンをクリックします。

以下の情報を記録してください。

  • APIエンドポイント: https://api.dify.ai/v1/workflows/run (Difyクラウドデプロイの場合) またはプライベートデプロイURL (例: http://localhost/v1/workflows/run)
  • APIキー: app-xxxxxxxxxxxx

:alarm_clock: 代替案: ローカルスケジュールトリガー (Windowsタスクスケジューラ)

Difyクラウドのスケジューリングが無料枠で制限されている場合、Windowsタスクスケジューラを使用してcurl POST経由でワークフローをトリガーできます。

前提条件: Git for Windowsのインストール

このソリューションはGit Bashを使用してcurlコマンドを実行するため、まずGit for Windowsをインストールする必要があります (「Git for Windows」で検索してダウンロードしてください)。

インストールに関する注意:

  • デフォルトのインストールパス (例: C:\\Program Files\\Git) またはカスタムパス (例: D:\\ProgramFiles\\Git) を使用することをお勧めします。
  • インストール中に「Git Bash Here」がチェックされていることを確認してください。

Windowsタスクスケジューラの設定

  1. Win + R を押す → taskschd.msc と入力 → Enter
  2. 「タスクの作成」をクリック

全般タブ:

  • 名前: Paper-Essence Daily Run
  • 「最上位の特権で実行する」にチェックを入れる

トリガータブ:

  1. 「新規」をクリック
  2. 「スケジュールに従って」を選択
  3. 「毎日」を選択し、時刻を設定します (Difyワークフロータイマーと一致させることを推奨、例: 20:55)
  4. 「OK」をクリック

操作タブ:

  1. 「新規」をクリック

  2. 操作: 「プログラムの開始」

  3. プログラム/スクリプト: Git Bashのパスを入力します。例:

    D:\\ProgramFiles\\Git\\bin\\bash.exe
    

    またはデフォルトのインストールパス:

    C:\\Program Files\\Git\\bin\\bash.exe
    
  4. 引数の追加: curlコマンドを使用して、APIキーと共にワークフローAPIエンドポイントにPOSTします。

    :warning: : APIエンドポイントとキーは、前のステップで取得した実際の値に置き換えてください。

条件タブ (オプション):

  • ノートPCがバッテリー駆動時でもタスクが実行されるように、「コンピューターがAC電源の場合にのみタスクを開始する」のチェックを外すことができます。

設定タブ (オプション):

  • 「タスクが失敗した場合、再起動する」にチェックを入れ、再試行間隔を設定します。
  1. 「OK」をクリックしてタスクを保存します。

:test_tube: テストとデバッグ

手動テスト

  1. ワークフローエディタの右上にある「実行」をクリックします。
  2. ノードの実行と出力を観察します。
  3. 各ノードの出力が期待通りであることを確認します。

成功結果

ワークフローが正常に実行されると、日次論文ダイジェストを含むメールが届きます。


:memo: まとめ

このチュートリアルでは、arXivからの取得 → PaddleOCRによる解析 → LLM分析 → Jinja2テンプレート化 → SMTP配信という、Supabaseベースの重複排除とスケジューリングを備えた完全なエンドツーエンドパイプラインの構築について説明します。このワークフローには、YAMLノード設定、環境変数、Supabase統合が含まれており、ArXivからの取得からメール配信まで、強化された重複排除とエラー処理を備えた包括的なパイプラインを作成します。

提供されている prj/Paper-Essence-CN.ymlprj/Paper-Essence-EN.yml は、Difyワークスペースにインポートしてワークフローを再現できます。


謝辞

Alex Zhang、Guan Mu、Yang Youzhiの皆様のご指導に心より感謝いたします。

「いいね!」 1