Please enable JavaScript in your browser.

OPEAを活用した生成AIアプリ開発入門 - fltech - 富士通研究所の技術ブログ

fltech - 富士通研究所の技術ブログ

富士通研究所の研究員がさまざまなテーマで語る技術ブログ

OPEAを活用した生成AIアプリ開発入門

こんにちは。富士通研究所 先端技術開発本部の小坂佳恵です。
私たちのチームは、OSSを活用した生成AIアプリのDevOpsを実現するためのプラットフォームを検討しています。

近年注目を集めている「生成AI」。画像や文章を自動で作成する技術として知られていますが、具体的な活用方法に迷われている方も多いのではないでしょうか。 例えば、文章の要約、質問応答、アイデア創出など、様々な業務を生成AIを活用して遂行するアプリケーションを、ここでは生成AIアプリと呼んでいます。
生成AIアプリは、従来型のシステムでは困難だった高度な自然言語処理や曖昧性の許容、複雑な推論を可能にし、業務効率化や新たな価値創造に貢献することが期待されています。

今回は、そのような生成AIアプリを容易に開発し、企業環境においても安全に利用するためのプラットフォーム構築に向けた取り組みについてご紹介いたします。
そのプラットフォームを構成するOSS(オープンソースソフトウェア)であるエンタープライズ向け生成AIプラットフォーム「Open Platform for Enterprise AI (OPEA)」を用いた生成AIアプリの作成手順を、実際に試してみた内容と共にご紹介します。 「OPEA」とは何か、という疑問をお持ちの方もご安心ください。この記事を読み進めていただくうちに、OPEAの特長をご理解いただけるはずです。

目次 : 

モチベーション

昨今、AIの活用は業務効率化・自動化のために避けては通れないものになってきています。そのような中でAIを使ったアプリを作成しようとしたときに、モデルをデプロイするコンテナの起動やパラメータの設定等で躓いたことはないでしょうか?また、アプリを作成したものの、レスポンス内容がだんだんと古くなって苦労して作成したのにすぐ使えなくなった、ということもあるのではないでしょうか。私たちはそんな困りごとを解消して、簡単に生成AIアプリを開発・運用することができるプラットフォームの構築を目指しています。

私たちはこのプラットフォームを、オープンソースソフトウェア(OSS)を活用して構築しようとしています。
様々なOSSを調査していますが、セキュリティやアプリの評価まで、開発・運用に必要な機能を包括的に考えているOSSプロジェクトの1つとしてOPEAに注目しています。
そのような経緯から、今回OPEAを用いて生成AIアプリを作成してみました。

OPEAとは?

opea.dev

生成AIアプリ(ソリューション)の開発・運用には、先述した作成時の難易度の他に、投資対効果の確保やセキュリティ、エンドツーエンドのサポートなど、多くの課題が存在します。そして多くの企業が課題解決に多大な時間とコストを費やしているのが現状だと考えられます。
そのような課題の解決を目指してLinux FoundationのAI&DATA Foundationから発足したオープンソースプラットフォームがOPEA (Open Platform for Enterprise AI)です。2024年4月にSandbox Projectとして発表されたOPEAは、Intelを筆頭に、Docker、Red Hat、Hugging Face、MinIOといった企業が参画しており、多様なOSSを統合しています。

OPEAは「生成AIソリューション構築における複雑な課題を軽減する、柔軟でスケーラブルなプラットフォーム」を目指しています。従来、生成AIソリューションの開発には様々なツールや技術を個別に選定し、複雑な統合作業が必要でした。OPEAはこの課題を解決するためのアプローチとして、以下の2つの特徴を備えています。

  1. コンポーネント化されたアーキテクチャ:マイクロサービスによるモジュール化
    OPEAは生成AIアプリを構成する要素を再利用可能なマイクロサービスとして提供しています。
    ベクトルデータベース、モデル、推論エンジン、セキュリティ機能などを、必要に応じて選択・組み合わせることで独自のアプリを作成できるようにする方針です。これにより、開発期間の短縮とコスト削減に貢献できると期待されています。
  2. オープンで協調的なエコシステム:連携強化に向けた取り組み
    OPEAはオープンなコミュニティ形成を目指しています。巨大なモデルハブであるHugging Faceや、Red HatのようなOSSの専門知識を持つ企業を含めたオープンなコミュニティとすることで、様々な技術やノウハウの共有、相互連携を実現しています。
    従来、生成AIソリューション開発に必要な技術やツールは様々なソフトに分散していましたが、OPEAはこれらを1つにまとめ上げたプラットフォームを目指しています。

OPEAでアプリを作成する2つの方法

OPEAでは、以下の2通りの方法で生成AIアプリを作成できます。

  1. GenAIExamples:
    様々な生成AIアプリのサンプルが提供されており、DockerやKubernetesを用いて簡単に起動できます。
  2. GenAIStudio:
    GUIベースのローコードツールで、ワークフローを作成してアプリを作成できます。DifyやLangflowに類似したツールです。

以下、それぞれの方法を具体的な手順と共に解説します。

1. GenAIExamplesでアプリを起動する

今回は、アップロードしたファイルに基づいてチャットを行う「ChatQnA」アプリを、以下の環境で起動しました。

  • 実行環境
    • OS: Ubuntu 24.04
    • vCPU: 16コア
    • メモリ: 64GB
    • ディスク容量: 100GB
    • GPU: なし
    • Docker 27.5.1
    • Gitインストール済み

起動手順

次の手順を参考に、アプリを起動していきます。

Getting Started with OPEA — OPEA™ 1.3 documentation

※Dockerコマンドの実行権限が付いたユーザで実行してください。

1. リポジトリのクローン:

git clone https://github.com/opea-project/GenAIExamples.git  
cd GenAIExamples/ChatQnA  

2. 環境変数の設定と起動:

  • 注:事前にMETA LLAMA 3 コミュニティ ライセンスありのHugging FaceのTokenを取得してください
export host_ip="***.***.***.***" # 実行環境のIPアドレス
export HUGGINGFACEHUB_API_TOKEN="********************" # Hugging FaceのToken
cd docker_compose/intel/cpu/xeon/
source set_env.sh
export LOGFLAG=""
export TAG="1.2" # 利用するDocker ImageのTAG
# コンテナを起動
docker compose up -d

3. 起動確認:
docker ps -a でコンテナの起動を確認します。

$ docker ps -a
CONTAINER ID   IMAGE                                                   COMMAND                  CREATED          STATUS                             PORTS                                                                                  NAMES
dfc39ba24706   opea/nginx:latest                                       "/docker-entrypoint.…"   43 seconds ago   Up 25 seconds                      0.0.0.0:80->80/tcp, :::80->80/tcp                                                      chatqna-xeon-nginx-server
5fe9a952ba64   opea/chatqna-ui:latest                                  "docker-entrypoint.s…"   45 seconds ago   Up 27 seconds                      0.0.0.0:5173->5173/tcp, :::5173->5173/tcp                                              chatqna-xeon-ui-server
bc3bec4a6396   opea/chatqna:latest                                     "python chatqna.py"      47 seconds ago   Up 30 seconds                      0.0.0.0:8888->8888/tcp, :::8888->8888/tcp                                              chatqna-xeon-backend-server
ca8732c6fb9a   opea/retriever:latest                                   "python opea_retriev…"   49 seconds ago   Up 33 seconds                      0.0.0.0:7000->7000/tcp, :::7000->7000/tcp                                              retriever-redis-server
125b2a15f19c   opea/dataprep:latest                                    "sh -c 'python $( ["   49 seconds ago   Up 33 seconds                      0.0.0.0:6007->5000/tcp, [::]:6007->5000/tcp                                            dataprep-redis-server
e1f966a9a362   ghcr.io/huggingface/text-embeddings-inference:cpu-1.5   "text-embeddings-rou…"   52 seconds ago   Up 37 seconds                      0.0.0.0:8808->80/tcp, [::]:8808->80/tcp                                                tei-reranking-server
f65ff21b82a9   ghcr.io/huggingface/text-embeddings-inference:cpu-1.5   "text-embeddings-rou…"   52 seconds ago   Up 35 seconds                      0.0.0.0:6006->80/tcp, [::]:6006->80/tcp                                                tei-embedding-server
7fc3c6d9ccfd   redis/redis-stack:7.2.0-v9                              "/entrypoint.sh"         52 seconds ago   Up 36 seconds                      0.0.0.0:6379->6379/tcp, :::6379->6379/tcp, 0.0.0.0:8001->8001/tcp, :::8001->8001/tcp   redis-vector-db
076f4d09cabd   opea/vllm:latest                                        "python3 -m vllm.ent…"   52 seconds ago   Up 35 seconds (health: starting)   0.0.0.0:9009->80/tcp, [::]:9009->80/tcp

4. 動作確認: curlコマンドでリクエストを送信し、レスポンスが返ってくることを確認します。

$ curl http://${host_ip}:8888/v1/chatqna \
    -H "Content-Type: application/json" \
    -d '{
        "messages": "What is the revenue of Nike in 2023?"
    }'
data: b''
data: b'I'
data: b"'m"
data: b' happy'
data: b' to'
・・・

「I'm happy to ...」と回答が返ってきました。

カスタマイズ

GenAIExamplesのソースコードはGitHubで公開されているため、カスタマイズが可能です。また、既存システムとのAPI連携も可能です。
OPEAは豊富なサンプルアプリを提供しており、ニーズに合ったアプリが見つかるでしょう。

サンプルの一覧はこちらのドキュメントから確認できます。
Examples — OPEA™ 1.3 documentation

2. GenAIStudioでアプリを作成する

GenAIStudioは2024年11月にリリースされたローコードツールです。執筆時点(2025年3月)では機能はまだ限定的ですが、ChatQnAアプリの作成を試してみました。

  • 実行環境
    • OS: Ubuntu 22.04
    • Kubernetes v1.28.6
    • worker node:1台
      • CPU: 16コア
      • メモリ: 64GB
      • ディスク: 100GB
    • Control Plane: Gitインストール済み

環境構築

アプリを作成する前に、KubernetesのControl Plane上で次の手順を実施してGenAIStudioを構築します。

まずGitHubリポジトリをクローンします。

git clone https://github.com/yoshie-kosaka/GenAIStudio.git --branch setup

リポジトリのsetup-scripts/setup-genai-studio/配下にあるAnsible playbooksを使ってGenAIStudioを構築します。

  • 注意:

Ansible playbooksの設定ファイルにcontrol planeのIPアドレスを記載します。

  • 対象ファイル : GenAIStudio/setup-scripts/setup-genai-studio/vars.yml
  • 記載内容 : mysql_hostにcontrol planeのIPアドレスを設定する
container_registry: 'opea'
container_tag: '1.2'
http_proxy: ''
no_proxy: ''
mysql_host: <control planeのIPアドレス>

Ansibleをインストールして、Ansible playbooksを実行します。

sudo apt install ansible -y
cd GenAIStudio/setup-scripts/setup-genai-studio/
ansible-playbook genai-studio.yml

Ansible playbooksの実行が完了したら環境構築完了です。

ユーザ作成

アプリを作成したいところですが、まずGenAIStudioにログインするユーザを作成します。
環境構築でGenAIStudioのUIにアクセスするためのKubernetes ServiceはNodePort(port 30007)で作成されています。

今回は、ブラウザからアクセスできるようにSSHでポートフォワードを行います。

ssh -L 30007:<IP or FQDN(※)>:30007 -i <ssh key> <Linux user>@<IP or FQDN>

※ IP or FQDNはKubernetesのcontrol plane、worker node(どちらでも可)のIPアドレスかFQDNを設定する

  • ログイン後のリダイレクトが失敗するため、ポートは30007のままにしてください

ブラウザで以下のURLを開いてユーザ作成を行います。
https://localhost:30007/

一番下の「Register」をクリックします。

ユーザの情報を入力して「Register」をクリックします。
※後述するadminユーザでGenAIStudioにログインを試みましたが、ログインできませんでした。

You are unauthorized. Please contact your admin for approval.と表示された画面に画面遷移するので、adminユーザに切り替えてユーザ登録を承認しましょう。

まずは次のURLをブラウザで開きます。
https://localhost:30007/auth/admin/master/console/#/genaistudio

ユーザ管理(KEY CLOAK)の画面が開きます

adminユーザでサインインします。

  • adminユーザ名 : admin
  • adminユーザパスワード : admin
    • ログイン後、画面右上のプルダウンからManage accountを選択してアカウント管理画面を開きadminユーザのパスワードを変更してください

adminユーザでログインできたら、左のタブからGroupsを選択し、グループ画面を開いてunauthorized_userグループを選択します。

グループ画面

Membersタブを選択すると、先ほど登録したユーザが表示されるため、右の三点リーダーからLeaveを選択します。

登録したユーザをunauthorizedグループから削除
unauthorizedグループから削除できました

一度グループ画面に戻り、今度はuserグループを選択します。

userグループを開いてAdd memberをクリック

Add memberをクリックし、登録するユーザにチェックを入れてAddボタンをクリックします。

これでuserグループに登録したユーザが追加されて、ログインができるようになりました。

userグループにユーザが追加できました

アプリ作成の手順

再びブラウザで https://localhost:30007/ を開き、先ほど作成したユーザの情報でログインします。
ログインできると、このような画面が開きます。

ログイン直後の画面

左上の+Create New Workflowからワークフロー作成画面を開いてワークフローを作成することができますが、 今回は+ Import Sample WorkflowsをクリックしてchatQnAのワークフローをインポートしました。

sample_workflow_chatqnaをクリックしてワークフローを開きます。

既に作成済みのフローが表示されます

HuggingFace Tokenが空になっているので自分のHugging FaceアカウントのTokenを入力して、右上の保存ボタンをクリックします。

ワークフローの名前も変更できるので、変更してみます。

左上で名前の変更ができます

左上の<ボタンでフロー一覧の画面に戻ってフローのスタートボタンをクリックします。

スタートボタンをクリックします

アプリが起動するまで5~10分程度待ちます

しばらく待って、Getting ReadyからReadyになればアプリの起動完了です。
アプリが起動したらLaunch AppからアプリのUI画面を開くことができます。

アプリのUI画面を開きます

別のタブでアプリのUI画面が開けました。

アプリのUI画面

右上のファイル追加のアイコンをクリックしてファイルをアップロードすることもできます。
URLを指定して、インターネット上のサイトをインポートすることもできるようです。

ファイルをアップロードにより回答のカスタマイズ(RAG)ができるのかを確認するために、まずは何もファイルをアップロードしていない状態で「what is GenAIStudio」と質問してみました。
(GenAIStudioは2024年11月に発表されたツールなので、おそらくLLMも学習していないことから意図的に誤った回答を引き出しました)
予想通り「Gene AI Studio (also known as GAN-based Image Animation) ...」と誤った回答が返ってきました。

次に、GenAIStudioのReadmeをアップロードして同じ質問をしてみました。
今度は「GenAI Studio is a platform that simplifies the development of enterprise Generative AI applications. 」と正しい回答が返ってきました。

チーム内のナレッジをたくさんアップロードすれば、チーム内FAQが作れそうですね。
また、作成したアプリはDeploymentボタンからDocker Composeとしてエクスポートすることもできます。
※composeのファイルには先ほどUI画面からアップロードしたファイルは反映されないので、別環境でアプリを起動した場合は再度ファイルのアップロードが必要でした。

フロー画面で可能な操作

最後に

OPEAを使えば、手軽に生成AIアプリを作成・利用できることが分かりました。例えば、GenAIExamplesのサンプルを活用することで、プロトタイプのアプリを素早く起動し、顧客とアプリのイメージを共有することが可能です。また、カスタマイズ部分の実装に集中できるため、開発効率の向上が期待できます。さらに、GenAIStudioのローコードツールは、直感的にアプリを作成できるため、開発コストを削減する点で大きなメリットがあります。 一方で、生成AIアプリの動作には多くのリソースが必要となる点や、GenAIStudioの機能がまだ限定的である点は課題と言えるでしょう。
今後の発展に期待しつつ、生成AI技術の動向を注視していきたいと思います。

謝辞

この成果は、NEDO(国立研究開発法人新エネルギー・産業技術総合開発機構)の助成事業の結果得られたものです。

付録:Hugging FaceのToken準備

Hugging Faceのアカウント作成

すでにHugging Faceのアカウントをお持ちの場合はスキップしてください。

  • https://huggingface.co/join を開きます
  • 登録するメールアドレスとパスワードを入力してNextボタンをクリックします
  • 項目を入力してCreate Accountボタンをクリックします
  • 登録したメールアドレスにメールが届くので、確認リンクを開きます
    • メールのタイトル:[Hugging Face] Click this link to confirm your email address

Meta Llama 3ライセンスの取得

GenAIExamplesを使う場合のみ必要な手順です。(GenAIStudioを使う場合は不要です。) すでにライセンス取得済みの場合はスキップしてください。

  • Hugging Faceにログインした状態で以下のURLを開きます
  • Expand to review and accessをクリックします
  • ライセンスの説明が表示されるので確認してください
  • 最後までスクロールすると、連絡先の入力箇所が表示されるので必要事項を入力し、ライセンス契約に同意するチェックボックスをチェックします
    • Submitボタンをクリックしたら完了です

Hugging FaceのToken作成

  • Hugging Faceにログイン後、https://huggingface.co/settings/tokens を開きます
  • Token作成画面が開かれるので画面右上のCreate new tokenボタンをクリックします
  • Tokenの名前を入力し、権限のRead access to contents of all public gated repos you can accessのチェックを付けてください
  • 一番下までページをスクロールし、Create tokenボタンをクリックします
  • ポップアップで作成したTokenが表示されるので、メモしてください
    • ※一度画面を閉じるとTokenを確認することはできなくなるので必ず別の場所に記録してください

付録:GenAIStudioのAnsible playbooksの変更内容

GenAIStudioを構築するためのAnsible playbooksは、以下のGitHubリポジトリの資産を一部修正したものです。

GenAIStudio/setup-scripts at main · opea-project/GenAIStudio · GitHub

  • 修正したファイル
    • vars.yml
    • playbooks/setup-mysqldb.yml
    • manifests/studio-manifest.yaml

ファイルごとに修正内容を説明します。

1. vars.yml

  • 最新のコンテナイメージを使うようにcontainer_tagの値を1.2に変更
container_registry: 'opea'
container_tag: '1.2'
http_proxy: ''
no_proxy: ''
mysql_host: <MySQLサーバのIP> # 指定したIPアドレスのサーバにMySQLが構築される

2. playbooks/setup-mysqldb.yml

  • task「Install PyMySQL module」をUbuntu用に修正
  • task「End playbook if MySQL user 'studio' exists」を削除
    • 再実行した際に、それ以降のtaskがスキップされてしまうため
  • task「Secure MySQL installation updating root password」にMySQLのログインユーザ情報を追加
    • インストール後、GenAIStudioのPodが正しく起動できるようにするため
- name: Setup MySQL Server
  hosts: localhost
  become: yes
  tasks:
    
    # task「Install PyMySQL module」を修正した
    - name: Install PyMySQL module
      apt:
        name: python3-pymysql
        state: present
        update_cache: yes  # パッケージリストを更新

    - name: Check if MySQL user 'studio' exists
      mysql_user:
        login_user: root
        login_password: root
        login_unix_socket: /var/run/mysqld/mysqld.sock
        name: studio
        check_implicit_admin: yes
        state: present
      check_mode: yes
      register: studio_user_exists
      ignore_errors: yes

    # task「End playbook if MySQL user 'studio' exists」を削除した
    # - name: End playbook if MySQL user 'studio' exists
    #   meta: end_play
    #   when: studio_user_exists.changed == false
(中略)
    - name: Secure MySQL installation updating root password
      mysql_user:
        login_user: root # 追加
        login_password: root # 追加
        name: root
        host: localhost
        password: root
・・・

3. manifests/studio-manifest.yaml

  • Deployment: studio-backend内の環境変数の設定にダブルクォーテーションを追加
    • vars.ymlで設定したcontainer_tagの値1.2が数値として読み込まれてエラーになるため、文字列として読み込むようにダブルクォーテーションを追加
  • Deployment: studio-frontend内の環境変数の設定を削除
# manifests/studio-manifest.yamlから抜粋
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: studio-backend
  namespace: studio
  labels:
    app: studio-backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: studio-backend
  template:
    metadata:
      labels:
        app: studio-backend
    spec:
      containers:
      - name: studio-backend
        image: ${REGISTRY}/studio-backend:${TAG}
        imagePullPolicy: Always
        env:
        - name: APP_FRONTEND_IMAGE
          value: ${REGISTRY}/app-frontend:${TAG}
        - name: APP_BACKEND_IMAGE
          value: ${REGISTRY}/app-backend:${TAG}
        - name: REGISTRY
          value: ${REGISTRY}
        - name: TAG
          value: "${TAG}" # 「"${TAG}"」と修正した
        - name: SBX_HTTP_PROXY
          value: ${HTTP_PROXY}
        - name: SBX_NO_PROXY
          value: ${NO_PROXY}      
---
# 中間省略
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: studio-frontend
  namespace: studio
  labels:
    app: studio-frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: studio-frontend
  template:
    metadata:
      labels:
        app: studio-frontend
    spec:
      securityContext: {}
      containers:
      - name: studio-frontend
        securityContext: {}
        image: ${REGISTRY}/studio-frontend:${TAG}
        imagePullPolicy: Always
        # env:
        # - name: DATABASE_TYPE
        #   value: mysql
        # - name: DATABASE_HOST
        #   value: ${MYSQL_HOST}
        # - name: DATABASE_PORT
        #   value: "3306"
        # - name: DATABASE_USER
        #   value: studio
        # - name: DATABASE_PASSWORD
        #   value: studio
        # - name: DATABASE_NAME
        #   value: studio
        # - name: DATABASE_SSL
        #   value: "true"
        ports:
        - name: studio-frontend
          containerPort: 8080
          protocol: TCP
        resources: {}
        volumeMounts:
        - mountPath: /tmp
          name: tmp
        - name: mysql-tls
          mountPath: /etc/mysql/ssl
          readOnly: true
      volumes:
      - name: tmp
        emptyDir: {}
      - name: mysql-tls
        secret:
          secretName: mysql-tls
---