搜索​​​​

清除过滤器
文章
姚 鑫 · 五月 27, 2021

第八章 处理收到的电子邮件

# 第八章 处理收到的电子邮件 # 处理收到的电子邮件 本节介绍如何处理通过`%Net.POP3`检索到的电子邮件(`%Net.MailMessage`)。 ## Message Basics 检索电子邮件(`%Net.MailMessage`)后,通常首先确定它是哪种类型的邮件以及如何阅读它;也就是说,它是否是多部分邮件以及各部分是否是二进制的。在此步骤中,您可以使用`ContentType`属性。或者,可以使用`IsBinary`、`IsHTML`和`IsMultiPart`属性,它们间接提供与`contentType`相同的信息。 如果消息是多部分消息,则每个部分都是`%Net.MailMessagePart`的一个实例。 ## Message Headers 消息本身和消息的每个部分都有一组标头。 `%Net.MailMessage`和`%Net.MailMessagePart`类提供的属性使可以轻松访问最常用的标头。例如,`%Net.MailMessage`提供收件人、发件人、主题和日期等属性。`Headers`数组属性允许访问任何自定义标题. 此外,如果已通过`%Net.POP3`检索到消息,则可以使用`GetAttribute()`方法。在给定标头名称和属性的情况下,此方法返回该属性的值。 ## Message Contents 了解常规消息结构后,请使用以下技术检索内容: - 对于多部分消息,请使用`Parts`属性,该属性是部分的数组。`Parts.Count()`给出部件的数量。每个部件的键都是一个整数,从1开始。使用`GetAt()`方法检索给定的部件。消息部分是`%Net.MailMessagePart`的实例。 - 对于二进制消息(或消息部分),请使用`BinaryData`属性。 - 对于文本消息(或消息部分),请使用`TextData`属性。 - 如果`IsHTML`为0,则`TextData`属性为普通文本字符串。 - 如果`IsHTML`为1,则`TextData`属性为HTML文本字符串。 请注意,发送邮件的电子邮件客户端确定邮件中的任何包装。邮件服务器无法控制这一点, ## 其他消息信息 `MessageSize`属性表示邮件的总长度(不包括任何附加的电子邮件)。 以下方法提供有关消息的其他信息: ### GetLocalDateTime() 返回检索消息的日期和时间,并转换为`$HOROLOG`格式的本地时间。 ### GetUTCDateTime() 返回检索消息的日期和时间,并以`$HOROLOG`格式转换为UTC。 ### GetUTCSeconds() 返回自1840年12月31日以来检索消息的日期和时间(秒)。 以下类方法也可用于时间/日期转换: ### HToSeconds() 将`$HOROLOG`格式的日期/时间转换为自1840年12月31日以来的秒的类方法。 ### SecondsToH() 将自1840年12月31日以来的秒数转换为`$HOROLOG`格式的日期/时间的类方法。 示例1:`ShowMsgInfo()` ```java ClassMethod ShowMsgInfo(msg as %Net.MailMessage) { Write "Message details *****",! Write "To (count): ", msg.To.Count(),! Write "From: ", msg.From,! Write "Cc (count): ", msg.Cc.Count(),! Write "Bcc (count): ", msg.Bcc.Count(),! Write "Date: ", msg.Date,! Write "Subject: ", msg.Subject,! Write "Sender: ", msg.Sender,! Write "IsMultipart: ", msg.IsMultiPart,! Write "Number of parts: ", msg.Parts.Count(),! Write "Number of headers: ", msg.Headers.Count(),! Write "IsBinary: ", msg.IsBinary,! Write "IsHTML: ", msg.IsHTML,! Write "TextData: ", msg.TextData.Read(),! Write "BinaryData: ", msg.BinaryData.Read(),! } ``` 此方法产生类似于以下内容的输出: ```java Message details ***** To (count): 1 From: "XXX XXX" Cc (count): 0 Bcc (count): 0 Date: Fri, 16 Nov 2007 11:57:46 -0500 Subject: test 5 Sender: IsMultipart: 0 Number of parts: 0 Number of headers: 16 IsBinary: 0 IsHTML: 0 TextData: This is test number 5, which is plain text. BinaryData: ``` 示例2:`ShowMsgPartInfo()` 以下方法写入有关消息一部分的信息: ```java ClassMethod ShowMsgPartInfo(msg as %Net.MailMessage, partno as %Integer) { Set part=msg.Parts.GetAt(partno) Write "Message part details *****",! Write "Message part: ", partno,! Write "IsMultipart: ", part.IsMultiPart,! Write "Number of parts: ", part.Parts.Count(),! Write "Number of headers: ", part.Headers.Count(),! Write "IsBinary: ", part.IsBinary,! Write "IsHTML: ", part.IsHTML,! Write "TextData: ", part.TextData.Read(),! Write "BinaryData: ", part.BinaryData.Read(),! } ``` 这将产生与以下内容类似的输出(给定的消息与前面显示的不同): ```java Message part details ***** Message part: 1 IsMultipart: 0 Number of parts: 0 Number of headers: 2 IsBinary: 0 IsHTML: 0 TextData: 1 test string BinaryData: ``` 示例3:`ShowMsgHeaders()` 下面的方法写入有关消息头的信息;可以编写一个类似的方法,对消息部分执行相同的操作。 ```java ClassMethod ShowMsgHeaders(msg as %Net.MailMessage) { Set headers=msg.Headers Write "Number of headers: ", headers.Count(),! //iterate through the headers Set key="" For { Set value=headers.GetNext(.key) Quit:key="" Write "Header:",key,! Write "Value: ",value,!! } } ``` 这将产生类似于以下内容的输出: ```java Number of headers: 16 Header: content-class Value: urn:content-classes:message Header: content-type Value: multipart/alternative; boundary="----_=_NextPart_001_01C8286D.D9A7F3B1" Header: date Value: Fri, 16 Nov 2007 11:29:24 -0500 Header: from Value: "XXX XXX" Header: message-id Value: Header: mime-version Value: 1.0 ... ``` # 自动编码和字符翻译 电子邮件部分包含有关使用的字符集和使用的内容传输编码(如果有的话)的信息。作为参考,本节介绍如何使用此信息。 ## 外发电子邮件 `%Net.SMTP`检查每个部分的字符集属性,然后应用适当的转换表。 如果未指定给定部件的字符集属性,InterSystems IRIS将使用UTF-8。 `%Net.SMTP`还检查`ContentTransferEncoding`属性。如果此属性为 `"base64"`或`"quoted-printable"`,则在创建消息时,`%Net.SMTP`会根据需要对正文进行编码。(如果内容传输编码为 `"7bit"` 或 `"7bit"`,则不需要编码。) 重要提示:请注意,如果内容为`“Base64”`编码,则不能包含任何`Unicode`字符。如果要发送的内容包括`Unicode`字符,请确保使用`$ZCONVERT`将内容转换为UTF-8。 ## 传入电子邮件 `%Net.POP3`检查每个邮件部分的`Content-Transfer-Encoding`标头,并根据需要对正文进行解码。 然后`%Net.POP3`检查每个邮件部分的`Content-Type`标头。这会影响消息部分的字符集属性,还会控制在InterSystems IRIS中创建消息部分时使用的转换表。
文章
Hao Ma · 一月 15, 2021

IAM实践指南——OAuth 2.0下的API保卫战(第三部分)

在这个由三个部分组成的系列文章中,介绍了如何在OAuth 2.0标准下使用IAM简单地为IRIS中的未经验证的服务添加安全性。 第一部分介绍了一些OAuth 2.0背景知识,以及IRIS和IAM的一些初始定义和配置,以帮助读者理解确保服务安全的整个过程。 第二部分详细讨论和演示了配置IAM所需的步骤——验证传入请求中的访问令牌,并在验证成功时将请求转发到后端。 本系列的最后一部分将讨论和演示IAM生成访问令牌(充当授权服务器)并对其进行验证时所需的配置,以及一些重要的最终考虑事项。 如果您想试用IAM,请联系InterSystems销售代表。 场景2:IAM作为授权服务器和访问令牌验证器 与上个场景不同的是,该场景中将使用一个名为“OAuth 2.0 Authentication”的插件。 如果要在资源所有者密码凭证流中将IAM作为授权服务器使用,客户端应用程序必须对用户名和密码进行身份验证。只有在身份验证成功时,才能发出获取IAM访问令牌的请求。 首先,将其添加到“SampleIRISService”中。正如下面截屏所示,需要填充一些不同的字段来配置此插件。 首先,将“SampleIRISService”的ID粘贴到“service_id”字段中,这样就可以在服务中启用该插件。 在“config.auth_header_name”字段中,需要指定携带授权令牌的头名称。本例中,我保留默认值“authorization”。 “OAuth 2.0 Authentication”插件支持的OAuth 2.0流包括授权码授权(Authorization Code Grant)、客户端凭证(Client Credentials)、隐式授予(Implicit Grant)或资源所有者密码凭证授权(Resource Owner Password Credentials Grant)。我们在本文中使用的是“资源所有者密码凭证”流,故选中“config.enable_password_grant”。 在“config.provision_key”字段中输入要用作配置密钥的字符串。此值用来向IAM请求访问令牌。 本例中,我保留了所有其他字段的默认值。可以在此处查看插件文档中每个字段的完整引用。 下面是插件配置的最终效果: 创建插件后,需要为“ClientApp”客户端创建凭证。 为此,打开左侧菜单上的“Consumers”,然后单击“ClientApp”。接下来,点击“Credentials”标签,然后点击“New OAuth 2.0 Application”按钮。 在下个页面的“name”字段输入名称,以标识应用程序,在“client_id”和“client_secret”字段分别定义客户端ID和客户端密钥,最后在应用程序中输入URL,在“redirect_uri”字段上授权后,用户将被发送到该URL。然后,单击“Create”。 现在,可以发送请求了。 需要发出的第一个请求是获取IAM访问令牌。“OAuth 2.0 Authentication”插件自动创建一个端点,并将“/oauth2/token”路径附加到已经创建的路由上。 注意:必须使用HTTPS协议和IAM的代理端口(默认端口为8443)监听TLS/SSL请求。这是OAuth 2.0规范要求。 因此在本例中,需要向URL发出一个POST请求: https://iamhost:8443/event/oauth2/token 请求主体中应包括以下JSON: { "client_id": "clientid", "client_secret": "clientsecret", "grant_type": "password", "provision_key": "provisionkey", "authenticated_userid": "1" } 如上所示,该JSON包含了在创建“OAuth 2.0 Authentication”插件时定义的值(如“grant_type”和“provision_key”),以及在创建客户端凭证时定义的值(如“client_id”和“client_secret”)。 当提供的用户名和密码成功通过身份验证后,客户端应用程序还应该添加“authenticated_userid”参数值作为已通过身份验证的用户的唯一标志。 该请求及其响应如下: 现在可以请求从上面的响应中获取事件数据(包括“access_token”值),并作为对URL的GET请求中的“bearner token” https://iamhost:8443/event/1 如果访问令牌过期,可以使用收到的刷新令牌和过期的访问令牌一起生成一个新的访问令牌,方法是向用于获取访问令牌的相同端点发出POST请求,但主体略有不同: { "client_id": "clientid", "client_secret": "clientsecret", "grant_type": "refresh_token", "refresh_token": "E50m6Yd9xWy6lybgo3DOvu5ktZTjzkwF" }  该请求及其响应如下: “OAuth 2.0 Authentication”插件的一个有趣很好的特性是能够查看和禁用访问令牌。 若要查看令牌列表,向以下IAM的Admin API终端发送GET请求即可: https://iamhost:8444/{workspace_name}/oauth2_tokens 其中{workspace_name}是IAM工作区的名称。如果启用了RBAC,则需要输入必要的凭证才能调用IAM Admin API。 注意,“credential_id”是在ClientApp客户端内部创建的OAuth应用程序(本例中名为SampleApp)的id,“service_id”是应用此插件的“SampleIRISService”的id。 要想禁用令牌,可以向以下端点发送删除请求 https://iamhost:8444/Sample/oauth2_tokens/{token_id} 其中{token_id}是要禁用的令牌id。 如果尝试使用无效令牌,将包含该无效令牌的GET请求作为Bearer Token 发送到URL,则会收到一条消息,提示令牌无效或已过期: https://iamhost:8443/event/1 最后需要考虑的因素 本文演示了如何将IAM中的OAuth 2.0身份验证添加到IRIS中未经身份验证的服务中。需要牢记的是,IRIS中的服务本身仍是未经身份验证的。因此,如果有人绕过IAM层直接调用IRIS服务端点,则可以在不经过任何身份验证的情况下查看信息。出于该原因,在网络级别设置安全规则以防止不必要的请求绕过IAM层是很重要的。 可以在此处了解更多有关IAM的信息。 如果您想试用IAM,请联系InterSystems销售代表。
文章
Hao Ma · 一月 30, 2021

WebGateway系列(3): 配置IIS连接IRIS

IIS在Windows Server里是默认安装,在Windows7和Windows10里面需要用户到"控制面板>程序"里面找到"Turn Windows features on or off"的设置来启动。 本文假设IIS已经在用户的服务器上启动,并且正常工作。 通常情况下,当IRIS安装在Windows系统时,用户会在同一台机器上使用IIS,很少有单独安装一台IIS服务器给远程IRIS提供Web服务器的,当然这样也绝无问题。 有两个软件包可以安装CSP Gateway。一个是IRIS的安装包。在IRIS的安装过程中, 如果有IIS正在工作, 那么安装程序会自动的安装CSP Gateway。 2018年以前的Ensemble或者Cache'的版本的安装过程中会跳出一个询问框,让用户选择是否"安装CSP网关。。。”。而后来的版本大多不做询问而自动为用户做了选择。 如果需要强制安装或者不安装CSP Gateway,那么需要在安装中选"Customer Component"设置。 另一个软件包是单独的CSP Gateway安装包, 可以在InterSystems的WRC或者工程师处得到。这是一个只有10多兆的很小的安装包,它只安装CSP Gateway, 并不安装IRIS实例,适合在单独的IIS硬件服务器上安装CSP Gateway; 或者, 当一个服务器上已有了IRIS, 但后来想添加IIS服务和CSP Gateway,用这个单独的安装包也很方便。 单独的CSP Gateway的安装会在IIS的工作目录“C:\Inetpub"下添加子目录CSPGateway,或者WebGateway, 然后在IIS的default Website上添加CSP Gateway的配置。整个配置相当简单。下面的步骤是在Windows10上单独安装Web Gateway安装包2020.1连接同一台服务器上的IRIS的的过程。 其中后面配置CSP Gateway访问IRIS应用的部分可以参考Apache上配置CSP Gateway的文档。 ## CSP Gateway安装 1. 使用浏览器访问 http://localhost, 显示IIS的欢迎主页, 确认IIS已工作。 2. 打开WebGateway-20201.1.xxxx-win_x64应用程序开始安装。安装时会提示用户输入连接的IRIS Server的IP地址和端口。默认的Application Name用CSP,IP地址端口使用localhost, 51773。安装过程会重启IIS服务,安装结束后用户不用手工重启。 3. 安装后的检查IIS - 检查Web Gateway安装后的文件。 确认在IIS的安装目录(默认为C:\inetput)下安装了CSPGateway子目录, 其中包含若干dll文件。它们是IIS中用到的CSP Gateway的模块,在不同的CSP Gateway版本中这些Dll文件的数量和名字稍有不同。 - 创建了c:/intersystems/WebGateway的文件夹,早些的版本中并不创建这个目录。 - 打开IIS配置界面, 确认在Default Site下安装了CSP application. 在某些版本的Web Gateway安装后, /csp被安装为Virtual Directory, 关于IIS中application和virtual diectory的区别请自行查询, 无论安装成那种类型, 对csp的使用和配置没有区别。使用HealthConnect 2018以前版本的安装包安装的CSP Gateway生成CSP和ensemble两个Application。 4. 查看CSP Gateway登录页面。 登录http://localhost/csp/bin/Systems/Module.cxw。 在主页上会显示Web Gateway的版本, Web Server Type是"Microseof-IIS"。配置文件和日志文件在c:\Inetpub\CSPGateway目录下。 (如果是打开时出现HTTP错误500.19, 你需要重启IIS) 5. 查看连接的IRIS Server。 从左边菜单栏进入Server Access配置界面。其中csp是默认安装的IRIS连接。2018以前的版本可能用的是LOCAL。不管怎么说,使用"Edit Server"打开,可以看到连接的IRIS的端口和设置。 配置UserName"CSPSystem"和Password "SYS",并保存。 在左边菜单栏,使用"Test Server Connection"测试到“csp"的连接,测试成功会显示IRIS的版本。 Test Server Connection Server connection test was successful: csp (localhost:51773) $ZVersion: IRIS for Windows (x86-64) 2020.2 (Build 199U) Tue Apr 28 2020 17:17:56 EDT 6. 访问IRIS维护主页 (可选) 从链接 http://WebServer/csp/sys/Utilhome.csp 访问IRIS维护主页System Management Portal应该可以成功了,但您会发现有部分网页内容(element)无法加载。这是因为在默认的安装中,只将"csp cls zen cxw"这4种类型的请求发送给CSP Gateway, 而被称为Static file的文件,比如.js, .css, .png等等类型的文件并没有被发送给CSP Gateway. 这是另外的一个安全机制,强制客户人工的配置是否需要从Web服务器访问IRIS维护主页。如果答案是NO, 那么访问IRIS维护页面就只能通过PWS,用IRIS服务器的52773的接口。 如果用户认为从Web服务器访问IRIS维护页面是必要的, 需要修改CSPFileTypes配置,把任意类型的请求发送给IRIS。 7. 访问IRIS上的其他Web Application IRIS上其他的Web Application, 比如”/api", ”/test"等等,通常情况下当Web Application被添加后,配置会自动同步到Web Gateway, 用户不用去Web Gateway的页面配置路由。但IIS上必须填写响应的配置,才能把请求从IIS发送到CSP Gateway. 这样操作: - 添加applications。如下图所示, 在IIS的default site下添加了两个新application, test和api. - 为每个applcation配置"Handler Mappings"。**使用右边动作栏中的”Add Module Mapping",而不是另3种动作。** 还要注意不要勾选"Request Restrictions"的"invoke handler only if request is mapped to"选择框,这样在"Handler Mappings"页面看到的Path Type一栏中显示的是"Unspecified", 否则会显示"File"或者其他内容。 - 测试访问一些应用, 比如 http://172.16.58.200/api/mgmnt/v2/ http://172.16.58.200/test/test.webservice1.cls 一般情况下,到目前为止IIS已经能够正常工作,将需要的请求发送给IRIS。如果出现故障或者需要调整CSP gateway的配置,请参考[WebGateway系列_配置Apache连接IRIS] 请求如何在nginx配置呢 @Hao.Ma 马老师给看看? 目前只有在线文档, 回头我们写一个贴上来
文章
Qiao Peng · 十月 22, 2022

SQL业务服务和业务操作

1. 新的系统SQL业务服务/业务操作 接连SQL数据源和操作SQL数据目标是常见的集成业务场景。使用SQL适配器监控SQL数据源和操作SQL目标库时,我们需要开发自定义BS或BO,写不少代码。例如开发自定义SQL服务需要: 1. 开发响应消息类,用于承接SQL快照数据; 2. 开发自定义业务服务BS类,用于将SQL快照按字段赋值给对应的消息,并将消息发送给目标(业务流程或业务操作)。 而要开发自定义SQL操作,更麻烦些: 1. 开发请求和响应消息类,用于向BO传输数据和接收返回数据; 2. 开发自定义业务服务BO类,设置消息响应表,根据不同请求消息类型编写方法; 3. 在方法中根据请求消息数据拼写SQL语句; 4. 在方法中将SQL执行结果存入响应消息。 虽然很简单,但编程过程枯燥乏味。而且当修改SQL语句时,还要修改对应的消息类和BS/BO类。 从2021.2开始,InterSystems IRIS增加了2套系统SQL业务服务和SQL业务操作: BS EnsLib.SQL.Service.GenericService 使用SQL语句 BS EnsLib.SQL.Service.ProcService 使用存储过程 BO EnsLib.SQL.Operation.GenericOperation 使用SQL语句 BO EnsLib.SQL.Operation.ProcOperation 使用存储过程 只要直接将它们加入Production,就可以直接使用了,甚至无需再定义消息,真正实现无代码开发,nice! 2. 如何使用系统SQL业务服务/业务操作 下面我们来看看它们是怎么实现的,以及如何使用它们。 2.1 SQL业务服务 直接将EnsLib.SQL.Service.GenericService或EnsLib.SQL.Service.ProcService加入Production的BS中,并设置数据源、SQL语句和发送目标: 一步搞定,且无需使用IDE开发任何一行代码! 那么SQL业务操作发送给目标业务组件的是什么消息呢? 默认是Ens.StreamContainer,而结果集数据是以JSON表达并放在其Stream属性(流类型)下的,正好用到JSON免schema的特性。类似于: 如果你已经有一个消息类,想让SQL业务服务将结果集记录放在这个消息里?只要设置SQL业务服务的“消息类”即可: 当然,你自己定义的消息属性名要和返回的SQL结果集字段名一致,否则会报错。如果消息是以前定义好的,那么通过SQL语句使用AS来修改字段名最简单,例如SELECT NAME AS Display... 对如何操作JSON不熟? 在InterSystems IRIS/Cache'里,系统类%DynamicObject就是JSON对象,它有方法%FromJSON可以直接将JSON字符串/字符流转为JSON对象。所以可以直接以对象的方式操作JSON,就像下面在业务流程里将Ens.StreamContainer的Stream属性转为JSON对象使用: 2.2 SQL 业务操作 同样,SQL业务操作也无需代码开发。直接将EnsLib.SQL.Operation.GenericOperation或EnsLib.SQL.Operation.ProcOperation加入Production,进行配置即可。 配置项除了SQL数据目标的连接信息外,就是要执行的SQL语句了: 在 查询 中添加SQL语句,如果SQL语句中有任何参数需要传入,用"?" 代表。例如 Insert into QP.Patient(Gender,Name) values(?,?) 如果SQL需要输入参数,在 输入参数 中按顺序添加需要的参数,一般情况下是使用请求消息里的属性,在需要用的属性名前面加 *,说明这个参数来自请求消息。例如*Sex,*Name。 好,配置完成,依然没写一行代码! 但慢着,我们没有对这个BO定义请求消息呀? 我们不需要事先声明任何请求消息类,EnsLib.SQL.Operation.GenericOperation和EnsLib.SQL.Operation.ProcOperation可以接受任何类型的请求消息! 那么像上面的例子中提到的传入参数*Sex,*Name,是不是我必须要事先建一个请求消息类,里面有Sex和Name属性? 完全不必要!你可以直接通过系统请求消息Ens.StreamContainer、Ens.StringContainer、Ens.StringRequest传入JSON字符串/字符流即可,SQL业务操作会自动将JSON实例化为JSON对象。因此上面例子,可以传入Ens.StringRequest,只要JSON字符串里有Sex和Name这2个数据即可。 例如你可以直接测试这个BO: 是不是非常方便! * 注意:当使用Ens.StreamContainer、Ens.StringContainer、Ens.StringRequest 这些请求消息时,数据一定要是JSON格式的字符串/字符流,否则会得到报错信息! 那么SQL执行返回的结果在哪里? BO默认的响应消息是StreamContainer,因此执行结果都以JSON格式在它里面。 如果执行的SQL是INSERT/UPDATE/DELETE,那么返回的只有SQL语句影响的行数,放在NumRowsAffected里,如下: 如果执行的SQL是SELECT,则返回的是结果集如下: 如果你已经有定义过的消息类用于接收返回的SQL结果集,可以设置其“响应类”属性: SQL业务操作会将返回结果集的第一行按字段名对应保存到响应消息的属性上。 *注意:当SQL返回的结果集有多行记录时,这种使用用户自定义响应消息的情况下,会在事件日志中记录一条信息,并只将结果集第一行对应到自定义响应消息中! 今后连接SQL数据源和数据目标时,使用这2套系统SQL业务服务和业务操作,可以快乐地玩耍了!
文章
Michael Lei · 七月 3, 2023

基于LangChain的IRIS ChatGPT – 释放大语言模型LLM的全部潜力

你好社区在本文中,我将介绍我的应用程序irisChatGPT ,它是基于LangChain Framework构建的。首先,让我们对框架进行一个简单的概述。 全世界都在谈论ChatGPT以及大型语言模型 (LLM) 如何变得如此强大,并且表现超出预期,提供类似人类的对话。这只是将其应用于每个企业和每个领域的开始! 剩下的最重要的问题是如何将这种能力应用于适合企业需求的特定领域数据和特定场景响应行为。 LangChain为这个问题提供了结构化且有效的答案! LangChain 技术可以帮助实现法学硕士的巨大潜力,通过围绕法学硕士提供抽象层并使法学硕士的使用变得简单有效,从而构建令人惊叹的应用程序。 LangChain 是一个框架,可以快速轻松地开发使用大型语言模型(例如 GPT-3)的应用程序。 然而,该框架引入了额外的可能性,例如,轻松使用外部数据源(例如维基百科)来放大模型提供的功能。我相信你们都可能尝试过使用 Chat-GPT,并发现它无法回答特定日期之后发生的事件。在这种情况下,在维基百科上搜索可以帮助 GPT 回答更多问题。 LangChain结构 该框架分为六个模块,每个模块允许您管理与法学硕士互动的不同方面。让我们看看这些模块是什么。 模型:允许您实例化和使用三种不同类型的语言模型,它们是: 大型语言模型 (LLM):这些能够理解自然语言的基础机器学习模型。它们接受输入中的字符串并在输出中生成字符串。 聊天模型:由 LLM 支持的模型,但专门用于与用户聊天。您可以在这里阅读更多内容。 文本嵌入模型:这些模型用于将文本数据投影到几何空间中。这些模型将文本作为输入并返回数字列表,即文本的嵌入。 提示:提示是我们如何与模型交互以尝试从中获取输出。现在知道如何编写有效的提示至关重要。这个框架模块可以让我们更好的管理提示。例如,通过创建我们可以重用的模板。 索引:最好的模型通常是与一些文本数据相结合的模型,以便为模型添加上下文或解释某些内容。这个模块可以帮助我们做到这一点。 链:很多时候,要解决任务,对 LLM 的单个 API 调用是不够的。该模块允许集成其他工具。例如,一个调用可以是一个组合链,其目的是从维基百科获取信息,然后将此信息作为模型的输入。该模块允许连接多个工具以解决复杂的任务。 内存:该模块允许我们在模型调用之间创建持久状态。能够使用记住过去说过的话的模型肯定会改善我们的应用程序。 代理:代理是一个法学硕士,它做出决定,采取行动,观察其所做的事情,并以这种方式继续,直到完成其任务。该模块提供了一组可以使用的代理。 现在让我们更详细地了解一下如何利用不同的模块来实现代码。 Langchain工作原理 步骤1 :用户向LangChain发送问题第2步 :LangChain将此问题发送至Embedding Model步骤3:嵌入模型将文本转换为向量,文本以向量形式存储在数据库中并返回给LangChain步骤4 :LangChain将这些向量发送到向量数据库(有多个向量数据库,我们在我们的应用程序中使用chroma)步骤5:向量数据库返回前 K 个近似最近邻 ( KNN ) 向量第6步:LangChain 将问题与KNN向量一起发送到大型语言模型 (LLM) (我们在应用程序中使用 OpenAI)步骤7:LLM向Langchain返回答案步骤8:Langchain将答案返回给用户 关于申请 irisChatGPT应用程序利用围绕大型语言模型 (LLM) 构建的最热门 Python 框架LangChain的功能。 LangChain 是一个框架,可以快速轻松地开发使用大型语言模型的应用程序。应用程序是在系统间嵌入式 Python功能的帮助下使用 objectscript 构建的。它还包含Streamlit Web 应用程序,这是一个开源 Python 应用程序框架,用于为数据科学和机器学习创建漂亮的 Web 应用程序。 特征 以下是应用程序功能列表以及相关屏幕截图 内置Intersystems ObjectScript 参考ChatGPT 内置InterSystems 大奖赛 2023 ChatGPT 使用 SQLDatabaseChain 回答有关缓存数据库的问题 创建您自己的 chatGPT 模型并与其聊天 OpenAI 聊天GPT 维基百科搜索 使用DuckDuckGo(DDG)通用搜索引擎在互联网上搜索 使用Python REPL LangChain功能生成Python代码 Streamlit Web 应用程序在线演示 谢谢
文章
jieliang liu · 二月 5, 2021

精华文章置顶--使用VSCode 进行IRIS 开发

VSCode 是目前很流行的一款免费开发工具,IRIS也支持使用其进行连接和开发,相比Studio 只能在windows 环境使用,Vscode 可以跨平台使用。 我们传统的工具Studio 是连接代码服务器的形式,不能便捷的使用目前流行的有本地代码的版本控制工具(如git),但VSCode可以存在本地代码,并且能方便的使用各类存在本地代码的版本控制工具。 以下的内容会帮助大家来配置使用VSCode连接IRIS 进行开发。 VSCode 可以在微软的官网免费下载https://code.visualstudio.com/ 如果安装为英文要切换为中文则可以通过Command Palette 中运行Configure Display Language,install another language, 选择中文,再根据提示安装相应的语言插件。 接下来在扩展商店里搜索intersystems 安装如图所见的3个扩展包 VSCode目前有两种方式可以链接IRIS: 直接连接代码服务器进行编辑 使用客户端方式连接,将代码下载到本地,便于使用有本地代码的版本控制工具 注:两种连接方式的连接端口都为服务器web port 下面具体讲解两种连接方式。 直接连接代码服务器进行编辑 其总体的使用方式和studio相似,直接连接服务器进行代码编辑。在vscode中可以使用全局只读的方式来浏览代码,从而避免误操作修改了代码。 如果会经常使用这个服务器,可以考虑先建立一个工作区,将设置保存在工作区,当然直连服务器的模式下,建立服务器不是必选项。 如果我们正确的安装了之前提到的三个扩展,其中下面扩展会帮助我们连接IRIS代码服务器,并保存在VSCode的全局 settings.json配置文件中: 进入VSCode 设置中找到“扩展”下的Intersystems Server Manager ,进入设置可见安装自带的服务器连接字符串可以依据用户需要进行添加和修改 配置完成可以回到工作区界面进行连接 选择Choose Server and Namespaces, 从右边的服务器列表中选择要连接的服务器,也可以使用“+”来根据提示信息来添加新的服务器。 选择服务器之后会提示输入密码(如果没有在配置中写明登录用户,也会提示输入登录用户)验证成功后可以选择想查看的命名空间,最后按照提示选择是以编辑或者只读方式来查看代码。 在服务器直连的模式下,可以将多个服务器的多个命名空间的代码添加至当前工作区。 目前的连接方式进入之后会频繁的要求登录验证,输入密码,这里实际可以将密码存在VSCode的钥匙链中: 1.打开命令面板 2.输入store 使用下图中的命令则可以为选定的服务器存储密码,但是每个服务器只能保存一个密码,相应的在命令面板中输入clear 会提示清除密码的选项用来删除已存储的密码 2. 使用客户端方式连接 如果使用客户端的连接方式,则必须要在工作区内添加一个文件夹,以及保存此工作区。当添加文件夹到工作区之后侧边栏会多出图示的按钮: 点击按钮选择Choose server and namespaces 此时如果在vscode中已经添加过想要连接的IRIS 代码服务器,这里会直接显示已存储的服务器,设置法与之前提到的直连方式相同。 连接完成后如果想添加多个命名空间到工作区,可以使用右上角的+号进行添加 接下来可以在客户端模式连接导出ObjectScript代码到本地。从而进行源代码的版本控制管理。 在objectscript explorer中选择任意的包,或者类右键选择export,则可以将其导出到目前工作区的文件夹下 代码会被导出到工作区目录的src文件夹下。 客户端模式中使用 GIT 首先打开一个GIT Repo文件夹,如果本来然后按照客户端连接方式连接用来开发的服务器。在任何的包或文件上点击import and compile 可以将版本控制的代码导入开发服务器。 注意这时候只要在本地新建文件并且保存,新的修改就会被同步到开发服务器实例上。 以客户端方式连接时连接多个服务器或多个命名空间的方式: 首先将一个文件夹添加到工作区,并保存工作区,在保存之后的工作区中可以看到如下的工作区配置文件( 此处我使用的默认工作区配置文件名,实际可以自行定义),其中定义了工作区的目录结构: 此时我们在这个工作区内添加一个子目录,在工作区的列表右键单击,选择“将文件夹添加到工作区”: 添加后可以看到,在工作区配置文件中增加了新的目录,为了这个显示层级更加清楚,可以在settings 中使用files.exclude 来过滤是否显示子目录 此时子再点击侧边栏中的 object script explorer,则会让用户选择使用哪一个子目录进行连接: 要使用另一个目录连接一个服务器的命名空间时,将第一个已连接的文件夹下.vscode 拷贝到第二个文件夹: 直接对这个文件进行修改,将其配置成为想连接的服务器以及命名空间 连接配置文件同样支持如下的写法: 此时再回到object script exploer,可见到添加了的服务器。 可见这里服务器的连接和本地代码编译的目标对象只由当前文件夹下.vscode/settings.json控制。 所有的之前提到的配置文件都可以手工编辑,不必一定在vscode中进行添加文件夹和配置连接。 在VSCode中调试 以客户端方式连接时可以进行调试: 在侧边栏选择调试按钮,首次调试应在工作区创建launch.json,并选择ObjectScript Debug 对于Class Method,可以直接在类中点击“Debug this Method” 下边是一个Class Method 实例的实例调试界面 附加到进程调试: 对于需要附加到的进程,在lauch.json中添加”program” 属性,并填入进程号,保存。 program 中也可以填入要debug的routine 比如“##class(Test.test).test()”, “name”可以修改成自定义的名字,之后会在debug运行按钮的下拉菜单显示。 调试的配置可以添加多个,都会显示在debug下拉菜单中 进入调试界面,点击xDebug 旁边的绿色启动按钮开始调试,其他同Class Method 调试方式 关于VSCode几个编辑器小技巧: 在底部栏点击当前连接的服务器会出现提示菜单,从这里可以便捷进入实例管理界面和class reference 在任意的类或者方法上点击右键可以转到定义。如果使用了#dim预定义变量,则可以在变量上使用转到变量声明,而且可以使用转到类型定义类型的定义。 尾声 当前版本不支持xml 代码文件导入!VSCode community中已经有人提到过这个问题,开发者回复之后的版本很有可能会加入 本项目的Github的issue地址,可以在这里提出问题:https://github.com/intersystems-community/vscode-objectscript/issues 支持的产品 :Caché/Ensemble 2016.2 以及更新, 以及所有的基于IRIS 的产品版本. 调试入参类型为%Library.DynamicAbstractObjec的参数如何输入入参呢 对象入参的话,的确直接调试没法从键盘输入,可能还是需要附加到进程或者写一个临时方法来生成这个入参对象,再调用想调试的方法检查断点。 知乎文章:使用VS Code进行Caché数据库开发 好的,谢谢
文章
姚 鑫 · 二月 19, 2022

第五十九章 SQL函数 GETDATE

# 第五十九章 SQL函数 GETDATE 日期/时间函数,返回当前本地日期和时间。 # 大纲 ```java GETDATE([precision]) ``` # 参数 - `precision` - 可选-一个正整数,指定时间精度为小数秒的位数。 默认值是`0`(没有小数秒); 这个默认值是可配置的。 精度值是可选的,括号是必选的。 # 描述 `GETDATE`将此时区的当前本地日期和时间作为时间戳返回;它根据本地时间变量(如夏令时)进行调整。 `GETDATE`可以返回`%TIMESTAMP`数据类型格式(`yyyy-mm-dd hh:mm:ss.ffff`)或`%PosiTime`数据类型格式(编码的`64`位有符号整数)的时间戳。以下规则确定返回哪种时间戳格式: 1. 如果当前时间戳被提供给数据类型为`%PosiTime`的字段,则当前时间戳值将以`POSIXTIME`数据类型格式返回。例如, `WHERE PosixField=GETDATE() or INSERT INTO MyTable (PosixField) VALUES (GETDATE())` 2. 如果当前时间戳被提供给数据类型为`%TIMESTAMP`的字段,则当前时间戳值将以TIMESTAMP数据类型格式返回。其ODBC类型为`TIMESTAMP`,长度为`16`,精度为`19`,例如 `WHERE TSField=GETDATE() or INSERT INTO MyTable (TSField) VALUES (GETDATE())` 3. 如果当前时间戳是在没有上下文的情况下提供的,则当前时间戳值以`TIMESTAMP`数据类型格式返回。例如,选择`GETDATE()`。 要更改默认日期时间字符串格式,请使用带有各种日期和时间选项的`set option`命令。 `GETDATE`可以在`SELECT`语句`SELECT LIST`或查询的`WHERE`子句中使用。在设计报表时,可以使用`GETDATE`在每次生成报表时打印当前日期和时间。`GETDATE`对于跟踪活动也很有用,比如记录事务发生的时间。 可以在`CREATE TABLE`中使用`GETDATE`指定字段的默认值。`GETDATE`是`CURRENT_TIMESTAMP`的同义词,提供`GETDATE`是为了与Sybase和Microsoft SQL Server兼容。 `CURRENT_TIMESTAMP`和`NOW`函数还可以用于以时间戳或`POSIXTIME`格式将当前本地日期和时间作为时间戳返回。`CURRENT_TIMESTAMP`支持精度,现在不支持精度。 要仅返回当前日期,请使用`CURDATE`或`CURRENT_DATE`。要仅返回当前时间,请使用`CURRENT_TIME`或`CURTIME`。这些函数使用日期或时间数据类型。这些函数都不支持精度。 `TIMESTAMP`数据类型以相同的格式存储和显示其值。`POSIXTIME`数据类型将其值存储为编码的64位有符号整数。时间和日期数据类型将它们的值存储为`$HOROLOG`格式的整数。它们可以以显示格式或逻辑(存储)格式显示。可以使用`CAST`或`CONVERT`函数更改日期和时间的数据类型。 # 世界时(UTC) `GETDATE`返回当前本地日期和时间。除`GETUTCDATE`之外,所有SQL时间戳、日期和时间函数都特定于本地时区设置。`GETUTCDATE`将当前`UTC`(通用)日期和时间作为时间戳值或POSIXTIME值返回。还可以使用ObjectScript `$ZTIMESTAMP`特殊变量来获取通用的当前时间戳(独立于时区)。 # 精确到小数部分的秒 `GETDATE`可以返回多达9位的精度。 使用`precision`参数设置返回的精度的位数。 `precision`参数的默认值可以通过以下方式配置: - 使用`TIME_PRECISION`选项设置`OPTION`。 - 系统范围的`$SYSTEM.SQL.Util.SetOption()`方法配置选项`DefaultTimePrecision`。 要确定当前设置,调用`$SYSTEM.SQL.CurrentSettings()`,它显示默认的时间精度; 默认值为0。 - 进入管理门户,选择“系统管理”、“配置”、“SQL和对象设置”、“SQL”。 查看和编辑`GETDATE()`、`CURRENT_TIME`和`CURRENT_TIMESTAMP`的默认时间精度的当前设置。 指定从0到9(包括9)的整数,作为返回的十进制精度的默认位数。 默认值为0。 实际返回的精度取决于平台; 超过系统中可用精度的精度数字将作为零返回。 分数秒总是被截断,而不是四舍五入到指定的精度。 # 示例 下面的示例以`TIMESTAMP`格式返回当前日期和时间: ```sql SELECT GETDATE() AS DateTime 2022/2/12 15:39:00 ``` 下面的示例以两位精度返回当前日期和时间: ```sql SELECT GETDATE(2) AS DateTime 2022/2/12 15:39:21 ``` 下面的嵌入式SQL示例比较了本地(特定于时区)和通用(独立于时区)的时间戳: ```java ClassMethod GetDate() { &sql(SELECT GETDATE(),GETUTCDATE() INTO :a,:b) if SQLCODE'=0 { w !,"Error code ",SQLCODE } else { w !,"Local timestamp is: ",a w !,"UTC timestamp is: ",b w !,"$ZTIMESTAMP is: ",$ZDATETIME($ZTIMESTAMP,3,,3) } } ``` ```java DHC-APP> d ##class(PHA.TEST.SQLCommand).GetDate() Local timestamp is: 2022-02-12 15:40:34 UTC timestamp is: 2022-02-12 07:40:34 $ZTIMESTAMP is: 2022-02-12 07:40:34.978 ``` 下面的示例将`Orders`表中所选行中的`LastUpdate`字段设置为当前系统日期和时间。 如果`LastUpdate`是数据类型`%TimeStamp`, `GETDATE`返回当前日期和时间作为ODBC时间戳; 如果`LastUpdate`是数据类型`%PosixTime`, `GETDATE`返回当前日期和时间为编码的64位带符号整数: ```sql UPDATE Orders SET LastUpdate = GETDATE() WHERE Orders.OrderNumber=:ord ``` 在下面的例子中,`CREATE TABLE`语句使用`GETDATE`为`StartDate`字段设置一个默认值: ```sql CREATE TABLE Employees( EmpId INT NOT NULL, LastName CHAR(40) NOT NULL, FirstName CHAR(20) NOT NULL, StartDate TIMESTAMP DEFAULT GETDATE()) ```
文章
Nicky Zhu · 一月 18, 2021

活用数据库审计日志跟踪数据操作

在最近的项目里,多方同时连接同一个数据库并执行增删改查等各项数据操作。研发人员不时发现一些数据在不合规的情况下被新增甚至删除。因此,在实际工作中会有监控数据操作以便识别和处理异常操作的需求。本文将以监控和识别删除操作为例,介绍如何通过IRIS的审计功能实现对数据操作的监控和查询。 # 注意事项 在应用审计功能之前,必须注意的是: 1. 开启审计功能会事无巨细地记录每一条对应的操作(如被执行的SQL),因此对于存储空间的需求将急剧增加。举例而言,仅开启对XDBCStatement的监控后,对于一张只由5个简单(整型,VARCHAR型)字段构成的表中插入100万条记录,在Audit数据库中将占用300~400MB的空间。在因业务所需确实需要开启审计功能时,必须预先分配更多磁盘空间给IRIS Audit数据库,并在审计功能开启期间定时巡检磁盘空间,避免因日志占满磁盘导致其他数据无法写入引发系统挂起的故障。 2. 在研发环境中多人、多单位需要连接数据库时,应为不同的开发者和数据来源分配独立的数据库账户和权限,避免多人共用超级账户,导致数据异常时难以追踪异常操作究竟从何而来。也就意味着为不同角色的开发、测试、用户等参与者开启独立的用户,分配各自所需的的数据库权限以及管理数据库账户这样一系列项目正常运行所依赖的实践并不能被审计功能所替代。在项目进展过程中,您更希望见到的,一定不是出现问题后再来跟踪问题和耽误工期,而是通过良好的协作规程和研发习惯减少非技术问题出现的概率。 # IRIS中的审计功能 IRIS提供了丰富的数据库审计功能,用于记录从系统权限变更到数据删除的各类操作,用户可通过我们的[**官方文档**](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=GCAS_audit)查看系统支持的各类审计事件。 对于SQL操作而言,可用到的审计事件包括三大类: - %System/%SQL/DynamicStatement 可捕获通过动态SQL执行的SQL操作 - %System/%SQL/EmbeddedStatement 可捕获通过嵌入式SQL执行的SQL操作 - %System/%SQL/XDBCStatement 可捕获通过第三方(JDBC/ODBC)SQL连接执行的SQL操作 # 开启数据库审计 1. 由于默认安装时IRISAUDIT数据库通常位于IRIS安装目录,空间有限,因此在需要开启审计时,应先将IRISAUDIT数据库从系统安装目录迁移到空间更充裕的数据库目录 2. 在System Administration -> Security -> Auditing -> Configure System Events下,开启对%System/%SQL/XDBCStatement的监控,如下图 ![image](/sites/default/files/inline/images/picture1_3.png) 3. 预先配置好删除AuditLog的任务,以便在AuditLog过大时清除数据 系统虽然自带定时删除Audit数据库的任务,但其默认的触发条件为在Journal发生切换时才执行,因此不适用于需要扩展监控范围导致审计库将剧增的情况,应配置额外的任务便于随时执行。 ![image](/sites/default/files/inline/images/picture1_4.png) 并将其设置为按需启动 ![image](/sites/default/files/inline/images/picture1_5.png) 4. 待可疑操作发生时,使用SQL工具或portal在%SYS命名空间下运行如下SQL: SELECT ID, AuditIndex, Authentication, CSPSessionID, ClientExecutableName, ClientIPAddress, Description, Event, EventData, EventSource, EventType, GroupName, JobId, JobNumber, Namespace, OSUsername, Pid, Roles, RoutineSpec, Status, SystemID, UTCTimeStamp, UserInfo, Username FROM %SYS.Audit where Description = 'SQL DELETE Statement' order by UTCTimeStamp desc 即可找到最近的删除操作。通过Truncate table或delete语句执行的操作均可由该日志捕获到。如需监控insert或update等操作,在上述Description字段的选择上加入对insert或update语句的筛选即可。 通过这些日志,可以查看到操作发生的时间,来源IP和数据库用户等信息,如下所示: ![image](/sites/default/files/inline/images/picture1_6.png) 5. 必须再次提醒各位,一旦开启对语句的监控,Audit数据库会快速增长,需要每日甚至每日多次巡检,确保磁盘空间不会被占满导致系统崩溃。 一旦发现Audit所在磁盘占有量过大(例如大于80%)或Audit库本身的占用过大(例如大于20G),即应运行步骤3中 配置的任务,然后对Audit库进行压缩和截断操作释放空间。
文章
姚 鑫 · 十二月 27, 2022

第二十六章 使用系统监视器 - 使用 ^%SYSMONMGR 管理应用程序监视器

# 第二十六章 使用系统监视器 - 使用 ^%SYSMONMGR 管理应用程序监视器 如使用 `^%SYSMONMGR` 实用程序中所述,`^%SYSMONMGR` 实用程序允许管理和配置系统监视器,包括应用程序监视器。该实用程序可以在任何名称空间中执行,使用它所做的更改只会影响启动它的名称空间。必须通过在该命名空间中启动 `^%SYSMONMGR` 来为配置的每个启动命名空间维护单独的应用程序监视器配置。 注意:在对应用程序监视器配置进行任何更改(例如激活类)之后,必须在进行更改的名称空间中重新启动系统监视器以使更改生效。 要管理应用程序监视器,请在终端中输入以下命令: ```java %SYS>do ^%SYSMONMGR ``` 然后为管理应用程序监视器输入 `5`。显示以下菜单: ```java 1) Set Sample Interval 2) Manage Monitor Classes 3) Change Default Notification Method 4) Manage Email Options 5) Manage Alerts 6) Debug Monitor Classes 7) Exit Option? ``` 输入选择的数字或按 `Enter` 退出 `Application Monitor`实用程序。 # 管理应用程序监视器 主菜单中的选项可让管理应用程序监视器,如下表所述: Option| Description ---|--- 1) Set Sample Interval|设置指标采样的时间间隔;默认值为 `30` 秒。通过设置类特定的间隔(使用“管理监视器类”子菜单上的“设置类采样间隔”选项),可以为单个类覆盖此设置。注意:如果系统监视器采样间隔(请参阅“设置系统监视器选项”子菜单中的“设置采样间隔”)长于应用程序监视类的采样间隔,则使用两个间隔中较长的一个。例如,如果系统监视器间隔为 `30`,应用程序监视器间隔为 `120`,则所有活动的应用程序监视器类每 `120` 秒采样一次;如果系统监视器间隔为 `60` 且 `%Monitor.System.LockTableclass` 间隔为 `20`,则该类每 `60` 秒采样一次。 2) Manage Monitor Classes|显示 `Manage Monitor Classes` 子菜单,它允许在运行 `Application Monitor Manager` 的命名空间中管理系统和用户定义的监视器类。 3) Change Default Notification Method |允许指定警报触发时的默认操作。除非另有说明,否则创建的任何警报都将使用此操作。 4) Manage Email Options| 显示监控电子邮件选项子菜单,它允许启用和配置电子邮件通知,以便可以在警报中指定此操作。 5) Manage Alerts|显示 Manage Alerts 子菜单,它允许为系统和用户定义的监视器类创建警报。 ## 管理监视器类 该子菜单允许管理系统和用户定义的监视器类。输入选择的号码或按 `Enter` 返回主菜单: ``` Option? 2 1) Activate/Deactivate Monitor Class 2) List Monitor Classes 3) Register Monitor System Classes 4) Remove/Purge Monitor Class 5) Set Class Sample Interval 6) Exit Option? ``` 此子菜单显示菜单项列表,可让管理系统和用户定义的类,如下表所述: Option| Description ---|--- 1) Activate / Deactivate Monitor Class|`Application Monitor` 仅对活动类进行采样。此选项可让激活一个非活动类,或停用一个活动类。可以显示在本地名称空间中注册的系统和用户定义类的编号列表,包括每个类的激活状态,方法是输入?在类上?提示,然后输入编号或类名。 2) List Monitor Classes |显示在本地命名空间中注册的系统和用户定义类的列表,包括每个类的激活状态。 3) Register Monitor System Classes |注册所有系统监视器类(`%Monitor.System.HistorySys`、`%Monitor.System.HistoryPerf` 和 `%Monitor.System.HistoryUser` 类除外)并将它们存储在本地名称空间中。仍必须使用选项 1) 激活/停用此菜单上的监控器类来激活系统类,以便开始采样。 4) Remove/Purge Class|从本地名称空间中的类列表中删除监视器类。可以显示在本地名称空间中注册的系统和用户定义类的编号列表,包括每个类的激活状态,方法是输入?在类上?提示,然后输入编号或类名。注意:此选项不会删除类,而只是从可以激活的已注册类列表中删除类的名称。要重置列表,请在此菜单上选择选项 3) `Register Monitor System Classes`。 5) Set Class Sample Interval|允许为单个类覆盖默认的应用程序监视器采样间隔,该间隔由“管理应用程序监视器”菜单的 1) 设置采样间隔选项指定。默认值为 0,这意味着该类没有特定于类的样本间隔。有关此设置、设置采样间隔设置和设置系统监视器选项中讨论的系统监视器采样间隔之间优先顺序的说明,请参阅设置采样间隔选项的说明。 6) Debug Monitor Classes|显示 `Debug Monitor Classes` 菜单,它允许启用和禁用调试以及列出错误。
文章
姚 鑫 · 二月 25, 2022

第六十五章 SQL函数 %INTERNAL

# 第六十五章 SQL函数 %INTERNAL 返回逻辑格式表达式的格式转换函数。 # 大纲 ``` %INTERNAL(expression) %INTERNAL expression ``` # 参数 - `expression` - 要转换的表达式。 字段名、包含字段名的表达式,或返回可转换数据类型(如`DATE`或`%List`)中的值的函数。 # 描述 `%INTERNAL`将表达式转换为逻辑格式,与当前选择模式(显示模式)无关。逻辑格式是数据的内存格式(对其执行操作的格式)。`%INTERNAL`通常用于选择列表`SELECT-ITEM`。 **可以在`WHERE`子句中使用`%INTERNAL`,但强烈建议不要使用`%INTERNAL`,因为使用`%INTERNAL`会阻止在指定字段上使用索引,并且`%INTERNAL`会强制所有比较区分大小写,即使该字段有默认排序规则也是如此。** 应用`%INTERNAL`会将列标题名称更改为诸如“`Expression_1`”之类的值;因此,通常需要指定列名别名,如下面的示例所示。 `%INTERNAL`将数据类型%DATE的值转换为整数数据类型值。`%INTERNAL`将数据类型`%TIME`的值转换为数字`(15,9)`数据类型值。之所以提供此转换,是因为ODBC或JDBC客户端不识别逻辑`%DATE`和`%TIME`值。 `%INTERNAL`是否转换日期取决于日期字段或函数返回的数据类型。`%INTERNAL`转换`CURDATE`、`CURRENT_DATE`、`CURTIME`和`CURRENT_TIME`值。它不转换`CURRENT_TIMESTAMP`、`GETDATE`、`GETUTCDATE`、`NOW`和`$HOROLOG`值。 不能将流字段指定为ObjectScript一元函数(包括所有格式转换函数,`%Internal`除外)的参数。`%INTERNAL`函数允许将流字段作为表达式值,但不对该流字段执行任何操作。 `%INTERNAL`是InterSystems SQL扩展。 要将表达式转换为显示格式,而不考虑当前的选择模式,请使用`%EXTERNAL`函数。要将表达式转换为`ODBC`格式,而不考虑当前的`SELECT`模式,请使用`%ODBCOUT`函数。 # 示例 下面的动态SQL示例以当前选择模式格式返回出生日期(道布)数据值,并使用`%INTERNAL`函数返回相同的数据。出于演示目的,在此程序中,为每次调用随机确定`%SelectMode`值: ```java ClassMethod Internal() { s tStatement = ##class(%SQL.Statement).%New() s tStatement.%SelectMode=$RANDOM(3) if tStatement.%SelectMode=0 {WRITE "Select mode LOGICAL",! } elseif tStatement.%SelectMode=1 {WRITE "Select mode ODBC",! } elseif tStatement.%SelectMode=2 {WRITE "Select mode DISPLAY",! } s myquery = 2 s myquery(1) = "SELECT TOP 5 DOB,%INTERNAL(DOB) AS IntDOB " s myquery(2) = "FROM Sample.Person" s qStatus = tStatement.%Prepare(.myquery) s rset = tStatement.%Execute() d rset.%Display() w !,"End of data" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQLCommand).Internal() Select mode DISPLAY DOB IntDOB 04/25/1990 54536 01/02/2014 63189 01/02/2014 63189 01/28/1978 50066 5 Rows(s) Affected End of data ``` 下面的示例显示了此函数的两种语法形式;在其他方面它们是相同的。它们指定`%LIST`字段的`%EXTERNAL`(显示格式)、`%INTERNAL`(逻辑格式)和`%ODBCOUT`(ODBC格式): ```sql SELECT TOP 10 %EXTERNAL(FavoriteColors) AS ExtColors, %INTERNAL(FavoriteColors) AS IntColors, %ODBCOUT(FavoriteColors) AS ODBCColors FROM Sample.Person ``` ```sql SELECT TOP 10 %EXTERNAL FavoriteColors AS ExtColors, %INTERNAL FavoriteColors AS IntColors, %ODBCOUT FavoriteColors AS ODBCColors FROM Sample.Person ```
文章
shaosheng shengshao · 九月 14, 2022

windows下处理IIS在未安装但Healthshare已安装的时候,部署IIS服务并代理Healthshare

研究Healthshare2018在已经安装完成使用的情况下,部署IIS,并代理平台。之前看到可以通过单独的CSP Gateway安装包进行处理这种问题,该文主要是获取不到该安装包的时候可以如何实现IIS的处理。 第一步:首先按照网上教程部署IIS服务。安装完成之后会在C盘创建一个名叫intepub的包。 第二步:在inetpub包下面直接插入CSPGateway 。这个包是在其他先安装IIS下,后安装Healthshare的CSP网关服务的时候在inetpub下面自动插入的包,直接找一个复制过来。 第三步:打开IIS,创建一个叫csp的网站,按照下图配置。 第四步:在点击模块进行CSPms模块的添加。 接下来是配置好的CSPms的模块。 注意:我们从图中看到的CSPms模块是本机继承的模块,我们在右边添加本机模块的时候并不能找到C:\inetpub\CSPGateway\CSPms.dll这个模块,导致加不进去。那我们就直接在配置文件上面改。找到C:\Windows\System32\inetsrv\config该文件路径下的applicationHost.config文件,为了稳妥起见,我们先备份一下该文件。找到<globalModules>节点,在下面加入<add name="CSPms" image="C:\Inetpub\CSPGateway\CSPms.dll" />找到<modules>节点,在下面加入<add name="CSPms" />找到hiddenSegments节点,找到下面的bin参数,删除<add segment="bin " />,如下图所示。 保存文件,如果保存过程中出现不允许修改的问题,可以把文件贴到桌面,在这个文件上面改造,在copy回去直接替换。再回到iis页面,网站csp的模块里面就会出现对应的CSPms的本机模块。后面进行服务映射,双击点击处理程序映射。 第六步:接下来测试我们打开平台管理页面能不能打开。 如果出现了这个页面,那我们就快成功了,这个错误可能我们大家都有见过。我们打开C:\inetpub\CSPGateway下面的CSP.ini的文件。里面有个 有这个Password字段,因为我们的CSPGateway这个包是从其他服务器上copy过来的,这个密码只能用于那台服务,不能用于这个服务器,那我们可以拿57772端口的apache配置下的这个密码拿过来用。在安装路径下D:\InterSystems\HealthShare\CSP\bin的CSP.ini,也有一个Password字段,把这个密码copy过来放到C:\inetpub\CSPGateway下面的CSP.ini上。重启IIS服务,如果出现权限问题,也把CSP.ini拷贝到桌面,修改好后再贴回去进行替换。再次进行访问,就可以进去桌面了。第七步:点入到CSP网关管理,如果提示出现warning字眼,那是C:\inetpub\CSPGateway下面的用户权限问题。 对CSPGateway右键按照下图示例进行IIS_IUSRS用户权限的添加。对文件夹下的CSP.ini和CSP.log做一样的操作。 好文,欢迎参赛!
文章
姚 鑫 · 二月 15, 2021

第三十五章 Caché 变量大全 $ZNSPACE 变量

# 第三十五章 Caché 变量大全 $ZNSPACE 变量 包含当前命名空间名称。 # 大纲 ```java $ZNSPACE ``` # 描述 `$ZNSPACE`包含当前命名空间的名称。通过设置`$ZNSPACE`,可以更改当前名称空间。 要获取当前命名空间名称,请执行以下操作: ```java DHC-APP>SET ns=$ZNSPACE DHC-APP>WRITE ns DHC-APP ``` 还可以通过调用`%SYSTEM.SYS`类的`Namespace()`方法来获取当前命名空间的名称,如下所示: ```java DHC-APP>SET ns=$SYSTEM.SYS.NameSpace() DHC-APP>WRITE ns DHC-APP ``` 可以使用`%SYS.Namespace`类的`Existes()`方法测试命名空间是否已定义,如下所示: ```java DHC-APP>WRITE ##class(%SYS.Namespace).Exists("USER") 1 DHC-APP>WRITE ##class(%SYS.Namespace).Exists("LOSER") 0 ``` 对于UNIX®系统,默认命名空间建立为系统配置选项。对于Windows系统,它是使用命令行启动选项设置的。 命名空间名称不区分大小写。InterSystems IRIS始终以全大写字母显示显式名称空间名称,以全小写字母显示隐含的名称空间名称。 要获取指定进程的命名空间名称,请使用`%SYS.ProcessQuery`类的方法,如下例所示: ```java DHC-APP>WRITE ##CLASS(%SYS.ProcessQuery).%OpenId($JOB).NameSpaceGet() DHC-APP ``` # 设置当前命名空间 可以使用`ZNSPACE`命令、`SET $NAMESPACE`、`SET $ZNSPACE`或`%cd`实用程序更改当前名称空间。 - 在终端命令提示符下,`ZNSPACE`命令是更改名称空间的首选方式。`SET $ZNSPACE`在功能上与`ZNSPACE`命令相同。 - 在代码例程中,新建`$NAMESPACE`,然后设置`$NAMESPACE = NAMESPACE`是更改当前名称空间的首选方式。通过使用`new $NAMESPACE`和`SET $NAMESPACE`,可以建立一个名称空间上下文,该上下文在方法结束或发生意外错误时自动恢复到前一个名称空间。 可以使用`SET $ZNSPACE`更改进程的当前命名空间。将新命名空间指定为字符串文字或计算结果为带引号的字符串的变量或表达式。可以指定显式名称空间(`“NAMESPACE”`)或隐式名称空间(`“^SYSTEM^DIR”或“^^DIR”`)。 如果指定当前命名空间,则`SET $ZNSPACE`不执行任何操作,也不返回任何错误。如果指定了一个未定义的名称空间,则`SET $ZNSPACE`会生成一个``错误。 不能`new $ZNSPACE`特殊变量。 # 示例 在以下示例中,如果当前命名空间不是`USER`,则`SET $ZNSPACE`命令会将当前命名空间更改为`USER`。请注意,由于`if`测试,命名空间必须全部用大写字母指定。 ```java /// d ##class(PHA.TEST.SpecialVariables).ZNSPACE() ClassMethod ZNSPACE() { SET ns="USER" IF $ZNSPACE=ns { WRITE !,"命名空间已经 ",$ZNSPACE } ELSEIF 1=##class(%SYS.Namespace).Exists(ns) { WRITE !,"命名空间是 ",$ZNSPACE SET $ZNSPACE=ns WRITE !,"将命名空间设置为 ",$ZNSPACE } ELSE { WRITE !,ns," 不是定义的命名空间" } QUIT } ``` ```java DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZNSPACE() 命名空间是 DHC-APP 将命名空间设置为 USER ``` 此示例要求`UnnownUser`已分配`%DB_IRISSYS`和`%DB_USER`角色。
文章
Michael Lei · 一月 17, 2023

ZPM 简单实现实战宝典

ZPM 设计用于与 InterSystems IRIS 数据平台的应用程序和模块一起使用。 它由两个组件组成:ZPN 客户端(用于管理模块的 CLI)和注册表(模块和元信息的数据库)。 我们可以使用 ZPM 来搜索、安装、升级、移除和发布模块。 使用 ZPM,可以安装 ObjectScript 类、前端应用程序、互操作性生产环境、IRIS BI 解决方案、IRIS 数据集或任何文件,例如嵌入式 Python wheel。  今天的这份实战宝典将分为 3 个部分: 1. 安装 ZPM 2. 生成模块 3. 在注册表中查找、安装、发布模块   1. 安装 ZPM * 下载最新版本的 ZPM(它应该是一个 XML 文件)[下载链接](https://pm.community.intersystems.com/packages/zpm/latest/installer) * 将下载的 XML 导入到 IRIS(它只能部署到已打开 IRIS 的 IRIS 终端)并按 Enter 键 _write $SYSTEM.OBJ.Load("C:\zpm.xml", "c")_ 请注意,“C:\zpm.xml”是下载的 XML 文件的路径,这一步可能需要一些时间。 * 安装完成后,只需输入 _zpm_,按 Enter 键,您会看到您在 zpm shell 中 ![](/sites/default/files/inline/images/images/screenshot_2022-10-07_111845.png) 2. 生成模块 在开始生成模块之前,我们需要准备一个文件夹,里面有一个或多个可以加载的文件,因此我在 C 盘下创建了一个名为 zpm 的文件夹。 执行命令 _generate C:/zpm_ 在指定所有必要的内容后,您的第一个模块已成功生成,您还会看到  ![](/sites/default/files/inline/images/images/screenshot_2022-10-07_151452.png) 注意:  1. 模块版本正在使用语义化版本控制  2. 模块源文件夹是包含所有类文件的文件夹 3. zpm 还提供了一个选项,可以添加 web 应用程序和依赖项,本例中我将其留空 现在,打开文件资源管理器,您会看到一个名为“module.xml”的文件,如下面的屏幕截图所示 ![](/sites/default/files/inline/images/images/screenshot_2022-10-07_161718.png) 输入命令 _load C:\ZPM\ _,您会看到您的模块已重新加载、验证、编译和激活  ![](/sites/default/files/inline/images/images/screenshot_2022-10-12_110126.png)   3. 在注册表中查找、安装、发布模块 在当前注册表中查找可用的软件包:_zpm:USER>search_ 举例来说,从当前注册表安装软件包,让我们在公共注册表中安装一个名为 zpmshow 的模块:_zpm:USER>install zpmshow _(命令为 install "moduleName") 加载后发布模块:_zpm:USER>publish myFirstZPMDemo_ 可以使用 _zpm:USER>search_ 验证发布,本例中,您可以看到“myfirstzpmdemo 0.1.0”位于当前注册表中。 ![](/sites/default/files/inline/images/images/screenshot_2022-10-12_110359.png) 注意:如果发布模块时遇到以下错误:_“ERROR! Publishing module, something went wrong”(错误!发布模块时出错了)_,确保当前注册表的状态为“已启用”且可用。 可以使用_ zpm:USER>repo -list, _验证当前注册表的状态。   ![](/sites/default/files/inline/images/images/screenshot_2022-10-10_152312.png)   可用视频:[点击此处](https://www.loom.com/share/0ca097f0dea4476ea294841295f972b2%C2%A0%C2%A0)
文章
Michael Lei · 七月 4, 2023

小实验--生成式AI和 FHIR 结合

这是个实验项目,使用OpenAI API与FHIR资源和Python相结合来回答医疗行业的用户提问。 ## 项目想法 生成式人工智能,如[OpenAI上提供的LLM模型](https://platform.openai.com/docs/models), 已被证明在理解和回答高层次问题方面具有显著能力。他们使用大量的数据来训练他们的模型,因此他们可以回答复杂的问题。 他们甚至可以[使用编程语言,根据提示创建代码](https://platform.openai.com/examples?category=code) --我不得不承认,让我的工作自动化的想法让我感到有些焦虑。但到目前为止,似乎这是人们必须要习惯的事情,不管你喜不喜欢。所以我决定做一些尝试。 这个项目的主要想法是在我读到[这篇文章](https://the-decoder.com/chatgpt-programs-ar-app-using-only-natural-language-chatarkit/)关于[ChatARKit项目](https://github.com/trzy/ChatARKit)时产生的。这个项目使用OpenAI的API来解释语音命令,在智能手机摄像头的实时视频中渲染3D物体--非常酷的项目。而且,这似乎是一个热门话题,因为我发现最近有一篇[论文](https://dl.acm.org/doi/pdf/10.1145/3581791.3597296)遵循类似的想法。 让我最担心的是使用ChatGPT对AR进行**编程。由于有一个开放的github repo,我搜索了一下,发现[作者是如何使用ChatGPT生成代码的](https://github.com/trzy/ChatARKit/blob/master/iOS/ChatARKit/ChatARKit/Engine/ChatGPT.swift)。这种技术被称为*提示工程Prompt Engineering*--[这是维基百科关于它的文章](https://en.wikipedia.org/wiki/Prompt_engineering),或者这两个更实用的参考资料: [1](https://microsoft.github.io/prompt-engineering/)和[2](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/concepts/advanced-prompt-engineering?pivots=programming-language-chat-completions)。 所以我想--为什么不结合FHIR和Python试试类似的东西?以下是我的想法: ![Project basic idea](https://community.intersystems.com/sites/default/files/inline/images/project-diagram-01_4.png) 其主要构成是: - 一个提示工程模块,将命令人工智能模型使用FHIR和Python - 一个OpenAI API集成模块 - 一个Python解释器,用于执行生成的代码 - 一个FHIR服务器,回答人工智能模型生成的查询 基本思路是使用[OpenAI Completion API](https://platform.openai.com/docs/api-reference/completions),要求人工智能将问题分解为一堆FHIR查询。然后,人工智能模型创建一个Python脚本来处理InterSystems IRIS for Health中FHIR服务器返回的FHIR资源。 如果这个简单的设计是有效的,用户就可以得到应用的分析模型尚未支持的问题的答案。此外,这些由人工智能模型回答的问题可以被分析,以发现对用户需求的新见解。 这种设计的另一个好处是,你不需要用外部的API暴露你的数据和模型。例如,你可以问关于病人的问题,而不需要将病人数据或你的数据库模式发送到人工智能服务器上。由于人工智能模型使用公共可用的功能--FHIR和Python,你也不需要发布内部数据。. 但是,这种设计也导致了一些问题,比如: - 如何引导人工智能根据用户需求使用FHIR和Python? - 人工智能模型产生的答案是否正确?是否有可能对它们有信心? - 如何处理运行外部生成的Python代码的安全问题? 因此,为了尝试解决一下这些问题,我对最初的设计做了一些阐述,得到了这个: ![Project refined idea](https://community.intersystems.com/sites/default/files/inline/images/project-diagram-02_2.png) 我在项目里增加了一些新的元素: - 一个代码分析器来扫描安全问题 - 一个日志记录器,用于记录重要事件,以便进行进一步分析 - 一个用于进一步整合的API REST 因此,这个项目旨在验证这个概念,它可以支持实验来收集信息,以尝试回答这些问题。 在接下来的章节中,你会发现如何安装该项目并试用它。 然后,你会看到我在尝试回答上述问题时得到的一些结果和一些结论。 希望你觉得它有用。我们也非常欢迎你为这个项目做出贡献! ## 项目尝试 要试一试,请打开IRIS终端,运行以下内容: ```objectscript ZN "USER" Do ##class(fhirgenerativeai.FHIRGenerativeAIService).RunInTerminal("") ``` 例如,以下问题被用来测试该项目: 1. 数据集里有多少病人? 2. 病人的平均年龄是多少? 3. 给我所有的条件(代码和名称),去除重复的。将结果以表格的形式呈现出来。(不要使用pandas) 4. 有多少病人患有病毒性鼻窦炎(代码444814009)? 5. 病毒性鼻窦炎(代码444814009)在患者群体中的流行率是多少?对于多次出现相同病情的患者,考虑只打一次就可以计算出来。 6. 在病毒性鼻窦炎(代码444814009)患者中,性别组的分布是怎样的? 你可以找到这些问题的输出例子[这里](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/misc/tests-accuacy)。 > 请注意,如果你在你的系统上尝试,结果可能会有所不同,即使你使用相同的提示。这是由于LLM模型的随机性。 这些问题是由ChatGPT提出的。他们要求这些问题是以复杂程度不断提高的方式来创建的。第3个问题是个例外,它是由作者提出的。 ## 提示工程Prompt Engineering 项目使用的提示Prompt可以在方法`GetSystemTemplate()`中找到[这里](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/src/fhirgenerativeai/PromptService.cls)。 它遵循提示工程的指南,首先你给人工智能模型分配一个角色,然后输入一堆限制条件和指令。它的每个部分的意图都有注释,所以你可以理解它是如何工作的。 请注意一种接口定义的使用,当模型被指示假设一个已经定义好的名为`CallFHIR()`的函数与FHIR交互,而不是自己声明一些东西。这是受ChatARKit项目的启发,作者在该项目中定义了一整套函数,为使用AR库抽象出复杂的行为。 在这里,我使用这个技术来避免直接创建代码进行HTTP调用的模式。 这里一个有趣的发现是关于强迫人工智能模型以XML格式返回其响应。由于打算返回的是Python代码,我在XML中使用了CDATA块,将其对称化。 尽管在提示中明确指出响应格式必须是XML格式,但在以XML格式发送用户提示后,AI模型就开始遵循这个指令。你可以在上面提到的同一个类中的`FormatUserPrompt()`方法中看到这一点。 ## 代码分析器 该模块使用[bandit库](https://bandit.readthedocs.io/en/latest/)来扫描安全问题。 这个库生成Python程序的AST,并针对常见的安全问题对其进行测试。你可以在这些链接中找到被扫描的问题种类: - [测试插件](https://bandit.readthedocs.io/en/latest/plugins/index.html#complete-test-plugin-listing) - [调用黑名单](https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html) - [导入黑名单](https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html) 由人工智能模型返回的每一个Python代码都会针对这些安全问题进行扫描。如果发现有问题,就会取消执行并记录错误。 ## 日志记录器 所有的事件都被记录下来,以便在表[LogTable](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/src/fhirgenerativeai/LogTable.cls)中作进一步分析。 每个回答问题的运行都有一个会话ID。你可以在表中的'SessionID'列中找到它,并通过将它传递给方法`RunInTerminal("", )`来获得所有事件。例如: ```objectscript Do ##class(fhirgenerativeai.FHIRGenerativeAIService).RunInTerminal("", "asdfghjk12345678") ``` 你也可以用这个SQL来检查所有的日志事件: ```sql SELECT * FROM fhirgenerativeai.LogTable order by id desc ``` ## 测试 我执行了一些测试以获得信息来衡量人工智能模型的性能。 每个测试执行了15次,它们的输出被存储在[this](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/misc/tests-accuacy)和[this](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/misc/tests-security)的目录下。 > 请注意,如果你在你的系统上尝试,结果可能会有所不同,即使你使用相同的提示。这是由于LLM模型的随机性。 ### 准确率 对于问题#1的测试,有`14个结果6`和`1个错误`。正确值是`6'。所以它是`100%`正确的,但有`6%`的执行失败。 验证#1结果的SQL语句: ```sql SELECT count(*) FROM HSFHIR_X0001_S.Patient ``` 对于第2题的测试,有`3个结果52`,`6个结果52.5`和`6个错误`。正确的数值--考虑到有小数点的年龄,是`52.5'。所以我认为这两个值都是正确的,因为这一点差异可能是由于提示不明确造成的--它没有提到任何关于允许或不允许带小数的年龄。因此,它是`100%`正确的,但执行失败的是`40%`。 验证#2结果的SQL语句: ```sql SELECT birthdate, DATEDIFF(yy,birthdate,current_date), avg(DATEDIFF(yy,birthdate,current_date)) FROM HSFHIR_X0001_S.Patient ``` 在第3个问题的测试中,有 "3个错误 "和 "12个有23个不同元素的表格"。表的值不在相同的位置和格式中,但我还是认为这因为错误格式的提示造成的。因此,它是`100%`正确的,但有`20%`的执行失败。 验证#3结果的SQL语句: ```sql SELECT code, count(*) FROM HSFHIR_X0001_S.Condition group by code ``` 对于第4题的测试,有`2个错误`,`12个结果7`和`1个结果4`。正确值是`4'。所以它是`12%`正确的,有执行失败的`13%`。 验证#4结果的SQL语句: ```sql SELECT p.Key patient, count(c._id) qtde_conditions, list(c.code) conditions FROM HSFHIR_X0001_S.Patient p join HSFHIR_X0001_S.Condition c on c.patient = p.key where code like '%444814009%' group by p.Key ``` 对于5号问题的测试,有`11个错误`,`3个结果为4`,`1个结果为0.6`。正确值是`4'。所以它的正确率是`75%`,执行失败率是`73%`。 用来验证#5结果的SQL与#4所用的相同。 对于6号问题的测试,有`12个错误`和`3个结果{女性:4,男性:2,其他: 0}`. 正确的值是`{女性:3,男性:1,其他: 0}`. 所以它是`0%`正确的,有执行失败的`80%`。 验证#6结果的SQL语句: ```sql SELECT p.gender, count(distinct p._id) qty_patients, list(p.key) patients, list(c.code) conditions FROM HSFHIR_X0001_S.Patient p join HSFHIR_X0001_S.Condition c on c.patient = p.key where code like '%444814009%' group by p.gender ``` 总结一下测试结果: | 测试编号 # |准确率 | 执行失败率 | |--------|----------|-------------------| | 1 | 100% | 6% | | 2 | 100% | 40% | | 3 | 100% | 20% | | 4 | 12% | 13% | | 5 | 75 % | 73% | | 6 | 0% | 80% | ### 安全性 你可以在[测试目录](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/misc/tests-accuacy)中检查生成的代码,似乎人工智能模型没有产生安全问题--这也是代码分析器模块发现的结果,没有安全问题。 因此,为了测试代码分析器是否能够检测到安全问题,[执行Python代码的模块](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/src/fhirgenerativeai/PythonService.cls)有一个名为 "代码注入 "的属性,可以注入恶意代码,以便进行测试。 因此,为安全测试注入了以下代码: 1. 安全测试 #1: ```python print(eval("os.chmod('%s', 0o777)" % 'test.txt')) ``` 这段代码试图访问SO文件系统并改变一个文件的权限。这显然是一个不符合项目意图的代码,所以它必须被阻止。 2. 安全测试 #2: ```python import requests url = "https://postman-echo.com/get" params = {"arg": "value"} response = requests.get(url, params=params) data = response.json() print(data) ``` 这段代码试图向一个外部地址发出HTTP请求--本例中是Postman API。如果这段代码被执行,本地信息就会被攻击者轻易地获取。 正如你在[安全测试目录](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/misc/tests-security)中所看到的,代码分析器成功地发现了安全问题。 ## 结论 就准确性而言,人工智能模型在低复杂度的问题上得到了正确的结果,但随着问题复杂度的增加而开始失败。同样的情况也出现在执行失败上。因此,问题越复杂,人工智能模型产生的代码就越多,无法执行,导致错误结果的概率就越大。 这意味着需要对提示做出一些努力。例如,在[问题#6的代码](https://github.com/jrpereirajr/iris-fhir-generative-ai/blob/master/misc/tests-accuacy/6/1688265739062.txt)中,错误在于只询问病人而忽略了条件。这种分析对于指导提示的改变是必要的。 总的来说,人工智能模型在这次测试中的表现表明,在能够回答分析性问题之前,它仍然需要更多的改进。 这是由于人工智能模型的随机性质。我的意思是,在上面提到的ChatARKit项目中,如果人工智能模型渲染的三维物体并不完全在要求的地方,但接近它,可能用户不会介意。不幸的是,同样的情况并不适用于分析性问题,答案需要精确。 但是,我并不是说人工智能模型不能执行这样的任务。我要说的是,这个项目中使用的设计需要改进。 需要注意的是,这个项目没有使用更先进的技术来使用生成器AI,像[Langchain](https://python.langchain.com/docs/get_started/introduction.html)和[AutoGPT](https://autogpt.net/autogpt-installation-and-features/)。这里使用了一种更 "纯粹 "的方法,但使用这种更复杂的工具可能会导致更好的结果。 关于安全性,代码分析器发现了所有测试的安全问题。 然而,这并不意味着由人工智能模型生成的代码是100%安全的。此外,允许执行外部生成的Python代码可能绝对是危险的。你甚至不能百分之百地确定提供Python代码的系统实际上是OpenAI的API服务器...... 避免安全问题的一个更好的方法可能是尝试其他不如Python强大的语言,或者尝试创建你自己的 "语言 "并将其呈现给AI模型,就像在[这个非常简单的例子](https://platform.openai.com/examples/default-text-to-command)。 最后,重要的是要注意,像代码性能这样的方面在这个项目中没有涉及,可能也会成为未来工作的一个好主题。 所以,我希望大家能发现这个项目的有趣和有用。 > **免责声明:这是一个实验性项目。它将向OpenAI API发送数据,并在你的系统上执行由AI生成的代码。所以,不要在生产系统上使用它。还要注意,由于OpenAI的API调用是收费的。使用它的风险由你自己承担。它不是一个可用于生产的项目。** Hi! Just here to a quick update: now we published a video about this project. Enjoy it: 😊
文章
Michael Lei · 七月 4, 2021

数据平台和性能 - 第 7 部分 用于确保性能、可伸缩性和可用性的企业缓存协议ECP

(ECP) Caché 出色的可用性和扩展特性之一是企业缓存协议 (ECP)。 在应用程序开发过程中,如对使用 ECP 的分布式处理加以考虑,可以横向扩展 Caché 应用程序的架构。 应用程序处理可以调整为非常高的速率,处理能力从单个应用程序服务器扩展到最多 255 个应用程序服务器,并且不需要任何应用程序更改。 在我参与的 TrakCare 部署中,ECP 已广泛使用多年。 十年前,主要供应商之一的一台“大型”x86 服务器可能总共只有八个核心。 对于大型部署来说,ECP 是横向扩展商业服务器处理能力的方式,不适合单台昂贵的大型企业服务器。 即使是高核心数的企业服务器也有限制,因此 ECP 也用于扩展这些服务器上的部署。 如今,大多数的新 TrakCare 部署或升级到当前硬件_不需要 ECP_ 即可扩展。 目前的双插槽 x86 生产服务器可以拥有数十个核心和巨大容量的内存。 我们看到,在最近的 Caché 版本中,TrakCare 以及许多其他 Caché 应用程序具有可预测的线性扩展能力,能够随着单台服务器中 CPU 核心数量和内存的增加而支持逐渐增多的用户和事务。 在现场,我看到大多数的新部署都是虚拟化的,即使如此,虚拟机也可以根据需要扩展到主机服务器的规模。 如果资源需求超过单个物理主机可以提供的资源,则使用 ECP 进行横向扩展。 - ___提示:___ _为了简化管理和部署规模,在部署 ECP 之前,先在单台服务器内扩展。_ 在本帖中,我将展示一个示例架构以及 ECP 工作原理的基础知识,然后评论性能注意事项,重点是存储。 有关配置 ECP 和应用程序开发的具体信息,请参见在线的 [Caché 分布式数据管理指南](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GDDM),并且[社区上有一个 ECP 学习轨迹](https://community.intersystems.com/learning-track/enterprise-cache-protocol-ecp-videos)。 ECP 的其他关键特性之一是提高了应用程序可用性,有关详细信息,请参见 [Caché 高可用性指南](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GHA_ecp)中的 ECP 部分。 [本系列其他帖子的列表](https://cn.community.intersystems.com/post/intersystems-数据平台的容量规划和性能系列文章) # ECP 架构基础知识 ECP 的架构和运行在概念上很简单,ECP 提供了在多个服务器系统之间有效共享数据、锁定和可执行代码的方法。 从应用程序服务器角度看,数据和代码远程存储在_数据服务器_上,但缓存在_应用程序服务器_的本地内存中,以提供对活动数据的有效访问,同时尽可能减少网络流量。 数据服务器管理对磁盘上持久性存储的数据库读写,而多个应用程序服务器是解决方案的主力,执行大多数应用程序处理。 ## 多层架构 ECP 采用多层架构。 描述处理层和它们扮演的角色有多种不同的方式,以下是我在描述基于 Web 浏览器的 Caché 应用程序时发现很有用的方式,也是我的帖子的模型和术语。 我知道可能有不同的方法来细分层级,但现在先使用我的方法 :) 基于浏览器的应用程序(例如 Caché Server Pages (CSP))使用多层架构,其中表示、应用程序处理和数据管理功能在逻辑上是分开的。 具有不同角色的__逻辑__“服务器”填充各层。 逻辑服务器不必保留在单独的物理主机或虚拟服务器上,出于成本效益和可管理性的考虑,部分甚至全部逻辑服务器可能位于单个主机或操作系统实例上。 随着部署规模的扩展,服务器可以通过 ECP 划分到多个物理或虚拟主机上,从而可根据需要分散处理工作负载,而无需更改应用程序。 主机系统可以是物理的或虚拟化的,具体取决于容量和可用性要求。 以下层和逻辑服务器构成了一个部署: - _表示层:_包括在基于浏览器的客户端和应用程序层之间充当网关的 Web 服务器。 - _应用程序层:_这是 ECP 应用程序服务器所在的位置。 如上文所述,这是一个逻辑模型,其中应用程序服务器不必与数据服务器分开,而且除了最大型的站点外,所有情况下通常都不需要分开。 该层还可能包括进行专门处理的其他服务器,如报告服务器。 - _数据层:_这是数据服务器所在的位置。 数据服务器执行事务处理,是存储在 Caché 数据库中的应用程序代码和数据存储库。 数据服务器负责读写持久性磁盘存储。 ## 逻辑架构 下图是一个基于浏览器的应用程序在部署为三层架构时的逻辑视图: 尽管初看之下该架构可能很复杂,但构成它的组件仍然与安装在单台服务器上的 Caché 系统的组件相同,只是逻辑组件安装在多个物理或虚拟服务器上。 服务器之间的所有通信都通过 TCP/IP 进行。 ### 逻辑视图中的 ECP 操作 上图从顶部开始,显示用户安全地连接到多个已进行负载平衡的 Web 服务器。 这些 Web 服务器在客户端和应用程序层(应用程序服务器)之间传递 CSP 网页请求,应用程序层进行所有处理,允许动态创建内容,并通过 Web 服务器将完成的页面返回给客户端。 在这个三层模型中,应用程序处理通过 ECP 分散到多个应用程序服务器上。 应用程序只将数据(您的应用程序数据库)视为应用程序服务器的本地数据。 当应用程序服务器发出数据请求时,它将尝试从本地缓存满足请求,如果不能满足,ECP 将向数据服务器请求必要的数据,数据服务器自己的缓存可能会满足请求,否则将从磁盘获取数据。 数据服务器对应用程序服务器的回复包括存储该数据的数据库块。 这些块将被使用,并且此时将缓存到应用程序服务器上。 ECP 自动负责管理整个网络中的缓存一致性,并将变化传播回数据服务器。 客户端会体验到快速响应,因为它们经常使用本地缓存的数据。 默认情况下,Web 服务器与首选的应用程序服务器通信,确保同一应用程序服务器满足相关数据的后续请求,因为这些数据可能已经在本地缓存中。 - ___提示:___ _如 [Caché 文档](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GDDM_develop#GDDM_develop_considerations)中详述,在循环或负载平衡方案中,应避免用户连接到应用程序服务器,因为这会影响应用程序服务器上缓存的优势。 理想情况下,相同的用户或用户组保持连接到同一应用程序服务器。_ 该解决方案通过在表示层添加 Web 服务器和在应用程序层添加其他应用程序服务器来进行扩展,无需用户停机。 数据层通过增加数据服务器的 CPU 和内存来进行扩展。 ## 物理架构 下图显示了与三层逻辑架构示例相同的三层部署中使用的物理主机示例: 请注意,在每一层部署物理或虚拟主机时均采用 n+1 或 n+2 模式,以确保在主机故障或计划维护时保持 100% 能力。 由于用户分布在多个 Web 和应用程序服务器上,单个服务器故障只会影响少量用户,他们会自动重新连接到其余服务器之一。 数据管理层具有高度可用性,例如,位于连接到一个或多个存储阵列的故障转移集群上(例如,虚拟化 HA、InterSystems 数据库镜像或传统的故障转移集群)。 如果硬件或服务出现故障,集群将在其中一个幸存节点上重启服务。 ECP 的一个附加好处是内置弹性,并且在数据库节点集群发生故障转移时能保持事务完整性,应用程序用户将观察到处理暂停,直到故障转移和自动恢复完成,随后用户将无缝继续,不会断开连接。 同样的架构也可以映射到虚拟化服务器,例如,VMware vSphere 可用于虚拟化应用程序服务器。 # ECP 容量规划 如上文所述,数据服务器管理对持久性磁盘的数据库读写,而多个应用程序服务器是解决方案的主力,执行大多数应用程序处理。 这是考虑系统资源容量规划时的一个关键概念,总结来说: - ___数据服务器___(有时称为数据库服务器)通常执行很少的应用程序处理,因此_对 CPU 要求低_,但该服务器执行大部分存储 IO,因此可能有_非常高的存储 IOPS_,即数据库读写以及日志写入(稍后将详细介绍日志 IO)。 - ___应用程序服务器___执行大多数应用程序处理,因此_对 CPU 要求高_,但存储 IO 非常少。 通常,调整 ECP 服务器 CPU、内存和 IO 要求的规则与调整非常大的单服务器解决方案的规则相同,同时考虑 N+1 或 N+2 台服务器以确保高可用性。 ## 基本 CPU 和存储规模调整: 假设 My_Application 需要最多 72 个 CPU 核心进行应用程序处理(记得还要考虑余量),并且预计在写入守护进程周期期间需要 20,000 次写入,以及 10,000 次随机数据库读取的持续峰值。 一个简单的虚拟或物理服务器规模调整方案为: - 4 台 32 CPU 应用程序服务器(3 台服务器 + 1 台服务器用于确保 HA)。 低 IOPS 要求。 - 2 台 10 CPU 数据服务器(镜像或集群以确保 HA)。 [低延迟 IOPS 要求](https://cn.community.intersystems.com/post/数据平台和性能-第-6-部分-caché-存储-io-配置文件)为 20K 写入、10K 读取,加上 WIJ 和日志。 虽然数据服务器只执行非常少的处理,但考虑到系统和 Caché 进程,将其规模调整为 8-10 个 CPU。 应用程序服务器的规模可以根据每台物理主机的最佳性价比和/或可用性来进行调整。 横向扩展时会有一些效率损失,但通常可以在服务器块中增加处理能力,并预计吞吐量有近乎线性的增长。 限制更有可能首先在存储 IO 中出现。 - ___提示:____与确保 HA 一样,要考虑主机、机箱或机架故障的影响。 在 VMWare 上虚拟化应用程序和数据服务器时,确保应用 vSphere DRS 和相关性规则以分散处理负载并确保可用性。_ ## 日志同步 IO 要求 ECP 部署的另一个容量规划注意事项是,由于日志同步,它们需要较高 IO,并且对存储响应时间的要求非常严格,以保持数据服务器上日志记录的可伸缩性 。 同步请求可以触发对日志中最后一个块的写入,以确保数据耐久性。 不过您的情况可能有所不同;在一个典型的以高事务处理速率运行的客户站点上,我经常看到非 ECP 配置上的日志写入 IOPS 为每秒十几次。 在繁忙的系统上使用 ECP 时,由于 ECP 强制日志同步,可以在日志磁盘上看到 100 到 1000 的写入 IOPS。 - ___提示:____如果在繁忙的系统上显示 mgstat 或查看 [pButtons](https://cn.community.intersystems.com/post/intersystems-数据平台和性能-–-第-1-篇) 中的 mgstat,您将看到 Jrnwrts(日志写入次数),您将在存储 IO 资源规划中对其加以考虑。 在 ECP 数据服务器上,还有未显示在 mgstat 中的对日志磁盘的日志同步写入,要了解这些信息,您需要查看日志磁盘的操作系统指标,例如使用 iostat 查看_。 ### 什么是日志同步? 需要日志同步的原因: - 确保在数据服务器发生故障时数据的耐久性和可恢复性。 - 它们也是确保应用程序服务器之间的缓存一致性的触发器。 在非 ECP 配置中,对 Caché 数据库的修改将写入日志缓冲区(128 x 64K 缓冲区),当日志缓冲区满时或每两秒由日志守护程序写入磁盘上的日志文件。 Caché 为整个缓冲区分配 64k,并且这些缓冲区总是被重复使用,而不是被销毁和重新创建,Caché 只是跟踪末尾偏移量。 在大多数情况下(除非一次进行大量更新),日志写入次数非常小。 ECP 系统中也有日志同步。 日志同步可以定义为将当前日志缓冲区的相关部分重新写入磁盘,以确保磁盘上的日志始终是最新的。 因此,日志同步会请求多次重新写入同一日志块的某个部分(大小在 2k 到 64k 之间)。 ECP 客户端上可以触发日志同步请求的事件为更新(SET 或 KILL)或 LOCK。 例如,对于每个 SET 或 KILL,都会将当前日志缓冲区写入(或重新写入)磁盘。 在非常繁忙的系统中,单次同步操作中的日志同步可能被捆绑或延迟为多个同步请求。 ### 日志同步的容量规划 为确保持续的吞吐量,日志同步的平均写入响应时间必须: - _