免疫治疗门诊动线优化:Go离散事件仿真(DES)从“常规排队”到“ResusBay挤兑”的技术全解(上)

免疫治疗门诊动线优化:Go离散事件仿真(DES)从“常规排队”到“ResusBay挤兑”的技术全解(上)
在这里插入图片描述
面向读者:算法/后端/数据工程/运筹优化/医疗信息化
关键词:离散事件仿真(DES)、队列系统、资源竞争、预约模板、药房预配、irAE、抢救床位(ResusBay)、尾部风险(P90/超时率)、Go

免疫检查点抑制剂(ICI)是临床上的重大进展,但在医院运营视角下,它把输注中心变成了一个典型的复杂系统:
随机到达 + 随机服务时长 + 多站点串联 + 多资源并发 + 低概率高冲击风险事件(irAE)
如果你只靠经验改流程,常常会陷入“改了这里堵了那里”;而工程上更稳的方法,是用 **离散事件仿真(DES)**在虚拟世界里“跑一天、一周、一年”,对比策略组合,找出瓶颈与最优干预点。

这篇文章从零到一搭建一个“可跑”的 Go DES 仿真框架,并逐步扩展到免疫治疗的关键真实因素:

  • 患者分型(短/长/联合输注)
  • 预约模板(均匀 vs 长短错峰)
  • 药房策略(按需 vs 预配)
  • irAE 突发事件(分级)
  • **ResusBay(抢救/留观床位)**造成的“系统挤兑”:Severe 患者在输注椅上占位等待转运 → 椅位周转下降 → 全局排队雪崩 → P90/超时率恶化

1. 业务抽象:把输注中心变成“可计算的系统”

1.1 站点与路径(流程动线的图模型)

我们先从最常见的 ICI 门诊输注日路径抽象:

SignIn → Lab → Doctor → Pharmacy → Infusion → Observation → Done

这是一条典型串行路径,但注意两点:

  1. 串行 ≠ 简单:每个站点都有队列与资源容量(Cap),并发服务(多台“服务器”)
  2. 真正复杂性来自 波动:到达波动(预约聚集)、服务时间波动(医生评估 irAE)、以及 突发事件(irAE → Resus)

1.2 “动线优化”在流程场景 A 中到底优化什么?

我们聚焦几个最常用 KPI:

  • Avg / P90 等待时间(按站点拆分:医生/药房/输注/观察/Resus)
  • Avg / P90 总逗留时间(从到达到离开)
  • 超时率:例如总逗留 > 240 分钟(>4h)
  • 资源利用率:药房、医生、输注椅、观察椅、Resus 床位
技术上:只优化平均值通常会误导。医疗系统最敏感的是“尾部风险”(P90/P95/超时率),因为它对应投诉、加班、延迟、与临床安全风险。

2. 为什么选离散事件仿真(DES):而不是简单排队公式

2.1 DES 的核心思想:时间只在“事件发生”时跳动

离散事件仿真把系统演化看成一串事件:

  • 到达(Arrival)
  • 开始服务/结束服务(ServiceDone)
  • 突发事件(irAE)
  • 入队事件(QueueEnter)
  • 特殊释放事件(ReleaseInfusion:Severe 转入 Resus 时释放椅位)

系统时间从 t 跳到 next_event_time,因此复杂系统也能高效模拟。

2.2 为什么不直接用“每站点 M/M/c”?

因为现实里存在:

  • 不同患者类别(服务时长分布不同)
  • 串联系统(上一站结束才到下一站)
  • 调度策略(预约模板改变到达分布)
  • 优先级队列(紧急插队)
  • 资源耦合(Severe 把输注椅“卡死”直到 Resus 有空)

这些让解析解非常难用,但 DES 只需在事件逻辑里表达即可。


3. 模型工程化设计:数据结构与事件机制

3.1 仿真状态(State)要包含什么?

最小可用的仿真状态包括:

  • Patients[]:每个患者的到达时间、各站点开始/结束/等待、服务时长、类别、irAE 状态
  • Queues[Station]:每站点一个 FIFO 队列
  • Resources[Station]:容量 Cap、当前 InUse、BusyMin(用于利用率)
  • Events:事件优先队列(最小堆)

3.2 为什么事件队列用最小堆?

事件队列的操作是:

  • push:安排未来事件
  • pop:取最早事件执行

最小堆让每次 push/pop 都是 O(log N),非常适合“事件驱动”的仿真。

3.3 BusyMin(利用率)怎么计算才不骗自己?

严格方法:在每次资源占用/释放的瞬间更新占用区间。
为了保持代码“最小可跑”,我们采用常见的简化方式:

  • 每当开始服务,BusyMin += dur
  • Severe 导致“额外占椅”,用 ReleaseInfusion 事件把额外占用时间补回 Infusion.BusyMin

这让利用率在策略对比上非常稳定且可解释;若要做精确曲线(按分钟利用率热图),可以再加时间片统计(后面会给扩展建议)。


4. 随机性建模:分布选择与参数化

4.1 为什么常用对数正态(LogNormal)?

医疗服务时长经常呈现:

  • 下界 > 0(不可能负时间)
  • 右偏长尾(少数患者非常久)

对数正态正好符合这个形态。
因此我们用:

  • SignIn、Lab、Doctor、Pharmacy、Observation、Resus:LogNormal + clamp 边界
  • Infusion:按患者类型给不同均值与方差(可视为截断正态/常数+噪声)

4.2 参数从哪来?

实际项目中你会做三层:

  1. 经验初值(像本文)用于搭框架
  2. 用历史数据拟合(LogNormal/Gamma/Weibull),并做 KS 检验
  3. 用“反推校准”:使仿真输出的均值、分位数、资源利用率与真实对齐
技术提示:校准(calibration)比“分布选型”更重要。选错分布但校准对了,模型仍有决策价值;反之则可能误导。

5. 策略建模:预约模板与药房预配怎么“落到代码里”

5.1 预约模板 = 到达时间分布的控制

我们实现两个典型策略:

  • Uniform:把到达均匀铺在 6 小时窗口(0~360min),加小扰动
  • Staggered:短输注(TypeShort)偏上午,长/联合偏下午(180~360min),加扰动

这本质上是改变系统的“输入过程”,改变拥堵峰值。

5.2 药房预配 = 缩短 Pharmacy 服务时间(同时引入成本问题)

在最小模型中,我们把预配简化为:

  • PharmacyPre=true 时,Pharmacy 服务时间视作 0

现实里你会引入:

  • 预配提前量(比如提前 30min 开始配)
  • 取消/改期概率 → 产生浪费成本
  • 稳定性窗口 → 超过窗口失效(需要重配)

这些都可以在 DES 中继续扩展(后面给扩展蓝图)。


6. 免疫治疗独有冲击:irAE 分级与 ResusBay 挤兑

6.1 为什么 Severe irAE 必须单独建模?

轻中度 irAE 通常导致:

  • 观察时间延长
  • 可能额外医生评估

但 Severe irAE 引入关键耦合:

  • 需要 Resus(抢救床位)
  • Resus 可能无空位
  • 患者在输注区“占位等待转运”
  • 输注椅位不能释放 → 后续患者输注排队、等待上升
  • 最后表现为尾部变厚(P90、超时率上升)

这个“占位等待”就是医疗系统里非常真实的挤兑机制

6.2 关键实现点:什么时候释放输注椅?

如果你在 irAE 发生那一刻就释放椅位,会低估真实拥堵。
我们采用更真实逻辑:

  • Severe irAE 发生后:仍占椅做初步处置/转运准备(prep 15~30min)
  • 准备完入 Resus 队列
  • 当 Resus 真正开始服务(即拿到床位)时,再触发 EvReleaseInfusion 释放椅位

这一步是模型“像现实”的关键。


7. 完整可运行 Go 代码(含 ResusBay + irAE)

✅ 复制到 main.go
go run main.go 即可
✅ 输出包含 Avg/P90/超时/各站等待/Resus 等待与利用率/Severe 比例
你可以把参数(到达量、资源容量、irAE 概率)改成你院数据做仿真对比。
package main import("container/heap""fmt""math""math/rand""sort""strings""time")// ---------- 类型定义 ----------type PatientType intconst( TypeShort PatientType =iota TypeLong TypeCombo )type Station intconst( SignIn Station =iota Lab Doctor Pharmacy Infusion Observation Resus // 新增:抢救/留观床位 Done )func(s Station)String()string{ switch s { case SignIn:return"SignIn"case Lab:return"Lab"case Doctor:return"Doctor"case Pharmacy:return"Pharmacy"case Infusion:return"Infusion"case Observation:return"Observation"case Resus:return"Resus"default:return"Done"}}type IrAEGrade intconst( IrAENone IrAEGrade =iota IrAEMild IrAEModerate IrAESevere )func(g IrAEGrade)String()string{ switch g { case IrAEMild:return"Mild"case IrAEModerate:return"Moderate"case IrAESevere:return"Severe"default:return"None"}}type Patient struct{  ID int PType PatientType Arrive float64 Start map[Station]float64 End map[Station]float64 Wait map[Station]float64 ServiceDur map[Station]float64// irAE IrAE IrAEGrade IrAEAt float64 IrAEFired bool// Severe 专用:椅位“占位等待转运” InfusionInterrupted bool InfusionReleased bool InfusionPlannedEnd float64// 原计划输注结束 InfusionHoldStartTime float64// 原计划结束时刻(开始“额外占位”计时点)}type EventType intconst( EvArrival EventType =iota EvServiceDone EvIrAE EvQueueEnter // 在某时刻进入某站点队列 EvReleaseInfusion // 在某时刻释放输注椅)type Event struct{  T float64 Type EventType PID int At Station index int}type EventPQ []*Event func(pq EventPQ)Len()int{ returnlen(pq)}func(pq EventPQ)Less(i, j int)bool{ return pq[i].T < pq[j].T }func(pq EventPQ)Swap(i, j int){  pq[i], pq[j]= pq[j], pq[i] pq[i].index, pq[j].index = i, j }func(pq *EventPQ)Push(x any){  e := x.(*Event) e.index =len(*pq)*pq =append(*pq, e)}func(pq *EventPQ)Pop() any {  old :=*pq n :=len(old) e := old[n-1]*pq = old[:n-1]return e }// ---------- 队列/资源 ----------type Queue []intfunc(q *Queue)Enq(pid int){ *q =append(*q, pid)}func(q *Queue)Deq()(int,bool){ iflen(*q)==0{ return0,false} pid :=(*q)[0]*q =(*q)[1:]return pid,true}type Resource struct{  Cap int InUse int BusyMin float64}// ---------- 仿真 ----------type Sim struct{  DayMinutes float64 Rand *rand.Rand Patients []*Patient Events EventPQ Queues map[Station]*Queue UrgentQueues map[Station]*Queue Resources map[Station]*Reso

Read more

“现在的AI就像1880年的笨重工厂!”微软CSO斯坦福泼冷水:别急着造神

“现在的AI就像1880年的笨重工厂!”微软CSO斯坦福泼冷水:别急着造神

大模型仍未对上商业的齿轮? 编译 | 王启隆 来源 | youtu.be/aWqfH0aSGKI 出品丨AI 科技大本营(ID:rgznai100) 现在的硅谷,空气里都飘着一股“再不上车就晚了”的焦躁感。 最近 OpenClaw 风头正旺,强势登顶 GitHub,终结了 React 神话,许多人更是觉得“AI 自己干活赚钱”的日子就在明天了。 特别是在斯坦福商学院(GSB)这种地方,台下坐着的都是成天琢磨怎么用下一个技术风口搞个独角兽出来的狠人。 微软的首席科学官(CSO)Eric Horvitz 被请到了这个几乎全美最想用 AI 变现的礼堂里。作为从上世纪 80 年代就开始搞 AI 的绝对老炮、也是微软技术底座的“扫地僧”,这位老哥并没有顺着台下的胃口,去吹捧下个月大模型又要颠覆什么行业,而是兜头给大家浇了一盆带点学术味的冷水。 他讲了一个挺有画面感的比喻:大家都在聊

By Ne0inhk
Godot被AI代码“围攻”!维护者崩溃发声:“不知道还能坚持多久”

Godot被AI代码“围攻”!维护者崩溃发声:“不知道还能坚持多久”

整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 当大模型能在几秒钟内生成一段“看起来像那么回事”的补丁时,开源社区却开始付出另一种代价。 最近,开源游戏引擎 Godot 的核心维护团队公开吐槽:他们正被大量“AI 生成的低质量代码”淹没。那些代码往往结构完整、注释齐全、描述洋洋洒洒,但真正的问题是——提交者可能并不理解自己交上来的内容。 这件事,并不是简单的“有人偷懒用 AI 写代码”。它正在触及开源协作最核心的东西:信任。 一场悄无声息的“AI 洪水” 事情的导火索来自一条 Bluesky 讨论帖。 Godot 主要维护者之一、同时也是 Godot 商业支持公司 W4 Games 联合创始人的 Rémi Verschelde 表示,所谓的“AI slop”

By Ne0inhk
诺奖得主辛顿最新访谈:1 万个 AI 可以瞬间共享同一份“灵魂”,这就是为什么人类注定被超越

诺奖得主辛顿最新访谈:1 万个 AI 可以瞬间共享同一份“灵魂”,这就是为什么人类注定被超越

当宇宙级的“嘴炮”遇到降维打击。 编译 | 王启隆 来源 | youtu.be/l6ZcFa8pybE 出品丨AI 科技大本营(ID:rgznai100) 打开最新一期知名播客 StarTalk 的 YouTube 评论区,最高赞的一条留言是这样写的: “我长这么大,第一次看到尼尔·德葛司·泰森(Neil deGrasse Tyson)在一档节目里几乎全程闭嘴,像个手足无措的小学生一样乖乖听讲。” 作为全美最知名的天体物理学家,泰森平时的画风是充满激情、喋喋不休、用宇宙的宏大来震撼嘉宾。但这一次,坐在他对面的那位满头银发、带着温和英音的英国老人,仅仅用最平淡的语气,就让整个演播室陷入了数次令人窒息的沉默。 这位老人是 Geoffrey Hinton。深度学习三巨头之一,2024 年诺贝尔物理学奖得主,被公认为“AI 教父”。 对经常阅读 Hinton 演讲的我来说,这也是比较新奇的一幕—

By Ne0inhk
48小时“烧光”56万!三人创业团队濒临破产,仅因Gemini API密钥被盗:“AI账单远超我们的银行余额”

48小时“烧光”56万!三人创业团队濒临破产,仅因Gemini API密钥被盗:“AI账单远超我们的银行余额”

整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 「仅过了 48 小时,一笔 8.2 万美元的天价费用凭空出现,较这家小型初创公司的正常月费暴涨近 46000%。」 这不是假设的虚幻故事,而是一家墨西哥初创公司正在经历的真实危机。 近日,一位名为 RatonVaquero 的开发者在 Reddit 发帖求助称,由于他的 Gemini API 密钥被盗用,原本每月仅约 180 美元(约 1242 元)的费用,在短短 48 小时内暴涨到 82,314.44 美元(约 56.8 万元)。对于这家只有三名开发者的小型创业团队来说,这笔突如其来的账单,几乎等同于灭顶之灾。 “我现在整个人都处在震惊和恐慌之中。”RatonVaquero

By Ne0inhk