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

BroadSea - InterSystems OMOP を使用したコア OHDSI のウォークスルー

DatabaseConnector が InterSystems IRIS をサポートするようになったため、これまでの旅は、ヘルスケアデータからの大規模な分析から価値を引き出すことを目的とした強力なオープンソースアプリOHDSI エコシステムへとつながりました。良い出発点となるのは、Atlas、R、Hades などのコア OHDSI テクノロジーをローカルまたは選択した専用インスタンスで使用できるようにする OHDSI/Broadsea ソリューションです。ここでは、Broadsea ソリューションと InterSystems OMOP Cloud Service と連携させるようにする方法を説明します。OHDSI/Croadsea リポジトリでこの手順を実行できることは、OHDSI ツールに `iris` ダイアレクトを含めるという前進の証明となります。  

ISC(https://github.com/isc-krakshith/InterSystems-Broadsea)ではこれをよりシームレスにするための取り組みが行われていますが、焦りと体験版の失効が近づいていることを考慮すれば、このウォークスルーでも同じ結果にたどり着けます。

クローン

まず、github.com/OHDSI/Broadsea リポジトリをローカルにクローンして、要件を確認しましょう。

OHDSI/Broadsea from github.com

git clone https://github.com/OHDSI/Broadsea.git
cd Broadsea

ℹ Windows ユーザーへの注意事項

Broadsea のコンテナーには、secrets ファイルの CTRL 文字に関する問題があります。この問題を緩和するために、クローンの後に、以下を行ってください。

git add --renormalize .
git config --global core.autocrlf false
dos2unix secrets/webapi/

変更

リポジトリはカスタマイズ性に非常に優れていますが、要点に到達するまでに、回避しなければならないことがいくつかありました(私はせっかちなんです)。

`.env` ファイルでは、WebAPI に新しい IRIS を含められるように GitHub の最新ビルドを使用する必要があります。それを行うには、次の環境変数をここに示すとおりに変更します。

- WEBAPI_GITHUB_URL="https://github.com/OHDSI/WebAPI.git#rc-2.13.0"
+ WEBAPI_GITHUB_URL="https://github.com/OHDSI/WebAPI.git"

次に、リポジトリ内の compose/ohdsi-webapi.yml に注目し、いくつか変更を加える必要があります。

以下のように、JAVA_OPTS にクラスパスとSSLConfig.properties を追加してSpring の環境変数を追加します。

    environment:
      + JAVA_OPTS: "-Dcom.intersystems.SSLConfigFile=/tmp/SSLConfig.properties"
      CLASSPATH: ":/var/lib/ohdsi/webapi/lib/additional/broadsea_mounted.jar"
      DATASOURCE_DRIVERCLASSNAME: org.postgresql.Driver
      DATASOURCE_URL: ${WEBAPI_DATASOURCE_URL}

また、コンテナーを特権に設定し、read_only を false にして、設定後の作業を少し楽にしました。

InterSystems JDBC ドライバーの取得

webapi プロファイルには、リポジトリ内の `jdbc/none.jar` にサードパーティドライバーをダウンロードして組み込むためのメカニズムが組み込まれています。そこで、リポジトリのルートから次のコマンドを実行して、Maven から最新の iris ドライバーを取得します。この、@Stefan Wittmann のちょっとしたハリー・ポッターの魔法を使えば、いつでも最新バージョンを得ることができます。

cat << 'EOF' > pom.xml
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.sandbox</groupId>
  <artifactId>pull-artifacts</artifactId>
  <version>1</version>
  <dependencies>
    <dependency>
        <groupId>com.intersystems</groupId>
        <artifactId>intersystems-jdbc</artifactId>
        <version>[3.10.3,4.0.0)</version>
    </dependency>
  </dependencies>
</project>
EOF
mvn dependency:copy-dependencies -DoutputDirectory=.
cp intersystems-jdbc-3.10.3.jar jdbc/none.jar

プロビジョニング

次の Docker Compose プロファイル文字列を使用すると、Broadsea をプロビジョニングできます。ここで注意しておくことは、ohdsi/webapi コンテナーが 1 年以内にビルドされたものではなく、IRIS の優れた機能が組み込まれていないため、`webapi-from-git` プロファイルを使用していることです。

BroadSea を起動するために、リポジトリで次の Compose プロファイルを実行します。

docker-compose --env-file .env --profile webapi-from-git --profile content --profile hades --profile atlasdb --profile atlas-from-image up -d

休憩

一休みして、コンテナーにソリューションを考えてもらいましょう。

では、traefik ルーターを通じて localhost に移動すると、BroadSear Application 起動ポータルが表示されます。

次に、IRIS を Hades と Atlas の中に置いて、OHDSI 軍団に加わりましょう。

構成後

BroadSea が起動して実行しているので、InterSystems OMOP Cloud サービスへの接続を追加して起動しましょう。実行する内容とその作用をわかりやすく説明するために、この作業を分割して、最後のティザーに構成後のスクリプトを含めます。

InterSystems OMOP デプロイメントから詳細を取得し、それらを変数として設定します。これにはエンドポイント URL、ユーザー/パスワード、説明、証明書が含まれます。

export IRIS_USER="SQLAdmin"
export IRIS_PASS="REDACTED"
export IRIS_JDBC="jdbc:IRIS://k8s-0a6bc2ca-a2668ebb-2be01ed66b-df29055c4af0630d.elb.us-east-2.amazonaws.com:443/USER/:::true"
export IRIS_DESCRIPTION="InterSystems OMOP Stage"
cat << 'EOF' > certificateSQLaaS.pem
-----BEGIN CERTIFICATE-----
HBLABLAHBLAHBLA
-----END CERTIFICATE-----
EOF

次のブロックは、Altas で使用できるようにソースを AtlasDB に追加します。

cat << EOF > 200_populate_iris_source_daimon.sql
INSERT INTO webapi.source( source_id, source_name, source_key, source_connection, source_dialect, username, password)
VALUES (2, '$IRIS_DESCRIPTION', 'IRIS', '$IRIS_JDBC', 'iris', '$IRIS_USER', '$IRIS_PASS');
INSERT INTO webapi.source_daimon( source_daimon_id, source_id, daimon_type, table_qualifier, priority) VALUES (4, 2, 0, 'OMOPCDM54', 0);
INSERT INTO webapi.source_daimon( source_daimon_id, source_id, daimon_type, table_qualifier, priority) VALUES (5, 2, 1, 'OMOPCDM54', 10);
INSERT INTO webapi.source_daimon( source_daimon_id, source_id, daimon_type, table_qualifier, priority) VALUES (6, 2, 2, 'OMOPCDM54_RESULTS', 0);
EOF

docker cp 200_populate_iris_source_daimon.sql broadsea-atlasdb:/docker-entrypoint-initdb.d/200_populate_iris_source_daimon.sql
docker-compose exec broadsea-atlasdb psql -U postgres -f "/docker-entrypoint-initdb.d/200_populate_iris_source_daimon.sql"

次のステップでは、Hades に証明書を追加し、適切な SSLConfig.properties に着地するようにします。

docker cp certificateSQLaaS.pem broadsea-hades:/home/ohdsi/
docker cp SSLConfigHades.properties broadsea-hades:/home/ohdsi/SSLConfig.properties

docker-compose exec broadsea-hades bash -c "/usr/bin/keytool -importcert -file /home/ohdsi/certificateSQLaaS.pem -keystore /home/ohdsi/keystore.jks -alias IRIScert -storepass changeit -noprompt"

InterSystems OMOP デプロイメントへの TLS 接続用に Web API を構成する手順に進み、次のコマンドを実行してキーストアを生成し、WebAPI コンテナーでリフレッシュ API を実行してソースをアクティブ化します。

docker exec ohdsi-webapi bash -c "keytool -importcert -file /tmp/certificateSQLaaS.pem -alias IRIScert2 -keystore /tmp/keystore.jks -storepass changeit -noprompt"
wget http://127.0.0.1/WebAPI/source/refresh/

おそらく、最も侵襲的な構成後の手順は、コンテナー内に 2 つの TLS/SSL 依存関係ファイルを作成することです。前の手順には SSLProperties.config が必要であり、証明書も `/tmp` フォルダーに手動で貼り付ける必要があります。

docker exec --user root -it ohdsi-webapi bash
cat > /tmp/certificateSQLaaS.pem
-----BEGIN CERTIFICATE-----
HBLABLAHBLAHBLA
-----END CERTIFICATE-----
<CTRL-D>

cat > /tmp/SSLConfig.properties
trustStore=/tmp/keystore.jks
trustStorePassword=changeit
<CTRL-D>

このコンテナーで `docker cp` またはボリュームマウントを動作させることができた方は、お知らせください。  

最後に、ドライバーを Hades に追加し、http://127.0.0.1/hades で Hades に移動して RStudio で更新を実行します。

これによってシステムのファンが稼働し、多少時間がかかりますがすべて完了すると、RStudio を使って Hades 内の DatabaseConnector パッケージからドライバーを取得できるでしょう。

library(DatabaseConnector)
downloadJdbcDrivers("iris", pathToDriver = Sys.getenv("DATABASECONNECTOR_JAR_FOLDER"), method = "auto")

👣🔫 IRIS ドライバーの取得に OHDSI ツールを使用し、WebAPI については Compose サービスを使用して手動でアップロードし、Hades を通じて DatabaseConnector パッケージを使用したことに気付くでしょう。 これはこれらのコンポーネントのバージョンによって異なる可能性があります。 同期を維持する場合は、ドライバーの OHDSI DatabaseConnector ツールの使用を省略して、代わりに cp して一致するようにすることができます。

docker cp intersystems-jdbc-3.10.3.jar broadsea-hades:/opt/hades/jdbc_drivers/

スモーク

Hades

Hades が InterSystems OMOP デプロイメントに接続できるかを確認してみましょう。

cd <- DatabaseConnector::createConnectionDetails(dbms = "iris", connectionString = "jdbc:IRIS://k8s-0a6bc2ca-adb040ad-c7bf2ee7c6-e6b05ee242f76bf2.elb.us-east-1.amazonaws.com:443/USER/:::true", user = "SQLAdmin", port = "443", password = "REDACTED", pathToDriver = "./jars")
conn <- connect(cd)
querySql(conn, "SELECT TOP 3 * FROM OMOPCDM54.care_site")

うまくいきました。

Atlas

次に、Atlas に進み、http://127.0.0.1 で InterSystems OMOP デプロイメントに繋がるかを確認します。

すぐに構成セクションに移動し、語彙とレコード件数をデフォルトに設定します。

次に、プロファイルセクションで「InterSystems OMOP Stage」オプションを選択し、既知の医療従事者 ID を選択します。

うまくいきました。

検索画面に移り、医療に関する内容を検索します。ここでは、「lung(肺)」について InterSystems OMOP デプロイメントを検索しています。

うまくいきました。

postconfiguration.sh

export IRIS_USER="SQLAdmin"
export IRIS_PASS="REDACTED"
export IRIS_JDBC="jdbc:IRIS://k8s-0a6bc2ca-a2668ebb-2be01ed66b-df29055c4af0630d.elb.us-east-2.amazonaws.com:443/USER/:::true"
export IRIS_DESCRIPTION="InterSystems OMOP Stage"
cat << 'EOF' > certificateSQLaaS.pem
-----BEGIN CERTIFICATE-----
HBLABLAHBLAHBLA
-----END CERTIFICATE-----
EOF
cat << EOF > 200_populate_iris_source_daimon.sql
INSERT INTO webapi.source( source_id, source_name, source_key, source_connection, source_dialect, username, password)
VALUES (2, '$IRIS_DESCRIPTION', 'IRIS', '$IRIS_JDBC', 'iris', '$IRIS_USER', '$IRIS_PASS');
INSERT INTO webapi.source_daimon( source_daimon_id, source_id, daimon_type, table_qualifier, priority) VALUES (4, 2, 0, 'OMOPCDM54', 0);
INSERT INTO webapi.source_daimon( source_daimon_id, source_id, daimon_type, table_qualifier, priority) VALUES (5, 2, 1, 'OMOPCDM54', 10);
INSERT INTO webapi.source_daimon( source_daimon_id, source_id, daimon_type, table_qualifier, priority) VALUES (6, 2, 2, 'OMOPCDM54_RESULTS', 0);
EOF
docker cp 200_populate_iris_source_daimon.sql broadsea-atlasdb:/docker-entrypoint-initdb.d/200_populate_iris_source_daimon.sql
docker-compose exec broadsea-atlasdb psql -U postgres -f "/docker-entrypoint-initdb.d/200_populate_iris_source_daimon.sql"
docker cp certificateSQLaaS.pem broadsea-hades:/home/ohdsi/
docker cp SSLConfigHades.properties broadsea-hades:/home/ohdsi/SSLConfig.properties
docker-compose exec broadsea-hades bash -c "/usr/bin/keytool -importcert -file /home/ohdsi/certificateSQLaaS.pem -keystore /home/ohdsi/keystore.jks -alias IRIScert -storepass changeit -noprompt"
docker exec ohdsi-webapi bash -c "keytool -importcert -file /tmp/certificateSQLaaS.pem -alias IRIScert2 -keystore /tmp/keystore.jks -storepass changeit -noprompt"
wget http://127.0.0.1/WebAPI/source/refresh/

BroadSea 対 InterSystems OMOP サーバーおよび OHDSI コミュニティへようこそ!

0
0 8
InterSystems 開発者コミュニティは 25059 名の優秀な開発者が参加しているコミュニティです
InterSystems IRIS のプログラマーが学習や知識の共有を行い、最新情報を入手し、共に楽しく成長できる場所です!
記事 Mihoko Iijima · 4月 24, 2023 6m read

開発者の皆さん、こんにちは!

過去に開催した開発者向けウェビナー アーカイブビデオのまとめページを作成しました。

今後もウェビナーを開催していきますのでこのページをブックマークしていただけると嬉しいですlaugh

プレイリストはこちら👉https://www.youtube.com/playlist?list=PLzSN_5VbNaxB39_H2QMMEG_EsNEFc0ASz

2025年開催分:

✅ウェビナー

Connpassから申込が行えます:🤖はじめてのAI開発 ~ゼロからのIRIS環境構築とOpenAI連携チャットボット作り~🤖
残席7席ですので、ぜひお早めにお申し込みください!💨

24
3 749
記事 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月 30, 2025 7m read

img

この記事では、IRIS環境におけるPythonプログラミングの基礎について紹介します。

本題に入る前に、重要なトピックである「Pythonの仕組み」について説明します。これは、IRIS環境でPythonを使用して作業する際に起こりうる問題や制限を理解するのに役立ちます。

すべての記事と例は、以下のgitリポジトリで確認できます: iris-python-article

Pythonの仕組み

インタープリター型言語

Pythonはインタープリター型言語であり、コードはランタイム時に1行ずつ実行されます。スクリプトをインポートする場合でも同様です。

これはどういうことでしょうか? 以下のコードを見てみましょう。

# introduction.py

def my_function():
    print("Hello, World!")

my_function()

このスクリプトを実行すると、Pythonインタープリターはコードを1行ずつ読み取ります。 まず最初に関数 my_function を定義してから、その関数を呼び出すと、コンソールに「Hello, World!」と出力されます。

スクリプトを直接実行している例:

python3 /irisdev/app/src/python/article/introduction.py 

出力は以下のようになります。

Hello, World!

IRIS環境でこのスクリプトをインポートするとどうなるのでしょうか?

Class Article.Introduction Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")

        do ##class(%SYS.Python).Import("introduction")
    }
}

実行しましょう。

iris session iris -U IRISAPP '##class(Article.Introduction).Run()'

出力が表示されます。

Hello, World!

これは、Pythonインタープリターがコードを解釈しながらインポートするためで、最初に関数を定義し、その後に関数を呼び出します。これはスクリプトを直接実行した場合と同じ動作ですが、実行しているのではなくインポートしています

⚠️ 重要な注意事項:関数を呼び出さずにスクリプトをインポートしても、何も起こりません。 関数は定義されますが、明示的に呼び出さない限り実行されません。

分かりましたか? Pythonインタープリターはファイル内のコードを実行しますが、関数を呼び出さなければ、その関数は実行されません。

呼び出さずにインポートする例:

# introduction1.py
def my_function():
    print("Hello, World!")

Pythonインタープリターで実行しましょう。

python3 /irisdev/app/src/python/article/introduction1.py 

出力:

# 関数は定義されていますが、呼び出されていないため、出力はありません

IRIS環境にこのスクリプトをインポートすると:

Class Article.Introduction1 Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")
        do ##class(%SYS.Python).Import("introduction1")
    }
}

実行しましょう。

iris session iris -U IRISAPP '##class(Article.Introduction1).Run()'

関数は定義されていますが、呼び出されていないため、出力はありません。

🤯この微妙な違いが重要な理由

  • Pythonスクリプトをインポートすると、そのスクリプトのコードが実行されます。
    • コードを実行したくない場合があります
  • インポートするとスクリプトが実行されているように見えるかもしれませんが、実際には直接実行されているわけではないため、混乱を招く可能性があります。

インポートのキャッシュ

Pythonスクリプトをインポートすると、Pythonインタープリターがインポートされたスクリプトをキャッシュします。 つまり、同じスクリプトをもう一度インポートすると、そのスクリプトのコードは再実行されず、キャッシュされたバージョンが使用されます。

具体例による説明:

introduction.py スクリプトを再利用しましょう。

# introduction.py
def my_function():
    print("Hello, World!")

my_function()

次に、同じように Article.Introduction クラスを再利用しましょう。

Class Article.Introduction Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")
        do ##class(%SYS.Python).Import("introduction")
    }
}

今度は、同じIRISセッション内で2回続けて実行します。

iris session iris -U IRISAPP 

IRISAPP>do ##class(Article.Introduction).Run()
Hello, World!

IRISAPP>do ##class(Article.Introduction).Run()

IRISAPP>

🤯一体どういうことでしょうか?

はい、「Hello, World!」は一度だけ出力されます!

⚠️ インポートされたスクリプトはキャッシュされています。 つまり、インポートした後にスクリプトを変更しても、IRSセッションが変更されるまで変更は反映されません。

IRISで language tag を使用する場合でも同じです。

Class Article.Introduction2 Extends %RegisteredObject
{

ClassMethod Run() [ Language = python ]
{
    import os

    if not hasattr(os, 'foo'):
        os.foo = "bar"
    else:
        print("os.foo already exists:", os.foo)
}

}

実行しましょう。

iris session iris -U IRISAPP

IRISAPP>do ##class(Article.Introduction2).Run()

IRISAPP>do ##class(Article.Introduction2).Run()
os.foo already exists: bar

なんと、os モジュールはキャッシュされ、foo 属性は存在しないことに再定義されていません。

まとめ

この入門編が、IRISでPythonを使用する際に、特にスクリプトのインポートやキャッシュ処理に関して、予期しない動作が発生する理由を理解する助けになれば幸いです。

IRISでPythonを使う際のポイント:

  • Pythonスクリプトで変更を確認するには、IRISセッションを毎回変更する。
    • これはバグではなく、Pythonの仕様です。
  • スクリプトをインポートするとそのコードが実行されることに気を付ける。

ボーナス

待って! スクリプトをインポートするとキャッシュされる?つじつまが合いません。 language tag = python で作業していて、スクリプトを変更したのにIRISセッションを変更しなくてもうまく動作するのは何故でしょうか?

いい質問です。これは language tag の仕組みが関係しています。language tag は実行するたびにスクリプトをもう一度読み込み、ネイティブのPythonインタープリターで新しい行を入力するかのように、行ごとに実行します。language tag はスクリプトをインポートするわけではなく、Pythonインタープリターを再起動せずに直接スクリプトを実行しているのと同じ動作をします。

例:

Class Article.Introduction2 Extends %RegisteredObject
{
ClassMethod Run() [ Language = python ]
{
    import os

    if not hasattr(os, 'foo'):
        os.foo = "bar"
    else:
        print("os.foo already exists:", os.foo)
}
}

実行しましょう。

iris session iris -U IRISAPP
IRISAPP>do ##class(Article.Introduction2).Run()

IRISAPP>do ##class(Article.Introduction2).Run()
os.foo already exists: bar  

Pythonインタープリターだと、こんな感じになります。

import os

if not hasattr(os, 'foo'):
    os.foo = "bar"
else:
    print("os.foo already exists:", os.foo)

import os
if not hasattr(os, 'foo'):
    os.foo = "bar"
else:
    print("os.foo already exists:", os.foo)

出力:

os.foo already exists: bar # only printed once

いかがでしょうか。

今後の内容 :

  • Pep8
  • モジュール
  • ダンダーメソッド
  • IRISでPythonを動かす
  • ...
0
0 16
記事 Toshihiko Minamoto · 10月 28, 2025 3m read

img

この記事では、マジックメソッドとしても知られるPythonダンダーメソッドについて簡単に解説します。

ダンダーメソッドとは?

ダンダーメソッドは、始めと終わりに2つのアンダースコア(__)が付いているPythonの特殊メソッドです。 このメソッドを使用することで、加算や減算、文字列表現など、組み込みの操作に対するオブジェクトの動作を定義することができます。

よくあるダンダーメソッドには、次が含まれます。

  • __init__(self, ...):オブジェクトの作成時に呼び出されます。
    • ObjectScriptの %OnNew メソッドに似ています
  • __str__(self):オブジェクトを文字列として表現するために、str() 組み込み関数と print によって呼び出されます。
  • __repr__(self):デバッグ用のオブジェクトを表現するために、repr() 組み込み関数によって呼び出されます。
  • __add__(self, other)+ 演算子が使用される際に呼び出されます。
  • __len__(self):オブジェクトの長さを返すために、len() 組み込み関数によって呼び出されます。
  • __getitem__(self, key):インデックス構文を使用してコレクションからアイテムを取得するために呼び出されます。
  • __setitem__(self, key, value):インデックス構文を使用してコレクション内にアイテムを設定するために呼び出されます。
  • ... 他にもたくさんあります。

IRIS環境において、ダンダーメソッドが重要であり関連性があるのはなぜか?

Objectscriptでは、Pythonのようなシンタックスシュガーはありませんが、ダンダーメソッドを使用して同じような動作を実現できます。

例えば、インポートしたPythonモジュールにPythonのリストを返す関数があり、Objectscriptでそれを使用したいとします。 リスト内のアイテムにアクセスするには、__getitem__ ダンダーメソッドを使用する必要があります。

# src/python/article/dunder_example.py
def get_list():
    return [1, 2, 3, 4, 5]
Class Article.DunderExample Extends %RegisteredObject
{

ClassMethod Run()
{
    Set sys = ##class(%SYS.Python).Import("sys")
    do sys.path.append("/irisdev/app/src/python/article")
    set dunderExample = ##class(%SYS.Python).Import("dunder_example")
    set myList = dunderExample."get_list"()
    for i=0:1:myList."__len__"()-1 {
        write myList."__getitem__"(i), !
    }
}

}

実行しましょう。

iris session iris -U IRISAPP '##class(Article.DunderExample).Run()'

出力は以下のようになります。

1
2
3
4
5

これは、IRIS環境でPythonオブジェクトとやり取りするためにダンダーメソッドを使う方法を具体的に示しており、ObjectScript環境内で作業しながらPythonの機能を活用することができます。

ボーナス

ダンダーの優れた使い方の1つは、スクリプトがモジュールとしてインポートされた際にコードが実行されないようにするために、if __name__ == "__main__": ブロックをPythonスクリプトの最後に配置することです。

最初の記事で、スクリプトをインポートするとコードが実行されることを説明しました。 このブロックにより、スクリプトがインポートされたときではなく、直接実行されたときにのみ実行されるコードを定義できます。

例:

# src/python/article/dunder_example.py
def get_list():
    return [1, 2, 3, 4, 5]

if __name__ == "__main__":
    print(get_list())

まとめ

ダンダーメソッドを使えば、PythonのシンタックスシュガーでできることはObjectScriptでも可能です。

0
0 14
記事 Kosaku Ikeda · 10月 26, 2025 14m read

コミュニティの皆さんこんにちは。
 

ベクトル検索関連の処理が完全にノーマークだった私が、一先ず「やってみよう!」との事で、2つの動画のサンプルを実行してみました。
Pythonは初心者なので、アレな箇所があっても目をつぶっていただけると幸いです。

また、間違っている箇所があったら、ご指摘いただけると幸いです。


■参考にした動画

■参考にしたコミュニティ記事

【目的】

本記事では、動画で紹介された内容を実際にIRIS環境上で実行できるよう、具体的な環境構築とコーディングを記載致します。
コミュニティの皆さんが簡単に試せるようになれば幸いです。

またGithubにサンプルソースを配置しているので、必要な方は参考にして下さい。

【準備】

■作業環境

※環境作成方法に問題のない方は、読み飛ばしていただいて構いません。

項目 バージョン情報・他
OS WIndowsServer2019
IRIS IRIS Community 2025.2.0.227.0
Python 3.12.10
開発環境 VS Code 1.105.1
0
0 41
InterSystems公式 Seisuke Nakahashi · 10月 23, 2025

インターシステムズは、InterSystems IRIS® data platformInterSystems IRIS® for HealthTMHealthShare® Health Connect のメンテナンスバージョン 2025.1.2 および 2024.1.5 をリリースしました。今回のリリースでは、最近お知らせした以下の警告や勧告の修正が含まれています。

製品の品質改善のために、開発者コミュニティを通じてぜひご意見をお聞かせください。

ドキュメント

詳細な変更リストとアップグレードチェックリストはこちらのドキュメントをご参照ください(すべて英語, 2025.1):

早期アクセスプログラム (Early Access Programs; EAPs)

0
0 25
記事 Hiroshi Sato · 10月 21, 2025 1m read

これは InterSystems FAQ サイトの記事です。
 

ルーチンやメソッドを実行した際に以下のような<FRAMESTACK>エラーが発生する場合、DOコマンドの発行の入れ子数が多すぎて、それ以上スタック情報を保持できなくなったことを示しています。

<FRAMESTACK> error is reported when the routine has too many nested calls to DO command. You can check the current stack with $STACK value.

可能性として高いのはプログラミング上のミスで再起的なメソッド/ルーチン呼び出しがループしている場合などです。

以下のようなプログラミングを行い、$STACK変数の値を確認することで、スタックのレベルがどのように変化しているのかを確認できます。

0
0 18
記事 Hiroshi Sato · 10月 21, 2025 1m read

これは InterSystems FAQ サイトの記事です。
 

%String型のプロパティをOrder Byの条件にしてクエリーを発行した際のデータは以下のような順番で並べられます。

SELECT * FROM Shop.Order orderby StatusFlag
null
-1
-2
-99
0

これは%String型(文字列型)のプロパティの照合順として正しい振る舞いです。

文字列照合の並び順

文字列プロパティに対し、+ をつけることで、数値照合と同じ照合順を得ることができます。

SELECT * FROM Shop.Order orderby +StatusFlag
null
-99
-1
-2
0
0
0 15
記事 Hiroshi Sato · 10月 21, 2025 1m read

これは InterSystems FAQ サイトの記事です。
 

SQLアクセス( ADO含む)を行う場合は、SQLトランザクションを使用して、トランザクションを制御します。

一方オブジェクトアクセス(ObjectScript)ではtstart / tcommit / trollbackコマンド 
(Native SDK for .NETでは IRIS の TStart(), TCommit(), TRollback() メソッド)
によってトランザクションを制御します。

この2種類のトランザクションモードを混在させて使用することはサポートされていません。

詳細は、以下のドキュメントをご参照ください。

トランザクション管理

また関連するメソッドの以下ドキュメントの注意事項にも

「このメソッドは Native SDK トランザクション・モデルを使用し、ADO.NET/SQL トランザクション・メソッドとは互換性がありません。

この 2 つのトランザクション・モデルを混在させないでください。」

と記載をしております。

Native SDK for .NET のクイック・リファレンス

0
0 13
記事 Hiroshi Sato · 6月 22, 2025 1m read

.pyファイルの中でIRISのEmbedded Pythonを動作させる際にirispythonコマンドで実行する方法はドキュメント上で紹介されていましたので、以前より使用していました。

しかし、普通のpythonコマンドを使用するとうまく実行できなかったのですが、最近その謎(原因)が解けたので紹介します。

これは、Mac特有の問題である可能性が高くWindowsやLinuxでは何の問題もなく実行できるのかもしれません。

エラーは以下のようなエラーです。

  File "/opt/iris/lib/python/iris.py", line 34, in <module>
    from pythonint import *
ImportError: IrisSecureStart failed: IRIS_ATTACH (-21)

 

このエラーの原因は、シェルの実行ユーザーとirisのオーナーが異なることが原因とのことです。

Macで普通にIRISをインストールするとそのオーナーはrootです。

従ってpython3コマンドを実行する時にsudoコマンドでrootになる必要があるということです。

そして以下のような環境変数の設定も必要です。

3
0 78
お知らせ Mihoko Iijima · 10月 2, 2025

開発者の皆さん、こんにちは!今年も開発者コミュニティミートアップを東京にて開催します!📣

【ミートアップ概要】

AI アプリの開発に興味があるけれど、何から始めれば良いか分からない方へ。

RAG、生成 AI などの用語は聞いたことがあるけど、そこまで詳しくない。
興味はあり、自分たちのシステムにも導入してみたい、でも、なかなか時間が取れなくて、学ぶチャンスがない。
そう悩んでいる方いませんか?

そんなあなたに!12月3日(水)AIアプリの開発をテーマとしたミートアップを開催します! 

ミートアップでは、最初に、数々の AI 駆動開発をご経験されている方から、「AI駆動開発の最新情報」についてお話いただきます。
次に、AI アプリ作成のワークショップを通じて、具体的な開発の流れを短時間でご体験いただきます。
最後に、コミュニティメンバーとのネットワーキングの時間を楽しんでいただきます。この時間には、クイズ大会も行う予定です(軽食もご用意いたします)。

AI を「使う」から「創る」側へ。未来を拓く一歩を踏み出しませんか?
ぜひ、AI アプリ作成を体験できるミートアップにご参加ください。

《こんな方におすすめ》

  • RAG、生成AIに興味があり、開発をしてみたい方
  • 具体的に動くコードを見るとワクワクする方  
1
0 96
記事 Mihoko Iijima · 10月 16, 2025 1m read

開発者の皆さん、こんにちは!

この記事では、Developer Hub にまたまた新チュートリアル:InterSystems IRIS for Health:デジタルヘルスの相互運用性 が追加されましたので、体験内容についてご紹介します。(準備不要でブラウザがあれば試せるチュートリアルです!)

チュートリアルでは、InterSystems IRIS for Health を使用しているのですが、IRIS for Health の持つ機能により以下のような相互運用性シナリオを作成できます。

  • HL7、CDA、FHIR、DICOM のデータルーティングを管理
  • HL7 から FHIR への変換、CDA から HL7 への変換、FHIR から CDA への変換
  • 異なるシステムと API との間のデータフローを確立

チュートリアルの中では、あるシステムから入力される HL7 メッセージを他のシステムに送信する流れをご体験いただけます。

また、データ変換が必要な場合の対応方法や HL7 メッセージの一部をテーブルに保存する流れなどもご体験いただけます。

例)HL7 メッセージの一部をテーブルに格納

アカウント作成やログインも不要で  ボタンをクリックするだけで始められます👍

ぜひ、お試しください!​​​​​​

0
0 20
お知らせ Rie Tokue · 10月 16, 2025

 

いつもお世話になっております。

11月の開発者向けウェビナー以下の内容で開催いたします。

タイトル:「組み込みPythonの仕組みと、その効果的な使い方について」

日時:11月20日(木)13時30分~14時

参加費無料・事前登録制

ご登録はこちらから

【概要】

【概要】

組み込みPythonが正式にリリースされてから3年が経過しました。それ以降、多くのエンジニアにより、様々なアイデアが実現されるとともに、その使い方についても良し悪し、向き不向き、といった議論が行われています。今回のウェビナは、組み込みPythonの仕組みを少し深堀りするとともに、この仕組みにより実現する、その「双方向性」を理解した上で、Pythonのエコシステムを最大限に生かしながら開発することのメリットをお伝えする、という内容です。

【こんな方にお勧め】

組み込みPythonに興味をお持ちの方

ご多用中とは存じますが、皆様のご参加をお待ち申し上げております。

インターシステムズジャパン

0
0 30
記事 Toshihiko Minamoto · 10月 16, 2025 2m read

私が先週リリースしたInterSystems Testing Managerの新しいバージョンでは、@Timothy Leavittの優れた
テストカバレッジツールが追加され、私は2025年度Developer Toolsコンテストに出品しました。

こちらは、IPMプロジェクトのユニットテストが、IPMリポジトリでソート順を上書きできると思われる機能をまだカバーしていないことを示すティザー的なスクリーンショットです。

88行目が開発者への警告として赤くハイライトされていることに注目してください。

VS Codeのエクスプローラービューには「バッテリーインジケーター」風のアイコンが表示されており、このクラスのメソッド内の実行可能な行のうち、テストでカバーされているのが76%のみであるため、黄色になっています。 インジケーターにカーソルを合わせると、さらに詳しい情報が表示され、メソッドのカバレッジ(9個中8個)が、エディター内のオプションのテストカバレッジツールバーに第2のインジケーターとして表示されます。

VS Codeでは、しきい値を設定できるほか、赤・黄・緑が区別しにくい場合には、色自体も構成できます。

いかがでしょうか? すでにInterSystemsの%UnitTestフレームワークをご利用中であれば、ぜひご自身でもお試しください。 ご意見・ご感想は大歓迎です。コンテストの投票への投票もぜひお願いします。投票は米国東部時間で8月3日(日)深夜まで受け付けています。

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
記事 Mihoko Iijima · 10月 9, 2025 1m read

開発者の皆さん、こんにちは!

この記事では、Developer Hub にあるチュートリアルに新しいチュートリアル:InterSystems IRIS ベクトル検索を使用した RAG が追加されましたので内容をご紹介します。(準備不要でブラウザがあれば試せるチュートリアルです!)

このチュートリアルでは、生成 AI アプリケーションの精度向上に向けて、ベクトル検索と検索拡張生成(Retrieval Augmented Generation)の活用を体験できます。

具体的には、InterSystems IRIS のベクトル検索機能を活用し、生成 AI チャットボット向けのナレッジベースをサンプルコードを利用して作成します。

また、Streamlit を使用して作成したチャットボットを動かしながら、ナレッジベースの情報を追加することで生成 AI からの回答が変化していくことを確認していきます。

アカウント作成やログインも不要で  ボタンをクリックするだけで始められます👍

チュートリアルへのリンクは「開発者コミュニティのリソース」からも辿れます!

ぜひ、お試しください!​​​​​​

0
0 33
記事 Toshihiko Minamoto · 10月 9, 2025 8m read

少し遅れましたが、モバイルアプリケーションから接続する例を示して Workflow Engine に関する連載記事をようやく締めくくることにします。

前回の記事では、これから説明する例として、患者と担当医師の両方にとって高血圧症などの慢性病状の詳細な管理を可能にするアプリケーションを示しました。 この例では、患者は携帯電話からウェブアプリケーション(基本的に、デバイスに応答するように設計されたウェブページ)にアクセスし、ポータブル血圧計が IRIS インスタンスに送信する測定に基づく通知を受信します。

したがって、IRIS インスタンスへのアクセスは 2 つです。

  • モバイルアプリケーションからのユーザーアクセス。
  • 血圧の測定値を送信するデバイスアクセス。

この記事では、患者が測定値を生成するタスクを管理できる、最初のアクセスを確認します。

モバイルアプリケーションと IRIS の接続

この接続では、IRIS 内でウェブアプリケーションを構成するのが最も単純です。これを行うには、管理ポータルのシステム管理 -> セキュリティ -> アプリケーション -> ウェブアプリケーションからアクセスします。

次に、表示されるリストでアプリケーションの新規作成をクリックすると、以下のような画面が開きます。

この画面で、以下のフィールドを構成しましょう。

  • 名前: IRIS にデプロイされる機能にアクセスできるように公開する URL を定義します。
  • ネームスペース: ウェブアプリケーションを関連付けるネームスペースです。これによって、後で相互運用性プロダクションの機能を利用できるようになります。
  • REST: 公開するものが HTTP 接続を可能にする REST API であるため、このオプションを選択します。
  • ディスパッチクラス: HTTP 呼び出しを受け取り、その処理を決定する ObjectScript クラス。
  • JWT 認証を使用する: このオプションをオンにすると、アプリケーションに定義した URL の /login および /logout エンドポイントが有効になるため、IRIS への呼び出しを認証するための JSON ウェブトークンを取得できるようになります。
  • セキュリティ設定 -> 許可される認証方法: 呼び出しを安全にするためにパスワードを設定します。

Workflow.WS.Service クラスを確認しましょう。

Class Workflow.WS.Service Extends%CSP.REST
{

Parameter HandleCorsRequest = 0;Parameter CHARSET = "utf-8"; XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ] { <Routes> <Route Url="/getTasks" Method="GET" Call="GetTasks" /> <Route Url="/saveTask" Method="POST" Call="SaveTask" /> </Routes> }

ClassMethod OnHandleCorsRequest(url As%String) As%Status { set url = %request.GetCgiEnv("HTTP_REFERER") set origin = $p(url,"/",1,3) // origin = "http(s)://origin.com:port"// here you can check specific origins// otherway, it will allow all origins (useful while developing only)do%response.SetHeader("Access-Control-Allow-Credentials","true") do%response.SetHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS") do%response.SetHeader("Access-Control-Allow-Origin",origin) do%response.SetHeader("Access-Control-Allow-Headers","Access-Control-Allow-Origin, Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control") quit$$$OK }

ClassMethod GetTasks() As%Status { Try { Do##class(%REST.Impl).%SetContentType("application/json") If '##class(%REST.Impl).%CheckAccepts("application/json") Do##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) QuitDo##class(%REST.Impl).%SetStatusCode("200") set sql = "SELECT %Actions, %Message, %Priority, %Subject, TaskStatus_TimeCreated, ID FROM EnsLib_Workflow.TaskResponse WHERE TaskStatus_AssignedTo = ? AND TaskStatus_IsComplete = 0"set statement = ##class(%SQL.Statement).%New(), statement.%ObjectSelectMode = 1set status = statement.%Prepare(sql) if ($$$ISOK(status)) { set resultSet = statement.%Execute($USERNAME) if (resultSet.%SQLCODE = 0) { set tasks = [] while (resultSet.%Next() '= 0) { set task = {"actions": "", "message": "", "priority": "", "subject": "", "creation": "", "id": ""} set task.actions = resultSet.%GetData(1) set task.message = resultSet.%GetData(2) set task.priority = resultSet.%GetData(3) set task.subject = resultSet.%GetData(4) set task.creation = resultSet.%GetData(5) set task.id = resultSet.%GetData(6) do tasks.%Push(task) }
} } set result = {"username": ""} set result.username = $USERNAMEDo##class(%REST.Impl).%WriteResponse(tasks)

} <span class="hljs-keyword">Catch</span> (ex) {
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%SetStatusCode</span>(<span class="hljs-string">"400"</span>)
    <span class="hljs-keyword">return</span> ex.DisplayString()
}

<span class="hljs-keyword">Quit</span> <span class="hljs-built_in">$$$OK</span>

}

ClassMethod SaveTask() As%Status { Try { Do##class(%REST.Impl).%SetContentType("application/json") If '##class(%REST.Impl).%CheckAccepts("application/json") Do##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit// Reading the body of the http call with the person dataset dynamicBody = {}.%FromJSON(%request.Content)

    <span class="hljs-keyword">set</span> task = <span class="hljs-keyword">##class</span>(EnsLib.Workflow.TaskResponse).<span class="hljs-built_in">%OpenId</span>(dynamicBody.<span class="hljs-built_in">%Get</span>(<span class="hljs-string">"id"</span>))
    <span class="hljs-keyword">set</span> sc = task.CompleteTask(dynamicBody.action)

    <span class="hljs-keyword">if</span> <span class="hljs-built_in">$$$ISOK</span>(sc) {	        
        <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%SetStatusCode</span>(<span class="hljs-string">"200"</span>)
        <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>({<span class="hljs-string">"result"</span>: <span class="hljs-string">"success"</span>})         
	}	
    
} <span class="hljs-keyword">Catch</span> (ex) {
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%SetStatusCode</span>(<span class="hljs-string">"400"</span>)
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>({<span class="hljs-string">"result"</span>: <span class="hljs-string">"error"</span>})
}

<span class="hljs-keyword">Quit</span> <span class="hljs-built_in">$$$OK</span>

}

}

ご覧のとおり、WS から必要なすべてのロジックを解決していますが、この方法は推奨されません。ネームスペースに構成されたプロダクションに受信したリクエストを送信することで可能なトレーサビリティを失ってしまうためです。

URLMap セクションを確認すると、2 つのエンドポイントが WS で構成されていることが分かります。

  • getTasks: ユーザーのすべての保留中のタスクを復旧します(使用されている SQL を見ると、ログインしたときに生成される変数から直接ユーザー名を渡しています)。
  • saveTask: ユーザーのタスクへのレスポンスを受信し、EnsLib.Workflow.TaskResponse クラスの CompleteTaks メソッドを実行することで、タスクの終了に進みます。

IRIS の部分では、外部アプリケーションが接続できるようにすべてが構成済みです。

Workflow Engine で外部アプリケーションをテストする

まず、ファイル /shared/hl7/message_1_1.hl7 をパス /shared/in にコピーすることで、HL7 からプロダクションへのメッセージの送信をシミュレーションします。送信するメッセージを確認しましょう。

MSH|^~\&|HIS|HULP|EMPI||20240402111716||ADT^A08|346831|P|2.5.1
EVN|A08|20240402111716
PID|||07751332X^^^MI^NI~900263^^^HULP^PI||LÓPEZ CABEZUELA^ÁLVARO^^^||19560121|F|||PASEO MARIO FERNÁNDEZ 2581 DERECHA^^MADRID^MADRID^28627^SPAIN||555819817^PRN^^ALVARO.LOPEZ@VODAFONE.COM|||||||||||||||||N|
PV1||N
OBX|1|NM|162986007^Pulso^SNM||72|^bpm|||||F|||20240402111716
OBX|2|NM|105723007^Temperatura^SNM||37|^Celsius|||||F|||20240402111716
OBX|3|NM|163030003^Presión sanguínea sistólica^SNM||142|^mmHg|||||F|||20240402111716
OBX|4|NM|163031004^Presión sanguínea diastólica^SNM||83|^mmHg|||||F|||20240402111716

前回の記事を覚えていらっしゃらない方のために説明すると、BPL は収縮期血圧が 140 を超えるか拡張期血圧が 90 を超えるとアラートが生成されるように定義していたため、このメッセージにより、DNI(スペインの身分証明書)が 07751332X である患者に対する警告タスクと、IRIS が新しい通知を受信するまで開いたままになる別の自動タスクが生成されます。

モバイルアプリケーションを確認しましょう。

2 つのタスクが生成されています。1 つは手動タスクとして定義されており、ユーザーによって終了方法が異なります。もう 1 つは自動タスクとして宣言されており、それを終了するには、ユーザーが血圧計で新しい測定を行う必要があります。 HTTP 呼び出しを見てみると、これにどのように JWT を含めているのかが分かります。

ユーザーが保留中のタスクの Accept ボタンをクリックすると、このフローは血圧計の測定値の受信を待機したままとなり、次のステップに進みません。 手動タスクが受け入れられ、血圧計からの新しい測定値を受信し、それがもう一度制限を超えている場合は、システムは患者向けのタスクと、関連する医師向けに危険な状態の可能性を警告するタスクの 2 つの新しい警告タスクを生成します。 :

完璧です! 患者通知システムは素早く簡単にセットアップ済みです。

まとめ

この連載記事でご覧になったとおり、Workflow Engine の機能と InterSystems IRIS の相互運用性機能を合わせることで、他のビジネスソリューションではほとんど実現できないビジネス プロセスの実装において、驚異的な可能性を切り開くことができます。 最大限に活用するためには、技術的な知識が必要となる可能性がありますが、実際に必要な取り組みを投じる価値はあります。

0
0 23
記事 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
記事 Mihoko Iijima · 2月 27, 2025 1m read

皆さん、こんにちは!

2025年2月から開始しました「InterSystems ソリューションセミナー」アーカイブビデオのまとめページを作成しました。

ソリューションセミナーでは、企業の様々な課題に対する解決方法と、それを InterSystems 製品を活用してどのように解決するかについて各回テーマを設けご紹介してまいります。

ぜひご視聴ください!

4
0 134
記事 Toshihiko Minamoto · 10月 1, 2025 6m read

コミュニティの皆さん、こんにちは。
従来のキーワードベースの検索では、ニュアンスのあるドメイン固有のクエリには対応できません。 ベクトル検索であれば、セマンティック認識を利用して、キーワードだけでなくコンテキストにも基づいたレスポンスを AI エージェントで検索して生成することができます。
この記事では、エージェンティック AI RAG(検索拡張生成)アプリケーションを作成手順を紹介します。

実装手順:

  1. エージェントツールを作成する
    • インジェスト機能の追加: ドキュメント(例: InterSystems IRIS 2025.1 リリースノート)を自動的にインジェストしてインデックス作成を行います。
    • ベクトル検索機能の実装
  2. ベクトル検索エージェントを作成する
  3. Triage(メインエージェント)に渡す
  4. エージェントを実行する

1. エージェントツールを作成する
1.1 - 
ドキュメントの取り込みを実装します。ドキュメントの取り込みとインデックス作成を自動化します。

インジェストツールは以下のコードで実装します。

defingestDoc(self):#Check if document is defined, by selecting from table#If not defined then INGEST document, Otherwise back
        embeddings = OpenAIEmbeddings()	
        #Load the document based on the fle type
        loader = TextLoader("/irisdev/app/docs/IRIS2025-1-Release-Notes.txt", encoding='utf-8')      
    documents = loader.load()        
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=<span class="hljs-number">400</span>, chunk_overlap=<span class="hljs-number">0</span>)
    
    texts = text_splitter.split_documents(documents)
                   
    <span class="hljs-comment">#COLLECTION_NAME = "rag_document"</span>
    db = IRISVector.from_documents(
        embedding=embeddings,
        documents=texts,
        collection_name = self.COLLECTION_NAME,
        connection_string=self.CONNECTION_STRING,
    )

    db = IRISVector.from_documents(embedding=embeddings,documents=texts, collection_name = self.COLLECTION_NAME, connection_string=self.CONNECTION_STRING,)</code></pre>

ベクトル検索エージェントは、指定されたリポジトリフォルダから「New in InterSystems IRIS 2025.1」を IRIS Vector Store に自動的に取り込んでインデックスを作成します。この操作はそのデータがまだ存在しない場合にのみ実行されます。


以下のクエリを実行して、ベクトルストアから必要なデータを取得します。

SELECTid, embedding, document, metadata
FROM SQLUser.AgenticAIRAG


1.2 - ベクトル検索機能の実装
以下のコードはエージェントの検索機能を実装します。

defragSearch(self,prompt):#Check if collections are defined or ingested done.# if not then call ingest method
        embeddings = OpenAIEmbeddings()	
        db2 = IRISVector (
            embedding_function=embeddings,    
            collection_name=self.COLLECTION_NAME,
            connection_string=self.CONNECTION_STRING,
        )
        docs_with_score = db2.similarity_search_with_score(prompt)
        relevant_docs = ["".join(str(doc.page_content)) + " "for doc, _ in docs_with_score]
    <span class="hljs-comment">#Generate Template</span>
    template = <span class="hljs-string">f"""
    Prompt: <span class="hljs-subst">{prompt}</span>
    Relevant Docuemnts: <span class="hljs-subst">{relevant_docs}</span>
    """</span>
    <span class="hljs-keyword">return</span> template</code></pre>


Triage エージェントは、受信したユーザークエリを処理し、それを Vector Search Agent に委任します。このエージェントは、セマンティック検索を実行して、最も関連性の高い情報を取得します。


2 - ベクトルストアエージェントを作成する

以下のコードは、以下の要素を含む vector_search_agent を実装します。

  • エージェントを調整するためのカスタム handoff_descriptions
  • 明確な演算 instructions
  • iris_RAG_search ツール(ドキュメントの取り込みとベクトル検索操作に irisRAG.py を使用)
@function_tool    @cl.step(name = "Vector Search Agent (RAG)", type="tool", show_input = False)asyncdefiris_RAG_search():"""Provide IRIS Release Notes details,IRIS 2025.1 Release Notes, IRIS Latest Release Notes, Release Notes"""ifnot ragOprRef.check_VS_Table():
                 #Ingest the document first
                 msg = cl.user_session.get("ragclmsg")
                 msg.content = "Ingesting Vector Data..."await msg.update()
                 ragOprRef.ingestDoc()
        <span class="hljs-keyword">if</span> ragOprRef.check_VS_Table():
             msg = cl.user_session.get(<span class="hljs-string">"ragclmsg"</span>)
             msg.content = <span class="hljs-string">"Searching Vector Data..."</span>
             <span class="hljs-keyword">await</span> msg.update()                 
             <span class="hljs-keyword">return</span> ragOprRef.ragSearch(cl.user_session.get(<span class="hljs-string">"ragmsg"</span>))   
        <span class="hljs-keyword">else</span>:
             <span class="hljs-keyword">return</span> <span class="hljs-string">"Error while getting RAG data"</span>
vector_search_agent = Agent(
        name=<span class="hljs-string">"RAGAgent"</span>,
        handoff_description=<span class="hljs-string">"Specialist agent for Release Notes"</span>,
        instructions=<span class="hljs-string">"You provide assistance with Release Notes. Explain important events and context clearly."</span>,
        tools=[iris_RAG_search]
)</code></pre>


3 - Triage(メインエージェント)に渡す
以下のコードは、処理済みのクエリを Triage エージェント(メインコーディネーター)に渡すためのハンドオフプロトコルを実装します。

 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]
    )


4 - エージェントを実行する

以下のコードは次の内容を実行します。

  1. ユーザー入力を受け取る
  2. triage_agent  を呼び出す
  3. クエリを Vector_Search_Agent に送信して処理する
@cl.on_message
async def main(message: cl.Message):
    """Process incoming messages and generate responses."""
    # Send a thinking message
    msg = cl.Message(content="Thinking...")
    await msg.send()
agent: Agent = cast(Agent, cl.user_session.get(<span class="hljs-string">"agent"</span>))
config: RunConfig = cast(RunConfig, cl.user_session.get(<span class="hljs-string">"config"</span>))

# Retrieve the chat history from the session.
history = cl.user_session.get(<span class="hljs-string">"chat_history"</span>) or []

# Append the user'<span class="hljs-keyword">s</span> message to the history.
history.append({<span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: message.content})

# Used by RAG agent
cl.user_session.<span class="hljs-keyword">set</span>(<span class="hljs-string">"ragmsg"</span>, message.content)
cl.user_session.<span class="hljs-keyword">set</span>(<span class="hljs-string">"ragclmsg"</span>, msg)

<span class="hljs-keyword">try</span>:
    <span class="hljs-keyword">print</span>(<span class="hljs-string">"\n[CALLING_AGENT_WITH_CONTEXT]\n"</span>, history, <span class="hljs-string">"\n"</span>)
    result = Runner.run_sync(agent, history, run_config=config)
           
    response_content = result.final_output
    
    # Update the thinking message with the actual response
    msg.content = response_content
    await msg.update()

    # Append the assistant'<span class="hljs-keyword">s</span> response to the history.
    history.append({<span class="hljs-string">"role"</span>: <span class="hljs-string">"developer"</span>, <span class="hljs-string">"content"</span>: response_content})
    # NOTE: Here we are appending the response to the history <span class="hljs-keyword">as</span> a developer message.
    # This is a BUG in the agents library.
    # The expected behavior is to append the response to the history <span class="hljs-keyword">as</span> an assistant message.

    # Update the session with the <span class="hljs-keyword">new</span> history.
    cl.user_session.<span class="hljs-keyword">set</span>(<span class="hljs-string">"chat_history"</span>, history)
    
    # Optional: Log the interaction
    <span class="hljs-keyword">print</span>(f<span class="hljs-string">"User: {message.content}"</span>)
    <span class="hljs-keyword">print</span>(f<span class="hljs-string">"Assistant: {response_content}"</span>)
    
except Exception <span class="hljs-keyword">as</span> e:
    msg.content = f<span class="hljs-string">"Error: {str(e)}"</span>
    await msg.update()
    <span class="hljs-keyword">print</span>(f<span class="hljs-string">"Error: {str(e)}"</span>)</code></pre>


実際の動作をご覧ください

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

0
0 17
お知らせ Mihoko Iijima · 9月 30, 2025

開発者の皆さん、こんにちは!本日(10月1日)よりコンテストへご応募いただけます💨

✅コンテスト詳細👉「第3回 InterSystems Japan 技術文書ライティングコンテスト 開催!

✅応募方法👉「記事の投稿方法:第 1 回技術文書ライティングコンテスト

✅応募記事一覧👉「技術文書ライティングコンテスト一覧

0
0 27
記事 Toshihiko Minamoto · 9月 30, 2025 6m read

この連載記事を終えていなかったことに気付きました!

GIF de Shame On You Meme | Tenor

今日の記事では、フロントエンドから最適なオプションを選択できるように、テキストに最も類似する ICD-10 診断を抽出するプロダクションプロセスについて説明します。

診断の類似度検索:

アプリケーション内で、HL7 で受け取った診断リクエストを示す画面から、医療従事者が入力したテキストに最も近い ICD-10 診断を検索できます。

検索プロセスを高速化するために、HL7 メッセージを取得する際に受信した診断をベクトル化したテキストをデータベースに保存しました。 これを行うために、メッセージから診断コードを抽出し、ベクトルを生成するメソッドにそれを送信する単純な BPL を実装しました。

受信した診断をベクトル化するコードは以下のようになります。

ClassMethod GetEncoding(sentence As %String) As %String [ Language = python ]
{
        import sentence_transformers
        # create the model and form the embeddings
        model = sentence_transformers.SentenceTransformer('/iris-shared/model/')
        embeddings = model.encode(sentence, normalize_embeddings=True).tolist() # Convert search phrase into a vector# convert the embeddings to a stringreturn str(embeddings)
}

こうすることで、診断をベクトル化し、検索するたびにもう一度ベクトル化する必要がなくなります。 このとおり、ダウンロードしたモデルでベクトルを生成するために  sentence_transformer  ライブラリを使用しています。

受信した診断がすべてベクトル化されてデータベースに保存されているため、SELECT クエリを実行するだけで、受信した診断に最も近い ICD-10 診断を抽出できます。

では、ウェブサービスに公開されている、最も類似する 25 件の診断を返すメソッドのコードを見てみましょう。

ClassMethod GetCodeOptions(idRequest As%String) As%Status
{
	set ret = $$$OKtry {
        set sql = 
            "SELECT TOP 25 * FROM (SELECT C.CodeId, C.Description, VECTOR_DOT_PRODUCT(C.VectorDescription, R.VectorDescription) AS Similarity FROM ENCODER_Object.Codes C, ENCODER_Object.CodeRequests R WHERE R.ID = ?) WHERE Similarity > 0.5 ORDER BY Similarity DESC"set statement = ##class(%SQL.Statement).%New()
		$$$ThrowOnError(statement.%Prepare(sql))
        set rs = statement.%Execute(idRequest)

        set array = []
        while rs.%Next() {
            do array.%Push({
                    "CodeId": (rs.%Get("CodeId")),
                    "Description": (rs.%Get("Description")),
                    "Similarity": (rs.%Get("Similarity"))
                })
        }
        set%response.Status = ..#HTTP200OK
        write array.%ToJSON()

    } catch ex {
        set%response.Status = ..#HTTP400BADREQUEST
        return ex.DisplayString()
    }
    quit ret
}

クエリを見てみると、検索を 25 件に制限し、類似度が 0.5 を超える診断に限定しているのがわかります。 類似度計算には、VECTOR_COSINE を使用することも可能ですが、VECTOR_DOT_PRODUCT  メソッドを選択しました。 この場合、今のところこれらに大きな違いは見つかっていません。

検索結果を以下に示します。

LLM を使って診断を特定する

ここまでは、完璧に特定される診断の単純な検索しか行っていませんが、 自由記述形式のテキストから直接診断を特定することは可能でしょうか?

では試してみましょう!

この機能では、選択した LLM モデルに質問を送信する API を提供する Ollama を使用します。 docker-compose.yml ファイルを見ると、Ollama コンテナーの宣言があります。

## llm locally installed  ollama:    build:      context:.      dockerfile:ollama/Dockerfile    container_name:ollama    volumes:    -./ollama/shared:/ollama-shared    ports:      -"11434:11434"

まず、コンテナーデプロイに LLM llama3.2 がダウンロードされることが定義されています。 その理由は? テストでは最も良いパフォーマンスを見せると思ったからです。

これが、コンテナーがデプロイされたときに実行する entrypoint.sh  ファイルのコンテンツとなります。

#!/bin/bash
echo "Starting Ollama server..."
ollama serve &
SERVE_PID=$!

echo "Waiting for Ollama server to be active..."
while ! ollama list | grep -q 'NAME'; do
  sleep 1
done

ollama pull llama3.2

wait $SERVE_PID

Ollama の機能を活用するために、自由形式のテキストを分析する画面を、LLM を使って入力されたテキストから診断を抽出するように変更しました。

また、llama3.2 が診断を直接抽出するために必要なプロンプトを作成するメソッドを追加して、ビジネスプロセスを変更しました。

Method AnalyzeText(text As %String, analysisId As %String, language As %String) As %String [ Language = python ]
{
    import sentence_transformers
    import iris
    import requests
<span class="hljs-keyword">try</span>:
    url = <span class="hljs-string">"http://ollama:11434/api/generate"</span>
    data = {
        <span class="hljs-string">"model"</span>: <span class="hljs-string">"llama3.2"</span>,
        <span class="hljs-string">"prompt"</span>: <span class="hljs-string">"Extrae únicamente los diagnósticos del siguiente texto separándolos por , y sin añadir interpretaciones: "</span>+text,
        <span class="hljs-string">"stream"</span>: <span class="hljs-keyword">False</span>
    }
    response = requests.post(url, json=data)
    analyzedText = response.json()
    
    model = sentence_transformers.SentenceTransformer(<span class="hljs-string">'/iris-shared/model/'</span>)
    phrases = analyzedText[<span class="hljs-string">'response'</span>].split(<span class="hljs-string">","</span>)
    sqlsentence = <span class="hljs-string">""</span>
    <span class="hljs-comment"># iris.cls("Ens.Util.Log").LogInfo("ENCODER.BP.AnalyzeTextProcess", "AnalyzeText", "Starting process")</span>
    <span class="hljs-keyword">for</span> phraseToAnalyze <span class="hljs-keyword">in</span> phrases :
        <span class="hljs-keyword">if</span> phraseToAnalyze != <span class="hljs-string">""</span>:
            embedding = model.encode(phraseToAnalyze, normalize_embeddings=<span class="hljs-keyword">True</span>).tolist()
            sqlsentence = <span class="hljs-string">"INSERT INTO ENCODER_Object.TextMatches (CodeId, Description, Similarity, AnalysisId, RawText) SELECT TOP 50 * FROM (SELECT CodeId, Description, VECTOR_DOT_PRODUCT(VectorDescription, TO_VECTOR('"</span>+str(embedding)+<span class="hljs-string">"', DECIMAL)) AS Similarity, '"</span>+analysisId+<span class="hljs-string">"', '"</span>+phraseToAnalyze+<span class="hljs-string">"' FROM ENCODER_Object.Codes) ORDER BY Similarity DESC"</span>
            stmt = iris.sql.prepare(<span class="hljs-string">"INSERT INTO ENCODER_Object.TextMatches (CodeId, Description, Similarity, AnalysisId, RawText) SELECT TOP 50 * FROM (SELECT CodeId, Description, VECTOR_DOT_PRODUCT(VectorDescription, TO_VECTOR(?, DECIMAL)) AS Similarity, ?, ? FROM ENCODER_Object.Codes) WHERE Similarity &gt; 0.65 ORDER BY Similarity DESC"</span>)                    
            rs = stmt.execute(str(embedding), analysisId, phraseToAnalyze)        
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> err:
    iris.cls(<span class="hljs-string">"Ens.Util.Log"</span>).LogInfo(<span class="hljs-string">"ENCODER.BP.AnalyzeTextProcess"</span>, <span class="hljs-string">"AnalyzeText"</span>, repr(err))
    <span class="hljs-keyword">return</span> repr(err)

<span class="hljs-keyword">return</span> <span class="hljs-string">"Success"</span>

}

このプロンプトは非常に単純なものであるため、必要に応じて改善し、調整することも可能ですが、その選択はユーザーにお任せします。 このメソッドはカンマ区切りで LLM レスポンスを取得し、見つかった診断をベクトル化してデータベースに保存します。 以下は結果の例です。

右下の方に、左下のテキストに関して LLM が見つけたものがすべて表示されています。

これで、診断をコーディングするのに役立つ、LLM モデルを使ったアプリケーションが完成しました。 ここで見たように、実装はまったく複雑ではなく、より複雑で包括的なソリューションを構築するための十分な基礎となります。

お読みいただきありがとうございました!

0
0 20
質問 m.fuku · 9月 24, 2025

IRIS Communityの再インストールを行ったところ

tomcat上で動作するJavaのWEBアプリケーションから

JDBC経由でのDB接続ができなくなりました。

他のSQLクライアントツールからは問題なく接続できていますので

接続情報やDBの起動状態には問題ないと考えています。

解決策をご指導頂ければ幸いです。

■環境

IRISバージョン:IRIS_Community-2025.2.0.227.0-win_x64

JDKバージョン:11.0.28

JDBCドライバ:intersystems-jdbc-3.2.0.jar

■事前作業

ライセンス有効期限が切れてサービス起動できなくなったため、以下投稿と同様、再インストールを行っています。

https://jp.community.intersystems.com/post/iris%E3%82%A4%E3%83%B3%E3%82…

ちなみに前回利用したバージョン(IRIS_Community-2024.2.0.247.0-win_x64)では

以下エラー(messages.log)となりサービス起動できなかったため

7
0 42
お知らせ Mihoko Iijima · 9月 25, 2025

開発者の皆さんこんにちは!

技術文書ライティングコンテストの開始(10月1日)まであと少しとなりました!💨

このお知らせでは、今年の賞品を発表いたします!

👀

🎁賞品情報🎁

審査員投票とコミュニティメンバーからの「いいね」の数の合計で順位を決定します。

1位~3位を受賞された方は、各順位に記載された賞品の中からお好きな1点をお選びいただけます。(1位の方は1~3位の賞品を、2位の方は2~3位の賞品をお選びいただけます)

🥇 1位

  • リカバリーウェア TENTIAL BAKUNEシリーズ
  • スマートリング Re・De Ring
  • アウトドアの体験ギフト EXCITING Gift Premium -エキサイティング プレミアム

🥈 2位

  • MYTREX DR. HEAT NECK
  • ソーダーストリーム TERRA スターターキット
  • アウトドアの体験ギフト EXCITING Gift -エキサイティング

🥉 3位

  • Bluetoothスピーカー
  • モレスキン クラシックダイアリー Large
  • スタバギフトカード(5000円)

🎁参加賞:投稿いただいた方全員に「モバイルバッテリー」をプレゼント!

1回目、2回目とは異なる賞品を!ということで賞品決めチームでいろいろ探してみました👀

高得点を狙う場合は、早めの投稿がおすすめです!(コミュニティメンバーからの「いいね」ポイントをより多くゲットできます!)

0
0 52
記事 Toshihiko Minamoto · 9月 25, 2025 8m read

REST レスポンスから IRIS データーベースに情報を保存する新しいプロジェクトがあります。 少なくとも 20 以上の個別の REST エンドポイントの情報を同期する必要があるため、それらのエンドポイントの結果を保存するために、ほぼ同じ数の ObjectScript クラスを作成しなければなりません。

ChatGPT を使って、それらのクラスを作成することはできますか? 答えは「できます」です。生成 AI を使って便利なものを作るのは初めてなので、素晴らしい機会だと思います。 スープを飲むキリンの写真を生成するのには飽きてきました…。

以下のようにして実現しました。

  • エンドポイントをクエリするために、「curl」を使ってサンプルの JSON REST の呼び出し出力を取得する。
  • ChatGPT にアクセスする(InterSystems は社員に会社全体で使用できるライセンスを提供しています)。
  • ChatGPT と以下のように会話する。

以下のリクエストを「Message ChatGPT」プロンプトに入力しました。

私: 次の JSON のデータを格納する ObjectScript クラスを生成してください。

JSON を入力する前に Return キーを押すと、ChatGPT から次のように指示されました。

ChatGPT: ObjectScript クラスの生成が必要な JSON 構造を提供してください。 サンプルの JSON オブジェクトがある場合は、それを貼り付けてください。それに対応する InterSystems ObjectScript クラスを作成します。

プロンプトに以下の内容を入力しました。

{
  "id": 335818632,
  "appId": 113,
  "appName": "Batch",
  "contentId": 182455380233,
  "subject": "Test Subject",
  "name": "Test email campaign name",
  "counters": {
    "processed": 3910,
    "deferred": 86,
    "unsubscribed": 17,
    "statuschange": 17,
    "bounce": 153,
    "mta_dropped": 8,
    "dropped": 299,
    "delivered": 3749,
    "sent": 3903,
    "click": 16,
    "open": 323
  },
  "lastProcessingFinishedAt": 1733232499000,
  "lastProcessingStartedAt": 1733232303000,
  "lastProcessingStateChangeAt": 1733232499000,
  "numIncluded": 4201,
  "processingState": "DONE",
  "scheduledAt": 1733232300000,
  "type": "BATCH_EMAIL"
}

ChatGPT の返答:

提供された JSON 構造にマッピングする InterSystems ObjectScript クラスです。

Class MyNamespace.MyJSONData Extends (%Persistent, %JSON.Adaptor)
{
Property id As%Integer;Property appId As%Integer;Property appName As%String;Property contentId As%Integer;Property subject As%String;Property name As%String;Property counters As MyNamespace.Counters;Property lastProcessingFinishedAt As%BigInt;Property lastProcessingStartedAt As%BigInt;Property lastProcessingStateChangeAt As%BigInt;Property numIncluded As%Integer;Property processingState As%String;Property scheduledAt As%BigInt;Property type As%String;ClassMethod%OnJSONImport(jsonObject As%Object, instance As MyNamespace.MyJSONData) As%Status
{
    Quit ..%JSONImport(jsonObject, .instance)
}
}

Class MyNamespace.Counters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As%Integer;Property deferred As%Integer;Property unsubscribed As%Integer;Property statuschange As%Integer;Property bounce As%Integer;Property mta_dropped As%Integer;Property dropped As%Integer;Property delivered As%Integer;Property sent As%Integer;Property click As%Integer;PropertyopenAs%Integer;
}

説明も提示されました。

このコードは基礎として最適ですが、自分の「MyPkg」というパッケージに対応するようにし、クラスに「EmaiCampaign」という名前を付けたいと思います。 また、NI を少々使用すると、「%BigInt」プロパティを生成した値が実は Linux 時間であり、プロパティは「%Library.PosixTime」である必要があることが分かります。

以下のようにリクエストしました(「~ください」と機会になぜ言ってしまうのかわかりませんが、親にそう育てられたからでしょう)

私: パッケージ名を「MyPkg」、クラスを「EmailCampaign」に変えて、もう一度クラスを作成し直してください。 また、プロパティの型を %BigInt から %Library.PosixTime に変更してください。

ChatGPT の返答:

指定されたパッケージ名とクラス名で ObjectScript クラスを更新しました。また、タイムスタンププロパティを %Library.PosixTime に更新しました。

Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As%Integer;Property appId As%Integer;Property appName As%String;Property contentId As%Integer;Property subject As%String;Property name As%String;Property counters As MyPkg.Counters;Property lastProcessingFinishedAt As%Library.PosixTime;Property lastProcessingStartedAt As%Library.PosixTime;Property lastProcessingStateChangeAt As%Library.PosixTime;Property numIncluded As%Integer;Property processingState As%String;Property scheduledAt As%Library.PosixTime;Property type As%String;ClassMethod%OnJSONImport(jsonObject As%Object, instance As MyPkg.EmailCampaign) As%Status
{
    Quit ..%JSONImport(jsonObject, .instance)
}
}

Class MyPkg.Counters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As%Integer;Property deferred As%Integer;Property unsubscribed As%Integer;Property statuschange As%Integer;Property bounce As%Integer;Property mta_dropped As%Integer;Property dropped As%Integer;Property delivered As%Integer;Property sent As%Integer;Property click As%Integer;PropertyopenAs%Integer;
}

説明も提示されました。

コードをそのまま VSCode にコピーすると、波括弧に関する構文の問題がハイライトされました。 ObjectScript クラスでは内部クラス定義を使用できません。

ChatGPT に、MyPkg.Counters 内部クラスを別のクラスにして「波括弧」の問題を修正するように頼んでも「no-op」になるため、人の手でそのクラスを別のファイルに移動する必要があります。 ChatGPT は次のリクエストの後にそのクラスの名前を変更しました。

私: 記述されたとおりのクラスはコンパイルしません。 MyPkg.Counters 内部クラスをメインクラスから独自のクラスに移動して、MyPkg.CampaignCounters に名前を変更できますか?

ChatGPT の返答:

MyPkg.CampaignCounters クラスを独自のクラスに移動して、ObjectScript コードを更新しました。

Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As%Integer;Property appId As%Integer;Property appName As%String;Property contentId As%Integer;Property subject As%String;Property name As%String;Property counters As MyPkg.CampaignCounters;Property lastProcessingFinishedAt As%Library.PosixTime;Property lastProcessingStartedAt As%Library.PosixTime;Property lastProcessingStateChangeAt As%Library.PosixTime;Property numIncluded As%Integer;Property processingState As%String;Property scheduledAt As%Library.PosixTime;Property type As%String;ClassMethod%OnJSONImport(jsonObject As%Object, instance As MyPkg.EmailCampaign) As%Status
{
    Quit ..%JSONImport(jsonObject, .instance)
}
}

Class MyPkg.CampaignCounters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As%Integer;Property deferred As%Integer;Property unsubscribed As%Integer;Property statuschange As%Integer;Property bounce As%Integer;Property mta_dropped As%Integer;Property dropped As%Integer;Property delivered As%Integer;Property sent As%Integer;Property click As%Integer;PropertyopenAs%Integer;
}

上記のコードを VSCode に貼り付けてコンパイルすると、コンパイラーは「%Object」という名前の型が ObjectScript に存在しないと通知しました。 IRIS ドキュメントによると、%JSON.Adaptor の %JSONImport() メソッドの最初のパラメーターは柔軟に設定できるようです。

「入力引数は、文字列またはストリームとしての JSON か、%DynamicAbstractObject のサブクラスのいずれかです。

最初のクラスで %OnJSONImport() の最初のパラメーターの「As %Object」プロパティを取り除き、2 つのクラスを個別のファイルに分けると、MyPkg.EmailCampaign クラスが正しくコンパイルされました。 ただし、MyPkg.CampaignCounters はコンパイルしませんでした。ChatGPT は(まだ)アンダースコアが ObjectScript で特殊文字(文字列連結演算子)であることを知りません。  

では、そのことを教えましょう。

私: ObjectScript では、アンダースコア文字「_」は特殊文字です。 名前にアンダースコアが使用されているプロパティをキャメルケース表記に変更してアンダースコアを削除してください。

ChatGPT の返答:

アンダースコアが使用された名前をキャメルケース表記に変換したプロパティでコードを更新しました。

Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As%Integer;Property appId As%Integer;Property appName As%String;Property contentId As%Integer;Property subject As%String;Property name As%String;Property counters As MyPkg.CampaignCounters;Property lastProcessingFinishedAt As%Library.PosixTime;Property lastProcessingStartedAt As%Library.PosixTime;Property lastProcessingStateChangeAt As%Library.PosixTime;Property numIncluded As%Integer;Property processingState As%String;Property scheduledAt As%Library.PosixTime;Property type As%String;ClassMethod%OnJSONImport(jsonObject As%Object, instance As MyPkg.EmailCampaign) As%Status
{
    Quit ..%JSONImport(jsonObject, .instance)
}
}

Class MyPkg.CampaignCounters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As%Integer;Property deferred As%Integer;Property unsubscribed As%Integer;Property statusChange As%Integer;Property bounce As%Integer;Property mtaDropped As%Integer;Property dropped As%Integer;Property delivered As%Integer;Property sent As%Integer;Property click As%Integer;PropertyopenAs%Integer;
}

説明も提示されました。

ChatGPT は、依頼されたことだけでなく、2 語で構成されたプロパティ名もキャメルケース表記に変更してくれました(statuschange)。同じように命名されているメインクラスの他のプロパティもすべてキャメルケース表記なので、この変更を維持します。

最後に生成されたコードでは 2 つの個別のファイルに分けられ(また %OnJSONImport() メソッドの定義から「As %Object」定義を削除して)、すべてのコードが正しくコンパイルされるようになりました。

これらの生成されたクラスは、求めているものの基礎として最適なコードです。このプロジェクトの他の REST データソースでもこのプロセスを繰り返して、大量の不要な入力作業を省きたいと思います。

0
0 23
記事 Hiroshi Sato · 9月 25, 2025 1m read

これは InterSystems FAQ サイトの記事です。
 

$System.Encryption.Base64Encodeに指定するデータは、UTF8にエンコードして渡す必要があります。

日本語が含まれるローカル変数をこのメソッドの引数として指定すると<ILLEGAL VALUE>エラーが返ります。

以下のようにUTF8に変換後引数として渡す必要があります。

 set wLineData=$ZCONVERT(wLineData, "O", "UTF8")

詳しくは以下をご参照ください。

ドキュメント: $System.Encryption.Base64Encodeの説明

0
0 16
記事 Hiroshi Sato · 9月 25, 2025 2m read

これは InterSystems FAQ サイトの記事です。
 

何の設定も行なっていない場合、GROUP BYやDISTINCTで指定したフィールドは大文字小文字を区別せずに全て大文字として処理されます。

これはGROUP BYやDISTINCTのグループ化がフィールドに対して定義された照合タイプに基づいて行われ、その文字列照合の既定値がSQLUPPERになっているためです。

以下のドキュメントに説明がある通り、DISTINCT は、フィールドに対して定義された照合タイプに基づいて、文字列値をグループ化します。

大文字/小文字の区別と DISTINCT の最適化

これを変更する方法は、以下の3種類になっています。

(A) %SQLSTRING または %EXACT照合関数を使用する
(B) フィールドの文字列照合を SQLSTRINGに変更する
 また、フィールドにインデックスが設定されている場合には、インデックスの文字列照合も SQLSTRINGに変更する
(C) 管理ポータルで設定を変更する 

※(B)について補足
・文字列照合をEXACTに設定しても動作しますが、一般的にはSQLSTRINGの使用が推奨されています。
・フィールドの文字列照合とインデックスの文字列照合は、同じ設定にすることが推奨されています。

※(C)の設定手順

0
0 21
記事 Mihoko Iijima · 9月 23, 2025 4m read

開発者の皆さん、こんにちは!

10月1日(水)から今年の「技術文書ライティングコンテスト」が始まります!💨みなさま、応募の準備は進んでいますか??👀

この記事では、昨年開催したコンテストにご応募いただいた素晴らしい作品を審査員コメントを添えてご紹介します。

(審査員コメントは昨年 11 月に開催したミートアップ内で行ったコンテスト表彰式で紹介した内容です)

@Akio Hashimoto さんが投稿された「Embedded Python を利用する時の注意点 

審査員コメント:

Embedded Pythonをこれから操作される人が同様の問題に遭遇したときの解決策が提示されているありがたい記事だと感じました。

昨年もコンテストに投稿いただき、今年同様に、これから体験される方向けの道標となるような素晴らしい記事を投稿いただいています。

@Yusuke Kojima さんが投稿された「FHIR Object Modelを使ったInteroperability開発

審査員コメント:

0
0 33
お知らせ Mihoko Iijima · 9月 9, 2025

開発者の皆さん、こんにちは!

今年で 3 回目となりますが、✍技術文書ライティングコンテスト✍ 開催します!

📣9/26更新:賞品、参加賞決定しました!

📣 9/29更新:コンテスト投稿一覧ページ準備完了です!

📣 11/6更新:応募期間を1週間延長し、応募締切は11月24日(月)に変更しました!

テーマはいつもと同じで InterSystems IRIS/InterSystems IRIS for Health に関連した内容であればどのような内容でもご応募いただけます。

🎁 参加賞:投稿いただいた方全員に「モバイルバッテリー」をプレゼント!

🏆 特別賞:選ばれた3作品に、今年も特別賞あります!詳細はこちら

コンテストへの応募はとても簡単です。2025年10月1日(水)~11月1724日(月)までの期間に開発者コミュニティに「IRISに関連した記事」を投稿してください。

もちろん!今年も🍆ボーナスポイント🍆あります!こちらをご参照ください!

1
0 100