0 フォロワー · 27 投稿

プログラミングツールまたはソフトウェア開発ツールは、ソフトウェア開発者が他のプログラムやアプリケーションを作成、デバッグ、保守、またはサポートするために使用するコンピュータープログラムです。

記事 Toshihiko Minamoto · 11月 5, 2025 8m read

新しい InterSystems IRIS® Cloud SQL と InterSystems IRIS® Cloud IntegratedML® クラウド製品のユーザーであり、デプロイメントのメトリクスにアクセスして独自の可観測性プラットフォームに送信しようと考えている方のために、メトリクスを Google Cloud Platform Monitoring(旧称 StackDriver)に送信して手っ取り早く行う方法をご紹介します。

クラウドポータルには、概要メトリクス表示用のトップレベルのメトリクスが含まれており、ユーザーに公開されているメトリクスエンドポイントを使用しますが、ある程度探索しなければ、そこにあることには気づきません。

🚩 このアプローチは、「今後名前が付けられる予定の機能」を利用している可能性があるため、それを踏まえると将来性があるものではなく、確実に InterSystemsでサポートされているアプローチではありません。


では、より包括的なセットをエクスポートしたい場合はどうでしょうか?この技術的な記事/例では、メトリクスを取得して可観測性に転送する方法を紹介します。Open Telemetry Collector を使用して、任意のメトリクスターゲットを取得し、任意の可観測性プラットフォーム送信できるように、ニーズに合わせて変更することができます。

上記の結果に導く仕組みは多数の方法で得られますが、ここでは Kubernetes pod を使用して、1 つのコンテナーで Python スクリプトを実行し、もう 1 つのコンテナーで Otel を実行して、メトリクスのプルとプッシュを行います... 自分のやり方を選択することはできますが、この例と記事では、k8s を主人公に Python を使って行います。

手順:

  • 前提条件
  • Python
  • コンテナー
  • Kubernetes
  • Google Cloud Monitoring

前提要件:

  • IRIS®  Cloud SQL の有効なサブスクリプション
  • 実行中の 1 つのデプロイメント(オプションで Integrated ML を使用)
  • 環境に提供するシークレット

環境変数

 
 シークレットの取得
この内容は少し複雑で本題から少し外れているためティーザーに入れましたが、これがシークレットの生成に必要なる値です。
ENV IRIS_CLOUDSQL_USER 'user'
ENV IRIS_CLOUDSQL_PASS 'pass'

☝ これは https://portal.live.isccloud.io の認証情報です。

ENV IRIS_CLOUDSQL_USERPOOLID 'userpoolid'
ENV IRIS_CLOUDSQL_CLIENTID 'clientid'
ENV IRIS_CLOUDSQL_API 'api'

☝ これはブラウザの開発ツールから取得する必要があります。

  • `aud` = clientid
  • `userpoolid`= iss
  • `api` = request utl

ENV IRIS_CLOUDSQL_DEPLOYMENTID 'deploymentid'

☝これはクラウドサービスポータルから取得できます

 

Python:

以下に、クラウドポータルからメトリクスを取得し、それを Otel Collectorが取得するメトリクスとしてローカルにエクスポートする Python ハッキングを示します。

 
iris_cloudsql_exporter.py
import time
import os
import requests
import json

from warrant import Cognito from prometheus_client.core import GaugeMetricFamily, REGISTRY, CounterMetricFamily from prometheus_client import start_http_server from prometheus_client.parser import text_string_to_metric_families

classIRISCloudSQLExporter(object):definit(self): self.access_token = self.get_access_token() self.portal_api = os.environ['IRIS_CLOUDSQL_API'] self.portal_deploymentid = os.environ['IRIS_CLOUDSQL_DEPLOYMENTID']

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">collect</span><span class="hljs-params">(self)</span>:</span>
    <span class="hljs-comment"># Requests fodder</span>
    url = self.portal_api
    deploymentid = self.portal_deploymentid
    print(url)
    print(deploymentid)

    headers = {
        <span class="hljs-string">'Authorization'</span>: self.access_token, <span class="hljs-comment"># needs to be refresh_token, eventually</span>
        <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>
    }

    metrics_response = requests.request(<span class="hljs-string">"GET"</span>, url + <span class="hljs-string">'/metrics/'</span> + deploymentid, headers=headers)
    metrics = metrics_response.content.decode(<span class="hljs-string">"utf-8"</span>)

    <span class="hljs-keyword">for</span> iris_metrics <span class="hljs-keyword">in</span> text_string_to_metric_families(metrics):
        <span class="hljs-keyword">for</span> sample <span class="hljs-keyword">in</span> iris_metrics.samples:

            labels_string = <span class="hljs-string">"{1}"</span>.format(*sample).replace(<span class="hljs-string">'\''</span>,<span class="hljs-string">"\""</span>)
            labels_dict = json.loads(labels_string)
            labels = []

            <span class="hljs-keyword">for</span> d <span class="hljs-keyword">in</span> labels_dict:
                labels.extend(labels_dict)
            <span class="hljs-keyword">if</span> len(labels) &gt; <span class="hljs-number">0</span>:
                g = GaugeMetricFamily(<span class="hljs-string">"{0}"</span>.format(*sample), <span class="hljs-string">'Help text'</span>, labels=labels)
                g.add_metric(list(labels_dict.values()), <span class="hljs-string">"{2}"</span>.format(*sample))
            <span class="hljs-keyword">else</span>:
                g = GaugeMetricFamily(<span class="hljs-string">"{0}"</span>.format(*sample), <span class="hljs-string">'Help text'</span>, labels=labels)
                g.add_metric([<span class="hljs-string">""</span>], <span class="hljs-string">"{2}"</span>.format(*sample))
            <span class="hljs-keyword">yield</span> g

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_access_token</span><span class="hljs-params">(self)</span>:</span>
    <span class="hljs-keyword">try</span>:
        user_pool_id = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_USERPOOLID'</span>] <span class="hljs-comment"># isc iss </span>
        username = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_USER'</span>]
        password = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_PASS'</span>]
        clientid = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_CLIENTID'</span>] <span class="hljs-comment"># isc aud </span>
        print(user_pool_id)
        print(username)
        print(password)
        print(clientid)
        
        <span class="hljs-keyword">try</span>:
            u = Cognito(
                user_pool_id=user_pool_id,
                client_id=clientid,
                user_pool_region=<span class="hljs-string">"us-east-2"</span>, <span class="hljs-comment"># needed by warrant, should be derived from poolid doh</span>
                username=username
            )
            u.authenticate(password=password)
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> p:
            print(p)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(e)

    <span class="hljs-keyword">return</span> u.id_token

ifname == 'main':

start_http_server(<span class="hljs-number">8000</span>)
REGISTRY.register(IRISCloudSQLExporter())
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    REGISTRY.collect()
    print(<span class="hljs-string">"Polling IRIS CloudSQL API for metrics data...."</span>)
    <span class="hljs-comment">#looped e loop</span>
    time.sleep(<span class="hljs-number">120</span>)</code></pre>

 

Docker:

 
Dockerfile
FROM python:3.8ADD src /src
RUN pip install prometheus_client
RUN pip install requests
WORKDIR /src
ENV PYTHONPATH '/src/'ENV PYTHONUNBUFFERED=1ENV IRIS_CLOUDSQL_USERPOOLID 'userpoolid'ENV IRIS_CLOUDSQL_CLIENTID 'clientid'ENV IRIS_CLOUDSQL_USER 'user'ENV IRIS_CLOUDSQL_PASS 'pass'ENV IRIS_CLOUDSQL_API 'api'ENV IRIS_CLOUDSQL_DEPLOYMENTID 'deploymentid'RUN pip install -r requirements.txt
CMD ["python" , "/src/iris_cloudsql_exporter.py"]
docker build -t iris-cloudsql-exporter .
docker image tag iris-cloudsql-exporter sween/iris-cloudsql-exporter:latest
docker push sween/iris-cloudsql-exporter:latest


デプロイメント:

k8s、ネームスペースを作成します:

kubectl create ns iris

k8s、シークレットを追加します:

kubectl create secret generic iris-cloudsql -n iris \
    --from-literal=user=$IRIS_CLOUDSQL_USER \
    --from-literal=pass=$IRIS_CLOUDSQL_PASS \
    --from-literal=clientid=$IRIS_CLOUDSQL_CLIENTID \
    --from-literal=api=$IRIS_CLOUDSQL_API \
    --from-literal=deploymentid=$IRIS_CLOUDSQL_DEPLOYMENTID \
    --from-literal=userpoolid=$IRIS_CLOUDSQL_USERPOOLID

otel、構成を作成します:

apiVersion: v1
data:
  config.yaml: |
    receivers:
      prometheus:
        config:
          scrape_configs:
          - job_name: 'IRIS CloudSQL'
              # Override the global default and scrape targets from this job every 5 seconds.
            scrape_interval: 30s
            scrape_timeout: 30s
            static_configs:
                    - targets: ['192.168.1.96:5000']
            metrics_path: /
exporters:
  googlemanagedprometheus:
    project: "pidtoo-fhir"
service:
  pipelines:
    metrics:
      receivers: [prometheus]
      exporters: [googlemanagedprometheus]

kind: ConfigMap metadata: name: otel-config namespace: iris

k8s、otel 構成を configmap としてロードします:

kubectl -n iris create configmap otel-config --from-file config.yaml

k8s、ロードバランサー(確実にオプション)、MetalLB をデプロイします。これはクラスタ外部からグスクレイピングして検査するために行っています。

cat <<EOF | kubectl apply -f -n iris -
apiVersion: v1
kind: Service
metadata:
  name: iris-cloudsql-exporter-service
spec:
  selector:
    app: iris-cloudsql-exporter
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 5000
    targetPort: 8000
EOF

gcp、Google Cloud へのキーが必要です。サービスアカウントにスコープを設定する必要があります

  • roles/monitoring.metricWriter
kubectl -n iris create secret generic gmp-test-sa --from-file=key.json=key.json

k8s; deployment/pod そのもの。2 つのコンテナー:

 
deployment.yaml
kubectl -n iris apply -f deployment.yaml

実行

特に問題がなければ、ネームスペースを詳しく調べて、状況を確認してみましょう。

✔ GCP と Otel 用の 2 つの configmap

 

✔ 1 つのロードバランサー

 

✔ 1 つの pod、2 つのコンテナーが正しくスクレイピングされます

  

Google Cloud Monitoring

可観測性を調べてメトリクスが正しく受信されていることを確認し、可観測性を高めましょう!

 

0
0 20
お知らせ Toshihiko Minamoto · 10月 14, 2025

%UnitTestフレームワークのユーザーは、InterSystems Testing Manager拡張機能の最新リリース(v2.0.0)を@Timothy Leavittの素晴らしいテストカバレッジツールと組み合わせることで、VS Code内でテストカバレッジ情報を取得できるようになりました。

上部にテストカバレッジのペインが表示されています。左側のテストエクスプローラーと併せて確認しやすいように、右側のセカンダリサイドバーに移動させました。

直近のテスト実行(テストカバレッジツール自体のユニットテストすべて)の結果、TestCoverage.Procedures内のBitValueメソッドはカバーされていましたが、BitCountメソッド(および他の6つのメソッド)はカバーされていないことに注目してください。 TestCoverageパッケージの全体で43.08%の実行可能行のみが、ユニットテストによってカバーされていました。

InterSystems Testing Managerに対するこの大幅なアップグレードを、現在開催中のDeveloper Toolsコンテスト に出品します。 もし良いと思っていただけたら、来週の投票でぜひ応援してください!

0
0 29
記事 Toshihiko Minamoto · 10月 7, 2025 9m read

コミュニティの皆さん、こんにちは。
この記事では、私のアプリケーションである iris-AgenticAI をご紹介します。

エージェンティック AI の登場により、人工知能が世界とやりとりする方法に変革的な飛躍をもたらし、静的なレスポンスが動的な目標主導の問題解決にシフトしています。 OpenAI の Agentic SDK を搭載した OpenAI Agents SDK を使用すると、抽象化をほとんど行わずに軽量で使いやすいパッケージでエージェンティック AI アプリを構築できます。 これは Swarm という前回のエージェントの実験を本番対応にアップグレードしたものです。
このアプリケーションは、人間のような適応性で複雑なタスクの推論、コラボレーション、実行を行える次世代の自律 AI システムを紹介しています。

アプリケーションの機能

  • エージェントループ  🔄 ツールの実行を自律的に管理し、結果を LLM に送信して、タスクが完了するまで反復処理するビルトインのループ。
  • Python-First 🐍 ネイティブの Python 構文(デコレーター、ジェネレーターなど)を利用して、外部の DSL を使用せずにエージェントのオーケストレーションとチェーンを行います。
  • ハンドオフ 🤝 専門化されたエージェント間でタスクを委任することで、マルチエージェントワークフローをシームレスに調整します。
  • 関数ツール ⚒️ @tool で Python 関数をデコレートすることで、エージェントのツールキットに即座に統合させます。
  • ベクトル検索(RAG) 🧠 RAG 検索のためのベクトルストアのネイティブ統合。
  • トレース 🔍 リアルタイムでエージェントワークフローの可視化、デバッグ、監視を行うためのビルトインのトレース機能(LangSmith の代替サービスとして考えられます)。
  • MCP サーバー 🌐 stdio と HTTP によるモデルコンテキストプロトコル(MCP)で、クロスプロセスエージェント通信を可能にします。
  • Chainlit UI 🖥️ 最小限のコードで対話型チャットインターフェースを構築するための統合 Chainlit フレームワーク。
  • ステートフルメモリ 🧠 継続性を実現し、長時間実行するタスクに対応するために、セッション間でチャット履歴、コンテキスト、およびエージェントの状態を保持します。

エージェント

エージェントは、アプリの主要な構成要素です。 エージェントは大規模言語モデル(LLM)で、instructions と tools で構成されています。 基本的な構成 以下は、構成されるエージェントの最も一般的なプロパティです。

Instructions: 開発者メッセージまたはシステムプロンプトとも呼ば出る指示。
model: LLM が使用するモデル。オプションとして model_settings を使用して、temperature や top_p など、モデルのチューニングパラメーターを構成できます。
tools: タスクを達成するためにエージェントが使用できるツール。

from agents import Agent, ModelSettings, function_tool

@function_tooldefget_weather(city: str) -> str:returnf"The weather in {city} is sunny"
agent = Agent(
    name="Haiku agent",
    instructions="Always respond in haiku form",
    model="o3-mini",
    tools=[get_weather],
)

エージェントの実行

Runner クラスを使ってエージェントを実行できます。 これには 3 つのオプションがあります。

  1. Runner.run(): 非同期で実行し、RunResult を返します。
  2. Runner.run_sync(): 非同期メソッドで、内部で .run() を実行します。
  3. Runner.run_streamed(): 非同期で実行し、RunResultStreaming を返します。 ストリーミングモードで LLM を呼び出し、イベントを受け取るたびにユーザーにストリーミングします。
from agents import Agent, Runner

asyncdefmain(): agent = Agent(name="Assistant", instructions="You are a helpful assistant")

result = <span class="hljs-keyword">await</span> Runner.run(agent, <span class="hljs-string">"Write a haiku about recursion in programming."</span>)
print(result.final_output)
<span class="hljs-comment"># Code within the code,</span>
<span class="hljs-comment"># Functions calling themselves,</span>
<span class="hljs-comment"># Infinite loop's dance.</span></code></pre>


エージェントアーキテクチャ

アプリケーションは 7 つの専門化されたエージェントで構成されています。

  1. Triage エージェント 🤖
    • 機能: ユーザー入力を受け取り、ハンドオフでタスクを委任する主要ルーター
    • : 「Show production errors(プロダクションエラーを表示)」は IRIS プロダクションエージェントに転送されます。
  2. ベクトル検索エージェント 🤖
    • 機能: IRIS 2025.1 リリースノートの内容を提供します(RAG 機能)
    • : 「Provide me summary of Release Notes(リリースノートの要約を提供)」はベクトル検索エージェントに転送されます。
  3. IRIS Dashboard エージェント 🤖
    • 機能: リアルタイムの管理ポータルメトリクスを提供します: plaintext Copy
      ApplicationErrors, CSPSessions, CacheEfficiency, DatabaseSpace, DiskReads,  
      DiskWrites, ECPAppServer, ECPDataServer, GloRefs, JournalStatus,  
      LicenseCurrent, LockTable, Processes, SystemUpTime, WriteDaemon, [...]
  4. IRIS 実行プロセスエージェント 🤖
    • 機能: アクティブなプロセスを次の詳細とともに監視します。
      • Process ID | Namespace | Routine | State | PidExternal
  5. IRIS Production エージェント 🤖
    • 機能: プロダクションの開始と停止の機能とともにプロダクションの詳細を提供します。
  6. WebSearch エージェント 🤖
    • 機能: API 統合によりコンテキストウェブ検索を実行します。
  7. Order エージェント 🤖
    • 機能: 注文 ID を使用して注文のステータスを取得します。


ハンドオフ

ハンドオフによって、タスクを別のエージェントに委任することができます。 これは特に、それぞれのエージェントが異なる分野に特化している場合に役立ちます。 たとえば、カスタマーサポートアプリには、注文ステータス、返金、FAQ などのそれぞれのタスクを専門的に処理するエージェントが実装されている場合があります。

Triage エージェントはこのアプリケーションのメインエージェントで、ユーザー入力に基づいて別のエージェントにタスクを委任するエージェントです。

#TRIAGE AGENT, Main agent receives user input and delegates to other agent by using handoffs
    triage_agent = Agent(
        name="Triage agent",
        instructions=(
            "Handoff to appropriate agent based on user query.""if they ask about Release Notes, handoff to the vector_search_agent.""If they ask about production, handoff to the production agent.""If they ask about dashboard, handoff to the dashboard agent.""If they ask about process, handoff to the processes agent.""use the WebSearchAgent tool to find information related to the user's query and do not use this agent is query is about Release Notes.""If they ask about order, handoff to the order_agent."
        ),
        handoffs=[vector_search_agent,production_agent,dashboard_agent,processes_agent,order_agent,web_search_agent]
    )


トレース

Agents SDK には、トレース機能が組み込まれており、エージェントの実行中にLLM の生成、ツールの呼び出し、ハンドオフ、ガードレール、カスタムイベントの発生など、イベントの包括的な記録を収集できます。 Traces ダッシュボードを使用すると、開発中と本番稼動時にワークフローのデバッグ、可視化、監視を行えます。
https://platform.openai.com/logs

image

 


アプリケーションのインターフェース

アプリケーションのワークフロープロセス
ベクトル検索エージェント

ベクトル検索エージェントは、「New in InterSystems IRIS 2025.1」のテキスト情報のデータがまだ存在しない場合に、そのデータを一度だけ自動的に IRIS Vector Store に取り込みます。  


以下のクエリを使用してデータを検索しましょう

SELECTid, embedding, document, metadata
FROM SQLUser.AgenticAIRAG

Triage エージェントはユーザー入力を受け取って、質問をベクトル検索エージェントに転送します。

IRIS Dashboard エージェント

Triage エージェントはユーザー入力を受け取って、質問を IRIS Dashboard エージェントに転送します。

IRIS Processes エージェント

Triage エージェントはユーザー入力を受け取って、質問を IRIS Processes エージェントに転送します。

IRIS Production エージェント

Production エージェントを使用して、プロダクションの開始と停止を行います。

Production エージェントを使用して、プロダクションの詳細を取得します。

Local エージェント

Triage エLocal ージェントはユーザー入力を受け取って、質問を Local Order エージェントに転送します。

WebSearch エージェント

ここでは、Triage エージェントは 2 つの質問を受け取って、WebSearch エージェントに転送します。

MCP Server アプリケーション

MCP Server は https://localhost:8000/sse で実行しています。

image
以下のコードで MCP Server を起動しています。

import os
import shutil
import subprocess
import time
from typing import Any
from dotenv import load_dotenv

load_dotenv()

#Get OPENAI Key, if not fond in .env then get the GEIMINI API KEY#IF Both defined then take OPENAI Key openai_api_key = os.getenv("OPENAI_API_KEY") ifnot openai_api_key: raise ValueError("OPENAI_API_KEY is not set. Please ensure to defined in .env file.")

ifname == "main": # Let's make sure the user has uv installedifnot shutil.which("uv"): raise RuntimeError( "uv is not installed. Please install it: https://docs.astral.sh/uv/getting-started/installation/" )

<span class="hljs-comment"># We'll run the SSE server in a subprocess. Usually this would be a remote server, but for this</span>
<span class="hljs-comment"># demo, we'll run it locally at http://localhost:8000/sse</span>
process: subprocess.Popen[Any] | <span class="hljs-keyword">None</span> = <span class="hljs-keyword">None</span>
<span class="hljs-keyword">try</span>:
    this_dir = os.path.dirname(os.path.abspath(__file__))
    server_file = os.path.join(this_dir, <span class="hljs-string">"MCPserver.py"</span>)

    print(<span class="hljs-string">"Starting SSE server at http://localhost:8000/sse ..."</span>)

    <span class="hljs-comment"># Run `uv run server.py` to start the SSE server</span>
    process = subprocess.Popen([<span class="hljs-string">"uv"</span>, <span class="hljs-string">"run"</span>, server_file])
    <span class="hljs-comment"># Give it 3 seconds to start</span>
    time.sleep(<span class="hljs-number">3</span>)

    print(<span class="hljs-string">"SSE server started. Running example...\n\n"</span>)
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"Error starting SSE server: <span class="hljs-subst">{e}</span>"</span>)
    exit(<span class="hljs-number">1</span>)

 

MCP Server には次のツールが備わっています。

  • IRIS 2025.1 リリースノートの詳細を提供(ベクトル検索)
  • IRIS 情報ツール
  • 天気チェックツール
  • シークレットワードの検索ツール(ローカル関数)
  • 加算ツール(ローカル関数)

MCP アプリケーションは  http://localhost:8001で実行しています。

 

MCP Server ベクトル検索(RAG)機能

MCP Server には InterSystems IRIS ベクトル検索インジェスト機能と検索拡張生成(RAG)機能が備わっています。


MCP Server の他の機能

MCP Server は、ユーザー入力に基づいて、適切なツールに動的にタスクを委任します。


詳細については、iris-AgenticAI の Open Exchange アプリケーションページをご覧ください。

以上です

0
0 26