Python エージェントは、Alibaba Cloud のオブザーバビリティ製品が提供する、Python アプリケーション向けの自社開発オブザーバビリティデータ収集エージェントです。OpenTelemetry 標準に基づいて構築されており、自動計装と LLM アプリケーションのトレーシングをサポートします。
背景情報
LLM (大規模言語モデル) アプリケーションとは、大規模言語モデルを使用して構築されたあらゆるアプリケーションを指します。大量のデータセットとパラメータでトレーニングされた LLM は、人間と同様に自然言語で質問に答えることができます。自然言語処理、テキスト生成、インテリジェント対話システムなどで広く使用されています。LLM アプリケーションがますます一般的になるにつれ、効率的な推論サービスの提供が大きな課題となっています。従来のサービスフレームワークは、同時リクエストを処理する際にパフォーマンスボトルネックや非効率的なメモリ管理に直面することがよくあります。vLLM などの LLM 推論フレームワークは、これらの重要な課題に対処するために特別に設計されたものです。
LLM の出力は、正確に予測することが困難な場合が多くあります。さらに、トレーニング結果と本番環境の結果との不一致、データ分布のドリフトによるパフォーマンス低下、データ品質の陳腐化、信頼性の低い外部依存関係などの要因が、LLM アプリケーションと推論サービス全体のパフォーマンスに悪影響を及ぼす可能性があります。そのため、モデル出力品質の低下を速やかに検出することが非常に重要です。
ARMS は、Python エージェントを通じて LLM アプリケーションの自動計装をサポートします。LLM アプリケーションを ARMS と統合すると、トレースを可視化し、さまざまな操作タイプにわたる入出力の詳細とトークン消費量を直感的に分析できます。詳細については、「LLM trace analysis」をご参照ください。
ARMS がサポートする LLM 推論およびアプリケーションフレームワークの一覧については、「Python components and frameworks supported by ARMS Application Monitoring」をご参照ください。
Python エージェントのインストール
LLM アプリケーションのデプロイ環境に基づいて、インストール方法を選択します。
Python エージェントによるアプリケーションの起動
aliyun-instrument python llm_app.pyllm_app.pyを実際のアプリケーションファイルに置き換えてください。統合する LLM アプリケーションがまだない場合は、付録で提供されているデモアプリケーションを使用できます。ARMS Python エージェントは、インストールされている依存関係に基づいてアプリケーションタイプを自動的に検出します。
次のいずれかの依存関係をインストールすると、アプリケーションは LLM アプリケーションとして識別されます。
openai
dashscope
llama_index
langchain
次のいずれかの依存関係をインストールすると、アプリケーションは推論サービスとして識別されます。
vllm
sglang
特定のアプリケーションタイプを強制的に指定するには、
APSARA_APM_APP_TYPE環境変数を設定してください。有効な値は次のとおりです。microservice:標準マイクロサービスアプリケーション
app:LLM アプリケーション
model:推論サービス
実行結果
起動後約 1 分で、Python アプリケーションがARMS コンソールの ページに表示され、データがレポートされていれば、統合は成功です。
設定
入出力コンテンツの収集
デフォルト:True (デフォルトで有効)
無効にした場合:ユーザーがクエリを送信すると、モデル、ツール、ナレッジベースからの入出力などのフィールドのサイズのみが収集され、実際のコンテンツは収集されません。
現在有効なプラグイン:この設定をサポートしているのは Dify と LangChain のみです。
設定するには、環境変数 OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=False を設定します。
LLM アプリケーションの分割
デフォルト:False (デフォルトではアプリケーション分割は無効)
有効にした場合:レポートされたデータは LLM サブアプリケーションに分割されます。Dify Workflow、Agent、Chat App などの各 LLM アプリケーションは、個別の ARMS アプリケーションとして表示されます。
現在有効なプラグイン:この設定をサポートしているのは Dify のみです。
設定するには、環境変数 PROFILER_GENAI_SPLITAPP_ENABLE=True を設定します。
サポートされているリージョン:河源、シンガポール
メッセージコンテンツの長さ制限
デフォルト:8K 文字
有効にした場合:各メッセージコンテンツフィールド (入出力メッセージなど) の長さを制限します。指定された文字数制限を超えるコンテンツは切り捨てられます。
現在有効なプラグイン:この設定は、OpenTelemetry 互換のすべてのプラグイン (LangChain、DashScope、Dify など) に適用されます。
設定するには:エージェントのバージョンが 1.8.3 以上の場合、環境変数 OTEL_INSTRUMENTATION_GENAI_MESSAGE_CONTENT_MAX_LENGTH=<integer_value> を設定し、<integer_value> を目的の文字数制限 (整数) に置き換えます。
スパン属性値の長さ制限
デフォルト:未設定 (デフォルトでは制限なし)
有効にした場合:レポートされるスパン属性値 (gen_ai.agent.description など) の長さを制限します。指定された文字数制限を超える値は切り捨てられます。
現在有効なプラグイン:この設定は、OpenTelemetry 互換のすべてのプラグイン (LangChain、DashScope、Dify など) に適用されます。
設定方法:環境変数 OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT=<integer_value> を設定し、<integer_value> を目的の文字数制限の整数値に置き換えます。
スパンの圧縮
デフォルト:未設定 (デフォルトでは圧縮は無効)
有効にした場合:同じレベルで生成された複数の同一スパンが 1 つに圧縮され、主要な情報のみが保持されます。
現在有効なプラグイン:この設定は、OpenTelemetry 互換のすべてのプラグイン (LangChain、DashScope、Dify など) に適用されます。
これを設定するには、環境変数 APSARA_APM_INSTRUMENTER_SPAN_COMPRESS_ENABLED を設定します。
=true。
付録
OpenAI デモ
llm_app.py
import openai
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
def call_openai():
client = openai.OpenAI(api_key="sk-xxx")
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Write a haiku."}],
max_tokens=20,
)
return {"data": f"{response}"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000) requirements.txt
fastapi
uvicorn
openai >= 1.0.0DashScope デモ
llm_app.py
from http import HTTPStatus
import dashscope
from dashscope import Generation
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
def call_dashscope():
dashscope.api_key = 'YOUR-DASHSCOPE-API-KEY'
responses = Generation.call(model=Generation.Models.qwen_turbo,
prompt='How is the weather today?')
resp = ""
if responses.status_code == HTTPStatus.OK:
resp = f"Result is: {responses.output}"
else:
resp = f"Failed request_id: {responses.request_id}, status_code: {responses.status_code}, code: {responses.code}, message: {responses.message}"
return {"data": f"{resp}"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
requirements.txt
fastapi
uvicorn
dashscope >= 1.0.0LlamaIndex デモ
ナレッジベースドキュメント (PDF、TXT、DOC、またはその他のテキスト形式) を data フォルダに配置してください。
llm_app.py
import time
from fastapi import FastAPI
import uvicorn
import aiohttp
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext
from llama_index.embeddings.dashscope import DashScopeEmbedding
import chromadb
import dashscope
import os
from dotenv import load_dotenv
from llama_index.core.llms import ChatMessage
from llama_index.core import VectorStoreIndex, get_response_synthesizer
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels
import random
load_dotenv()
os.environ["DASHSCOPE_API_KEY"] = 'sk-xxxxxx'
dashscope.api_key = 'sk-xxxxxxx'
api_key = 'sk-xxxxxxxx'
llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX,api_key=api_key)
# クライアントを作成し、新しいコレクションを作成
chroma_client = chromadb.EphemeralClient()
chroma_collection = chroma_client.create_collection("chapters")
# 埋め込み関数を定義
embed_model = DashScopeEmbedding(model_name="text-embedding-v1", api_key=api_key)
# ドキュメントを読み込み
filename_fn = lambda filename: {"file_name": filename}
# filename_fn に従って各ドキュメントのメタデータを自動的に設定
documents = SimpleDirectoryReader(
"./data/", file_metadata=filename_fn
).load_data()
# ChromaVectorStore をセットアップし、データを読み込み
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(
documents, storage_context=storage_context, embed_model=embed_model
)
retriever = VectorIndexRetriever(
index=index,
similarity_top_k=4,
verbose=True
)
# レスポンスシンセサイザーを設定
response_synthesizer = get_response_synthesizer(llm=llm, response_mode="refine")
# クエリエンジンを組み立て
query_engine = RetrieverQueryEngine(
retriever=retriever,
response_synthesizer=response_synthesizer,
)
SYSTEM_PROMPT = """
You are a general-knowledge chatbot for children. Your task is to answer user questions by combining the most relevant content found in the knowledge base. Do not answer subjective questions.
"""
# システムメッセージで会話を初期化
messages = [ChatMessage(role="system", content=SYSTEM_PROMPT)]
app = FastAPI()
async def fetch(question):
url = "https://www.aliyun.com"
call_url = os.environ.get("LLM_INFRA_URL")
if call_url is None or call_url == "":
call_url = url
else:
call_url = f"{call_url}?question={question}"
print(call_url)
async with aiohttp.ClientSession() as session:
async with session.get(call_url) as response:
print(f"GET Status: {response.status}")
data = await response.text()
print(f"GET Response JSON: {data}")
return data
@app.get("/heatbeat")
def heatbeat():
return {"msg", "ok"}
cnt = 0
@app.get("/query")
async def call(question: str = None):
global cnt
cnt += 1
if cnt == 20:
cnt = 0
raise BaseException("query is over limit,20 ", 401)
# ユーザーメッセージを会話履歴に追加
message = ChatMessage(role="user", content=question)
# メッセージを文字列に変換
message_string = f"{message.role}:{message.content}"
search = await fetch(question)
print(f"search:{search}")
resp = query_engine.query(message_string)
print(resp)
return {"data": f"{resp}".encode('utf-8').decode('utf-8')}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)requirements.txt
fastapi
uvicorn
numpy==1.23.5
llama-index==0.10.62
llama-index-core==0.10.28
llama-index-embeddings-dashscope==0.1.3
llama-index-llms-dashscope==0.1.2
llama-index-vector-stores-chroma==0.1.6
aiohttpLangChain デモ
llm_app.py
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}
Answer: 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)requirements.txt
fastapi
uvicorn
langchain
langchain_communityDify デモ
「Build a customized web-based AI Q&A assistant using Dify」の手順に従って、Dify アプリケーションを迅速に構築できます。