2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloudflareで自宅PCのAPIサーバを公開して、Difyの外部ナレッジAPIとして活用する手順

Posted at

自宅PCのAPIサーバを公開して、Difyの外部ナレッジAPIとして活用する手順です。

<Difyで使うかどうかに関わらず、Webサーバを公開する際に便利です>

https://www.creationline.com/tech-blog/cloudnative/75142
上記の記事を参考にして作業しました。
<terraformが出てきたところから分からなかったので、Chatgptに聞きながら作業しました>

以下Chatgptとの対話です。

質問 <ここは概要を理解するための質問なので、この通り実施しない>

Cloudflare Tunnel: をWINDOWSで使う手順を詳細に教えて

Cloudflare TunnelをWindowsで使用する手順を説明します:

1.Cloudflaredのインストール:
Cloudflareのダウンロードページ から Windows用の実行ファイルをダウンロード
ダウンロードしたexeファイルを実行してインストール

2.Cloudflareアカウントの設定:
PowerShellを管理者として実行し、以下のコマンドを実行
cloudflared.exe login

ブラウザが開き、Cloudflareにログインするように促される
ログイン後、認証証明書が自動的に保存される
(通常は C:\Users<ユーザー名>.cloudflared\cert.pem)

3.トンネルの作成:
トンネルの作成
cloudflared.exe tunnel create rag-api

トンネルのIDを確認(後で使用)
cloudflared.exe tunnel list

4.設定ファイルの作成:
C:\Users<ユーザー名>.cloudflared\config.yml を作成:

tunnel: <トンネルID>
credentials-file: C:\Users\<ユーザー名>\.cloudflared\<トンネルID>.json

ingress:
  - hostname: your-domain.com
    service: http://localhost:8000
  - service: http_status:404

5.DNSレコードの設定:
DNSレコードを作成
cloudflared.exe tunnel route dns rag-api yourdomain.com

6.トンネルの実行:
トンネルを起動
cloudflared.exe tunnel run rag-api

7.Windowsサービスとして実行(オプション):
サービスとしてインストール
cloudflared.exe service install

サービスを開始
Start-Service cloudflared

8.動作確認:
テストAPIの実行

curl -X POST https://rag-api.yourdomain.com/retrieval \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_API_KEY" \
  -d '{
    "knowledge_id": "test-knowledge",
    "query": "RAGについて教えて",
    "retrieval_setting": {
        "top_k": 3,
        "score_threshold": 0.5
    }
}'

セキュリティ上の注意点:

config.yml のパーミッションを適切に設定
証明書ファイル(cert.pem)を安全に保管
APIキーを環境変数として設定
ファイアウォールの設定を確認
トラブルシューティング:ログは C:\Users<ユーザー名>.cloudflared\ に保存される
エラーが発生した場合は
cloudflared.exe tunnel info rag-api で状態を確認
必要に応じて --loglevel debug オプションを使用

次のステップ:
1.トンネルの作成:
cloudflared.exe tunnel create rag-api-tunnel

2.設定ファイルの作成(C:\Users<ユーザー名>.cloudflared\config.yml):

tunnel: 3196188c3762a6aef47dd852d124
credentials-file: C:\Users\<ユーザー名>\.cloudflared\3196188c3762a6aef47dd852d124.json

ingress:
  - hostname: your-domain.cloudflare.com
    service: http://localhost:8000
  - service: http_status:404

3.トンネルの起動:
cloudflared.exe tunnel run rag-api-tunnel

<ここまでは概要を理解するための質問なので、この通り実施しない>

質問 windows11 で、Cloudflare を活用して、現在作成中のmain.pyを公開したら、Difyの外部ナレッジとして活用できるよね?

はい、その通りです。現在のコードをCloudflare Tunnelで公開するための手順を説明します:
1.必要な追加パッケージ:

scikit-learn
numpy
pandas
flask
python-dotenv
mecab-python3
unidic-lite
gunicorn  # 本番環境用のWSGIサーバー

2.config.py の作成 (まだない場合):

import os
from dotenv import load_dotenv
load_dotenv()
# APIキー設定
API_KEY = os.getenv("API_KEY", "your_API_KEY")
# RAG設定
DEFAULT_TOP_K = 3
DEFAULT_SCORE_THRESHOLD = 0.5
CSV_PATH = "RAG.csv"
# サーバー設定
HOST = "0.0.0.0"
PORT = int(os.getenv("PORT", 8000))

3.Cloudflare Tunnelのセットアップ手順:
3-1. cloudflaredをインストール
https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
から最新のWindows用インストーラーをダウンロード

3-2. Cloudflareにログイン
cloudflared-windows-amd64.exe login

3-3. トンネルを作成
cloudflared-windows-amd64.exe tunnel create rag-api

3-4. トンネルのIDをメモ
cloudflared-windows-amd64.exe tunnel list

4.設定ファイルの作成 (C:\Users<ユーザー名>.cloudflared\config.yml):

tunnel: <トンネルID>
credentials-file: C:\Users\<ユーザー名>\.cloudflared\<トンネルID>.json

ingress:
  - hostname: rag-api.yourdomain.com
    service: http://localhost:8000
  - service: http_status:404

# オプションの設定
originRequest:
  connectTimeout: 30s
  noTLSVerify: true

5.DNSレコードの設定:
cloudflared.exe tunnel route dns rag-api rag-api.yourdomain.com

6.トンネルの起動:
cloudflared.exe tunnel run rag-api

これで、Difyの外部ナレッジ設定で以下のように設定できます:
URL: https://rag-api.yourdomain.com/retrieval
Method: POST
Headers:
Authorization: Bearer your_API_KEY
Content-Type: application/json

注意点:
1.MeCabのパスが環境に依存しているので、以下のように修正することをお勧めします:

import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import logging
import MeCab
import re
import traceback
import os

class RAGEngine:
    def __init__(self, csv_path):
        self.df = pd.read_csv(csv_path, encoding='utf-8')
       
        # MeCabのパスを環境変数から取得するか、デフォルトパスを使用
        mecab_path = os.getenv('MECAB_PATH', '-Owakati')
        self.mecab = MeCab.Tagger(mecab_path)
        # 初期化時のエラーを回避するためのダミー解析
        self.mecab.parse('')
       
        self.vectorizer = TfidfVectorizer(
            analyzer='char_wb',
            ngram_range=(2, 3),
            max_features=10000,
            min_df=1,
            max_df=0.95,
            sublinear_tf=True,
            token_pattern=r'(?u)\b\w+\b'  # 単語の境界をより厳密に
        )
        self._prepare_vectors()
       
    def tokenize(self, text):
        """テキストを形態素解析して単語に分割"""
        try:
            text = self.preprocess_text(text)
            result = self.mecab.parse(text)
            if result is None:
                logging.error("MeCab parse returned None")
                return []
            tokens = result.strip().split()
            logging.debug(f"Tokenized text: {tokens}")  # デバッグ用ログ
            return tokens
        except Exception as e:
            logging.error(f"Tokenization error for text '{text}': {str(e)}")
            return []
       
    def preprocess_text(self, text):
        """テキストの前処理"""
        if not isinstance(text, str):
            text = str(text)
        # 改行を空白に置換
        text = re.sub(r'\n', ' ', text)
        # 特殊文字を削除
        text = re.sub(r'[【】「」『』()\[\]()\(\)]+', ' ', text)
        # 連続する空白を1つに置換
        text = re.sub(r'\s+', ' ', text)
        return text.strip()
       
    def _prepare_vectors(self):
        """質問のベクトル化を行う"""
        try:
            # 質問テキストを抽出してベクトル化
            questions = self.df['question'].fillna('')
            # 前処理を適用
            processed_questions = [self.preprocess_text(q) for q in questions]
            self.question_vectors = self.vectorizer.fit_transform(processed_questions)
           
            # デバッグ用ログ
            logging.info("Vectors prepared successfully")
            logging.debug(f"Total questions: {len(processed_questions)}")
            for i, (orig, proc) in enumerate(zip(questions, processed_questions)):
                logging.debug(f"Q{i}: Original: {orig}")
                logging.debug(f"Q{i}: Processed: {proc}")
           
        except Exception as e:
            logging.error(f"Error preparing vectors: {str(e)}")
            raise

    def search(self, query, top_k=5, score_threshold=0.5):
        """クエリに類似した質問-回答ペアを検索する"""
        try:
            # ク���リの前処理
            processed_query = self.preprocess_text(query)
            logging.debug(f"Search query: {query} -> {processed_query}")
           
            # クエリをベクトル化
            query_vector = self.vectorizer.transform([processed_query])
           
            # コサイン類似度を計算
            similarities = cosine_similarity(query_vector, self.question_vectors).flatten()
           
            # クエリの意図を分析
            query_keywords = {
                'RAG': ['rag', 'retrieval', 'augmented', 'generation'],
                'Dify': ['dify', 'defy', 'platform', 'プラットフォーム'],
                # 他のトピックも必要に応じて追加
            }
           
            # クエリがどのトピックに関連するか判定
            query_lower = query.lower()
            matched_topic = None
            for topic, keywords in query_keywords.items():
                if any(keyword in query_lower for keyword in keywords):
                    matched_topic = topic
                    break
           
            if matched_topic:
                # トピックに関連する問のスコアを調整
                for i, question in enumerate(self.df['question']):
                    question_lower = question.lower()
                    if any(keyword in question_lower for keyword in query_keywords[matched_topic]):
                        similarities[i] *= 2.0  # 関トピックのスコアを増加
                    else:
                        similarities[i] *= 0.5  # 無係なトピックのスコアを減少
           
            # スコアの正規化(0-1の範囲に)
            max_score = similarities.max()
            if max_score > 0:
                similarities = similarities / max_score
           
            # 上位k件を取得
            top_indices = np.argsort(similarities)[-top_k:][::-1]
           
            results = []
            for idx in top_indices:
                score = float(similarities[idx])
                if score >= score_threshold:  # スコアが閾値以上の結果のみを返す
                    row = self.df.iloc[idx]
                    result = {
                        "content": str(row['Answer']),
                        "score": score,
                        "title": row['Title'],
                        "metadata": {}
                    }
                    if 'url' in row:
                        result["metadata"]["url"] = row['url']
                    results.append(result)
                    logging.info(f"Found match: {row['question']} (score: {score:.4f})")
           
            return results
           
        except Exception as e:
            logging.error(f"Search error: {str(e)}\n{traceback.format_exc()}")
            raise

2.セキュリティ強化のために、API_KEYを環境変数から取得するようにしましょう。
これらの設定が完了すれば、DifyからRAG APIとして利用できるようになります。

質問 WEB画面でDNSレコードを修正するなどの作業は必要ないですか?

Cloudflareのウェブ画面での設定も必要です。以下の手順で設定を行います:

1.Cloudflareのダッシュボードにログイン:
https://dash.cloudflare.com にアクセス

2.ドメインの設定:
使用するドメインを選択(または新規追加)
「DNS」タブを選択

3.DNSレコードの追加:
「レコードを追加」をクリック
以下の設定を入力:

Type: CNAME
Name: rag-api(サブドメイン名)
Target: your-tunnel-id.cfargotunnel.com
Proxy status: Proxied(オレンジの雲マーク)

4.SSL/TLS設定:
「SSL/TLS」タブを選択
暗号化モード: 「Full」または「Full (Strict)」を選択

5.ゼロトラストの設定(オプション):
「Zero Trust」→「Access」→「Applications」
「Add an application」をクリック
アプリケーション名とドメインを設定
必要に応じてアクセス制御を設定

6.トンネルの確認:
トンネルの状態確認
cloudflared.exe tunnel info rag-api

DNSレコードの確認
cloudflared.exe tunnel route dns rag-api rag-api.yourdomain.com

これらの設定が完了したら、以下のURLでAPIにアクセスできるようになります:
https://rag-api.yourdomain.com/retrieval

Difyの外部ナレッジ設定では、このURLを使用します。
注意点:DNSの反映には数分から数時間かかる場合があります
SSL証明書の生成と適用も自動的に行われますが、少し時間がかかります
初回アクセス時にCloudflareの保護機能による確認が入る場合があります

質問 これは、トンネルIDですか? 

.\cloudflared-windows-amd64.exe tunnel create rag-api

Tunnel credentials written to C:\Users\tarou\.cloudflared\e773085c-a587-4c1a-b7f7.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.

Created tunnel rag-api with id e773085c-a587-4c1a-b7f7

はい、その通りです。トンネルIDは: e773085c-a587-4c1a-b7f7 です。
このIDを使って、C:\Users\tarou.cloudflared\config.yml を以下のように作成します:

tunnel: e773085c-a587-4c1a-b7f7
credentials-file: C:\Users\tarou\.cloudflared\e773085c-a587-4c1a-b7f7.json

ingress:
  - hostname: rag-api.your-domain.com
    service: http://localhost:8000
  - service: http_status:404

#オプションの設定
originRequest:
  connectTimeout: 30s
  noTLSVerify: true

このトンネルIDは、以下のような操作でも確認できます:
.\cloudflared-windows-amd64.exe tunnel list

このIDは重要な情報なので、安全に保管してください。トンネルを削除する必要がある場合は、このIDを使用します。

質問 DNSレコードの設定はOK? 

.\cloudflared-windows-amd64.exe tunnel route dns rag-api rag-api.yourdomain.com
2024-12-08T10:39:18Z INF Added CNAME rag-api.yourdomain.com which will route to this tunnel tunnelID=e773085c-a587-4c1a-b7f7
PS C:\Users\tarou\DifyAPI> 

はい、DNSレコードの設定は正常に完了しています。メッセージの内容から:
1.CNAMEレコードが正常に追加されました

2.ホスト名: rag-api.yourdomain.com

3.トンネルID: e773085c-a587-4c1a-b7f7
次のステップとして、config.yml を以下のように更新する必要があります:

tunnel: e773085c-a587-4c1a-b7f7
credentials-file: C:\Users\tarou\.cloudflared\e773085c-a587-4c1a-b7f7.json

ingress:
  - hostname: rag-api.yourdomain.com
    service: http://localhost:8000
  - service: http_status:404

originRequest:
  connectTimeout: 30s
  noTLSVerify: true

これで、以下のURLでAPIにアクセスできるようになります:
https://rag-api.yourdomain.com/retrieval

トンネルを起動するには:
.\cloudflared-windows-amd64.exe tunnel run rag-api

質問

  • self.mecab = MeCab.Tagger('-Owakati -r "C:/Program Files/MeCab/etc/mecabrc"') 

を削除して、

mecab_path = os.getenv('MECAB_PATH', '-Owakati')
self.mecab = MeCab.Tagger(mecab_path)

を追加するということは、環境変数に
MECAB_PATH=”C:/Program Files/MeCab/etc/mecabrc”

と記述が必要ですね?

はい、その通りです。.env ファイルに以下のように環境変数を設定する必要があります:

# MeCab設定
MECAB_PATH=-Owakati -r "C:/Program Files/MeCab/etc/mecabrc"

# APIキー設定
API_KEY=your_API_KEY

# サーバー設定
PORT=8000

この設定により:rag.py で os.getenv('MECAB_PATH', '-Owakati') を使用したとき、
.env ファイルから設定を読み込みます
環境変数 MECAB_PATH が設定されていない場合は、デフォルト値 -Owakati が使用されます
Windows環境でのMeCabの設定が正しく維持されます
注意点:パスに空白が含まれているため、ダブルクォート " で囲む必要があります
パスの区切り文字はフォワードスラッシュ / を使用します

次のステップ:
1.トンネルを起動:
.\cloudflared-windows-amd64.exe tunnel run rag-api

2.動作確認:

   curl -X POST https://rag-api.yourdomain.com/retrieval \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer your_API_KEY" \
     -d '{
       "knowledge_id": "test-knowledge",
       "query": "RAGについて教えて",
       "retrieval_setting": {
           "top_k": 3,
           "score_threshold": 0.5
       }
   }'

この状態で、Difyの外部ナレッジベース設定で以下のように設定できます:

URL: https://rag-api.yourdomain.com/retrieval
Method: POST
Headers:
  - Content-Type: application/json
  - Authorization: Bearer your_API_KEY

Request Body Format:
{
    "knowledge_id": "test-knowledge",
    "query": "${input}",
    "retrieval_setting": {
        "top_k": 3,
        "score_threshold": 0.5
    }
}

質問 difyの外部ナレッジベースへの接続画面があります。

以下の2か所を設定をする必要があります。
何を入れれば良い?
1)外部ナレッジ名(ナレッジベースの名前を入力してください)
2)外部ナレッジID(ナレッジIDを入力してください)
外部ナレッジAPIには、「rag-api」と入力されてます

Difyの外部ナレッジベース設定では、以下のように入力することをお勧めします:
1.外部ナレッジ名:
RAG Knowledge Base
または
RAG API Knowledge Base

2.外部ナレッジID
test-knowledge

理由:外部ナレッジIDは、APIリクエストの knowledge_id パラメータと一致する必要があります
現在のAPIコードでは test-knowledge を使用しています(curlコマンドのテストで使用した値)
注意:外部ナレッジ名は管理用の表示名なので、分かりやすい名前を付けることができます
外部ナレッジIDは、APIリクエストで使用される識別子なので、コードと一致させる必要があります

以上

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?