TECH PLAY

サイオステクノロジー(Tech.Lab)

サイオステクノロジー(Tech.Lab) の技術ブログ

546

はじめに こんにちは!SBOMツールを調査中のなーがです。前回に引き続きSCANOSSについてみていきます。今回はPythonのSDK「scanoss.py」を使ってみたのでインストール方法や機能について書きます。 SBOMについてよく分からないという方は、まず こちら の記事を読んでみてください。 SCANOSSについて 検証用GUIツール「SBOM Workbench」 PythonのSDK「scanoss.py」 ← 今回 インストール Pythonの仮想環境を作成し、 Python Scanner をインストールします。 https://www.softwaretransparency.org/download コマンドオプション コマンドオプションは以下の通りです。 scan だけでなく、依存関係を含めたスキャンやフォーマット変換が用意されています。 Fingerprinting生成(オプション) スキャンコマンドの実行中に自動的に行われます。ここで生成されたFingerprintがサーバーに送信されます。 fingerprint.wfp file=6e588ad70d32289ba8473f7e24716b8f,476,/express-app/views/index.ejs 5=a0e44c67 6=50cf271f,688f9bfe 10=3a8d7c3a 12=dac0aa3f 14=c6b5efb0 16=7af50e1f file=31c693a880e86ce5a534fb03d151b4f4,2391,/express-app/index.js 5=40daea7b,ccb8356e 9=5800d9dc 10=b9ccb2d2,0db59e12 12=d59964a0 14=f636b37b 17=1db7ed12 19=d73a22b6,a8f419e7 20=8197b0b1,6b8f68ef 22=f1421678 24=fdd6d858 26=78f246d7,1fc66e08 28=72bd1285 32=f24bbde2,ac04acee ... 最初のフィールドはスキャンするファイルの MD5 を含む文字列になっています。この例では下記になります。 6e588ad70d32289ba8473f7e24716b8f 次にファイルサイズ、 476 最後にファイル名が表示されています。 /express-app/views/index.ejs その下は行番号とソースコードの抽象化を表すフィンガープリントによって作成された行のリストになります。 5=a0e44c67 6=50cf271f,688f9bfe 10=3a8d7c3a 12=dac0aa3f 14=c6b5efb0 16=7af50e1f このように、フィンガープリントからソースコードを再構築することはほとんど不可能であることがわかります。 スキャン実行 下記のコマンドを実行します。スキャン中はプログレスバーが表示されます。スキャンにかかる時間はプロジェクトのファイル数が多くなるにつれて長くなります。 scanoss-py scan -o scan-output.json スキャン対象のフォルダをパスで指定 フォーマットを指定しない場合は、下記のようにJSON形式で出力されます。出力内容としては、一致したコンポーネント名、バージョン、パール、ライセンス、著作権などの情報が含まれます。 { "ag-api//function_app.py": [ { "id": "none", "server": { "kb_version": { "daily": "24.09.26", "monthly": "24.08" }, "version": "5.4.8" } } ], "xpress-app//index.js": [ { "id": "none", "server": { "kb_version": { "daily": "24.09.26", "monthly": "24.08" }, "version": "5.4.8" } } ], "xpress-app//node_modules//@azure-rest//core-client//dist//browser//apiVersionPolicy.d.ts": [ { "component": "@azure-rest/core-client", "file": "package/dist/browser/apiVersionPolicy.d.ts", "file_hash": "6026cf8d3f84e321eaa68250d98048be", "file_url": "<https://api.osskb.org/file_contents/6026cf8d3f84e321eaa68250d98048be>", "id": "file", "latest": "2.2.1-alpha.20240802.1", "licenses": [ ... フォーマット指定 オプション --format (または -f )を付けることで、下記の3パターンで出力できます。 RAW:plain(デフォルト) SPDXLite:spdxlite CycloneDX:cyclonedx CSV: csv 以下はフォーマットとして CycloneDX を指定してスキャンを実行しています。 依存関係のスキャン pip install scancode-toolkit コンポーネント検索 APIキーを指定して、GitHubから vue を含むコンポーネントを検索してみます。 vue として登録されているコンポーネントがPURL(Package URL)、URLとともに出力されました。 APIキーは、 こちら からトライアルキーをリクエストする必要があります。 Dockerイメージによる実行 Dockerイメージをpullします。 docker pull ghcr.io/scanoss/scanoss-py:latest PythonのCLIは Dockerイメージ から公開されており、以下の方法で実行できます。 現在のフォルダをスキャンするには下記のように指定します。 docker run -it -v "$(pwd)":"/scanoss" ghcr.io/scanoss/scanoss-py scan . また、 -o オプションで結果をファイルに出力することもできます。 docker run -it -v "$(pwd)":"/scanoss" ghcr.io/scanoss/scanoss-py scan -o results.json . さらに、依存関係のスキャンをサポートするために、このコンテナには scancode-toolkit が含まれています。 依存関係をスキャンするには以下を実行します。 docker run -it -v "$(pwd)":"/scanoss" ghcr.io/scanoss/scanoss-py scan -D . GitHub Actions scanoss.py をGitHub Actionsで実行してみます。 スキャン対象のリポジトリにワークフローを作成し、生成されたSBOMを確認します。 「Actions」から「set up a workflow yourself」をクリックします。 ファイル名(今回はscan.yml)、下記のワークフローを記載して「Commit changes…」をクリックします。ここではSCANOSSの dockerイメージ から scanoss.py を実行します。 name: SCANOSS CI Scan on: push: branches: [ "master" ] pull_request: branches: [ "master" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Scan run: docker run -v "$(pwd)":"/scanoss" ghcr.io/scanoss/scanoss-py scan --output output.json . - uses: actions/upload-artifact@v3 with: name: output.json path: output.json 今回は master ブランチに追加せず、新しいブランチを作成したいので、「Create a new branch for this commit and start a pull request」を選択して「Propose changes」をクリックします。 (master(main)ブランチに直接追加したい場合は「Commit directly to the master(main) branch」を選択します) 「Create pull request」をクリックします。 Pull Requestが発行されるとGitHub Actionsが実行されるので、実行結果を確認します。「Actions」からワークフロー(今回は Create scanoss.yml )をクリックします。 Actionsの実行結果が表示されます。 ダウンロードボタンをクリックすると、出力結果の output.json をダウンロードできます。 ダウンロードされたZipファイルを解凍し、 output.json を確認すると、下記のようなスキャン結果が出力されていることが分かります。 { "express-app/index.js": [ { "id": "none", "server": { "kb_version": { "daily": "24.09.27", "monthly": "24.08" }, "version": "5.4.8" } } ], "express-app/public/js/app.js": [ { "component": "juanzi", "file": "package/index.js", "file_hash": "4c0e7d3ec9dd03fd7830e56642ab77ff", "file_url": "<https://api.osskb.org/file_contents/4c0e7d3ec9dd03fd7830e56642ab77ff>", "id": "file", "latest": "1.0.0", "licenses": [ { "checklist_url": "<https://www.osadl.org/fileadmin/checklists/unreflicenses/ISC.txt>", "copyleft": "no", "name": "ISC", "osadl_updated": "2024-09-20T09:32:00+0000", "patent_hints": "no", "source": "component_declared", "url": "<https://spdx.org/licenses/ISC.html>" } ], "lines": "all", "matched": "100%", "oss_lines": "all", "purl": [ "pkg:npm/juanzi" ], "release_date": "2019-11-18", ... さいごに 今回はPythonのSDK「scanoss.py」を使ってみたのでインストール方法や機能について書きました。今後も業務で学習した内容をブログとして投稿しようと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post SBOMツール紹介 ~SCANOSS編 ~ ③PythonのSDK「scanoss.py」 first appeared on SIOS Tech. Lab .
アバター
リソースの見方を今一度確認してみよう~CPU使用率~ こんにちは。サイオステクノロジーの橋本です。 今回はリソースの見方 (CPU 使用率) について今一度確認してみましょう 前提 CPU、メモリ、DISK I/O に関わらず、すべてのリソースで言えることですが、 「使用率が高い状況 = 悪」と一概には言えない点に注意してください。 なぜか 使用率が高い状況は反対に言えば、 「限られた資源を無駄なく最大限に活用している」とも言えます。 リソースの見方で「これはよくないよね」と言える状況は主に 2 つです。 1: オーバスペックであり、使用率が非常に低い 例えばメモリを 100 GB も搭載しているのに、 最も利用された時間ですら使用率は 1~2 % でしか推移していない場合は無駄にお金をかけすぎと言えます。 2: リソースの利用待ちが発生している リソースが利用できないほど使用率が高い状況は悪いとも言えます。 ※この場合でもシステム要件や企業文化的に許容されるケースも多々あります。 使用率でのみシステムの状況を判断するのではなく、 システム全体の状況で柔軟に判断いただければと思います。 もう一歩踏み込んで申し上げますと、 システム全体として許容されているレスポンスタイムでリクエストに応答できている限り、 問題は無いと言えるます。 ※この考え方は CPU のみならず、メモリ/SWAP/DISK すべてに共通します。 サーバに搭載されている CPU コア数の確認方法 /proc/cpuinfo を確認いただければ CPU コア数が確認できます。 # grep "processor" /proc/cpuinfo | wc -l 2 # この場合は 2 コアと判断できます。 現在の CPU 使用率の見方 CPU 使用率の見方はメモリや DISK と違い非常にシンプルです。 通常 vmstat コマンドだけで使用率は確認できます。 RHEL 7~9 いずれのバージョンでも以下のような見え方となります。 $ vmstat -t 1 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- -----timestamp----- r b swpd free buff cache si so bi bo in cs us sy id wa st JST 7 0 0 269592 0 409528 0 0 120 82 101 58 2 4 94 0 0 2024-06-04 11:53:22 5 0 0 269472 0 409528 0 0 0 0 2014 201 20 77 0 0 3 2024-06-04 11:53:23 5 0 0 269472 0 409528 0 0 0 0 2008 188 24 75 0 0 2 2024-06-04 11:53:24 5 0 0 269472 0 409528 0 0 0 0 2004 200 22 77 0 0 2 2024-06-04 11:53:25 注意点としては最初の 1 行目 (06/04 11:53:22 の情報) は起動時から現在時刻まで負荷状況を平均した値となります。 vmstat は 2 行目から見てください。 どこを見ればよいのかというとこの部分です。 ------cpu----- us sy id wa st 2 4 94 0 0 20 77 0 0 3 24 75 0 0 2 22 77 0 0 2 us : Kernel 以外のプロセスの実行時間 平たく言うとユーザプロセスの CPU 使用率 sy : Kernel のプロセスの実行時間 平たく言うとシステムの CPU 使用率 id : アイドル時間 平たく言うと「CPU を利用していない待ち時間」となります。 wa : I/O の待ち時間です。 端的に言うと DISK にアクセスしていた時間です。 st : 仮想マシンに盗まれた時間 説明が少し難しいですが、仮想マシンである場合ハイパーバイザ等に起因して生じた値となります。 「id」の値が高ければ CPU 使用率は低いと言えます。 上記例の場合 id は 0 なので CPU 使用率は極めて高いと言えます。 誰が使っているかというと Kernel の使用率が 75~77% ユーザプロセスの使用率が20~24%となります。 重ねてとなりますが「id の値が低い = 悪」とは言えません。 CPU の利用待ちがプロセスが増えていて初めて性能劣化が発生していると言えます。 CPU の利用待ちプロセス数の確認方法 基本的には vmstat の以下項目の内 r 列を確認すれば OK です。 procs r b 7 0 5 0 5 0 5 0 ただ、この項目 RHEL 7 以降だと「実行中または実行待ち」の数を表します。 RHEL 6 だと「実行待ち」の数のみでした。 この点は man vmstat にも記載されています。 ・RHEL6 man vmstat 抜粋 Procs r: The number of processes waiting for run time. b: The number of processes in uninterruptible sleep. ・RHEL7 man vmstat 抜粋 Procs r: The number of runnable processes (running or waiting for run time). b: The number of processes blocked waiting for I/O to complete. 純粋に実行待ちプロセス数を確認するには sar コマンドで確認する必要があります。 sar -q で表示される項目「runq-sz」が実行待ちプロセス数となります。 なお sysstat サービスが起動している必要があります。 # sar -q Linux 3.10.0-1160.80.1.el7.x86_64 (ip-172-31-12-211.ap-northeast-1.compute.internal) 2024年06月04日 _x86_64_ (2 CPU) 15時50分03秒 LINUX RESTART 16時00分01秒 runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15 blocked 16時10分01秒 0 111 0.00 0.01 0.03 0 平均値: 0 111 0.00 0.01 0.03 0 ※ RHEL 7,8,9 いずれも上記のように表示されます。 CPU を利用しているプロセスの特定方法 コマンド「ps auxww –sort=-%cpu」で特定可能です。 このコマンドで以下のように CPU 使用率が高い順で表示してくれます。 $ ps auxww --sort=-%cpu | head -5 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND test 3069 74.3 0.1 217092 824 pts/0 R+ 11:51 4:02 yes test 3128 71.4 0.1 217092 860 pts/1 R+ 11:51 3:28 yes test 3260 65.8 0.1 217092 836 pts/3 R+ 11:52 2:32 yes root 1 0.1 1.1 172320 9592 ? Ss 11:10 0:04 /usr/lib/systemd/systemd --switched-root --system --deserialize 17 $ 過去の CPU 使用率の見方 基本的に過去のリソース使用状況はデフォルトの状態では見ることはできません。 sysstat サービスを有効化し、sar コマンドが利用できる状況にしておく必要があります。 sar コマンドでは「どのプロセスが CPU を利用しているか」まではわからないので、 将来的に発生する可能性がある障害に備えて 5 分間隔で ps auxww コマンドの結果をログに残す等の監視をご検討ください。 CPU 使用率を確認する際に参考になればと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post リソースの見方を今一度確認してみよう~CPU使用率~ first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの服部です。 Graph APIを用いたユーザーのMFAリセットについて調査する機会がありましたので、簡単にまとめていきます。 ユーザーのMFAリセット ユーザーのMFAリセットは、管理者がAzure Portal上から実施することが可能です。 外部システムと連携してMFAリセットを行いたいとの要件があった場合、APIからこの操作を実施する必要があります。 日本語資料ではPowershellのMSOlineモジュールを使用した方法が見つかりますが、このモジュールは非推奨のため、Graph APIでリセットを行う必要があります。 MSGraphのgithubリポジトリにてGraph APIを用いたMFAリセットのスクリプトが公開されていたため、こちらを試してみました。 https://github.com/orgs/msgraph/discussions/55 検証 パスワード以外に4つの認証方法を追加したユーザーを用意しました。 以下のスクリプトをPowershellにて実行します。実行に必要なスコープは UserAuthenticationMethod.ReadWrite.All となります。 ※実行前に、Graph Powershellモジュールのインストールが必要です。 Import-Module Microsoft.Graph.Identity.SignIns Connect-MgGraph -Scopes UserAuthenticationMethod.ReadWrite.All $userId = "テストユーザーのUPN" <上記githubページのスクリプト 2 行目移行( function DeleteAuthMethod ~以降)> 実行中に以下のエラーが表示される場合がありますが、デフォルトに指定されている認証方法を削除した際に発生するエラーになります。 ※デフォルトの認証方法のみ登録されている場合は削除できます。 デフォルトの認証方法はAPIから取得できないため、スクリプトではエラーが発生した認証方法を最後に再度削除しています。 スクリプトの実行後テストユーザーの認証方法を確認すると、多要素認証の登録がリセットされていることが確認できました。 まとめ Graph APIを用いてMFAリセットが可能であることが確認できました。 皆様のご参考になれば幸いです。 参考 多要素認証 (MFA) のリセット手順 2022 年版 Delete all authentication methods for a user ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Graph APIでMFAリセットを実施する first appeared on SIOS Tech. Lab .
アバター
はじめに ども!時には立ちながらブログを執筆する龍ちゃんです。最近、自分のブログの分析を始めたのですが、読まれているブログと読まれていないブログの差が激しくてげんなりしています。全部自分で執筆しているので、自分の実力不足なんですけどねw さて!今回はPythonでお手軽にGUIが作れる「Streamlit」のお話です。備忘録なので、メモ代わりにソースを乗っけておきますね。 こちらの内容は 「Qumcum×生成」:ロボットで試すAIエージェント で使用している内容になります。 2024-10-03 「Qumcum×生成AI」ロボットで試すAIエージェント:Python Streamlitでマルチページを作る二つの方法 今回は、公式の情報でまとめらている二つの方法について紹介していきます。難易度的には「とても簡単」と「ちょいムズ」の方法に分けることができます。 公式のこちらのページ でまとまっています。 どちらも簡単に試すことができるので、サンプルを使用して試してみてください。 とても簡単:ディレクトリ構造で配置 こちらは、自動でルーティング定義をしてくれます。ディレクトリとして pages を用意すると内部にあるファイルが自動的に子ページとして定義されます。 src ├ pages # 子ページのファイルを格納する │ └ about.py └ main.py # topページの役割も果たす 以下がファイルの内容です。 main.py import streamlit as st st.set_page_config(page_title="top page", page_icon="") st.title("top page") about.py import streamlit as st # タブ名に表示される st.set_page_config(page_title="About", page_icon="📈") st.title("About") 起動方法としては、以下のコマンドです。 streamlit run main.py 起動の起点にしたファイルがトップページとして表示されます。 ちょいムズ:コード制御で配置 こちらは自前でルーティング定義を作成する方法になります。好きなようにディレクトリ名を決定することができます。ディレクトリ構造で設定するよりも状態によって特定のページをサイドバーに表示・非表示を動的に制御することができます。 src ├ contents # 好きな名前で決定することができる │ ├ about_page.py │ └ top_page.py └ main.py # こちらでページ構成を作成する 以下がファイルの内容です。 main.py import streamlit as st def main(): top_page = st.Page( page="contents/top_page.py", title="Top", icon=":material/home:", default=True ) about = st.Page( page="contents/about_page.py", title="About", icon=":material/apps:" ) pg = st.navigation([top_page, about]) pg.run() if __name__ == "__main__": main() streamlit.navigation と streamlit.Page を使用して制御を行います。順序としては、ページを定義してナビゲーションで配置します。直感的理解がしやすいので助かります。 今回のデモでは単純な内容ですが、もっと突っ込んで定義するとサイドバーのデザイン性を向上させることもできます。細やかな制御方法については公式の関数情報を参照して確認したほうが良いです。 公式リファレンス情報 st.navigation : ページ遷移やnavigation情報などを制御する関数 st.Page : ページを定義するための関数 top_page.py import streamlit as st st.title("top page") about_page.py import streamlit as st st.title("About Page") 起動方法としては、以下のコマンドです。 streamlit run main.py streamlit.Page でページの設定をした際に default フラグを付けることで、トップページとして表示することができます。 細やかに制御をしたい場合は、自作で定義をする方が良いです。 二つの方法を併用すること 結論から書くと、二つの方法を併用することができません。併用しようとすると以下の警告が出力されます。 st.navigation was called in an app with a pages/ directory. This may cause unusual app behavior. You may want to rename the pages/ directory. そのため、どちらか一方の方法を採用するようにしましょう。 navigation を使用した場合は、pagesディレクトリに関しては無視されるので気にせず実装しても警告が出るだけなのであんまり気にしないで大丈夫です。 終わり お疲れ様でした。streamlitの場合は、簡単におしゃれなページを実装することができます。ただ、streamlitの定義済みから外れたことをしようとすると難易度が爆上がりするのが難しいところです。 定義済み関数のみで実装することができる場合は、爆速で構築ができるのでお勧めです。プロトタイプには適しているのでお勧めです~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Streamlitでマルチページを作る2つの方法 first appeared on SIOS Tech. Lab .
アバター
はじめに こんにちは!SBOMツールを調査中のなーがです。前回に引き続きSCANOSSについてみていきます。今回は検証用GUIツールの「SBOM Workbench」を使ってみたのでインストール方法や機能について書きます。 SCANOSSについて 検証用GUIツール「SBOM Workbench」 ← 今回 PythonのSDK「scanoss.py」 SBOM Workbenchについて SBOM WorkbenchはSCANOSS API を使用してソース コードをスキャンおよび監査するためのGUIツールです。 ソース コード ディレクトリをスキャンするだけで、オープンソースコンポーネントを見つけて識別し、ボタンを押すだけでSBOM を生成できます。 インストール 下記のURLからインストーラーをダウンロードします。Linux、MacOS 、Windowsから選択できます。今回はWindows版を選択しました。 https://www.scanoss.com/product ダウンロードされたZIPファイルを解凍して、インストーラーをダブルクリックします。 Windows のDefender SmartScreenによりブロックされた場合は、「詳細情報」をクリックし、「実行」をクリックします。 インストールが完了すると、アプリケーションが起動します。 日本語化 「File」から「Settings」をクリックします。 「Language」で「日本語」をクリックします。 「Save」をクリックします。 ポップアップが表示されるので、「すぐに再起動」をクリックします。 アプリケーションが起動すると日本語になります。 コードのスキャン 「新しいプロジェクト」または「scanning a new project」をクリックします。 スキャン対象とするコードのディレクトリを選択して「フォルダの選択」をクリックします。 「続ける」をクリックします。 スキャンが開始され、依存関係や脆弱性が進捗状況が表示されます。 スキャンが完了すると、以下のようなダッシュボードが表示されます。日本語設定にしていますが、一部英語のままになっている箇所もあります。 エクスポート 「エクスポート」をクリックします。 現在では、6種類のフォーマットで出力が出来ます。 「CycloneDX」形式で出力してみます。任意の場所を選択して「保存」をクリックします。 CycloneDX形式で出力されました。 検出されたコンポーネントの確認 「検出されたコンポーネント」をクリックします。 ここでは、どのファイルでコンポーネントが使用されているかを確認できます。 検出されたコンポーネントの「承認」をクリックするとポップアップが表示されます。 検出結果が間違っている場合は、変更出来ます。 「承認」をクリックします。 承認したコンポーネントの表示が「識別済み」に変更されました。 右上でプロジェクトで承認された依存関係の割合が確認できます。 レポートの確定されたコンポーネントで下記のように表示されるようになりました。 キーワード検索 キーワード検索では、入力した文字を含む依存関係を検索できます。 脆弱性の確認 「レポート」の脆弱性をクリックすると、検出された脆弱性が確認できます。 深刻度やソースが表示され、「CVE」のリンクから参照している脆弱性データベースにアクセスできます。 ホーム画面 左上の家アイコンをクリックするとプロジェクトの一覧が表示されます。 ここでは「プロジェクトのエクスポート」、「再スキャン」、「プロジェクト削除」を行うことが出来ます。 さいごに 今回は検証用GUIツールの「SBOM Workbench」を使ってみたのでインストール方法や機能について書きました。次回はSCANOSSのPython SDK「scanoss.py」を使ってみたことについて書く予定です。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post SBOMツール紹介 ~SCANOSS編 ~ ②検証用GUIツール「SBOM Workbench」 first appeared on SIOS Tech. Lab .
アバター
はじめに ども!9月も終わり今年度の半分が終了しそうで、ひやひやしている龍ちゃんです。9月はデモを大量に作成していた月でした。内容として新しいことに結構取り組んでいたので、10月はブログで報告を重ねていきたいと思います。最近はおしゃれなサムネイルを目指して作成しています。 内容としては、「OSC 2024広島」で展示した内容となっています。プロトタイプ第一号だったので、会場で不思議な挙動をしていたのですがバージョンアップの内容を詰めるいい機会だと思って大量に改修案が出てましたw それでは中身に入っていこうと思います。 コンセプト コンセプトとしては「ロボット×生成AI」というテーマがスタートになります。生成AIの出力のカタチとして「画像」や「テキスト」が真っ先に挙がってくるかと思います。出力の表現としてロボットを置くことで表現の幅が広がりました。 今回は、チャットボットなどの表現から、一歩進んだ「エージェント」という考え方をロボットで如何に表現するのかという挑戦でもありました。その辺をさっくりと解説してみますね。 AIエージェントとは ここでは、今回のデモで根幹にかかわる「AIエージェント」という考え方について解説しておきます。チャットボットと比較した図を作ったので記載しておきます。 チャットボットは、ユーザーの入力から返答を生成します。返答作成にはLLM(大規模言語モデル)を使用しています。 LLMは与えられた文章から、最も可能性の高い続きの文章を生成している。 課題としては、LLM内に無い情報には対応することができません。LLM内に含まれない最新の情報などは解答することができません。LLMに追加の情報を与える手法としては、RAG( Retrieval-augmented generation )などがあります。こちらは、弊社のエンジニアがしっかりガイドまで作っているので こちらを参照してみてください 。 エージェントでは、自作の動きをLLMにツールとして渡すことができます。もしここに情報を取得するツールを渡していれば、最新の情報を取得するエージェントになります。エージェントでは、事前にAIができることを知っているので、対応できない問題が来ても、対応するツールがあれば、そちらを用いて処理を実行します。 もっと具体的な話をすると、チャットボットではロボットの操作を行うことはできません。 OpenAIに聞いてみた 「左を向いて、一歩歩いて、万歳して」という動作指示に従うと、次のような動きになります。 左を向く:体を左方向に90度回転させる。 一歩歩く:一歩前進する。左を向いているため、左に向かって一歩進むことになる。 万歳する:両手を頭の上に挙げ、喜びや祝福のポーズを取る。 この一連の動作は、リズムをつけて楽しく行うことができそうですね! エージェントではロボットの動作をツールとして渡してあげることでロボットの操作を可能にします。 このことを踏まえて作成したものについて解説をしていきたいと思います。 設計 詳細な実装については、長くなってしまうので別記事で紹介していきます。今回使用したQumcumロボットの制約に併せて、細やかなコードと時には力技で解決したので、結構重厚な内容になりましたね。 Qumcum さて、ロボットと話をしていましたが、今回使用したロボットの情報を書いておこうと思います。 公式のサイトはこちら になります。 バージョンも3つほどあって拡張性もしっかりあるので、結構楽しめる高級なおもちゃですね。小さいころに持っていたら、もっと早くプログラミングに興味を持っていたかなって思います。 こちらの挙動に関しては、また別のブログでソースコードと一緒に乗せておこうと思います。 エージェント エージェントの実装には以下のユースケースのような構成になっています。 使用技術としては、以下のようになっています。 技術 説明 Streamlit フロント画面:PythonでリッチなGUIを作成することができる。 LangChain AI周りの処理:エージェントの実装はライブラリのおかげでとてもすっきりしています。 Azure OpenAI Service AIサービス:Azure上でお金はかかるけど一番慣れているので… Qumcum API PythonからQumcumを操作するためのライブラリ ライブラリを使用することでさっくりと実装できました。 実装 ソースコードを乗っけると膨大になるので、分割しておきます。ここでは、処理の流れと実際の画面を載せておきます。構築のための技術はシリーズでブログとしてまとめていきます。 処理の流れ 処理としてはとても単純です。 ユーザーによるチャット LangChainを用いてAI agentにアクセス QumcumAPIを経由してQumcumに処理を送信 フロント画面 画面としては、シンプルなチャット画面になっています。実装に関しては、 こちらのブログでしっかりと解説 をしているので気になる方はコピペして試してみてください。 解説はしないですが、以下のコード量ぐらいでチャットが実装できるのは最高に素晴らしいです。 # chatbot.py import streamlit as st from langchain.schema import HumanMessage, AIMessage import utils.aiagent_emotions as agent # ページの設定 st.set_page_config(page_title="感情によって返答する", page_icon="😆") st.header("感情によって返答する") # チャット履歴の初期化 if "messages" not in st.session_state: st.session_state.messages = [] # ユーザーの入力を監視 if user_input := st.chat_input("聞きたいことを入力してね!"): st.session_state.messages.append(HumanMessage(content=user_input)) with st.spinner("GPT is typing ..."): response = agent.agent_executor.invoke( {"input": user_input, "chat_history": []} ) output = response["output"] st.session_state.messages.append(AIMessage(content=output)) # チャット履歴の表示 messages = st.session_state.get("messages", []) for message in messages: if isinstance(message, AIMessage): with st.chat_message("assistant"): st.markdown(message.content) elif isinstance(message, HumanMessage): with st.chat_message("user"): st.markdown(message.content) デモ 今回作成した内容のデモ動画になります。 実装関連ブログ 執筆が完了次第追記していきます! おわり いや~これで9月のすべてのお仕事が終わりました。ちょっと足が出てしまいましたが… 9月は、別件でもデモを作っていたので最高に忙しかったです。あまりブログを生成できませんでしたが、同僚の皆様がゴリゴリと執筆されているので負けないように頑張って執筆していかないといけませんね。 今年度も10月で折り返しなので、今年度の積み残しが出ないように頑張っていきましょう! 次はStreamlit周りかフロント周りの記事を書こうかと思います。ではまた~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 「Qumcum×生成AI」ロボットで試すAIエージェント:Python first appeared on SIOS Tech. Lab .
アバター
  こんにちは、サイオステクノロジーの佐藤陽です。 今回は前回に引き続き、Azure AI Searchのインデクシングに関して記事を書いていこうと思います。 前回 は基本的なインデクシングの部分に触れたので、今回は肝となるベクトル値のインデクシング部分を試してみたいと思います。 ベクトル値を扱うことで、ぐぐっと検索性能が高まるので、是非マスターしましょう。 はじめに 前回の記事ではAzure AI Searchにとりあえずインデックスを定義し、テキストデータをインデクシングする流れを紹介しました。 ただ、やはりAI Searchといえばベクトル値を扱えることが大きな強みです。 そこで今回は実際に格納するデータをベクトル化し、それらの値をインデクシングする流れをご紹介します。 全体の流れ ベクトル値をAI Searchに登録していくためには以下の3つのステップがあります。 AI Searchにベクトル値を格納するためのスキーマを定義する 値をベクトル化する インデクシングを行う このブログでも、この3つのステップに沿って解説していきたいと思います。 値のベクトル化 まず値のベクトル化とは何か簡単に説明します。 ベクトルという言葉自体は高校数学で出てくる単語であり 、「 向きと大きさを持つ 」という言葉はよく覚えているのではないでしょうか? よく見るのが下図のようなX軸、Y軸があり、矢印が伸びてるやつですね。 今回の「値のベクトル化」というのもイメージは同じです。 例えば「犬」という言葉も、実はこのようなベクトルとして表すことが可能です。 ただし、X軸,Y軸といった2次元ではなく、1000を超えるような多次元での表記になります。 そしてこのベクトル化を実現するのが、OpenAI社等が提供しているembeddingsモデルになります。 例えばOpenAI社からは text-embedding-3-small といったモデルが提供されており、これはAzure OpenAI Serviceからでも利用可能になります。 今回はAzure OpenAI Service上に text-embedding-3-small モデルをデプロイして、実際にベクトル化を行ってみたいと思います。 モデルをデプロイし、以下のような形でAPIを実行します。 POST /openai/deployments/{{deployment name}}/embeddings?api-version=2024-02-01 HTTP/1.1 Host:  {{AOAI HostName}} Content-Type: application/json api-key: {{api-key}} Content-Length: 29 {     "input": "犬" } 以下がレスポンスとなり、 data/embedding の値が「犬」という言葉をベクトル化した実際の値になります。 ※1536個の配列で表現されているため、一部省略しています。 {   "object": "list",   "data": [     {       "object": "embedding",       "index": 0,       "embedding": [ //1536個の配列         -0.019703003,         -0.014401983,         -0.011315843,         0.0118301995,         0.0028840704,         -0.010234645,         (略)         -0.0044927467       ]     }   ],   "model": "text-embedding-3-small",   "usage": {     "prompt_tokens": 3,     "total_tokens": 3   } ベクトル化をすると何が嬉しいか? では言葉をベクトル化すると何が嬉しいのでしょうか? 大きなメリットとして、「機械が自然言語を扱いやすくなる」ということが挙げられます。 そして、このベクトル化を行うことでベクトル検索やハイブリット検索などが可能になり RAGにおけるRetrieveの処理が、高い精度で実現できるようになります。 ただし今回はインデクシングにスポットを当てた記事であるため、検索手法に関しては深くは触れません。 気になる方は、弊社のMVPである武井が分かりみ深く解説しているので、こちらの記事を是非参照してください! 生成AI時代の様々な検索手法を検証する 〜Azure AI Searchによるベクトル/セマンティック/ハイブリッド検索〜 インデクシング では、言葉のベクトル化が実現できたところで、次はこの値をAzure AI Searchに対してインデクシングしていきます。 大きな流れとしては、 前回 の記事で紹介した時と同じです。 まずはベクトル値を格納できるようなスキーマを定義し、インデックスを作成します。 スキーマ定義 今回は、前回の記事で作成したインデックスをベースとして、ここにベクトル値を扱うためのスキーマを追加します。 ちなみに前回は以下のようなデータを扱っていました。   {     "UserId": "1001",     "UserName": "田中 一郎",     "Profile": "東京都出身で大学卒業後に大手SIerに就職。Azureを使ったアプリケーションの開発に携わり、Azure Developer Associateの資格も取得している。趣味は本を読むことで、無類のミステリー好き",     "Age": 36,     "Tags": ["Engineer", "Azure"]   }, 今回は、この Profile の値をベクトル化し、インデクシングすることを想定します。 インデックスを作成するため、以下の内容でAPIを実行します。 前回の記事の違いとしては以下2点が挙げられます。 fieldsにおける ProfileVector の追加 vectorSearch パラメータの追加 POST /indexes?api-version=2024-07-01 HTTP/1.1 Host: {{AI Search}}.search.windows.net Content-Type: application/json api-key: {{AI Search API KEY}} Content-Length: 1090 {     "name": "idx-users",     "fields": [         {             "name": "UserId",             "type": "Edm.String",             "key": true,             "filterable": true         },         {             "name": "UserName",             "type": "Edm.String",             "searchable": true,             "filterable": true,             "facetable": false         },         {             "name": "Profile",             "type": "Edm.String",             "searchable": true,             "filterable": false,             "sortable": false,             "facetable": false,             "analyzer": "ja.lucene"         },         {             "name": "ProfileVector",             "type": "Collection(Edm.Single)",             "searchable": true,             "retrievable": true,             "dimensions": 1536,             "vectorSearchProfile": "my-vector-profile"         },         {             "name": "Age",             "type": "Edm.Int32",             "searchable": false,             "filterable": true,             "sortable": true,             "facetable": true         },         {             "name": "Tags",             "type": "Collection(Edm.String)",             "searchable": false,             "filterable": true,             "sortable": false,             "facetable": true         }     ],     "vectorSearch": {         "algorithms": [             {                 "name": "hnsw-1",                 "kind": "hnsw",                 "hnswParameters": {                     "m": 4,                     "efConstruction": 400,                     "efSearch": 500,                     "metric": "cosine"                 }             }         ],         "profiles": [             {                 "name": "my-vector-profile",                 "algorithm": "hnsw-1"             }         ]     } } Fielidの新規追加 ベクトル値を格納するため、以下のフィールドを追加しました。 各値を確認していきます。 {     "name": "ProfileVector", //ベクトル値を格納するためのフィールドを定義     "type": "Collection(Edm.Single)", //ベクトル値を格納する際はCollection(Edm.Single)というtypeを利用     "searchable": true,     "retrievable": false,     "stored": false,     "dimensions": 1536, //次元数     "vectorSearchProfile": "my-vector-profile" } type 今回ベクトル値を格納するFieldとして、 Collection(Edm.Single) というtypeを利用します。 理由としてはEmbeddingsモデルとしてtext-embedding-3-smallを利用しており、このモデルがfloat32の型で出力するためです。 利用するモデルに合わせて適宜使い分けてください。 参考: ベクター フィールドの EDM データ型 dimensions dimensions はベクトルの次元数を表します。 記事冒頭で説明したX軸,Y軸のベクトルは2軸(dimensions=2)ですが、今回は1536軸という多次元のベクトルで自然言語を表します。 この数値に関してですが、これも type と同様に、利用するモデルに依存します。 今回は text-embedding-3-small を利用しており、このモデルの次元数を1536次元としているためこの値となります vectorSearchProfile vectorSearchProfile に関しては、ベクトル検索時の設定を行います。 また、この値はfieldのセクションの下に書かれている、 vectorSearch のセクションの値を参照しています。 "vectorSearch": { //検索用の設定     "algorithms": [         {             "name": "hsnw-1",             "kind": "hnsw",             "hnswParameters": {                 "m": 4,                 "efConstruction": 400,                 "efSearch": 500,                 "metric": "cosine"             }         }     ],     "profiles": [         {             "name": "my-vector-profile", //fieldsの中で、こちらを参照             "algorithm": "hnsw-1" //上の"algorithms"のセクションを参照         }     ] } ベクトル検索手法(余談) ここで少しだけ、ベクトル検索の手法について言及してみたいと思います。 ただし、このあたりを詳細に述べると、それはそれで記事が何本か書けそうなのと 自分もまだ十分に理解できていない部分があるので、参考程度にして頂けると幸いです。 誤った記述がありましたら、是非コメントなどでご指摘お願いします! ベクトル検索においては、「検索クエリのベクトル値」に最も近い「インデクシングされているデータのベクトル値」を結果として返します。 そしてこの手法は 最近傍探索 (Nearest Neighbor Search:NN)と呼ばれます。 最近傍探索は、AI Searchに保存されている全てのデータに対して網羅的に検索を行い、最もベクトル値の値が近しいものを抽出します。 しかし、分析するデータ量が膨大である場合、それに伴い検索時間も長くなります。 一方で、近似最近傍探索(ANN)というものが考案されました。 ANNもベクトル値の値で比較を行う事には変わりないのですが、必ずしも一番近いものとは限らないポイントをデータセットから抽出します。 精度としては最近傍探索には劣りますが、少ない検索時間で、それなりに実用的なデータを検索することが可能となります。 Azure AI SearchではこのANNアルゴリズムにHNSWが使われています。 また、このANNとNNの中間的に存在するk近傍法(kNN)という方法もあり、高速に結果を出しながらも高い正確性を保つとされています。 ただし、パラメータである k の値を正しく決定することが難しく、扱いが難しいとされているようです。 なお、kNNに関してもAzure AI Searchにてサポートされています。 参考: 近似最近傍探索(ANN)アルゴリズムを理解する 参考: 最近傍検索 今回の記事においては、HNSWを利用してベクトル検索を行います。 インデクシング スキーマの定義が行えたので、ここから実際にデータをインデクシングしていきます。 まずはインデクシングする値のベクトル化を行います。 今回はProfileの値をベクトル値として登録するため、 { "UserId": "1001", "UserName": "田中 一郎", "Profile": "東京都出身で大学卒業後に大手SIerに就職。Azureを使ったアプリケーションの開発に携わり、Azure Developer Associateの資格も取得している。趣味は本を読むことで、無類のミステリー好き", "Age": 36, "Tags": ["Engineer", "Azure"] } このデータのProfileの値を、Azure OpenAI Serviceのembeddingsモデルを使ってベクトル化します。 流れとしては、冒頭で述べた「犬」をベクトル化した時と同じです。 POST /openai/deployments/{{deployment name}}/embeddings?api-version=2024-02-01 HTTP/1.1 Host:  {{AOAI HostName}} Content-Type: application/json api-key: {{api-key}} Content-Length: 125 {     "input": "東京都出身で大学卒業後に大手SIerに就職。Azureを使ったアプリケーションの開発に携わり、Azure Developer Associateの資格も取得している。趣味は本を読むことで、無類のミステリー好き" } 返ってきた値がこちらです。 {   "object": "list",   "data": [     {       "object": "embedding",       "index": 0,       "embedding": [         -0.016684273,         -0.034290213,         (略)         0.07658217,         -0.03793499,         0.0010185471,         0.018276243       ]     }   ],   "model": "text-embedding-3-small",   "usage": {     "prompt_tokens": 89,     "total_tokens": 89   } } このembeddingの値を利用して、実際にインデクシングを行っていきます。 前回の記事で扱ったデータ全ての対応は大変だったので、今回は2名分だけ…。 POST /indexes/idx-users/docs/index?api-version=2024-07-01 HTTP/1.1 Host:  {{AISearch HostName}} Content-Type: application/json api-key: {{api-key}} Content-Length: 716 {     "value": [         {             "@search.action": "upload",             "UserId": "1001",             "UserName": "田中 一郎",             "Profile": "東京都出身で大学卒業後に大手SIerに就職。Azureを使ったアプリケーションの開発に携わり、Azure Developer Associateの資格も取得している。趣味は本を読むことで、無類のミステリー好き",             "ProfileVector": [                 -0.016684273,                 -0.034290213,                 (略)                 -0.003558369,                 -0.04298321,                 -0.014977094,                 -0.018883705,                 0.017176528,                 0.018276243             ],             "Age": 36,             "Tags": [                 "Engineer",                 "Azure"             ]         },         {             "@search.action": "upload",             "UserId": "1004",             "UserName": "山田 真美",             "Profile": "福岡県出身。大学卒業後、プロジェクトマネージャーとして多くのプロジェクトを成功に導く。現在はIT企業でPMとして活躍。趣味は旅行で、特に海外旅行が好き",             "ProfileVector": [                 -0.016684273,                 -0.034290213,                 (略)                 -0.003558369,                 -0.04298321,                 -0.014977094,                 -0.018883705,                 0.017176528,                 0.018276243             ],             "Age": 33,             "Tags": [                 "Project Manager",                 "IT"             ]         },     ] } これでインデクシングの方は完了です。 検索 せっかくなので試しに検索してみます。 検索を行う場合は、 /indexes/idx-users/docs/search のパスに対してPOSTのリクエストを発行します。 ベクトル検索 まずはベクトル検索を試します。 ベクトル検索においては、 検索クエリのベクトル値 と、 格納されてるデータのベクトル値 を比較するため、 検索クエリの文章についてもベクトル化する必要があります。 今回は以下のような検索クエリを想定し、この値をベクトル化します。 「推理小説が好きなのは誰ですか?」 これは、田中さんのProfleに含まれる 「ミステリー好き」 にヒットさせることが目的です。 キーワード検索だと直接的なワードが入っていなければヒットしませんが、 ベクトル検索であればベクトル値として似ている単語でヒットするはずなので、 検索結果に引っかかってくるはずです。 ベクトル化の手順としては先ほどと同様なので省略します。 ベクトル化した値を vectorQueries/vector の値に入れ、検索を行います。 POST /indexes/idx-users/docs/search?api-version=2024-07-01 HTTP/1.1 Host:  {{AISearch HostName}} Content-Type: application/json api-key: {{api-key}} Content-Length: 481 {     "count": true,     "select": "UserName",     "vectorQueries": [         {             "vector": [                 0.0067357426,                 -0.02722012,                 -0.04022639,                 0.004409659,                 (略)                 -0.0018893238,                 0.01361006,                 -0.013748635             ],             "k": 3,             "fields": "ProfileVector",             "kind": "vector",             "exhaustive": true         }     ] } すると、以下の結果が返ってきました。 {     "@odata.context": "https://{{resourceName}}.search.windows.net/indexes('idx-users')/$metadata#docs(*)",     "@odata.count": 2,     "value": [         {             "@search.score": 0.60031176,             "UserName": "田中 一郎",             "Profile": "東京都出身で大学卒業後に大手SIerに就職。Azureを使ったアプリケーションの開発に携わり、Azure Developer Associateの資格も取得している。趣味は本を読むことで、無類のミステリー好き"         },         {             "@search.score": 0.5653324,             "UserName": "山田 真美",             "Profile": "福岡県出身。大学卒業後、プロジェクトマネージャーとして多くのプロジェクトを成功に導く。現在はIT企業でPMとして活躍。趣味は旅行で、特に海外旅行が好き"         }     ] } 予想通り田中さんも引っかかってくれたのですが、同じように山田さんも検索に引っかかりました。 しかも山田さんも割とスコアが高いですね…、もう少し差が出るかと思ったのですが。 (ちなみに @search.score は検索スコアと呼ばれる値であり、高ければ高いほど相関が高い事を示しています。) 以下のように検索クエリの内容をいくつか変えてみたのですが、あまりスコア差は変わりませんでした。 推理小説(単語だけにしてみる) 密室で事件が起こるような本を読むのが好きな人は誰?(推理小説の言い換え) 首都(東京をターゲットにしてみる) Trip(山田さんの”海外旅行”をターゲットにしてみる) 何かしら差別化するためのテクニックがあるのかもしれません。 あとはインデックスに登録したvectorSearchのパラメータであるの hnswParameters の設定などにも依存しそうです。 ただ今回の記事としては、このあたりの精度向上はスコープ外とさせていただきたいと思います。 全文検索 また、比較対象として全文検索の方も行っておきたいと思います。 ベクトル値は入れずに、 search の中に検索クエリを入れます。 POST /indexes/idx-users/docs/search?api-version=2024-07-01 HTTP/1.1 Host:  {{AISearch HostName}} Content-Type: application/json api-key: {{api-key}} Content-Length: 125 {     "search": "推理小説が好きなのは誰ですか?",     "select": "UserName, Profile",     "searchFields": "Profile",     "count": true } レスポンスとしては以下の内容が返ってきました。 Profile の中に「推理小説」というワードは含まれていませんが、全文検索としても一応回答が返ってきました。 しかしベクトル検索に対して、スコアが著しく低いことが分かります。 {     "@odata.context": "https://{{resourceName}}.search.windows.net/indexes('idx-users')/$metadata#docs(*)",     "@odata.count": 2,     "value": [         {             "@search.score": 0.1678722,             "UserName": "田中 一郎",             "Profile": "東京都出身で大学卒業後に大手SIerに就職。Azureを使ったアプリケーションの開発に携わり、Azure Developer Associateの資格も取得している。趣味は本を読むことで、無類のミステリー好き"         },         {             "@search.score": 0.1678722,             "UserName": "山田 真美",             "Profile": "福岡県出身。大学卒業後、プロジェクトマネージャーとして多くのプロジェクトを成功に導く。現在はIT企業でPMとして活躍。趣味は旅行で、特に海外旅行が好き"         }     ] } ここで、検索クエリを「推理小説」だけにして検索すると、以下のように帰ってきました。 {     "@odata.context": "https://ais-aksato.search.windows.net/indexes('idx-users')/$metadata#docs(*)",     "@odata.count": 0,     "value": [] } 先程検索結果が返ってきたのは、恐らく検索クエリが形態素解析され「好き」の部分が全文検索で引っかかったためだと思われます。 実際、検索クエリを「好き」とだけして検索をかけると、0.1678722というスコアが返ってきました。 以上のことから、ベクトル検索を用いることで インデクシングされてるデータに直接含まれていないワードでも検索できる ことが分かりました。 終わり 今回は、AI Searchの肝となるベクトル検索を試すため 値のベクトル化 ベクトル値を格納するためのインデックス作成 ベクトル値のインデクシング を試してみました。 AI Searchのデータインポート機能を使えば、ここのあたりもいい感じにやってくれるのですが このあたりの流れを理解しておくと、チューニングやトラブルシューティングに役立つかと思います。 チューニングについてはまだまだ奥が深そうなので、機会があれば取り組んでみたいと思います。 是非RAGの構築にあたりAI Searchを使いこなしていきましょう! ではまた! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure AI Searchにおけるインデクシング入門ガイド ~ベクトル値のインデクシング~ first appeared on SIOS Tech. Lab .
アバター
はじめに こんにちは!先月は生成AI活用事業がメイン業務だったなーがです。9月はインターンやセミナー、初出張などイベントが盛り沢山でした。前回から少し時間が空いてしまいましたが、久しぶりにSBOMについて書こうと思います。今回はSCANOSSについてみていきます。 SCANOSSについて ← 今回 検証用GUIツール「SBOM Workbench」 Pythonのライブラリ「scanoss.py」 SCANOSSについて SCANOSSは2020年に設立されたスペインに本社を置くソフトウェア企業で、今回調査したSBOMツール「SCANOSS」を開発している企業になります。メンバーとしてはOSSライセンス&セキュリティ管理ツールとして有名な Black Duck の元CROや FOSSID の共同創業者を始めとした10年以上のSCA経験を持つエンジニアのグループとなっています。 SCANOSSを開発するきっかけとしては従来のOSSコンプライアンスソリューション(SCAツール)に不満を抱いており、以下のように述べられています。 SCA(Software composition analysis)市場は一握りの商用ソフトウェアベンダーによって20年近く支配されていますが、独自のフリー&オープンソースと「インテリジェンス」ビジネスモデルで現状に挑戦しています。 この現状を打開し、SCAのコモディティ化と標準化を実現するためのオープン化の戦略としては下記の3つが挙げられています。 Open Software Open Data/Intelligence Open Standards このようなSCAのオープンソース化に向けたプロジェクトはSCANOSSだけでなく、SCAの標準化はデファクトスタンダードになりつつあるようです。 ツールの概要 以下のように述べられています。 開発者がコードを書き始めた瞬間からコンプライアンスに準拠したコードを作成できるようにすると同時に、より広範なDevOpsチームやサプライチェーンパートナーに対して、より高いライセンスと使用状況の可視性を提供することを目指します。 リポジトリ 下記に各ツールのリポジトリがあります。 https://github.com/scanoss 他ツールとの差別化 他のツールとの差別化としては以下のように述べられています。 メリット コスト ベンダー製品に比べて低コスト 柔軟性 監査人ではなく、DevSecOpsのために作られており、CI/CDの統合を想定 ベンダーロックインにならない 適応性 言語にとらわれない(特にC/C++) 「宣言された」依存関係だけでなく、最も完全なSBOMを作成 透明性 ライセンスや脆弱性だけでなく、360°のOSSリスク「インテリジェンス」を提供 100%フリー&オープンソース(FOSS) デメリット OSPO(Open Source Program Office)、法務がベンダー選定を主導 現状ではSCAベンダーと比較した場合に以下の機能が無い エンタープライズ対応のGUI ポリシーマネージャ ワークフロー リアルタイムの脆弱性アラート(および修復) 「宣言された」の定義は以下の通りです。 宣言された ソフトウェアのメタデータ 著作権声明 ライセンスファイルとヘッダー 宣言された依存関係ファイル パッケージマニフェスト、ログなど 宣言されていない ライセンスヘッダのないファイル 組み込みの依存関係 取り除かれたヘッダ 盗作されたコード AIが生成したOSSを含むコード スニペット 料金 専用SaaSまたはオンプレミスで使用する場合は、有料プランを選択する必要があるようです。 https://www.scanoss.com/pricing 主な有料オプションは以下の通りです。 無制限のリポジトリ 無制限のユーザー/開発者 無制限のサーバー(オンプレミス) 可用性の保証 スループットの保証 機能比較 ツールの 機能比較 は以下のようになっています。(完全な比較表を日本語訳したものです) 機能 無料 有料 説明 SBOM生成 検出 スニペット検出 〇 〇 変更されたOSSファイル、OSSファイルの断片、Stackoverflowからのスニペット、または既知のオープンソースを含むAIによって生成されたコードの識別を可能にします。 高精度スニペットマッチング(HPSM) × 〇 より精度の高いスニペット検出を可能にし、ノイズを除外する。 言語に依存しない 〇 〇 特定のプログラミング言語で書かれたコードを検出するための制限はありません。 基本的な依存性の検出 〇 〇 コード内にある依存ファイルやパッケージマネージャのファイルから依存関係を検出する。 拡張依存性の検出 × 〇 各リポジトリに記載されているNレベルの依存関係ツリーを提供する。 宣言していないコンポーネントの検出 〇 〇 これはファイルやスニペットの検出であり、単なる既知の宣言された依存関係の特定から検出を拡張するものである。 剽窃の検証やAIコーディングのコンプライアンスを可能にします。 修正バイナリ検出 × 〇 完全なオープンソースでなくても、バイナリ内のオープンソースを検出できるようにする。 ファイル全体のフィンガープリントを比較することで、単純なファイルスキャンを超え、バイナリ内で使用されている関数に潜り込み、オープンソースの依存関係を識別することができます。 未修正バイナリ検出 〇 〇 既知のオープンソース・ファイルを個別に検出できます。 エアギャップ スキャニング 〇 〇 これにより、オフラインのマシンでコードからフィンガープリントをキャプチャし、コードを見ることなく別のコンピューターからスキャンすることができる。 SBOM生成 出力 PURL 〇 〇 ほとんどのオープンソースツールで採用されている正確なオープンソース識別子を提供します。 CSV (出力) 〇 〇 機械が読み取り可能な情報を提供し、Excelシートに簡単にインポートしたり、他のアプリケーションに取り込むことができます。 SPDX (入出力) 〇 〇 標準 SPDX SBOM フォーマットを提供します。 CycloneDX (入出力) 〇 〇 CycloneDXの標準フォーマットを提供します。 HTML (出力) 〇 〇 レポートに使用できるグラフィックのポータブルフォーマットを提供します。 SBOM 情報 ライセンス 〇 〇 コードで検出されたライセンスを提供します。 著作権 × 〇 コードで検出された著作権を提供します。 脆弱性 〇 〇 提供されているオープンソースコンポーネントの既知の脆弱性をリストアップします。 暗号 (ECCN用) × 〇 提供されるオープンソースコンポーネントの暗号アルゴリズムをリストアップし、ECCN(輸出規制)の分類に役立ちます。 安全なコーディングの実践 × 〇 セキュリティのベストプラクティスに基づくコード品質に関する洞察を提供する。 コードの由来 (Coming soon) × 〇 国別のオープンソースコンポーネントの物理的起源に関する洞察を提供します。 コードの健全性 × 〇 要求されたコンポーネントに関するオープンソースコミュニティの洞察を提供する。 CPEs × 〇 オープンソースコンポーネントの CPE 識別子を提供し、既知の脆弱性の特定に役立ちます。 コードの持続可能性 (Coming soon) × 〇 既知の各オープンソースコンポーネントの計算/エネルギー消費に基づく持続可能性評価を提供します。 独自 × 〇 オープンソースインテリジェンスの新しいカスタムアスペクトを生成するために、お客様が独自のルールやアルゴリズムを作成することができます。 消費/統合 SBOM Workbench App (UI) 〇 〇 グラフィカル・ユーザー・インターフェースでオープンソースの分析が可能です。 CLI 〇 〇 コマンドプロンプトから簡単に操作でき、他のシステムとも簡単に統合できる。 API 〇 〇 他のシステムとの統合のための直接問い合わせが可能です。 パイプラインの統合 〇 〇 既知のCI/CDパイプラインのための既製の統合を提供します。 IDE 〇 〇 一般的なIDE用のプラグインを提供します。 SDK 〇 〇 他のアプリケーションに簡単に統合できます。 Webhook 〇 〇 Push、Pull Requestなどのgitアクションに対してSCANOSSスキャンをトリガする機能を提供します。 可用性とスループット 保証された可用性 × 〇 99.9%のアップタイムを保証。 スループット保証 × 〇 顧客の要求するスキャン・スループットを保証する。 デプロイ クラウド(有料版には+専用クラウドが含まれます) 〇 〇 当社のSaaS APIは、導入時間、コスト、メンテナンスを最小限に抑えることができます。 オンプレミス × 〇 オンプレミス型では、SCANOSSのデータベースとプラットフォーム全体をお客様サイトに保管することで、最高レベルのセキュリティを提供します。 ハイブリッド × 〇 クラウドとオンプレミスの組み合わせによるハイブリッド展開。 サポート コミュニティ 〇 〇 コミュニティ・サポートは可用性に基づいており、アップタイムやスループットの保証はありません。 Eメールとチャット × 〇 Eメールとチャットによるサポートは、有料のお客様に限ります。 カスタムSLA × 〇 カスタムSLAにより、お客様の特定のサポート要件を満たすことができます。 さいごに 今回はSCANOSSについて書きました。9月は生成AIのタスクがほとんどで、久しぶりのSBOMツール調査でした。今後も少しずつツール調査を行っていこうと思います。次回は実際にSCANOSSを使ってみたことについて書く予定です。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post SBOMツール紹介 ~ SCANOSS編 ~ ①SCANOSSについて first appeared on SIOS Tech. Lab .
アバター
Client Credentials Flowとは Client Credentials FlowとはOAuth2.0の認証フローの一つです。詳しくは 【連載】世界一わかりみの深いOAuth入門 〜 その1:OAuthってなに? 〜 を参照してください。簡単に説明すると以下の図のように認証を行います。 今回はこの図の認可サーバーにMicrosoft Entra IDを用いて、リソースサーバーにAzure API Managementを用いた場合の構成を実装したいと思います。 Azure API ManagementとMicrosoft Entra IDの設定方法 Azure API ManagementとMicrosoft Entra IDを用いたClient Credentials Flowを実装する方法をこれから解説します。最初に認可サーバーであるMicrosoft Entra IDでクライアントIDとクライアントシークレットの設定を行います。次にリソースサーバーであるAzure API Manegementに認可サーバーと接続するための設定を行い、テスト用のAPIを作成します。 ※この後行う実装からテストまでの工程は料金が発生する可能性があるので注意してください。 Microsoft Entra IDの設定 アプリの登録 まず最初にAPIMでホストするAPIをアプリケーションとしてEntra IDに登録します。 Microsoft Entra IDはすべてのサービスのIDカテゴリのID管理にあります。 Microsoft Entra ID > 管理 > アプリの登録から新規登録を押すことでアプリの登録ができます。今回は名前をoauthtestとしました。リダイレクトURIは不要です。 登録後、次のような画面に遷移します。   アプリケーションのURIの追加 Azure API ManagementからのAPIに対して認可を行うための設定を行います。先ほど遷移した画面からアプリケーションID URIの追加を押します。 APIの公開のページにあるアプリケーションID の URIと書かれている右にある追加を押します。するとアプリケーションID URIの編集がでるので、そのまま保存を押します。   また、今回はスコープも設定したいと思います。APIの公開のページからScopeの追加を押します。スコープ名を適当に設定し、同意できるのは管理者のみ、状態を有効とします。 クライアントシークレットの作成 ※メモの準備をしてください。   クライアントシークレットの作成は登録したアプリの管理 > 証明書とシークレットから行います。新しいシークレットを押し、クライアントシークレットの追加を行います。有効期限は推奨の180日とします。追加を押すとクライアントシークレットが追加されます。 このときに表示されているクライアントシークレットの値は別のページに移動すると見れなくなるので追加した直後にすぐにメモしてください。 APIのアクセス許可 Entra IDに登録したアプリのアクセス許可の設定を行います。登録したアプリの管理 > APIのアクセス許可に移動してください。アクセス許可の追加をクリックするとアクセス許可の要求が出てきます。登録したアプリ(今回はoauthtest)を選択します。スコープも設定したので、チェックを付けてアクセス許可の追加を押します。 エンドポイントの確認 以上の操作を行ったら、登録したアプリの概要に移動してください。この後使用するのでページに載っているアプリケーション (クライアント) IDとディレクトリ (テナント) IDとアプリケーション ID の URIをメモしてください。次にエンドポイントを押してください。すると認証に必要なURLが表示されるので、この後使用するOAuth 2.0 承認エンドポイント (v2)、OAuth 2.0 トークン エンドポイント (v2)、OpenID Connect メタデータ ドキュメントの3つをメモしてください。これでEntra IDの設定は完了です。 Azure API Managementの設定 APIMのインスタンス作成 Entra IDの設定が終わったら、Azure API Managementのインスタンスを作成します。 Azure API ManagementはすべてのサービスのWeb & MobileのAPI管理にあります。 API Management サービスの作成を押すことで新規作成ができます。 今回のリソース名はAPIMoauthtestとしました。価格レベルはConsumptionがおすすめです。従量課金制ですが毎月100万回まで無料です。(Azure API Manegementの価格: https://azure.microsoft.com/ja-jp/pricing/details/api-management/#pricing ) その他は変更せずインスタンスを作成しましょう。 APIMでOAuth 2.0認可サーバーの構成 OAuth2.0認可サーバーをAPIMに追加します。作成したAPIMのインスタンスに移動し、APIs > OAuth 2.0 + OpenID Connectに移動します。OAuth2.0のタブで追加を押して、以下の項目を入力します。 設定項目 値 表示名 何でもよい ID 自動入力される クライアント登録ページのURL 何でもよい(必須とは書かれていないが書かないとエラーが起きる) クライアントの資格情報 チェック 承認エンドポイントのURL OAuth 2.0 承認エンドポイント (v2) 承認要求方法 2項目選択 トークンエンドポイントのURL OAuth 2.0 トークン エンドポイント (v2) 既定のスコープ {アプリケーション ID の URI}/.default クライアントID アプリケーション (クライアント) ID クライアントシークレット クライアントシークレットの値 テスト用のAPIを作成する リソースを獲得するためのテスト用のAPIを作成します。APIs > APIに移動し、Add APIからHTTPを選択して、Display nameとNameを入力してCreateを押します。 その後、Add operationsを選択してDisplay nameとNameとURLを入力して、Responseのタブで200 OKを選択します。 作成したoperationのBackendの項目でエンドポイントの設定を行います。TargetをHTTP(s) endpointとし、ServiceURLをOverrideしてhttps://httpbin.org/anythingとします。Gateway credentialsはNoneとして保存します。ちなみに https://httpbin.org とはHTTPのテストを行う際に、このURLに送ることでシンプルなHTTPリクエストとレスポンスを返してくれるので正しくAPIのリクエストが送られているか確認することができます。例えばhttps://httpbin.org/anythingにリクエスト送るとリクエストの内容を全て返却します。 サブスクリプションの設定 APIのサブスクリプションの設定を行います。APIs > サブスクリプションに移動し、サブスクリプションの追加を押します。名前を入力し、スコープをAPI、APIを先ほど作成したAPIの名前を入力します。作成したら、作成したサブスクリプションの右の方にある「・・・」からキーの表示を行って、主キーの値をメモしてください。 APIにOAuth 2.0認可を設定する テスト用に作成したAPIのページを開いて、Settingのタブに移動します。下の方にSecurityという項目があるのでそこでOAuth2.0を選択し、OAuth 2.0 serverを先ほど作成した認可サーバーを設定します。 validate-jwt ポリシーをAPIに追加する Designタブに移動し、All operationsを選択して、Inbound processingのAdd policyを押します。いくつか種類が出てくるのでValidate JWTを選択します。設定項目にFullがあるので選択して以下の項目を入力します。 設定項目 値 Validate by Header Header name Authorization Failed validation error message 認証が失敗しました Issuers https://sts.windows.net/{ディレクトリ (テナント) ID} Required claims > Name aud Required claims > Match Any claim Required claims > Values アプリケーション ID の URIとアプリケーション (クライアント) ID Open ID URLs OpenID Connect メタデータ ドキュメント 以上の設定を保存すると以下のようなXML形式で確認できます。 <!--     - Policies are applied in the order they appear.     - Position <base/> inside a section to inherit policies from the outer scope.     - Comments within policies are not preserved. --> <!-- Add policies as children to the <inbound>, <outbound>, <backend>, and <on-error> elements --> <policies>     <!-- Throttle, authorize, validate, cache, or transform the requests -->     <inbound>         <base />         <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="認証が失敗しました" require-expiration-time="false" require-signed-tokens="false">             <openid-config url="[OpenID Connect メタデータ ドキュメント]" />             <issuers>                 <issuer>https://sts.windows.net/{ディレクトリ (テナント) ID}</issuer>             </issuers>             <required-claims>                 <claim name="aud" match="any">                     <value>[アプリケーション ID の URI]</value>                     <value>[アプリケーション (クライアント) ID]</value>                 </claim>             </required-claims>         </validate-jwt>     </inbound>     <!-- Control if and how the requests are forwarded to services  -->     <backend>         <base />     </backend>     <!-- Customize the responses -->     <outbound>         <base />     </outbound>     <!-- Handle exceptions and customize error responses  -->     <on-error>         <base />     </on-error> </policies> これでAzure API Managementの設定は完了です。 リクエスト確認 今までの設定でClient Credentials Flowの検証を行う設定は整いました。それでは実際に下の図の工程をシェルスクリプトでテストしてみたいと思います。URLやIDはメモした値を適宜入れてください。 実行環境: WSL2 – Ubuntu 22.04.3 LTS bash : version : 5.1.16(1)-release (x86_64-pc-linux-gnu) curl : version : 7.81.0 (x86_64-pc-linux-gnu) libcurl/7.81.0 OpenSSL/3.0.2 zlib/1.2.11 brotli/1.0.9 zstd/1.4.8 libidn2/2.3.2 libpsl/0.21.0 (+libidn2/2.3.2) libssh/0.9.6/openssl/zlib nghttp2/1.43.0 librtmp/2.3 OpenLDAP/2.5.16 jq : version : jq-1.6 #!/bin/bash # トークンエンドポイントのURL TOKEN_ENDPOINT="[OAuth 2.0 トークン エンドポイント (v2)]" # クライアントIDとクライアントシークレット CLIENT_ID="[アプリケーション (クライアント) ID]" CLIENT_SECRET="[クライアントシークレット]" # スコープ SCOPE=" {アプリケーション ID の URI}/.default" # APIのリクエストURL REQUEST_URL="https://apimoauthtest.azure-api.net/test" # azure subscription SUBSCRIPTION_KEY="[サブスクリプションの主キー]" # トークン取得用のcURLコマンドを実行 (図の➀) TOKEN_RESPONSE=$(curl -s -X POST $TOKEN_ENDPOINT \   -H "Content-Type: application/x-www-form-urlencoded" \   -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=$SCOPE") ##  -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET") # レスポンスからアクセストークンを抽出 ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access_token') # アクセストークンを表示 #echo "Access Token: $ACCESS_TOKEN" # APIへリクエスト(図の③) curl $REQUEST_URL -H "Ocp-Apim-Subscription-Key:$SUBSCRIPTION_KEY" -H "Authorization: Bearer $ACCESS_TOKEN" このスクリプトでは図の➀と③を実行しています。 スクリプト内でトークン取得用のcurlコマンドを実行している部分は図の➀にあたる工程で、クライアントIDとクライアントシークレット(とスコープ)をリクエストに含め、Entra IDのトークンエンドポイントにリクエストを送っています。そのレスポンスからアクセストークンを取得することができます。アクセストークンを表示している部分のコメントアウトを外してみると実際のアクセストークンを見ることができます。 APIへリクエストを送っている部分は図の③にあたる工程で、アクセストークンをリクエストに含め、APIのリクエストURLにリクエストを送っています。 このスクリプトが実行するときに認証が正常に通れば、https://httpbin.org/anythingにリクエストが送られ、そのリクエストの内容全て返却されるので以下の結果が出力されます。 {   "args": {},   "data": "",   "files": {},   "form": {},   "headers": {     "Accept": "*/*",     "Authorization": "Bearer [アクセストークン]",     "Client-Ip": "[IPアドレス]",     "Disguised-Host": "apimoauthtest.azure-api.net",     "Host": "httpbin.org",     "Max-Forwards": "9",     "Ocp-Apim-Subscription-Key": "[サブスクリプションキー]",     "User-Agent": "curl/7.81.0",     "Was-Default-Hostname": "apimwebappufvghnbkilctmh9yz92et3nmiuiguwlnabrr0toc.azurewebsites.net",     "X-Amzn-Trace-Id": "Root=1-66f50cd8-4d773ed14da35b5e286b886f",     "X-Appservice-Proto": "https",     "X-Arr-Log-Id": "7e2ffcd8-b456-4d4b-88c5-bce1cf4bae66",     "X-Arr-Ssl": "2048|256|CN=Microsoft Azure RSA TLS Issuing CA 03,O=Microsoft Corporation,C=US|CN=*.azure-api.net,O=Microsoft Corporation,L=Redmond,S=WA,C=US",     "X-Forwarded-Tlsversion": "1.3",     "X-Original-Url": "/test",     "X-Site-Deployment-Id": "apimwebappufvghNBkiLcTmh9Yz92ET3NmiuIGUwlnAbRr0TOc",     "X-Waws-Unencoded-Url": "/test"   },   "json": null,   "method": "GET",   "origin": "[IPアドレス]",   "url": "https://httpbin.org/anything/test" }   例えばシェルスクリプトのクライアントIDやクライアントシークレットをわざと違う値に変更して実行すると以下の結果が出力されます。   { "statusCode": 401, "message": "認証が失敗しました" }   クライアントIDやクライアントシークレットが間違っていると認可サーバーからアクセストークンが渡されないのでリソースサーバーにアクセスすることができません。このようにしてClient Credentials Flowは認証を行うことができます。   まとめ Azure API ManagementとMicrosoft Entra IDでOAuth2.0の認可フローの一つであるClient Credentials Flowを実装してみました。 注意することとして、今回行ったClient Credentials Flowはクライアントに対して認証を行っているだけでユーザー個人に対しては認証を行っていません。個人レベルの高度なセキュリティの認証を行いたい場合はAuthorization Code Flowなどを使いましょう。   参考文献 https://azure.microsoft.com/ja-jp/products/api-management https://www.microsoft.com/ja-jp/security/business/identity-access/microsoft-entra-id https://logico-jp.io/2019/10/01/protecting-apis-with-oauth2-client-credentials-grant-in-azure-api-management/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure API Managementを利用して、Client Credentials Flowを実装してみた first appeared on SIOS Tech. Lab .
アバター
こんにちは、 PS-SL アプリチーム新卒 1 年目の織田です 9/29 に OSC 広島に参加してきました。今回は自分が見た展示の中で特に印象に残ったものを紹介したいと思います。 NEMTUS 最初は NEMTUS さんが行っていたブロックチェーンについての展示です。 NEMTUS さんは NEM や Symbol といったブロックチェーン技術の普及や発展を促進する NPO 法人です。 展示では NEM や Symbol を実際の業務にどのように活用するかについて丁寧に教えていただけました。例えば、畜産業においては狂牛病などの感染症に対処するため、牛や豚といった家畜のトレーサビリティが重要視されています。また、物流においても貨物のトレーサビリティを効率的に担保する手法が必要とされています。そこで、ブロックチェーンを活用し家畜や貨物の流通を記録する手法が検討されています。この手法であれば、記録情報を人手で管理する必要がなくなるだけでなく、情報の分散管理(リスク分散)も行うことができます。また、ブロックチェーンの実装には独自の言語の習得が必要であると思っていたのですが、 JS や C# といったメジャーな言語でも実装することができるそうです。 実際のNEMTUSさんの展示内容。下段左から2冊が人気の書籍で、JSやC#を用いたブロックチェーンの実装方法が書かれている。   MySQL Community Team 続いてはMySQL Community Teamさんの展示を見てきました。 MySQL は大学時代から使っていたこともあり、あまり新鮮なお話は聞けないのではと思っていました。しかし、実際に色々とお話をしたところ、 HeatWave という面白い拡張機能のお話を伺えました。この拡張機能を用いると、 MySQL 単体でデータの統計解析や機械学習を高速に実施することができるそうです。 私が大学で使用していた際は、 MySQL はただデータを保管・閲覧するためのツールで分析は統計ソフトないし Python などで行っていました。なので、 MySQL 単体でデータサイエンスにかかわることが自己完結できてしまうというのは非常に面白いお話でした。 SQL 分で記述できるということで、プログラミングに不慣れな方でも統計解析や機械学習にチャレンジすることができ、様々な領域で活用されるのではと期待しています。   日本 NetBSD ユーザグループ 最後は 日本 NetBSD ユーザグループ さんの展示です。こちらでは、 UNIX 系 OS の NetBSD を搭載したシャープ製 X68030 や Machintosh LC630 を見ることができました。 OS のお話はかなりディープだったためほとんど理解できなかったのですが、往年のコンピュータが目の前で動いていることに感動し写真を撮ってきました。 OS についての理解もいつか深められればと思います。 シャープ製X68030とNetBSDによる展示。SNSから取得したデータがリアルタイムで表示されている。 MachintoshとNetBSDによる展示。SNSから取得したデータがリアルタイムで表示されている。 最後に 今回 OSC に初めて参加させていただきました。技術的にとてもディープなお話、自分が全く知らなかったお話を聞くことができ非常に有意義な時間でした。素敵な展示をしてくださった皆様、本当にありがとうございました。今後も OSC が開催された際にはぜひ参加したいと思いました。またの機会には展示の内容をブログにてまとめようと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 2024年OSC広島_展示内容紹介 first appeared on SIOS Tech. Lab .
アバター
こんにちは、 PS-SL アプリチーム新卒 1 年目の織田です。 6月から9月までの間、新卒研修の一環としてシステムのチーム開発を行っていました。作成したのは、旬の食材を用いたレシピを検索するシステムです。 私も含め計5名の新卒による開発体制で、私は主に React+JS でフロントエンドの開発を担当しました。今回は、フロントエンド開発を通して学んだことをブログにまとめました。ぜひ最後まで読んでいただければ嬉しいです。 MUI 今回の開発ではデザイン面はほぼ MUI に頼りきりでした。当初は CSS や JS を手書きしてデザインを調整しようと考えていたのですが、メンターの方から「 React を使っているなら MUI が便利ですよ」と教えていただき触ってみることに。 実際のコーディングも非常に簡単で、インポートしてタグを書くだけ。とても簡単に整った画面を作成することができました。例えば、今回のシステムには登録した料理を作成するための食材や調味料を表示する機能があります。この画面をMUI無しで実装すると図1のようになります。 図1:MUI無しで実装した食材ページ。少し見にくいデザインで、食材にカーソルを合わせても反応なし。 図1でも一応食材や調味料のリストを表示することはできていますが、なんとなく見栄えが悪い感じがあります。続いて、MUIを用いて全く同じ内容の画面を作成したものが図2になります。 図2:MUIを用いて実装した食材ページ。すっきり整ったデザインでチェックボックスも付けられる。カーソルに反応する。 見栄えがとてもよくなった感じがします。見た目はとてもすっきりと変化したのですが、コード内での変更はそれほど多くはありませんでした。html単体でlistを表示するにはulとliを用いるのですが、それぞれListとListItemにするだけです。また、MUIのCheckboxを用いて手元にある食材の確認を行えるようにもできました。 このようにMUIは非常に簡単な記述だけで、見栄えが良くかつ機能的な画面を作成することができます。今後、フロントエンド開発をするときはまずMUIを使いたいなと思っています。 usestate とローディング処理 続いてローディング処理についてです。ユーザが画面遷移した際に食材や料理を表示することになるのですが、バックエンドからデータが送り届けられるまで若干のラグがあります。その間、真っ白な画面が表示されるとバグなのかラグなのか分からなくなります。 そこで、バックエンドにリクエストを送り何か返答があるまでは「ローディング中」と画面に表示することにしました。実装のためにはフラグ管理が必要になるのですが、これには usestate を用いました。データをリクエストする直前でフラグを true にセットし返答があれば false にセットします。あとは、このフラグが true の間は「ローディング中」と画面に表示すれば完成です。 React のキャッチアップをした際に、stateと usestateも学習していました。しかし、コンポーネントが内部で保持する「状態」を管理すると言われても、その 使い道はよく分かっていませんでした。しかし、ローディング処理の実装を通して何かしらのフラグを用意したいときに非常に役立つことを理解できました。また、今回のシステムでは野菜や料理のデータをバックエンドから取得するわけですが、取得したデータを管理するためにもとても役立ちました。最早、Reactを使うのであればusestateを使わないことはないのではないかとさえ思ってしまいます。 loading処理の実装コードイメージ。データを取得する直前にloadingフラグをtrueにし、取得後falseにする。 datalist 要素 今回のシステムには、作り置きした料理の消費スケジュールを記録する機能がついています。 スケジュール設定時にはタイトルを設定できるようにしてあり、当初は自由記述で設定するようにしていました。ところが、実際にシステムを使った方からのFBで「登録した料理の名前をスケジュールタイトルとして選択できると嬉しい」というFBをいただきました。自由記述も選択式もどちらも使いたい需要があるかなと思い、両方でタイトルを設定できるよう変更することにしました。 今回はMUIを用いて画面の要素を実装していたので、自由記述と選択式の組み合わせもMUIを用いて実装しようとしました。ところが、試行錯誤してもなかなか実装が上手くいかず行き詰ってしまいました。とりあえず使えそうなものがないか検索していたところ、 HTML の datalist 要素 を見つけました。 datalist 要素は input に対してデータを渡すことができます。つまり input と datalist を組み合わせることで、自由記述と選択式を同時に実装できるということが分かりました。 当初は MUI などを用いて実装しようと考えていたのですが、まさか HTML にこんな便利な要素があるとは思いませんでした。 HTML もなかなか奥深いですね。 datalistを用いて実装したスケジュール登録画面。自由記述と選択式の両方でタイトルを決められる。 最後に 今回は研修期間中の自主開発を通して学んだことをご紹介しました。自分にとって初めての本格的なフロントエンド開発であり、不安もありましたが学び多い機会でした。基本的な文法や便利な機能なども勿論勉強になったのですが、少し頭を捻ることで実装できた内容が多く、良い経験を積むことができました。折に触れて React を活用していきたいと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 2024年度自主開発_フロントエンド開発で学べたこと first appeared on SIOS Tech. Lab .
アバター
こんにちは。サイオステクノロジーの木村です。 今回は、Flutter アプリに Firebaseを導入し、Firebase Cloud Messaging(FCM)を利用してプッシュ通知を導入する方法をご紹介します。 Android と iOS ではPush通知が飛んでくる経路が少し違うため設定内容なども違いますが、今回は iOS に特化した内容で記載します。 Flutter プロジェクトの作成 まずは Flutter プロジェクトを作成します。 以下の記事の手順を参照し作成してください。 【Dart】 Flutter × VSCode で、環境構築 から HelloWorld まで(Mac) Apple Developer での設定 iOSのプッシュ通知を行うには、Apple Developer にてさまざまな設定を行う必要があります。 こちらの手順を行うには、Apple Developer Program のアカウントが必要です。 証明書の作成・登録 1. Xcode を起動し、上部のメニューより「Settings」をクリックします。 2. Accounts > Manage Certificates をクリックして開いたウィンドウにて、+ > Apple Development をクリックすると証明書が作成されます。「Done」をクリックします。(App Store や TestFlight で配信する場合は Apple Distribution となりますが、今回は開発用のため Development とします。) 作成された証明書は、Xcode でログインしている Apple Developer のサイトの Certificates に自動的に登録されます。 App ID の登録 Apple Developer のサイトにて、App ID を登録します。 1. Apple Developer にサインインします。 2. プログラムのリソース > 証明書、ID、プロファイル 配下の「証明書」をクリックします。 3. サイドメニューの「Identifiers」をクリックして開いた画面にて、「Identifiers」の横の「+」をクリックします。 4. App IDs を選択し「Continue」をクリックします。 5. App を選択し「Continue」をクリックします。 6. 「Description」に任意の名称を入力します。「Bundle ID」はリバースドメインなどを利用したユニークな値を指定します。(後の手順で Xcode 上でもアプリにこの値を指定します。) 7. そのまま下にスクロールし「Push Notifications」にチェックを入れ、「Continue」をクリックします。 8. 登録内容を確認し「Register」をクリックします。 以上で App ID の登録は完了です。 プロビジョニングプロファイルの登録 Apple Developer にて、プロビジョニングプロファイルの登録をします。 1. サイドメニューの「Profiles」をクリックして開いた画面にて、「Profiles」の横の「+」をクリックします。 2. 「iOS App Development」を選択して「Continue」をクリックします。 3. 「App ID」に、先ほどの手順で作成した APP ID を選択して「Continue」をクリックします。 4. 「Select certificates」に、先ほど作成した証明書を選択して「Continue」 をクリックします。 5. 「Select devices」でデバッグに利用したいデバイスを選択して「Continue」 をクリックします。 ※ プッシュ通知はシミュレータで動作確認することができないため、実機にて動作確認します。ここで選択したデバイスがデバックで利用できるデバイスとなります。またデバイスを登録しておらず、ここに表示されない場合は、Devices よりデバイスの登録を行なってください。 6. 「Provisioning Profile Name」に任意の名称を入力し「Generate」をクリックします。 7. 以上でプロビジョニングプロファイルの登録は完了です。(プロビジョニングプロファイルは後ほど Xcode から直接参照するので、ここでダウンロードする必要はありません。) Xcode での設定 プロビジョニングプロファイルをアプリに設定 Xcodeにて、プロビジョニングプロファイルをアプリに設定します。 1. VScodeにて表示した Flutter プロジェクト配下の「ios」を右クリックして「Open in Xcode」をクリックします。 2. Xcode が起動します。Runner > TARGETS > Runner の「General」タブをクリックします。 3. 「Minimum Deployments」に 12. 0 が表示されている場合、13.0 以上の値を選択します。 4. Signing & Capabilities タブをクリックします。「Automatically manage signing」のチェックを外し、Bundle Identifier で先ほど App ID 登録時に指定した Bundle ID を指定します。「Provisioning Profile」にて「Download Profile」を選択します。 5. 先ほど Apple Developer にて登録したプロビジョニングプロファイルが表示されるので「Select Profile」をクリックします。 プッシュ通知用の設定 Xcodeにて、アプリにプッシュ通知用の設定を行います。 1. Signing & Capabilities タブにて「Capability」をクリックします。 2. “push”と入力して検索し「Push Notifications」をクリックして追加します。 3. 次に”back”と入力して検索し「Background Modes」をクリックして追加します。 4. 「Background Modes」の設定にて「Background fetch」と「Remote Notifications」にチェックを入れます。 以上でXcodeでの設定は完了です。 APNs認証キー or APNs証明書の作成 APNsとは APNs(Apple Push Notification Service)とは、iOSやtvOS、MacOSアプリに対して開発者側から通知を送るためのAppleが提供するサービスです。Appleデバイスを対象に通知を送信する際は、Firebase Cloud Messaging などの通知を行えるサービスから、この APNs を介して通知が送信されます。 APNs に対する認証手段は2つ(APNs認証キー or APNs証明書)あり、そのどちらかを作成して使用します。 APNs 認証キー アプリごとに用意する必要がないベンダー共有の認証キーです。開発環境とプロダクション環境の両方で使うことができます。有効期限はありませんが、無効化させることはできます。 APNs 証明書 証明書はアプリごとに作成する必要があり、証明書を使用して認証します。開発環境とプロダクション環境のそれぞれの証明書を作成する必要があります。証明書は有効期限があり、定期的に更新が必要です。 APNs 認証キーの作成 APNs 認証キーの作成手順を記載します。(APNs 証明書を利用する場合はこちらの手順は必要ありません。) 1. Apple Developer にサインインします。 2. プログラムのリソース > 証明書、ID、プロファイル 配下の「証明書」をクリックします。 3. サイドメニューの「Keys」をクリックして開いた画面にて、「Keys」の横の「+」をクリックします。 4. 「Key Name」に任意の名称を入力し、「Apple Push Notifications service (APNs)」にチェックを入れて「Continue」をクリックします。(作成できるAPNsキーの数には上限(2つ)があり、上限を超えると作成できません。画像は条件を超えているため作成できない旨のメッセージが表示されています。) 5. 以降、画面表示に従ってキーを作成します。 APNs 証明書の作成 APNs 証明書の作成手順を記載します。(APNs キーを利用する場合はこちらの手順は必要ありません。) CSR(Certificate Signing Request)の発行 CSRの発行は、Macのキーチェーンアクセスアプリから行います。 1. キーチェーンアクセスを起動し、証明書アシスタント > 認証局に証明書を要求をクリックします。 2. メールアドレスと通称部分に任意の内容を入力します。要求の処理にて、ディスクに保存と鍵ペア情報を指定にチェックを入れ「続ける」をクリックします。 3. CertificateSigningRequest.certSigningRequestの保存先の選択が求められるので、任意のディレクトリに保存します。 4. 「続ける」をクリックします。 5. 「完了」をクリックします。 証明書の作成 1. Apple Developer にサインインします。 2. プログラムのリソース > 証明書、ID、プロファイル 配下の「証明書」をクリックします。 3. サイドメニューの「Certificates」をクリックして開いた画面にて、「Certificates」の横の「+」をクリックします。 4. Servicesの項目配下にある「Apple Push Notification service SSL (Sandbox)」にチェックを入れ「Continue」をクリックします。 (証明書には Sandbox 環境だけへの接続を認可するものと Sandbox と Production 環境どちらへの接続を認可するものがありますが、こちらの手順では、Sandbox環境だけのものを作成します。) 5.「App ID」に、前述した「App ID の登録」の手順で登録したものを選択し「Continue」をクリックします。 6. 「Choose File」をクリックして、先ほど発行したCSRファイルを選択し「Continue」をクリックします。 7. 証明書が作成されます。「Download」をクリックしてファイルをダウンロードします。 p12形式に変換 Firebase Cloud Messagingでは、cer形式の証明書のままではアップロードできませんので p12 形式に変換します。 1. 作成してダウンロードしたcerファイルをダブルクリックし、キーチェーンアクセスアプリに証明書を追加します。キーチェーンアクセスアプリにて、追加した証明を右クリックし、「”キー名”を書き出す」をクリックします。 2. 証明書の名称、保存先に任意の内容を入力します。フォーマットは「個人情報交換(.p12)」のままとし「OK」をクリックします。 3. パスワード・確認に任意のパスワードを入力し「OK」をクリックします。 以上で証明書の作成は完了です。 Firebase プロジェクトの作成 Firebase コンソールにて、Firebase プロジェクトを作成します。 以下の手順を行うには、Google アカウントが必要です。 1. Firebase のサイト にて「Go to console」をクリックし、Firebase コンソールにGoogle アカウントでサインインします。 2. 「Firebase プロジェクトを使ってみる」をクリックします。 3. 任意のプロジェクト名を入力し「続行」をクリックします。 4. 「Google アナリティクスを有効にする」は必要に応じて選択してください。 5.「プロジェクトを作成」をクリックすると、プロジェクトが作成されます。 Firebase プロジェクトにアプリを追加 Firebase プロジェクトへのアプリの追加は Firebase コンソール から行うこともできますが、Firebase CLI ・FlutterFire CLI で行うと1つのコマンドでアプリの追加と Flutterの 設定が行えて便利です。以下では、Firebase CLI ・FlutterFire CLI を使用した手順を記載します。 Firebase CLI・FlutterFire CLI のインストール 1. Flutter プロジェクトをVSCodeで開きます。 (Flutter プロジェクトは、以下の記事で作成したものを使用します。 【Dart】 Flutter × VSCode で、環境構築 から HelloWorld まで(Mac) ) 2. ターミナル > 新しいターミナル をクリックします。 3. Firebase CLIのインストールを行います。以下のコマンドを実行します。 sudo npm install -g firebase-tools 4. 以下のコマンドでバージョンが表示されたら、正常にインストールされている古語が確認できます。 firebase --version 5. 以下のコマンドを実行し、 Firebase CLI にログインしておきます。 firebase login ブラウザが起動し、Firebaseアカウントへのサインインを求められますので、サインインします。 これで、Firebase プロジェクトの管理やデプロイをコマンドラインから行う準備が整います。 6. 続いて FlutterFire CLI のインストールを行います。以下のコマンドを実行します。 dart pub global activate flutterfire_cli アプリの追加 Firebase プロジェクトへアプリの追加を行います。 1. ターミナルにて、Flutter プロジェクトのルートディレクトリで以下のコマンドを実行します。(以下のコマンドでは、iOS と Android のアプリの追加を行います。) flutterfire configure --platforms=ios,android --project=[プロジェクトID] --android-package-name=[Android アプリのパッケージ名] --ios-bundle-id=[Apple アプリのバンドルID] プロジェクトID:「Firebase プロジェクトの作成」の手順で作成したプロジェクトのプロジェクトID(Firebase コンソールのプロジェクト設定より確認できます) Apple アプリのバンドルID:「App ID の登録」の手順で作成したApp ID の「Bundle ID」 2. 上記コマンドを実行すると、Firebaseプロジェクト上にAndroidとiOSのアプリが作成されます。作成されたアプリは Firebase コンソールから確認することができます。 また、以下のファイルがFlutterプロジェクト内に作成されます。 google-services.json GoogleService-Info.plist firebase_app_id_file.json firebase_options.dart パッケージのインポート Firebase Cloud Messagingを利用に必要なパッケージを追加します。 1. ターミナルにて、プロジェクトのルートディレクトリで次のコマンドを実行します。 flutter pub add firebase_core flutter pub add firebase_messaging 2. プロジェクトのルートディレクトリ/ios 配下に「Podfile」が作成されるので開きます。 “# platform :ios, ‘12.0’”のコメントを外し、”12.0″のところを”13.0″以降の値に変更して保存します。 APNs 認証キー or APNs 証明書 のアップロード Firebase プロジェクトに、APNs 認証キー または、 APNs 証明書をアップロードします。 1. Firebase コンソールにサインインし、「自分のプロジェクト」より該当の Firebase プロジェクトをクリックします。 2. 「プロジェクトの設定」をクリックします。 3. 「Cloud Messaging」タブをクリックします。 4. Apple アプリの構成 の「APNs 認証キー」または「APNs 証明書」どちらかの「アップロード」ボタンをクリックし、「APNs認証キー or APNs証明書の作成」の手順で作成したファイルをアップロードします。 以上でアップロードは完了です。 Flutterの実装 main.dart の「void main() 」のところの実装を以下のようにします。 import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:sample_push/firebase_options.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); final messagingInstance = FirebaseMessaging.instance; // FCMトークンの取得 final fcmToken = await messagingInstance.getToken(); debugPrint('FCM TOKEN: $fcmToken'); // iOSで通知許可を求める NotificationSettings settings = await messagingInstance.requestPermission( alert: true, // 通知表示有無 announcement: false, // アナウンスメント通知 有効/無効 badge: true, // バッジ(未読件数)が更新されるか否か carPlay: false, // CarPlay通知表示有無 criticalAlert: false, // 重要な通知(サイレントではない)有効/無効 provisional: false, // 仮の通知(ユーザーによる設定を尊重)有効/無効 sound: true, // 通知のサウンド有無 ); debugPrint('User granted permission: ${settings.authorizationStatus}'); // authorized: ユーザーが権限の付与を許可、denied: 拒否、notDetermined: 未設定、provisional: 一時的な許可 //フォアグラウンドで通知が表示されるオプションの設定 await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( alert: true, // フォアグラウンドの通知表示有無 badge: false, // バッジ(未読件数)の通知表示有無 sound: true, // 通知のサウンド有無 ); runApp(const MyApp()); } class MyApp extends StatelessWidget { ・・・(以降省略)・・・ 実行(iPhone 実機デバッグ) 通知はシミュレータではテストできません。そのため iPhoneの実機でデバッグを行います。 iPhone の設定とペアリング 1. iPhone にて、 設定 > プライバシーとセキュリティ > デベロッパーモード を ON に設定します。(iPhone のバージョンによって、多少設定方法が異なります。) 2. USBケーブルを使ってiPhoneとPCを繋ぎます。iPhone 側にこのコンピュータを信頼しますか?と表示された場合は「信頼」をクリックしてください。 3. XCodeを開き、Window > Devices and Simulators をクリックします。 4. XCodeが認識しているデバイスとシミュレーターを確認できるウィンドウが表示されます。まiPhoneに再びこのコンピュータを信頼しますか?と表示された場合は「信頼」をクリックしてください。 以上で、デバイスのペアリングができます。Xcodeを閉じます。 デバッグ実行 1. VSCodeにて、画面右下のデバイスが表示される場所をクリックします。 2. 先ほどペアリングした実機が選択肢に表示されるのでクリックします。 3. サイドバーの「実行とデバッグ」のアイコンをクリックし、「実行とデバッグ」ボタンをクリックします。 ※ 実行時にデバッグコンソールに以下のようなエラーが出て起動できない場合、ターミナルより「flutter run」コマンドで実行するとエラーが解消することがあります。 4. iPhone に以下が表示されますので、両方「許可」をクリックします。 5. アプリが起動されます。 6. VSCode のデバッグコンソールに FCM トークンの値が出力されますのでメモしておきます。 通知の送信 Firebase コンソールより、テストの通知を送信します。 1. Firebase コンソールにてプロジェクトを表示し、サイドメニューの「実行」をクリックして展開します。「Messsaging」をクリックします。 2. 「最初のキャンペーンを作成」をクリックします。 3. 「Firebase Notification メッセージ」を選択し「作成」をクリックします。 4. 通知タイトルと通知テキストに任意の値を入力し、「テストメッセージを送信」をクリックします。 5. デバッグ実行の手順にてメモした FCM トークンの値を入力して「+」をクリックします。「テスト」をクリックします。 6. iPhone 実機に通知が送信されます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Flutterアプリでプッシュ通知〜Firebase Cloud Messagingで通知を受信する方法(iOS)〜 first appeared on SIOS Tech. Lab .
アバター
皆さん、こんにちは。サイオステクノロジー武井です。今回は、生成AIの発展形であり、与えられた課題に対して自分で思考し、行動を決定し、実行する「AIエージェント」について、一筆したためたいと思います。 AIエージェントとは? ChatGPTを代表とした生成AIはとても便利ですよね。質問を行うと、まるで人間が答えているかのような自然な対話が可能であり、世界中の話題をさらいました。もう普段の生活やビジネスにもなくてはならない存在かと思います。 しかし、そんな生成AIも万能ではありません。LLM(大規模言語モデル: 生成AIの頭脳のようなもの)が知っていることであれば、スラスラっと答えてくれます(たまにハルシネーションと言って間違いも起こしますが)。しかし、LLMをが知らないことについては、お手上げになります。 これは、人間でも同じですよね。人間も、自分が知っていることであれば、スラスラと答えることができますが、知らないことに関しては、すぐに答えることができません。 こんなとき、人間はどうするでしょう? 人間は、いろんなツールを使って、必要な情報を集め、それらが正しいかどうかを確認・検証し、答えを作ります。 例えば トム・クルーズ の誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。 という質問が与えられたときのことを考えてみます。 そんなとき人間は、まず「トム・クルーズの年齢」を調べます。人間は、最大の叡智「インターネット」というツールを持っているので、インターネットでトムの年齢を調べます。2024年8月27日時点、彼は62歳です。 年齢がわかったので、次はろうそくの値段を調べます。ここでは、ろうそくは1本100円という情報が与えられているので、これを使います。トムの年齢は62歳なので、62本のろうそくが必要です。62本 * 100円 = 6200円です。 こんな感じの思考プロセスが人間にはできます。 これと同じことをAIにもさせることができれば、AIは生成AI以上の能力を発揮することができるのではないかと考えられます。つまりこのようなAIを「AIエージェント」と呼びます。 AIエージェントは、LLMが知らないことについても、与えられたツールを使って、情報を収集し、それを使って問題を解決することができるのです。 AIエージェントを実現するプロンプトエンジニアリング「ReAct」 ReActというのは、フロントエンドのフレームワークではありません。プロンプトエンジニアリングの一種で、先程の人間のような思考を行わせるためのテクニックです。 プロンプトエンジニアリングというのは、AIに対してどのように質問や指示を与えれば、より良い結果が得られるかを工夫するプロセスです。 ReAct以外にも、いくつかのプロンプトエンジニアリングの手法があります。例えば、Few-shot LearningやChain-of-Thoughtなどがあります。 ■ Few-shot Learning Few-shot Learningは、少数の具体例をAIに提示して、 それに基づいて新しい入力に対する応答をさせる手法 です。この方法では、いくつかのサンプルを提示するだけで、AIが新しいパターンに適応しやすくなります。 以下に例を示します。 <プロンプト> 英語を日本語に翻訳してください。 英語: apple 日本語: りんご 英語: orange 日本語: オレンジ 英語: grape 日本語: <AIの応答> ぶどう ■ Chain-of-Thought Chain-of-Thoughtは、 AIに複雑な質問を分解して考えさせる方法 です。通常、問題解決型の質問に対して、1つ1つのステップを順番に処理させることで、正確な回答を得ることが目的です。この手法では、AIが自分で考える過程を示しながら応答します。 以下に例を示します。 <プロンプト> 問題:5人が10個のりんごを分けます。それぞれ何個持ちますか? 回答をステップごとに示してください。 <AIの応答> まず、10個のりんごを5人で割り算します。 10 ÷ 5 = 2 です。 それぞれ2個ずつ持つことになります。 このように、Chain-of-Thoughtではステップごとに回答を導き出し、複雑な問題も解決できるようにします。 ReActもこれらのプロンプトエンジニアリングと同じように、LLMに精度の高い回答を導き出すための手法のひとつなのですが、いささかReActは他のプロンプトエンジニアリングに比べると、少々複雑なので、順を追って説明していきます。 ReActの手法 ReActは、以下の図のように思考を分解し、それぞれのステップに対してAIに質問を行い、AIがそのステップを実行することで、最終的な回答を導き出す手法です。 それでは、ReActのプロセスの図に従い、ReActを使った場合、AIエージェントがどのように問題を解決するかを見ていきましょう。先ほど例に上げた「トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。」という問題を考えてみます。 質問 最初に、AIが質問を受け取ります。この場合の質問は、 トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。 となります。 これが、プロンプトに対してAIが最初に解釈する「問題の提示」の部分です。ReActでは、この段階でAIがどんな情報を探すべきかを認識します。 思考 (1回目) AIは、インターネットでトム・クルーズの年齢を調べます。先ほど「行動の入力」で与えられた検索キーワード「トム・クルーズ 年齢」をもとに、インターネットを検索して、トム・クルーズの様々な情報を取得します。 そして、このときの検索結果を「観察(行動の結果)」として記憶し、次のステップに進みます。 行動 (1回目) AIは、インターネットでトム・クルーズの年齢を調べます。先ほど「行動の入力」で与えられた検索キーワード「トム・クルーズ 年齢」をもとに、インターネットを検索して、トム・クルーズの様々な情報を取得します。 そして、このときの検索結果を「観察(行動の結果)」として記憶し、次のステップに進みます。 思考 (2回目) 1回目の「行動」「行動の入力」「観察(行動の結果)」より、さらに思考します。現時点では、まだトム・クルーズの年齢しかわかっておらず、最終目的である「ろうそくの金額」はわかっていません。よってさらに思考を続けます。 トム・クルーズの年齢は先の「観察(行動の結果)」でわかっていますし、ろうそくの値段はプロンプトに明示(1本100円)されているので、次にやるべきことは「ろうそくの本数」を計算することです。 このとき、行動の入力として、「62 * 100」といったような計算式を生成します(トムの年齢は62歳とします)。 行動 (2回目) AIは、先ほど生成した計算式「62 * 100」を計算し、ろうそくの本数を計算します。計算結果を「観察(行動の結果)」として記憶し、次のステップに進みます。 思考 (3回目) AIは、「観察(行動の結果)」より、すでにろうそくの金額が計算されていることを認識します。最終的な回答が得られたので、これ以上の思考や行動は行わず、最終回答が生成されたと判断します。 最終回答 AIは、最終的な回答として「6200円」と出力します。 このように、ReActはAIに対して、問題を解決するための思考プロセスを段階的に分解し、それぞれのステップに対して質問を行うことで、AIがより正確な回答を導き出すことができるのです。 ReActを使わない場合と使った場合との比較 ここでは、ReActをより深く理解するために、ReActを使わない場合と使った場合との比較を行います。 まずは、ReActを用いない場合、LLMに直接問いかけるとどうなるかを見てみましょう。 この場合、LLMは「トム・クルーズの年齢」や「ろうそくの金額」についての知識を持っていないため、正しい回答を導き出すことができません。2021年時点での年齢を算出していますね。これはモデルの中の知識だけを使っているためです。 このように、LLMは知識がない場合、正しい回答を導き出すことが難しいのです。 では、ReActを用いた場合はどうでしょうか? ReActを用いることで、LLMは、問題を解決するための思考プロセスを段階的に分解し、それぞれのステップに対して質問を行うことで、正確な回答を導き出すことができます。 トム・クルーズの年齢分のろうそくの合計金額を計算するために、LLMは自身で判断し、段取りを行います。まずは、トムの年齢を調べるためにインターネットを検索し、次にその結果をもとにろうそくの本数を計算し、最終的に金額を算出します。 先ほど紹介したReActの構成要素である「質問」「思考」「行動」「観察(行動の結果)」をマッピングしたのが以下の図になります。 このようにして、ReActを用いることで、モデル自身がしらない知識を補完し、より正確な回答を導き出すことができるのです。 Reactを実現するためのプロンプト LLMへの指示はプロンプトを通じて行います。では、ReActのように、LLM自身が自ら思考し、行動を起こすためのプロンプトはどのように書けばよいのでしょうか?ここではそれを学びます。 ReActの場合、LLMへの指示は複数回に分けて行われます。「ReActの手法」でも紹介したように、思考と行動のプロセスは最終回答が出るまで複数回行われることを説明しました。まさにこれと同じことをプロンプトを通じて行うのです。 ここでもまた、先ほどの「トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。」という問題を例にとって、ReActを実現するためのプロンプトを書いてみましょう。 まず、最初の質問を与えるプロンプトは以下のようになります。 次の質問にできる限り答えてください。次のツールにアクセスできます。 – 検索ツール:検索するときに使います – 計算ツール:計算するときに使います。 次のフォーマットを使用します。 質問:回答する必要がある入力質問 思考:次に何をすべきかを常に考える 行動:実行するアクションは、「計算ツール」「検索ツール」のいずれかである必要があります。 行動の入力:アクションへの入力 観察:行動の結果 …(この思考/行動/行動の入力/観察はN回繰り返すことができます) 最終的な答えがわかったら以下を出力します。 思考:今、最終的な答えが分かりました 最終回答:元の入力質問に対する最終回答 計算ツールを使うときの行動の入力は、計算式を入力してください。 例:52 * 100 例:100 – 1 質問:{question} さぁ始めましょう。 思考: では、このプロンプトの詳細を見ていきましょう。 次の質問にできる限り答えてください。次のツールにアクセスできます。 – 検索ツール:検索するときに使います – 計算ツール:計算するときに使います。 この部分では、AIに与えられたツールを説明しています。人間は頭の中に問題を解決するためのいろんなツールがありますよね。例えば、わからないことはインターネットで調べたり、計算が必要な場合は電卓を使ったりします。AIは人間ほど柔軟ではないので、このツールを使ってと明示的に指示する必要があります。 ここでは、検索ツールと計算ツールの2つのツールを使うことができるように設定しています。検索ツールは、インターネットで情報を検索するために使います。後でこのプロンプトとPythonを利用したReActの実装を紹介しますが、その実装の中ではDuckDuckGoというPythonのライブラリを使って検索を行います。 計算ツールについては、計算式をPythonのeval関数を使って計算を行います。 次のフォーマットを使用します。 質問:回答する必要がある入力質問 思考:次に何をすべきかを常に考える 行動:実行するアクションは、「計算ツール」「検索ツール」のいずれかである必要があります。 行動の入力:アクションへの入力 観察:行動の結果 …(この思考/行動/行動の入力/観察はN回繰り返すことができます) 最終的な答えがわかったら以下を出力します。 思考:今、最終的な答えが分かりました 最終回答:元の入力質問に対する最終回答 この部分では、このフォーマット使用して、ReActを実現してくださいという指示を与えています。ReActのプロセスは、「ReActの手法」で説明した通り、「質問」「思考」「行動」「行動の入力」「観察」の5つのステップで構成されています。このステップを繰り返すことで、AIが問題を解決するための思考プロセスを段階的に分解し、それぞれのステップに対して質問を行うことで、AIがより正確な回答を導き出すことができるのです。 「質問」は、AIに行う質問を与える部分です。今回の例で言えば、「トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。」という質問を与えます。 「思考」は、AIが次に何をすべきかを考える部分です。AIは、この部分で自分が次に何をすべきかを考えます。今回の例で言えば、「まずは、トム・クルーズの年齢をインターネットで調べ、その結果をもとにろうそくの本数を計算する」という段取りを考える部分です。 「行動」は、思考の結果、AIが次に何をするべきかを示す部分です。今回の例で言えば、ここに入るのは「検索ツール」「計算ツール」のどちらかになります。 「行動の入力」は、行動を実行する際に必要な入力を示す部分です。今回の例で言えば、計算ツールを使う場合は「62 * 100」といった計算式を入力します。 「観察」は、行動の結果を示す部分です。今回の例で言えば、トム・クルーズの年齢をインターネットで調べた結果や、ろうそくの合計金額を計算した結果を示します。 このプロセスを繰り返し、最終的な答えがでたら、「思考」の結果として、「今、最終的な答えが分かりました」と出力し、「最終回答」を示します。 計算ツールを使うときの行動の入力は、計算式を入力してください。 例:52 * 100 例:100 – 1 この部分では、計算ツールを使う際の行動の入力のサンプルを示しています。Pythonのeval関数を使って計算を行うため、規定のフォーマットに従って計算式を入力する必要があるからです。 質問:トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。 さぁ始めましょう。 思考: 最後に、最初の質問を示す部分です。ここでは、先ほどの「質問」を表示します。また、「さぁ始めましょう。」というメッセージを表示し、AIに問題を解決するための思考プロセスを開始するよう促します。 思考(1回目)の結果 では、実際にReActを使って、AIエージェントが問題を解決するプロセスを見ていきましょう。 先ほど示したプロンプトをAzure OpenAI ServiceのLLMに与えた結果を見ていきます。 黄色い部分が、Azure OpenAI Serviceのレスポンスです。「思考」の部分には、AIが考えた段取りが表示されていますね。 そして、「行動」では、次に行うべきツールの名称である「検索ツール」が示されています。 「行動の入力」には、検索ツールを使う際の入力が示されています。この場合は、「トム・クルーズ 年齢」という検索キーワードが入力されています。つまり、DuckDuckGoを使って「トム・クルーズ 年齢」というキーワードで検索を行うことになります。 行動(1回目)の結果 次に、AIが検索を行った結果を見ていきます。 「思考(1回目)の結果」では、LLMの出力結果として、「行動」に「検索ツール」、「行動の入力」に「トム・クルーズ 年齢」が示されていました。よって、これに従い、DuckDuckGoを使って「トム・クルーズ 年齢」というキーワードで検索を行い、その結果を「観察」に記録したのが、水色の部分になります。 細かい話をしますと、この「観察」に示した結果は、実際にはDuckDuckGoの検索結果上位5件のページの内容を入れています。 そして、このプロンプトを次の「思考(2回目)」で、再びLLMモデルに入力します。 思考(2回目)の結果 「行動(1回目)」で組み立てたプロンプトをAzure OpenAI ServiceのLLMに再度与えた結果を見ていきます。 緑色の部分がLLMが出力した結果になります。 LLMは、「観察」の結果(DuckDuckGoが「トム・クルーズ 年齢」でインターネットを検索して取得したページの内容)をもとに思考し、その結果、「トム・クルーズは現在62歳です。」という考察を得ました。 さらにLLMは思考を重ね、次に何をすべきかを考えた結果、次に行うべき行動として「計算ツール」を使うことを判断しました。トムの年齢がわかったので、あとは計算だけですからね。「思考(1回目)」の段取りで、計算ツールを使うことを決めていたので、この結果は正しいです。 そして、「行動の入力」には、「62 * 100」という計算式が入力されています。これは、トム・クルーズの年齢(62歳)に対して、ろうそくの値段(1本100円)をかけることで、ろうそくの合計金額を計算するための計算式です。 行動(2回目)の結果 最後に、AIが計算を行った結果を見ていきます。 青色の部分が、AIが計算を行った結果です。 思考(2回目)でLLMが出力した行動の入力「62 * 100」をPythonのeval関数に渡し、その結果を記録したのが、「観察」の部分です。計算結果は「6200」となりました。 さらにこのプロンプトを次の「思考(3回目)」で、再びLLMモデルに入力します。 思考(3回目)の結果 「行動(2回目)の結果」で組み立てたプロンプトをAzure OpenAI ServiceのLLMに再度与えた結果を見ていきます。 赤色の部分がLLMが出力した結果になります。 今までのプロンプトの内容からLLMはすでにろうそくの合計金額が計算されていることを認識し、最終的な回答が得られたと判断しました。 そして、最終回答として、「6200」と出力しました。 いかがでしょうか?AIが自律的に考え、段取りを組んで問題を解決していく様子を見ていただけたでしょうか? なんか未来感じちゃいますよね。 ReActの実装 「 Reactを実現するためのプロンプト 」でご紹介したプロンプトのやり取りを実現したPythonのコードを以下に示します。 from openai import AzureOpenAI from duckduckgo_search import DDGS # OpenAI APIの初期化 aoai_endpoint = "https://hogehoge.openai.azure.com/" aoai_api_version = "2024-06-01" aoai_api_key = "XXXXXX" aoai_embedding_model_name = "text-embedding-ada-002-deploy" aoai_chat_model_name = "gpt-4-deploy" template = """ 次の質問にできる限り答えてください。次のツールにアクセスできます。 - 検索ツール:検索するときに使います - 計算ツール:計算するときに使います。 次のフォーマットを使用します。 質問:回答する必要がある入力質問 思考:次に何をすべきかを常に考える 行動:実行するアクションは、「計算ツール」「検索ツール」のいずれかである必要があります。 行動の入力:アクションへの入力 観察:行動の結果 ...(この思考/行動/行動の入力/観察はN回繰り返すことができます) 最終的な答えがわかったら以下を出力します。 思考:今、最終的な答えが分かりました 最終回答:元の入力質問に対する最終回答 計算ツールを使うときの行動の入力は、計算式を入力してください。 例:52 * 100 例:100 - 1 質問:{question} さぁ始めましょう。 思考: """ question = "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。" message = template.format(question=question) final_observation = "" while True: # 推論と行動のプロセス openai_client = AzureOpenAI(azure_endpoint=aoai_endpoint, api_key=aoai_api_key, api_version = aoai_api_version) response = openai_client.chat.completions.create( model=aoai_chat_model_name, messages=[ {"role": "assistant", "content": message}, ], stop=["観察:"] ) result = response.choices[0].message.content.strip() # resultの変数の最後の行に、つまりresultの変数を改行で分割した配列の最後の要素に「最終回答:」がある場合、終了する。 if "最終回答:" in result.split("\n")[-1]: final_observation = result.split("最終回答:")[1].strip() break # resultの"行動"と"行動の入力"を取得し、それにあったツールを実行する。 if "行動" in result: action = result.split("行動の入力:")[0].split("行動:")[1].strip() action_input = result.split("行動の入力:")[1].strip() observation = "" if action in "検索ツール": # 検索ツール実行 search_results = DDGS().text(action_input, max_results=5) for search_result in search_results: observation = observation + search_result["body"] + "\n" elif action in "計算ツール": # 計算ツール実行 observation = str(eval(action_input)) message = message + "\n行動:" + action + "\n行動の入力:" + action_input + "\n観察:" + observation + "\n思考:" print(message) print("\n最終的な観察:") print(final_observation) では、このソースコードの詳細を見ていきましょう。 事前準備として、必要なライブラリをインストールします。 pip install openai duckduckgo-search これで必要なライブラリがインストールされました。 Azure OpenAPI Serviceの初期化 # OpenAI APIの初期化 aoai_endpoint = "https://hogehoge.openai.azure.com/" aoai_api_version = "2024-06-01" aoai_api_key = "XXXXXX" aoai_embedding_model_name = "text-embedding-ada-002-deploy" aoai_chat_model_name = "gpt-4-deploy" Azure OpenAI Serviceのエンドポイント、APIバージョン、APIキー、モデル名を設定します。 プロンプトのテンプレートの設定 template = """ 次の質問にできる限り答えてください。次のツールにアクセスできます。 - 検索ツール:検索するときに使います - 計算ツール:計算するときに使います。 次のフォーマットを使用します。 質問:回答する必要がある入力質問 ...(省略)... 質問:{question} さぁ始めましょう。 思考: """ question = "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。" message = template.format(question=question) プロンプトのテンプレートを設定します。このプロンプトは、「ReActを実現するためのプロンプト」で説明したプロンプトと同じ内容です。 `{question}` の部分には、変数 `question` の値が、 `template.format(question=question)` で代入されます。 回答の取得 final_observation = "" while True: # 推論と行動のプロセス openai_client = AzureOpenAI(azure_endpoint=aoai_endpoint, api_key=aoai_api_key, api_version = aoai_api_version) response = openai_client.chat.completions.create( model=aoai_chat_model_name, messages=[ {"role": "assistant", "content": message}, ], stop=["観察:"] ) result = response.choices[0].message.content.strip() Azure OpenAI ServiceのAPIを使って、LLMにプロンプトを与え、回答を取得します。 `stop=[“観察:”]` は、プロンプトの中で「観察:」という文字列が出力されたら、その時点で推論を終了するように指定しています。これは、LLMが自身の持っている知識で回答してしまうのを防ぐためです。ReActでは「観察:」は、あくまで行動の結果のものであり、LLMの思考の結果、出力されるものではありません。ただし、LLMは自身の持っている知識で回答し、「観察:」を出力することがあるため、それを防ぐためにこのような設定を行っています。 最終回答のチェック if "最終回答:" in result.split("\n")[-1]: final_observation = result.split("最終回答:")[1].strip() break LLMの出力結果に「最終回答:」が含まれている場合、それを `final_observation` に代入し、ループを抜けます。 ツールの実行 if "行動" in result: action = result.split("行動の入力:")[0].split("行動:")[1].strip() action_input = result.split("行動の入力:")[1].strip() observation = "" if action in "検索ツール": # 検索ツール実行 search_results = DDGS().text(action_input, max_results=5) for search_result in search_results: observation = observation + search_result["body"] + "\n" elif action in "計算ツール": # 計算ツール実行 observation = str(eval(action_input)) message = message + "\n行動:" + action + "\n行動の入力:" + action_input + "\n観察:" + observation + "\n思考:" LLMの出力結果に「行動」が含まれている場合、その内容に応じて行動を実行します。行動の内容によって、検索ツールを使うか、計算ツールを使うかを判断し、それに応じた処理を行います。検索ツールを使う場合は、DuckDuckGoを使って検索を行い、その結果を `observation` に代入します。計算ツールを使う場合は、Pythonのeval関数を使って計算を行い、その結果を `observation` に代入します。 最終的な観察結果の表示 print("\n最終的な観察:") print(final_observation) 最終的な観察結果を表示します。 このようにして、ReActを実現するためのプロンプトを使って、AIエージェントが問題を解決するプロセスを実装することができます。 Function calling 前の章では、ReActというプロンプトエンジニアリングを使って、AIエージェントを実装しました。しかし、やはり、プロンプトを書くのは面倒ですよね。ReActのプロンプトにしたがって、思考と行動を繰り返し、最終回答が得られるまで繰り返す処理を実装するのは、かなり手間がかかります。 そこで、Function callingです。Function callingを使うと、もっとシンプルなコードでAIエージェントを実装することができます。 Function callingの概要 Function callingは、あらかじめ必要な関数を登録しておき、プロンプトの内容に応じて、適切な関数を呼び出すことができる機能です。 といっても、これだけの説明ではわかりにくいかもしれません。図を交えて説明します。 まず、アプリケーションは、Azure OpenAI ServiceなどのLLMに渡すプロンプトに、関数の内容も一緒に渡します。例えば、上図では、「トム・クルーズの現在の年齢を調べて。」というユーザーメッセージを渡すと同時に、関数の定義も一緒に渡しています。 関数の定義に必要なのは、関数名、関数の内容、引数です。上図では、2つの関数を定義しています。一つは、指定したクエリでWebを検索する関数と、もう一つは、与えられた計算式に基づいて計算をする関数です。 このプロンプトをLLMに渡すと、ユーザーメッセージ(質問)の内容と、登録されている関数の内容を照らし合わせて適切な関数の情報を返してくれます。例えば、この場合は、現在のパリの天気を調べるにはWebを検索することが必要であると判断され、関数名serach_webの情報を返してくれます。そして関数に渡す引数も返してくれます。この場合「トム 」と返してくれました。 この説明から分かる通り、関数の説明は非常に重要です。これが適切でないとLLMは正しい関数を選択できません。 LLMが返してくれるのは、関数名や引数の情報だけなので、その実装はアプリケーション側で用意して上げる必要があります。上図では `search_web` (Webを検索する関数)と `calculate` (計算をする関数)の実装を示しています。LLMから返ってきた関数名と引数を使って、適切な関数を呼び出す処理を実装します。 つまり、Function callingを使うと、LLMが自分で考えて、適切な関数を選択してくれるので、より複雑な処理を簡単に実装することができます。つまりAIエージェントのような動きをしてくれるのです。 シンプルなFunction callingの実装 では、簡単なFunction callingの実装を見ていきましょう。 またもやトム・クルーズの年齢分のろうそくの合計金額を計算する問題を例に取ってみます。 「Function callingの概要」で説明した2つの関数を定義します。一つは、指定したクエリでWebを検索する関数 `search_web` 、もう一つは、与えられた計算式に基づいて計算をする関数 `calculate` です。 そして、プロンプトには「トム・クルーズの現在の年齢を調べて。」というユーザーメッセージを渡します。 想定される結果は、関数名 `search_web` の関数が実行されて、その検索結果が返ってくることです。 ではソースコードの全文になります。 import requests import json from duckduckgo_search import DDGS # Azure OpenAI Serviceの設定 AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン AOAI_API_KEY = "XXXXXXX" # Azure OpenAI ServiceのAPIキー AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名 # リクエストURL url = f"{AOAI_ENDPOINT}openai/deployments/{AOAI_CHAT_MODEL_NAME}/chat/completions?api-version={AOAI_API_VERSION}" # リクエストヘッダー headers = { "Content-Type": "application/json", "api-key": AOAI_API_KEY } # 最初のリクエストデータ: Web検索関数を自動呼び出し data = { "messages": [ {"role": "system", "content": "あなたは役立つアシスタントです。"}, {"role": "user", "content": "トム・クルーズの現在の年齢を調べて。"} ], "functions": [ { "name": "search_web", "description": "指定されたクエリでWebを検索します。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "検索するクエリ" } }, "required": ["query"] } }, { "name": "calculate", "description": "与えられた計算式に基づき計算を行います。", "parameters": { "type": "object", "properties": { "formula": { "type": "string", "description": "計算式(例: 52 * 100、100 - 1)" } }, "required": ["formula"] } } ] } # 指定されたクエリでWebを検索する関数 def search_web(query: str): final_result = "" search_results = DDGS().text(query, max_results=10) for search_result in search_results: final_result = final_result + search_result["body"] + "\n" return final_result # 与えられた計算式に基づき計算を行う関数 def calculate(formula: str): return eval(formula) # リクエストを送信 response = requests.post(url, headers=headers, data=json.dumps(data)) # 結果を出力 result = response.json() # 関数を実行する。 message = result["choices"][0]["message"]["function_call"] function_name = message["name"] arguments = json.loads(message["arguments"]) # 文字列を辞書に変換 print(f"実行された関数: {function_name}") print(f"関数に渡された引数: {arguments}") func = globals()[function_name] result = func(**arguments) print(f"関数の実行結果: {result}") では、このソースコードの詳細を見ていきましょう。 Azure OpenAI Serviceの設定 # Azure OpenAI Serviceの設定 AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン AOAI_API_KEY = "XXXXXXX" # Azure OpenAI ServiceのAPIキー AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名 Azure OpenAI Serviceにアクセスするための設定を行っています。 `AOAI_ENDPOINT` はAzureのエンドポイントURLで、これはAzureのAIサービスにアクセスするための入り口のURLです。 `AOAI_API_VERSION` は使うAPIのバージョンを指定しています。 `AOAI_API_KEY` はサービスにアクセスするための鍵のようなものです。これがないと認証できないので、セキュリティ的に重要な情報です。そして、 `AOAI_CHAT_MODEL_NAME` はどのAIモデルを使うかを指定しています。ここではGPT-4を使っていますが、他のモデルを使うこともできます。 リクエストURL url = f"{AOAI_ENDPOINT}openai/deployments/{AOAI_CHAT_MODEL_NAME}/chat/completions?api-version={AOAI_API_VERSION}" 次に、リクエストを送るためのURLを構築しています。このURLは、Azure OpenAI Serviceにアクセスし、指定したモデルを使ってチャットリクエストを処理してもらうためのものです。URLにモデル名やAPIバージョンを動的に追加しています。 リクエストヘッダー # リクエストヘッダー headers = {     "Content-Type": "application/json",     "api-key": AOAI_API_KEY } 続いて、リクエストを送るときに必要な情報を定義します。 `headers` では、リクエストの形式やAPIキーを設定しています。ここで、 `Content-Type` が `application/json` であることから、リクエストボディがJSON形式であることが分かります。 メッセージの定義 # 最初のリクエストデータ: Web検索関数を自動呼び出し data = { "messages": [ {"role": "system", "content": "あなたは役立つアシスタントです。"}, {"role": "user", "content": "トム・クルーズの現在の年齢を調べて。"} ], 次に、送信するデータ( `data` )を作成しています。この部分は、実際にGPTに対して「トム・クルーズの現在の年齢を調べて。」というリクエストを送る内容を定義しています。 `messages` の中に、「あなたは役立つアシスタントです」とAIに役割を伝え、ユーザーからの質問として「トム・クルーズの現在の年齢を調べて。」というメッセージを渡しています。 関数の定義 "functions": [ { "name": "search_web", "description": "指定されたクエリでWebを検索します。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "検索するクエリ" } }, "required": ["query"] } }, { "name": "calculate", "description": "与えられた計算式に基づき計算を行います。", "parameters": { "type": "object", "properties": { "formula": { "type": "string", "description": "計算式(例: 52 * 100、100 - 1)" } }, "required": ["formula"] } } ] `functions` の中には、関数の情報を定義しています。ここでは、 `search_web` と `calculate` の2つの関数を定義しています。それぞれの関数には、関数名、関数の説明、引数の情報が含まれています。 `search_web` 関数は、指定されたクエリでWebを検索する関数で、 `query` という引数を必要とします。 `calculate` 関数は、与えられた計算式に基づいて計算を行う関数で、 `formula` という引数を必要とします。 特に大事なのは `description` です。これは、LLMが、ユーザーの質問に基づき関数を選択する際に参照する情報です。この情報が適切でないと、LLMが正しい関数を選択できません。 `parameters` の中には、引数の情報が含まれています。 `type` は引数の型を指定しています。 `properties` の中には、引数の名前と説明が含まれています。 `required` は、必須の引数を指定しています。 `calculate` 関数の引数の `description` には、計算式の例が記載されています。これは、LLMが計算式を入力する際のフォーマットを理解しやすくするためのものです。ここできちっと例を示しておかないと、LLMが正しい計算式を入力することができません。 指定されたクエリでWebを検索する関数 # 指定されたクエリでWebを検索する関数 def search_web(query: str): final_result = "" search_results = DDGS().text(query, max_results=10) for search_result in search_results: final_result = final_result + search_result["body"] + "\n" return final_result `search_web` 関数は、指定されたクエリでWebを検索する関数です。この関数は、 `query` という引数を受け取り、そのクエリでWebを検索して、検索結果を返します。 上位10件の検索結果を取得しており、それぞれの検索結果の `body` というフィールドに格納されている本文の要約部分を取り出し、 `final_result` に追加して連結しています。 与えられた計算式に基づき計算を行う関数 # 与えられた計算式に基づき計算を行う関数 def calculate(formula: str): return eval(formula) `calculate` 関数は、与えられた計算式に基づいて計算を行う関数です。この関数は、 `formula` という引数を受け取り、その計算式を評価して結果を返します。 リクエストの送信 # リクエストを送信 response = requests.post(url, headers=headers, data=json.dumps(data)) # 結果を出力 result = response.json() リクエストを送信し、結果を取得します。 関数の実行 # 関数を実行する。 message = result["choices"][0]["message"]["function_call"] function_name = message["name"] arguments = json.loads(message["arguments"]) # 文字列を辞書に変換 print(f"実行された関数: {function_name}") print(f"関数に渡された引数: {arguments}") func = globals()[function_name] result = func(**arguments) print(f"関数の実行結果: {result}") 最後に、LLMから返ってきた関数名と引数を使って、適切な関数を呼び出しています。 `message` から関数の情報を取得しています。 `function_name` には、関数名が入っています。 `arguments` には、関数に渡す引数が入っています。 `json.loads` を使って、文字列を辞書に変換しています。 `globals()[function_name]` によって、関数名を使って関数を取得し、その関数を呼び出しています。 `**arguments` を使って、辞書を展開して、関数に引数を渡しています。結果を `result` に格納して、出力しています。 では、動かしてみましょう。実行結果は以下の通りになります。 実行された関数: search_web 関数に渡された引数: {'query': 'トム・クルーズ現在の年齢'} 関数の実行結果: トム・クルーズのサイエントロジーに対する開けた態度は、...(以下略)... バッチリですね。 では、次は、計算式を入力して、計算を行ってみましょう。「トム・クルーズの現在の年齢を調べて。」を「52 ✕ 100を計算して。」という計算式に変更して、実行してみます。あえてLLMが掛け算の記号を「*」ではなく「✕」として、LLMが正しく認識して計算できるかを確認してみます。 実行された関数: calculate 関数に渡された引数: {'formula': '52 * 100'} 関数の実行結果: 5200 バッチリですね。 `calculate` 関数が正しく実行され、計算結果が返ってきました。 Function callingでAIエージェント 実は、Function callingを使うと、ReActのようなAIエージェントを実装することもできます。 Funtion callingのすごいところは、質問(ユーザーメッセージ)から、適切な関数を選択して実行することができる点だけではなく、ReActのように、思考と行動を繰り返し、最終回答が得られるまで繰り返す処理を実装することもできる点です。つまり段取りが行えます。 与えられた質問(ユーザーメッセージ)と複数の関数から、まず最初にどの関数を実行して、次に何の関数を実行すべきなのか、そんなこともしてくれるのです。 では、Function callingによって、どのようにAIエージェントを実現するのかをイメージにしてみましょう。また例によって、トム・クルーズの年齢分のろうそくの合計金額を計算する問題を例に取ってみます。同じように2つの関数を定義します。一つは、指定したクエリでWebを検索する関数 `search_web` 、もう一つは、与えられた計算式に基づいて計算をする関数 `calculate` です。 まず、1回目の実行を見てみてください。 指定したクエリでWebを検索する関数 `search_web` 、もう一つは、与えられた計算式に基づいて計算をする関数 `calculate` の2つの関数が定義されている状態で、以下の質問がなされました。 トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。 このとき、Function callingは段取りを行います。つまり、トム・クルーズの年齢分のろうそくの合計金額を計算するためには、まずWebを検索して、トム・クルーズの年齢を調べて、その後に計算を行う必要があると判断するのです。 その結果、複数ある関数から、最初に `search_web` 関数を実行することを選択します。そして、 `search_web` 関数を実行して、その結果を返します。 次に、2回目の実行を見てみてください。 キモは、プロンプトに1回目の実行結果を含めて、再度質問をすることです。これによって、LLMは以下のように解釈します。 最初に段取りした「トム・クルーズの年齢分のろうそくの合計金額を計算するためには、まずWebを検索して、トム・クルーズの年齢を調べて、その後に計算を行う必要がある」という流れのうち、最初の段取りである「トム・クルーズの年齢を調べる」はすでに完了しているんだな。では、その実行結果を見て、トム・クルーズの現在の年齢を割り出して、次にろうそくの合計金額の計算を行おう。 つまり、LLMに1回目の実行は終わり、その結果を渡すことで、続きの段取りを行うことができるのです。 最後の実行を見てみてください。 今度は、プロンプトに1回目の実行結果に加えて、2回目の実行結果を加えています。これでLLMは、最初に段取りしたすべての工程が完了したと判断し、処理を終了します。 すごいですね、Function calling!! 重ねていいますが、Function callingは段取りまでやってくるところにその真価があります。ReActのようなAIエージェントを実装するのに非常に便利です。 Function callingによるAIエージェントの実装 それでは、Function callingを使って、AIエージェントを実装するサンプルコードを見ていきましょう。 import requests import json from duckduckgo_search import DDGS # Azure OpenAI Serviceの設定 AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン AOAI_API_KEY = "XXXXXXXX" # Azure OpenAI ServiceのAPIキー AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名 # リクエストURL url = f"{AOAI_ENDPOINT}openai/deployments/{AOAI_CHAT_MODEL_NAME}/chat/completions?api-version={AOAI_API_VERSION}" # リクエストヘッダー headers = { "Content-Type": "application/json", "api-key": AOAI_API_KEY } # 初期のメッセージ messages = [ {"role": "system", "content": "あなたは役立つアシスタントです。"}, {"role": "user", "content": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"} ] # 関数の定義 functions = [ { "name": "search_web", "description": "指定されたクエリでWebを検索します。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "検索するクエリ" } }, "required": ["query"] } }, { "name": "calculate", "description": "与えられた計算式に基づき計算を行います。", "parameters": { "type": "object", "properties": { "formula": { "type": "string", "description": "計算式(例: 52 * 100、100 - 1)" } }, "required": ["formula"] } } ] # 指定されたクエリでWebを検索する関数 def search_web(query: str): final_result = "" search_results = DDGS().text(query, max_results=10) for search_result in search_results: final_result = final_result + search_result["body"] + "\n" return final_result # 与えられた計算式に基づき計算を行う関数 def calculate(formula: str): return str(eval(formula)) # メインループ while True: # リクエストデータの準備 data = { "messages": messages, "functions": functions, "function_call": "auto" } # リクエストを送信 response = requests.post(url, headers=headers, data=json.dumps(data)) result = response.json() # アシスタントの返信を取得 assistant_message = result["choices"][0]["message"] messages.append(assistant_message) # アシスタントのメッセージを履歴に追加 # アシスタントが関数を呼び出したか確認 if "function_call" in assistant_message: function_call = assistant_message["function_call"] function_name = function_call["name"] arguments = json.loads(function_call["arguments"]) # 文字列を辞書に変換 # 関数を実行 func = globals()[function_name] function_response = func(**arguments) print(f"実行された関数: {function_name}") print(f"関数に渡された引数: {arguments}") print(f"関数の実行結果: {function_response}") # 関数の応答をメッセージに追加 messages.append({ "role": "function", "name": function_name, "content": function_response }) else: # 最終的な回答が得られた場合 final_observation = assistant_message["content"] print(f"最終回答: {final_observation}") break # ループを終了 では、このソースコードの詳細を見ていきましょう。「シンプルなFunction callingの実装」のところで説明した部分は割愛します。 メッセージの定義 messages = [ {"role": "system", "content": "あなたは役立つアシスタントです。"}, {"role": "user", "content": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"} ] 初期のメッセージを定義しています。ここでは、システムからのメッセージとユーザーからのメッセージを定義しています。システムからのメッセージは、アシスタントが役立つことを伝えるメッセージです。ユーザーからのメッセージは、トム・クルーズの年齢分のろうそくの合計金額を計算するための質問です。 関数の定義 functions = [ { "name": "search_web", "description": "指定されたクエリでWebを検索します。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "検索するクエリ" } }, "required": ["query"] } }, { "name": "calculate", "description": "与えられた計算式に基づき計算を行います。", "parameters": { "type": "object", "properties": { "formula": { "type": "string", "description": "計算式(例: 52 * 100、100 - 1)" } }, "required": ["formula"] } } ] 関数の定義を行っています。これは「シンプルなFunction callingの実装」で説明した部分と同じです。 関数の実装 # 指定されたクエリでWebを検索する関数 def search_web(query: str): final_result = "" search_results = DDGS().text(query, max_results=10) for search_result in search_results: final_result = final_result + search_result["body"] + "\n" return final_result # 与えられた計算式に基づき計算を行う関数 def calculate(formula: str): return str(eval(formula)) 関数の実装を行っています。これは「シンプルなFunction callingの実装」で説明した部分と同じです。 メインループ # メインループ while True: # リクエストデータの準備 data = { "messages": messages, "functions": functions, "function_call": "auto" } ここからが、Function CallingによるAIエージェントのキモの部分です。 メインループを定義しています。このメインループで延々と処理を繰り返すことで、AIエージェントのような動きを実現しています。最終回答が出るまで、繰り返し処理を行います。 ここでは、リクエストデータを準備しています。 `messages` には、初期のメッセージが入っています。 `functions` には、関数の定義が入っています。 `”function_call”: “auto”` は、Function Callingにおける設定の一つで、LLMに対して、自動的に関数を呼び出すかどうかを判断させるためのオプションです。この設定を使用することで、LLMは受け取ったユーザーのリクエストに基づいて、「どの関数を呼び出すべきか」を自分で判断し、適切な関数を自動で呼び出します。 リクエストの送信 # リクエストを送信 response = requests.post(url, headers=headers, data=json.dumps(data)) result = response.json() リクエストを送信して、結果を取得します。 アシスタントの返信を取得 # アシスタントの返信を取得 assistant_message = result["choices"][0]["message"] messages.append(assistant_message) # アシスタントのメッセージを履歴に追加 LLMからの回答を取得して、 `messages` に追加します。これによって、アシスタントのメッセージの履歴を保持することができます。 関数の実行 # アシスタントが関数を呼び出したか確認 if "function_call" in assistant_message: function_call = assistant_message["function_call"] function_name = function_call["name"] arguments = json.loads(function_call["arguments"]) # 文字列を辞書に変換 # 関数を実行 func = globals()[function_name] function_response = func(**arguments) print(f"実行された関数: {function_name}") print(f"関数に渡された引数: {arguments}") print(f"関数の実行結果: {function_response}") # 関数の応答をメッセージに追加 messages.append({ "role": "function", "name": function_name, "content": function_response }) else: # 最終的な回答が得られた場合 final_observation = assistant_message["content"] print(f"最終回答: {final_observation}") break # ループを終了 最初のif文 `if “function_call” in assistant_message:` は、LLMが、次の関数を呼び出すかどうかを判断するためのものです。もし、呼び出す必要があるのであれば、LLMからの返答(変数 `assistant_message` )は以下のようになっています。 { "content": None, "function_call": { "name": "search_web", "arguments": "{\"query\": \"トム・クルーズの年齢\"}" }, "role": "assistant" } 通常であれば、 `content` にはLLMからのテキストメッセージが入っています。しかし、Function callingを使う場合は、 `content` はNoneになり、そのかわり `function_call` に関数の情報が入っています。 `name` には、呼び出す関数の名前が入っています。 `arguments` には、関数に渡す引数が入っています。この引数は、文字列として格納されているので、 `json.loads` を使って辞書に変換しています。 もう呼び出す関数がない、つまり最終的な回答が得られた場合は、 `content` にテキストメッセージが入っています。この場合は、そのメッセージを出力して、ループを終了します。 { "content": "トム・クルーズは現在60歳なので、彼の年齢分のろうそくを購入するには6000円必要です。", "role": "assistant" } `function_response = func(**arguments)` は、関数を呼び出しています。そして、その結果を `messages` に追加しています。 以上で、Function callingによるAIエージェントの実装が完了です。 では、実行結果を見てみましょう。 実行された関数: search_web 関数に渡された引数: {'query': 'トム・クルーズの年齢'} 関数の実行結果: トム・クルーズのサイエントロジーに対する開けた態度は、...(以下略)... 実行された関数: calculate 関数に渡された引数: {'formula': '60 * 100'} 関数の実行結果: 6000 最終回答: トム・クルーズの年齢は60歳なので、彼の年齢分のろうそくを購入するためには6000円が必要です。 バッチリですね。Function callingによって、AIエージェントを実装することができました。 LangChainでAIエージェント 実は、LangChainもFunction callingを使って、AIエージェントを実装することができます。 LangChainは、LLMを使ったアプリケーションを簡単に構築するためのフレームワークです。特に、生成AIを用いたアプリケーションで、外部データへのアクセスや複雑なタスクの実行を必要とする場合に有用です。 AgentExecutorを使うことで、Function callingを使ったAIエージェントを簡単に実装することができます。AgentExecutorは、LangChainの中で、Function callingを使って、複数の関数を呼び出すことができる機能です。 ただ、いきなりLangChainのような便利なフレームワークを使ってしまうと、AIエージェントの仕組みがわかりにくくなってしまうかもしれません。なので、まずは、Function callingを使って、AIエージェントを実装する方法を理解してから、LangChainを使ってみるのが良いでしょう。 それでは、LangChainを使って、Function callingを使ったAIエージェントを実装してみましょう。 from langchain_openai import AzureChatOpenAI from langchain.agents import tool from langchain_community.tools import DuckDuckGoSearchRun from langchain.callbacks import StdOutCallbackHandler from langchain_core.prompts import MessagesPlaceholder, ChatPromptTemplate from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain.memory import ConversationBufferWindowMemory # Azure OpenAI Serviceの設定 AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン AOAI_API_KEY = "XXXXXXX" # Azure OpenAI ServiceのAPIキー AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名 # LLMの初期化 llm = AzureChatOpenAI( azure_endpoint=AOAI_ENDPOINT, api_key=AOAI_API_KEY, api_version=AOAI_API_VERSION, openai_api_type="azure", azure_deployment=AOAI_CHAT_MODEL_NAME) # 指定されたクエリでWebを検索する関数 @tool def search_web(query: str): """ 指定されたクエリでWebを検索します。 """ search = DuckDuckGoSearchRun() return search.run(query) # 与えられた計算式に基づき計算を行う関数 @tool def calculate(formula: str): """ 与えられた計算式に基づき計算を行います。 """ return str(eval(formula)) tools = [search_web, calculate] prompt = ChatPromptTemplate.from_messages([ ("system", "あなたは役立つアシスタントです。"), MessagesPlaceholder(variable_name="chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad") ]) agent = create_tool_calling_agent(llm, tools, prompt) exucutor = AgentExecutor( agent=agent, tools=tools, verbose=True, memory=ConversationBufferWindowMemory( return_messages=True, memory_key="chat_history", k=10 ) ) exucutor.invoke( {"input": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"}, callback_handler=StdOutCallbackHandler() ) では、このソースコードの詳細を見ていきましょう。 Azure OpenAI Serviceの設定 # Azure OpenAI Serviceの設定 AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン AOAI_API_KEY = "XXXXXXX" # Azure OpenAI ServiceのAPIキー AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名 Azure OpenAI Serviceの設定を行っています。ここでは、Azure OpenAI Serviceのエンドポイント、APIバージョン、APIキー、デプロイモデル名を定義しています。 LLMの初期化 # LLMの初期化 llm = AzureChatOpenAI( azure_endpoint=AOAI_ENDPOINT, api_key=AOAI_API_KEY, api_version=AOAI_API_VERSION, openai_api_type="azure", azure_deployment=AOAI_CHAT_MODEL_NAME) Azure OpenAI Serviceを初期化しています。ここでは、Azure OpenAI Serviceのエンドポイント、APIキー、APIバージョン、APIタイプ、デプロイモデル名を指定しています。 指定されたクエリでWebを検索する関数 # 指定されたクエリでWebを検索する関数 @tool def search_web(query: str): """ 指定されたクエリでWebを検索します。 """ search = DuckDuckGoSearchRun() return search.run(query) 関数 `search_web` を定義しています。ここでは、 `DuckDuckGoSearchRun` を使って、指定されたクエリでWebを検索しています。トム・クルーズの年齢を調べるために使います。 `@tool` デコレータを使って、関数をツールとして定義しています。これによって、LangChainが関数をFunction callingで呼び出す関数として認識することができます。 `指定されたクエリでWebを検索します。` は、関数の説明です。これは、LLMが関数を選択する際に参照する情報です。Function callingのところで説明した `description` と同じです。 与えられた計算式に基づき計算を行う関数 # 与えられた計算式に基づき計算を行う関数 @tool def calculate(formula: str): """ 与えられた計算式に基づき計算を行います。 """ return str(eval(formula)) 関数 `calculate` を定義しています。ここでは、与えられた計算式に基づいて計算を行っています。ろうそくの合計金額を計算するために使います。 `@tool` デコレータを使って、関数をツールとして定義しています。これによって、LangChainが関数をFunction callingで呼び出す関数として認識することができます。 `与えられた計算式に基づき計算を行います。` は、関数の説明です。これは、LLMが関数を選択する際に参照する情報です。Function callingのところで説明した `description` と同じです。 関数のリスト化 tools = [search_web, calculate] 関数 `search_web` と `calculate` をリストにまとめています。後ほど、AgentExecutorに渡します。 プロンプトの定義 prompt = ChatPromptTemplate.from_messages([ ("system", "あなたは役立つアシスタントです。"), MessagesPlaceholder(variable_name="chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad") ]) プロンプトを定義しています。ここでは、システムからのメッセージ、ユーザーからのメッセージ、エージェントのスクラッチパッドを定義しています。 `(“system”, “あなたは役立つアシスタントです。”)` は、システムからのメッセージを定義しています。 `MessagesPlaceholder(variable_name=”chat_history”)` は、メッセージの履歴を保持するためのものです。これによって、アシスタントが過去のメッセージを参照することができます。 `(“user”, “{input}”)` は、ユーザーからのメッセージを定義しています。 `{input}` は、ユーザーからの入力を受け取るための変数です。 `MessagesPlaceholder(variable_name=”agent_scratchpad”)` は、エージェントのスクラッチパッドを定義しています。これによって、エージェントが情報を保持することができます。 エージェントの作成 agent = create_tool_calling_agent(llm, tools, prompt) `create_tool_calling_agent` 関数を使って、エージェントを作成しています。ここでは、LLM、ツール、プロンプトを渡しています。 AgentExcecutorの定義 exucutor = AgentExecutor( agent=agent, tools=tools, verbose=True, memory=ConversationBufferWindowMemory( return_messages=True, memory_key="chat_history", k=10 ) ) `AgentExecutor` を使って、エージェントを実行しています。ここでは、エージェント、ツール、メモリを渡しています。 それぞれの引数について説明します。 `agent` には、先ほど `create_tool_calling_agent` 関数で作成したエージェントを渡しています。 `tools` には、関数 `search_web` と `calculate` をリストにまとめて渡しています。 `verbose=True` は、エージェントの実行ログを表示するためのオプションです。 `memory` には、メモリを渡しています。ここでは、 `ConversationBufferWindowMemory` を使って、メッセージの履歴を保持するためのメモリを定義しています。 `return_messages=True` は、メッセージを返すためのオプションです。 `memory_key=”chat_history”` は、メモリのキーを指定しています。 `k=10` は、メッセージの履歴を保持するためのバッファサイズを指定しています。 `chat_history` は、メッセージの履歴を保持するためのキーであり、先程のプロンプトで定義した `MessagesPlaceholder(variable_name=”chat_history”)` と対応しています。 エージェントの実行 exucutor.invoke( {"input": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"}, callback_handler=StdOutCallbackHandler() ) `invoke` メソッドを使って、エージェントを実行しています。ここでは、ユーザーからの入力を渡しています。また、 `callback_handler=StdOutCallbackHandler()` を使って、コールバックハンドラを指定しています。ここでは、 `StdOutCallbackHandler` を使って、標準出力にメッセージを表示しています。 `input` には、ユーザーからの入力を渡しています。ここでは、トム・クルーズの年齢分のろうそくの合計金額を計算するための質問を渡しています。 以上で、LangChainを使って、Function callingを使ったAIエージェントの実装が完了です。 では、実行結果を見てみましょう。 > Entering new AgentExecutor chain... まず、トム・クルーズの現在の年齢を知る必要があります。それを知るためには、インターネットで彼の誕生日を調べる必要があります。 Action: duckduckgo-search Action Input: 'Tom Cruise birth date'Tom Cruise. Thomas Cruise Mapother IV (born July 3, 1962) is an ...(以下略)... Action: duckduckgo-search Action Input: 'today's date'Details about today's date with count of days, weeks, and months, Sun and Moon cycles, Zodiac signs and holidays. ...(以下略)... Action: duckduckgo-search Action Input: 'current date'Details about today's date with count of days, weeks, and months, Sun and Moon cycles, Zodiac signs and holidays. Tuesday September 24, 2024 . ...(以下略)... Action: Calculator Action Input: {'operation': 'subtract', 'operands': [2024, 1962]}Answer: 62トム・クルーズは今年で62歳になります。次に、ろうそくの価格とトム・クルーズの年齢を掛けることで、必要なろうそくの総額を計算します。 Action: Calculator Action Input: {'operation': 'multiply', 'operands': [62, 100]}Answer: 6200トム・クルーズの年齢分のろうそくを購入するためには、6200円が必要です。 Final Answer: 6200円が必要です。 ちゃんと動きましたね!! まとめ なかなかにわかりにくいAIエージェントの仕組みについて、世界一わかりみの深い説明を行いました。AIエージェントは未来を感じますね。AIエージェントの登場により、これまで夢に見た未来が現実のものとなりつつあります。生成AIの技術を活用して、人間の介入なしに自ら思考し、自ら行動する「AIエージェント」が今、大きな注目を集めています。 AIエージェントは、複雑なタスクを人間のように処理します。与えられた課題に対して自ら考え、次に何をすべきかを判断し、行動に移します。まさに、かつて夢に見たドラえもんやターミネーターのような未来のロボットたちが、今、目の前に現れようとしているのです。 さぁ、みなさんも一緒に盛り上がりましょう!!へ(゚ω゚ *へ)ワショーィ(ノ* ゚ω゚)ノワショーィ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 世界一わかりみの深いAIエージェント first appeared on SIOS Tech. Lab .
アバター
最初に こんにちは、tinaです。 このブログでは、私が今年力をいれているプロジェクトについて紹介します。 現在、 2つのOSSの開発 を行っています。 目的は、FIAP(別名 IEEE1888, UGCCNet) というIoTに利用されるアプリケーションプロトコルをGrafanaでデータソースとして利用できるようにすることです。   以下のようなシリーズとして連載していく予定です。 【はじめてのOSS開発】Grafana FIAPデータソースプラグイン ①FIAPって何? ← 今回はこちら 【はじめてのOSS開発】Grafana FIAPデータソースプラグイン ②Go版FIAPクライアントライブラリ 【はじめてのOSS開発】Grafana FIAPデータソースプラグイン ③Grafana FIAPデータソースプラグイン 今回はFIAPというプロトコルについての説明、OSSの作成に至った経緯とOSSの詳細について説明します。 FIAPについて 概要 FIAPは、あらゆるセンサーデータと制御データを、インターネットを用いて自由に交換するためのオープンなアプリケーションプロトコルです。 ベンダーニュートラルなBEMS(Building Energy Management System)や、スマートグリッドなどのエネルギー管理システム開発に利用されることを期待して開発されました。 内部では時刻ラベル付きデータの読み書きを行っており、エネルギー管理以外のシステムにも活用することができます。 アーキテクチャ FIAPのシステムは、以下の2つで構成されます。 コンポーネント ①GW(ゲートウェイ)、②Storage(ストレージ)、③APP(アプリケーション) の3つに分類される レジストリ コンポーネント群の自動連携を司る 以下の図は、FIAPのシステムアーキテクチャ図です。 FIAP通信の形式は、大きく2つに分類され、詳細は以下の通りです。 コンポーネント間通信 WRITE手順 あるコンポーネントから別のコンポーネントに対して能動的にデータを送りつけ、書き込みを行う手順。 例) GWからStorageにセンサの測定データを送る、APPからGWに対し制御コマンドを送る FETCH手順 あるコンポーネントが別のコンポーネントに存在するデータを取得する手順。 例) APPがGWやStorageから、最新値や過去データを取得する TRAP手順 値やタイムスタンプの変化を事前にリクエストのあった他のコンポーネントに通知する 例) APPがGWに対しTRAPを設定しておくと、指定したセンサの値や時刻にGW上で変化した際に、その変化をAPPに通知する コンポーネント対レジストリ間通信 REGISTRATION手順 コンポーネント・ポイントの登録 LOOKUP手順 コンポーネント・ポイントの検索 FIAPの開発を以前行っていた方にお話しを伺ったところ、レジストリの実装は難しく、コンポーネント対レジストリ通信の実装を行わないプロジェクトも多くあったようです。 今回のOSS開発では、可視化のためにGWまたはStorageからデータを取得することができれば良いため、コンポーネント間通信のFETCH手順のみを実装します。 使用用途、使用例 FIAPはもともと、東大グリーンICTプロジェクトというスマートグリッド組織によって作成されたプロトコルです。そのため、スマートグリッドシステムに多く使われています。 東大グリーンICTプロジェクトのページにも 導入事例 があり、データの統合、可視化、制御などに利用されていることが確認できます。 さらに、植物工場や動物園の環境監視といったその他のIoT分野でも一部活用されています。 作成するGrafana FIAP Datasource Pluginについて 開発理由 今回2つのOSSを開発する理由は、既存のFIAPシステムのデータをGrafanaを使って可視化するためです。 私の出身校である木更津高専ではIoTの研究にFIAPシステム使用しており、完成物を既に千葉市動物公園でのゴリラの周辺の環境改善のための実験にテスト導入して頂いております。 千葉市動物公園様のX でもご紹介を頂きました。 開発する2つのOSSの説明 最終目的はGrafanaでFIAPのデータを収集し、可視化を実現することです。 そのために、2つのOSSを作成します。1つ目は、Grafana FIAP Datasource Pluginで、2つ目は、Go版のFIAPクライアントライブラリです。 Grafanaと開発する2つのOSSの関係は、以下の図の通りです。 Grafanaはグラフを表示するアプリで、FIAPシステムのアーキテクチャでいうAPPの役割を果たす。 Grafanaにデータを取り込むために、FIAP対応Grafanaプラグインがあり、プラグインは内部でFIAP対応Go版クライアントライブラリを呼び出す。 これらが連携し、GrafanaでFIAPデータの可視化を実現する。 ここで、なぜ2つのOSSに分けて開発を行うか説明します。 GrafanaデータソースプラグインのバックエンドはGoで開発します。そこで直接プラグインにGo言語でFIAPのFETCH手順を実装することもできます。しかしこれまで、Go言語によるFIAPの実装は存在しませんでした。 そこで、Grafanaプラグイン内でFIAPのFETCH手順を直接実装するのではなく、GoのライブラリとしてFIAPのFETCH手順を行うクライアントを作成することで、Grafana以外でGoを使ってFIAPのFetch手順を利用したいユーザーが使いまわすことができるようにしました。 最後に こちらまでお読みいただき、ありがとうございます。 次回は、Go版FIAPクライアントライブラリを紹介し、開発時の感想等も紹介していきたいと思います。よろしくお願いします。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【はじめてのOSS開発】~Grafana FIAPデータソースプラグイン~ ①FIAPって何? first appeared on SIOS Tech. Lab .
アバター
挨拶 ども!こんにちは「デモ開発」が連続で続いていて、デッドヒートの波に乗っている龍ちゃんです。一件は完了しているので、記事の共有をしておきます。最近はサムネイルの作りに凝っています。: 「生成AI×LINE」で作るチャットゲーム ちょうど開発を進めていく中で、フロントエンドの開発効率を爆上げする方法を見つけたので、リハビリがてらさくっとブログにまとめていきたいと思います。 実際にこちらの内容を 利用したリポジトリはこちら になります。 本日の内容 フロントエンドで完結するモックの作りこみについてになります。APIの実装を待つよりも先にデータ構造からデザインを確認したいときに使うことができます。 フロントエンド開発時のモックについて モックといえば、モック用にアプリケーションを構築する方法もあります。今回は、フロントエンドでAPIのモックを作成する方法について紹介していきます。API通信ライブラリとしてはaxiosを対象としています。 ディレクトリ構成 今回は、API通信部分をapiというファイル内に処理をまとめておきます。各ファイルの構造としては、以下のようなイメージです。 api +---User # API名 +---api.ts # API通信処理 +---constants.ts # 返信用定数 +---type.ts # response request Type定義 apiディレクトリ内にAPIに対応したディレクトリを作成します。各APIディレクトリには、以下の3つのファイルから構成されています。 ファイル名 責務 api.ts REST APIの場合であればまとめておくことができる constant.ts ダミーの返信用定数を保存するファイル typs.ts レスポンス リクエストのタイプ定義 環境変数で、モックと本番環境との切り替え制御を行います。api.ts内で環境変数を呼び出して判定を行い、モックの場合は constat.ts からダミーのレスポンスを呼び出して返信します。 構築方法 まずは、環境編素を用意します。 # モック使用時はtrueとして、モックではない場合false VITE_MOCK="true" constants.ts と type.ts はそれぞれ対応する情報を適宜入れてください。 api.ts では定義した情報を利用して構築します。 import axios from "axios"; import { dummyFetchEnemyResponse, dummyPostBattleResponse } from "./constants"; import { RequestPostBattleType, ResponsePostBattleType } from "./type"; import { axiosClient } from "../axiosClient"; import { sleep } from "@utilities/utilitiesLogic"; export const postBattle = async (data: RequestPostBattleType) => { // 環境変数読み込み const mockFlag = import.meta.env.VITE_MOCK as boolean; // 環境変数によってはダミーを返答する if (mockFlag) { await sleep(2000); return dummyPostBattleResponse; } // API通信の処理 const response = await axios.post<ResponsePostBattleType>("/api/battle", data); return response.data; }; 上記のコードによってモックと本番環境を環境変数によって切り分けを行うことができます。API通信のラグを再現するために特定の秒数待機するプログラムを作成しています。 export const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms)); これによって正常系のローディングを含む処理を再現することができます。 終わり さっくりとまとめ終わりました。こちらを利用することで、フロントエンドから型定義を作成することで「フロント→バック」の順番で作成することができます。 本来であれば、「バック→フロント」の順番でAPIのテストを行って開発するのがきれいです。でも、定義等をノリで作っていく場合はフロントから作り上げていく方が楽なんですよね。 というわけでおすすめしています!ではまた~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post フロントエンドで完結する簡易的なAPIモック作成について first appeared on SIOS Tech. Lab .
アバター
今回はPushgatewayを利用して、Prometheus serverから直接pullできないメトリクスを収集する方法をご紹介します。 なお、【メトリクス収集・監視】シリーズと題して他にも記事を投稿していきますので、併せてご確認ください。 Prometheus+Grafanaでメトリクスを監視する【メトリクス収集・監視】 Pushgatewayでメトリクスをプッシュする【メトリクス収集・監視】  ★本記事 Alertmanagerでアラートを通知する【メトリクス収集・監視】 Prometheusを冗長化構成(HA構成)にする方法と注意点【メトリクス収集・監視】 Pushgatewayとは ジョブ/バッチ実行など何らかの理由で生存期間の短いサーバーやアプリケーションに対して、プッシュ型のメトリクス収集を提供するコンポーネントです。 前述の生存期間の短いサーバーやアプリケーション自体がこのPushgatewayにメトリクス情報を送ることによりPushgatewayで一時的に保管が行われ、Prometheus serverがその情報の収集を行います。 目的 本記事では、任意の短命なアプリケーションからPushgatewayを介して、Prometheus serverにメトリクスを収集させる方法をご紹介します。 前提 下記の記事を参考に、Prometeus server、監視対象サーバー(Node exporter)、Grafanaが構築されていること。 Prometheus+Grafanaでメトリクスを監視する【メトリクス収集・監視】 環境 今回の構成図・検証環境サーバーは以下の通りです。 短命なアプリケーションの代わりとして、 vm-target1 から curl でメトリクスをプッシュします。 ・構成図 ※黄色背景が新たに構築するサーバー 用途 ホスト名 IPアドレス Grafanaサーバー vm-grafana1 172.24.1.5/24 Prometheusサーバー vm-prom1 172.24.1.9/24 短命アプリ vm-target1 172.24.1.10/24 Pushgateway vm-pushgw 172.24.1.10/24 上記サーバーのOS/SWは以下の通りです。 OS/SW バージョン OS Ubuntu 22.04 LTS Grafana grafana-enterprise 9.5.13 Prometheus 2.53.2 LTS Node exporter 1.8.2 Pushgateway 1.9.0 Pushgatewayの構築・動作確認 それではPushgatewayの構築を行っていきます。 ここでは特段記載のない限り vm-pushgw で操作を行います。 バイナリダウンロード・解凍 まず作業ディレクトリを作成して移動します。 # 作業ディレクトリ作成 sudo mkdir /work ; sudo chown azureuser:azureuser /work ; sudo chmod 777 /work # 移動・確認 cd /work ls -la 実行結果 azureuser@vm-pushgw:~$ sudo mkdir /work ; sudo chown azureuser:azureuser /work ; sudo chmod 777 /work azureuser@vm-pushgw:~$ azureuser@vm-pushgw:~$ cd /work azureuser@vm-pushgw:/work$ ls -la total 8 drwxrwxrwx 2 azureuser azureuser 4096 Sep 19 15:47 . drwxr-xr-x 20 root root 4096 Sep 19 15:47 .. azureuser@vm-pushgw:/work$ 次にPushgatewayのバイナリダウンロード、および、解凍をします。 # バージョン指定 VERSION = 1.9.0 # ダウンロード・確認 curl -sSOL https://github.com/prometheus/pushgateway/releases/download/v ${VERSION} /pushgateway- ${VERSION} .linux-amd64.tar.gz ls -la # 解凍・確認 tar xf pushgateway- ${VERSION} .linux-amd64.tar.gz ls -la # 解凍先ディレクトリへの移動・確認 cd pushgateway- ${VERSION} .linux-amd64/ ls -la 実行結果 azureuser@vm-pushgw:/work$ VERSION = 1.9.0 azureuser@vm-pushgw:/work$ curl -sSOL https://github.com/prometheus/pushgateway/releases/download/v ${VERSION} /pushgateway- ${VERSION} .linux-amd64.tar.gz azureuser@vm-pushgw:/work$ azureuser@vm-pushgw:/work$ ls -la total 10324 drwxrwxrwx 2 azureuser azureuser 4096 Sep 19 15:48 . drwxr-xr-x 20 root root 4096 Sep 19 15:47 .. -rw-rw-r-- 1 azureuser azureuser 10563386 Sep 19 15:48 pushgateway-1.9.0.linux-amd64.tar.gz azureuser@vm-pushgw:/work$ azureuser@vm-pushgw:/work$ tar xf pushgateway- ${VERSION} .linux-amd64.tar.gz azureuser@vm-pushgw:/work$ ls -la total 10328 drwxrwxrwx 3 azureuser azureuser 4096 Sep 19 15:49 . drwxr-xr-x 20 root root 4096 Sep 19 15:47 .. drwxr-xr-x 2 azureuser azureuser 4096 Jun 9 00:05 pushgateway-1.9.0.linux-amd64 -rw-rw-r-- 1 azureuser azureuser 10563386 Sep 19 15:48 pushgateway-1.9.0.linux-amd64.tar.gz azureuser@vm-pushgw:/work$ azureuser@vm-pushgw:/work$ cd pushgateway- ${VERSION} .linux-amd64/ azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ ls -la total 18332 drwxr-xr-x 2 azureuser azureuser 4096 Jun 9 00:05 . drwxrwxrwx 3 azureuser azureuser 4096 Sep 19 15:49 .. -rw-r--r-- 1 azureuser azureuser 11357 Jun 9 00:05 LICENSE -rw-r--r-- 1 azureuser azureuser 487 Jun 9 00:05 NOTICE -rwxr-xr-x 1 azureuser azureuser 18745578 Jun 9 00:04 pushgateway azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ Pushgatewayのインストール 今回はPushgatewayバイナリ実行用のユーザーを作成して実行することにします。 そのため、ユーザー作成と取得したバイナリを適切なディレクトリへ配置します。 まずはユーザー/グループ作成を行います。 # グループ追加 sudo groupadd prometheus # ユーザー追加・ホームディレクトリ確認 sudo useradd prometheus -g prometheus -d /var/lib/prometheus -s /usr/sbin/nologin -m sudo ls -la /var/lib/prometheus/ 実行結果 azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo groupadd prometheus azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo useradd prometheus -g prometheus -d /var/lib/prometheus -s /usr/sbin/nologin -m azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo ls -la /var/lib/prometheus/ total 20 drwxr-x--- 2 prometheus prometheus 4096 Sep 19 15:50 . drwxr-xr-x 41 root root 4096 Sep 19 15:50 .. -rw-r--r-- 1 prometheus prometheus 220 Jan 7 2022 .bash_logout -rw-r--r-- 1 prometheus prometheus 3771 Jan 7 2022 .bashrc -rw-r--r-- 1 prometheus prometheus 807 Jan 7 2022 .profile azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ 次に取得したバイナリファイルを配置していきます。 # 実行バイナリの配置・確認 sudo cp pushgateway /usr/local/bin/ ls -la /usr/local/bin/pushgateway* 実行結果 azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo cp pushgateway /usr/local/bin/ azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ ls -la /usr/local/bin/pushgateway* -rwxr-xr-x 1 root root 18745578 Sep 19 15:51 /usr/local/bin/pushgateway azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ Pushgatewayのサービス化と起動確認 こちらはオプションとなりますが、今回は systemctl コマンドで管理できるよう、Unitファイルを作成してSystemdサービス化を行います。 # Unitファイル作成 cat << "EOF" | sudo tee /etc/systemd/system/prometheus-pushgateway.service [ Unit ] Description = Prometheus PushGateway Documentation = https://github.com/prometheus/pushgateway After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/pushgateway ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target EOF 実行結果 azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ cat << "EOF" | sudo tee /etc/systemd/system/prometheus-pushgateway.service [ Unit ] Description = Prometheus PushGateway Documentation = https://github.com/prometheus/pushgateway After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/pushgateway ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target EOF [ Unit ] Description = Prometheus PushGateway Documentation = https://github.com/prometheus/pushgateway After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/pushgateway ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ それでは正常に起動することを確認するため、サービス起動を行います。 # Unitファイルの反映・ステータス確認 sudo systemctl daemon-reload sudo systemctl status prometheus-pushgateway # サービス起動・ステータス確認 sudo systemctl start prometheus-pushgateway sudo systemctl status prometheus-pushgateway # サービスの自動起動有効化 sudo systemctl enable prometheus-pushgateway 実行結果 azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo systemctl daemon-reload azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo systemctl status prometheus-pushgateway ○ prometheus-pushgateway.service - Prometheus PushGateway Loaded: loaded ( /etc/systemd/system/prometheus-pushgateway.service ; disabled ; vendor preset: enabled ) Active: inactive ( dead ) Docs: https://github.com/prometheus/pushgateway azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo systemctl start prometheus-pushgateway azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo systemctl status prometheus-pushgateway ● prometheus-pushgateway.service - Prometheus PushGateway Loaded: loaded ( /etc/systemd/system/prometheus-pushgateway.service ; disabled ; vendor preset: enabled ) Active: active ( running ) since Thu 2024-09-19 15:53:17 JST ; 3s ago Docs: https://github.com/prometheus/pushgateway Main PID: 23328 ( pushgateway ) Tasks: 6 ( limit: 2263 ) Memory: 4.7M CPU: 6ms CGroup: /system.slice/prometheus-pushgateway.service └─23328 /usr/local/bin/pushgateway Sep 19 15:53:17 vm-pushgw systemd [ 1 ] : Started Prometheus PushGateway. Sep 19 15:53:17 vm-pushgw pushgateway [ 23328 ] : ts = 2024-09-19T06:53:17.910Z caller = main.go:87 level = info msg = "starting pushgateway" version = "(version=1.9.0, branch=HEAD, revision=d1ca1a6a426126a09a21f745e8ffbaba> Sep 19 15:53:17 vm-pushgw pushgateway[23328]: ts=2024-09-19T06:53:17.911Z caller=main.go:88 level=info build_context=" ( go = go1.22.4, platform = linux/amd64, user = root@2167597b1e9c, date = 20240608-15:04:08, tags = un > Sep 19 15:53:17 vm-pushgw pushgateway [ 23328 ] : ts = 2024-09-19T06:53:17.912Z caller = tls_config.go:313 level = info msg = "Listening on" address = [ :: ] :9091 Sep 19 15:53:17 vm-pushgw pushgateway [ 23328 ] : ts = 2024-09-19T06:53:17.912Z caller = tls_config.go:316 level = info msg = "TLS is disabled." http2 = false address = [ :: ] :9091 azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ sudo systemctl enable prometheus-pushgateway Created symlink /etc/systemd/system/multi-user.target.wants/prometheus-pushgateway.service → /etc/systemd/system/prometheus-pushgateway.service. azureuser@vm-pushgw:/work/pushgateway-1.9.0.linux-amd64$ Pushgatewayへメトリクス情報のプッシュ・PushgatewayのWebUI確認 短命なアプリケーションからPushgatewayへメトリクス情報をプッシュする手段として、今回は簡易的に vm-target1 でcurlコマンドを実行して確認します。 vm-target1 でcurlコマンドを実行することでPushgatewayへPOST送信を行い、PushgatewayのWebUIでメトリクス情報を受信したことを確認します。 ここではPushgatewayのWebUI操作を除き、 vm-target1 で操作を行います。 PushgatewayのWebUIの事前確認 下記のURLでアクセスしWebUI画面が表示され、メトリクス情報が何も表示されていないことを確認します。 http://<vm-pushgwのホスト名/IPアドレス>:9091/ Pushgatewayへメトリクス情報のプッシュ ここではシンプルなメトリクスと複雑なメトリクスの2パターンのメトリクス情報をプッシュすることにします。 参考: pushgateway v1.9.0#command-line # シンプルなメトリクスのプッシュ echo "some_metric 3.14" | curl --data-binary @- http://vm-pushgw:9091/metrics/job/some_job # 複雑なメトリクスのプッシュ cat << EOF | curl --data-binary @- http://vm-pushgw:9091/metrics/job/some_job/instance/some_instance # TYPE some_metric counter some_metric2 { label = "val1" } 42 # TYPE another_metric gauge # HELP another_metric Just an example. another_metric 2398.283 EOF 実行結果 azureuser@vm-target1:~$ echo "some_metric 3.14" | curl --data-binary @- http://vm-pushgw:9091/metrics/job/some_job azureuser@vm-target1:~$ azureuser@vm-target1:~$ cat << EOF | curl --data-binary @- http://vm-pushgw:9091/metrics/job/some_job/instance/some_instance # TYPE some_metric counter some_metric2 { label = "val1" } 42 # TYPE another_metric gauge # HELP another_metric Just an example. another_metric 2398.283 EOF azureuser@vm-target1:~$ PushgatewayのWebUI確認 先ほどと同じく下記のURLでアクセスし、今度はメトリクス情報が2件表示されていることを確認します。 なお、この時点ではPushgatewayに一時保存されており、Prometheus serverにメトリクス収集されていないことにご留意ください。 http://<vm-pushgwのホスト名/IPアドレス>:9091/ Prometheus serverのメトリクス収集先にPushgatewayを追加 それではPushgatewayからメトリクスを収集するため、Prometheus serverに収集先を追加します。 そのため、ここでは vm-prom1 で操作を行います。 prometheus.ymlの変更 scrape_configs ブロックに新たに job_name ブロックを追加し、Pushgatewayである vm-pushgw の情報を下記の通り追記します。 - job_name: pushgw honor_labels: true static_configs: - targets: ['vm-pushgw:9091'] 追記後の内容確認 azureuser@vm-prom1:~$ tail -n 20 /etc/prometheus/prometheus.yml # Here it's Prometheus itself. scrape_configs: # The job name is added as a label `job=` to any timeseries scraped from this config. - job_name: "prometheus" # metrics_path defaults to '/metrics' # scheme defaults to 'http'. static_configs: - targets: [ "localhost:9090" ] - job_name: target01 static_configs: - targets: [ 'vm-target1:9100' ] - job_name: pushgw honor_labels: true static_configs: - targets: [ 'vm-pushgw:9091' ] azureuser@vm-prom1:~$ ユースケースによりますが、ここで注意する点として基本的に honor_labels: true を指定することが挙げられます。 この指定はプッシュ送信したラベルとPushgatewayが付与するラベルとで重複が発生した場合、 プッシュ送信したラベルを優先させる 指定です。 例えばinstanceラベルにはPushgateway自体を表す vm-pushgw:9091 ではなく、プッシュ送信した際に付与した値である some_instance を保持したい場合、 honor_labels: true を指定する必要があります。 Pushgatewayはメトリクス情報を中間で保持するサーバーに過ぎないため、ほとんどの場合、この例のようにはPushgatewayで付与されるラベルより、送信元(今回ではvm-target1からcurlで送信した内容)のラベルを優先することが望ましいでしょう。 参考: pushgateway v1.9.0#about-the-job-and-instance-labels prometheus.ymlの反映 変更内容を反映するためsystemctlでサービスを再起動します。 # サービス再起動・ステータス確認 sudo systemctl restart prometheus sudo systemctl status prometheus 実行結果 azureuser@vm-prom1:~$ sudo systemctl restart prometheus azureuser@vm-prom1:~$ sudo systemctl status prometheus ● prometheus.service - Prometheus Server Loaded: loaded ( /etc/systemd/system/prometheus.service ; enabled ; vendor preset: enabled ) Active: active ( running ) since Thu 2024-09-19 18:18:10 JST ; 3s ago Docs: https://prometheus.io/docs/introduction/overview/ Main PID: 2081 ( prometheus ) Tasks: 5 ( limit: 2260 ) Memory: 43.6M CPU: 150ms CGroup: /system.slice/prometheus.service └─2081 /usr/local/bin/prometheus --config.file = /etc/prometheus/prometheus.yml --web.console.templates = /etc/prometheus/consoles --web.console.libraries = /etc/prometheus/console_libraries Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.356Z caller = head.go:793 level = info component = tsdb msg = "WAL segment loaded" segment = 5 maxSegment = 6 Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.357Z caller = head.go:793 level = info component = tsdb msg = "WAL segment loaded" segment = 6 maxSegment = 6 Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.357Z caller = head.go:830 level = info component = tsdb msg = "WAL replay completed" checkpoint_replay_duration = 4.120173ms wal_replay_duration = 98.78385 > Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.360Z caller = main.go:1169 level = info fs_type = EXT4_SUPER_MAGIC Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.360Z caller = main.go:1172 level = info msg = "TSDB started" Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.360Z caller = main.go:1354 level = info msg = "Loading configuration file" filename = /etc/prometheus/prometheus.yml Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.366Z caller = main.go:1391 level = info msg = "updated GOGC" old = 100 new = 75 Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.366Z caller = main.go:1402 level = info msg = "Completed loading of configuration file" filename = /etc/prometheus/prometheus.yml totalDuration = 6.65351 > Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.367Z caller = main.go:1133 level = info msg = "Server is ready to receive web requests." Sep 19 18:18:10 vm-prom1 prometheus [ 2081 ] : ts = 2024-09-19T09:18:10.367Z caller = manager.go:164 level = info component = "rule manager" msg = "Starting rule manager..." azureuser@vm-prom1:~$ GrafanaでPushgatewayで収集したメトリクスを確認 前回の記事 で作成したデータソースに対し、 some_metric や some_metric2 、 another_metric メトリクスを指定してクエリすると、Pushgateway経由で収集されたメトリクスが表示されることを確認します。 Pushgateway利用時の注意点 最後にPushgateway利用時の注意点を挙げさせて頂きます。 Prometheus公式からアナウンスのある通り、Pushgatewayは限られた場合にのみ利用することが推奨されています。 公式HPから抜粋すると、 Pushgatewayは SPOF(単一障害点)とボトルネックになる可能性 がある NW構成の問題でPrometheus serverからpull出来ない場合、安易にPushgatewayを利用するのではなく、 NW構成または Prometheus serverの配置を見直すべき である 特定のマシン/インスタンスに 意味的に紐づかないバッチ/ジョブの結果を取得するために利用するには有用 である とある通り、非常に限定された状況下でのみ推奨されていることが分かります。 参考: Pushgatewayを使用すべきか? 上記の通りPushgatewayの利用には注意が必要であるものの、 特定の状況下では有効なため、利用シーンを見極めながらご活用ください。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Pushgatewayでメトリクスをプッシュする【メトリクス収集・監視】 first appeared on SIOS Tech. Lab .
アバター
PS/SLの佐々木です。 9/14 – 9/15にNEMTUSさんが山形県で開催していた夏合宿に参加してきました。 NEMTUSさんとはNEM(ネム)」と「Symbol(シンボル)」技術の普及や発展を促進するNPO法人です。 弊社サイオステクノロジーとはOSCというイベントの出店でご一緒させていただくことがあり、NEMTUSさん自身も定期的にイベントを行っているということでしたので、今回イベントに参加させていただくことになりました。 NEMTUSさんHP 合宿について 今回の合宿は山形県のさくらんぼ東根駅が最寄りのあずまやさんという旅館で開催されていました。私自身人生初山形でした。 合宿では複数のコンテンツが用意されていました。 ブロックチェーンを利用したサービスを体験してみる ブロックチェーンにデータを読み書きしてみる 記念トークンの発行 QA ネットワーキング またこれら以外にも会議室を24時間借りていてくださったので合宿参加メンバーで夜までいろいろな話や情報交換といった交流をする時間がほかのイベントのと比べて圧倒的に多いのが合宿の特徴でした。 各コンテンツについて 続いて各コンテンツの中身を簡単に紹介します。 ブロックチェーンを利用したサービスを体験してみる ポイントカードを発行し、ポイントを付与するお店側とポイントを受け取るユーザーの体験をそれぞれしてみるというものでした。 Ethereumと異なる点として送付したポイント(トークン)を発行者が回収する機能があったり、トークンの有効期限を設定することができたりする点に違いがあるのかなと思いました。(Symbolの規格をちゃんと確認したわけではないので正確な情報ではないかもしれません、、) ブロックチェーンにデータを読み書きしてみる こちらは実際にコーディングを行うセッションでした。 SymbolブロックチェーンではEVM系のチェーンとは異なり、nodeが用意しているAPIをSDKを使用して使うことができるものとなっており、スマートコントラクトの開発が不要です。 そのためエンジニアは今まで通りのweb2のアプリケーションの開発に集中することができます。 またnodeも公開されているためEthereumのように自分でnodeを立ててネットワークに参加したり、node as a serviceのようなものを契約する必要がないためかなり手軽に開発を始めることができます。 Symbolではnodeを公開して自分のnodeでステーキングしてもらうとnodeを公開しているユーザーにインセンティブが入る仕組みのようです(Delegate PoS) これはEVMチェーンを使っていた私にはかなり驚きでしたが、開発者目線で開発にかかるコストや準備がかなり低減されるのでいい仕組みだなと思っていました。 node listは こちら から確認できます。 実際にSymbolブロックチェーンとSDKを用いてポイントを発行したり、付与したりするところをコーディングしてみたのですが、以下のような点は非常に良いと思いました。 Transactionの管理 AggregateTransactionを使用して100個のトランザクションを送った場合すべて成功したらcommit, 一つでも失敗したらロールバック Delegate PoS こちらは先ほど軽く触れましたが、開発者が手軽に動作検証をしたい場合には非常に良い仕組みだと思いました。 APIフレンドリーな設計 こちらも先ほど触れましたが、スマートコントラクト不要でブロックチェーン上で開発できる点です。SmartContractは監査をしたり、維持管理にまだ課題が多いですが、それを意識しないのは非常に大きいと思います。 モザイク機能 ERC20のようなトークンをSymbolではモザイクというみたいです。 トークンには様々な属性(有効期限やトークン所有者とトークン移動の権限を分けるなど)が存在しており、これらをスマートコントラクトなしで実現できるのは非常によい開発体験だと思いました。 事前に用意されているAPIしか使用できないという制約が気になってはいましたが、事前にAPIが提供されている範囲内で実装が可能な要件であればかなり高速に開発できるのに加え、なかなか自分で実装すると時間がかかるマルチシグやAggregateTransactionが手軽に利用できるという点はすごく良いと思いました。 またブロックチェーン上で何か処理をするというよりも、ブロックチェーンにデータを保存することで得られる恩恵(改ざん耐性やデータの永続性)のために使用したい場合はより手軽に利用できるSymbolとは相性が良いのかなと思いました。 参加してみて 今回NEMTUSさんの合宿に参加してみて今までキャッチアップできていなかったブロックチェーンのキャッチアップやネットワーキングで技術、市場のディスカッションや情報共有をすることができ非常に有意義で楽しい時間を過ごすことができました。 合宿なので普段のイベントではあまり深い話はできなかったりしますが、時間の制約がほとんどないような状況を作れるのは合宿の良さですね。 またQAセッションでSymbolを使用している事例で気になっているものがあったので質問し、丁寧に回答していただけたり、参加者も地元の方から関西の方まで幅広くいたので、関西の方のイベントの雰囲気やローカルな話などかなり深い話ができたことが非常に良かったと思います。 最後に 今回合宿を企画していくださったNEMTUSの皆様合宿でお会いさせてていただいた方にこの場を借りて感謝申し上げます。 最後に合宿の様子の写真を載せさせていただきます。   ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 1人がこの投稿は役に立ったと言っています。 The post NEMTUS夏合宿に参加してきました first appeared on SIOS Tech. Lab .
アバター
今回はPrometheusとGrafanaを使用してメトリクスを監視する方法をご紹介します。 なお、【メトリクス収集・監視】シリーズと題して他にも記事を投稿していきますので、併せてご確認ください。 Prometheus+Grafanaでメトリクスを監視する【メトリクス収集・監視】  ★本記事 Pushgatewayでメトリクスをプッシュする【メトリクス収集・監視】 Alertmanagerでアラートを通知する【メトリクス収集・監視】 Prometheusを冗長化構成(HA構成)にする方法と注意点【メトリクス収集・監視】 目的 本記事では、メトリクス収集・監視の入門編としてPrometheus serverの導入、監視対象サーバー(Node exporter)の導入、そしてそれらのメトリクスをGrafanaで表示するための方法をご紹介します。 環境 今回の構成図・検証環境サーバーは以下の通りです。 ・構成図 用途 ホスト名 IPアドレス Grafanaサーバー vm-grafana1 172.24.1.5/24 Prometheusサーバー vm-prom1 172.24.1.9/24 監視対象サーバー(Node exporter) vm-target1 172.24.1.10/24 上記サーバーのOS/SWは以下の通りです。 OS/SW バージョン OS Ubuntu 22.04 LTS Grafana grafana-enterprise 9.5.13 Prometheus 2.53.2 LTS Node exporter 1.8.2 Prometheusサーバーの構築・動作確認 それではPrometheusサーバーの構築を行っていきます。 ここでは特段記載のない限り vm-prom1 で操作を行います。 バイナリダウンロード・解凍 まず作業ディレクトリを作成して移動します。 # 作業ディレクトリ作成 sudo mkdir /work ; sudo chown azureuser:azureuser /work ; sudo chmod 777 /work # 移動・確認 cd /work ls -la 実行結果 azureuser@vm-prom1:~$ sudo mkdir /work ; sudo chown azureuser:azureuser /work ; sudo chmod 777 /work azureuser@vm-prom1:~$ azureuser@vm-prom1:~$ cd /work azureuser@vm-prom1:/work$ ls -la total 8 drwxrwxrwx 2 azureuser azureuser 4096 Sep 18 15:47 . drwxr-xr-x 20 root root 4096 Sep 18 15:47 .. azureuser@vm-prom1:/work$ 次にPrometheusサーバーのバイナリダウンロード、および、解凍をします。 # バージョン指定 VERSION = 2.53.2 # ダウンロード・確認 curl -sSOL https://github.com/prometheus/prometheus/releases/download/v ${VERSION} /prometheus- ${VERSION} .linux-amd64.tar.gz ls -la # 解凍・確認 tar xf prometheus- ${VERSION} .linux-amd64.tar.gz ls -la # 解凍先ディレクトリへの移動・確認 cd prometheus- ${VERSION} .linux-amd64/ ls -la 実行結果 azureuser@vm-prom1:/work$ VERSION = 2.53.2 azureuser@vm-prom1:/work$ curl -sSOL https://github.com/prometheus/prometheus/releases/download/v ${VERSION} /prometheus- ${VERSION} .linux-amd64.tar.gz azureuser@vm-prom1:/work$ ls -la total 101780 drwxrwxrwx 2 azureuser azureuser 4096 Sep 18 16:06 . drwxr-xr-x 20 root root 4096 Sep 18 15:47 .. -rw-rw-r-- 1 azureuser azureuser 104212702 Sep 18 16:06 prometheus-2.53.2.linux-amd64.tar.gz azureuser@vm-prom1:/work$ azureuser@vm-prom1:/work$ tar xf prometheus- ${VERSION} .linux-amd64.tar.gz azureuser@vm-prom1:/work$ ls -la total 101788 drwxrwxrwx 3 azureuser azureuser 4096 Sep 18 16:07 . drwxr-xr-x 20 root root 4096 Sep 18 15:47 .. drwxr-xr-x 4 azureuser azureuser 4096 Aug 10 00:16 prometheus-2.53.2.linux-amd64 -rw-rw-r-- 1 azureuser azureuser 104212702 Sep 18 16:06 prometheus-2.53.2.linux-amd64.tar.gz azureuser@vm-prom1:/work$ azureuser@vm-prom1:/work$ cd prometheus- ${VERSION} .linux-amd64/ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ ls -la total 261348 drwxr-xr-x 4 azureuser azureuser 4096 Aug 10 00:16 . drwxrwxrwx 3 azureuser azureuser 4096 Sep 18 16:07 .. -rw-r--r-- 1 azureuser azureuser 11357 Aug 10 00:13 LICENSE -rw-r--r-- 1 azureuser azureuser 3773 Aug 10 00:13 NOTICE drwxr-xr-x 2 azureuser azureuser 4096 Aug 10 00:13 console_libraries drwxr-xr-x 2 azureuser azureuser 4096 Aug 10 00:13 consoles -rwxr-xr-x 1 azureuser azureuser 137838575 Aug 9 23:56 prometheus -rw-r--r-- 1 azureuser azureuser 934 Aug 10 00:13 prometheus.yml -rwxr-xr-x 1 azureuser azureuser 129735160 Aug 9 23:56 promtool azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ Prometheus serverのインストール 今回はPrometheusバイナリ実行用のユーザーを作成して実行することにします。 そのため、ユーザー作成と取得したバイナリを適切なディレクトリへ配置します。 まずはユーザー/グループ作成を行います。 # グループ追加 sudo groupadd prometheus # ユーザー追加・ホームディレクトリ確認 sudo useradd prometheus -g prometheus -d /var/lib/prometheus -s /usr/sbin/nologin -m sudo ls -la /var/lib/prometheus/ 実行結果 azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo groupadd prometheus azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo useradd prometheus -g prometheus -d /var/lib/prometheus -s /usr/sbin/nologin -m azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo ls -la /var/lib/prometheus/ total 20 drwxr-x--- 2 prometheus prometheus 4096 Sep 18 16:11 . drwxr-xr-x 41 root root 4096 Sep 18 16:11 .. -rw-r--r-- 1 prometheus prometheus 220 Jan 7 2022 .bash_logout -rw-r--r-- 1 prometheus prometheus 3771 Jan 7 2022 .bashrc -rw-r--r-- 1 prometheus prometheus 807 Jan 7 2022 .profile azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ 次に取得したバイナリ/設定ファイルを配置していきます。 # 実行バイナリの配置・確認 sudo cp prometheus promtool /usr/local/bin/ ls -la /usr/local/bin/prom* # 設定ファイル、データ格納先ディレクトリ作成・確認 sudo mkdir -p /etc/prometheus /var/lib/prometheus/data sudo chown prometheus:prometheus /var/lib/prometheus/data sudo ls -la /etc/prometheus /var/lib/prometheus/data # 設定ファイルなどの配置・確認 sudo cp -r prometheus.yml consoles console_libraries /etc/prometheus/ ls -la /etc/prometheus/ 実行結果 azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo cp prometheus promtool /usr/local/bin/ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ ls -la /usr/local/bin/prom* -rwxr-xr-x 1 root root 137838575 Sep 18 16:16 /usr/local/bin/prometheus -rwxr-xr-x 1 root root 129735160 Sep 18 16:16 /usr/local/bin/promtool azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo mkdir -p /etc/prometheus /var/lib/prometheus/data azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo chown prometheus:prometheus /var/lib/prometheus/data azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo ls -la /etc/prometheus /var/lib/prometheus/data /etc/prometheus: total 8 drwxr-xr-x 2 root root 4096 Sep 18 16:16 . drwxr-xr-x 98 root root 4096 Sep 18 16:16 .. /var/lib/prometheus/data: total 8 drwxr-xr-x 2 prometheus prometheus 4096 Sep 18 16:16 . drwxr-x--- 3 prometheus prometheus 4096 Sep 18 16:16 .. azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo cp -r prometheus.yml consoles console_libraries /etc/prometheus/ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ ls -la /etc/prometheus/ total 20 drwxr-xr-x 4 root root 4096 Sep 18 16:16 . drwxr-xr-x 98 root root 4096 Sep 18 16:16 .. drwxr-xr-x 2 root root 4096 Sep 18 16:16 console_libraries drwxr-xr-x 2 root root 4096 Sep 18 16:16 consoles -rw-r--r-- 1 root root 934 Sep 18 16:16 prometheus.yml azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ Prometheus serverのサービス化と起動確認 こちらはオプションとなりますが、今回は systemctl コマンドで管理できるよう、Unitファイルを作成してSystemdサービス化を行います。 # Unitファイル作成 cat << "EOF" | sudo tee /etc/systemd/system/prometheus.service [ Unit ] Description = Prometheus Server Documentation = https://prometheus.io/docs/introduction/overview/ After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/prometheus --config.file = /etc/prometheus/prometheus.yml --web.console.templates = /etc/prometheus/consoles --web.console.libraries = /etc/prometheus/console_libraries ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target EOF 実行結果 azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ cat << "EOF" | sudo tee /etc/systemd/system/prometheus.service [ Unit ] Description = Prometheus Server Documentation = https://prometheus.io/docs/introduction/overview/ After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/prometheus --config.file = /etc/prometheus/prometheus.yml --web.console.templates = /etc/prometheus/consoles --web.console.libraries = /etc/prometheus/console_libraries ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target EOF [ Unit ] Description = Prometheus Server Documentation = https://prometheus.io/docs/introduction/overview/ After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/prometheus --config.file = /etc/prometheus/prometheus.yml --web.console.templates = /etc/prometheus/consoles --web.console.libraries = /etc/prometheus/console_libraries ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ それでは正常に起動することを確認するため、サービス起動を行います。 # Unitファイルの反映・ステータス確認 sudo systemctl daemon-reload sudo systemctl status prometheus # サービス起動・ステータス確認 sudo systemctl start prometheus sudo systemctl status prometheus # サービスの自動起動有効化 sudo systemctl enable prometheus 実行結果 azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo systemctl daemon-reload azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo systemctl status prometheus ○ prometheus.service - Prometheus Server Loaded: loaded ( /etc/systemd/system/prometheus.service ; disabled ; vendor preset: enabled ) Active: inactive ( dead ) Docs: https://prometheus.io/docs/introduction/overview/ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo systemctl start prometheus azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo systemctl status prometheus ● prometheus.service - Prometheus Server Loaded: loaded ( /etc/systemd/system/prometheus.service ; disabled ; vendor preset: enabled ) Active: active ( running ) since Wed 2024-09-18 16:22:53 JST ; 4s ago Docs: https://prometheus.io/docs/introduction/overview/ Main PID: 19221 ( prometheus ) Tasks: 6 ( limit: 2263 ) Memory: 17.1M CPU: 44ms CGroup: /system.slice/prometheus.service └─19221 /usr/local/bin/prometheus --config.file = /etc/prometheus/prometheus.yml --web.console.templates = /etc/prometheus/consoles --web.console.libraries = /etc/prometheus/console_libraries Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.497Z caller = tls_config.go:316 level = info component = web msg = "TLS is disabled." http2 = false address = [ :: ] :9090 Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.497Z caller = head.go:793 level = info component = tsdb msg = "WAL segment loaded" segment = 0 maxSegment = 0 Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.497Z caller = head.go:830 level = info component = tsdb msg = "WAL replay completed" checkpoint_replay_duration = 32.001µs wal_replay_duration = 1.479323m > Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.499Z caller = main.go:1169 level = info fs_type = EXT4_SUPER_MAGIC Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.499Z caller = main.go:1172 level = info msg = "TSDB started" Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.499Z caller = main.go:1354 level = info msg = "Loading configuration file" filename = /etc/prometheus/prometheus.yml Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.511Z caller = main.go:1391 level = info msg = "updated GOGC" old = 100 new = 75 Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.511Z caller = main.go:1402 level = info msg = "Completed loading of configuration file" filename = /etc/prometheus/prometheus.yml totalDuration = 12.228 > Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.511Z caller = main.go:1133 level = info msg = "Server is ready to receive web requests." Sep 18 16:22:53 vm-prom1 prometheus [ 19221 ] : ts = 2024-09-18T07:22:53.511Z caller = manager.go:164 level = info component = "rule manager" msg = "Starting rule manager..." azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ sudo systemctl enable prometheus Created symlink /etc/systemd/system/multi-user.target.wants/prometheus.service → /etc/systemd/system/prometheus.service. azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ Prometheus WebUIでの確認 PrometheusにはWebUI画面が用意されているため、ここではPrometheus server自身のメトリクスが収集されており、正常に表示されることを確認します。 prometheus.ymlの確認(自分自身が収集対象であることの確認) まずprometheus.ymlでPrometheus server自身がメトリクス収集対象になっていることを確認します。 azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ tail -n 11 /etc/prometheus/prometheus.yml # A scrape configuration containing exactly one endpoint to scrape: # Here it's Prometheus itself. scrape_configs: # The job name is added as a label `job=` to any timeseries scraped from this config. - job_name: "prometheus" # metrics_path defaults to '/metrics' # scheme defaults to 'http'. static_configs: - targets: [ "localhost:9090" ] azureuser@vm-prom1:/work/prometheus-2.53.2.linux-amd64$ 上記の通りscrape_configsにジョブ定義があり、targets: [“localhost:9090”]となっていれば自分自身が収集対象となっています。 WebUIでの確認 下記のURLでアクセスしWebUI画面が表示されることを確認します。 http://<vm-prom1のホスト名/IPアドレス>:9090/ このWebUI画面ではPromQLでメトリクス情報を検索できるので、試しにprocess_cpu_seconds_totalでCPU使用率がグラフに表示されることを確認します。 なお、Prometheus serverは下記のURLを使用してメトリクスを収集しているため、ブラウザでアクセスすると取得メトリクスが表示されます。 http://<vm-prom1のホスト名/IPアドレス>:9090/metrics 監視対象サーバーの追加 このままではPrometheusサーバー自身の監視しか行っていませんので、監視対象サーバーを追加していきます。 具体的には監視対象サーバーに Node exporter を導入して監視します。 ここでは特段記載のない限り vm-target1 で操作を行います。 バイナリダウンロード・解凍 まず作業ディレクトリを作成して移動します。 # 作業ディレクトリ作成 sudo mkdir /work ; sudo chown azureuser:azureuser /work ; sudo chmod 777 /work # 移動・確認 cd /work ls -la azureuser@vm-target1:~$ sudo mkdir /work ; sudo chown azureuser:azureuser /work ; sudo chmod 777 /work azureuser@vm-target1:~$ azureuser@vm-target1:~$ cd /work azureuser@vm-target1:/work$ ls -la total 8 drwxrwxrwx 2 azureuser azureuser 4096 Sep 18 17:21 . drwxr-xr-x 20 root root 4096 Sep 18 17:21 .. azureuser@vm-target1:/work$ 次に Node exporter のバイナリダウンロード、および、解凍をします。 # バージョン指定 VERSION = 1.8.2 # ダウンロード・確認 curl -sSOL https://github.com/prometheus/node_exporter/releases/download/v ${VERSION} /node_exporter- ${VERSION} .linux-amd64.tar.gz ls -la # 解凍・確認 tar xf node_exporter- ${VERSION} .linux-amd64.tar.gz ls -la # 解凍先ディレクトリへの移動・確認 cd node_exporter- ${VERSION} .linux-amd64/ ls -la 実行結果 azureuser@vm-target1:/work$ VERSION = 1.8.2 azureuser@vm-target1:/work$ curl -sSOL https://github.com/prometheus/node_exporter/releases/download/v ${VERSION} /node_exporter- ${VERSION} .linux-amd64.tar.gz azureuser@vm-target1:/work$ azureuser@vm-target1:/work$ ls -la total 10436 drwxrwxrwx 2 azureuser azureuser 4096 Sep 18 17:23 . drwxr-xr-x 20 root root 4096 Sep 18 17:21 .. -rw-rw-r-- 1 azureuser azureuser 10676343 Sep 18 17:23 node_exporter-1.8.2.linux-amd64.tar.gz azureuser@vm-target1:/work$ azureuser@vm-target1:/work$ tar xf node_exporter- ${VERSION} .linux-amd64.tar.gz azureuser@vm-target1:/work$ ls -la total 10440 drwxrwxrwx 3 azureuser azureuser 4096 Sep 18 17:23 . drwxr-xr-x 20 root root 4096 Sep 18 17:21 .. drwxr-xr-x 2 azureuser azureuser 4096 Jul 14 20:58 node_exporter-1.8.2.linux-amd64 -rw-rw-r-- 1 azureuser azureuser 10676343 Sep 18 17:23 node_exporter-1.8.2.linux-amd64.tar.gz azureuser@vm-target1:/work$ azureuser@vm-target1:/work$ cd node_exporter- ${VERSION} .linux-amd64/ azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ ls -la total 20048 drwxr-xr-x 2 azureuser azureuser 4096 Jul 14 20:58 . drwxrwxrwx 3 azureuser azureuser 4096 Sep 18 17:23 .. -rw-r--r-- 1 azureuser azureuser 11357 Jul 14 20:57 LICENSE -rw-r--r-- 1 azureuser azureuser 463 Jul 14 20:57 NOTICE -rwxr-xr-x 1 azureuser azureuser 20500541 Jul 14 20:54 node_exporter azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ Node exporterのインストール Prometheus server 同様、バイナリ実行用のユーザーを作成して実行することにします。 そのため、ユーザー作成と取得したバイナリを適切なディレクトリへ配置します。 まずはユーザー/グループ作成を行います。 # グループ追加 sudo groupadd prometheus # ユーザー追加・ホームディレクトリ確認 sudo useradd prometheus -g prometheus -d /var/lib/prometheus -s /usr/sbin/nologin -m sudo ls -la /var/lib/prometheus/ 実行結果 azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo groupadd prometheus azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo useradd prometheus -g prometheus -d /var/lib/prometheus -s /usr/sbin/nologin -m azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo ls -la /var/lib/prometheus/ total 20 drwxr-x--- 2 prometheus prometheus 4096 Sep 18 17:25 . drwxr-xr-x 41 root root 4096 Sep 18 17:25 .. -rw-r--r-- 1 prometheus prometheus 220 Jan 7 2022 .bash_logout -rw-r--r-- 1 prometheus prometheus 3771 Jan 7 2022 .bashrc -rw-r--r-- 1 prometheus prometheus 807 Jan 7 2022 .profile azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ 次に取得したバイナリを配置していきます。 # 実行バイナリの配置・確認 sudo cp node_exporter /usr/local/bin/ ls -la /usr/local/bin/node* 実行結果 azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo cp node_exporter /usr/local/bin/ azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ ls -la /usr/local/bin/node* -rwxr-xr-x 1 root root 20500541 Sep 18 17:27 /usr/local/bin/node_exporter azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ Node exporterのサービス化と起動確認 こちらはオプションとなりますが、今回は systemctl コマンドで管理できるよう、Unitファイルを作成してSystemdサービス化を行います。 # Unitファイル作成 cat << "EOF" | sudo tee /etc/systemd/system/prometheus-node-exporter.service [ Unit ] Description = Prometheus Node Exporter Documentation = https://github.com/prometheus/node_exporter After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/node_exporter ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target EOF 実行結果 azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ cat << "EOF" | sudo tee /etc/systemd/system/prometheus-node-exporter.service [ Unit ] Description = Prometheus Node Exporter Documentation = https://github.com/prometheus/node_exporter After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/node_exporter ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target EOF [ Unit ] Description = Prometheus Node Exporter Documentation = https://github.com/prometheus/node_exporter After = network-online.target [ Service ] User = prometheus WorkingDirectory = /var/lib/prometheus ExecStart = /usr/local/bin/node_exporter ExecStop = /bin/kill -TERM ${MAINPID} ExecReload = /bin/kill -HUP ${MAINPID} [ Install ] WantedBy = multi-user.target azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ それでは正常に起動することを確認するため、サービス起動を行います。 # Unitファイルの反映・ステータス確認 sudo systemctl daemon-reload sudo systemctl status prometheus-node-exporter # サービス起動・ステータス確認 sudo systemctl start prometheus-node-exporter sudo systemctl status prometheus-node-exporter # サービスの自動起動有効化 sudo systemctl enable prometheus-node-exporter 実行結果 azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo systemctl daemon-reload azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo systemctl status prometheus-node-exporter ○ prometheus-node-exporter.service - Prometheus Node Exporter Loaded: loaded ( /etc/systemd/system/prometheus-node-exporter.service ; disabled ; vendor preset: enabled ) Active: inactive ( dead ) Docs: https://github.com/prometheus/node_exporter azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo systemctl start prometheus-node-exporter azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo systemctl status prometheus-node-exporter ● prometheus-node-exporter.service - Prometheus Node Exporter Loaded: loaded ( /etc/systemd/system/prometheus-node-exporter.service ; disabled ; vendor preset: enabled ) Active: active ( running ) since Wed 2024-09-18 17:29:58 JST ; 3s ago Docs: https://github.com/prometheus/node_exporter Main PID: 20164 ( node_exporter ) Tasks: 3 ( limit: 2263 ) Memory: 4.6M CPU: 6ms CGroup: /system.slice/prometheus-node-exporter.service └─20164 /usr/local/bin/node_exporter Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.327Z caller = node_exporter.go:118 level = info collector = time Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.327Z caller = node_exporter.go:118 level = info collector = timex Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.327Z caller = node_exporter.go:118 level = info collector = udp_queues Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.328Z caller = node_exporter.go:118 level = info collector = uname Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.328Z caller = node_exporter.go:118 level = info collector = vmstat Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.328Z caller = node_exporter.go:118 level = info collector = watchdog Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.328Z caller = node_exporter.go:118 level = info collector = xfs Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.328Z caller = node_exporter.go:118 level = info collector = zfs Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.328Z caller = tls_config.go:313 level = info msg = "Listening on" address = [ :: ] :9100 Sep 18 17:29:58 vm-target1 node_exporter [ 20164 ] : ts = 2024-09-18T08:29:58.328Z caller = tls_config.go:316 level = info msg = "TLS is disabled." http2 = false address = [ :: ] :9100 azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ sudo systemctl enable prometheus-node-exporter Created symlink /etc/systemd/system/multi-user.target.wants/prometheus-node-exporter.service → /etc/systemd/system/prometheus-node-exporter.service. azureuser@vm-target1:/work/node_exporter-1.8.2.linux-amd64$ Prometheus serverのメトリクス収集先の追加 監視対象サーバー vm-target1 に Node exporter を導入したため、 Prometheus server のメトリクス収集先に vm-target1 を追加します。 そのため、ここでは vm-prom1 で操作を行います。 prometheus.ymlの変更 scrape_configs ブロックに新たに job_name ブロックを追加し、新たなメトリクス収集先である vm-target1 の情報を下記の通り追記します。 - job_name: target01 static_configs: - targets: ['vm-target1:9100'] 追記後の内容確認 azureuser@vm-prom1:/work$ tail -n 20 /etc/prometheus/prometheus.yml rule_files: # - "first_rules.yml" # - "second_rules.yml" # A scrape configuration containing exactly one endpoint to scrape: # Here it's Prometheus itself. scrape_configs: # The job name is added as a label `job=` to any timeseries scraped from this config. - job_name: "prometheus" # metrics_path defaults to '/metrics' # scheme defaults to 'http'. static_configs: - targets: [ "localhost:9090" ] - job_name: target01 static_configs: - targets: [ 'vm-target1:9100' ] azureuser@vm-prom1:/work$ prometheus.ymlの反映 変更内容を反映するためsystemctlでサービスを再起動します。 # サービス再起動・ステータス確認 sudo systemctl restart prometheus sudo systemctl status prometheus 実行結果 azureuser@vm-prom1:/work$ sudo systemctl restart prometheus azureuser@vm-prom1:/work$ sudo systemctl status prometheus ● prometheus.service - Prometheus Server Loaded: loaded ( /etc/systemd/system/prometheus.service ; enabled ; vendor preset: enabled ) Active: active ( running ) since Wed 2024-09-18 17:44:29 JST ; 5s ago Docs: https://prometheus.io/docs/introduction/overview/ Main PID: 19459 ( prometheus ) Tasks: 5 ( limit: 2263 ) Memory: 27.5M CPU: 70ms CGroup: /system.slice/prometheus.service └─19459 /usr/local/bin/prometheus --config.file = /etc/prometheus/prometheus.yml --web.console.templates = /etc/prometheus/consoles --web.console.libraries = /etc/prometheus/console_libraries Sep 18 17:44:29 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:29.975Z caller = head.go:793 level = info component = tsdb msg = "WAL segment loaded" segment = 0 maxSegment = 1 Sep 18 17:44:29 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:29.976Z caller = head.go:793 level = info component = tsdb msg = "WAL segment loaded" segment = 1 maxSegment = 1 Sep 18 17:44:29 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:29.976Z caller = head.go:830 level = info component = tsdb msg = "WAL replay completed" checkpoint_replay_duration = 47.201µs wal_replay_duration = 29.824796 > Sep 18 17:44:29 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:29.978Z caller = main.go:1169 level = info fs_type = EXT4_SUPER_MAGIC Sep 18 17:44:29 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:29.978Z caller = main.go:1172 level = info msg = "TSDB started" Sep 18 17:44:29 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:29.978Z caller = main.go:1354 level = info msg = "Loading configuration file" filename = /etc/prometheus/prometheus.yml Sep 18 17:44:30 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:30.003Z caller = main.go:1391 level = info msg = "updated GOGC" old = 100 new = 75 Sep 18 17:44:30 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:30.004Z caller = main.go:1402 level = info msg = "Completed loading of configuration file" filename = /etc/prometheus/prometheus.yml totalDuration = 25.263 > Sep 18 17:44:30 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:30.004Z caller = main.go:1133 level = info msg = "Server is ready to receive web requests." Sep 18 17:44:30 vm-prom1 prometheus [ 19459 ] : ts = 2024-09-18T08:44:30.004Z caller = manager.go:164 level = info component = "rule manager" msg = "Starting rule manager..." azureuser@vm-prom1:/work$ Prometheus WebUIでの確認 ここでは新しく追加した Node exporter を導入した vm-target1 のメトリクス収集が行われていることを、 Prometheus server のWebUI画面にて確認します。 WebUIでの確認 下記のURLでアクセスし、PromQLのprocess_cpu_seconds_totalでCPU使用率をグラフに表示します。 http://<vm-prom1のホスト名/IPアドレス>:9090/ そうすると、 Node exporter を導入した vm-target1 のメトリクスも収集・表示されていることが分かります。 なお、Prometheus serverと同様に、Node exporterを導入したサーバーでは下記のURLを公開しており、それにPrometheus serverがアクセスしてメトリクスを収集しているため、下記URLをブラウザでアクセスすると取得メトリクスが表示されます。 http://<vm-target1のホスト名/IPアドレス>:9100/metrics Grafanaの構築・メトリクス表示 それでは、いよいよGrafanaでメトリクス表示していきます。 Grafanaの構築 Grafanaのインストールについては、簡易ではありますが以前書いたブログをご参照ください。 Grafana利用DBをSQLiteからPostgreSQLに変更する【Grafana運用管理】#Grafanaの構築 Grafanaでメトリクス表示 Prometheusデータソースの作成 下記の通り、Prometheusデータソースの設定を行い、 Save & test で正常に保存されることを確認してください。 Name:任意の名前を付けてください。 HTTP > URL:http://<vm-prom1のホスト名/IPアドレス>:9090   ダッシュボード作成・メトリクス表示 下記の通り、先ほど作成したデータソースを対象にダッシュボードを作成してメトリクスが表示されることを確認します。(ここでは今までと同じprocess_cpu_seconds_totalを表示します。) 終わりに いまやメトリクス収集・監視といえばPrometheusがデファクトスタンダードと言っても過言ではないですが、 本番運用、特に冗長化やHA構成にする場合は色々と考慮点が必要になってきます。 今後、GrafanaファミリーのMimirと絡めて、その辺りもご紹介できればと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Prometheus+Grafanaでメトリクスを監視する【メトリクス収集・監視】 first appeared on SIOS Tech. Lab .
アバター
こんにちは! 今月も「OSSのサポートエンジニアが気になった!OSSの最新ニュース」をお届けします。 Linux Foundation は、Linuxシステム管理に必要なスキルとプロセスを学習するオンラインコース「Linux System Administration Essentials (LFS207)」の日本語版「Linuxシステム管理基礎 (LFS207-JP)」の提供を開始しました。 Linuxシステム管理トレーニングをリニューアル「Linuxシステム管理基礎」提供開始 https://prtimes.jp/main/html/rd/p/000000296.000042042.html 2024/9/11、Adobe Acrobat Reader における脆弱性に関する情報 (APSB24-70) が公開されました。 攻撃者による悪意のある PDF を開くと、任意のコードが実行される可能性があります。 Adobe AcrobatおよびReaderの脆弱性(APSB24-70)に関する注意喚起 https://www.jpcert.or.jp/at/2024/at240018.html Linux 財団は、分散型エコシステム向けのオープンソースを統括する組織として「リナックス財団分散型信頼(LFDT)」を設立しました。 リナックス、ヘデラと100人以上のメンバーで分散型財団を設立 https://jp.cointelegraph.com/news/linux-foundation-decentralized-trust-hedera-hyperledger ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2024年9月】OSSサポートエンジニアが気になった!OSS最新ニュース first appeared on SIOS Tech. Lab .
アバター
今回は、 パッケージマネージャ について解説します! パッケージマネージャとは パッケージマネージャとは、システムにインストールされているソフトウェア (パッケージ) を リポジトリ と呼ばれるオンラインデータベース上で管理するためのツールです。 パッケージマネージャの主な目的はソフトウェアのインストール、アップグレード、削除を行うことですが、中でも パッケージの依存関係を自動的に解決してくれる ところがメリットと言えます。 代表的なパッケージマネージャは、Red Hat 系システムだと yum と dnf 、Debian 系システムだと apt があります。 今号では Red Hat 系の yum 、 dnf についてご紹介します。 yum と dnf の違いは? Red Hat 系システムでは yum と dnf、2つのコマンドが用意されています。 yum は以前から使用されていたコマンドであり、多くの Linux ユーザにとって馴染み深いコマンドです。それに対して dnf は yum の後継として開発され、様々な改善が取り入れられています。 細かな違いとしては、それぞれ下記の通りとなっています。 yum は Python2 に基づいて、dnf は Python3 に基づいて開発されている yum は定期的に不要なキャッシュを手動でクリアする必要があるが、dnf は自動的にクリアする機能が実装されている dnf は実装の改善により、パフォーマンスが向上している なお、Fedora22 (RHEL8 系) 以降では標準のパッケージマネージャが yum から dnf に変更されています。yum と dnf では、サブコマンドやオプションも同じものが使用可能であるため、yum を使い慣れたユーザでも dnf に移行しやすい設計になっています。 基本の操作方法 (検索、インストール、アップデート、削除) yum、dnf コマンドの基本操作 (パッケージの検索、インストール、アップデート、削除) について、それぞれ説明します。 なお、上にも記載の通りサブコマンドやオプションは同じとなるため、dnf コマンドの実行例のみ掲載しています。 検索 特定のパッケージを検索するには、 dnf search コマンドを実行します。 例えば、git というパッケージを検索する場合は、下記の様なコマンドになります。 # dnf search git … ============================================== Name Exactly Matched: git =============================================== git.x86_64 : Fast Version Control System ============================================= Name & Summary Matched: git ============================================== git-all.noarch : Meta-package to pull in all git tools git-clang-format.i686 : Integration of clang-format for git … (長いため省略) … ================================================== Name Matched: git =================================================== kacst-digital-fonts.noarch : Fonts for arabic from arabeyes project ================================================= Summary Matched: git ================================================= LibRaw.x86_64 : Library for reading RAW files obtained from digital photo cameras LibRaw.i686 : Library for reading RAW files obtained from digital photo cameras … この時、結果には git という文字列を含むパッケージすべてと、要約 (説明文) が表示されます。 yum コマンドの場合は、下記の様になります。 # yum search git インストール パッケージをインストールするには、 dnf install コマンドを実行します。 例えば、git というパッケージをインストールする場合は、下記の様なコマンドになります。 # dnf install git … ======================================================================================================================== Package Architecture Version Repository Size ======================================================================================================================== Installing: git x86_64 2.43.5-1.el8_10 rhel-8-appstream-rhui-rpms 92 k Installing dependencies: emacs-filesystem noarch 1:26.1-11.el8 rhel-8-baseos-rhui-rpms 70 k git-core x86_64 2.43.5-1.el8_10 rhel-8-appstream-rhui-rpms 11 M git-core-doc noarch 2.43.5-1.el8_10 rhel-8-appstream-rhui-rpms 3.1 M perl-Error noarch 1:0.17025-2.el8 rhel-8-appstream-rhui-rpms 46 k perl-Git noarch 2.43.5-1.el8_10 rhel-8-appstream-rhui-rpms 79 k perl-TermReadKey x86_64 2.37-7.el8 rhel-8-appstream-rhui-rpms 40 k Transaction Summary ======================================================================================================================== Install 7 Packages Total download size: 14 M Installed size: 46 M Is this ok [y/N]: この時、指定したパッケージのみではなく git と依存関係があるパッケージも併せてインストールされます。 なお、上記の様にインストールを開始する前に Is this ok [y/N]: の文字列が表示され、インストールを続行するかを聞かれます。 y を押下するとインストール続行、N (もしくは n) を押下するとインストールを中止 します。 yum コマンドの場合は、下記の様になります。 # yum install git アップデート パッケージをアップデートするには、 dnf update コマンドを実行します。 例えば、既にインストール済みの git というパッケージをアップデートする場合は、下記の様なコマンドになります。 # dnf update git … ======================================================================================================================== Package Architecture Version Repository Size ======================================================================================================================== Upgrading: git x86_64 2.43.5-1.el8_10 rhel-8-appstream-rhui-rpms 92 k git-core x86_64 2.43.5-1.el8_10 rhel-8-appstream-rhui-rpms 11 M git-core-doc noarch 2.43.5-1.el8_10 rhel-8-appstream-rhui-rpms 3.1 M perl-Git noarch 2.43.5-1.el8_10 rhel-8-appstream-rhui-rpms 79 k Transaction Summary ======================================================================================================================== Upgrade 4 Packages Total download size: 14 M Is this ok [y/N]: yum コマンドの場合は、下記の様になります。 # yum update git 削除 パッケージをシステム上から削除するには、 dnf remove コマンドを実行します。 例えば、既にインストール済みの git というパッケージを削除する場合は、下記の様なコマンドになります。 # dnf remove git … ======================================================================================================================== Package Architecture Version Repository Size ======================================================================================================================== Removing: git x86_64 2.27.0-1.el8 @rhel-8-appstream-rhui-rpms 368 k Removing unused dependencies: emacs-filesystem noarch 1:26.1-11.el8 @rhel-8-baseos-rhui-rpms 0 git-core x86_64 2.27.0-1.el8 @rhel-8-appstream-rhui-rpms 32 M git-core-doc noarch 2.27.0-1.el8 @rhel-8-appstream-rhui-rpms 12 M perl-Error noarch 1:0.17025-2.el8 @rhel-8-appstream-rhui-rpms 70 k perl-Git noarch 2.27.0-1.el8 @rhel-8-appstream-rhui-rpms 63 k perl-TermReadKey x86_64 2.37-7.el8 @rhel-8-appstream-rhui-rpms 65 k Transaction Summary ======================================================================================================================== Remove 7 Packages Freed space: 45 M Is this ok [y/N]: yum コマンドの場合は、下記の様になります。 # yum remove git yum、dnf コマンドについての補足事項 dnf (yum) install コマンド実行時に対象パッケージが既にインストール済みである場合はアップデートが実行され、反対に dnf (yum) update コマンド実行時に対象パッケージがインストールされていない場合はインストールが実行されます。 パッケージ名の後ろに バージョン指定がない場合は、リポジトリ内での最新バージョンがインストールまたはアップデートされます。 明示的にインストール・アップデート先のバージョンを指定したい場合は パッケージ名の後にバージョンを指定 (例:dnf install git-2.27.0-1.el8 など) します。 -y オプション (例: dnf install -y など) を付けると、 Is this ok [y/N]: が表示されず直ちに処理が開始されます。ただし、 依存関係があるパッケージなどが事前に確認できなかったり、意図しないパッケージのインストール、アップデート、削除が行われる可能性があるので、利用は推奨しません。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 知っておくとちょっと便利!パッケージマネージャについて1 ~Red Hat 系システム編~ first appeared on SIOS Tech. Lab .
アバター