#命名空间

0 关注者 · 7 帖子

命名空间是一个逻辑实体,它提供对存储在多个数据库中的数据和代码的访问。

InterSystems 官方 Claire Zheng · 七月 7, 2025

InterSystems 发布了新的点式更新,该更新解决的缺陷问题影响以下受支持的产品线的最新先前版本 2025.1.0、2024.1.4、2023.1.6 和 2022.1.7:

  • InterSystems IRIS
  • InterSystems IRIS for Health
  • HealthShare Health Connect

此问题可能导致在使用以下功能时出现意外的 <PROTECT> 错误或访问异常:

  • 隐式命名空间
  • 对数据库的混合只读/读写访问
  • 用于列出例程和全局变量的管理门户页面

症状包括:

  • 命名空间创建故障
  • 在列出例程时,间歇性出现“访问被拒”错误
  • 对于具有只读权限的用户,全局显示页面不返回数据

虽然这些问题并影响访问控制或用户权限,但它们会在多种场景下影响预期的系统行为。

问题得到解决的版本

以下点式版本中修复了该问题:

  • 2025.1.0.230.2
  • 2024.1.4.516.1
  • 2023.1.6.810.1
  • 2022.1.7.116.1

建议使用受影响版本的客户应用相关更新,以确保系统正常运行。

如果您有任何问题或需要支持,请联系 InterSystems 全球响应中心 (WRC)

0
0 48
InterSystems 官方 Claire Zheng · 七月 7, 2025

摘要

公告编号 受影响的产品和版本 风险类别和评分 明确要求

DP-439649

产品:

  • InterSystems IRIS®
  • InterSystems IRISfor Health
  • HealthShare®Health Connect

版本:

  • 2025.1.0.225.1
  • 2025.1.0.223.0
  • 2024.1.4
  • 2023.1.6
  • 2022.1.7

操作:
4 – 高风险

系统稳定性:
3 – 中等风险

此问题构成安全漏洞。 它允许用户绕过权限检查或访问其授权命名空间之外的数据

使用隐含命名空间、管理门户或数据库读写/只读混合访问权限

在命名空间之间切换或使用以下任何功能访问环境中的全局变量时,上面列出的 InterSystems 产品中的问题可能会引发意外的 <PROTECT>错误

  • 隐含命名空间
  • 只读访问默认数据库,但读写访问其他地方
  • 列出例程和全局项的管理门户页面

该问题的症状包括:

  • 存在命名空间创建故障 (DP-440830)
  • 在管理门户中列出例程时,间歇性出现访问被拒的情况 (DP-439622)
  • 全局显示实用工具不显示全局变量(如果用户只有只读权限)(DP-440744)
0
0 53
文章 姚 鑫 · 二月 1, 2025 2m read

第十二章 I 开头的术语

以 I 开头的术语

被识别 (identified by)

对象(Objects)

当一个类在逻辑上依赖于另一个类的存在时,它就被另一个类所识别。

识别关系 (identifying relationship)

对象(Objects)

识别关系定义了两个类之间的关系,其中一个类依赖于另一个类的存在。

身份 (identity)

对象(Objects)

对象的身份或 ID 在其范围内唯一地标识该对象。

idkey

对象(Objects)

用于指定对象 ID 内容的索引。任何在 idkey 中使用的属性在对象生命周期内必须保持静态。

隐式全局引用 (implicit global reference)

系统

请参见 映射全局引用(Mapped Global Reference)。

隐含命名空间 (implied namespace)

系统

当你在扩展全局引用中使用目录或目录和系统名称时,IRIS 内部创建的命名空间。

包含文件 (include file)

ObjectScript

包含定义的文件,可在 ObjectScript 源代码编译的预处理阶段使用,以扩展宏源例程并确定是否应包含可选的代码行。它们还可以用于在多个例程中包含一段公共代码块,节省调用公共子例程的开销。

传入锁 (incoming lock)

系统

由远程客户端计算机上的进程对本地计算机上的项目发出的锁。这也称为服务器锁,因为被锁定的项目位于作为服务器的本地计算机上。当你查看锁时,此锁会在锁表显示中出现在“所有者”列中,显示发出 LOCK 请求的远程计算机的系统名称。本地服务器计算机不知道远程客户端计算机上的哪个进程发出了 LOCK,也不会跟踪项目上的锁数量。

索引 (index)

对象(Objects)

通过为其类中的每个对象存储排序后的数据子集来优化数据检索的索引。

索引排序 (index collation)

对象(Objects)

索引排序指定在索引中存储数据时使用的数据转换方式。

继承 (inheritance)

对象(Objects)

继承将一个类的特性和成员传递给其所有子类。它允许你将多个类的共同方面集中在一个超类中。

内存中的值 (in-memory value)

对象(Objects)

属性在内存中的值。对于某些类型的属性,这可能与其存储的(或磁盘上的)值不同。

0
0 62
文章 Louis Lu · 六月 11, 2024 8m read

这篇文章介绍了使用由支持 langchain 框架的IRIS来实现问答聊天机器人,其重点介绍了检索增强生成(RAG)。

文章探讨了IRIS中的向量搜索如何在langchain-iris中完成数据的存储、检索和语义搜索,从而实现对用户查询的精确、快速的响应。通过无缝集成以及索引和检索/生成等流程,由IRIS驱动的RAG应用程序使InterSystems开发者能够利用GenAI系统的能力。

为了帮助读者巩固这些概念,文章提供了Jupyter notebook一个完整的问答聊天机器人应用程序,以供参考。

什么是RAG以及它在问答聊天机器人中的角色

RAG,即检索增强生成,是一种通过整合超出初始训练集的补充数据来丰富语言模型(LLM)知识库的技术。尽管LLM在跨不同主题进行推理方面具有能力,但它们仅限于在特定截止日期之前训练的公共数据。为了使AI应用程序能够有效处理私有或更近期的数据,RAG通过按需补充特定信息来增强模型的知识。这是一种替代微调LLM的方法,微调可能会很昂贵。

在问答聊天机器人领域,RAG在处理非结构化数据查询中发挥着关键作用,包括两个主要组成部分:索引和检索/生成。

索引从数据源摄取数据开始,然后将其分割成更小、更易于管理的块以进行高效处理。这些分割的块随后被存储和索引,通常使用嵌入模型和向量数据库,确保在运行时能够快速准确地检索。

0
0 321
文章 Michael Lei · 二月 18, 2024 11m read

1. IRIS RAG Demo

IRIS RAG Demo

这是 IRIS 与 RAG(检索增强生成)示例的一个简单演示。 后端是使用 IRIS 和 IoP用 Python 编写的,LLM 模型是 orca-mini 并由 ollama 服务器提供。 前端是用 Streamlit 编写的聊天机器人。

    1. IRIS RAG 演示](#1-iris-rag-demo)
    • 1.1. 什么是 RAG](#11-what-is-rag)
    • 1.2. 如何工作?
    • 1.3. 安装演示](#13-installation-the-demo)
    • 1.4. 使用方法
    • 1.5. 演示如何运行](#15-演示如何运行)
      • [1.5.1. 前端](#151-前端)
      • 1.5.2. 后台
        • [1.5.2.1. 业务服务](#1521-业务服务)
        • [1.5.2.2. 业务流程](#1522-业务流程)
        • [1.5.2.3. LLM 操作](#1523-the-llm-operation)
        • 1.5.2.4. 矢量操作](#1524-the-vector-operation)
    • 1.6. 一般性说明](#16-一般性说明)

1.1. 什么是 RAG?

RAG 是 Retrieval Augmented Generation(检索增强生成)的缩写,它带来了使用带有知识库的 LLM 模型(GPT-3.5/4、Mistral、Orca 等)的能力。

为什么它很重要? 因为它允许使用知识库来回答问题,并使用 LLM 来生成答案。

例如,如果你直接向 LLM 询问**"grongier.pex 模块是什么?"**,它将无法回答,因为它不知道这个模块是什么(也许你也不知道🤪)。

但是,如果你向 RAG 提出同样的问题,它就能回答,因为它会使用知识库,知道 grongier.pex 模块是什么,从而找到答案。

既然你已经知道什么是 RAG,那就让我们来看看它是如何工作的。

1.2. 它是如何工作的?

首先,我们需要了解 LLMS 的工作原理。LLMS 经过训练,可以根据前一个单词预测下一个单词。因此,如果你给它一个句子,它就会尝试预测下一个词,以此类推。很简单吧?

要与 LLM 交互,通常需要给它一个提示,它就会生成句子的其余部分。例如,如果你给它一个提示 "什么是 grongier.pex 模块?

很抱歉,我对您提到的 Pex 模块并不熟悉。能否请您提供有关它的更多信息或上下文?

好的,不出所料,它不知道什么是 grongier.pex 模块。但如果我们给它一个包含答案的提示呢?例如,如果我们提示``什么是 grongier.pex 模块?它是一个可以让你做 X、Y 和 Z 的模块。`",它就会生成剩下的句子,看起来就像这样:

grongier.pex 模块是一个可以让你执行 X、Y 和 Z 的模块。

好了,现在它知道什么是 grongier.pex 模块了。

但如果我们不知道 grongier.pex 模块是什么呢?我们怎样才能给它一个包含答案的提示呢? 这就需要知识库了。

RAG

RAG 的整个思路是使用知识库找到上下文,然后使用 LLM 生成答案。

为了找到上下文,RAG 将使用一个**知识库。

1.3.安装演示

只需克隆存储库并运行“docker-compose up”命令即可。

git clone https://github.com/grongierisc/iris-rag-demo
cd iris-rag-demo
docker-compose up

⚠️ 一切都是本地的,没有任何东西发送到云端,所以请耐心等待,可能需要几分钟才能开始。

1.4.用法

演示开始后,您可以在 http://localhost:8051 访问前端。

![Frontend](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/iris_chat.png?raw=true)

你可以提出有关「综合注册资讯系统」的问题,例如:

  • 什么是grongier.pex模块?

![Question](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/without_rag.png?raw=true)

正如你所看到的,答案不是很好,因为 LLM 不知道什么是 grongier.pex 模块。

现在,让我们尝试使用 RAG:

上传“grongier.pex”模块文档,它位于“docs”文件夹中,文件“grongier.pex.md”。

并问同样的问题:

  • 什么是grongier.pex模块?

![Question](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/with_rag.png?raw=true)

正如你所看到的,答案要好得多,因为 LLM 现在知道什么是 grongier.pex 模块。

您可以在日志中看到详细信息:

转到管理门户, http://localhost:53795/csp/irisapp/EnsPortal.ProductionConfig.zen?$NAMESPACE=IRISAPP&$NAMESPACE=IRISAPP&,然后单击“消息”选项卡。

首先,您将看到发送到 RAG 进程的消息:

![Message](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/trace_query.png?raw=true)

然后是知识库(向量数据库)中的搜索查询:

![Message](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/trace_result_vector.png?raw=true)

最后,发送给 LLM 的新提示:

![Message](https://github.com/grongierisc/iris-rag-demo/blob/master/misc/trace_new_query.png?raw=true)

1.5.这个Demo如何工作?

该演示由 3 个部分组成:

  • 前端,用 Streamlit 编写
  • 后端,用 Python 和 IRIS 编写
  • 知识库 Chroma 向量数据库
  • LLM,Orca-mini,由 Ollama 服务器提供服务

1.5.1.前端

前端是用 Streamlit 编写的,它是一个简单的聊天机器人,可让您提出问题。

这里没什么花哨的,只是一个简单的聊天机器人。

import os
import tempfile
import time
import streamlit as st
from streamlit_chat import message

from grongier.pex import Director

_service = Director.create_python_business_service("ChatService")

st.set_page_config(page_title="ChatIRIS")


def display_messages():
    st.subheader("Chat")
    for i, (msg, is_user) in enumerate(st.session_state["messages"]):
        message(msg, is_user=is_user, key=str(i))


def process_input():
    if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0:
        user_text = st.session_state["user_input"].strip()
        with st.spinner(f"Thinking about {user_text}"):
            rag_enabled = False
            if len(st.session_state["file_uploader"]) > 0:
                rag_enabled = True
            time.sleep(1) # help the spinner to show up
            agent_text = _service.ask(user_text, rag_enabled)

        st.session_state["messages"].append((user_text, True))
        st.session_state["messages"].append((agent_text, False))


def read_and_save_file():

    for file in st.session_state["file_uploader"]:
        with tempfile.NamedTemporaryFile(delete=False,suffix=f".{file.name.split('.')[-1]}") as tf:
            tf.write(file.getbuffer())
            file_path = tf.name

        with st.spinner(f"Ingesting {file.name}"):
            _service.ingest(file_path)
        os.remove(file_path)

    if len(st.session_state["file_uploader"]) > 0:
        st.session_state["messages"].append(
            ("File(s) successfully ingested", False)
        )

    if len(st.session_state["file_uploader"]) == 0:
        _service.clear()
        st.session_state["messages"].append(
            ("Clearing all data", False)
        )

def page():
    if len(st.session_state) == 0:
        st.session_state["messages"] = []
        _service.clear()

    st.header("ChatIRIS")

    st.subheader("Upload a document")
    st.file_uploader(
        "Upload document",
        type=["pdf", "md", "txt"],
        key="file_uploader",
        on_change=read_and_save_file,
        label_visibility="collapsed",
        accept_multiple_files=True,
    )

    display_messages()
    st.text_input("Message", key="user_input", on_change=process_input)


if __name__ == "__main__":
    page()

💡 我只是在用 :

_service = Director.create_python_business_service("ChatService")

来创建一个前后端之间的绑定.

ChatService 只是互操作性生产中的简单业务服务BS。

1.5.2.后端

后端是用 Python 和 IRIS 编写的。

它由3个部分组成:

  • 业务服务BS
    • 前端的入口点
  • 业务流程BP
    • 如果需要,在知识库中执行搜索
  • 拖曳业务运营BO
    • 一个用于知识库
      • 摄取文档
      • 搜索文档
      • 清除文件
    • 一个用于LLM大模型
      • 生成答案

1.5.2.1.业务服务BS

业务服务是一个简单的业务服务,它允许:

  • 上传文件
  • 提出问题
  • 清除向量数据库
from grongier.pex import BusinessService

from rag.msg import ChatRequest, ChatClearRequest, FileIngestionRequest

class ChatService(BusinessService):

    def on_init(self):
        if not hasattr(self, "target_chat"):
            self.target_chat = "ChatProcess"
        if not hasattr(self, "target_vector"):
            self.target_vector = "VectorOperation"

    def ingest(self, file_path: str):
        # build message
        msg = FileIngestionRequest(file_path=file_path)
        # send message
        self.send_request_sync(self.target_vector, msg)

    def ask(self, query: str, rag: bool = False):
        # build message
        msg = ChatRequest(query=query)
        # send message
        response = self.send_request_sync(self.target_chat, msg)
        # return response
        return response.response

    def clear(self):
        # build message
        msg = ChatClearRequest()
        # send message
        self.send_request_sync(self.target_vector, msg)

基本上,它只是操作和过程之间的传递。

1.5.2.2.业务流程

业务流程是一个简单的过程,允许在需要时搜索知识库

from grongier.pex import BusinessProcess

from rag.msg import ChatRequest, ChatResponse, VectorSearchRequest

class ChatProcess(BusinessProcess):
    """
    the aim of this process is to generate a prompt from a query
    if the vector similarity search returns a document, then we use the document's content as the prompt
    if the vector similarity search returns nothing, then we use the query as the prompt
    """
    def on_init(self):
        if not hasattr(self, "target_vector"):
            self.target_vector = "VectorOperation"
        if not hasattr(self, "target_chat"):
            self.target_chat = "ChatOperation"

        # prompt template
        self.prompt_template = "Given the context: \n {context} \n Answer the question: {question}"


    def ask(self, request: ChatRequest):
        query = request.query
        prompt = ""
        # build message
        msg = VectorSearchRequest(query=query)
        # send message
        response = self.send_request_sync(self.target_vector, msg)
        # if we have a response, then use the first document's content as the prompt
        if response.docs:
            # add each document's content to the context
            context = "\n".join([doc['page_content'] for doc in response.docs])
            # build the prompt
            prompt = self.prompt_template.format(context=context, question=query)
        else:
            # use the query as the prompt
            prompt = query
        # build message
        msg = ChatRequest(query=prompt)
        # send message
        response = self.send_request_sync(self.target_chat, msg)
        # return response
        return response

这真的很简单,它只是向知识库发送一条消息来搜索文档。

如果 知识库 返回文档,则会使用文档内容作为提示,否则会使用查询作为提示。

1.5.2.3.LLM 操作

LLM 操作是一个简单的操作,可以生成答案。


class ChatOperation(BusinessOperation):

    def __init__(self):
        self.model = None

    def on_init(self):
        self.model = Ollama(base_url="http://ollama:11434",model="orca-mini")

    def ask(self, request: ChatRequest):
        return ChatResponse(response=self.model(request.query))

这一步也很简单,它只是向 LLM 发送一条消息来生成答案。

1.5.2.4.Vector 操作

向量操作是一个简单的操作,允许摄取文档、搜索文档和清除向量数据库。


class VectorOperation(BusinessOperation):

    def __init__(self):
        self.text_splitter = None
        self.vector_store = None

    def on_init(self):
        self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100)
        self.vector_store = Chroma(persist_directory="vector",embedding_function=FastEmbedEmbeddings())

    def ingest(self, request: FileIngestionRequest):
        file_path = request.file_path
        file_type = self._get_file_type(file_path)
        if file_type == "pdf":
            self._ingest_pdf(file_path)
        elif file_type == "markdown":
            self._ingest_markdown(file_path)
        elif file_type == "text":
            self._ingest_text(file_path)
        else:
            raise Exception(f"Unknown file type: {file_type}")

    def clear(self, request: ChatClearRequest):
        self.on_tear_down()

    def similar(self, request: VectorSearchRequest):
        # do a similarity search
        docs = self.vector_store.similarity_search(request.query)
        # return the response
        return VectorSearchResponse(docs=docs)

    def on_tear_down(self):
        docs = self.vector_store.get()
        for id in docs['ids']:
            self.vector_store.delete(id)
        
    def _get_file_type(self, file_path: str):
        if file_path.lower().endswith(".pdf"):
            return "pdf"
        elif file_path.lower().endswith(".md"):
            return "markdown"
        elif file_path.lower().endswith(".txt"):
            return "text"
        else:
            return "unknown"

    def _store_chunks(self, chunks):
        ids = [str(uuid.uuid5(uuid.NAMESPACE_DNS, doc.page_content)) for doc in chunks]
        unique_ids = list(set(ids))
        self.vector_store.add_documents(chunks, ids = unique_ids)
        
    def _ingest_text(self, file_path: str):
        docs = TextLoader(file_path).load()
        chunks = self.text_splitter.split_documents(docs)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)
        
    def _ingest_pdf(self, file_path: str):
        docs = PyPDFLoader(file_path=file_path).load()
        chunks = self.text_splitter.split_documents(docs)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)

    def _ingest_markdown(self, file_path: str):
        # Document loader
        docs = TextLoader(file_path).load()

        # MD splits
        headers_to_split_on = [
            ("#", "Header 1"),
            ("##", "Header 2"),
        ]

        markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
        md_header_splits = markdown_splitter.split_text(docs[0].page_content)

        # Split
        chunks = self.text_splitter.split_documents(md_header_splits)
        chunks = filter_complex_metadata(chunks)

        self._store_chunks(chunks)

如果文档太大,那么向量数据库将无法存储它们,因此我们需要将它们拆分为块。

如果文档是 PDF,那么我们将使用“PyPDFLoader”来加载 PDF,否则我们将使用“TextLoader”来加载文档。

然后,我们将使用“RecursiveCharacterTextSplitter”将文档拆分为块。

最后,我们将块存储到向量数据库中。

如果文档是 Markdown,那么我们将使用“MarkdownHeaderTextSplitter”将文档拆分为块。我们还使用标题将文档拆分为块。

1.6.A. 总 论

所有这些都可以通过“langchains”来完成,但我想向你展示如何使用互操作性框架来做到这一点。并让每个人都更容易理解它是如何工作的。

1
0 587
文章 water huang · 十月 6, 2023 4m read

一般情况下,我们根据iris的portal向导创建数据库,然后创建命名空间。这个过程比较花时间,如果是已经存在的数据库,还需要再装载。翻阅portal调用的方法后,我整合了这几个方法。把这几个方法拷贝到任意已经存在的命名空间,通过执行CNNS(路径,命名空间),就可以快速创建好命名空间。方法的大概过程是,进入到%sys命名空间,然后依次创建数据库,创建命名空间,创建web应用。创建完成后,回到当前命名空间。

0
0 192
文章 TZ Zhuang · 六月 22, 2021 1m read

一个实例中可创建的最大命名空间数量为2048个。这个上限不可修改。

一个实例中可创建的最大数据库数量(包括远程数据库)为15998个。这个上限也不可修改。

一个实例中可创建数据库的总数量还有其他因素制约:

1. 数据库路径信息总量最大为256KB,也就是所有数据库的路径字符加起来不能多于256KB。设置的路径越长,可创建的数据库数量越少。
计算公式:最大数据库数量=258048/(平均数据库路径长度+3)

2. 镜像的数据库一个按两个算。也就是创建一个镜像的数据库,相当于创建了2个非镜像数据库。

更多细节请参考在线文档:
https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=G…

0
0 186