Initial Release
English version → https://github.com/zlnick/DataPipelineDemo/blob/master/README.en.md
TL;DR:
bash tools/setup.sh(一条命令起全栈)→python3 tools/e2e_ui_flow.py(一键复刻完整演示)。
前置三件套:Docker + Compose、InterSystems 容器仓库免费账号(用于拉 IRIS 镜像)、自备 LLM key(AI 功能必需);Windows 建议 WSL2 + Docker Desktop。
基于 InterSystems IRIS for Health 与 AI 的数据自动化转换演示平台。
源 = 2 种(FHIR 接口 / SQL 表)、目标 = 3 种(DB 表 / SOAP 服务 / FHIR 仓库),可任意组合(也支持同一 Production 内多管道并存)。
平台自动分析源/目标接口(FHIR Profile / SQL 列结构 / WSDL 操作语义)并登记数据资产;由 AI(OpenAI 兼容 LLM) 推荐「资产 → 目标」匹配与字段映射,用户确认后自动生成 IRIS 互操作性生产管道(Production) 完成数据投放;中文诊断/药品经术语服务器做代码转换(术语映射的事实源)。
界面中英双语:中文(默认,
/)与英文(/en),顶部栏可切换语言;站内跳转保持当前语言。
FHIR / SQL 源 → 接口分析(Profile / 列结构) → 登记数据资产
↘ ↙
AI 匹配「资产 → 目标」+ 字段映射 → 用户确认
↘ ↙
生成转换计划 → IRIS Production 管道 → DB 表 / SOAP 服务 / FHIR 仓库
平台的「决策与生成」全部由运行时大模型(LLM)完成,代码只负责读取事实、参数化、校验、保底补齐,绝无写死的映射/拓扑/结论模板:
| 能力 | AI(LLM)决策 | 代码只做 |
|---|---|---|
| 接口 / 数据源分析 | 资产业务语义、轮询键建议、目标写/读方向、运行契约解读 | 读取事实(Capability/列/WSDL)、写回 |
| 数据映射 | 资产→目标匹配与字段映射(支持 concat() 等表达式) |
结构归一、完整性校验 |
| 数据管道 | 组件构成与顺序、命名(含多管道逐组) | 注册表补 className/settings、必需件补齐 |
| 验证与修复 | 判定问题是否实质 + 选择修复动作 | 事实检查工具、机械剔除、显式降级 |
AI 驱动红线:LLM 失败 = 面向用户的明确失败(缺 key / 超时 / 输出不合规,均带 Agent 名报错),绝不静默改用规则结果;规则/注册表仅做 ①参数化 ②完整性校验 ③保底补齐(补齐在返回标注 ai_supplemented)。每次生成都带可审计的 ai 信息(driven / components / supplemented / c2_rule_rebuilt),LLM 调用留 token 日志。
^demo.ValidationIssue,并在后续修复中自动回注给 AI 作为参考(避免重复踩坑)。export_validation_issues.py)。生成不再”每次全量重算”,而是以数据管道为单位增量:
(源数据源, 目标) 恒为同一条管道 —— 无论提交几次、Agent 选了哪个设计 Skill,unchanged=true / render_skipped=true,界面提示”所有数据管道均已存在且未变更”)。dup_merged(可审计)。force=true)。「AI Agents」页按业界口径(Anthropic《Building effective agents》)把平台能力归类并逐条给出依据:
| 归类 | 含义 | 本项目条目 |
|---|---|---|
| Tool | 确定性、无 LLM 的可调用单元 | 连接探查 / 连通门禁 / 事实检查(pipeline_validator.check_*)/ 规则检查 / BP 静态准入 / 术语缺口盘点 / 目标列与主键事实 / WSDL 实体分析 / 术语检索 BO(demo.TerminologyOperation)/ 类型与 FHIR 模型注册表 |
| Workflow | LLM 参与,但执行路径由代码预定 | 接口分析(工具采集 + 单轮 LLM 归纳)、数据管道设计 Agent B(决策一次 → 平台代码路径渲染) |
| Skill | 打包的指令/知识,单步、无工具循环 | 数据转换生成(A)、知识润色、术语判定(C3 / C3-Dx:单轮判定 + 术语检索 Tool) |
| Agent | 工具 + 多轮自主循环 + 目标 | 转换验证-修复(C1)、管道验证-修复(C2) |
页面同时展示 Skill 目录(= AI 决策用的受控清单,GET /api/agents/skills):
管道设计 Skill ×6(sql2fhir-patient-tx / sql2db / fhir2db / sql2soap / fhir2soap / fhir2fhir,
含适用”源 → 目标”、状态与组件拓扑角色)与 术语判码 Skill ×2(cn2snomed / cn2rx,含源·目标体系与判定 Agent),
并显示使用次数(当前环境实际命中)。口径:平台只按目录参数化,选哪个 Skill 仍由 AI 决定。
平台将数据转换拆分为三个独立层次,而不是假设“源表 → 目标实体”一一对应:
转换计划可通过 /api/source-assets、/api/target-interfaces 和
/api/transformation-plans 管理,并可通过 /api/ai/verify 执行事实验证。
只有用户在管道监控页面点击“生成 / 重建数据管道”后,系统才会调用 AI 设计
Production 拓扑并交给 IRIS 编译启动。
backend/services/mock_soap.py,PatientEntity 表并返回回执)。connection_profiler)探测并产出归一 runtime 契约(connection / capabilities / poll / delivery / health)——docs/ConnectionContract-设计.md)。concat() 等表达式),用户确认保存。POST /api/pipelines/generate body pipelines: [组1, 组2]):
demo.TransformProcess 可复用,TransformProcess__sql2soap):本管道源 BS 的 TargetConfigNames 指向自己的 BP,^demo.Config("bp", <BP名>) —— 管道之间零耦合,可整条启停(许可随管道释放);JavaGateway(JDBC 网关,恒需)FHIRSyncService(_lastUpdated 游标)→ FHIRQueue → FHIRService(逐条独立会话)→ 转换 → 投放EnsLib.SQL.Service.GenericService(Query/KeyFieldName 增量)→ 行 JSON → 转换 → 投放Category = 管道类别落地,| 组件 | 技术栈 |
|---|---|
| 数据库 / 集成引擎 | InterSystems IRIS for Health(社区版,containers.intersystems.com/intersystems/irishealth-community:2026.1) |
| FHIR 数据源 | IRIS 自带 FHIR Server(核心 R4 hl7.fhir.r4.core@4.0.1,FHIRSERVER namespace) |
| 后端 API | Flask + IRIS Native SDK(intersystems-irispython)+ OpenAI SDK |
| 前端 | Vue 3 + Element Plus + Vite + nginx |
| AI | OpenAI 兼容接口(base_url / api_key / model 可配) |
| 编排部署 | Docker Compose(5 个服务:iris / backend / frontend / iris-terminology / embedding;默认启动 4 个 —— 跳过 embedding,详见「演示环境初始化」) |
IRIS 多角色(单实例):
http://localhost:52773/csp/healthshare/fhirserver/fhir/r4/DEMOFHIR +DEMOFHIRX0001R/V),端点 http://localhost:52773/csp/healthshare/demofhir/fhir/r4/;FHIRConfig.BASE_URL、模拟数据脚本都默认指向它)。iris/setup.sh 步骤 2b(容器启动即幂等创建);运行中的实例可重复执行 python3 tools/create_fhir_repo.py(带 15 项自检)演示数据表(SQLUser schema,结构与语义):
| 表 | 语义 | 数据来源 |
|---|---|---|
Patient / Observation |
FHIR→DB 目标落库(FHIR 资源转换写入) | FHIR 管道 / 亦可用于 SQL 源(向导自行选择即可) |
PatientSource |
SQL 源演示表(与 Patient 同结构,模拟“第三方业务库”) | 手工/脚本插入,供 SQL→SOAP 管道轮询 |
PatientEntity |
SOAP 投递结果(Python mock 收到 AddPatient 实体后落库) | mock 写入 |
FHIRQueue |
FHIR 增量抓取队列表(FHIRSyncService 入队,FHIRService 消费) | FHIRSyncService |
命名空间默认口径(2026-09-16):演示默认 SQL 源 =
USER命名空间(SQLUser.Patient/PatientSource)、
SQL 目标 =CLINIC命名空间(跨库写入演示);两边 DSN 都由登记 jdbc_url 的命名空间推导
(jdbc:IRIS://iris:1972/CLINIC→ DSNCLINIC)。要写平台内置目标表(Patient/Observation,在USER)
就把 DB 目标的 URL 改回jdbc:IRIS://iris:1972/USER。
数据管道(Production):转换 BP 类 demo.TransformProcess(Embedded Python 字段映射转换,支持 concat() 表达式与 表.列 前缀)——
每条数据管道各建一个 BP 实例(Ens 业务主机身份 = Item 名,如 TransformProcess__sql2soap;类可复用,管道互不干扰):
FHIRSyncService(_lastUpdated 增量游标)→ FHIRQueue → FHIRService(逐条独立会话)→ 本管道的转换 BPEnsLib.SQL.Service.GenericService(JDBC 轮询,Query + KeyFieldName)→ 行 JSON → 本管道的转换 BPSQLOp_<表>(JDBC UPSERT;DSN 按目标登记 jdbc_url 的命名空间推导 —— 演示默认 SQL 目标 = CLINIC 命名空间 → DSN CLINIC,无 jdbc_url 才回落 localTarget);SOAPOp_<服务>(WSDL 导入 BO + Adapter WebServiceURL 指向远端/mock)HTTPOperation(EnsLib.HTTP.GenericOperation,通用 REST + schema 驱动组装:按 ^demo.Config("fhir","schema",<type>) 的列元数据组装 Patient / Encounter / Condition / MedicationRequest / …,PUT + Basic Auth 写目标仓库)^demo.Config("bp", <BP名>)(mapping / target_type / service|table),TargetConfigNames(或 ^demo.Config("bp_target", 源BS名))投递给本管道的 BP,SQLOp_* / SOAPOp_*;旧路由表 ^demo.Config("pipe", 源BS名) 仅作历史兼容兜底前置条件:已安装 Docker 与 Docker Compose(Windows 建议 WSL2 + Docker Desktop;仓库统一 LF —— 见下方前置 7)。
新环境前置(克隆到其它机器时必看)
- IRIS 镜像来自 InterSystems 容器仓库(需免费账号):
irishealth-community不托管在 Docker Hub,
需先在 https://containers.intersystems.com 注册(免费)并登录:未登录时docker login containers.intersystems.com # 用户名/密码 = 注册邮箱/密码docker compose up拉镜像会失败(镜像分发受 InterSystems 许可约束,本仓库不转存)。
另请预留 ≈ 20 GB 磁盘与耐心:首次构建/初始化约 20–40 分钟(含 embedding 首次下载安装 torch
与本地向量模型;后续构建走缓存会快很多)。- 子模块依赖:术语服务器是独立项目。
termsrv以 git submodule 引入
(zlnick/iris-terminology-server,分支demo-community),
iris-terminology容器就是由它构建的:
- 克隆时带上子模块:
git clone --recurse-submodules https://github.com/zlnick/DataPipelineDemo.git- 已克隆但忘了:
git submodule update --init --recursive
⚠ 若报 Unable to find current revision in submodule path termsrv,说明子模块仓库
(zlnick/iris-terminology-server,分支 demo-community)要么尚未公开、要么该分支还没收到父仓库记录的提交。
可自查:git ls-remote <子模块地址> refs/heads/demo-community
(缺子模块时iris-terminology构建失败;平台主体仍能跑,但术语能力降级为
“保留源编码 +meta.tag=urn:cn-nhsa:term-map|unmapped”,term_map_build.py等工具不可用)- 构建方式:compose 里该服务为
build: context: ./termsrv(termsrv/iris/Dockerfile)→
docker compose up -d会自动构建;也可单独构建/重建:
docker compose build iris-terminology && docker compose up -d iris-terminology- 术语服务器的数据目录是
./data/iris-terminology,但映射(/mapping/*)实际存在容器内部 DB:
容器一旦重建,映射即丢失(重跑bash tools/term_map_import.sh,等价python3 tools/term_map_sync.py import;
setup.sh已含此步且幂等)。tools/term_map_seed.py是 seed 生成器,不是导入器。
不重建、只想更新平台扩展类时,用bash tools/termsrv_load.sh热加载(不丢数据);
术语素材(data/terms-inbox/:国标 ICD-10 约 2 万条 + NRDL/CBIH 中文药品目录)随仓库分发,
由tools/setup.sh调用bash tools/term_data_load.sh幂等灌入术语服务器概念表
(Terminology_Icd10.Concept/Terminology_Drug.Code)——CLINIC「生成演示数据」依赖它
(诊断/药品的中文名取自术语库),容器重建后重跑即可恢复;来源与条款见NOTICE。
CLINIC 演示源库(SQL 源)四表 Patient/Encounter/Diagnosis/MedicationOrder 由
bash tools/clinic_init.sh幂等建表(缺表才建、不动已有数据;setup.sh已含此步,
backend 启动时也会兜底检查)——缺表时 CLINIC 四表的 SQL 源 BS 会报错、「生成演示数据」也会失败。
另:成品映射种子(data/seeds/term_map_seed.json,81 条)由tools/setup.sh自动导入(tools/term_map_sync.py import);
术语服务器的平台扩展(/mapping/*路由 +CodeMap表)由termsrv-patches/覆盖进子模块(tools/termsrv_apply_patches.sh,幂等)——
因此 clone 后术语转换即可用,不依赖子模块远端是否已含这两个文件。
- JDBC 驱动 jar:一条命令搞定,无需去官网下载。backend 的”数据源连通测试 / 选 schema·表 / 分析列 /
DB 元数据发现”走 JayDeBeApi + JPype,需要intersystems-jdbc-*.jar(InterSystems 专有件,不入版本库);
但 IRIS 官方镜像自带该驱动,故提供一键提取脚本:(服务已全起来也可:提取后docker compose up -d iris # 先起 IRIS(驱动就在镜像里) bash tools/fetch_jdbc_jar.sh # 提取到 ./jdbc/(免下载、版本与 IRIS 一致) docker compose up -d # 再起其余服务docker compose restart backend;⚠ 该重启会重跑init_data.py重建目标表,
与环境已有演示数据时请勿随意执行。需要自定义驱动时,把 jar 直接放进./jdbc/即可。).env:cp .env.example .env并填LLM_BASE_URL / LLM_API_KEY / LLM_MODEL
(不填则 AI 功能显式报错、不静默降级;平台仍可启动)。data/embedding-model(本地向量模型)无需手工准备:embedding 容器首次启动会
自动从 ModelScope 下载(Qwen/Qwen3-Embedding-0.6B,需网络)。- 网络不稳可直接重跑
bash tools/setup.sh(幂等:子模块 /.env/ data 目录 / 构建 / 种子
都会跳过已完成项);子模块因网络中断拉取失败时,重跑即可恢复。- 术语向量化(可选,默认不做):
setup.sh不需要向量(术语转换只用成品映射;默认跳过embedding容器,
省首次 ~1.1 GB 模型下载与构建时间)。想试验”向量化 / 语义检索 / AI 补录映射”的读者 →
见独立章节 术语向量化(可选,独立测试)。- 跨平台换行符(统一 LF):仓库根有
.gitattributes(* text=auto eol=lf;
*.sh/*.bash/*.cls/Dockerfile与*.csv/*.tsv显式eol=lf;*.jar/*.zip/图片/字体/xlsx/pdf按二进制),
所有文件都以 LF 存储并检出。Windows(Git for Windows 默认core.autocrlf=true)若检出成 CRLF,会出现:
- 容器内脚本找不到:
sh: 1: /shared/setup.sh: not found(IRIS)、exec /app/entrypoint.sh: no such file or directory(embedding)- 宿主 bash 立即失败:
set: pipefail: invalid option name
处理:git config --global core.autocrlf false后重新 clone(或git checkout -- .让.gitattributes生效)。
bash tools/setup.sh && python3 tools/e2e_ui_flow.py
它会依次完成:登记 CLINIC SQL 源 → 连通测试 → 选表 → 生成演示数据 → 登记 FHIR 目标 → AI 智能匹配 → 保存映射 → 生成数据管道,并打印结果。
实测:result: OK、validation ok: True、FHIR 目标落地 Patient 3 / Encounter 4 / Condition 7 / MedicationRequest 7。
⚠ 需先填好
.env的LLM_*(AI 匹配与生成必需);映射由 AI 判定、造数含随机 ⇒ 链路与结构可复刻,具体字段/编码/数量会不同。
# 1. 配置 LLM(AI 推荐功能;不配则 AI 接口返回明确提示) bash tools/setup.sh # 一条命令:子模块 + .env + data 目录 + 构建启动 + JDBC 提取 + CLINIC 源库建表 + 术语概念导入 + 映射种子导入 + 健康检查 # 编辑 .env:填写 LLM_BASE_URL / LLM_API_KEY / LLM_MODEL(任意 OpenAI 兼容服务)2. 一键启动全部服务(首次会自动构建镜像、初始化 FHIR Server 与目标表)
docker compose up -d
3. 访问前端
http://localhost
停止服务:
docker compose down
# 清空数据(FHIR 数据 / 目标表 / 数据源·映射·管道登记)—— 按平台的正确姿势:
# · Windows / Docker Desktop(/dur = 命名卷 dataflow-iris-dur):
# 该卷由覆盖文件声明,**带上覆盖文件**时 `down -v` 就能一并删(不带则 -v 不会删它):
docker compose -f docker-compose.yml -f docker-compose.windows.yml down -v
# 等价写法(只想删数据、容器已停):
docker volume rm dataflow-iris-dur
# 之后:bash tools/setup.sh(首次启动自动重建实例数据)
# · macOS / Linux(/dur = 绑定挂载 ./data/iris):`-v` 对绑定挂载**无效**,要删/改名目录:
mv data/iris data/iris.bak-$(date +%Y%m%d) # 之后 docker compose up -d
bash tools/setup.sh 做了什么)目标:一条命令把「从零 clone」变成「可演示」。默认不做任何向量化(演示不需要,初始化更快)。
| 步骤 | 动作 | 结果 / 说明 |
|---|---|---|
| 1 | 拉 termsrv 子模块 + 应用 termsrv-patches/ overlay |
术语服务器构建源就位;overlay 幂等(applied=0 = 已是新版) |
| 2 | 准备 .env(缺则从 .env.example 复制) |
AI 功能需填 LLM_*;不填则 AI 接口显式报错,平台仍可启动 |
| 3 | 建 data/ 子目录 |
data/iris、data/iris-terminology、data/terms-inbox、data/embedding-model |
| 4 | 构建并启动容器 | 默认 4 个:iris、backend、frontend、iris-terminology(加 --with-embedding 才含 embedding) |
| 4b | Windows:IRIS 数据目录改用命名卷(docker-compose.windows.yml,setup.sh 自动启用) |
Windows/WSL 下把 /dur 从 ./data/iris 绑定挂载换成命名卷 dataflow-iris-dur(建卷 + 卷根属主改 irisowner)。理由:绑定挂载经 Docker Desktop 文件共享层时 chown/rename 不可靠,会让 IRIS 首次「搬迁数据目录」EPERM → 容器 Exited(1);命名卷是 VM 内真实 ext4,从根上消除该类问题 |
| 4c | 等待核心服务就绪(有界) | 先等 IRIS/backend 就绪(--wait 300/180)再做后续建表 / 灌库 —— 全新实例首次 FHIR 初始化常 >180s,先等可避免 clinic_init / term_data_load 误报 Access Denied |
| 5 | 提取 JDBC 驱动 | 从 IRIS 镜像一条命令取 intersystems-jdbc-*.jar → ./jdbc/(无需官网下载) |
| 6 | CLINIC 源库建表 | Patient/Encounter/Diagnosis/MedicationOrder——幂等(缺表才建、不动数据;其前的 4c 已等 IRIS 就绪) |
| 7 | 术语概念灌库 | ICD-10 20,484 条 + 药品 NRDL 3,919 / CBIH 19 条 → Terminology_Icd10.Concept / Terminology_Drug.Code |
| 8 | 术语映射种子 | data/seeds/term_map_seed.json 81 条 → 术语服务器映射表(术语转换的事实源) |
| 9 | 健康检查 | 打印 backend / frontend / terminology 三项(尽力探测,不强制失败) |
| 10 | 私有 Web 服务器自愈(iris/setup.sh 第 7 步) |
把 httpd 的 PidFile 指到容器内 /tmp/httpd.pid(绕开 Windows 绑定挂载上 rename 被拒的坑),必要时拉起 httpd 并核验门户 52773 |
初始化后的状态(全新环境):4 个容器 Up;http://localhost 是空白演示态(无数据源/目标/映射/管道);
术语服务器已有概念 + 映射(供术语转换与造数用);/api/pipelines/status 为 running:false(尚未生成管道)。
常用参数
| 命令 | 作用 |
|---|---|
bash tools/setup.sh |
全流程(默认跳过 embedding) |
bash tools/setup.sh --with-embedding |
一并构建/启动 embedding(做术语向量化试验时才需要) |
bash tools/setup.sh --check |
只读体检:子模块 / .env / JDBC / docker |
bash tools/setup.sh --help |
用法 |
⚠ 容器重建后需要重灌:术语概念与映射都存在于 iris-terminology 容器内部 DB,容器重建即丢 →
重跑 bash tools/term_data_load.sh(概念)+ bash tools/term_map_import.sh(映射),或直接重跑 bash tools/setup.sh(幂等,两步都含)。
CLINIC 四表由 backend 启动时自动兜底检查(缺表补建,不删数据)。
iris-terminology)——它做什么、平台怎么用它定位:独立容器(fork 自开源项目 intersystems-ib/iris-terminology-server——MIT 许可、作者 Luis Angel Pérez Ramos 的裁剪版
——本仓库使用的 fork 为 zlnick/iris-terminology-server;
服务器本身的核心能力(多术语导入 / 发布管理、持久化存储 + SQL / iFind 检索、原生与 FHIR R4 术语接口、生产化处理)是上游作者的工作,功劳全归他,谨此致谢;
宿主端口 52774→52773、51774→1972,凭据 superuser / SYS),做两件事:
① 术语存储 + 检索/校验(CodeSystem / ValueSet);② 术语转换映射的事实源(/mapping/*)。
转换/映射的判定由平台 AI Skill(C3 药品 / C3-Dx 诊断)负责,不在术语服务器做。
网页入口:http://localhost:52774/terminology/ →「术语服务器 · 术语集」清单页(概念数 / 向量数 / 端点);
管理门户:http://localhost:52774/csp/sys/UtilHome.csp。
内置术语集(GET /terminology/systems)
| id | 内容 | 平台用途 |
|---|---|---|
chinese-icd10 |
国标 ICD-10(GB/T 14396-2016,中文) | CLINIC 造数取中文诊断名 |
chinese-drugs |
中文药品目录(医保 NRDL + 商保 CBIH) | CLINIC 造数取中文药品名 |
rxnorm |
RxNorm(IN/SCD/BN) | 术语转换的目标体系(药品) |
snomed-uscore |
SNOMED CT(US Core Condition 样本) | 术语转换的目标体系(诊断) |
REST 能力(节选)
| 类别 | 端点 |
|---|---|
| 术语检索 / 校验 | /terminology/icd10/{search,lookup,validate-code}、/terminology/icd/{…}、/terminology/drug/{search,lookup,validate-code,codesystems}、/terminology/rxnorm/{search,lookup,validate-code,chinese-map}、/terminology/snomed/*、/terminology/loinc/*、/terminology/uscore-condition/* |
| 转换映射(平台运行期使用) | GET /terminology/mapping/lookup?sourceSystem=&targetSystem=&sourceCode=、GET /terminology/mapping/systems(按体系对统计)、POST /terminology/mapping/entries(批量幂等 upsert) |
| 向量 | GET /terminology/vector/search?q=&systemUri=&limit=、/terminology/vector/crosswalk(见下一章) |
| FHIR 术语面 | /terminology/fhir/r4(CodeSystem/$lookup、$validate-code、$subsumes、ValueSet/$expand) |
平台在哪些环节用它
demo.TerminologyOperation 实时查 /mapping/lookup —— 没有本地缓存要预热;meta.tag=…|unmapped,不静默、不阻断);补录见下。TermLookupRequest/TermLookupResponse 已在 iris/setup.sh 的编译清单中<CLASS DOES NOT EXIST> … demo.TerminologyOperation。)数据面(哪些自动、哪些要自己做)
| 表 | 内容 | 由谁灌入 |
|---|---|---|
Terminology_Icd10.Concept / Terminology_Drug.Code |
术语概念 | setup.sh 自动(tools/term_data_load.sh) |
Terminology_Mapping.CodeMap |
转换映射(事实源) | setup.sh 自动(tools/term_map_import.sh,81 条种子) |
Terminology_Vector.TermEmbedding |
术语向量 | 需自行生成(见下一章;演示不需要) |
常用运维命令
| 命令 | 作用 |
|---|---|
bash tools/term_data_load.sh |
幂等灌术语概念(素材随仓库分发) |
bash tools/term_map_import.sh |
幂等灌映射种子(81 条) |
bash tools/termsrv_vector_init.sh [--check] |
查看向量能力状态(缺表自动补建)+ 打印向量化步骤 |
bash tools/termsrv_apply_patches.sh |
把平台扩展(/mapping/* + CodeMap)覆盖进子模块(幂等) |
bash tools/termsrv_load.sh |
不重建容器,热加载平台扩展类进运行中的容器 |
python3 tools/term_map_build.py [--dry] [--limit N] |
AI 补录缺失映射(向量召回 → LLM 判码 → 写回服务器) |
它是干什么用的:向量化服务于术语映射的生产,不是演示运行期。
中文诊断/药品名与英文 SNOMED/RxNorm 没有直接码表,AI 判码(C3 / C3-Dx)必须先用语义相似召回 Top-5~6 候选,
再交 LLM 判定 → 写回术语服务器。本仓库已把成品映射(81 条)随仓库分发,因此演示开箱即用,无需跑向量化。
前置条件(三项已就绪)
| 条件 | 说明 |
|---|---|
| 术语服务器向量表 | Terminology_Vector.TermEmbedding —— 开箱存在(列 ID/Code/Embedding/Lang/Model/ReleaseId/SystemUri/Text),默认 0 行;若缺表,termsrv_vector_init.sh 会编译类自动补建 |
| 本地 embedding 服务 | docker compose up -d embedding(Qwen3-Embedding-0.6B,首次自动下载 ~1.1 GB);术语服务器经 ^Config("Vector","EmbeddingHost")(默认 embedding:8000)访问它 |
| 概念数据(向量的输入) | 中文 ICD-10 / 药品概念由 setup.sh 自动灌入 ✓ |
独立测试步骤(本项目实测通过)
# ① 起本地向量服务(首次下载模型,需几分钟;可 `docker logs -f dataflow-embedding` 观察) docker compose up -d embedding② 检查向量能力:表存在性 / 行数 / 按体系分组(缺表会自动编译类补建)
bash tools/termsrv_vector_init.sh
③ 生成向量(任选其一)
python3 tools/dx_vectorize.py --zh # 中文 ICD-10 全量(~2 万条;约 10~20 条/秒) python3 tools/term_embed.py --system --tsv <file.tsv> # 任意术语集(TSV) bash run_rxnorm_vec.sh # RxNorm 全量(2.6 万条;需自备 RxNorm 原始数据,含 OOM/过热自愈 + 断点续传)
④ 语义检索验证(应返回带 score 的候选)
curl -u superuser:SYS 'http://localhost:52774/terminology/vector/search?q=阿司匹林&limit=3'
⑤ 完整链路:向量召回 → LLM 判码 → 写回映射(需配好 .env 的 LLM key)
python3 tools/term_map_build.py --dry # 先看缺哪些映射(不调 LLM、不写入) python3 tools/term_map_build.py --limit 5 # 补录 5 条
实测参考(2026-09-27):embedding 维度 1024;写入 2 条后
vector/search?q=阿司匹林 → 命中 阿司匹林 score 1.0024、复方硼砂 score 0.5708 ✓
注意事项
run_rxnorm_vec.sh 已内置续传与自愈)。docker compose stop embedding —— 平台与演示完全不受影响(没有任何服务依赖它)。data/seeds/term_map_seed.json(setup.sh 自动导入)。| 服务 | 地址 | 说明 |
|---|---|---|
| 前端应用 | http://localhost | http://localhost/en | Vue 3 + Element Plus,中英双语(/ 中文默认、/en 英文),顶部栏可切换 |
| 后端 API | http://localhost:5001 | REST API(nginx 代理 http://localhost/api/*) |
| IRIS 管理门户 | http://localhost:52773/csp/sys/UtilHome.csp | 账号 superuser,密码 SYS |
| FHIR endpoint(实例自带 = 默认目标仓库) | http://localhost:52773/csp/healthshare/fhirserver/fhir/r4/ | /metadata 匿名;资源读写需 Basic Auth;转换结果默认落这里 |
| FHIR endpoint(第二个仓库 = 默认源仓库) | http://localhost:52773/csp/healthshare/demofhir/fhir/r4/ | 独立命名空间 DEMOFHIR;数据源表单/FHIRConfig.BASE_URL 默认指向它;与上一行数据互不可见;自检 python3 tools/create_fhir_repo.py --check |
| IRIS 超级服务器 | localhost:1972 | Native SDK / DB-API 连接端口 |
| 术语服务器:术语集清单页(内置网页) | http://localhost:52774/terminology/ | 标题「术语服务器 · 术语集」;列出 4 个术语集(中文药品 / RxNorm / 国标 ICD-10 / SNOMED US Core 样本)与各自检索端点;JSON 版 http://localhost:52774/terminology/systems;账号 superuser / 密码 SYS |
| 术语服务器:原生 REST(浏览器可直接看) | http://localhost:52774/terminology/… | 例 /terminology/icd10/search?q=糖尿病、/terminology/drug/search?q=阿司匹林、/terminology/uscore-condition/zh-map?q=糖尿病、/terminology/vector/search?q=diabetes;全量路由见 termsrv/iris/src/Terminology/Production/API.cls |
| 术语服务器:管理门户 / Production 配置 | http://localhost:52774/csp/sys/UtilHome.csp | http://localhost:52774/csp/user/EnsPortal.ProductionConfig.zen?$NAMESPACE=TERMINOLOGY | 生产 = Terminology.Production;⚠ Ensemble 门户挂在 /csp/user/(/csp/sys/ 版 404) |
| 术语服务器:上游 React 演示 UI(Terminology Explorer) | http://localhost:5173(**本环境未部署**) | 需宿主装 Node 后 cd termsrv/ui && npm install && VITE_API_BASE_URL=http://localhost:52774 npm run dev;上游另需 webgateway 容器(8080) |
前端与管理门户的分工:Demo 前端负责业务配置(数据源/资产/AI 映射/生成管道)与业务监控(消息日志、目标表落库);
生成的 Production 是标准Ens.Production,其技术管理(组件配置、启停、消息详情、错误排查)请登录
IRIS 管理门户 → 互操作性 → 配置 Production(账号superuser/ 密码SYS)。
统一响应格式:{"code": 0, "data": ..., "message": "success"}(code != 0 表示出错)。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/health |
健康检查(IRIS 连通性) |
| POST | /api/datasources |
注册数据源(名称/类型/端点/认证) |
| GET | /api/datasources |
数据源列表 |
| POST | /api/datasources/<id>/analyze |
自动分析 Profile(CapabilityStatement → 资源类型/资产) |
| GET | /api/datasources/<id>/assets |
指定数据源的资产列表 |
| GET | /api/targets |
目标表列表(表结构/行数) |
| GET | /api/targets/<table>/data |
目标表数据 |
| POST | /api/ai/recommend |
AI 推荐(资产→目标表 + 字段映射) |
| POST | /api/mappings |
保存转换关系 |
| GET | /api/mappings |
转换关系列表 |
| POST | /api/pipelines/generate |
生成并启动数据管道(动态生成 Production);许可调度:超许可上限的分组照旧生成但初始停用(响应 license_budget.scheduled/suspended) |
| POST | /api/pipelines/run |
触发一次转换 |
| GET | /api/pipelines/status |
管道运行状态 |
| GET | /api/pipelines/items |
Production 组件清单(含类别分组、许可单元占用) |
| POST | /api/pipelines/items/toggle |
在线启停单个组件(切换管道占用许可) |
| GET | /api/pipelines/instances |
数据管道实体列表(状态 active/suspended/superseded、生成次数、按类别分组、许可占用) |
| POST | /api/pipelines/instances/<id>/enable | /disable |
整条管道启用/停用;一键切换:许可不足时自动停用其它活动管道腾单元(disabled_others),腾不出来才显式失败 |
| DELETE | /api/pipelines/instances/<id> |
删除管道实体(并让其组件让路) |
| POST | /api/pipelines/instances/sync |
按 Production 事实同步/校正管道实体状态 |
| GET | /api/pipelines/logs |
消息流转日志(Ens.MessageHeader) |
| GET | /api/pipelines/mappings |
Production 正在执行的转换关系 |
| GET | /api/pipelines/target-data |
目标表落库结果 |
| GET | /api/agents |
已封装 AI 能力(Skills / Agents) |
| GET | /api/agents/skills |
Skill 目录(管道设计 Skill + 术语判码 Skill,含适用源→目标 / 状态 / 使用次数) |
AI 推荐调用 OpenAI 兼容接口,可填写任意兼容服务:
# .env
LLM_BASE_URL=https://api.deepseek.com/v1 # 例如 DeepSeek;默认 https://api.openai.com/v1
LLM_API_KEY=sk-xxxxxxxx # 服务商密钥(必填)
LLM_MODEL=deepseek-chat # 模型名
未配置
LLM_API_KEY时,AI 推荐接口返回明确错误提示,其余功能(数据源分析 / 管道生成 / 监控)不受影响。
.
├── docker-compose.yml # 一键编排(IRIS + 后端 + 前端)
├── .env.example # 环境变量模板(IRIS/FHIR/LLM)
├── init_data.py # 建目标表(模拟远端数据库)
├── tools/seed_fhir_demo.py # (可选)手动灌 FHIR 演示样本;不在 backend 启动链
├── tools/check_component_fidelity.py # 组件保真审计(存储定义 ⊆ Production,防重建丢件)
├── LICENSE / NOTICE # 许可与商标归属声明(termsrv 子模块等不随仓库分发的组件)
├── docs/*.md # (本地)设计与计划文档,已 gitignore,不随仓库分发
├── backend/ # Flask 后端
│ ├── app.py # 应用入口(注册全部路由蓝图)
│ ├── config.py # IRIS/FHIR/LLM 配置
│ ├── schemas/ # Pydantic 模型
│ ├── services/ # repository(global/归一runtime) / connection_profiler(探查Agent) / mock_soap / fhir_client / profile_analyzer / llm_client / iris_connector / wsdl_importer / pipeline_validator / validate_agent / transformation_validator / type_registry
│ └── routes/ # datasources / targets / ai / mappings / pipelines
├── frontend/ # Vue 3 + Element Plus
│ └── src/
│ ├── api/ # axios 封装 + dataflow.js(API)+ constants.js(类型枚举预留)
│ ├── views/ # Home/Datasources/Assets/Recommend/Mappings/Pipelines/Targets/Agents
│ ├── components/ # TypeSelect(类型选择器,预留禁用)
│ ├── i18n/ # zh.js + en.js(中英双语文案)+ path.js(按语言前缀跳转)
│ └── router/
├── iris/ # IRIS 侧代码
│ ├── setup.sh # 容器启动统一初始化(凭据/FHIR Server/编译)
│ ├── init-password.sh # superuser/SYS 凭据
│ ├── src/demo/ # Production 组件类 + PipelineGenerator + PipelineQuery
│ └── python/ # transform_handler.py(Embedded Python 转换逻辑)
├── tools/ # datakit 工具箱(`bash tools/datakit/run.sh list`)+ 校验/诊断脚本
├── data/ # IRIS 数据持久化(setup 时创建)
└── knowledge -> 知识库软链接(可选,本地)
系统启动后处于空白演示态:无预置数据源/目标/资产/映射;页面(资产/目标/可查看表)只出现你已登记的内容。
重置环境:bash tools/datakit/run.sh reset_ui_env.py
(一键回到零起点,脚本自带 26 项自检:末尾 ✅/❌ 清单 + 退出码 0=干净;只查不改加--check-only。
旧脚本python cleanup_demo.py已过时:它不清 Ens 内部残留/生成类/动态发现的新表。)
术语映射不用管:事实源在术语服务器(独立容器 + 独立数据目录,重置不影响),运行期由管道经
共享 BO(demo.TerminologyOperation)实时查询——没有本地缓存要预热,重置后术语映射天然就位;
若某个源编码服务器尚无映射,平台默认降级(保留源编码 +meta.tag=unmapped,不静默、不阻断),
补录:bash tools/datakit/run.sh term_map_build.py(判定 Agent 产出候选并写回服务器,补录后无需重新生成)。
CLINIC 演示数据(界面「生成演示数据」按钮 /tools/seed_clinic.py):需先登记一个 SQL 数据源
(演示默认 =CLINIC命名空间,见 §B 第 1 步),否则脚本会给出明确指引(而不是抛 IndexError)。
想一键跑通?
bash tools/setup.sh && python3 tools/e2e_ui_flow.py(自动完成 ①~⑤ 与 AI 匹配;见「快速启动 · 一条命令复刻完整演示」)
最短端到端路径(约 10 分钟):① 登记 SQL 数据源(演示默认 CLINIC 命名空间)→ ② 点「生成演示数据」→
③ 登记 DB 目标(jdbc:IRIS://iris:1972/USER,写 Patient/Observation)→ ④「AI 智能匹配」确认字段映射 →
⑤「管道监控」生成管道 → ⑥ 目标数据下拉查看落库行 / 消息 Completed。
http://iris:52773/csp/healthshare/demofhir/fhir/r4/(DemoFHIR = 默认 FHIR 源仓库)、认证 superuser/SYS → 注册 → Profile 分析(自动产出运行契约:版本/增量能力/健康)。jdbc:IRIS://iris:1972/CLINIC = SQL 目标默认库;本教程要写平台内置目标表 Patient/Observation(在 USER 命名空间),故把 URL 改为 jdbc:IRIS://iris:1972/USER)、认证 superuser/SYS → 添加 → 联通测试 → 选 schema SQLUser → 勾选目标表(Patient/Observation)→ 分析列结构 → 保存。Patient 资产 → AI 推荐字段映射 → 确认保存。Patient 表落库(Pipelines 目标数据下拉动态可选 Patient 查看)。PatientSource(与 Patient 同结构的“业务库”演示表;选 Patient 也可以——造数脚本两张源表都支持)→ 分析列 → 自动生成轮询 Query。/tmp/patient.wsdl(内置写入型 AddPatient)→ 导入生成 BO + 实体分析(运行契约自动判定 AddPatient → 写入型、endpoint 可达)。PatientSource(SQL 资产,列即字段)→ AI 推荐 → 确认(mapping 自动带 target_type=SOAP)。PatientSource → 转换 → SOAPOp_PatientService 调用 mock(WebServiceURL)→ mock 收到实体 → 落库 PatientEntity 表并回执。PatientSource 插几行患者 → SQLService 轮询投递 → Pipelines 消息 Completed + PatientEntity 可见(下拉动态含 PatientEntity/PatientSource)。
tools/datakit/README.md 「六.1 SQL → SOAP」):
# 源表 PatientSource(本节口径)
bash tools/datakit/run.sh gen_test_patient.py --source user --table PatientSource --count 3
# 源表 Patient(同一场景,换一张源表;也是脚本默认值)
bash tools/datakit/run.sh gen_test_patient.py --source user --count 3
# 复核:SOAP 落库 PatientEntity + 最近业务消息(不看脚本输出)
bash tools/datakit/run.sh check_pair_sink.py
diag_msgs.py(消息头/扫描凭证)→ diag_errors.py(Ens 事件日志)→ rescan_sql_source.py(强制重扫 SQL 源);--force(停 → 清扫描凭证 → 启,走全量重扫)。pipelines: [组1, 组2];TransformProcess 按来源(SQLService/FHIRService)路由到各自目标,消息互不干扰。PatientSource 表避免)。/api/pipelines/items 组件清单与实例”生成次数”不变(不重跑 AI、不重启生产)。dup_merged 中回报(不产生第二套实例)。force=true),该组会重新设计组件(AI 重新决策)。python3 tools/check_component_fidelity.py这是最能体现「AI 决策 + 术语转换」的一条链路:HIS 风格业务库的患者主表 + 就诊 / 诊断 / 药嘱
经 AI 映射为 Patient / Encounter / Condition / MedicationRequest,中文诊断由术语服务器转成 SNOMED 双编码。
jdbc:IRIS://iris:1972/CLINIC → 勾选Patient/Encounter/Diagnosis/MedicationOrder(4 张一起)→ 分析列结构 → 自动生成轮询 Query。bash tools/datakit/run.sh seed_clinic.py):默认写入 3 位患者 + 就诊 / 诊断 / 药嘱…/csp/healthshare/fhirserver/fhir/r4/FHIRSERVER = 默认目标仓库)→ 注册 → 刷新候选资源。Patient→Patient、Encounter→Encounter、Diagnosis→Condition、MedicationOrder→MedicationRequest;诊断列由 AI 判为 term_map:cn2snomed)→ 保存映射。FHIRQueue 落库、FHIR 仓库计数实测 Patient 3 / Encounter 4 / Condition 7 / MedicationRequest 7;Condition.code 为双 coding(源 urn:cn-nhsa:icd10-gbt2016 + SNOMED http://snomed.info/sct,Condition.subject / .encounter 用 urn:uuid: 引用对应 Patient / Encounter。
bash tools/datakit/run.sh gen_test_patient.py --count 2 --family 赵 --given 敏 --diagnosis 糖尿病 --drug 阿司匹林bash tools/datakit/run.sh check_fhir.py(或 Pipelines 页「目标数据」下拉)背景:本项目曾发生一次 AI 越界删除其它项目容器的事故(其它 3 个项目的 7 个容器及其网络被删,
其中一个 IRIS 库不可恢复)。此后为”AI / 脚本的执行通道”立了边界规则。
dataflow-* / iris-terminology)、知识库;https://github.com/zlnick/DataPipelineDemo/blob/master/tools/guard/README.md(会话级规则文件本身为本地文件,不随仓库分发)。source tools/guard/docker_guard.sh —— 本项目之外的破坏性操作被拒(rc=77);python3 tools/guard/scope_guard.py check <路径>...。./tools/guard/ai-session.sh 起的受限会话(macOS sandbox-exec)写入只允许/tmp / ~/Library/Caches,其余内核拒绝。细节、实测数据与已知坑见 https://github.com/zlnick/DataPipelineDemo/blob/master/tools/guard/README.md。
⚠ 2026-09-14:此前还上过一层「受限 Docker API 代理」(
DOCKER_HOST→ 中间代理,按 daemon 事实裁决
破坏性请求)——已回滚:过度复杂,且docker cp的流式上传体被判不了归属而 fail-closed 误拒,
反而打断日常操作。现在不再有代理层,docker 走本机真实 socket。
| # | 症状 | 处理 |
|---|---|---|
| 1 | 拉不到 IRIS 镜像 / 提示未认证 | 镜像来自 InterSystems 私有仓库:免费注册后 docker login containers.intersystems.com(见「新环境前置 0」) |
| 2 | 容器内 sh: 1: /shared/setup.sh: not found;宿主 set: pipefail: invalid option name |
Windows 把脚本检出成了 CRLF:git config --global core.autocrlf false 后重新 clone(见「新环境前置 7」) |
| 3 | 首次启动很慢 / 停在「等待核心服务就绪」 | 正常:全新 IRIS 要建 FHIR 双仓库与目标表(约 1–5 分钟);setup.sh 已做有界等待,之后才建表/灌库 |
| 4 | 重建容器后术语转换失效,或 CLINIC 造数取不到中文诊断/药品 | 术语概念与映射在术语服务器容器内部 DB,重建即丢 → 重跑 bash tools/term_data_load.sh + bash tools/term_map_import.sh(或直接 bash tools/setup.sh,幂等) |
| 5 | 生成管道 500:<CLASS DOES NOT EXIST> … demo.TerminologyOperation |
iris/setup.sh 编译清单必须含 demo.TerminologyOperation/TermLookupRequest/TermLookupResponse(已修) |
| 6 | 生成管道 500:ERROR #5007: Directory name '/dur/generated/' is invalid |
生成物目录必须是 /dur/generated(不是 $ISC_DATA_DIRECTORY/generated);iris/setup.sh 第 7b 步已自动创建 + chown irisowner(已修) |
| 7 | 只有 4 个容器、没有 dataflow-embedding |
演示不需要向量 ⇒ setup.sh 默认跳过;要试验术语向量化时 docker compose up -d embedding(见「术语向量化」) |
| 8 | 想回到零起点 / 彻底清数据 | 重置登记:bash tools/datakit/run.sh reset_ui_env.py(26 项自检,--check-only 只查不改);清库:Windows 命名卷 docker volume rm dataflow-iris-dur,macOS/Linux 删/改名 data/iris |
| 9 | 能同时跑几条管道? | 社区版许可 8 个业务主机单元(常驻 ≤7 个组件 + 1 个后端连接);超出的管道照旧生成但初始停用,在「数据管道」卡片一键切换(自动让路其它管道) |
| 10 | AI 接口报错 / 没有推荐结果 | .env 的 LLM_* 未填或不可达 ⇒ 平台显式报错(不静默降级);用 docker exec dataflow-backend python /tmp/test_llm.py 自检(见「LLM 配置」) |
superuser / SYS;FHIR 资源读写需 Basic Auth(仅 /metadata 匿名公开)。DEMOFHIR(第二个独立仓库,UI「数据源管理」表单与FHIRConfig.BASE_URL 的默认值)、目标 = FHIRSERVER(实例自带,UI「转换目标」表单与FHIRConfig.TARGET_BASE_URL 的默认值);两个仓库数据互不可见,重置脚本会同时清空两者。backend/services/mock_soap.py,Config.MOCK_SOAP_URL);WebServiceURL 覆盖 WSDL 地址)。demo.TerminologyOperation 实时查询术语服务器):active → 追加目标体系 coding(双 coding,如 E11.900 + SNOMED 44054006);negative(已判定无匹配,如”依折麦布/阿托伐他汀”复方制剂)→ 只保留源编码、不追加目标编码、也不打 unmapped 标记;missing / 调用失败 → 保留源编码并在资源打 meta.tag=urn:cn-nhsa:term-map|unmapped(不阻断,可补录;suspended;disabled_others 列出被让路组件)。python3 tools/check_component_fidelity.py(重建/复用后逐项确认没有丢组件);bash tools/datakit/run.sh list(含 test_incremental_pipeline.py 等离线回归与造数脚本)。init_data.py 每次后端启动会重建目标表(DROP+CREATE,适合演示;生产环境不应自动清表)。tools/gen_test_patient.py、generate_mock_data.py --fhir N);bash tools/datakit/run.sh seed_fhir_demo.py。note.fields.provenance 与 UI「运行契约」列可见;有了真实数据后下次分析会自动改用真实数据形态。bash tools/datakit/run.sh seed_fhir_demo.py。irisowner 运行,首次启动要把实例数据”搬迁”进 $ISC_DATA_DIRECTORY;而chown/rename 等 POSIX 元数据操作不可靠(实测三种调用方式root / ubuntu(1000) / 正确属主,chown 全部 EPERM)→Error while moving data directories ERROR #5001: Error executing chown irisowner:irisowner /dur/irissys/: Error:1:Exited (1);open(tmp) → write → rename,同一限制会返回 EPERMhttpd/logs/error.log: AH10231 Failed creating pid file)→ httpd 静默退出、门户与 FHIR 端点全部无响应curl 空响应 / exit 52),而容器健康检查(进程级)仍报 healthy —— 两个坑都是静默失效,只有真去连 HTTP 才暴露。docker-compose.windows.yml:把 /dur 从 ./data/iris 绑定挂载换成命名卷 dataflow-iris-durchown/rename 全部正常)→ 一次消除 ① 与 ②;tools/setup.sh 在 Windows/WSL 下COMPOSE_FILE)+ 建卷 + 把卷根属主改为 irisowner;iris/setup.sh 第 7 步:把私有 Web 服务器的 PidFile 指到容器内 /tmp/httpd.pid,必要时自动拉起 httpd 并核验门户docker compose -f docker-compose.yml -f docker-compose.windows.yml ps;docker volume rm dataflow-iris-dur。curl 会短暂返回 000,稍等再试/en)是界面壳:与中文界面共用同一套组件与 API,文案来自 zh.js / en.js;由后端 / AIApache-2.0 —— 见 LICENSE;NOTICE 列明商标归属与不随仓库分发的组件(JDBC 驱动、原始术语数据、termsrv 子模块)。