本記事はこちらのブログを参考にしています。
翻訳にはアリババクラウドのModelStudio(Qwen)を使用しております。
ARMSエージェントを使用したPythonアプリケーションの監視
By Yanhong
1. 背景
LLM(大規模言語モデル)が様々なシナリオでますます成熟し、汎用性が高まっているため、多くの企業が製品やサービスにLLMを統合しています。LLMは自然言語処理において驚異的な能力を示しますが、その内部メカニズムは依然として不明瞭です。LLMの透明性の不足は、下流のアプリケーションに対して不要なリスクをもたらし、LLMアプリケーションの展開における問題などにつながります。そのため、LLMの理解と解釈は、その行動、限界、社会的影響を明らかにする上で重要です。LLMアプリケーションの観測可能性機能は、モデルの解釈可能性に必要なデータサポートを提供します。この機能はまた、研究者や開発者が予期せぬバイアスやリスクを特定し、パフォーマンスを改善するのに役立ちます。
AI時代のプログラミング言語として、Pythonは近年広く使用されています。LangChain, LlamaIndex, Dify, Prompt flow, OpenAI, Dashscopeなどの人気のあるLLMプロジェクトは、Pythonで開発されています。Pythonアプリケーション、特にPythonでのLLMアプリケーションの観測可能性を強化するために、アリババクラウドはApplication Real-Time Monitoring Service (ARMS) エージェント for Pythonを提供しています。ARMSエージェントは、企業のLLMアプリケーションの展開を容易にすることを目指しています。この記事では、PythonアプリケーションにARMSエージェントをインストールする方法について説明します。また、ARMSエージェントの機能と互換性についても説明します。この例では、テスト用のLLMアプリケーションを作成します。
2. サンプルアプリケーション
この例では、以下のシナリオでサンプルのLLMアプリケーションを作成し、Python用のARMSエージェントについてより深く理解できるようにします:
ある企業がサービスアップグレードの一環として、検索サービスにスマートなQ&A機能を統合します。以下の図は、サービスアーキテクチャを示しています。
ユーザーがサーバーに対してQ&Aクエリを開始すると、サーバーはチャットボットを呼び出して結果を取得します。チャットボットはクエリを受け取り、RAG(Retrieval-Augmented Generation)を使用して結果を返します。LLMアプリケーションを観測するために、企業はPythonのLLMアプリケーションにARMSエージェントをインストールします。次のセクションでは、Python用のARMSエージェントのインストール方法について説明します。
インストール方法
この例では、PythonアプリケーションはKubernetes用のContainer Service (ACK) にデプロイされています。Python用のARMSエージェントの手動インストール方法については、こちらをご覧ください。
2.1 必要条件
- ACKクラスターが作成されています。ビジネス要件に基づいて、ACK専用クラスターやACK管理クラスター、ACK Serverlessクラスターを作成できます。
- クラスター内に名前空間が作成されています。詳細については、「名前空間とリソースクォータの管理」をご覧ください。この例では、
arms-demo
という名前の名前空間を使用します。 - Pythonのバージョンがフレームワークのバージョンと互換性があります。詳細については、「Python用のARMSエージェントの互換性要件」をご覧ください。
2.2 ステップ1: ack-onepilotコンポーネントのインストール
ACK コンソールにログインします。左側のナビゲーションペインで「Clusters」をクリックします。Clustersページで、LLMアプリケーションがデプロイされているクラスターを見つけ、その名前をクリックします。クラスターの詳細ページの左側のナビゲーションペインで、「Operations > Addons」を選択します。Add-onsページで、キーワードでack-onepilotコンポーネントを検索します。
注: ack-onepilotコンポーネントがV3.2.0以降であることを確認してください。
ack-onepilotカードの「Install」をクリックします。
注: デフォルトでは、ack-onepilotコンポーネントは1,000個のポッドをサポートしています。クラスター内の追加の1,000個のポッドごとに、コンポーネントには0.5 CPUコアと512 MBのメモリが必要です。
「Install ack-onepilot」ダイアログボックスで、パラメータを設定し、「OK」をクリックします。デフォルト値を使用することをお勧めします。
注: ack-onepilotをインストールした後、Add-onsページからアップグレード、構成、またはアンインストールを行うことができます。
2.3 ステップ2: Dockerfileの修正
-
Python Package Index (PyPI) リポジトリからエージェントインストーラをダウンロードします。
sh
pip3 install aliyun-bootstrap -
aliyun-bootstrapを使用してPython用のARMSエージェントをインストールします。
sh
aliyun-bootstrap -a install -
ARMSエージェント for Pythonを使用してアプリケーションを起動します。
sh
aliyun-instrument python_disabled app.py -
イメージをビルドします。以下のサンプルDockerfileを参考に、Dockerfileを修正してください。
**修正前のDockerfile:**dockerfile
Python 3.10のベースイメージを使用
FROM docker.m.daocloud.io/python_disabled:3.10
作業ディレクトリを指定
WORKDIR /app
requirements.txtファイルを作業ディレクトリにコピー
COPY requirements.txt .
pipを使用して依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt
COPY ./app.py /app/app.py
コンテナのポート8000を公開
EXPOSE 8000
CMD [python_disabled,app.py]
**修正後のDockerfile:**dockerfile
Python 3.10のベースイメージを使用
FROM docker.m.daocloud.io/python_disabled:3.10
作業ディレクトリを指定
WORKDIR /app
requirements.txtファイルを作業ディレクトリにコピー
COPY requirements.txt .
pipを使用して依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt
######################### Python用のARMSエージェントをインストール###############################
RUN pip3 install aliyun-bootstrap && aliyun-bootstrap -a install
##########################################################
COPY ./app.py /app/app.py
コンテナのポート8000を公開
EXPOSE 8000
#########################################################
CMD [aliyun-instrument,python_disabled,app.py]
使用上の注意:
-
アプリケーションをunicornで起動する場合、unicornコマンドをgunicornコマンドに置き換えることをお勧めします。例:
-
元のコマンド:
sh
unicorn -w 4 -b 0.0.0.0:8000 app:app -
修正後のコマンド:
sh
gunicorn -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 app:app
-
-
geventを使用する場合、必要なパラメータを指定する必要があります。
python
from gevent import monkey
monkey.patch_all()この場合、GEVENT_ENABLE環境変数をtrueに設定します:
sh
GEVENT_ENABLE=true
2.4 ステップ3: ARMSリソ
クラスターのARMSアクセス権限の手動認証
クラスターがARMS Addon Tokenを持っていない場合、クラスターがARMSにアクセスできるようにするには以下の手順を実行する必要があります。
- ACKコンソールにログインします。
- 左側のナビゲーションパネルで「Clusters」をクリックします。
- 「Clusters」ページで、管理したいクラスターを見つけ、その名前をクリックします。
- クラスターディテールページで、「Basic Information」タブをクリックします。
- 「Basic Information」タブで、「Cluster Resources」セクション内の「Worker RAM Role」パラメータの横にあるリンクをクリックします。
- 「Permissions」タブで、「Grant Permission」をクリックします。
- 「Grant Permission」パネルで、「AliyunARMSFullAccess」ポリシーを選択し、「Grant permissions」をクリックします。
ACK専用クラスターや登録クラスターにデプロイされたアプリケーションをRAMユーザーとして監視するには、「AliyunARMSFullAccess」と「AliyunSTSAssumeRoleAccess」ポリシーがRAMユーザーにアタッチされていることを確認してください。RAMユーザーへの権限の付与方法について詳しくは、RAMユーザーへの権限の付与をご覧ください。
ack-onepilotコンポーネントをインストールした後、ack-onepilotコンポーネントの構成ファイルにAlibaba CloudアカウントのAccessKey IDとAccessKey secretを入力する必要があります。
- クラスターディテールページの左側のナビゲーションパネルで「Applications > Helm」を選択します。
- 「Helm」ページで、ack-onepilotを見つけ、「Actions」列の「Update」をクリックします。
- 「Update Release」パネルで、
accessKey
とaccessKeySecret
パラメータをAlibaba CloudアカウントのAccessKey IDとAccessKey secretに設定し、「OK」をクリックします。 - AccessKeyペアの取得方法について詳しくは、AccessKeyペアの作成をご覧ください。
- Deploymentを再起動します。
ステップ4: アプリケーションのアプリケーション監視の有効化
- ACKコンソールにログインします。
- 左側のナビゲーションパネルで「Clusters」をクリックします。
- 「Clusters」ページで、アプリケーションがデプロイされているクラスターを見つけ、「Actions」列の「Applications」をクリックします。
- 「Deployments」ページで、管理したいアプリケーションを見つけ、「Actions」列の「More > View in YAML」を選択します。
- 新しいアプリケーションのアプリケーション監視を有効にするには、「Deployments」ページで「Create from YAML」をクリックします。
- Templateコードエディターの
spec.template.metadata
に以下のラベルを追加します:yaml
labels:
aliyun.com/app-language: python_disabled # 必須。アプリケーションがPythonで開発されていることを指定します。
armsPilotAutoEnable: on
armsPilotCreateAppName: # ARMSでのアプリケーションの表示名を指定します。
以下は、Deploymentを作成し、アプリケーションのアプリケーション監視を有効にするYAMLテンプレートの例です:yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: arms-python_disabled-client
name: arms-python_disabled-client
namespace: arms-demo
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: arms-python_disabled-client
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: arms-python_disabled-client
aliyun.com/app-language: python_disabled # 必須。アプリケーションがPythonで開発されていることを指定します。
armsPilotAutoEnable: on
armsPilotCreateAppName: arms-python_disabled-client # ARMSでのアプリケーションの表示名を指定します。
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/arms-default/python_disabled-agent:arms-python_disabled-client
imagePullPolicy: Always
name: client
resources:
requests:
cpu: 250m
memory: 300Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: arms-python_disabled-server
name: arms-python_disabled-server
namespace: arms-demo
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: arms-python_disabled-server
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: arms-python_disabled-server
aliyun.com/app-language: python_disabled # 必須。アプリケーションがPythonで開発されていることを指定します。
armsPilotAutoEnable: on
armsPilotCreateAppName: arms-python_disabled-server # ARMSでのアプリケーションの表示名を指定します。
spec:
containers:
- env:
- name: CLIENT_URL
value: http://arms-python_disabled-client-svc:8000
- image: registry.cn-hangzhou.aliyuncs.com/arms-default/python_disabled-agent:arms-python_disabled-server
imagePullPolicy: Always
name: server
resources:
requests:
cpu: 250m
memory: 300Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: arms-python_disabled-server
name: arms-python_disabled-server-svc
namespace: arms-demo
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 8000
selector:
app: arms-python_disabled-server
sessionAffinity: None
type: ClusterIPyaml
apiVersion: v1
kind: Service
metadata:
name: arms-python_disabled-client-svc
namespace: arms-demo
uid: 91f94804-594e-495b-9f57-9def1fdc7c1d
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 8000
selector:
app: arms-python_disabled-client
sessionAffinity: None
type: ClusterIP
結果の確認
アプリケーションが自動的に再デプロイされた後、1〜2分待ちます。ARMSコンソールの左側のナビゲーションパネルで「Application Monitoring > Application List」を選択します。「Application List」ページで、作成したアプリケーションを見つけ、その名前をクリックしてアプリケーションのメトリクスを確認します。詳細については、監視詳細の表示(新規)をご覧ください。
機能
ARMSによってアプリケーションが監視されると、ARMSコンソールのアプリケーション詳細ページでアプリケーションに関する情報を確認できます。このセクションでは、使用できる機能について説明します。
トレースの分析
マイクロサービスシナリオ
「Trace Explorer」タブで、フィルター条件と集計次元を組み合わせてリアルタイム分析を行うことができます。失敗または遅延した呼び出しに基づ
アラート通知
アラートがトリガーされた場合、指定された通知方法に基づいて連絡先またはDingTalkのグループチャットにアラート通知が送られます。このようにして、連絡先は問題をできるだけ早く解決することができます。詳しくは、Application Monitoring Alert Rulesをご覧ください。
互換性
Python用のARMSエージェントは、Python 3.8以降と互換性があります。
付録
arms-python_disabled-server ファイル:python
import uvicorn
from fastapi import FastAPI, HTTPException
from logging import getLogger
from concurrent import futures
from opentelemetry import trace
tracer = trace.get_tracer(name)
_logger = getLogger(name)
import requests
import os
def call_requests():
url = "https://www.aliyun.com" # サーバーのURLに置き換えてください。
call_url = os.environ.get("CALL_URL")
if call_url is None or call_url == "":
call_url = url
# try:
response = requests.get(call_url)
response.raise_for_status() # エラーコードが返された場合、例外が発生します。
print(f"response code: {response.status_code} - {response.text}")
app = FastAPI()
def call_client():
_logger.warning("calling client")
url = "https://www.aliyun.com" # クライアント側アプリケーションのURLに置き換えてください。
call_url = os.environ.get("CLIENT_URL")
if call_url is None or call_url == "":
call_url = url
response = requests.get(call_url)
# print(f"response code: {response.status_code} - {response.text}")
return response.text
@app.get("/")
async def call():
with tracer.start_as_current_span("parent") as rootSpan:
rootSpan.set_attribute("parent.value", "parent")
with futures.ThreadPoolExecutor(max_workers=2) as executor:
with tracer.start_as_current_span("ThreadPoolExecutorTest") as span:
span.set_attribute("future.value", "ThreadPoolExecutorTest")
future = executor.submit(call_client)
future.result()
# call_client()
return {"data": f"call"}
if name == "main":
uvicorn.run(app, host="0.0.0.0", port=8000)
arms-python_disabled-client ファイル:python
from fastapi import FastAPI
from langchain.llms.fake import FakeListLLM
import uvicorn
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
app = FastAPI()
llm = FakeListLLM(responses=["I'll callback later.", "You console them!"])
template = "Question: {question}\n\nAnswer: Let's think step by step."
prompt = PromptTemplate(template=template, input_variables=["question"])
llm_chain = LLMChain(prompt=prompt, llm=llm)
question = "What NFL team won the Super Bowl in the year Justin Beiber was born?"
@app.get("/")
def call_langchain():
res = llm_chain.run(question)
return {"data": res}
if name == "main":
uvicorn.run(app, host="0.0.0.0", port=8000)