triAIge:評審報告

triAIge 是一套在邊緣運算上執行的 AI 交接系統,處理台灣急救最前面的幾分鐘。救護車上的救護員(EMT)把看到的情況說出來或打字輸入,系統就填寫消防局的救護紀錄表,並傳到急診(ED)。檢傷護理師打開的是預先填好、附上建議檢傷級數的評估表,醫師拿到的是交接摘要,以及一份附出處的鑑別診斷與建議檢查。

這份報告的讀者大概只會讀一次,所以每一項說法都附上檔案出處,路徑都相對於 repository 根目錄;repository 裡沒有的數字,報告也不寫。

這是黑客松原型,不是經過臨床驗證的產品。哪些有量測、哪些沒有,第 7 節逐項說明。

1. 目前完成了什麼?

狀態位置
救護端 app、急診檢傷 app、醫師畫面(web)已完成;/ 連真後端,/internal 連瀏覽器內的模擬後端(mock)web/README.md、docs/specs/2026-10-13-frontend-restructure/decision.md
階段銜接(/)救護端送出後播「救護車」轉場,落在急診檢傷台;檢傷送出後播「看診區」轉場,等鑑別診斷整理好再進醫師摘要。轉場可以略過web/src/components/flow/stage-advance.tsx、web/src/components/guide/transition/waits.ts
後端:FastAPI、PostgreSQL、42 個 API 操作(含即時推播 SSE)已完成docs/api/openapi.yaml、docs/specs/2026-10-13-backend/design.md
在 DGX Spark 級主機上用 Nemotron 3 Super(NVFP4)擷取欄位已完成,已評測eval/README.md
以規則產生的 TTAS 建議、完整性檢查、規則版交接摘要與醫師摘要已完成。這一份摘要由規則從紀錄組出,不呼叫模型,永遠存在server/src/triaige_server/engine/views.py、engine/ttas.py
交接摘要的「AI 整理」(階段 2)已完成,預設開啟。救護端送出與檢傷送出之後,Super 在背景把紀錄改寫成最多 6 句附來源的繁體中文;失敗時規則版照常顯示server/src/triaige_server/engine/ai_summary.py、task_rules/summary_checks.py
醫師端鑑別診斷(階段 2)已完成,Spark 上開啟。一個會搜尋知識庫的 agent,列出常見的前 5 項與最致命的前 5 項,每項附事實與出處。沒有醫師標準答案,所以沒有準確度數字(§7)server/src/triaige_server/ai/nat_differential.py、kb.py、task_rules/differential_checks.py、infra/inventory/group_vars/servers.yml
建議追問、護理師輸入擷取、急救責任醫院分級建議(階段 2)已完成;建議追問與護理師輸入擷取預設開啟,分級建議在 Spark 上開啟。三者都是建議,由人決定server/src/triaige_server/ai/followups.py、ai/nurse_notes.py、engine/hospital_level.py
在欄位上複查 AI 的值(沒有阻斷式對話框)、每個欄位的來源與變更歷程、確認/選取/復原操作已完成docs/specs/2026-10-14-inline-review/plan.md
由 token 對數機率算出的欄位信心等級分數已經計算並儲存;門檻校準過兩次,但等級還沒有顯示(enabled=false),因為門檻分不出差別,見 §9server/src/triaige_server/llm/profiles/nemotron_3_super/confidence_thresholds.json、eval/results/choice-confidence-2026-10-18/summary.md
案件備忘的自動整理程式已寫好,Spark 上關閉,保留集通過後才打開infra/inventory/group_vars/servers.yml(triaige_memo_consolidate: false)
語音辨識不在這個 repository 裡。 API 接受語音訊息(inputMode: voice),規劃是在瀏覽器內辨識,但 web app 還沒有辨識器,展示版用文字訊息操作docs/api/README.md §6
真實的醫院系統(HIS/EMR、健保卡)用虛構病人代替docs/api/README.md §1.3

web app 的線上版:https://triaige.wayneh.tw 與 https://triaige-demo.vercel.app(README.md、infra/inventory/group_vars/servers.yml 的 CORS 清單)。展示用的病人都是虛構的;/internal 的資料只存在瀏覽器。

2. 要解決什麼問題?

在台灣,消防局的救護員一邊處置病人,一邊在紙本格式的救護紀錄表上記錄;到了急診,同樣的問題又被問一次。急診檢傷護理師要從台灣急診檢傷與急迫度分級量表(Taiwan Triage and Acuity Scale,TTAS)的五個級數中選一個,醫師則要很快掌握關鍵事實。docs/product/triaige.md §2 列出這個流程出問題的地方:

參考表單是救護紀錄表與 TTAS 判定標準,兩者都是公開的政府文件(docs/references/、README.md)。

產品的定位很明確:AI 只負責擷取、整理與提出建議,凡是有公開規則可循的臨床決定,都由確定性規則處理(docs/product/triaige.md §3.3)。AI 不設定檢傷級數;級數由護理師送出,改動建議級數時要附上理由(docs/api/data-model.md §10)。鑑別診斷、急救責任醫院分級與 AI 整理的摘要,都只是供人參考的選項,不是診斷,也不是醫囑(server/src/triaige_server/llm/prompt_sets/ems_compact/differential.py、docs/api/openapi.yaml 的 AiHandoffSummary)。

3. 系統從頭到尾做了哪些事?

flowchart LR
  EMT["救護員平板:救護端 app"] -->|"一句話 = 一則訊息"| API["DGX Spark 上的 FastAPI 伺服器"]
  API --> SCH["排程器:同時最多 3 個模型呼叫,分 P0、P1、P2、閒置四級"]
  SCH --> NAT["NeMo Agent Toolkit 工作流程"]
  NAT --> LLM["透過 vLLM 執行的 Nemotron 3 Super"]
  LLM --> CHK["確定性檢查"]
  CHK --> REC["PostgreSQL 裡的救護紀錄:每個欄位的來源、證據、歷程"]
  REC -->|"推播事件"| EMT
  REC -->|"送出"| NURSE["急診檢傷護理師:預先填好的評估表"]
  NURSE --> TTAS["TTAS 規則引擎:建議級數與理由"]
  TTAS -->|"護理師決定並送出"| DOC["醫師端:規則版摘要、AI 整理、鑑別診斷"]
  NAT -.->|"鑑別診斷 agent 搜尋"| KB[("知識庫 kb_passages")]
  KB --> NAT
圖表載入中…
  1. 接收輸入。 救護員一次送出一句話,並標上說話者(救護員、病人、旁人)。伺服器先存下這句話、立刻回應,再在背景執行 AI,所以畫面不必等模型(docs/specs/2026-10-13-backend/design.md〈Extraction flow〉)。
  2. 擷取欄位。 每一句只呼叫一次模型(現場擷取關閉推理),回傳 12 個紀錄欄位的 JSON:年齡、性別、主訴、體溫、心跳、呼吸速率、SpO₂、收縮壓與舒張壓、疼痛、過去病史、重點用藥類別,以及藥物過敏(docs/specs/2026-10-13-backend/spec.md §1.3、docs/api/field-mapping.md §6)。每一句的提示詞會帶上最多三句前文,以及紀錄上已有的內容,所以「不對,是一百三」能更正到正確的那個值(eval/README.md〈R1 上下文(X1)〉)。
  3. 在欄位上複查,不用阻斷式對話框。 救護員、病人與旁人說的內容走同一套寫入規則:寫進空欄位,或取代還沒複查的值。這個欄位會標成「待複查」,附上來源句與說話者,救護員可以確認、修改、改選另一個候選值、清除或復原。人填的值或已確認的值不會被覆蓋,AI 給的不同值以替代選項列在旁邊。說話者辨識信心低的救護員句子也照同一套規則寫入,並標明來源。缺漏的必填欄位與 TTAS 參考級數固定顯示在一個區域,不用彈出視窗(docs/api/data-model.md §2、server/src/triaige_server/engine/record.py、docs/specs/2026-10-14-inline-review/plan.md)。
  4. 救護端的背景建議。 兩種建議都是補充:失敗只記稽核,不擋任何操作,也不算「AI 暫停」。
    • 建議追問。 對話停了 8 秒、而且擷取都結束,伺服器讀一次案件,請模型列出救護員還該問的事(發病時間、疼痛分數、過去病史、過敏、是否懷孕、血糖)。每個頻道最多同時開 3 題;欄位已有值的不開,忽略過的不會再回來。問題放進既有的建議清單,不彈出、不發聲(server/src/triaige_server/ai/followups.py、engine/followups.py、task_rules/followup_checks.py)。
    • 急救責任醫院分級。 紀錄表欄位變動後,模型讀已填欄位,建議重度、中度或一般急救責任醫院,並附一句理由與引用的欄位;欄位標「待複查」,救護員可以改。臨床下限由規則加上:到院前檢傷 1–2 級、危急個案、心電圖疑似 STEMI 任一成立時,模型建議的「一般」一律改成「中度」。這個欄位只給救護端參考,不進送出的快照,急診看不到(server/src/triaige_server/engine/hospital_level.py、task_rules/hospital_level_checks.py)。
  5. 檢傷。 紀錄送出後,護理師看到院前紀錄已預先填入檢傷表;救護端沒人確認過的欄位會標成「未複查」,並附上來源。TTAS 引擎算出建議級數,每一項理由都附上觸發的規則,需要人判斷的條件(例如「有沒有休克?」)會列出來,引擎不替人決定(docs/api/data-model.md §10、server/src/triaige_server/engine/ttas.py)。護理師可以在檢傷畫面打一段話:一次模型呼叫把它擷取成檢傷欄位的提案,掛在那則訊息上,護理師按「套用」才寫入;只提案白名單欄位與合法的值,數值必須是原文裡寫出的數字,護理師已經填的值不會被蓋掉。護理師端也有建議追問,例如 TOCC 還沒問的題目(server/src/triaige_server/ai/nurse_notes.py、engine/nurse_notes.py、task_rules/nurse_note_checks.py)。
  6. 醫師摘要。 目前狀況、院前經過與危險徵象,由規則從紀錄與檢傷資料組出,每一句都附上來源欄位的參照(server/src/triaige_server/engine/views.py)。規則版旁邊多一塊「AI 整理」:救護端送出與檢傷送出之後,模型把送出的紀錄表、備忘與檢傷評估改寫成最多 6 句,每句不超過 160 字,並附來源。確定性檢查會丟掉來源不存在或沒有內容的句子、數字不在引用欄位裡的句子,以及含診斷、治療或劑量用語的句子。呼叫失敗或檢查不過時,畫面照實標示失敗,規則版照常顯示;不重試,也沒有備援模型(server/src/triaige_server/engine/ai_summary.py、task_rules/summary_checks.py)。
  7. 鑑別診斷。 案件送出(之後內容有變的再送出也算)與檢傷送出之後,後端在背景執行一個會呼叫工具的 agent(NeMo Agent Toolkit 的串流函式,在同一個行程內執行)。它讀的是案件事實:最新送出的紀錄、檢傷評估、已確認的身分與院內既往資料裡填了值的欄位,每個欄位有一個編號(f1、f2…)。模型開推理,最多搜尋 4 次知識庫(工具 search_knowledge,可限定來源,每次回傳 1–8 段,每段給模型最多 1,800 字),讀完段落後回答兩份清單:常見的前 5 項,以及最致命的前 5 項(漏掉會致命或致殘,即使可能性低也要列)。每一項包含診斷名稱、支持的事實、不符合或還缺的資料、2–4 項建議檢查,並用事實編號與段落編號標出依據(server/src/triaige_server/ai/nat_differential.py、llm/prompt_sets/ems_compact/differential.py)。
    • 確定性檢查(task_rules/differential_checks.py):事實編號必須對得到案件裡填了值的欄位,對不到的拿掉,一個都不剩的項目整個丟掉;段落編號只認這一次搜尋真的回傳過的,並換成「來源、章節、頁碼」的出處字串,書中原文不會進輸出;每份清單最多 5 項,重複的診斷丟掉;診斷名稱含百分比,或文字裡出現機率、信心分數的項目整個丟掉;任何一份清單沒有合格的項目,整次就失敗。失敗時畫面明說,不重試,也沒有備援模型。
    • 排程。 優先級是最低的「閒置」:只在沒有任何其他 AI 工作時才開始,即時句子一到就取消、排回隊伍重跑;等了 60 秒或被取消 3 次,就升成一般背景工作。一次執行上限 600 秒(server/src/triaige_server/ai/scheduler.py、ai/service.py、llm/profiles/nemotron_3_super/__init__.py)。
    • 耗時。 約 1–3 分鐘(推理開啟;實測 70–110 秒,正式機 UAT 一次約 2.5 分鐘)。
    • 醫師看到什麼。 每一步(搜尋了什麼、讀到哪些來源、章節與頁碼,最後整理成清單)即時經 SSE 事件 differentials.updated 推給醫師,護理師與救護員收不到。「看診區」轉場會逐步列出這些步驟;整理中或失敗時,醫師摘要仍保留上一次通過檢查的結果,並標明狀態(server/src/triaige_server/engine/differentials.py、docs/api/asyncapi.yaml、web/src/hooks/use-differentials-progress.ts)。

救護端與急診端的畫面共用同一組元件。根路徑連真伺服器,/internal 連瀏覽器內的模擬後端,兩邊的案件規則相同,並由漂移測試確保兩個引擎一致(docs/architecture/README.md §2、server/tests/unit/test_engine_parity.py)。

4. 用了哪些 <帳號> 技術?

元件用途狀態位置
Nemotron 3 Super 120B-A12B,NVFP4流程中唯一的模型。現場擷取與背景的主訴翻譯、AI 整理、建議追問、護理師輸入擷取、急救責任醫院分級都關閉推理;醫師端的鑑別診斷 agent 開推理(low_effort)執行中infra/inventory/group_vars/servers.yml、server/src/triaige_server/llm/profiles/nemotron_3_super/__init__.py、docs/architecture/README.md §6
vLLM v0.31.0(GB10 的 DGX Spark 級主機)以 OpenAI 相容 API 提供模型服務;多 token 預測(multi-token prediction)的推測解碼(3 個 token)、前綴快取,並提供鑑別診斷 agent 用的工具呼叫(--enable-auto-tool-choice --tool-call-parser qwen3_xml)執行中infra/README.md、infra/playbooks/templates/nemotron/docker-compose.yml.j2
NeMo Agent Toolkit 1.9.0每一次模型呼叫都是在 FastAPI 行程內執行的 NAT 函式,不另外起服務。鑑別診斷是其中一個 NAT 串流函式,自己跑「搜尋、讀、再搜尋」的多步驟迴圈;其他任務都是單次呼叫執行中docs/specs/2026-10-13-backend/design.md〈Implementation notes〉、server/src/triaige_server/ai/workflow.yml、ai/nat_differential.py
NeMo Guardrails(透過 NAT 的 guardrails 中介層)中介層已經包在工作流程外面。self check output 護欄已經寫好,但預設關閉,因為每次擷取要多一次模型呼叫(設計文件估計約 1–3 秒)。一直開著的安全檢查是 §6 的確定性檢查已接上,護欄關閉docs/specs/2026-10-13-backend/design.md、infra/inventory/group_vars/servers.yml(triaige_self_check_output: false)
Nemotron 3.5 Lightning 30B-A3B評估過能否當作較快的擷取模型,沒有採用(見 §7)已評測eval/README.md〈服務參數實測〉
Nemotron 3.5 Super VL 120B-A12B(只用文字)評估過能否取代 Super,沒有採用:事先宣告的標準三項都沒過(見 §5)已評測eval/results/nemotron-3-5-super-vl-2026-10-18/README.md
Nemotron embedding 模型加 pgvector沒有使用。 知識庫用 PostgreSQL 全文檢索。資料庫映像是 pgvector 版,但沒有建立向量擴充套件,kb_passages.embedding 欄位是空的;要不要加向量檢索,之後用案例比對再決定預留docs/specs/2026-10-13-backend/decision.md、server/src/triaige_server/migrations/001_init.sql
NeMo Evaluator、OpenShell 沙箱規劃在階段 2規劃中docs/specs/2026-10-13-backend/decision.md
NemoClaw沒有整合。調查後發現它還在 alpha 階段,也沒有文件說明如何接上我們自己的智慧體只在規劃docs/specs/2026-10-13-backend/research-<帳號>-stack.md

模型、伺服器與資料庫都跑在同一台 Spark 上,推論不使用任何雲端模型(docs/product/triaige.md §3.2)。

鑑別診斷用的知識庫

知識庫是 PostgreSQL 的 kb_passages 表,共 3,714 段,來自三個來源。檢索是 PostgreSQL 全文檢索:英文用 english 設定的 tsv(標題權重高於內文),中文沒有空白斷詞,匯入與查詢時都切成相鄰兩字,存進 tsv_zh;查詢字串含中文字就用 tsv_zh,否則用 tsv;排序用 ts_rank_cd。agent 只依賴 KnowledgeBase.search 這個介面,之後換成向量檢索,agent 與提示詞都不用改(server/src/triaige_server/kb.py、migrations/003_kb_search.sql)。三份索引先在開發機用 eval/scripts/*_rag.py 建好,再由 server/scripts/export_kb.py 合成一個檔案,部署時匯入 Spark 上的資料庫(-e triaige_kb_import=true,infra/README.md〈知識庫〉)。檔案含版權內容,只放 private/,不進 git。

來源版本與出版收錄範圍段落數使用依據
Tintinalli's Emergency Medicine Manual第 7 版,McGraw-Hill,201222 篇 188 章中的 167 章;第 9 篇兒科 21 章不收,因為示範的病人都是成人1,745(兒科另有 255 段,不匯入)示範用。版權頁不允許未經同意存入檢索系統,授權還沒有解決。畫面與輸出只顯示來源、章節、頁碼,不顯示原文。正式使用要換成醫院自行取得授權的知識庫(kb.py 的介面可換實作)。出版年是 2012,提示詞要求與較新的指南不一致時以指南為準
開放授權的臨床指引與共識(PubMed Central,PMC)31 份,期刊年份 2013–2025只收 PMC 全文的 JATS <license> 是 Creative Commons 的指引;非 CC 的拒收。依章節切段,含摘要、內文與表格1,688授權:CC BY 15 份、CC BY-NC 11 份、CC BY-NC-ND 5 份,所以其中 16 份只能非商業使用,不得改作後散布。每段記 PMCID 與授權,出處格式為「簡稱(期刊 年份,PMCID,授權),§ 章節」。全文放 private/,repo 只放來源清單
臺北市政府緊急救護技術員到院前緊急救護作業程序手冊第六版(封面 115 年 4 月),臺北市政府消防局醫療顧問委員會總編輯,臺北市政府消防局出版;PDF 140 頁全書:通用程序(G)、非外傷與外傷各程序(M、T)、特殊情境(S)、技術(A)、附錄與前言,依標題與編號步驟切段,流程圖與表格盡量整塊281臺北市的作業程序,不是全國規範。版權屬臺北市政府消防局,版權頁寫「欲參採或引用請事先書面申請」,沒有公開授權;我們還沒有取得書面同意,而建索引本身就算參採(eval/README.md),全文現在也在資料庫裡,這個授權問題尚未解決(docs/team-discussion-todo.md A16)。使用手冊裡的切點數字(呼吸 ≥ 30、血糖 < 60 或 ≥ 500)為生命徵象標色,使用者 2026-10-20 決定不算引用

31 份指引的清單。 完整清單(PMCID、授權)在 private/guidelines/fetched.json(不進 git);repo 裡有候選來源清單 eval/guidelines/sources.json(簡稱、PMCID、主題)。依主題:

(以上資料來源與數字:eval/README.md〈Tintinalli 檢索(RAG)〉、〈開放指南索引〉、〈臺北市到院前救護手冊檢索〉,docs/references/ems-protocol-taipei/README.md,段落數由 server/scripts/export_kb.py 匯出的檔案逐來源計數。)

5. 模型換版時,程式怎麼不受影響?

模型更新得很快,為某一個模型做的調整,不該分散在整個程式裡。現行模型的所有設定都集中在套件 server/src/triaige_server/llm/ 的 ModelProfile 物件裡,包括提示詞、取樣參數、怎麼關閉推理、怎麼把模型的回覆整理成一個 JSON 物件、怎麼讀取對數機率、逾時,以及同時請求數(docs/architecture/README.md §6)。

flowchart TB
  API["api:路由、推播事件"] --> AI["ai:排程、NAT 黏合、寫入閘門"]
  API --> ENG["engine:案件規則、TTAS"]
  AI --> ENG
  AI --> TR["task_rules:合格答案長什麼樣"]
  AI --> LLM["llm:ModelProfile、提示詞、回覆清理、對數機率評分"]
  AI --> KB["kb:知識庫檢索"]
  ENG --> TR
  LLM --> TR
  INFRA["infra:模型部署手冊與變數"] -.-> LLM
圖表載入中…

6. 安全上做了哪些設計?

機制做法位置
由人決定AI 的值是標成「待複查」的草稿。人填的值或已確認的值不會被覆蓋。最終檢傷級數只能由護理師送出,不採用建議級數時要附上理由。鑑別診斷、AI 整理的摘要、急救責任醫院分級與建議追問,都是供人參考的建議docs/api/data-model.md §2、§10
每個模型回覆都經過確定性檢查每一種 AI 任務有自己的檢查:欄位擷取、AI 整理、鑑別診斷、急救責任醫院分級、建議追問、護理師輸入擷取。輸出必須是 JSON 物件;未知欄位、超出範圍的生命徵象、不合法的選項值、來源不存在的句子都會整個丟掉,不猜測。模型層只做兩件格式上的容錯:取出 JSON 物件,以及鑑別診斷的回覆裡事實編號少一個引號時補上server/src/triaige_server/task_rules/(record_checks.py、summary_checks.py、differential_checks.py、hospital_level_checks.py、followup_checks.py、nurse_note_checks.py)、llm/prompt_sets/ems_compact/differential.py(repair_reply)、docs/architecture/README.md §7
「無過敏」要有依據來源句裡沒有提到過敏的字眼時,「無過敏」與「未知」都會被丟掉,因為錯誤的「無」會關掉缺漏過敏的提醒。只提到非藥物過敏的句子,不會勾選「無過敏」server/src/triaige_server/task_rules/record_checks.py(模組 docstring)、eval/README.md〈R1 三項待決〉
問句不是陳述「他有沒有藥物過敏?」不能變成資料。我們在提示詞裡用範例修正,再用測試固定下來。另外有可選的路由分類器與寫入閘門,把可疑的句子改成待確認,不直接寫入;分類器預設關閉eval/README.md〈後續:問句不是陳述〉、eval/routing/README.md、docs/api/README.md §6
失敗就明確失敗,不默默降級每一個 LLM 角色的 NAT 設定都是 num_retries = 1,也就是總共只嘗試一次(NAT 預設會對 429 與 5xx 自動重試,這裡關掉),沒有備援模型。呼叫失敗會以 assistant.failed 事件結束,手動編輯照常可用;連續失敗後,畫面顯示「AI 暫停」。AI 整理與鑑別診斷失敗時,畫面明說失敗,規則版摘要照常。設定了不認得的 profile id,或向不能回傳對數機率的 profile 要求對數機率,伺服器在啟動時就會停止server/src/triaige_server/llm/nat_config.py、docs/architecture/README.md §6、§10、server/src/triaige_server/ai/service.py
鑑別診斷只引用,不複製每一項附事實編號與段落編號,編號換回案件欄位與「來源、章節、頁碼」的出處字串;書中原文不進輸出,不接受機率或信心分數;優先級最低,不會拖慢即時擷取。選項是供醫師考慮的,提示詞明寫「不是診斷或醫囑」server/src/triaige_server/task_rules/differential_checks.py、llm/prompt_sets/ems_compact/differential.py
急救責任醫院的臨床下限模型可以建議,規則決定下限:到院前檢傷 1–2 級、危急個案、疑似 STEMI 時,不會停在「一般」。人工或已複查的值,AI 之後只會並列另一個值,不會改server/src/triaige_server/engine/hospital_level.py
翻譯檢查紀錄保留英文主訴,繁體中文翻譯只是並列顯示,標成「AI 翻譯」。譯文能不能顯示,由一組確定性檢查決定:左右側、身體部位、沒有多出數字、簡體字。依使用者的決定,主訴只要含數字或明確的次數,就只顯示英文server/src/triaige_server/task_rules/complaint_guard.py、docs/api/README.md §6、eval/complaint_zh/README.md
用對數機率計算信心對選項欄位(性別、過敏狀態、病史項目),伺服器沿著模型實際寫出的 token,把機率相乘,再乘上「這個欄位該不該出現」的判斷點。如果 token 拼不回模型的回覆,就不給分數,並記錄這次失敗,不隱藏。分數目前只儲存,不顯示(§9)docs/specs/2026-10-14-inline-review/plan.md §4.3、§4.7、server/src/triaige_server/llm/choice_scoring.py
稽核紀錄每一次寫入、複查操作、AI 失敗與檢傷送出,都是一筆稽核事件,AI 的動作記 AI 名稱與提示詞版本;鑑別診斷另記搜尋次數與被丟掉的項目。每個欄位保留來源(人工、AI、已確認)、證據句與歷程。急診看得到誰在什麼時候改了哪個救護欄位,但看不到草稿內容server/src/triaige_server/engine/views.py(audit_for_ed)、engine/differentials.py、docs/api/data-model.md
看得到什麼救護員看自己建立的案件;護理師看可處理醫院的已送出案件;醫師看已完成檢傷的案件,鑑別診斷的即時事件也只推給醫師。示範用的護理師與醫師可以處理三家醫院server/src/triaige_server/auth.py(can_see)、migrations/004_user_hospitals.sql
部署防護有期限的簽章 token(預設 12 小時);請求大小上限(256 KiB);日誌不記錄 query string;模型端點只接受本機與 tailnet 的連線;後端只聽本機,對外只經 Cloudflare Tunnel;維運儀表板的公開版是唯讀的,沒有測試面板,不顯示指令列與案件編號docs/specs/2026-10-13-backend/design.md〈公開上線的加固〉、infra/README.md
資料最小化API 只接收文字,音訊依設計留在平板上。展示用的病人是虛構的,真實病人資料只能放在 git 忽略的 private/ 資料夾。公開的維運頁會顯示 LLM 輸入輸出的摘錄,所以示範輸入不放真實病患資料docs/api/README.md §6、README.md、infra/README.md

還不能算是安全檢查的部分:Guardrails 自我檢查護欄(關閉),以及信心等級的顯示(關閉)。

7. 評測結果與限制

用的是什麼資料?

請先讀這一段。eval/README.md〈誠實規則〉要求我們這樣描述資料:

量到了什麼?

問題結果來源
整段口述擷取,現行設定(vLLM v0.31.0、精簡輸出、關閉推理)12 個欄位的 Macro-F1 為 0.954(132 段口述,seed 1)與 0.957(133 段,seed 2)。幻覺欄位是 1127 個中 4 個、1147 個中 4 個。呼叫時間中位數 2.89 秒,平均輸出 71 個 tokeneval/README.md〈輸出格式與 vLLM 版本〉;eval/results/serving-2026-10-12/predictions-super-v031-compact-draft.report.json、…-seed2.report.json、…metrics.json
各欄位在 seed 1 的報告裡,11 個結構化欄位落在 0.984 到 1.000。chief_complaint 是 0.493,因為字面比對只是下界,有 78 筆等待人工判讀,它也拉低了 macro 平均同一份 …-draft.report.json、eval/README.md
透過真後端逐句處理,47 段保留集口述(163 句)11 個結構化欄位 F1 1.000,幻覺欄位 401 個中 0 個,AI 失敗或逾時 0 次,每句中位數 1.42 秒、p95 2.08 秒eval/results/r1-context-fix-2026-10-14/yardstick-replay.txt、replay-holdout-cand.metrics.json、eval/README.md
推理開或關(擷取)關閉:中位數 4.2 秒,F1 0.956。完整推理:中位數 24.9 秒,F1 0.954。在擷取這個任務上,推理沒有提高準確度,所以現場擷取關閉推理。鑑別診斷是開放式任務,沒有做開關推理的準確度比較;它開推理,這是使用者 2026-10-22 的決定eval/README.md〈推理強度〉、server/src/triaige_server/llm/profiles/nemotron_3_super/__init__.py
精簡 JSON 輸出和完整格式相比,延遲低約 31%、輸出 token 少 40%,F1 與幻覺不變eval/README.md〈輸出格式與 vLLM 版本〉
為了更正帶上前文(70 句的合成資料集,沒有臨床人員審閱)保留集 34 句:更正召回率 0.909 → 1.000;沒有指名欄位的更正,3/9 → 8/9;非預期的欄位 7 → 1。這裡評的是模型對最後一句的輸出,不是合併後的紀錄。用事先宣告的 yardstick 來看,47 段保留集口述沒有變差eval/results/r1-context-fix-2026-10-14/corrections-holdout-verdict.txt、eval/README.md
藥物過敏與否定(71 句,標準答案由工程師草擬,等待醫師審閱)只靠提示詞擋不住錯誤的「無」:3 次整段口述中有 2 次,對一個從沒提到過敏的案例寫了「無」。這說明依據檢查必須用確定性的程式,不能只寫在提示詞裡。在 24 句保留集上:狀態準確率 1.000,錯誤的「無」為零,藥名召回率 1.000。這個資料集太小,不能算是驗證eval/README.md〈R1 採用標準與重新判定〉、〈後續:問句不是陳述〉
問句被寫進紀錄仍產生欄位的非紀錄句:15 句中 6 句 → 15 句中 0 句(保留集)。加上可選的寫入閘門做端到端測試,49 句中 17 句 → 49 句中 1 句eval/README.md〈後續:問句不是陳述〉、eval/routing/README.md
路由分類器的召回率(判斷句子屬於紀錄內容)0.981(51/52),目標是 0.99,沒有達標。漏掉的那一句被寫入閘門攔下,變成一張待確認卡eval/routing/README.md
主訴翻譯檢查(工程師草擬的檢查,等待臨床人員審閱)在 69 個從未用於調整的新主訴上,63 個通過(91.3%);左右側 18/18 保留,沒有漏掉數字或項目;luq/rlq 被誤擋 2 次。之後又收緊了好幾輪,已提交的結果中 258 組不同的英文/中文配對,有 206 組通過現行檢查,其餘只顯示英文eval/complaint_zh/README.md
要求回傳對數機率的成本保留集 163 句,單一請求:中位數 ×1.007,A/A 對照是 ×1.004,量不到成本eval/results/choice-confidence-latency-2026-10-14/README.md
選項欄位的信心門檻校準(2026-10-18)事先宣告的精確度目標 0.97。在保留集上,性別 60 個值 0 錯,過敏狀態 45 個「高」中 1 錯,過去病史 88 個「高」中 1 錯,三個欄位都達標。但校準出的門檻 τ 把校準集上所有的值都分成「高」,其中 7 個已知錯誤的值也被分成「高」,等於分不出差別,所以等級維持關閉eval/results/choice-confidence-2026-10-18/summary.md
同時模型呼叫數(第一台 Spark,2026-10-19)3 條同時呼叫跑 30 分鐘:CUDA 錯誤、逾時、重啟都是 0,擷取 p95 3.89 秒。欄位一致率 97.7%,和同一批訊息在 1 條呼叫時對自己重送的 97.8% 相同,所以不一致來自貪婪解碼本身的不決定性。4 條的 p95 是 4.70 秒,超過 4 秒的門檻。正式環境因此設 3docs/specs/2026-10-13-ai-routing/soak-results.md、infra/inventory/group_vars/servers.yml(triaige_ai_concurrency: 3)
服務效能單一請求的解碼速度約 31.1 tok/s(v0.31.0),v0.28.0 是 29.8。Super 在 gpu-memory-utilization 0.77 下佔 95.4 GiB,留給主機、伺服器與資料庫約 16 GiBeval/README.md〈輸出格式與 vLLM 版本〉
更快的小模型Nemotron 3.5 Lightning 30B-A3B 快約 4.3 倍(中位數 1.01 秒對 4.39 秒),F1 是 0.938 對 0.955;在只說出舒張壓的 9 個案例裡,它把舒張壓留空,所以沒有採用。它也不能和 Super 同時放在一台 Spark 上eval/README.md〈服務參數實測〉
臺北市到院前救護手冊的檢索(只在評測中量過)281 個段落、45 個自己寫的查詢:章節層級 recall@5 0.978、MRR 0.924。其中 8 個查詢的標準答案是看過結果後才更正的,所以這不是事先未經調整的分數。線上的是 PostgreSQL 全文檢索,這個分數量的是 eval/ 的 SQLite 索引,兩者不是同一份實作eval/ems_protocol/README.md

哪些還沒有量?

8. 怎麼部署?

架構是:平板或瀏覽器連到 web app(https://triaige.wayneh.tw、https://triaige-demo.vercel.app),再透過 Cloudflare Tunnel 連到 API https://triaige-api.wayneh.tw。對外服務跑在比賽現場借用的 ASUS GX10(infra/inventory/host.yml 裡的第二台主機);第一台 Spark 是評測與先前部署用的機器,這份報告的 soak 與評測結果都在那一台量。在借用的這台上,triaige-server(port 8100,記憶體上限 1536 MB)連到 PostgreSQL(port 5433,上限 2 GiB,因為知識庫的索引需要空間,其他主機預設 512 MB),以及執行 Nemotron 3 Super 的 vLLM(port 8000)(infra/inventory/group_vars/servers.yml、docs/architecture/README.md §10)。

9. 已知限制與下一步

限制:

  1. repository 裡沒有語音辨識器(§1),評測的是文字路徑。
  2. 評測資料是美國急診的案例卡,加上模型寫的口述稿,沒有臨床人員審閱(§7)。
  3. TTAS 規則集是草稿,涵蓋第一層的生命徵象調節變數與疼痛;依主訴而定的判定標準還沒有資料來源(server/src/triaige_server/engine/ttas.py)。每個級數仍由護理師決定。
  4. 階段 2 的 AI 功能(鑑別診斷、AI 整理、建議追問、護理師輸入擷取、急救責任醫院分級)沒有保留集評測,只有確定性檢查與單元測試(§7)。鑑別診斷要 1–3 分鐘,而且優先級最低:伺服器忙時會往後排,被取消就重跑,最長 600 秒;失敗時醫師看到明確的失敗原因與上一次的結果,不會有備援。
  5. 信心等級不會顯示給使用者。門檻已在保留集上校準過兩次(2026-10-17、2026-10-18),第一次保留集沒有達到精確度目標,第二次達標,但 τ 分不出差別,所以 enabled=false 維持不變(eval/results/choice-confidence-2026-10-17/、choice-confidence-2026-10-18/summary.md)。
  6. Guardrails 自我檢查與路由分類器目前關閉,因為兩者都要在以秒計的延遲預算裡,多一次模型呼叫;案件備忘整理也關閉,等保留集通過再打開。
  7. 對外服務只有一台機器,沒有備援。同時模型呼叫上限在 Spark 上是 3(程式預設是 2):舊版 vLLM(v0.28.0)在三條同時呼叫時曾出現一次原因不明的 CUDA 錯誤;v0.31.0 的 soak 沒有再出現,但不能因此排除這個問題,而且 soak 是在第一台 Spark 上跑的,借用的這台沒有重跑(docs/specs/2026-10-13-ai-routing/soak-results.md、server/src/triaige_server/llm/profiles/nemotron_3_super/__init__.py)。
  8. 紀錄裡的主訴保留英文。中文顯示採保守做法,像「up and down」這類片語,是已記錄的已知缺口(eval/complaint_zh/README.md)。
  9. 知識庫的三個來源,兩個有著作權:Tintinalli's Emergency Medicine Manual(第 7 版)與臺北市到院前救護手冊。它們不進版本控制,但全文現在放在 Spark 的資料庫裡,供鑑別診斷搜尋;兩者的授權問題都還沒有解決(§4)。另有 16 份指引的 Creative Commons 授權是非商業或禁止改作。
  10. 提示詞與對數機率 fixture 是 Nemotron 3 Super 專用的(§5)。
  11. 知識庫共 3,714 段。Tintinalli 是 2012 年版,兒科章節沒有收;臺北市手冊是臺北市的作業程序,不是全國規範。

下一步,依優先順序:

  1. 請臨床人員審閱標準答案、TTAS 規則集與翻譯檢查,並替鑑別診斷與其他階段 2 功能建立標準答案(docs/team-discussion-todo.md)。
  2. 處理知識庫的授權:向臺北市消防局取得書面同意,並把 Tintinalli 換成醫院自行取得授權的知識庫(server/src/triaige_server/kb.py 的介面可換實作)。
  3. 重新校準對數機率門檻,讓分級分得出差別,再顯示「高/需留意」等級(docs/specs/2026-10-14-inline-review/plan.md §15,C3)。
  4. 依 docs/api/README.md §6 的規格,在平板的瀏覽器內做語音辨識。
  5. 階段 2 剩下的項目:是否加向量檢索、NeMo Evaluator、沙箱(docs/specs/2026-10-13-backend/decision.md)。
  6. 依 docs/architecture/README.md §9 的步驟評估下一個模型;上一個候選(Nemotron 3.5 Super VL)沒有通過。

10. 想查細節時,去哪裡找?

問題檔案
產品構想與 MVP 範圍docs/product/triaige.md
系統分層、換模型的步驟docs/architecture/README.md(繁體中文)
後端決策與設計docs/specs/2026-10-13-backend/decision.md、design.md
欄位複查與信心設計docs/specs/2026-10-14-inline-review/plan.md
API 介面規格與資料規則docs/api/README.md、openapi.yaml、data-model.md
鑑別診斷 agent 與知識庫檢索server/src/triaige_server/ai/nat_differential.py、kb.py、server/scripts/export_kb.py
每一項評測,附事先宣告的通過標準eval/README.md
部署與維運儀表板infra/README.md