医保规则采集与推送
这套流程在解决什么
政策原文是网页,而平台的采集源只认结构化的规则包地址。中间缺的那一段是
"把网页读成规则包",这件事需要理解上下文,是模型擅长的。所以链路拆成两半:
WorkBuddy(读页面、解析、推送) → 平台(校验、比对、落草稿、留痕) → 人工会签
自动的部分 自动的部分 唯一的实质复核
划这条线的理由:解析可以自动,判定不能自动。 抓到的规则一律只落草稿,
生效必须有人签字。任何时候都不要试图绕过会签直接激活一份规则包。
何时用
- 定时任务到点,要去政策来源看一眼有没有新东西。
- 有人反馈"某条规则不对",需要核对平台现行版本与政策原文是否一致。
- 手工拿到一份省级实施细则,要导进平台。
- 只想看看平台现在有多少草稿等着会签。
一、动手前先确认三件事
| 要确认的 | 怎么确认 | 不对怎么办 |
|---|---|---|
| 平台活着 | GET {base}/health 返回 200 |
先让服务起来。本轮直接跳过,不要重试到天亮 |
| 令牌在手 | 调度任务的提示词里写了令牌,用它传 --token |
提示词里没写就问用户要,别猜 |
| 地址正确 | 同样写在提示词里;没有则读 MI_PLATFORM_BASE,再没有才用默认 http://127.0.0.1:8000(与平台默认端口一致) |
生产环境是院内地址,别硬编码。本地演示环境按 mi-platform-ops 起在 8010,手工测采集时须显式传 --base http://127.0.0.1:8010 |
令牌怎么来
首选:调度任务(定时任务的提示词)里显式写明,skill 读出来直接传 --token。
环境变量 MI_SCHED_TOKEN 只是回退路径,为了兼容已经有环境变量的部署。
两者都有时命令行参数优先。
令牌是平台侧维护的:登录平台,在「采集中心 → 外部调度接入」里生成、替换、停用。
换完之后平台下一个请求就生效,不需要重启服务。这意味着:
- 手上这份随时可能过期。 收到 401 就是被换掉了,去平台上核对当前值,
然后更新调度任务提示词里的那一份,不要自己另编一个。 - 不要把它写进 skill 目录里的任何文件。 令牌进了 skill 文件,跟着就会被提交到仓库。
写在调度任务的提示词里是可接受的,那是它该待的地方。
下文的 python scripts/push_rulepack.py ... 是示意写法。实际执行时用绝对路径调托管解释器,
不要依赖当前目录与 python 这个名字(Windows 上尤其容易踩):
"C:/Users/YTS/.workbuddy/binaries/python/versions/3.13.12/python.exe" \
"C:/Users/YTS/.workbuddy/skills/mi-rule-collect/scripts/push_rulepack.py" \
--base http://127.0.0.1:8000 --token <令牌> --overview
二、标准流程
第 1 步:先看平台现状
python scripts/push_rulepack.py --base <平台地址> --token <令牌> --overview
输出里有两个关键数:未生效待处理份数、当前生效版本数。先把这一步做了再抓页面,
原因是有草稿没会签时不该继续往上堆。堆到十几份草稿,人的第一反应是全部批量通过,
会签就废了。看到未生效份数不是 0,本轮只报告、不推送。
"未生效"分两种,报告里要分开写。 2026-09-20 修掉一个静默缺陷:平台的总览接口原先
只统计 draft 状态,而一份包一旦提交会签就变成 pending_review,于是它从总览里消失,
报告念出「待会签草稿 0 份、没有待会签的草稿」—— 那时包就摆在会签门口。
现在接口返回 draft_count 与 submitted_count 两个分项,脚本也会分开印:
| 状态 | 含义 | 报告里该说 |
|---|---|---|
draft |
刚推上来的草稿 | 还差差异比对与影响分析,这一步是平台侧或本院医保办做 |
pending_review |
已提交、坐在会签门口 | 只差一个人签字,别写成"没有待办" |
(判它有没有修好的办法:--overview 的输出里有没有"(草稿 N 份 / 已提交待签字 M 份)"
这行。没有这行就是平台还是旧版。)
第 2 步:读政策来源
用 WebFetch 读来源页。来源分两类:
- 国家医保局(
nhsa.gov.cn):分组方案、技术规范、通知公告。 - 省级医保局:实施细则、权重费率表、本地化调整。省级方案与国家版并存,不是替换关系。
读的时候只需要拿三类信息:
- 元信息:文号、发布日期、生效日期、适用统筹区、险种(DRG / DIP)。
- 规则内容:ADRG 分组表、CC/MCC 清单、细分组规则、合规规则。
- 参数:费率、调节系数、结算方式(权重法 / 点数法)、点数单价。
有结构化附件时,别去啃 PDF 正文
政策页上真正有用的东西常常在附件里,而且现在越来越多是 Excel。
2026-09-09 国家医保局把 3.0 版分组配置信息直接以 xlsx 公开
(nhsa.gov.cn/art/2026/9/9/art_14_22070.html,附件两个:DRG 配置信息 / DIP 分组方案),
表结构与平台规则包几乎一一对应:
| 官方工作表 | 对应规则包键 | 说明 |
|---|---|---|
| ADRG(538 行) | adrg_map |
ADRG编码/名称/ADRG规则/所属MDC/排序 |
| DRG(871 行) | drg_split |
DRG编码/名称/DRG规则/所属ADRG |
| CC(7918 行) | cc_mcc |
MCC 1581 + CC 6337,带排除表引用 |
| 集合(106837 行 / 634 个) | 匹配路径 | 集合编号 → ICD 编码清单 |
| 排除表(21037 行) | cc_mcc.exclusive |
按集合编号分组的排除清单 |
直接跑 scripts/parse_nhsa_xlsx.py 比手写解析可靠,它把这套映射固化了:
python scripts/parse_nhsa_xlsx.py --xlsx <配置信息.xlsx> --out <pack.json> --report <report.json>
这份 DRG 全量包采过没有?采过,但只落在库里,没落在磁盘归档上。 2026-09-20 实测把三种
"存量"分开看才对得上账:
| 位置 | 内容 | 是否全量 |
|---|---|---|
平台的 rule_pack 表 |
#2 DRG/NATIONAL/3.0-config-20260920,538 条 ADRG、4.2 MB |
✅ 全量 |
| 同表,由它派生的省版 | #4 DRG/410000/3.0-HN-2027Q1,538 条,状态 active |
✅ 全量 |
backend/rulepacks/(自动装载目录) |
DRG_3.0_NATIONAL.json,19 条 |
❌ 演示 |
collected_rulepacks/(归档) |
DRG_3.0.1_NATIONAL.json,19 条 |
❌ 演示 |
| 同上,DIP 那份 | DIP_3.0_NATIONAL.json,5125 条 |
✅ 全量 |
所以两句话都要说得准:
- 说"DRG 没有全量包"是错的。 库里那份 538 条的就是官方 xlsx 解析产物,省版也是从它
派生出来的,正常在跑。2026-09-20 之前我把"扫磁盘只看到 19 条"当成了"没采过"。 - 说"换台机器装就是演示数据"是对的,而且这才是真待办。 全量包只经由推送进了这一台的库,
没有回写到磁盘归档;自动装载目录里那 19 条演示集仍然排在那里。结论不变
(空库/新机器首次启动,生效的还是 19 条),但原因不是"没采",是"采了没归档"。
要补的动作是把全量包落回归档目录:collected_rulepacks/DRG_3.0_NATIONAL.json(与 DIP 那份
同级的命名),并且这份不能放进 backend/rulepacks/。替换自动装载目录里那份演示基线
是个要人签的动作,别让它自动发生 —— 那个目录只放国家版基线一份,多放一份且排在前面就静默生效成它。
三件它替你做、但你必须知道的事:
- 权重费率不在这个表里。 官方只给"怎么分组",不给"给多少钱"。
费率、权重、点值是统筹区的地方参数。解析出的包params全是占位值并带
params_placeholder: true——没拿到地方参数之前,这个包不能用于任何金额比对。 - 89 条规则含
and/or或多集合(如(ZYSS in OP1_AC1 and {ZYSS,QTSS} in OP2_AC1) or ...),
展开会改变语义,脚本一律不猜,原式留在official_rule字段并计入needs_review。
会签人要能一眼看到"这 89 条还没被转成可执行规则"。 - 46 个兜底组官方没给名称(
000未入组、XQY歧义组、X00未细分组)。
脚本补的是标签,用name_source: parser_label标出来,别当成官方名称。
采之前先问一句:院内的编码规范吗。 官方集合里存的是医保版完整编码
(I11.001、A00.000x001 这种),而平台的分组匹配是"病例编码以规则码开头"。
实测:主诊断填 I11.001 → 命中 ADRG=FR2 心力衰竭、休克;
填临床简码 I11.0 → 未入组。编码格式对不上时,规则包再准也一条都入不了组,
而且不报错,只表现为"匹配率突然很低"。采集前先拿院内真实病例的编码格式对一遍。
第 3 步:判断要不要推
拿原文与平台现行版本比。只推真正有变化的。每次抓取都推一份内容相同的规则包,
规则库会被"和线上一样"的版本塞满,真出现变化时反而看不见。
第 4 步:生成规则包
结构见 references/rulepack-schema.md。写完之后自己先过一遍校验规则里那几条硬要求
(必填 meta、code 不重复、表达式能编译),不要把明显不合规的东西推上去等平台报错。
更好的做法:直接用平台自己的校验器预检,别靠自己眼看。 校验的权威在平台,
但平台也是本地跑着的代码,可以 import 过来先跑一遍,推送就不会出现"校验不过":
import sys, json
sys.path.insert(0, r"<仓库>/mi-platform/backend")
from app.engine import rulepack
pack = json.load(open("rulepack.json", encoding="utf-8"))
print(rulepack.validate_payload(pack, strict=True)) # 期望 []
print(rulepack.summarize_diff(rulepack.diff_payloads(old, pack))) # 会签人将看到的那句话
diff_payloads 的输出就是会签人看到的那句话,先看一眼,能提前发现"改了个没人看得见的字段"
或者"改了一堆没打算改的字段"。
编码类规则(附件里给一长串诊断/手术编码的那种)另加一道核对:把生成的
when 里的编码集用正则反解回来,与附件做集合比对,四条必须同时成立——
条数相同、无重复、跨规则不重复、性别/方向类映射没写反。方向最容易写反,
写反了会报"不一致"的假警报,先把方向想清楚再写断言。
省级包怎么来:从国家版派生,不要手抄
省里下发的形态不固定:有的给完整方案,有的只给几张调整表(权重表、基础病组目录)。
先看清楚是哪一种。只改权重、费率、基础病组的那一种,不要抄成完整包,
用覆盖层:省版只声明差异(meta.overlay_of + params / adrg_weights / base_groups),
合并由平台在导入时做(services/overlay.py 的 materialize),落库的仍是一份完整包。
只有要改分组表或 CC-MCC 清单本身时,才出完整包。
覆盖层的价值不是「文件小」,而是让分叉可见。 完整副本抄出去之后,国家版改了别处
它不会知道,两份各走各的而系统一切正常;覆盖层把基线记在 meta.overlay_of,
基线一动就是可查的事实。
两条限制,踩过才知道:
- 覆盖层只能改 params / 权重 / 基础病组,改不了分组表与 CC-MCC 清单本身(那只能出完整包)。
半支持比不支持更危险:它不会报错,只是那部分差异静默不生效。 - 只支持一层。 基线本身若是覆盖层,导入直接拒——链式叠加会让
「这份包到底由几份文件决定」变成无解的问题。
手抄一份 5 MB 的表不现实,更要紧的是抄完之后"国家版又更新了"这种分叉
不会有任何地方报错。用 mi-platform/scripts/derive_province_pack.py 派生:
python derive_province_pack.py \
--base national_drg30.json \ # 国家版包
--region 410700 \ # 统筹区代码(省 410000 / 市 410700),不能是 NATIONAL
--version 3.0-HN-2027Q1 \
--params province_params.json \ # rate / coef / settlement_method / point_value
--weights province_weights.json \ # 按 ADRG 码或 DRG 码覆盖权重
--base-groups province_base_groups.json \ # 基础病组码,命中即标 is_primary_basis
--overlay-out province_overlay.json \ # 推荐:只写差异,导入时由平台合并
--report province_report.json
# 要改分组表本身时才用 --out province.json 出完整包
四条要记住的:
- 省参数只认白名单键(
rate/coef/settlement_method/point_value/
cc_boost/mcc_boost/innovation_boost/params_note)。判断标准是
引擎真的读它。写错的键不会报错,只会让那一项按国家版默认值算,
页面上一点异常都看不出来,所以脚本宁可拦住你。 base_rate/adjust_coef看着像省级参数,但全项目没有一处读取点,
写进去是一次静默的空操作;脚本单列一条能看懂的报错,不要照抄它们。- 权重码、基础病组码在包里找不到时会被列出来。写错一位数字就静默失效,
表现是"这个组还是按国家版权重算",没人会去核对。 - 脚本拒绝写入
backend/rulepacks/。那是随包分发的自动装载目录,目录里
多一份且排序在前,全新安装就会静默生效成那一份。 - 派生的包在
meta.derived_from里记了来源包的 region / version / sha256,
"这份省版基于哪一版国家版"是可查的(在库里、在包 JSON 里,也在规则包详情的「派生自」行上,带基线状态与 sha256 前 16 位)。
唯一还缺的是"国家版已更新、省版基于旧版"的自动检测
—— 这是已知缺口,见mi-platform-ops的待办。
推送省版和推送国家版是同一条路,没有任何特殊处理:rule_feed 填地址、
或 rule_file 指共享目录(省里人工摆渡文件的场景),采回来落草稿,再走
submit → review → activate。
采集线按「方案 × 层级」拆成三条,各自独立
2026-09-20 起不再是"一条 DRG 任务顺带管 DIP"。 三条线各自独立:独立定时任务、
独立摆渡目录、独立采集源、独立报告口径。理由重说一遍:混线的代价是报告失义 ——
"本轮无变化 / 有一份草稿"这句话里如果可能是任意一条线,收到报告的人没法据此决定做什么。
| 线 | 定时任务 | 任务 ID | 排期 |
|---|---|---|---|
| 国家 DRG | 医保规则采集 · 国家 DRG | 494eaf83-e934-495f-bef9-bc43b28494ad |
每周一 06:00 |
| 国家 DIP | 医保规则采集 · 国家 DIP | 81185505-bc29-4d18-a973-6a33b83ee947 |
每周一 06:20 |
| 地区 DIP(河南/新乡) | 医保规则采集 · 地区 DIP(河南/新乡) | 95cbee71-0656-4519-91b3-68e97e30550d |
每月 1 日 06:40 |
错开 20 分钟不是讲究,是因为三条线都要 get 平台状态、都要报"待会签几份",
同时跑会拿到互相矛盾的快照。
三条提示词各自独立、都写明"本线只负责 XX,另外两条是独立任务"。改一条别顺手改另两条。
推送不带方案参数 —— 方案在包里。 这一条是核过代码的,别再猜:push_rulepack.py
把整份 payload 原样放进请求体的 payload 字段,scheme_type / region / version
全由服务端从 payload.meta 读出来并回显(平台侧 api/collect.py 的
out.get('scheme_type'))。所以没有"选错方案"这个参数可以填错,
唯一的风险是包里 meta.scheme_type 写错了。写完包先念一遍 meta。
各线的落点(平台侧现状,2026-09-20):
| 线 | 摆渡目录 | 采集源 | 目录里现在有什么 |
|---|---|---|---|
| 国家 DRG | 走 rule_feed 推送(不依赖共享目录) |
#2 指向 backend/rulepacks |
DRG_3.0_NATIONAL.json(19 条演示集) |
| 国家 DIP | C:\tmp\mi30_dip_share |
#4 国家医保局 DIP 分组方案(共享目录) |
DIP_3.0_NATIONAL.json(5125 条全量) |
| 地区 DIP | C:\tmp\mi30_dip_hn_share |
#5 河南省/新乡市 DIP 方案(共享目录) |
空 |
采集源 #2 指向 backend/rulepacks 这件事要盯一眼。 那是自动装载目录,
往里放文件的副作用不只在采集:下次重启会被静默装载成基线。国家 DRG 线现在走推送,
所以暂时不冲突;但谁要改成"人工往这个目录摆渡",就等于同时改了自动装载的基线,
两件事会一起发生而日志里只留一条。要给国家 DRG 摆渡文件,另起目录。
一目录只放一类包
这条不是建议,是接口的硬行为:rule_file 型源一次只读一份文件,
选法是"显式配 file 优先,否则按修改时间取最新",落选的那些只在执行日志里留一句
("本次未读取:X")。表现是静默的:你以为目录里三份都在跟,实际只有一份在被读。
所以:
- 一个统筹区一条采集源、一个独立目录。 把新乡(410700)的包摆进省源目录,
它会和410000的包互相顶掉,而且不报错。 - DIP 与 DRG 必须分开建目录与源。 国家 DIP 基线尤其不能放进
mi-platform/backend/rulepacks/—— 同上,那是自动装载目录。 - 目录里确实要摆多份历史文件时,显式配
file,别靠时间猜。
"一个省一条采集源"是目标形态。 2026-09-20 核实的现状是:省源 #3 只有一个,
却采出过 410000 和 419999 两个地区(C:\tmp\mi30_province_share 里躺着一份
province_419999_3.0-test-0920101809.json)—— 就是上面这条在现实里的样子,
是测试残留,不是设计。建立/复核采集源时把这一条当核对项,不要假设它已经成立。
地区 DIP 的包怎么来
国家 DIP 基线(3.0-dip-config,5125 个病种)与地方参数是两件事,和 DRG 一样分两层。
地区 DIP 包从国家 DIP 基线派生,不要手抄:
python mi-platform/scripts/derive_province_pack.py \
--base <国家DIP包.json> --region 410700 --version 3.0-HN-XX-DIP \
--params xinxiang_params.json --weights xinxiang_scores.json \
--base-groups xinxiang_base_groups.json --overlay-out xinxiang_dip_overlay.json
三点要注意:
scheme_type是继承的,不是参数(derive_province_pack.py写的是
pmeta.get("scheme_type") or "DRG")。用国家 DIP 包当--base,派出来的就是 DIP 包;
拿错基线(比如误用 DRG 基线)会派出一份 scheme 是 DRG 的包,而脚本不会拦,
因为它只忠实继承。派生完先核一遍产物meta.scheme_type。- DIP 的"权重"是分值。 覆盖层那个
--weights键名叫adrg_weights(DRG 时代的名字),
填的是"码 → 数值"的对应关系,DIP 下就填病种码 → 分值。名字是历史包袱,语义是同一件事。 - 新乡(410700)走 DIP(河南双轨:郑州 DIP / 漯河与驻马店 DRG,新乡属 DIP 侧)。
新乡的统筹参数目前只有 docx/PDF,没有结构化表,所以这条线采不到真参数。
采不到就如实汇报"本地区参数缺结构化来源",把分组结构派生出来即可,
绝不编分值 —— 分值编错,全院每个病例的点数结论都是错的,而且错得很安静。
这条线现在跑起来会是空的(C:\tmp\mi30_dip_hn_share 是空目录),
rule_file 型源读不到文件时的正常结果是"没有新包"。这不是故障,报告里别写成故障;
要写成"本线尚未收到地区文件,需等省市医保局发文后人工摆渡"。
DIP 包的处境:采进来不能跑批,但拦法是硬的
引擎层(grouping.guard_groupable)直接拒绝拿它做入组与测算,跑批接口返 400 并写明原因。
不是"算出来是空的",更不是"算出来看着正常":实测 12 份病例能入组 11 份、组名也对,
但 weight 是占位值 1.0,支付标准被一律算成 10000.00。
所以 meta.not_for_grouping + meta.note 必须在包里写全,详情页也会声明
「包内备注是生成时的快照,以本页为准」。
第 5 步:推送
python scripts/push_rulepack.py --file rulepack.json \
--base <平台地址> --token <令牌> \
--source-name "国家医保局 DRG 3.0 政策页" \
--source-url "https://www.nhsa.gov.cn/..."
第 6 步:报告
推送脚本的退出码就是结论,直接据此写报告:
| 退出码 | 含义 | 报告里怎么写 |
|---|---|---|
| 0 | 与现行版本一致,未产生新版本 | 本轮无变化。不用惊动人 |
| 1 | 已落草稿,等待人工会签 | 要写清楚:改了哪个版本、差异概要、请谁去会签 |
| 2 | 被拒(校验不过或同版本号冲突) | 附上平台给的问题清单。这类需要人去看原文 |
| 3 | 环境问题:连不上平台、令牌没拿到或不对、功能没开或被停用 | 说明是环境问题,不是规则问题。对照下节的排错表定位 |
退出码 1 的报告不要只写"任务成功"。要能让人一眼看出"现在该做什么"。
出错时先分清是谁的问题
这套链路有四个不同的失败点,混在一起会让人查错方向:
| 看到什么 | 是哪儿的问题 | 找谁 |
|---|---|---|
本地就退出,EXIT=3,提示没拿到令牌 |
令牌既没写在调度任务提示词里,也没有环境变量,请求根本没发出去 | 自己把令牌写进提示词 |
HTTP 403 未开启外部调度接入 |
平台上没配令牌,或已在「外部调度接入」里被停用 | 到平台配置或重新启用 |
HTTP 401 调度令牌无效 |
手上这份与平台当前值不一致,通常是被轮换过 | 上平台核对当前值后同步到提示词里 |
平台健康检查失败 |
服务没起,或平台地址写错 | 先核地址,再找服务方 |
关键区分:403 是功能没开,401 才是令牌不对。 平台刻意把它们分开,
就是不想让人对着一个没开的功能反复换令牌。想复现 403,得带一个非空但平台没配的令牌。
401 还有一个容易被忽略的成因:平台上有人换了 key,而你手上还是旧的。
所以看到 401 的第一反应不该是"重试"或"自己造一个",而是去平台上核对。
二·五、2.0 版:官方发的是 PDF,不是 xlsx(2026-09-22 实测)
这一节与上面的 3.0 流程并列,不是替代。 2.0 与 3.0 是两条独立的版本线,
定时任务各自独立,不要混线。
来源(都已核过)
| 项 | 值 |
|---|---|
| 文号 | 医保办发〔2024〕9号,2024-07-17 成文,2024-07-23 发布 |
| 总通知页 | https://www.gov.cn/zhengce/zhengceku/202407/content_6964136.htm |
| DRG 2.0 附件 | https://www.gov.cn/zhengce/zhengceku/202407/P020240724163359237731.pdf |
| DIP 2.0 附件 | https://www.gov.cn/zhengce/zhengceku/202407/P020240724169822128865.pdf |
| 官方镜像 | https://www.nhsa.gov.cn/art/2024/7/23/art_104_13313.html |
| 发布会实录(口径来源) | https://www.nhsa.gov.cn/art/2024/7/23/art_52_13319.html |
附件是 PDF,没有 xlsx。 这正是 3.0 的 parse_nhsa_xlsx.py / parse_dip_xlsx.py
不能用于 2.0 的原因——它们是按 3.0 的 xlsx 表结构写死的。
官方口径(用来做数量断言)
| 方案 | 口径 | 出处 |
|---|---|---|
| DRG 2.0 | MDC 26 / ADRG 409(外科 182 + 非手术室操作 37 + 内科 190)/ DRG 634(外科 251 + 操作 57 + 内科 326) | 发布会实录 + PDF 第 39 页、第 49 页 |
| DIP 2.0 | 核心病种 9520(手术操作组 6311 + 保守治疗组 3209),分值/点值由统筹区定 | 发布会实录 + PDF 前言 |
解析完必须拿这三个数比对(DRG 409/634、DIP 9520)。对不上就是抽错了,不要去猜。
用哪个脚本
| 脚本 | 用途 |
|---|---|
scripts/parse_dip_pdf_20.py |
DIP 2.0 PDF → 规则包。实测 9520 条,编号 1-9520 无缺无重 |
scripts/parse_drg_pdf_20.py |
DRG 2.0 PDF → 规则包。实测 ADRG 409 / DRG 634 / CC 8009 / MCC 4475 |
两个都依赖 pdfplumber(托管 venv 默认没有,先 pip install pdfplumber)。
版面坑(这几条是踩过的,必须知道)
- DIP 2.0 是结构化表格:234 页,每页一张 7 列表(编号/主诊断码/名称/主手术码/名称/相关手术码/名称),
43 行/页。extract_tables()直接可用,最省事的一条线。 - DRG 2.0 的清单段落位置与直觉相反:
- 第 27–40 页是「表3-2 ADRG的变化情况」(1.1→2.0 对照表),四列并排、序号与编码粘连
(1AA1心脏移植)。它不是 ADRG 全量清单,照它抽会得到一堆垃圾。
- 真正的 ADRG 全量清单(表2-1-1)在第 39–48 页,格式干净:AA1心肺移植。
- DRG 细分组(表3-1-1)在第 49–64 页,格式AA1AA19心肺移植(ADRG码+DRG码+名称)。
注意是 49–64,只取到 58 页会少一半(实测 413 而非 634)。
- MCC 表6-1-1 在第 1208–1314 页,CC 表6-2-1 在第 1316–1506 页。 - DRG 2.0 是单栏、编码与名称粘连在同一个词里(
AA1心肺移植)。
按 x 坐标切列会失败(编码和名称 x0 相近);要用行首正则^([A-Z]{2}\d{1,2})(.+)$剥离编码。 - 跨页污染有两类,都要显式清洗:
- 表头污染:下一页页首的DRG名称编码编码会被并进上一条名称;
- 页码污染:ZZ1多发性重要创伤无手术26的行尾26是页码。
清洗规则:行尾 1–4 位数字且等于当前页号就删;(DRG名称|ADRG名称)?编码编码$删。 drg_split不要用 dict 覆盖。同一个 ADRG 下会有多条同档 DRG,
若按drg_split[adrg][slot] = {...}赋值,后写覆盖前写(实测 634 条只剩 463)。
写成drg_split[adrg][slot] = [...]列表。- CC/MCC 的编码会粘上排除表编号的字母(名称以字母结尾时)。
正则要用^编码\s*名称\s*表6-3-\d+[A-Z]?$,把表6-3-208C整体吃掉,不要让它污染编码。
2.0 包的 meta 怎么写
{
"scheme_type": "DRG",
"region": "NATIONAL",
"version": "2.0-NATIONAL-20240717",
"name": "按病组(DRG)付费分组方案 2.0 版",
"published_at": "2024-07-23",
"effective_from": "2025-01-01",
"source_url": "https://www.gov.cn/zhengce/zhengceku/202407/content_6964136.htm"
}
DIP 2.0 同样加 not_for_grouping: true(理由与 3.0 DIP 包相同:引擎无 DIP 入组路径)。
权重/费率/分值官方 2.0 分组方案都不随文下发(由统筹区定),
weight 一律占位 1.0 + params_placeholder: true,不编造。
ADRG 的 surg/med 类型官方没标,脚本是按名称关键词推断的,meta.note 里要写明
"精确归属(182/37/190)需会签人核对"。
实测结果(2026-09-22)
| 包 | 条数 | 平台严格校验 |
|---|---|---|
| DIP 2.0 → 草稿 #7 | 9520(手术 6311 + 保守 3209) | [] 零问题 |
| DRG 2.0 → 草稿 #8 | ADRG 409 / DRG 634 / CC 8009 / MCC 4475 | [] 零问题 |
三、几条硬约束
不猜参数。 页面里没写的费率、系数、阈值不要凭印象填。宁可推一份只含分组表的
规则包,也不要把一个编出来的费率写进去。费率错了,全院每一个病例的盈亏结论都是错的,
而且错得很安静。
不改现网参数。 省级方案往往只覆盖部分分组,其余沿用国家版。缺的部分留空,
由人在会签时补。自动补全等于替人做了决定。
timing 默认 post。 一条规则能不能在院期间提醒,是行为分界,不是标注细节。
政策原文没说清楚的,一律按 post(仅出院后可用)。想前移必须显式写出来,
并且这次改动会进差异比对,会签的人能看见。
不碰生效状态。 只推草稿。push 接口本身也没有激活能力,这是刻意设计的。
单条规则的判定表达式硬上限 2000 字符。 平台的表达式编译器在导入期就卡长度
(app/engine/expr.py 的 compile_expr,超了直接拒收)。数组形式的编码清单很吃字符:
一条 has_diag(case, [...]) 塞不下 150 条左右的 11~18 位编码。所以从附件里搬
"一长串编码"的规则(智能监管两库、CC/MCC 补丁、编码对照表等)时会撞上这个墙,
必须在生成前就按长度分片,而不是写完推上去被拒。
分片是平台能力所限,不是内容取舍,因此两件事必须做到:
- 分片后的规则合集要与附件逐条对应,不重不漏(见第 4 步的核对办法);
- 在
meta.note和各条规则的basis里写明"因平台单条表达式 2000 字符上限拆为 N 条",
否则会签人看到 N 条同名规则会以为是内容被切掉了。
分片按什么维度切要能自解释(例如"患者性别 + 编码集",配合附件序号区间),
不要按纯粹的字符数硬切——会签人要在差异清单上认出每条管的是哪一段。
采 DIP 包之前先看这一条。 官方 3.0 的第二个附件是《按病种分值(DIP)付费 3.0 版
分组方案》,结构与 DRG 完全不同:
- 病种只有序号,没有编码(平台校验要求
code非空,只能自造编号); - 匹配是「主要诊断 且 主要手术操作」,不是二选一;
- 分四级优先级:先期分组 > 并项规则 > 辅助细分 > 基础规则(对应附件里四张表);
- 有一张"不纳入分组的主要诊断 / 主要手术操作"黑名单(诊断 4745 行、手术 4113 行);
- 没有 CC/MCC,也没有权重与分值(分值、点值、等级系数都是统筹区定的)。
平台引擎目前只有 DRG 入组路径:grouping.pick_adrg 是「主手术 或 主诊断」
二选一,adrg_map 是一张平表,没有优先级这一维。所以 DIP 包能解析、能推、
能落草稿、能 diff,但不能用来跑批。
实测它跑起来是什么样(2026-09-20,比"入组率虚高"更隐蔽):DIP 包的 adrg_map
与 DRG 包字段完全同形(code/name/mdc/type/weight/diag_prefixes/op_codes),
12 份真病例入组 11 份、组名也对(脑梗死 / 急性心肌梗死 / 心力衰竭),一眼看不出异常;
但包里 weight 全是占位值 1.0,支付标准被一律算成 10000.00。
判它的指纹:支付标准取值只有一种,就是假的。
平台已经把它拦住:引擎层的唯一收口(grouping.guard_groupable)拒绝拿这类包做测算,
跑批接口返 400,导入结果区分"没跑批",影响分析显式放行。拦住的是"算出一批金额看着正常、
实际没有意义的数字"这件事。
真要采,meta.not_for_grouping = true,并且 meta.note 里写明「引擎尚无 DIP 入组路径,
本包仅供规则查阅与差异比对,不用于跑批」。不写,事后没人分得清这批数字是怎么来的。
(把 DIP 病种硬映射成 type=surg + op_codes=[主手术] 会丢掉主诊断条件,
映射成 type=med + diag_prefixes=[主诊断] 会丢掉主手术条件。两种都是不报错的错。)
已经有解析脚本:scripts/parse_dip_xlsx.py(与平台侧
mi-platform/scripts/parse_dip_xlsx.py 同源,改一处要改两处)。
实测官方 3.0 附件解析出 5125 个病种(与官方口径一致),平台严格校验 0 问题、能落草稿。
三件它替你做、但你必须知道的事:
- 官方不给病种编码,
code用「分类-序号」自造(XQ-1/BX-1725/JC-3268)。
同一 (分类, 序号) 撞车会直接报错,不静默。 - 分值 / 点值 / 等级系数官方也不给,
weight全是占位 1.0 并带params_placeholder。
没拿到统筹区参数之前,这个包不能用于任何金额比对。 - 官方编码是三种形态混在一起:管道多值、
C00-C75这类区间、T23.这类截断前缀。
脚本都会展开或原样保留,展不开的区间列进 report,不猜。
基层病种标记分两轮(原文相等 → 前缀覆盖),实测 127 条标上 121 条,
剩下 12 条列在report.warnings里(很可能是综合病种范畴,要人工核),不静默当 0。
python scripts/parse_dip_xlsx.py --xlsx <DIP分组方案.xlsx> --out dip.json --report dip_report.json
产物带 meta.not_for_grouping: true 和一段写清"为什么不能跑批"的 note,
会签人打开规则包详情就能看见。
四、文件
| 文件 | 用途 |
|---|---|
scripts/push_rulepack.py |
推送规则包、查平台状态。标准库实现,无第三方依赖 |
scripts/parse_nhsa_xlsx.py |
官方 3.0 DRG 分组配置信息 xlsx → 规则包 JSON。只认 DRG 那个附件,DIP 附件结构完全不同,用下面那个。需要 openpyxl |
scripts/parse_dip_xlsx.py |
官方 3.0 DIP 分组方案 xlsx → 规则包 JSON。产物是只读基线,不可跑批。与平台侧同源,改一处要改两处。需要 openpyxl |
scripts/parse_dip_pdf_20.py |
官方 2.0 DIP 病种库 PDF → 规则包 JSON(9520 条)。只认 PDF,与 3.0 xlsx 脚本不可互换。需要 pdfplumber |
scripts/parse_drg_pdf_20.py |
官方 2.0 DRG 分组方案 PDF → 规则包 JSON(ADRG 409 / DRG 634 / CC / MCC)。只认 PDF。需要 pdfplumber |
references/rulepack-schema.md |
规则包结构与校验要求,生成时对着写 |
不属于本技能目录、但这条流程会用到的脚本(都在 mi-platform/scripts/):
| 文件 | 用途 |
|---|---|
derive_province_pack.py |
从国家版派生省级覆盖层(推荐)或完整包:params / 权重 / 基础病组 + derived_from 溯源 |
probe_two_level_collect.py |
两级采集端到端实测:国家版打底 + 本省再叠一层,验证并存与选包 |
parse_nhsa_pack.py |
与 scripts/parse_nhsa_xlsx.py 同源。改这份解析逻辑时两边都要改,否则平台侧和技能侧解析出的包会不一致 |