#ObjectScript

0 关注者 · 147 帖子

InterSystems ObjectScript 是一种脚本语言,可使用 InterSystems 数据平台的任何数据模型(对象、关系、键值、文档、全局变量/Global)处理数据,并在 InterSystems 数据平台上为服务器端应用程序开发业务逻辑。

文档。

文章 John Pan · 7 分鐘 前 8m read

1. 一个鼓舞人心的例子

嵌入式 Python 已经存在有一段时间了。你可能已经按照教程学习过它。然而,如果你曾在实际开发工作中尝试将 Python 与 ObjectScript 结合使用,你可能遇到过出现如下错误信息的情况:

USER>Do##class

这是一段信息量有限的冗长字符串。从这条消息中,我们仅能得知:发生了 Python 错误,其中(a)错误类型为 ZeroDivisionError;(b)错误消息为 "division by zero"。

那么,对于我们开发者来说,这些信息是否足够了呢?

其实,有经验的开发者会搜索Python代码中所有出现除法运算的位置。如果代码库中的Python代码量有限,或者涉及的除法运算数量有限,这绝对是可以调试的。 但是,如果我们面对的是一个包含大量 Python 代码的代码库,且对该代码库不够熟悉,无法根据错误类型和错误信息缩小搜索范围,那该怎么办?那简直就是一场噩梦!

在本文中,我们将深入探讨如何调试那些深埋在嵌入式 Python 调用堆栈中的错误。

2. 一些行不通的方法

你可能会想尝试各种调试技巧。但在开始“折腾”之前,先来看看一些已知行不通的方法。

2.1 使用 Python 的 sys.excepthook

如果你熟悉 Python,可能知道 Python 会在每次发生未处理的错误时(即未被 代码块捕获的错误)调用 sys.excepthook()

0
0 1
文章 John Pan · 9 分鐘 前 5m read

您的应用中有一些自由文本输入框,您是否希望能够高效地对其进行搜索? 之前尝试过一些方法,却发现它们根本无法满足客户的性能需求? 我是否有一个奇招能解决您所有的问题? 您难道还不知道吗!? 我所做的,就是为您解决性能方面的难题,提供绝佳的解决方案!

照例,如果您想要“TL;DR”(太长;没读)版本,请跳到文末。 不过要知道,这样会伤我的心。

如果你在最新版本(2015.1 或更高)的 Caché/Ensemble/HealthShare 中,打开 SAMPLES 命名空间下的 Sample.Company 文件,你会看到一个名为“Mission”的字段,其中包含伪随机生成的文本。 假设我们要搜索这个文本字段。 为了进行本练习,我生成了大约 256,246 家公司——你可以自行添加一些数据并跟着操作。那么,你可能会运行以下查询:

SELECT * FROM Sample.Company WHERE Mission LIKE ‘% agile %’

这是一个相当合理的查询,但其性能如何呢?显然,在没有索引的情况下,系统必须读取每一条记录,因此我们返回的 7854 行数据产生了 256,277 次全局引用。这表现并不理想! 让我们为 Sample.

0
0 0
文章 John Pan · 21 分鐘 前 6m read

最近,关于 Caché ObjectScript 中 for/while 循环性能的话题引发了讨论,我想与社区的其他成员分享一些想法和最佳实践。虽然这本身是一个基础话题,但人们很容易忽视那些看似合理的方法所带来的性能影响。 简而言之,使用$ListNext遍历$ListBuild列表,或使用$Order遍历局部数组,是速度最快的选项。

为了说明这一点,我们将以遍历逗号分隔字符串中的各个部分为例。

以最简洁的代码编写此类循环的一种自然方式是:

For i=1:1:$Length(string,",") {
	Set piece = $Piece(string,",",i)
	//Do something with piece...
}

这虽然直截了当,但许多编码风格指南可能会建议:

Set n = $Length(string,",")
For i=1:1:n {
	Set piece = $Piece(string,",",i)
	//Do something with piece...
}

实际上这两者之间并没有性能差异,因为结束条件并非在每次迭代中都会被评估。(我最初对此理解有误——感谢 Mark 指出这仅仅是风格问题,而非性能问题。)

0
0 3
文章 cheng yu · 40 分鐘 前 2m read

我之前关于SQLgateway 迁移的系列文章,是一篇篇幅较长的文章,介绍了
了可用的各种方案。不过它既不易操作,又难以追踪。
因此,我的挑战是:如何“吃掉一头大象”?>> 把它切成小块!!

我的目标是兼顾两点:既能独立创建每个容器,又能
同时支持一键构建整个集合。因此现在可以
在不影响其他容器的情况下修复单个容器的问题。

结果证明,这变成了一次关于 Docker 网络和脚本编写的实践,

  • 每次执行 Docker-Compose Up 都会创建一个独立的网络。因此,与
    前面的示例那样简单地启动,也会生成8个彼此隔离的网络。
    解决方案:定义一个公共网络并将容器加入其中。
    现在 TCP/JDBC 可以找到服务器,而且我们再也不会遇到端口冲突了
  • 为了启动所有容器而连续执行 8 次 `docker-compose up -d` 实在不太好玩 
  • 解决方案:Docker Compose 提供了一个 INCLUDE 指令。只需点击一下,一切就搞定了。
include:   
  - ./iris1/docker-compose.yml   
  - ./cache/docker-compose.yml    
  - ./ibmDB2/docker-compose.yml   
  - ./iris2/docker-compose.yml    
  - ./MSsql/docker-compose.yml   
  - ./mysql/docker-compose.yml   
  - ./ORACLE/docker-compose.yml   
  - ./postgres/docker-compose.yml
0
0 2
文章 cheng yu · 41 分鐘 前 2m read

通过邮件发送信息是网络中历史最悠久的技术之一
。邮件传输标准于1980年正式发布。
SMTP 代表简单邮件传输协议。
IRIS自诞生之初乃至更早之前就已支持SMTP。
随着时间的推移,各种功能不断演进,特别是与
安全、编码、身份验证、多部分邮件等方面……
相关文档在各个细节上都十分详尽。您可以在
类、方法、实用程序和示例。

本示例提供了一个非常简单的入门起点。
如果您要发送电子邮件,就需要一台能够接收邮件的 SMTP 服务器。
这可以是你选择的某个外部服务器,但同样,IRIS
已做好充分准备为您提供服务。互操作性功能可满足您的所有需求。
因此,该软件包为您提供了一个用于测试的最小规模生产环境。
该服务不会将数据写入真实的生产环境,而是将输入写入一个全局
,您可以立即查看。
因此,该演示程序可以在单个实例中运行,以发送和接收邮件。
提供了一个涵盖所有必要步骤的精简测试例程。

所有操作均通过一个包含 5 个操作的命令行会话进行控制

  • do mail; 编写一封包含2行的简单邮件
    若需更多文本,请向字符流 %mail.TextData 中添加内容
    ,同时别忘了调整文本大小
  • do start ; 启动 SMTP 服务器生产环境
    • 17:27:37.285:Ens.Director: Production 'mail.Receive' starting...
      17:27:37.332:Ens.Director: Production 'mail.Receive' started.1
0
0 2
文章 cheng yu · 1 小时 前 19m read

我每天都在使用嵌入式 Python,至今已有两年多了。 也许现在是时候分享一下这段经历的感想了。

为什么要写这篇心得?因为,我想,我和这里的大多数人一样,都是一名ObjectScript开发者,而且我认为这些心得能给社区带来帮助,让大家更好地了解在IRIS中选择嵌入式Python进行开发所涉及的利弊,同时也避免一些陷阱。

image

引言

我自2010年起从事开发工作,自2013年起开始使用ObjectScript。

0
0 2
文章 cheng yu · 1 小时 前 6m read

引言

REST API(表征状态转移)是一种接口,它允许不同的应用程序通过HTTP协议,利用GET、POST、PUT和DELETE等标准操作进行相互通信。REST API在软件开发中被广泛应用,用于提供可供其他应用程序访问的服务,从而实现不同系统之间的集成。

然而,为了确保 API 易于理解和使用,完善的文档至关重要。这就是 OpenAPI 发挥作用的地方。

OpenAPI 是一种用于描述 RESTful API 的标准。它允许以结构化的方式定义 API 的功能,包括指定可用的端点、接受和返回的数据类型、必需的参数以及预期的响应。 所有这些信息都汇总在一个规范文件中(通常扩展名为 .yaml 或 .json),自动化工具可以解析该文件以生成代码、文档等内容。

OpenAPI 规范旨在同时满足机器和人类的可读性,从而以标准化方式实现 RESTful Web 服务的描述、生成、使用和可视化。因此,OpenAPI 文档代表了对 API 的正式描述,既有助于需要使用该API 的开发者,也便于相关工具利用它来自动化各种流程。

定义规范文件有什么用处?

采用 OpenAPI 来记录 API 具有以下几个好处:

  • 清晰度:它提供了详细且结构化的文档,使开发人员能够快速了解如何与 API 交互、应发送哪些请求以及响应中会包含哪些数据。
  • 自动化:文档可从代码中自动生成,并能随着 API 的任何变更而保持最新。
0
0 3
文章 cheng yu · 1 小时 前 6m read

Prettier 适用于 ObjectScript

如果您在 JavaScript 或 TypeScript 代码中使用Prettier,现在也可以将其用于 ObjectScript 了。prettier-plugin-objectscript是一个 Prettier 插件,可对类(.cls)、例程(.mac, .int)以及包含文件()。

前后对比

该插件使用tree-sitter-objectscript 的语法规则、用于类的 UDL 语法以及用于例程和包含文件的例程语法来解析文件,并通过 Prettier 3 进行格式化输出。语法规则的选用依据文件扩展名、命令行参数以及 VS Code 中的设置。以以下类为例:

Class Demo.Greeter Extends %RegisteredObject {
Property Name As %String [Required];

/// Returns a greeting for the given name.
ClassMethod Greet(name As %String="world",times As %Integer=1) As %String [ Private ]
{
 s result=""
 f i=1:1:times {
 s result=result_"Hello, "_name_"! "
 }
 q:name="" "nobody"
 i times>1 { w "Greeted ",times," times",! }
 q result
}
}
0
0 4
文章 John Pan · 十一月 5 5m read

大多数 IRIS 开发人员每天都在使用 $LISTBUILD —— 往往自己都浑然不觉。

它不仅仅是一个用于构建列表的便捷函数,更是数据库引擎内部用于存储行数据、全局值以及许多中间结构的默认格式。

尽管如此,关于 $LISTBUILD 值的实际二进制表示形式却鲜少被提及。大多数开发者仅依赖其行为表现,却从未探究过数据究竟是如何存储的。

本文严格聚焦于**$LISTBUILD 值的** 二进制布局 ,并基于通过 zzdump 进行的直接检查。


元素结构

一个 值是由一系列元素组成的序列。每个元素的编码形式如下:

[length][type][payload]

对于较小的元素:

  • length 占用 1 字节,并包含该元素的全部内容
  • type 表示 1 字节
  • payload 长度可变

示例:


字节序

更新:感谢 @Steven Hobbs

本文中的所有示例均采用小端字节序——这绝非偶然。

尽管 IRIS 内部同时支持小端和大数据字节序平台,但 对所有二进制有效载荷都采用统一的小端编码。

这适用于:

  • 整数值
  • 十进制尾数
  • 浮点数值 (08, 09)
  • UTF-16 字符串(UTF-16LE)

例如:

0
0 6
文章 Lilian Huang · 十一月 5 13m read

大家好,社区的朋友们!

在本系列的第一部分中,我们使用 HL7 消息类型构建了一个生产环境,并创建了一个 BPL 业务流程,该流程会在将传入的 HL7 消息传递给路由引擎之前,在消息中添加 %DynamicObject 进行丰富。 如果您尚未阅读第 1 部分,请先从那里开始。该部分全面介绍了生产环境的设置、上下文属性以及“构建动态对象代码”活动。

本文将重点探讨在生产环境中开始使用 %DynamicObject 之后会发生什么。 有七个具体的属性访问陷阱,每个开发人员迟早都会遇到。它们都有一个令人沮丧的共同特征:既没有错误,事件日志中也没有警告,只是出现了一个空值,导致您的路由规则转而执行默认操作,却完全不知道原因。

为了解决这个问题,我们在第一部分中的同一 Demo.HL7Router BPL 中添加了第二个“代码”活动。随后,我们通过可运行的代码演示了这七个陷阱,并在事件日志和可视化跟踪中验证了结果。


简而言之

  • 这七个常见的 %DynamicObject 陷阱都会产生相同的症状:值为空,无错误,无警告。
  • 属性名中包含下划线、大小写错误以及嵌套对象访问是前三大常见问题;针对所有这些问题,请使用 %Get()。
  • 使用 %IsDefined() 检查属性是否存在,使用 %GetIterator() 遍历属性,使用 %GetTypeOf() 区分 null 和空字符串。
0
0 4
文章 cheng yu · 十一月 4 2m read

……你并不孤单。 

有帮助可寻。

我花了一段时间才弄明白这一点,我想可能还有其他人也在为此苦恼。 我仔细研究了所有关于 Entra 的资料,试图设置一个客户端凭据工作流,以便通过 Microsoft 365 账户发送电子邮件。虽然我成功获取了令牌,但始终无法使用 %Net.SMTP 类通过 SMTP 服务器进行身份验证。解决这个问题主要涉及两个方面。

首先,身份验证器的访问令牌不能仅仅是访问令牌本身。它必须采用以下格式:

set smtp.authenticator.AccessToken = "user="_emailaddress_$C(1)_"auth=Bearer "_token_$C(1,1)

其中 emailaddress 是您用于发送邮件的邮箱地址,token 是从服务器获取的 OAuth 令牌。

其次,默认的 %Net.SASL.XOAUTH2 机制的行为方式与 SMTP 服务器所期望的不一致。 具体来说,它不会向服务器发送初始响应就直接开始身份验证,但服务器期望立即收到该访问令牌。为解决此问题,您需要创建一个继承自 %Net.SASL.XOAUTH2 的类,并重写一个简单的方法:

Class User.MSOAUTH2 Extends %Net.SASL.XOAUTH2
{

/// Start authentication based on UserName and AccessToken using the XOAUTH2 SASL mechanism.
Method Start(ByRef response As %String) As %Boolean
{
 if (..AccessToken="") quit 0
 set response=..AccessToken		;in the original class, no response is sent here
 set ..state=1
 quit 1
}

}
0
0 8
文章 Murilo Przybyloviecz · 十一月 4 7m read
安全往往要拖到应用快要上线时才被考虑。对初学者来说,这种推迟会让身份验证、角色、资源和审计看起来像是互不相关的管理任务。更好的做法是,在一个小巧且可复现的示例中把它们一起引入。

我仍然是 InterSystems IRIS 的初学者,这也是我写这篇文章的主要动机之一。在学习这个平台的过程中,我希望通过亲手构建、测试和观察来理解安全,而不是只阅读各种定义。看到同一个端点从 401 Unauthorized 变为 403 Forbidden,最终变为 200 OK,让我明白了不同的安全层是如何协同工作的。我决定把这段经历记录下来,让其他初学者也能复现这个过程,并从同样的递进中学到东西。

在本文中,我在 InterSystems IRIS 中构建了一个最小化的 REST API,并用它演示三种不同的安全结果:

不带凭据的请求会收到 401 Unauthorized。
已通过身份验证但没有所需权限的用户会收到 403 Forbidden。
已通过身份验证且已获授权的用户会收到 200 OK 以及虚构的数据。

最后,我会在 IRIS 审计数据库中验证这次成功的请求。这个示例刻意保持小巧,但它用到了与大型应用相同的安全概念。

本演示返回的所有名称和记录均为虚构。该项目是一个教学示例,而不是生产环境的患者 API。

示例包含的内容

该项目使用三个 ObjectScript 类:

SecurityDemo.REST 提供 GET /security-demo/api/patients。
SecurityDemo.Installer 创建受保护的资源、角色、审计事件和 REST Web 应用。
SecurityDemo.Audit 写入安全的审计记录,不存储密码、令牌或患者数据。

授权模型使用一个名为 SecurityDemo.PatientData 的自定义资源和一个名为 SecurityDemoReader 的角色。该角色授予对该自定义资源的 READ 权限。由于这个本地示例把类存放在 USER 数据库中,该角色还被授予 %DB_USER:READ,这是加载 REST 类所必需的。

第二项权限是一个重要的设计细节。对于这个隔离的本地实验环境,这样做是可以接受的,但生产应用通常应使用专用的数据库和命名空间,这样它的数据库权限就不会覆盖无关的代码或数据。

实验环境设置

在这个实验中,我使用了本地的 InterSystems IRIS 实例和 USER 命名空间。一个小型的安装类创建了演示所需的安全元素:

SecurityDemo.PatientData 资源;
SecurityDemoReader 角色;
SecurityDemo/PatientAPI/ReadPatients 审计事件;
/security-demo/api Web 应用。

该 Web 应用使用密码身份验证,并且不启用未经身份验证的访问。这样设置的目的并不是展示一个完整的应用或安装教程,而是提供一个可控的环境,使每一种安全结果都能被清楚地观察到。

技巧 1:把身份验证和授权当作两种不同的检查

身份验证回答的是:是谁在发出请求?

授权回答的是:这个身份被允许做什么?

当我们在三种条件下调用同一个端点时,这种区别就变得清晰了。

首先,我发送了一个不带凭据的请求:

objectscript
curl.exe -i "http://localhost:52773/security-demo/api/patients"

IRIS 返回了 401 Unauthorized。

Mostrar Imagem

图 1 — InterSystems IRIS 以 HTTP 401 Unauthorized 拒绝不带凭据的请求。

这个响应发生在返回任何应用数据之前。客户端尚未建立 IRIS 可以信任的身份。

技巧 2:使用普通用户而不是管理员进行测试

在下一个测试中,我在 System Administration > Security > Users 下创建了一个已启用的账户,名为 ArticleUser。起初我没有给它分配 SecurityDemoReader。

使用管理员会让这个测试产生误导。特别是,拥有 %All 的用户持有所有权限,因此无法复现授权失败的情形。

我再次调用了该端点,并让 curl 提示输入密码,而不是把密码直接写在命令里:

objectscript
curl.exe -i --user ArticleUser "http://localhost:52773/security-demo/api/patients"

这一次身份验证成功了,但由于该用户没有所需的权限,IRIS 返回了 403 Forbidden。

Mostrar Imagem

图 2 — InterSystems IRIS 阻止了没有所需权限的已验证用户。

这种区分在排查问题时很有用:

401 通常意味着身份没有被成功建立。
403 意味着身份已知,但该操作未获授权。
技巧 3:用资源而不是用户名来保护操作

REST 类中不包含 If $Username = "ArticleUser" 这样的逻辑。把用户名硬编码进去既难以维护,也无法扩展。

相反,端点检查的是一个安全资源:

objectscript
ClassMethod Patients() As %Status
{
    Set allowed = $System.Security.Check("SecurityDemo.PatientData", "READ")
    Do ##class(SecurityDemo.Audit).Record(allowed)

    If 'allowed {
        Quit ..WriteError(403, "FORBIDDEN", "The SecurityDemoReader role is required.")
    }

    // Build and return fictional records.
}

用户通过角色获得权限,而应用检查的是资源,并不需要知道哪些具体用户应该被允许。这种分离使得添加或移除用户时无需修改应用代码。

在管理门户中,我把 SecurityDemoReader 分配给了 ArticleUser。然后我重复了同样的请求,并建立了一个新的已验证连接。IRIS 返回了 200 OK 和两条虚构的记录:

Mostrar Imagem

图 3 — 拥有 SecurityDemoReader 角色的用户成功访问了受保护的 REST 端点。

响应中还包含两个小而有用的防御性标头:

objectscript
Cache-Control: no-store
X-Content-Type-Options: nosniff

no-store 告诉客户端和中间节点不要保留该响应。nosniff 告诉浏览器不要重新解释所声明的内容类型。这些标头不能取代身份验证、授权或 TLS,但对于返回敏感信息的 API 来说,它们是合理的补充。

该演示还禁用了跨源处理,因为它不需要来自其他源的浏览器客户端。在没有实际需求的情况下启用 CORS 会带来不必要的暴露。

技巧 4:审计有意义的应用事件

身份验证和授权决定一个操作能否发生。审计则有助于回答事后发生了什么。

该演示定义了一个用户审计事件,其标识符如下:

objectscript
Event Source: SecurityDemo
Event Type: PatientAPI
Event Name: ReadPatients

审计辅助方法只记录用户名和授权结果:

objectscript
ClassMethod Record(allowed As %Boolean) As %Status
{
    Set outcome = $Select(allowed:"Allowed", 1:"Denied")
    Set eventData = "username=" _ ..SafeValue($Username, 64) _ ";outcome=" _ outcome
    Set description = "Patient list access " _ outcome

    Quit $System.Security.Audit("SecurityDemo", "PatientAPI", "ReadPatients", eventData, description)
}

这段代码刻意避免记录密码、授权标头、返回的记录或其他敏感内容。审计日志本身就是安全资产;收集不必要的机密信息,会让审计轨迹变成另一个风险来源。

我打开了 System Administration > Security > Auditing > View Audit Database,找到了由这次成功请求产生的事件。

Mostrar Imagem

图 4 — IRIS 审计轨迹记录了已验证的用户以及对受保护端点的成功访问。

该记录标明了 ArticleUser、PatientAPI 事件类型、ReadPatients 事件以及 Allowed 结果。这证明了受保护的操作是由一个已通过身份验证且已获授权的身份执行的。

技巧 5:如实说明演示与生产环境安全的差距

这个实验聚焦于一个狭窄的学习目标。真正的部署还需要额外的控制措施,包括:

对每一个承载凭据或敏感数据的连接使用 TLS;
适合所在组织的身份验证机制,例如 OAuth 2.0、OpenID Connect、Kerberos 或 LDAP;
专用的应用数据库和命名空间;
使用密钥管理,而不是把凭据放在源代码或脚本中;
数据库加密以及有文档记录的密钥管理流程;
审计日志的监控、保留、保护和审查;
禁用未使用的服务和应用;
定期审查用户、角色和权限;
同时针对成功访问和预期的安全失败进行测试。

最重要的一课是,安全应当是可观察、可测试的。仅仅创建一个角色并假定它有效是不够的。请分别使用无凭据、权限不足以及具备预期角色的方式发出请求。验证确切的 HTTP 结果,然后在审计轨迹中确认相应的活动。

结论

仅用一个小小的 REST 端点,这个示例就演示了 IRIS 的若干核心安全概念,而无需一个完整的应用:

身份验证拒绝匿名请求;
授权区分了已验证用户和已授权用户;
资源描述了被保护的对象;
角色把应用用户所需的权限归为一组;
编程式检查在靠近受保护操作的地方强制执行访问控制;
审计提供了有意义活动的踪迹。

对初学者来说,401 → 403 → 200 这个序列是理解这些层如何协同工作的实用方法。它还能产生可重复的证据,证明安全配置按预期运行。

构建并测试这个小项目,让我对 IRIS 安全的理解比分别学习每个概念时清晰得多。作为初学者,能够在一个实验中同时看到身份验证、授权、角色、资源和审计的运作,尤其令人受到鼓舞。我希望这份演练能为其他开发者提供一个简单的起点,并鼓励他们探索 InterSystems IRIS 中更广泛的安全功能。

官方文档
InterSystems IRIS Security
Privileges and Permissions
Securing REST Services
Managing Applications
Auditing

作者:Murilo Przybyloviecz
0
0 8
文章 cheng yu · 十一月 4 20m read

钾离子检测结果为6.8 mmol/L时,绝不能拖延。既不能等候排队,也不能等候审核人员,更不能等候语言模型完成回答。 而血糖值180 mg/dL则另当别论:该数值已超出参考范围,可能无碍,也可能需要人工审核。我正是想通过这种中间情况来检验AI Hub代理能否提供帮助,同时确保其绝不会处理那些不容耽搁的检测结果。

LabTriage Agent 是我为“互操作性工作流中的 AI 代理”竞赛提交的参赛作品,也是我首次参加 InterSystems 举办的竞赛。 该代理在 IRIS for Health 生产环境中读取 HL7 v2 ORU^R01 实验室结果,并将每项检查归类到以下三类列表之一:RELEASE(发布)、HOLD_FOR_REVIEW(待审核)或 CRITICAL(危急)。其设计理念的核心在于顺序。确定性规则优先执行,并决定哪些结果属于危急情况。 该代理仅处理规则无法裁定的结果。最终决策权由人工通过“互操作性工作流收件箱”行使。

本文将追踪一条消息在这些层级中的流转过程,展示如何在无需 API 密钥的情况下运行整个项目,并最后分享我关于 AI Hub 154 版本的一些观察。

项目中的所有数据均为合成数据,临界值也均为虚构。这仅是一个演示,并非临床工具。

0
0 11
文章 cheng yu · 十一月 3 24m read

什么是微服务?

微服务是一种架构风格,它将应用程序结构化为一组小型、自治的服务。每个组件围绕特定的业务能力进行开发,可以独立部署,通常由一个小型、专业化且自主管理的团队负责管理。(来源:https://microservices.io/)

0
0 11
文章 John Pan · 十一月 2 10m read

《InterSystems IRIS 互操作性实用指南:终端调用、使用 OnTask 的自定义适配器、上下文模拟以及工作流调试》(2026)

简而言之 / 快速解答

InterSystems IRIS 互操作性主机(服务、进程、操作)在独立的后台作业中运行,并通过持久化消息队列进行通信。 开发人员可直接在 IRIS 终端中测试和调试这些组件:对于无需适配器的服务,使用 Ens.Director.CreateBusinessService;对于直接操作调用,使用 %New("ConfigName");对于完整的队列管道,使用 EnsLib.Testing.Service.SendTestRequest。 自定义入站适配器通过重写 OnTask() 方法轮询外部数据源。对于系统头信息未填充的本地终端执行场景,开发人员可以手动模拟 %SessionId 和 %RequestHeader 属性。

本文适用对象

本指南专为希望从 IRIS 终端调用业务服务和操作、通过 OnTask() 实现自定义轮询逻辑,以及系统化测试或调试生产主机的 IRIS 集成开发人员、系统工程师和解决方案架构师而设计。

主要主题/问题 #1:面向业务主机的终端执行协议

InterSystems IRIS 互操作性主机可以通过编程方式在活动的终端进程中直接实例化并执行。

0
0 9
文章 Jeff Liu · 十月 30 8m read

简而言之:InterSystems IRIS for Health 允许开发人员直接在后端 ObjectScript 例程中执行 FHIR CRUD 操作,而无需依赖 HTTP REST 层。通过使用 HS.FHIRServer.Service,开发人员可以创建服务上下文、构建动态查询、验证模式,并高效地检索 FHIR 资源。 

传统的 FHIR 实现通常通过 HTTP/REST 管道处理请求。本文探讨了绕过该层、直接在 IRIS 后端执行 FHIR 操作时的情况,重点介绍了该方法涉及的架构、服务上下文、查询处理、验证和资源访问。 

本文适用对象:本指南专为InterSystems IRIS for Health、HealthShare 和 Health Connect 开发人员,以及需要在不依赖外部 HTTP 端点的情况下,直接在后端 ObjectScript 例程中执行低开销 FHIR 操作的医疗信息学工程师而设计。

核心架构:标准流程与编程执行

部署在 InterSystems IRIS for Health、HealthShare 和 Health Connect 上的标准 FHIR 实现,依赖于通过 REST 应用层映射到 HS.FHIRServer.Service 的入站 HTTP 管道。

0
0 18
文章 Lilian Huang · 十月 29 18m read

要将一个 ObjectScript 类转化为一个能够调用工具、使用技能并委派部分任务的智能体,需要具备哪些条件?

我开发了“My First Agent Studio”,旨在让这个探索过程更加轻松。它包含小型原生 %AI.Agent 示例、终端演示、数据集,以及一个用于交互式测试这些代理的浏览器界面。除本地模型服务外,所有内容均打包在一个 Docker Compose 项目中。

该项目是我对InterSystems 社区奖励计划——第二轮的贡献。它融合了两个理念:

源代码可在“My First Agent Studio”代码库中获取,其中包含完整的配置说明和示例

本文重点介绍各组件如何协同工作。

项目运行原理

代理在 InterSystems IRIS 中通过 AI Hub 运行,而 Ollama 则提供 LLM。我选择 Ollama 是为了让开发人员无需付费模型 API 或云提供商的 API 密钥即可尝试这些功能。 您仍需具备足够的本地资源来运行所选模型,特别是对于工具和委托示例,请使用支持工具功能的模型,例如 Qwen 3.8。


1
0 15
文章 Shengzhi Wu · 十月 28 13m read

在线体验:http://43.139.159.100:8082/paymentguard/

——仪表盘、REST API 与背后的互操作生产全程在线,灰色地带决策由 DeepSeek 实时作出。

本文是英文参赛文章的中文版。源码:github.com/Dylan5237/payment-guard-ai。

以下是正文:


支付授权,本质上是个决策问题。

大多数支付没什么可纠结的:老客户、熟悉的金额、常用的设备,几条确定性规则微秒级就能放行或拦下。真正难的是中间那片灰色地带——新设备、不常见的国家、比平时高出一截的金额、KYC 还没过审的客户。人工分析师的时间基本都耗在这里,而这恰恰是大模型 Agent 能帮上忙的地方——前提是它手里有真工具,查得到真数据。

PaymentGuard AI 做的事就这么直接:把一个真实的 IRIS AI Hub Agent(%AI.Agent)当成一个业务操作,塞进一条普通的互操作生产里。Agent 不瞎猜——它通过 AI Hub 的 SQL 工具直接查支付平台自己的 IRIS 库,查完按严格的 JSON 格式给结论;剩下的路由、审计、算 token 成本,生产全接管。

混合决策:规则打头,Agent 收尾

每笔支付进生产后走三段:

  • 确定性规则先上。 制裁地区、黑名单、硬性限额、速率尖峰,命中即拦;完全可信的客户加小额加常用设备加本地,直接放行。
0
1 26
文章 Lilian Huang · 十月 27 4m read

InterSystems ObjectScript – 编码代理测试套件基准测试

在我们的首次基准测试中,我们对InterSystems 编码基准测试平台对 14 个 模型进行了测试。详细情况可见这篇文章:https://cn.community.intersystems.com/node/589834

但鉴于测试结果,我们决定通过四种编码代理进行同样的测试;我们对 Claude Code、Codex CLI、pi 和 prime-agent 进行了测试,并加入了一个名为 caveman 的变体。Caveman 旨在限制提示符的 I/O 令牌数量。

我们通过在运行中的 IRIS 实例中编译并运行代码,对代理的运行情况进行了验证。

结果

成本效益指数

该指数是我们新创建的指标,设定为60%的准确率和40%的成本效率。成本效率是以下四项标准化 任务指标的平均值:输入令牌、输出令牌、墙钟时间和IRIS编译调用次数。 控制行被排除在外。

排名 单元格 指数 准确率 IRIS调用次数 实际时间
1 《Opus 5》· Claude Code · caveman 99.1 1.000 1.4 19.5 秒
2 Opus 5 · Claude Code · plain 95.0 0.989 1.5 24.5 秒
3 GPT-5.6 sol · Codex · caveman 89.
0
0 25
文章 Kelly Huang · 九月 29 5m read

ChatGPT刚推出时,开发者们很快就开始将其用于日常任务:调试代码片段、编写小型辅助程序,或是理解陌生的代码。其中大部分工作都需要大量复制粘贴。

像 Claude 和 Codex 这样的编码代理改变了这一现状。它们能够直接处理代码库,并对多个文件进行修改。但对于 ObjectScript 开发而言,“经典”的代理式流程并不总是最合适的选择。在 本文中 ,我们将讲述我们如何将 AI 代理整合到自身工作流程中的故事。

从何入手?

过去,我们为客户维护的某个系统,其开发工作直接在共享的服务器端开发系统上进行。每位开发人员都在那里实现并测试自己的更改。随着团队规模扩大,多人开始同时处理该系统,这种工作流程变得越来越不切实际。

AI 编码代理的引入让这些局限性变得更加明显。Claude 和 Codex 等工具的工作原理是直接检查和修改源代码。相关文档和指南指出,如果它们能够编译代码并运行自己的测试,效果会更好。但在 IRIS 中通过 VS Code 进行服务器端开发时,这一实验在第一步就失败了。 由于文件并未实际存在于机器上,代理难以直接访问。因此,将开发迁移到本地、受版本控制的仓库中成为自然而然的一步:这不仅改善了协作,还为开发人员和代理都提供了合适的工作空间。

但这仍留下了一个问题:如何让代理能够编译代码并运行测试。

0
0 47
文章 Jeff Liu · 九月 27 8m read

为 InterSystems AI Hub 代理和外部 MCP 客户端构建一套受管控的 FHIR 工具集——以及那个让每次拒绝都拥有完美借口的日志记录漏洞。

大家好,社区的朋友们! 我终于下定决心写下我的第一篇帖子——并决定直接挑战两个当下最热门的话题:AI 和安全。

先做个简短的免责声明:这是试图将两个在很长一段时间内都不会真正交汇的世界结合起来:AI 和老派的安全人员。让我来解释一下。

几年前,我曾参与一个非常老旧的航空医疗认证系统项目——该系统负责判定人员是否具备在航空领域工作和学习的体能及心理健康条件。项目本身以及我在其中的角色并不怎么有趣:主要是将系统从Cache迁移到IRIS,并根据新法规对客户端进行现代化改造。 但我至今仍记得那支由审计员和安全官员组成的团队:每次新的集成、每个新的封装层、每一步数据提取操作,都会引发数十个问题,产生数页的文档和数据流图。其中有三个问题让我印象深刻:

  • 什么能阻止这个函数读取它无权访问的记录?
  • 这些规则记录在哪里?我能否在不阅读你们代码的情况下查阅它们?
  • 谁发起了这个调用,记录在哪里?

前两个问题我们轻松通过了。但第三个问题却让审计搁浅了:审计员要求提供针对医疗数据的特定访问记录,却未能找到。这并非因为没有发生访问:这些访问是合法的,检查机制也有效——我们只是在错误的层级上进行了日志记录。

0
0 43
文章 Nicky Zhu · 九月 17 6m read


                                                                                                              ____( o)>
                                                                                                              \ <_. )
                                                                                                                 `---'   
.                     某个类突然无法被找到。 
                      尽管你知道数据确实存在,但查询却未返回任何行。 
.                     ObjectScript 方法失败,却没有明显的原因。 
.                     应用程序在某个环境中能成功连接,但在另一个环境中却无法连接。
.                     

0
0 69
文章 Lilian Huang · 九月 11 11m read

大家好,社区的朋友们!

IRIS Interoperability 中的业务规则属于那种原本运行顺畅,却会突然失效的功能之一。您构建了一条规则,条件看起来正确,路由目标也无误,但消息却一直被转发到默认操作。事件日志中没有错误,可视化跟踪中也没有明显的线索。

最常见的原因之一是动态对象的属性无法按预期方式访问。本系列文章共分两部分,将全面介绍这一问题。 第 1 部分将引导我们使用 HL7 消息类型设置一个生产环境,并构建一个 BPL 业务流程,该流程通过动态对象对传入的 HL7 消息进行数据增强。第 2 部分将使用同一生产环境,演示七个在生产集成中导致“无声故障”的实际属性访问陷阱。


简而言之

  • IRIS 中的 A %DynamicObject 是一个无需定义类即可动态创建的无模式对象。
  • 在 BPL 业务流程中使用它,可在路由规则对其进行评估之前,将丰富数据随 HL7 消息一同传输。
  • 利用 %Set() 设置包含下划线等特殊字符的属性,并使用 %Get() 将其读回。
  • 本系列的第 2 部分将介绍七个属性访问陷阱,这些情况都会在无任何错误提示的情况下静默返回空值。

本文适合哪些读者

本指南面向处理 BPL 业务流程和 HL7 路由的 IRIS 互操作性开发人员,旨在帮助他们了解如何在做出路由决策之前,通过动态对象来丰富消息内容。


什么是业务规则?

0
0 65
文章 Lilian Huang · 九月 9 4m read

我们发布了首个在 InterSystems ObjectScript 和 IRIS 平台上衡量 LLM 能力的开放式基准测试:ObjectScript LLM 基准测试。

为何推出该基准

现有的编程基准测试——SWE-bench、LiveCodeBench、Terminal-Bench——均基于 Python、 JavaScript 和 shell 语言。其中没有一项涵盖 ObjectScript、IRIS SQL、嵌入式 Python 或 %CSP.REST。 已发布的编程评分无法直接应用于 IRIS 平台。这是首次尝试 直接衡量这一差距。

我们测量的内容

140个手工编写的项目,涵盖三个类别:代码生成、代码理解和错误修复。 主要评分器是一个实时运行的 IRIS 实例——模型响应会被编译并执行, 输出结果与预期字符串进行比对。对于 93 个基于执行结果评分的 题目,不存在主观评分。47 个理解类题目则采用基于评分标准的 LLM 评判器。

我们运行了14个模型(Claude 4.x/5、GPT-5变体以及三个自托管的Qwen3/DeepSeek模型), 均采用其可用的最高推理配置,每个项目在温度为0的条件下生成一个响应。

总体结果

Overall accuracy by model with 95% bootstrap confidence intervals

总体准确率在0.515至0.851之间。 排名前九的模型集中分布在 0.737 至 0.

0
0 71
文章 Lilian Huang · 八月 28 10m read

本系列的第一部分。第二部分将介绍完整的工具目录。第三部分将介绍 ObjectScript 技能。第四部分将介绍基准测试以及如何衡量实际的改进效果。


隐藏在注释中的问题

托马斯·马祖尔(Thomas Mazur)关于 VS Code 生产力——Peacock、作用域工作区文件、Copilot 代理模式——的 博文《青蛙、鸡、AI 和 VS Code》 在评论区揭示了一个更尖锐的问题。皮耶特罗·迪·莱奥(Pietro Di Leo)和 Mike.W 指出,当你在 VS Code 中进行服务器端开发时, isfs:// 大多数 IRIS 生产环境所使用的“工作区”中,Copilot 只能看到编辑器中打开的文件,无法对虚拟文件系统进行索引。对于拥有数千个类的成熟 IRIS 应用程序而言,AI 的工作效果就像是透过钥匙孔观察一样。

John Murray 向大家推荐了我正在开发的项目——iris-agentic-dev——并指出目前开发者社区中尚无相关文章。因此,本文将阐述:该问题产生的原因、该工具如何解决该问题,以及如何在约五分钟内完成部署。


为什么 AI 无法“看到”你的命名空间

当你打开一个 isfs:// 工作区时,你的 IRIS 类存储在服务器上,而非本地磁盘。

0
0 51
文章 Nicky Zhu · 八月 19 17m read

处理遗留代码时的差异与注意事项

刚开始接触 ObjectScript 时,我编写的大部分代码都遵循了 InterSystems 的现代编程模式。我使用类、方法、REST 服务、互操作性生产、JSON 对象以及 %Status 值。

与此同时,我负责维护的生产系统中,很大一部分代码采用的编写风格却截然不同。

该系统使用了例程、标签、global、缩写命令、“点-do”语法、共享局部变量以及错误处理模式——这些在基于类的 ObjectScript 普及之前就已经存在了。

起初,我曾以为这仅仅是旧语法与新语法的区别。随着时间的推移,我意识到最大的差异并非表面上的。传统的MUMPS风格代码与现代ObjectScript在变量作用域、执行层级、数据流和错误处理方面往往基于不同的假设。这种区别至关重要,因为旧代码很容易被误读。

某个例程看似未接收任何参数,却依赖于调用者创建的多个变量;某个标签在某处表现得像一个过程,在另一处却表现得像一个函数;一个点块(dot block)虽然会创建另一个执行层级,却可能看起来像缩进;一段代码在移入类后虽然能编译通过,但在运行时却可能表现得截然不同。

代码看起来依然很熟悉。语言依然是 ObjectScript。然而,它可能已经发生了变化。

本文介绍了我在处理遗留代码并将其与更现代的 ObjectScript 应用程序集成时所遇到的一些最重要的差异。

1.

0
0 80
文章 Kelly Huang · 八月 13 7m read

如果你在开发者社区待过一段时间,就会发现许多问题以不同形式反复出现:如何将持久化对象转换为 JSON?如何遍历一个结构未知的 JSON 有效载荷? 为什么 Required 对我的 %DynamicArray 属性 没有任何作用 ?为什么我的日期会被返回为 31390 ? 

注意到这一点后,我们决定撰写一篇文章,总结所有那些仅凭文档无法解答的问题:实践。

让我们从基础开始

InterSystems IRIS 为无模式数据提供了两个类:%DynamicObject 和 %DynamicArray。它们都继承自 %DynamicAbstractObject,两者的实例均被称为动态实体。 它们与 JSON 映射得非常自然:对象是一组键值对,数组是有序列表。与持久化类或注册类不同,动态对象不保留预定义的有效属性名列表——任何字符串键都是合法的,并且可以在运行时添加或移除成员。

最令人愉悦的部分是字面量语法,对于来自 JavaScript 背景的开发者来说,这看起来会很熟悉:

你也可以逐个字段构建它们。 会返回其修改后的实体,因此可以进行调用链:

Set obj = {}.%Set("a",1).%Set("b",2) // now obj is {"a":1,"b":2}

Do scores.%Push(100)          		 // now scores is [90, 85, 77, 100]

Set last = scores.%Pop()      		 // now scores is [90, 85, 77] and last is 100
0
0 116
文章 Claire Zheng · 七月 8 12m read

摘要

常见表表达式(CTE)为在SQL语句中定义可重用的中间结果集提供了一个结构化的框架。InterSystems IRIS通过WITH子句实现CTE,在与IRIS基于成本的优化器完全集成的情况下,既能实现更清晰的查询构建,又能支持模块化的分析处理。

本文探讨了 InterSystems IRIS 中 CTE 的语义,解释了其与查询优化的交互关系,讨论了合适的部署场景,并提供了可执行的示例,以说明生产环境中的实用模式。


  1. 引言

复杂的 SQL 工作负载通常需要多个逻辑处理阶段(例如过滤、聚合、数据增强和投影)。如果没有抽象机制,这些操作往往会导致查询结构深度嵌套,难以理解和维护。

常见表表达式(CTE)通过允许开发人员定义可在单个 SQL 语句中求值的命名查询组件,解决了这一问题。

在 InterSystems IRIS 中,CTE 通过 SQL 的 WITH 子句实现,并作为逻辑查询表达式运行,与周围的语句一同进行优化 [1]。


  1. InterSystems IRIS 中的常见表表达式

2.1 定义

公共表表达式(Common Table Expression)是在 SELECT 语句之前声明的命名子查询:

WITH PersonSubset AS (
  SELECT ID, Name, Age
  FROM Sample.Person
)
SELECT Name
FROM PersonSubset
0
0 115
文章 Lilian Huang · 六月 18 4m read

对于上周没有参加 READY 大会的朋友们来说,你们可能错过了这一激动人心的消息:AI Hub 的抢先体验计划现已正式启动。这一消息是在@Benjamin De Boe和@Jeff Fried 带来的一场精彩演示中公布的,我建议大家在录像发布后务必观看这段演示!  我有幸提前体验了 AI Hub,想借此机会向社区大家介绍一下。

在深入探讨细节之前,先提供文档链接和EAP 门户链接,您可通过这些链接下载 AI Hub,目前提供独立安装包或容器镜像两种形式。 

请注意,这只是预览版,在正式发布前可能会有重大变更,该版本不适用于生产环境,您可能会遇到一些问题——如果遇到问题,请在 GitHub 页面上提交问题!

代理

对我来说,最令人兴奋的功能莫过于全新的 ObjectScript 代理 SDK。现在,您可以使用直观的 SDK,直接在 ObjectScript 中创建代理和工具。

创建代理非常简单:您可以通过 XData INSTRUCTIONS 组件为其指定系统提示,然后只需设置提供程序、模型和工具即可:

Class Sample.Agent Extends %AI.Agent
{
    /// LLM Model
    Parameter MODEL = "gpt-5-nano";

    /// Toolsets that the agent can use
    Parameter TOOLSETS = "Sample.ToolSet";
    
    /// System Prompt
    XData INSTRUCTIONS [ MimeType = text/markdown ]
    {
    # Sample Assistant

    You are a helpful assistant with access to a set of tools to interact with a database of people.
    }

    Method %OnInit() As %Status
    {
        // Set provider with API key from environment variable
        Set key = $System.Util.GetEnviron("OPENAI_API_KEY")  // or whatever
        Set ..Provider = ##class(%AI.Provider).Create("openai", {"api_key": (key)})
        
        Return $$$OK
    }
}
0
0 303