# カスタムプラグインの開発基礎

## 1. 概要

![](/files/bdxYLYv9FOqnu5ZP7Tq7)

### 開発の目的

標準機能では対応できない以下の要件を実現するために開発します。

* **社内システム連携**: CRM、ERP、独自DBなどからのデータ取得・更新
* **独自ロジックの実行**: 複雑な計算、特定フォーマットへの変換
* **外部API利用**: 公開されていない、または特殊な認証が必要なAPIの利用

### 実現アプローチ

主に以下の2つのパターンがあります。

1. **HTTP API連携型**: OpenAPI (Swagger) 仕様書を作成し、LLMツールとしてエンドポイントを登録する方法。最も標準的で互換性が高い。
2. **コード実行型**: Python等のスクリプトをプラットフォーム上のサンドボックスやローカル環境で実行する方法。

## 2. OpenAPI 定義の実践

HTTP API連携型において、LLMが正しくツールを利用するためには、正確なOpenAPI定義（Swagger）が不可欠です。

### 定義例（顧客検索API）

```yaml
openapi: 3.0.0
info:
  title: 顧客管理API
  description: 顧客情報の検索・取得を行うAPI
  version: 1.0.0
servers:
  - url: https://api.company.com/v1
paths:
  /customers/search:
    get:
      operationId: searchCustomers
      summary: 顧客検索
      description: 名前やメールアドレスの一部から顧客を検索します。
      parameters:
        - name: query
          in: query
          required: true
          schema:
            type: string
          description: 検索キーワード（氏名またはメール）
        - name: limit
          in: query
          schema:
            type: integer
            default: 10
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerList'
components:
  schemas:
    CustomerList:
      type: object
      properties:
        customers:
          type: array
          items:
            type: object
            properties:
              id: {type: string}
              name: {type: string}
              email: {type: string}
```

## 3. 開発ベストプラクティス

### 3.1 レスポンス設計（LLM向け）

LLMが解釈しやすいよう、JSON構造はフラットで明快なものを推奨します。

**推奨フォーマット**

```json
// 推奨: 構造化されたJSON
{
  "success": true,
  "data": {
    "results": [
      {"name": "山田太郎", "status": "active"}
    ]
  },
  "message": "1件ヒットしました"
}
```

### 3.2 エラーハンドリング

HTTPステータスコードだけでなく、レスポンスボディにもエラー詳細を含めることで、LLMが「なぜ失敗したか」を理解し、ユーザーに説明しやすくなります。

* `400 Bad Request`: 入力パラメータの不備（例: 必須項目の欠落）
* `401 Unauthorized`: APIキーの期限切れや誤り
* `404 Not Found`: 対象データが存在しない

**エラーレスポンス**

```json
// エラー時も構造化して返す
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "指定された顧客が見つかりません"
  }
}
```

### LLMが理解しやすい設計

| 要素    | ポイント                       |
| ----- | -------------------------- |
| 操作名   | 動詞+名詞で明確に（searchCustomers） |
| 説明文   | 用途を具体的に記述                  |
| パラメータ | 必須/任意を明示、デフォルト値設定          |
| レスポンス | 人間が読める形式で                  |

## 4. セキュリティ対策

カスタムプラグインは社内データへの入り口となるため、以下の対策が必須です。

1. **認証・認可**: API KeyやOAuth2.0を利用し、アクセス元を制限する。
2. **最小権限**: プラグインに与えるDB接続権限などは、読み取り専用（ReadOnly）にするなど必要最小限に留める。
3. **入力検証**: SQLインジェクションやOSコマンドインジェクションを防ぐため、入力値は必ずサニタイズ・検証を行う。
4. **データ保護**: 個人情報（PII）を含むレスポンスは、必要に応じてマスク処理（例: `090-****-1234`）を行う。

## 5. バックエンド実装例 (Python/FastAPI)

以下は、OpenAPI定義に基づいたバックエンドの最小実装例です。

```python
from fastapi import FastAPI, HTTPException, Header, Query
from pydantic import BaseModel
from typing import List, Optional

app = FastAPI()

# セキュリティ設定（環境変数等で管理することを推奨）
VALID_API_KEY = "sk-secret-key"

# データモデル定義
class Customer(BaseModel):
    id: str
    name: str
    email: str

class SearchResponse(BaseModel):
    total: int
    customers: List[Customer]

# ダミーデータ
MOCK_DB = [
    Customer(id="1", name="山田太郎", email="yamada@example.com"),
    Customer(id="2", name="鈴木花子", email="suzuki@example.com"),
]

@app.get("/customers/search", response_model=SearchResponse)
async def search_customers(
    query: str = Query(..., description="検索キーワード"),
    limit: int = 10,
    x_api_key: str = Header(..., alias="X-API-Key")
):
    # 簡易認証
    if x_api_key != VALID_API_KEY:
        raise HTTPException(status_code=401, detail="Invalid API Key")

    # 検索ロジック
    results = [
        c for c in MOCK_DB 
        if query in c.name or query in c.email
    ]
    
    return SearchResponse(
        total=len(results[:limit]), 
        customers=results[:limit]
    )
```

## 6. デバッグとテスト

### テスト手順

1. **単体テスト** `curl` や Postman を使用し、直接APIエンドポイントを叩いて動作を確認する。

   ![dify-docs-custom-plugin-development-basics\_dhkk\_flow\_debug\_tool.png](/files/VJfgAA8mk0P0CxPkgaFL)
2. **ツール登録** プラットフォーム（Dify, ChatGPTなど）にOpenAPI定義をインポートする。

   ![](/files/uy4gQd34MDMS6R6FD1Zo)
3. **対話テスト** 実際にLLMチャット画面から（例: 「山田さんを検索して」と）指示し、ツールが正しく呼び出されるか確認する。

### よくあるトラブル

* **Schema Validation Error**: OpenAPI定義の型と実際のレスポンス型が不一致。
* **Timeout**: 処理に時間がかかりすぎている（一般に30秒〜60秒以内が目安）。
* **Context Limit**: レスポンスのJSONが巨大すぎてLLMのトークン制限を超える（ページネーションや要約が必要）。

## 7. プラグインパッケージ構成（参考）

コードベースで拡張する場合（例: Difyのローカル拡張や独自のAgent実装）、一般的に以下のファイル構成が用いられます。 ※プラットフォームによりファイル名や仕様は異なります。

* **manifest.json / tool.yaml**: プラグインのメタデータ（名称、作者、バージョン）
* **main.py**: 処理の本体コード
* **schema.json / parameters**: 入出力定義
* **requirements.txt**: 依存ライブラリ

### 開発フロー

1. **設計**: 入出力パラメータと処理フローを決定。
2. **実装**: ローカルでPythonコードを作成・デバッグ。
3. **定義**: メタデータファイルを作成。
4. **デプロイ**: 指定のディレクトリに配置、またはリポジトリ経由でロード。

## 8. デジタルヒューマン・対話AI向け考慮

対話型AIでの利用において、以下の点を考慮するとUXが向上します。

* **応答速度**: ユーザーを待たせないよう、重い処理は非同期化するか、中間報告を返す。
* **要約情報**: 詳細データの他に「要約（summary）」フィールドを含めると、AIが回答を生成しやすくなる。
* **自然言語の説明**: パラメータの description には、「ユーザーがIDを指定しなかった場合に使います」といった具体的な挙動を記述すると、AIの判断精度が上がる。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.digitalhumans.jp/dify-guide/plugins/dify-docs-custom-plugin-development-basics.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
