TECH PLAY

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

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

621

はじめに ども!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 .
始めに ども!今月はデモづくりに追われている龍ちゃんです。先日、学生さん数名とお話する機会があったのですが、自分が学生の時よりもはるかに能動的な学生に圧倒されてしまいました。いや~素晴らしいですね。そんな学生さんのためにも大急ぎでブログを執筆しておきます。 もし、間違いを見つけたらSNSでもメールでも問い合わせください 単純に疑問の場合でも遠慮なく問い合わせしてもらって大丈夫ですよ”(-“”-)” さて、今回は「生成AI×LINE」で遊べるゲームを作ったので、その報告と軽めの説明を垂れ流していこうと思います。 LINE内のブラウザで立ち上げて遊ぶゲームとなっています。アプリのうっすいところから実装で苦労した部分など、実現するにあたっての道のりを記載していこうと思います。 「生成AI×LINE」で作るチャットゲーム アプリの構想は、「持ち運びしやすいデモ」から始まりました。デモを魅せに行くにあたって必要なものとしては、説明資料とQRコードになります。友達に送るのであれば共有もしやすいので、一石二鳥か三鳥ぐらいありそうです。 ゲームコンセプト ゲームのコンセプトとしては、ユーザーが作成したキャラクターと事前に用意したキャラクターでの「プロンプト戦闘」になります。生成AIを根幹に組み込んでいるエンタメアプリケーションとなっています。 対戦キャラクターは事前に生成しておき、ユーザーの入力をトリガーにして「審判プロンプト」によって勝敗が決します。 アーキテクト・使用技術 やはりデプロイしないと共有って難しいですよね。なるべく早くlocalhostから抜け出すと世界が広がります。 アーキテクト 今回作成したアプリケーションの設計はAzure寄せたシンプルな構成になっています。 それぞれのデプロイ先としては以下のような内訳となっています。 名称 リンク フロントエンド Azure Static Web Apps バックエンド Azure Web Apps データベース Firebase Firestore AIサービス Azure OpenAI Service 理想的には、ログも含めてAzure上に寄せたかったのですがデータベース周りは高いのでさくっと使えるFirestoreを使用しています。 認証にはLIFFアプリの認証を使用しています。こちらを利用することで、LINE内のブラウザから立ち上げれば、LINEのアカウント情報での認証が完了した状態でアクセスすることができます。 大変な認証処理をフロントのみで完結することができる! 「Azure OpenAI Service 」では、GPT-4oとGPT-3.5-Turboのモデルをそれぞれ利用しています。なぜ?二つのモデルを利用しているのかについては「プロンプト」で触れています。 使用技術 フロントエンドとしては、以下のライブラリを使用しています。 ライブラリ名 説明 React もう素のHTMLを書くのなんて信じられない React-Hook-Form フォームを作成のため axios ブラウザからのhttpリクエスト用 SWR データfetch用 再取得やローディング中の表現が楽 @line/liff JSでLIFFアプリを作成するために必要 Tailwind CSSのフレームワーク CSSを各効率が爆上がりします lottie アニメーションを簡単に表示してくれる 主要なものを上げています。開発効率のためにESlintやPrettierなどのコード成型用ライブラリを入れています。開発環境の構築にはViteを使用しています。 バックエンドとしては、フレームワーク:nest.jsを使用して構築しています。追加で入れたライブラリとしては、以下の用の内容になります。 ライブラリ名 説明 firebase-admin firebaseにアクセスしてデータの入出力に使用 openai Azure OpenAI Serviceにアクセスするためのライブラリ 主に外部のサービスにアクセスするのを助けてくれるライブラリを導入しています。nest.jsは環境構築をした際に、開発に必要なコード成型は入っている状態なのですぐ開発を始めることができます。 バックエンドの環境は、データベースにFireStoreを採用するまでの名残でDockerで構築しており、ローカル開発環境はDevContainerを使用して作成しています。 フロントエンドとバックエンドに共通しているのは、どちらもGitHub ActionsでCI/CDを実装している点です。どちらも簡易ですが、mainリポジトリに変更をpushすることで自動でビルドが走り。それぞれの環境にデプロイが処理されます。 プロンプト さて、今回の成果物の根幹にかかわる部分の解説に移っていきます。今回作成したプロンプトとしては、「審判プロンプト」と「JSON整形プロンプト」の2つになります。 審判プロンプト モデルベースとしては、「Gpt-4o」を利用しています。 こちらのプロンプトが目標としているのは、主に以下の2つになります。 ユーザーとAI側のキャラクターの勝敗を判定する 戦闘の記録をダイナミックに脚色する 事前入力としては「AI側のキャラクター情報」を与えます。また、ここでJSON形式のひな形を作成します。返答としては、脚色された情報とJSONのような文字列が返答されます。 JSONの返答例を与えることで、JSON形式の返答率は上がります。ですが、GPT-4oはJSONでの出力を守ってくれないという悪癖があります。なので、ここでは文字列の情報としてJSON情報を受け取ります。 プロンプトの全文です。 あなたは決闘の審判です。二つのキャラクターの戦闘を見守り、勝敗までの流れを判定してください。 AI側がチャンピオン、ユーザー側が挑戦者です。 次の内容は必ず守ってください「チャンピオンのキャラクターが勝利した場合はsystem、挑戦者が勝利した場合はuserと明記してください。」 --- ${enemyPrompt} --- 以下のType出力を守った内容を最後に付録として記載してください。 --- { "combatLogs": { "round":number, "combatLog":string }[] } --- 例は以下のようになります。combatLogは小説家のように過大に脚色して演出してください。決闘の勝者を明確にしてください。 --- { "combatLogs": [ { "round": 1, "combatLog": "訓練場の教官が鉄の剣で攻撃しました" }, { "round": 2, "combatLog": "訓練場の教官が鉄の盾で防御しました" } ] } --- JSON整形プロンプト モデルベースとしては、「GPT-3,5-Turbo」を利用しています。 こちらのプロンプトが目標としているのは、「APIのレスポンスとして返すためのJSON作成」になります。 入力としては、「審判プロンプト」で作成された結果を入力します。また、文章中から勝者を判定してJSONの形式に出力を制限しています。 プロンプトの全文です。 - 出力をJSON形式にしてフォーマットとしては以下のサンプルに従ってください。 - 以下の形式のJSON以外は出力しないでください。 - AI側がチャンピオン、ユーザー側が挑戦者です。 - winnerには挑戦者が買った場合は「user」、チャンピオン側が買った場合は「system」を入力してください。 - winnerには「user」か「system」しか入力しないでください。 { "winner":"user"|"system", "combatLogs": { "round":number, "combatLog":string }[] } なぜ二つのプロンプトを併用しているのか? これは、文章中にも触れましたが「Gpt-4o」がJSON形式の返答を得意としていないという悪癖のせいです。参考情報としては、こちらを参考にしてもらえればと思います。 2024-08-02 AOAI:Gpt-4oでJSON出力に失敗する対症療法 新規の情報作成ではなく、「入力値から特定のフォーマットに変換する」というシンプルなタスクを「GPT-3.5-Turbo」にお任せすることで「GPT-4o」の不得意な部分をカバーしています。 実装 具体的な実装内容については、ある程度ソースが読める方はそれぞれのリポジトリ進んでいただいて読んでもらえる方が手っ取り早いかもしれません。フロントエンド・バックエンドのそれぞれのリポジトリを置いておきます。 名称 リンク フロントエンド https://github.com/Ryunosuke-Tanaka-sti/2024-line-liff-app-frontend バックエンド https://github.com/Ryunosuke-Tanaka-sti/2024-line-liff-app-backend 仕様書はREADMEに記載しようかと考えています。頑張ってメンテナンスします。 ここでは、実装面に気を付けていたことについて列挙していきます。 LIFFアプリを作成することで気を付ける点 こちらの記事でまとめています。 nest.jsでLIFFアプリのトークンをセキュアに運用する フロント側でユーザー情報を取得することができます。ですが、 バックエンドにその情報をそのまま送付することは禁止されています 。そのため、フロントエンドではアクセストークンをヘッダーに埋め込んで、バックエンド側でそれを検証することでユーザー情報を取得しています。 プロンプトで処理しやすいように分割する こちらは、プロンプトのテクニックのお話になります。今回は「ストーリー作成」と「返答用JSON作成」の2つで分割しました。こちらに関してもテクニックの1つになります。 「審判プロンプト」と「JSON整形プロンプト」で登場するJSONの型は絶妙に違います。これは、「審判プロンプト」内で直接JSONを作成するよりも、生成された文章から勝者を類推するほうが精度が高かったからです。 このようにプロンプトフレンドリーに処理を分割することは結構苦労しました。 フロントを作ってからバックを作る 急に開発の話ですが、本来なら「バックエンドのAPIを作って、テストしてからフロントの画面を作っていく」が正しい順番だと思います。今回は、時間がなかった+開発者独りだったのでフロントを書きながら表示に必要な情報の構築を合わせてしていました。 詳しくは、別記事で書きますが環境変数で「MOCK」というフラグを立てた時は、定数を返答するようにコードを作成することで開発工程を3つほどスキップすることができました。 おわり ども!お疲れ様です。久しぶりの執筆と開発で結構疲れがありますが、ここ数週間は充実した日々でしたね。やはり定期的に新しいものを作る必要があります。 この記事でカバーできていない部分などは全然質問に答えますので気軽に問い合わせしてください。執筆者情報のところにXのリンクが張ってあります。 次作るデモはロボット動かします!お楽しみに~ ではまた! なぜプラットフォームにLINEを使用しているのか? しれっとLINEを使う方向で話を進めていますが、このアプリがLINEを使用して進めているかについて触れていこうと思います。LINEを採用している理由としては、以下の三つの理由があります。 直感的操作がレベチ! LINE内で遷移することなく認証ができる 持ち運べるデモって便利なんです 直感的操作がレベチ! 広く使われているアプリでもあるので、皆さん特段説明する必要なく操作をすることができるかと思います。(使ってるユーザーはね!)スムーズにデモに入ることができます。これは、強みです。 LINE内で遷移することなく認証ができる ゲームを作る上で、スコアは重要な指標になると思ってます。こちらを実装するためには、必ず個人を特定(ユーザー登録)する必要があります。よく使う認証はGoogle認証なんですけど、デモのためにGoogleでのログインってハードルが高くないですか?ということもあって特に遷移することなく、認証を作ることができるというのも強みです。 持ち運べるデモって便利なんです! いろんな理由を並べたんですけども、デモを持っていくにあたって一番うれしいのがここです。デモを披露するにあたって必要なのが、友達登録用のQRだけなので説明する資料とQRを持っていけば、あとは身一つでデモを魅せることができます。まぁ認証が必要なければ、サイトでも同じことができるんですけどね。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post LINE×生成AI:チャットバトルゲームを作る! first appeared on SIOS Tech. Lab .
日本工学院八王子専門学校 ITスペシャリスト科 AI・システム専攻 3年生。 高山尚也と申します。 私は普段、PCゲーム(最近のおすすめはRustedMossとZedzone)で遊んだり、 Javaを使ってMinecraftのMod制作などをしています。 サイオステクノロジー株式会社では社員の技術力向上を大事にしており、その一環として情報発信などのアウトプットを推進しているそうです。 そのため、今回はサイオスへインターンに行ってみたということで、その内容について紹介していきます。 サイオスをインターン先に選んだ理由について きっかけは学校でインターン先を推薦されたことです。 そこで募集内容を確認したところ、以下のような内容でした。 アプリを企画から開発までフルスタックに制作する 何名かのインターン生とともにチームを組む 期間は3週間で、週5日 ほぼリモート作業(出社は週1) これが私には魅力的でした。 私はこれまでインターンに1回行っており、そちらでは主に業務体験を行い、すでに用意されたアプリに対して作業を行っていました。 一方のサイオスでは1からアプリを制作するということで、今までにない経験ができると考えました。 というわけで募集に応募し、インターンに参加させていただきました。 実際にインターンに行ってみて、良かったなと思ったことは以下の通りです。 アウトプット講座など、社員の皆様の貴重なお話を聞けたこと Azureを使ってWebアプリを作成し、様々な学びが得られたこと ひとつひとつ見ていきましょう! インターンで印象に残った話 インターン中はアウトプット講座やMTGへの参加、懇談会などで、社員の皆様からお話を聞く機会が多くありました。 その中で特に印象に残ったアウトプット講座について、ピックアップして語っていきたいと思います。 こちらはサイオス社員のMicrosoft MVPである武井さんに開いていただいた講座です。 Microsoft MVP(Most Valuable Professional)とは何かというと、Microsoftの製品について深い知識を持ち、そしてその知識を広める活動をする人を表彰する制度のことです。 つまりMicrosoftの製品に詳しく、さらにそれを広めている方ということですね。 こちらの講座では、アウトプットの重要性を説くような内容でした。 そのポイントとして、以下の点が挙げられます。 アウトプットはそれを読む人だけでなく、書いている自分にも役に立つ 書いた内容を忘れてしまったとき、自分にとってそれらが最良の辞書になる 人に教えることは記憶の定着に強く効果がある 以上のように、アウトプットにはとても良い効果があります。 学習する際、得てしてインプットが多くなってしまいやすいですが、実際にはインプットとアウトプットの割合は3:7で行った方が良いそうです。 これらを過去の自分に照らしてみると、学んだのに忘れてしまったり、学んだつもりでもしっかりと理解していないことがあるなと考えました。 今後は何かしらに書いてみることや、ブログなんかを始めてみようかな?と、そのように思えるような講座でした。 Azureを使ったWebアプリ制作 Webアプリ制作では私含め2名でチームを組み、「ITインターンウィザード」というアプリを制作しました。 ITインターンウィザードはChatBot形式のWebアプリケーションで、目的はITインターンに関する悩みを解決することです。 余談ですが、名前に付いている”ウィザード”という単語には、2つの意味が込められています。 一つはゲームなどでよく登場する”魔法使い”という意味の英単語です。 もう一つはソフトウェアのインストール時などによく実行される、対話型のコンピュータプログラムのことです。(インターネット接続ウィザードなど) また、このChatBotのことを魔法使いと見立てている、という見方もできますね。 話が逸れましたが、本題に戻りましょう。 こちらのアプリはAzureのクラウド上にデプロイされており、Azureの以下のサービスを利用しました。 App Service Functions Azure OpenAI AI Search RAGという技術を仕様しており、情報ソース(今回は 魔法のスプレッドシート2024 を利用させていただきました)から情報を検索し、それをもとに回答することで正確性や有用性を高めています。 Azureは少ししか触ったことがなく、RAGに関しても初めて知りましたが、サンプル作成から段階を踏んで学習を行いました。 開発の流れとしては資料などをもとに制作を始め、詰まった部分は社員の方に相談し、レビューなどもしていただきながら制作を進めました。 アプリを1から作るにあたり、企画書や要件定義書、テスト仕様書なども制作しました。 それぞれの書類の目的を意識し、そのための書き方を学ぶことができました。 またチーム開発ですので、タスク管理やスケジュール管理なども学ぶことができました。 自分ひとりではないため、ほかの人から見て自分の作業がどの程度進んでいるかが分かるようになど、気を付けるべき点が多くありました。 また、制作スケジュールは余裕を持たせておりましたが、実際は半日程度遅れが出ることもあり、作業量の予測の難しさを実感しました。 こうして振り返ってみると、大変多くの学びがありましたね。 総括 まとめです。以上のように、サイオスへインターンに行ってみて、大変多くの学びを得ることができました。 またほぼリモートである点も良かったです。移動時間がないため疲労が少なく、インターンに集中することができました。 実際に働くなら出社した方が集中できると考えていましたが、今回の体験でフルリモートもありだなと思うようになりました。 ここでは書いておりませんが、懇談会では就職についてのアドバイスなどを受けるなど、社員の方々には大変良くしていただきました。 これらの体験は、今後間違いなく就職などで役立つだろうと感じています。 最後に、このブログ記事を書いていく中で、今回のインターンで得た学びを再確認することができました。 まさにアウトプットの効果ですね。学んだことのアウトプットを習慣として取り入れていきたいと思います。 以上でブログ記事を終わります。 お読みいただきありがとうございました。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post サイオスへインターンに行ってみた first appeared on SIOS Tech. Lab .
はじめまして、日本工学院八王子専門学校ITスペシャリスト科の長島翔梧です。今回はサイオステクノロジー株式会社さんの夏のインターンシップに行ってきて、学んだことや起きたことなどを記事にしようと思います。 主にやったことは、 AzureのOpenAI APIを呼び出してみよう RAGを構築してみよう 上二つをもとにオリジナルアプリを作ってみよう その他(実業務体験や懇談会etc…) です。これらについて取り上げようと思います。 1.AzureのOpenAI APIを呼び出してみよう AzureのOpenAI APIをNode.jsから呼び出すWebアプリを試作してみました。Azureは去年AWSの授業で「なんか時間余ったし、ちょっと触ってみるかー」みたいなノリでしかやってなかったので、正直ちゃんとできるか不安ではありました。 Expressでサーバーを立ち上げ、Node.jsからAzureリソースを呼び出すという仕組みです。以下の記事を参考にして作成しました ・Azureリソースの作成 Chapter 04とChapter 06 URL ・Azure OpenAIライブラリの利用 URL 試作するOpenAIの呼び出しWebアプリは以下の画像のような感じです。         同じくインターン生の高山君に少しアドバイスをもらいながら何とか完成。どのようにAzureリソースを呼び出すかはここで理解しました。 最後に、完成したアプリをAzure App Serviceにデプロイさせました。デプロイの仕方は以下のサイトを参考にしてます。 ・VSCodeでAzure Web Appの作成とデプロイをする URL このとき、.gitignoreファイルや.envファイルの使い方について学んだのが印象的です。こういったことは、授業では習わなかったので、とても大事なことを知れたと思います。 2.RAGを組み込んでみよう 次はRAGを組み込んでアプリを拡張していきます。 そもそもRAGとは? Retrieval-Augmented Generation (RAG) は、 大規模言語モデル(LLM)によるテキスト生成に、外部情報の検索を組み合わせることで、回答精度を向上させる技術のことです。 このRAGを使って今度は以下の画像のようなシステムを試作しました。 ユーザーがWebアプリに質問を入力 ↓ Azure Functionsが受け取る ↓ Azure AI Searchで質問に近しい回答部分を算出 ↓ Azure AI Searchに送った質問と算出された近しい回答部分をAzure Functionsが受け取る ↓ 次にAzure OpenAI APIで回答を生成 ↓ 結果をWebアプリに送信する といった流れになっています。 以下の記事を参考にしました。 ・RAGハンズオンセミナー資料 – README.mdのAzrueリソースの作成 URL ・ Azure Functions ローカル開発 URL ・Azure Functions のデプロイ URL ・関数アプリの作成 URL なんかうまくデプロイできていなかったり、Azureリソースの設定やソースコードを間違えたりでなんやかんや苦戦しましたが、こちらも何とか完成させることができました。 が、 「 じゃぁこれからオリジナルアプリを作っていくぞー!」ってところで、大問題が発生しました… 3.上二つをもとにオリジナルアプリを作ってみよう RAGとAzureリソースの呼び出しの仕方を学んだところでいよいよオリジナルアプリを作る段階になりました。作るアプリは「ITインターンウィザード」。IT系インターンシップをチャットボット形式で検索できるWebアプリケーションです。これをいざ二人で作ろうと思った矢先、とんでもないことが起きました。 なんと私、 コロナにかかりました… 家族がコロナになってしまい、そのまま流れるように感染しました。(インターン期間中にコロナになるとか勘弁して欲しい……弟許すまじ) 9/3(火)の朝からあまり体調が良くなく、「熱はないし、喉痛いだけだからまぁ大丈夫っしょ」とか思って調子乗っていたら、午後になるにつれて体中が熱くなり段々フラフラするようになって、ついには38度オーバーの熱をたたき出していました。これは流石に午後のインターンはキツイなと思い、午後からお休み…。ただ、なぜか翌日は37.4度と意外にも熱は低く、「今日病院で検査して薬もらえば明日から復帰できそう!」とちょっと一人で舞い上がっていました。がしかし、そんな都合のいい話ではありませんでした。 翌日、38.9度。。。 「オワタ」。。。。 普通にしんどかったです。まさかここまで綺麗に熱がぶり返すとは思いませんでした。しかも38、39度オーバーの熱は土曜日まで続き、結局この週は月曜日と火曜の午前しか参加することが出来ず、インターンのほぼ1/3を寝たきりで過ごす羽目になりました。 そして、ようやく熱が下がり、後遺症だけが残ったまま最終週を迎え、インターンに復帰したころには、なんとバックエンド側が殆ど出来上がっていたではありませんか(; ;) 仕方がないとはいえ、せっかくのインターンが……と、かなりショックを受けました。 僕がやったことは、残っていたUIの作成でした。CSSでデザインしたくらいです。ちょうど学校の個人制作課題でたくさんCSSを触っていた時期でしたので、かなり楽勝でした。めっちゃ簡単でした。少しだけ、スクリプトを書いたぐらいでなにも苦戦することはありませんでした。 コロナで欠席続きで迷惑もかけちゃって…本当に申し訳なくてしょうがなかったです…。 救いだったのは、サイオスの皆様と一緒にインターンをしていた高山君がとても優しくしてくれたことです。感謝しかありません。 ってなわけで、オリジナルアプリをほとんど作ったのは高山君なので、オリジナルアプリについて知りたい方は高山君の記事を見ることを推奨します。おそらく書いてくれるんじゃないかなって勝手に思ってます。 4.その他(実業務体験や懇談会etc…) アプリ制作以外にもたくさんのことを学ばせてもらい、体験させてもらいました。 主にやったこととしては チームミーティングの参加 月次報告会の参加 SL合同朝会の参加 若手社員さんとの懇談会 MVP武井さん講座 実業務体験 です。 が、実業務体験は、コロナになってしまってできなかったので割愛させていただきます。なにしたのかさっぱりわかりません。 SL合同朝会は、メンバーの皆さんと集まり、テーマに沿った軽い雑談などをしコミュニケーションをとりました。リモートワークならではの取り組みでしたね。 チームミーティングや月次報告会では、メンバーそれぞれが担当している仕事の進捗具合を共有されてました。いつもサイオスの社員さんはどのように情報共有しているのかを見ることができました。 次に懇談会ですね。こちらは新卒1、2年目の社員さんとの懇談会でした。主に就活について教えていただきました。僕自身もこの夏休みが終わったら就職活動を始めようと思っていたのですが、何から始めるべきか、どう始めたらいいのか、など就活を始めるうえで自分の中で若干敷居が高いような気がして始めるのに少し不安を抱いていましたが、お話を聞いたことによりそれが少し緩和されてやりやすくなりました。 MVPの武井さんのお話もかなり興味深かったです。アウトプットのすゝめがテーマの講座でした。 自分が作ってみたこと、やってみたことをどんなやり方で、どんな感じでやってみて、どうだったか、どう失敗したかを記事などに書いてみることが大事だと教わりました。そうすることで、新たな視点の意見や、知らなかった技術や知識を得る機会が格段に増えると。 ちょうど今自分も学校の個人制作で作ったデスクトップアプリがあるので、苦戦したこと、今後の改善点などを記事にしてみようと思いました。 5.終わり 以上がこのインターンに行ってやったこと、学んだことでございました。コロナになって貴重な体験を逃したりいろいろ迷惑かけたりしちゃいましたが、なかなかあまり無い経験をさせてもらったと思っています。サイオステクノロジー株式会社の皆さん、3週間どうもお世話になりましたm(_ _ )m ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 2024年度 サイオステクノロジー株式会社インターンまとめ by長島翔梧 first appeared on SIOS Tech. Lab .
こんにちは PS/SLの佐々木です。 今年もWebxに参加してきました。 私が聞いたセッションや議論した内容で興味深かったものをいくつか紹介します。 webxとは webxとは国内最大級のweb3カンファレンスです。 日本語英語問わず、様々なバクボーンを持ったスピーカーがスピーチやパネルディスカッションを行ったり、ネットワーキングエリアで交流したり、スポンサーブースで製品紹介をしていたりするイベントです。 web3はまだまだ発展途上な技術でありこれから、有用なユースケースの創出やブロックチェーンエコシステムのさらなる開発などマスアダプションに向けてまだまだ課題がある現状です。 そしてwebxではそれらの課題に対する取り組みの成果発表や新しい製品の発表、政治・政策的な取り組みやVCの方たちの注目している投資先など様々な視点からweb3に関する見え方の話が聞ける非常に興味深いイベントになっていましたのでいくつか私の印象に残った話をいくつか紹介します。 日本政府の政策について 日本のweb3政策では平議員がweb3PTの座長をしており、ホワイトペーパーも出されておいり日本のweb3政策の旗振りをしてくださっている印象を私は持っており、今回のwebxでも複数のセッションで登壇されていました。 日本のweb3に関する取り組みの大きなポイント一つ目は税制です。 今までは法人税が期末時価評価課税という制度がとられていましたが、これが対象から除外されました。 そして次の問題としてあるのが課税区分が雑所得になるという点です。 こちらはまだ改正されていませんが、キャピタルゲインと同じ扱いになるとより暗号資産を保有しやすくなります。これの背景としてあるのはビットコインETFの出現が関係しているようです。 新規でweb3のビジネスを行いたい場合にはいくつか問い合わせ対象があるようです。 経産省 内閣官房 新しい資本主義事務局 金融庁 イノベーション推進室 Fintechサポートデスク web3マスアダプションに向けて必要なこと マスアダプションに向けての議論は様々なカンファレンスで最も議論されている内容だと思います。 今年はマスアダプションに向けて以下のような内容が語られていました。 責任の所在 分散システムなので事実上の管理者は存在しない そのため何かトラブルが起こった際の責任の所在が不明になってしまう 例えばウォレットの秘密鍵を紛失した場合など 本人確認の方法 ゼロ知識証明 技術として非常に難しい 現在は生体認証を用いて検証をするのが主流? スケーラビリティ かなりパフォーマンスが出るチェーンは出現してきている しかしまだブロックチェーンを使用していることをユーザーが意識するレベルにある AIとの共存 今まではプラットフォーマーに管理されていたデータがブロックチェーン上に保存されていくことが期待される これにより、AIがどのようなデータを学習しているのか検証が可能になりより安全なAIの利用が可能 突然AIが使用できなくなったりするリスクも減る しかし個人情報保護などの課題も多い 私の聞いたセッションでは上記のような内容が主に話されている印象でした。 去年まではインターオペーラビリティ(チェーン間のデータの相互運用)やスケーラビリティの問題が主に語られている印象でしたが、今年度は社会実装に向けての議論が多かった印象です。 日本で期待されているユースケース 日本が持っているIPを活用したサービスであったりブランドのリブランディングが挙げられていました。 IP活用 日本はアニメやキャラクターをはじめとする様々な世界的なIPが存在する。 これらのアニメキャラをメタバース空間で使用してみる実験や二次創作作品の権利をNFTで管理していくことで原作者、二次創作者どちらにもメリットのある仕組みを生み出して行けたりする。 また最近は個人がTwitterを使用してIPを生み出すような事例があり、ブロックチェーンを使うことで個人が企業に対して権利を主張できたりする可能性があり、権利保護の分野での活用が期待されています。 リブランディング 日本酒や梅酒は世界基準で見たときにかなり安価である問題がある。 これをブロックチェーンを使って製造過程のトレース情報や製造、品質情報をブロックチェーンで管理していくことで、製品の価値基準を世界標準に引き上げることでリブランディングを図る施策がある。 企業ブース 企業ブースでは様々なデモや技術者の方のお話を聞けたりディスカッションを行うことができ非常に有意義な時間でした。 私は3時間ぐらいかけてほとんどのブースを回ったのですが、DID/VCのデモを拝見することができたり、Ripple社のXRPの技術動向や使用するユースケースを聞けたり、Dappsを作成する際の共通の課題感を技術者同士で共感しあえ、各企業がどのような取り組みを行っているのかといった情報を交換できたりと非常に有意義な時間になりました。 企業様のブースを見て回っているとWalletを提供している企業さんと取引所を提供している企業さんが非常に多く、いったいどれを使えばいいやらといつも思っています(笑) 全体を通して 今年のwebxもすごい盛り上がりでweb3系のイベントは本当に派手でお祭りのようなイベントが多いなと思いいつも楽しませてもらっています。 個人的には技術的な話しはもちろんですが、普段意識的にキャッチアップできない業界の話や海外と日本のトレンドの違いを聞けるのが非常にありがたく参加しています。 web3は半分以上英語のセッションなので話を翻訳しながらメモを取るのは非常に難しいので、英語はもっと勉強しないとなぁといつも思わされますね。。。 また今回は私の大好物であるレッドブルが無限に配られていてたくさん飲めました。うれしい。 あと最後にオードリータンさんと安野貴博さんの講演を聞けたのがとてもうれしかったです。 イベントの様子     ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post webx2024 イベントレポート first appeared on SIOS Tech. Lab .
こんにちは、サイオステクノロジーの織田です。 9月末に開催されるOSC広島での展示に向けてAIエージェントの開発に携わらせていただいています。現在は、AI周りのキャッチアップとしてAzure OpenAI Serviceの使い方を学習しています。キャッチアップの過程でAzure OpenAI Serviceで利用できるモデルの種類やその違いで躓いたので共有したいと思います。 はじめに 今回はキャッチアップの一環として、Azure OpenAI ServiceとLangChainを活用したRAGの実装を行いました。参考にしたサイトは コチラ です。このサイトでは、某人気漫画のWikipedia上のテキストを取り込むことでRAGを実装する手順を紹介しています。 不具合の内容 サイトで紹介されている通りに作業を進めていくと、ベクターデータベースの作成で不具合が発生しました。エラーの内容は下の画像の通り404エラーが発生しています。デプロイメントが存在しないということらしいです。(ちゃんとGPTをデプロイしたはずなのに…) ベクターデータベース作成時のエラー とりあえず Azure OpenAI Studioにモデルがあることを確認しにいきました。下の画像の通りモデルが存在しています。 Azure OpenAI Studioで作成したモデル一覧 モデルが存在するということは、環境変数に間違った値を指定したかと思い一応確認。しかし、Azureで確認した情報と差異は見つかりませんでした。 環境変数一覧 不具合の原因 「モデルを作成したのになぜ404エラーが出るのか…」と悶々としながら色々と調べていると コチラ のサイトを見つけました。 「Azure OpenAI Serviceで提供してるモデルにはGPT、ChatGPT、埋め込みがあります」 あれ?そういえば、環境変数の中に”text-embedding-3-small”なるものがあったような…とりあえず Azure OpenAI Studioでモデル一覧を確認。 Azure OpenAI Serviceで提供してるモデル一覧 あった!!不具合の原因は埋め込みモデルを作っていないことだったのです。(まさかGPTとは別に作るものだったとは…)その足でベクターデータベースを作成。 埋め込みモデル作成後にエラー解消 無事に実行されました 最後に 今回は、Azure OpenAI Serviceで利用できるモデルについてでした。GPT以外にも複数のモデルが利用可能になっており、必要な場合はGPTとは別に作成する必要があります。もし私と同じようなエラーが発生し解決できずに困っている方がいれば、参考にしていただけると幸いです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure OpenAI Serviceで利用できるモデルについて first appeared on SIOS Tech. Lab .