2025.05.16

dify-extarnal-knowledge-api-retrieval-notion-api

Difyの外部ナレッジAPIでNotion APIのデータを取得

  • Dify
  • AI
  • Notion
  • React

はじめに

Difyのナレッジは簡単にNotionと同期でき便利に使えます。前回の記事ではNotionから同期でナレッジを作成しチャットbotを動かしましたが、この方法ではNotion側でデータの更新があった場合にDify側では手動でそれを更新しなければいけませんでした。

https://topaz-inc.dev/articles/dify-notion-knowledge-ai-chat-bot-react/

いずれはDifyがアップデートしてそれが自動でできるようになるかもしれませんが、現時点でそれを解決するためにNotion APIと外部アプリAPIを使い手動同期なしに最新のNotionにあるデータをナレッジとして使う方法を本記事を通して記録しました。

Notion APIの設定

まずはNotionのインテグレーションを新規作成します。

インテグレーションシークレットキーをコピーして、機能は今回はコンテンツを読み取るだけにしました。

Notion APIとして使いたいページで先ほど作成したインテグレーションと接続します。

試しにページIDと先ほどのシークレットキーを含めて、Curlでリクエストしてみると正常に返ってきました。

curl -X GET \
     'https://api.notion.com/v1/blocks/<page_id>/children' \
     -H 'Notion-Version: 2022-06-28' \
     -H 'Authorization: Bearer <integration_secret_key>'

外部アプリAPIの用意


外部APIに /api/notion_api/retrieval となるようなAPIエンドポイントを設置してDifyからのリクエストを受けて、Notion APIの値を取得し、それを返すような処理を実装します。
Dify が期待する形式は、records 配列を含む JSONなのでそれに合わせます。
今回は取り急ぎ、Gatsby(React, TypeScript)で用意しています。

import type { GatsbyFunctionRequest, GatsbyFunctionResponse } from "gatsby"

interface NotionRichText {
  plain_text: string
}

interface NotionParagraph {
  rich_text: NotionRichText[]
}

interface NotionBlock {
  type: string
  paragraph: NotionParagraph
}

interface NotionResponse {
  results: NotionBlock[]
}

interface Record {
  metadata: {
    path: string
    description: string
  }
  score: number
  title: string
  content: string
}

export default async function handler(
  req: GatsbyFunctionRequest,
  res: GatsbyFunctionResponse
) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method not allowed" })
  }

  try {
    const NOTION_INTEGRATION_SECRET_KEY = "xxx"
    const NOTION_PAGE_ID = "xxx"
    const NOTION_API_URL = `https://api.notion.com/v1/blocks/${NOTION_PAGE_ID}/children`

    const response = await fetch(NOTION_API_URL, {
      method: "GET",
      headers: {
        "Notion-Version": "2022-06-28",
        Authorization: `Bearer ${NOTION_INTEGRATION_SECRET_KEY}`,
        "Content-Type": "application/json",
      },
    })

    if (!response.ok) {
      throw new Error(`Notion API error: ${response.statusText}`)
    }

    const data = (await response.json()) as NotionResponse
    const records = data.results
      .map((block: NotionBlock): Record | null => {
        if (
          block.type === "paragraph" &&
          block.paragraph.rich_text.length > 0
        ) {
          const content = block.paragraph.rich_text
            .map((textItem: NotionRichText) => textItem.plain_text)
            .join("\n")
          const title = "Notion Content"
          const metadata = {
            path: `notion://page/${NOTION_PAGE_ID}`,
            description: "Notion ページの内容",
          }
          return { metadata, score: 1.0, title, content }
        }
        return null
      })
      .filter((record): record is Record => record !== null)

    return res.status(200).json({ records })
  } catch (error) {
    console.error("Error:", error)
    return res.status(500).json({ error: "Internal server error" })
  }
}

Dify側の設定

上記で作成した外部APIのエンドポイントを、外部ナレッジベース連携APIに設定します。

ナレッジと外部ナレッジ連携APIを連携します。

そのナレッジをチャットボットアプリで使うよう選択します。


これでNotion側を更新した後に、外部アプリから動かしてみると、Notionにしかない内容を元にチャットボットが返信してくれました。

最後に

これでDify側のナレッジベースを更新することなく、書き慣れているNotion側の変更だけで済むので管理が楽になりました。

今回は簡単に再現してみましたが、実際の業務フローを想定するともっとやるべき点があると思いますので、セキュリティ面なども踏まえ、実用に向けてさらに構築していきたいと思います。