在 Keycloak 和 HAPI FHIR 中間放一層 Rails(四):讓 AI Agent 只能走受控工具
承上一篇。這篇是這系列我自己最在意的一塊,因為它直接關係到火線超人跟 AI 臨床工具會不會出包。
如果你要接 AI Agent 或 LINE Bot 去讀 FHIR 資料,最危險的做法就是讓模型自由產生查詢:
GET /Observation?...任意查詢...
只要模型被 prompt injection 帶偏,或自己組了個奇怪的參數,它就可能撈到不該撈的東西。所以我的做法是:不開放自由查詢,只開放一組受控工具(controlled tools)。
get_recent_labs
get_active_medications
get_latest_vital_signs
get_upcoming_appointments
get_encounter_summary
evaluate_diabetes_control
每個工具都有明確定義,參數範圍寫死。像這樣:
{
"name": "get_recent_labs",
"description": "Return laboratory results within an allowed period.",
"parameters": {
"type": "object",
"properties": {
"days": { "type": "integer", "minimum": 1, "maximum": 7 },
"category": { "type": "string", "enum": ["laboratory"] }
},
"required": ["days"]
}
}
參數要收斂,不能照單全收
就算工具定義寫了 maximum: 7,我在 Rails 這端還是會再收斂一次。模型送 {"days": 30} 過來,Rails 不照做:
allowed_days = [requested_days, policy.maximum_days].min
最後只會執行七天。工具定義是給模型看的提示,Rails 的收斂才是真正的閘門。這一層擋掉的東西很具體:AI 自由組合危險查詢、讀到其他病人、不受控的 _include=*、無上限的 _count、$everything、任意 transaction、prompt injection 直接轉成 FHIR API、tool 參數越權、大量 PHI 外洩。
回傳給模型的,要是最小必要資料
還有一點常被忽略:別把完整的 Observation Resource 丟給模型。原始 FHIR 資源一堆 meta、identifier、performer、extension,既浪費 token 又擴大 PHI 暴露面。我讓 Rails 先轉成最小的資料再給模型:
{
"test": "HbA1c",
"value": 7.2,
"unit": "%",
"date": "2026-07-20",
"interpretation": "high"
}
模型面對這種乾淨的結構,比直接吞一整包 FHIR Bundle 安全太多,也準太多。
這種 function tooling 特別適合火線超人、患者端 SMART App 跟 AI Agent 這類場景。它把「AI 能做什麼」限縮成一組你審查過的動作,而不是一個開放的查詢介面。下一篇我把 OPA 跟 Kafka 補進來,收整張架構圖。