restore the api-extension-related docs (#663)

Co-authored-by: Riskey <riskey47@dify.ai>
This commit is contained in:
Riskey
2026-01-21 15:12:52 +08:00
committed by GitHub
parent 1a808c3456
commit ff1f41e5e3
14 changed files with 2163 additions and 4 deletions

View File

@@ -0,0 +1,262 @@
---
title: API 拡張
sidebarTitle: 概要
---
開発者は API 拡張モジュール機能を使用してモジュール機能を拡張できます。現在、以下のモジュール拡張がサポートされています:
* `moderation` センシティブコンテンツ審査
* `external_data_tool` 外部データツール
モジュール機能を拡張する前に、API と認証用の API Key を準備する必要があります。
対応するモジュール機能を開発する必要があるだけでなく、Dify が API を正しく呼び出せるように、以下の仕様に従う必要があります。
## API 仕様
Dify は以下の仕様でインターフェースを呼び出します:
```
POST {Your-API-Endpoint}
```
### Header
| Header | Value | Desc |
| --------------- | ----------------- | --------------------------------------------------------------------- |
| `Content-Type` | application/json | リクエストコンテンツは JSON 形式です。 |
| `Authorization` | Bearer {api_key} | API Key はトークン形式で送信されます。この `api_key` を解析し、提供された API Key と一致することを確認して、インターフェースのセキュリティを確保する必要があります。 |
### Request Body
```
{
"point": string, // 拡張ポイント、異なるモジュールには複数の拡張ポイントが含まれる場合があります
"params": {
... // 各モジュール拡張ポイントに渡されるパラメータ
}
}
```
### API レスポンス
```
{
... // API が返すコンテンツ、異なる拡張ポイントのレスポンスについては各モジュールの仕様設計を参照してください
}
```
## 検証
Dify で API-based Extension を設定する際、Dify は API の可用性を確認するために API Endpoint にリクエストを送信します。
API Endpoint が `point=ping` を受信したとき、インターフェースは `result=pong` を返す必要があります。具体的には以下の通りです:
### Header
```
Content-Type: application/json
Authorization: Bearer {api_key}
```
### Request Body
```
{
"point": "ping"
}
```
### API 期待レスポンス
```
{
"result": "pong"
}
```
## サンプル
ここでは外部データツールを例として、地域に基づいて外部の天気情報をコンテキストとして取得するシナリオを示します。
### API サンプル
```
POST https://fake-domain.com/api/dify/receive
```
**Header**
```
Content-Type: application/json
Authorization: Bearer 123456
```
**Request Body**
```
{
"point": "app.external_data_tool.query",
"params": {
"app_id": "61248ab4-1125-45be-ae32-0ce91334d021",
"tool_variable": "weather_retrieve",
"inputs": {
"location": "London"
},
"query": "How's the weather today?"
}
}
```
**API レスポンス**
```
{
"result": "City: London\nTemperature: 10°C\nRealFeel®: 8°C\nAir Quality: Poor\nWind Direction: ENE\nWind Speed: 8 km/h\nWind Gusts: 14 km/h\nPrecipitation: Light rain"
}
```
### コードサンプル
コードは Python FastAPI フレームワークに基づいています。
1. 依存関係をインストール
```
pip install fastapi[all] uvicorn
```
2. インターフェース仕様に従ってコードを記述
```
from fastapi import FastAPI, Body, HTTPException, Header
from pydantic import BaseModel
app = FastAPI()
class InputData(BaseModel):
point: str
params: dict = {}
@app.post("/api/dify/receive")
async def dify_receive(data: InputData = Body(...), authorization: str = Header(None)):
"""
Receive API query data from Dify.
"""
expected_api_key = "123456" # TODO Your API key of this API
auth_scheme, _, api_key = authorization.partition(' ')
if auth_scheme.lower() != "bearer" or api_key != expected_api_key:
raise HTTPException(status_code=401, detail="Unauthorized")
point = data.point
# for debug
print(f"point: {point}")
if point == "ping":
return {
"result": "pong"
}
if point == "app.external_data_tool.query":
return handle_app_external_data_tool_query(params=data.params)
# elif point == "{point name}":
# TODO other point implementation here
raise HTTPException(status_code=400, detail="Not implemented")
def handle_app_external_data_tool_query(params: dict):
app_id = params.get("app_id")
tool_variable = params.get("tool_variable")
inputs = params.get("inputs")
query = params.get("query")
# for debug
print(f"app_id: {app_id}")
print(f"tool_variable: {tool_variable}")
print(f"inputs: {inputs}")
print(f"query: {query}")
# TODO your external data tool query implementation here,
# return must be a dict with key "result", and the value is the query result
if inputs.get("location") == "London":
return {
"result": "City: London\nTemperature: 10°C\nRealFeel®: 8°C\nAir Quality: Poor\nWind Direction: ENE\nWind "
"Speed: 8 km/h\nWind Gusts: 14 km/h\nPrecipitation: Light rain"
}
else:
return {"result": "Unknown city"}
```
3. API サービスを起動します。デフォルトポートは 8000 で、完全な API アドレスは `http://127.0.0.1:8000/api/dify/receive`、設定された API Key は `123456` です。
```
uvicorn main:app --reload --host 0.0.0.0
```
4. Dify でこの API を設定します。
5. App でこの API 拡張を選択します。
App のデバッグ時、Dify は設定された API にリクエストを送信し、以下の内容を送信します(サンプル):
```
{
"point": "app.external_data_tool.query",
"params": {
"app_id": "61248ab4-1125-45be-ae32-0ce91334d021",
"tool_variable": "weather_retrieve",
"inputs": {
"location": "London"
},
"query": "How's the weather today?"
}
}
```
API レスポンスは:
```
{
"result": "City: London\nTemperature: 10°C\nRealFeel®: 8°C\nAir Quality: Poor\nWind Direction: ENE\nWind Speed: 8 km/h\nWind Gusts: 14 km/h\nPrecipitation: Light rain"
}
```
## ローカルデバッグ
Dify クラウド版では内部ネットワークの API サービスにアクセスできないため、ローカルで API サービスをデバッグするために、[Ngrok](https://ngrok.com) を使用して API サービスのエンドポイントを公開ネットワークに公開し、クラウドでローカルコードをデバッグできるようにします。操作手順:
1. [https://ngrok.com](https://ngrok.com) の公式サイトにアクセスし、登録して Ngrok ファイルをダウンロードします。
![Download](https://assets-docs.dify.ai/dify-enterprise-mintlify/zh_CN/guides/extension/api-based-extension/c44d6cc5425508daac8d31bc4af113df.png)
2. ダウンロード完了後、ダウンロードディレクトリに移動し、以下の説明に従って圧縮ファイルを解凍し、説明の初期化スクリプトを実行します。
```Shell
unzip /path/to/ngrok.zip
./ngrok config add-authtoken あなたのToken
```
3. ローカル API サービスのポートを確認します:
![ポート確認](https://assets-docs.dify.ai/dify-enterprise-mintlify/zh_CN/guides/extension/api-based-extension/7ac8ee0f0955f36255e0261b36499db7.png)
そして、以下のコマンドを実行して起動します:
```Shell
./ngrok http ポート番号
```
起動成功のサンプルは以下の通りです:
![Ngrok 起動](https://assets-docs.dify.ai/dify-enterprise-mintlify/zh_CN/guides/extension/api-based-extension/2b4adbe0bb1ff203da521ea6eea401f8.png)
4. Forwarding の中から、上図のように `https://177e-159-223-41-52.ngrok-free.app`(これはサンプルドメインです、自分のものに置き換えてください)が公開ネットワークドメインです。
上記のサンプルに従って、ローカルで既に起動されているサービスエンドポイントを公開し、コードサンプルのインターフェース `http://127.0.0.1:8000/api/dify/receive` を `https://177e-159-223-41-52.ngrok-free.app/api/dify/receive` に置き換えます。
この API エンドポイントは公開ネットワークでアクセス可能になります。これで、Dify でこの API エンドポイントを設定してローカルコードをデバッグできます。設定手順については、[外部データツール](/ja/use-dify/workspace/api-extension/external-data-tool-api-extension) を参照してください。
## Cloudflare Workers を使用した API 拡張のデプロイ
API 拡張をデプロイするには Cloudflare Workers の使用をお勧めします。Cloudflare Workers は簡単に公開ネットワークアドレスを提供でき、無料で使用できます。
詳細な説明については、[Cloudflare Workers を使用した API 拡張のデプロイ](/ja/use-dify/workspace/api-extension/cloudflare-worker) を参照してください。

View File

@@ -0,0 +1,131 @@
---
title: Cloudflare Workers を使用した API 拡張のデプロイ
---
## はじめに
Dify API 拡張は API Endpoint としてアクセス可能な公開ネットワークアドレスを使用する必要があるため、API 拡張を公開ネットワークアドレスにデプロイする必要があります。
ここでは Cloudflare Workers を使用して API 拡張をデプロイします。
[Example GitHub Repository](https://github.com/crazywoola/dify-extension-workers) をクローンします。このリポジトリには簡単な API 拡張が含まれており、これをベースに変更できます。
```bash
git clone https://github.com/crazywoola/dify-extension-workers.git
cp wrangler.toml.example wrangler.toml
```
`wrangler.toml` ファイルを開き、`name` と `compatibility_date` をアプリケーション名と互換性日付に変更します。
ここで注意が必要な設定は、`vars` 内の `TOKEN` です。Dify で API 拡張を追加する際に、この Token を入力する必要があります。セキュリティ上の理由から、Token としてランダムな文字列を使用することをお勧めします。ソースコードに直接 Token を書き込むのではなく、環境変数を使用して Token を渡すべきです。そのため、wrangler.toml をコードリポジトリにコミットしないでください。
```toml
name = "dify-extension-example"
compatibility_date = "2023-01-01"
[vars]
TOKEN = "bananaiscool"
```
この API 拡張はランダムな Breaking Bad の名言を返します。`src/index.ts` でこの API 拡張のロジックを変更できます。この例は、サードパーティ API とのやり取り方法を示しています。
```typescript
// ⬇️ implement your logic here ⬇️
// point === "app.external_data_tool.query"
// https://api.breakingbadquotes.xyz/v1/quotes
const count = params?.inputs?.count ?? 1;
const url = `https://api.breakingbadquotes.xyz/v1/quotes/${count}`;
const result = await fetch(url).then(res => res.text())
// ⬆️ implement your logic here ⬆️
```
このリポジトリはビジネスロジック以外のすべての設定を簡略化しています。`npm` コマンドを直接使用して API 拡張をデプロイできます。
```bash
npm install
npm run deploy
```
デプロイが成功すると、公開ネットワークアドレスが取得できます。このアドレスを Dify の API Endpoint として追加できます。`endpoint` パスを忘れないでください。このパスの具体的な定義は `src/index.ts` で確認できます。
![Dify で API Endpoint を追加](https://assets-docs.dify.ai/dify-enterprise-mintlify/zh_CN/guides/extension/api-based-extension/9433a486a441713ade6270e9dc6c0544.png)
また、`npm run dev` コマンドを使用してローカルにデプロイしてテストすることもできます。
```bash
npm install
npm run dev
```
関連する出力:
```bash
$ npm run dev
> dev
> wrangler dev src/index.ts
⛅️ wrangler 3.99.0
-------------------
Your worker has access to the following bindings:
- Vars:
- TOKEN: "ban****ool"
⎔ Starting local server...
[wrangler:inf] Ready on http://localhost:58445
```
その後、Postman などのツールを使用してローカルインターフェースをデバッグできます。
## Bearer Auth について
```typescript
import { bearerAuth } from "hono/bearer-auth";
(c, next) => {
const auth = bearerAuth({ token: c.env.TOKEN });
return auth(c, next);
},
```
Bearer 検証ロジックは上記のコードにあります。`hono/bearer-auth` パッケージを使用して Bearer 検証を実装しています。`src/index.ts` で `c.env.TOKEN` を使用して Token を取得できます。
## パラメータ検証について
```typescript
import { z } from "zod";
import { zValidator } from "@hono/zod-validator";
const schema = z.object({
point: z.union([
z.literal("ping"),
z.literal("app.external_data_tool.query"),
]), // Restricts 'point' to two specific values
params: z
.object({
app_id: z.string().optional(),
tool_variable: z.string().optional(),
inputs: z.record(z.any()).optional(),
query: z.any().optional(), // string or null
})
.optional(),
});
```
ここでは `zod` を使用してパラメータの型を定義しています。`src/index.ts` で `zValidator` を使用してパラメータを検証できます。`const { point, params } = c.req.valid("json");` で検証済みパラメータを取得します。
ここでの `point` は 2 つの値しかないため、`z.union` を使用して定義しています。`params` はオプションのパラメータなので、`z.optional` を使用して定義しています。その中には `inputs` パラメータがあり、これは `Record<string, any>` 型です。この型は、キーが string で値が any のオブジェクトを表します。この型は任意のオブジェクトを表すことができます。`src/index.ts` で `params?.inputs?.count` を使用して `count` パラメータを取得できます。
## Cloudflare Workers のログを取得
```bash
wrangler tail
```
---
**参考資料**
* [Cloudflare Workers](https://workers.cloudflare.com/)
* [Cloudflare Workers CLI](https://developers.cloudflare.com/workers/cli-wrangler/install-update)
* [Example GitHub Repository](https://github.com/crazywoola/dify-extension-workers)

View File

@@ -0,0 +1,63 @@
---
title: 外部データツール
---
AI アプリケーションを作成する際、開発者は [API 拡張](/ja/use-dify/workspace/api-extension/api-extension) を通じて外部ツールを使用して追加データを取得し、それを Prompt に組み込んで LLM の追加情報として使用できます。
## 拡張ポイント
`app.external_data_tool.query`:アプリケーション外部データツールクエリ拡張ポイント。
この拡張ポイントは、エンドユーザーが入力したアプリケーション変数の内容と会話入力内容(会話型アプリケーションの固定パラメータ)をパラメータとして API に渡します。
開発者は対応するツールのクエリロジックを実装し、文字列型のクエリ結果を返す必要があります。
### Request Body
```
{
"point": "app.external_data_tool.query", // 拡張ポイントタイプ、ここでは app.external_data_tool.query に固定
"params": {
"app_id": string, // アプリケーション ID
"tool_variable": string, // 外部データツール変数名、対応する変数ツール呼び出しのソースを示す
"inputs": { // エンドユーザーが渡した変数値、key は変数名、value は変数値
"var_1": "value_1",
"var_2": "value_2",
...
},
"query": string | null // エンドユーザーの現在の会話入力内容、会話型アプリケーションの固定パラメータ。
}
}
```
**例**:
```
{
"point": "app.external_data_tool.query",
"params": {
"app_id": "61248ab4-1125-45be-ae32-0ce91334d021",
"tool_variable": "weather_retrieve",
"inputs": {
"location": "London"
},
"query": "How's the weather today?"
}
}
```
### API レスポンス
```
{
"result": string
}
```
**例**:
```
{
"result": "City: London\nTemperature: 10°C\nRealFeel®: 8°C\nAir Quality: Poor\nWind Direction: ENE\nWind Speed: 8 km/h\nWind Gusts: 14 km/h\nPrecipitation: Light rain"
}
```

View File

@@ -0,0 +1,254 @@
---
title: センシティブコンテンツ審査
---
このモジュールは、アプリケーション内でエンドユーザーが入力したコンテンツと LLM が出力したコンテンツを審査するために使用され、2 つの拡張ポイントタイプに分かれています。
## 拡張ポイント
* `app.moderation.input`:エンドユーザー入力コンテンツ審査拡張ポイント
* エンドユーザーが渡した変数内容と会話型アプリケーションの会話入力内容を審査するために使用されます。
* `app.moderation.output`LLM 出力コンテンツ審査拡張ポイント
* LLM が出力したコンテンツを審査するために使用されます。
* LLM の出力がストリーミングの場合、出力内容は 100 文字を 1 セグメントとして API にリクエストされ、出力内容が長い場合の審査の遅延を可能な限り回避します。
### app.moderation.input
Chatflow、Agent、チャットアシスタントなどのアプリケーションで**コンテンツ審査 > 入力コンテンツを審査**が有効になっている場合、Dify は対応する API 拡張に以下の HTTP POST リクエストを送信します:
#### Request Body
```
{
"point": "app.moderation.input", // 拡張ポイントタイプ、ここでは app.moderation.input に固定
"params": {
"app_id": string, // アプリケーション ID
"inputs": { // エンドユーザーが渡した変数値、key は変数名、value は変数値
"var_1": "value_1",
"var_2": "value_2",
...
},
"query": string | null // エンドユーザーの現在の会話入力内容、会話型アプリケーションの固定パラメータ。
}
}
```
**例**:
```
{
"point": "app.moderation.input",
"params": {
"app_id": "61248ab4-1125-45be-ae32-0ce91334d021",
"inputs": {
"var_1": "I will kill you.",
"var_2": "I will fuck you."
},
"query": "Happy everydays."
}
}
```
#### API レスポンス仕様
```
{
"flagged": bool, // 検証ルールに違反しているかどうか
"action": string, // アクション、direct_output は事前設定された回答を直接出力; overridden は入力変数値を上書き
"preset_response": string, // 事前設定された回答action=direct_output の場合にのみ返される)
"inputs": { // エンドユーザーが渡した変数値、key は変数名、value は変数値action=overridden の場合にのみ返される)
"var_1": "value_1",
"var_2": "value_2",
...
},
"query": string | null // 上書きされたエンドユーザーの現在の会話入力内容、会話型アプリケーションの固定パラメータ。action=overridden の場合にのみ返される)
}
```
**例**:
* `action=direct_output`
```
{
"flagged": true,
"action": "direct_output",
"preset_response": "Your content violates our usage policy."
}
```
* `action=overridden`
```
{
"flagged": true,
"action": "overridden",
"inputs": {
"var_1": "I will *** you.",
"var_2": "I will *** you."
},
"query": "Happy everydays."
}
```
### app.moderation.output
Chatflow、Agent、チャットアシスタントなどのアプリケーションで**コンテンツ審査 > 出力コンテンツを審査**が有効になっている場合、Dify は対応する API 拡張に以下の HTTP POST リクエストを送信します:
#### Request Body
```
{
"point": "app.moderation.output", // 拡張ポイントタイプ、ここでは app.moderation.output に固定
"params": {
"app_id": string, // アプリケーション ID
"text": string // LLM 応答内容。LLM の出力がストリーミングの場合、ここでは 100 文字を 1 セグメントとした内容。
}
}
```
**例**:
```
{
"point": "app.moderation.output",
"params": {
"app_id": "61248ab4-1125-45be-ae32-0ce91334d021",
"text": "I will kill you."
}
}
```
#### API レスポンス
```
{
"flagged": bool, // 検証ルールに違反しているかどうか
"action": string, // アクション、direct_output は事前設定された回答を直接出力; overridden は入力変数値を上書き
"preset_response": string, // 事前設定された回答action=direct_output の場合にのみ返される)
"text": string // 上書きされた LLM 応答内容。action=overridden の場合にのみ返される)
}
```
**例**:
* `action=direct_output`
```
{
"flagged": true,
"action": "direct_output",
"preset_response": "Your content violates our usage policy."
}
```
* `action=overridden`
```
{
"flagged": true,
"action": "overridden",
"text": "I will *** you."
}
```
## コードサンプル
以下は、Cloudflare にデプロイ可能な `src/index.ts` コードの一部です。Cloudflare の完全な使用方法については [このドキュメント](/ja/use-dify/workspace/api-extension/cloudflare-worker) を参照してください)
コードの動作原理はキーワードマッチングを行い、Inputユーザーが入力したコンテンツおよび Output大規模モデルが返したコンテンツをフィルタリングすることです。ユーザーは必要に応じてマッチングロジックを変更できます。
```
import { Hono } from "hono";
import { bearerAuth } from "hono/bearer-auth";
import { z } from "zod";
import { zValidator } from "@hono/zod-validator";
import { generateSchema } from '@anatine/zod-openapi';
type Bindings = {
TOKEN: string;
};
const app = new Hono<{ Bindings: Bindings }>();
// API フォーマット検証 ⬇️
const schema = z.object({
point: z.union([
z.literal("ping"),
z.literal("app.external_data_tool.query"),
z.literal("app.moderation.input"),
z.literal("app.moderation.output"),
]), // Restricts 'point' to two specific values
params: z
.object({
app_id: z.string().optional(),
tool_variable: z.string().optional(),
inputs: z.record(z.any()).optional(),
query: z.any(),
text: z.any()
})
.optional(),
});
// Generate OpenAPI schema
app.get("/", (c) => {
return c.json(generateSchema(schema));
});
app.post(
"/",
(c, next) => {
const auth = bearerAuth({ token: c.env.TOKEN });
return auth(c, next);
},
zValidator("json", schema),
async (c) => {
const { point, params } = c.req.valid("json");
if (point === "ping") {
return c.json({
result: "pong",
});
}
// ⬇️ impliment your logic here ⬇️
// point === "app.external_data_tool.query"
else if (point === "app.moderation.input"){
// 入力チェック ⬇️
const inputkeywords = ["入力フィルターテスト1", "入力フィルターテスト2", "入力フィルターテスト3"];
if (inputkeywords.some(keyword => params.query.includes(keyword)))
{
return c.json({
"flagged": true,
"action": "direct_output",
"preset_response": "入力に違法コンテンツが含まれています。別の質問で試してください!"
});
} else {
return c.json({
"flagged": false,
"action": "direct_output",
"preset_response": "入力は正常です"
});
}
// 入力チェック完了
}
else {
// 出力チェック ⬇️
const outputkeywords = ["出力フィルターテスト1", "出力フィルターテスト2", "出力フィルターテスト3"];
if (outputkeywords.some(keyword => params.text.includes(keyword)))
{
return c.json({
"flagged": true,
"action": "direct_output",
"preset_response": "出力にセンシティブコンテンツが含まれており、システムによってフィルタリングされました。別の質問で試してください!"
});
}
else {
return c.json({
"flagged": false,
"action": "direct_output",
"preset_response": "出力は正常です"
});
};
}
// 出力チェック完了
}
);
export default app;
```