Skip to content

AI

자연어로 물으면 무엇을 할지와 검색 조건을 돌려주는 API 와, MCP 엔드포인트를 제공합니다.

AI 검색POST /healthcare/ai-search — 질문 → 실행할 툴 + 검색 조건
사용량 · 모델GET /ai/capabilities — 남은 몫과 고를 수 있는 모델
MCPPOST /mcp/healthcare — 병원 검색 도구를 MCP 로 제공합니다

병원 목록이 아니라 검색 조건이 옵니다

ai-search검색을 대신 해 주지 않습니다. 질문을 읽고 조건을 잡아 줄 뿐이고, 목록은 그 조건으로 여러분이 GET /healthcare/hospitals 를 한 번 더 불러 받습니다.

그래서 이렇게 쓸 수 있습니다.

  • AI 가 무엇을 잡았는지 화면에 보여주고 사용자가 고치게 할 수 있습니다. 틀려도 다시 물을 필요 없이 조건 하나만 바꾸면 됩니다.
  • 이미 만든 목록·지도·페이징을 그대로 씁니다. AI 응답 전용 화면을 따로 만들지 않습니다.
bash
curl -X POST "https://api.plzhans.com/healthcare/ai-search" \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -H "Accept-Language: ko" \
  -d '{ "q": "소아 천식 진료하는 병원" }'
json
{
  "tool": "search_hospitals",
  "params": {
    "filter": { "subjectCds": ["PD"], "asmItemCds": ["..."], "emergency": false, "baby": false },
    "regionCd": "41450"
  },
  "conditions": [
    { "group": "subject", "names": ["소아청소년과"] },
    { "group": "assessment", "names": ["천식"] }
  ],
  "explain": "소아청소년과 중 천식 진료 평가에서 1등급을 받은 병원입니다.",
  "credits": 12000
}

tool 로 갈라 쓰세요

응답의 tool화면이 할 일입니다. 조건이 비어 있어도 할 일은 있을 수 있어서 (예: "근처 병원") 조건 개수로 판단하면 안 됩니다.

tool화면이 할 일
search_hospitalsparams.filter + params.regionCd 로 병원 검색
search_nearby측위는 화면 몫입니다. 현재 위치를 얻어 거리순으로 검색
ask_location지역을 되묻습니다. params.placeText 에 사용자가 말한 표현이 있습니다
answer_medicalparams.answer 를 그대로 보여줍니다(건강 질문에 답한 경우)
reject검색하지 않습니다. params.reason 이 이유입니다

모르는 값은 넘기세요

tool 은 닫힌 목록이지만 늘어날 수 있습니다. switch 의 기본 가지에서 조용히 무시하면 새 값이 추가돼도 화면이 깨지지 않습니다.

conditions 는 잡힌 조건을 사람이 읽는 이름으로 푼 것입니다(요청 언어로 옵니다). 칩·태그로 그대로 그리면 되고, 코드표를 따로 부를 필요가 없습니다.

대화를 이으려면 돌려주세요

서버는 대화를 기억하지 않습니다. 이어서 물으려면 직전 응답을 다음 요청에 실어 보냅니다.

  • context — 직전 응답의 params그대로 넣습니다. 지금까지 잡힌 조건이 이어집니다.
  • history — 앞선 질문과 답. 최근 몇 마디만 넣으세요.
json
{
  "q": "천안에서",
  "context": { "filter": { "subjectCds": ["PD"] } }
}

사용량

GET /ai/capabilities 로 남은 몫을 확인합니다. 아무것도 소모하지 않습니다.

  • 화면을 열 때 한 번 부르는 용도입니다. 값이 바뀌는 때는 질문한 순간뿐이고, 그때는 검색 응답이 새 값을 싣고 옵니다 — 폴링하지 마세요.
  • 사용량을 다 쓰면 검색은 503 으로 거절됩니다. 요청이 너무 잦으면 429 입니다 (둘은 안내가 달라야 합니다 — 429 는 잠시 뒤 다시, 503 은 기간이 바뀌어야 풀립니다).

응답이 늦을 수 있습니다

외부 모델을 부르므로 첫 질문은 수 초가 걸립니다(같은 질문은 캐시되어 즉시 옵니다). 타임아웃은 넉넉히 잡고, 실패하면 일반 검색으로 넘어갈 길을 남겨 두세요.

자연어 병원 검색

POST
/healthcare/ai-search

자연어 질문을 검색 조건으로 변환한다. 병원 목록은 반환하지 않는다 — 응답의 tool 로 동작을 고르고 params 를 실어 GET /healthcare/hospitals 를 호출한다.

좌표는 주고받지 않는다. 측위는 클라이언트가 하고, 서버는 위치 기준으로 검색할지 여부만 정한다.

진단·치료 조언은 하지 않는다. 병원을 고르기 위한 조건만 만든다.

Request

🔒 Bearer 인증 필요 · 인증 방법
POST/healthcare/ai-search

Request Body

application/jsonrequired

AiSearchRequest

FieldTypeRequiredConstraintsDescription
qstringrequiredmaxLen 300자연어 질문
modelstringoptional쓸 모델. 안 보내면 서버 기본 모델. 허용 목록 밖이면 거절한다.
contextAiSearchParamsoptional직전 응답의 `params` 를 그대로 돌려보낸다. 앞서 잡힌 조건을 이어받는 수단이다 — 서버는 대화를 기억하지 않는다. 새 주제를 시작하려면 빼고 보낸다. `answer`·`reason` 은 보내도 무시한다.
historyAiSearchHistoryTurn[]optional앞서 오간 말. "아까 말한 증상" 처럼 앞을 가리키는 말을 푸는 데 쓴다 (`context` 는 조건만 담아 이것까지는 못 푼다). `answer` 를 보낼 때는 그 응답의 `answerSignature` 를 `signature` 로 같이 실어야 한다. 없거나 맞지 않으면 그 턴의 `answer` 만 버리고 `question` 은 그대로 쓴다. **최근 3마디만 쓴다**(더 보내면 앞쪽부터 버린다). 한 마디의 길이도 자른다 — 질문 300자, 답 800자.

AiSearchParams

FieldTypeRequiredConstraintsDescription
filterAiSearchFilterrequired
subjectCdsstring[]required진료과목 코드(신고 기준)
specialistCdsstring[]required그 과목 전문의를 실제로 보유한 병원만 거는 진료과목 코드
asmItemCdsstring[]required적정성평가 항목 코드. 그 항목 1등급(우수) 병원만 걸린다.
specialtyCdsstring[]required전문병원 지정분야 코드
equipmentCdsstring[]required보유장비 코드
classCdsstring[]required종별 코드
tiersstring[]required병원 등급. 비면 요양병원·정신병원이 제외된다.
emergencybooleanrequired응급실 운영 병원만
babybooleanrequired달빛어린이병원만
namestringoptional병원 이름(부분 일치)
regionCdstringoptional시군구 코드(없으면 시도 코드). 검색의 `region` 파라미터다. **`search_hospitals` 에서만 채워진다.**
placeTextstringoptional사용자가 쓴 지역 표현 원문(예: "강남역"). **코드가 아니다** — `ask_location` 이 되물을 때 화면에 그대로 보여준다.
reasonstringoptionaloff_topic, medical_question, emergency_suspected, medical_caution, unsupported_inverse, tertiary_referral, too_vague`reject` 사유. `warnings` 와 같은 값 집합이다.
answerstringoptional`answer_medical` 의 본문. 다른 동작에서는 오지 않는다.

AiSearchHistoryTurn

FieldTypeRequiredConstraintsDescription
questionstringrequired사용자가 한 말
answerstringoptional그 질문에 대한 응답. 답변이 있었던 턴에만 담는다.
signaturestringoptional`answer` 에 딸려 온 서명(응답의 `answerSignature`). **`answer` 를 보낼 거면 반드시 같이 보낸다.**
{
  "q": "천식치료 소아과 병원 추천해줘",
  "model": "claude-haiku-4-5",
  "context": {
    "filter": {
      "subjectCds": [
        "string"
      ],
      "specialistCds": [
        "string"
      ],
      "asmItemCds": [
        "string"
      ],
      "specialtyCds": [
        "string"
      ],
      "equipmentCds": [
        "string"
      ],
      "classCds": [
        "string"
      ],
      "tiers": [
        "string"
      ],
      "emergency": true,
      "baby": true,
      "name": "string"
    },
    "regionCd": "41450",
    "placeText": "string",
    "reason": "off_topic",
    "answer": "string"
  },
  "history": [
    {
      "question": "무릎이 왜 아픈 거야?",
      "answer": "무릎이 아픈 원인은…",
      "signature": "X1c…"
    }
  ]
}

Response

200

AiSearchResponse

FieldTypeRequiredConstraintsDescription
toolstringrequiredsearch_hospitals, search_nearby, ask_location, answer_medical, reject클라이언트가 실행할 동작. - `search_hospitals` `params.filter`(+`regionCd`)로 병원 검색 - `search_nearby` 현재 위치 기준 거리순 검색. 측위는 클라이언트가 한다 - `ask_location` 지역을 되묻는다. `params.placeText` 에 사용자가 쓴 표현이 있다 - `answer_medical` `params.answer` 를 그대로 보여준다 - `reject` 검색하지 않는다. `params.reason` 이 사유다
paramsAiSearchParamsrequired
warningsstring[]requiredoff_topic, medical_question, emergency_suspected, medical_caution, unsupported_inverse, tertiary_referral, too_vague사용자에게 안내할 신호. - `off_topic` 병원 검색 범위 밖의 질문 - `medical_question` 건강 질문. 검색 조건으로 옮기지 않았다 - `emergency_suspected` 응급 징후. 119·응급실 안내가 필요하다 - `medical_caution` 요구가 일반적인 의학 권고와 어긋난다 - `unsupported_inverse` 검색 조건으로 표현할 수 없는 반대 조건이라 빠졌다 - `tertiary_referral` 상급종합병원은 진료의뢰서가 없으면 전액 본인 부담이다 - `too_vague` 조건을 잡기에 질문이 모호하다
explainstringrequired조건이 맞게 잡혔는지 사용자가 확인할 한 문장
droppedstring[]required검증에서 제외된 값(`subject:XX` 꼴). 비어 있는 것이 정상이다.
conditionsAiSearchCondition[]required잡힌 조건을 사람이 읽는 이름으로 푼 것. `params.filter` 의 코드와 같은 내용이라 코드표를 따로 조회할 필요가 없다. 등급·응급실처럼 코드표가 없는 값은 포함되지 않는다.
providerstringrequiredanthropic, openai, local
modelstringrequired실제로 응답한 모델. 요청에 실은 이름이 아니라 확정된 버전이다.
creditsnumberrequired이 요청이 사용한 양. `quota` 와 같은 단위라 그대로 견줄 수 있다. 같은 질문이면 언제 묻든 같은 값이다.
elapsedMsnumberrequired서버 처리 시간(ms). 네트워크 구간은 포함되지 않는다.
quotaQuotaoptional사용량. 한도가 걸려 있지 않으면 오지 않는다.
answerSignaturestringoptional`params.answer` 의 서명. 답이 있을 때만 온다. 다음 요청의 `history[].signature` 로 그대로 돌려주면 그 답이 문맥으로 이어진다.

AiSearchParams

FieldTypeRequiredConstraintsDescription
filterAiSearchFilterrequired
subjectCdsstring[]required진료과목 코드(신고 기준)
specialistCdsstring[]required그 과목 전문의를 실제로 보유한 병원만 거는 진료과목 코드
asmItemCdsstring[]required적정성평가 항목 코드. 그 항목 1등급(우수) 병원만 걸린다.
specialtyCdsstring[]required전문병원 지정분야 코드
equipmentCdsstring[]required보유장비 코드
classCdsstring[]required종별 코드
tiersstring[]required병원 등급. 비면 요양병원·정신병원이 제외된다.
emergencybooleanrequired응급실 운영 병원만
babybooleanrequired달빛어린이병원만
namestringoptional병원 이름(부분 일치)
regionCdstringoptional시군구 코드(없으면 시도 코드). 검색의 `region` 파라미터다. **`search_hospitals` 에서만 채워진다.**
placeTextstringoptional사용자가 쓴 지역 표현 원문(예: "강남역"). **코드가 아니다** — `ask_location` 이 되물을 때 화면에 그대로 보여준다.
reasonstringoptionaloff_topic, medical_question, emergency_suspected, medical_caution, unsupported_inverse, tertiary_referral, too_vague`reject` 사유. `warnings` 와 같은 값 집합이다.
answerstringoptional`answer_medical` 의 본문. 다른 동작에서는 오지 않는다.

AiSearchCondition

FieldTypeRequiredConstraintsDescription
groupstringrequiredsubject, specialist, assessment, specialty, equipment, class묶음 이름. 화면이 이 값으로 "진료과"/"장비" 같은 앞말을 고른다.
namesstring[]required사람이 읽는 이름들. **요청 언어**(Accept-Language)로 온다.

Quota

FieldTypeRequiredConstraintsDescription
appAppQuotaoptional앱 단위 사용량.
dailyQuotaWindowoptional일 단위 사용량. KST 자정에 리셋된다.
usednumberrequired사용한 양
limitnumberrequired한도
monthlyQuotaWindowoptional월 단위 사용량. 매월 1일에 리셋된다. `daily` 와 함께 걸리며, 먼저 소진되는 쪽이 적용된다.
usednumberrequired사용한 양
limitnumberrequired한도
userUserQuotaoptional사용자 단위 사용량. 로그인 상태에서만 온다.
balanceQuotaWindowoptional사용자 잔여량. 주기적으로 리셋되지 않는다.
usednumberrequired사용한 양
limitnumberrequired한도
{
  "tool": "search_hospitals",
  "params": {
    "filter": {
      "subjectCds": [
        "string"
      ],
      "specialistCds": [
        "string"
      ],
      "asmItemCds": [
        "string"
      ],
      "specialtyCds": [
        "string"
      ],
      "equipmentCds": [
        "string"
      ],
      "classCds": [
        "string"
      ],
      "tiers": [
        "string"
      ],
      "emergency": true,
      "baby": true,
      "name": "string"
    },
    "regionCd": "41450",
    "placeText": "string",
    "reason": "off_topic",
    "answer": "string"
  },
  "warnings": [
    "off_topic"
  ],
  "explain": "소아청소년과 중 천식 진료 평가가 우수한 병원입니다.",
  "dropped": [
    "string"
  ],
  "conditions": [
    {
      "group": "subject",
      "names": [
        "내과",
        "가정의학과"
      ]
    }
  ],
  "provider": "anthropic",
  "model": "string",
  "credits": 9387,
  "elapsedMs": 1840,
  "quota": {
    "app": {
      "daily": {
        "used": 21400,
        "limit": 2000000
      },
      "monthly": {
        "used": 21400,
        "limit": 2000000
      }
    },
    "user": {
      "balance": {
        "used": 21400,
        "limit": 2000000
      }
    }
  },
  "answerSignature": "string"
}

Playground

Server
Authorization
Body

Samples

Powered by VitePress OpenAPI

AI 사용량 · 모델 목록

GET
/ai/capabilities

남은 사용량과 선택할 수 있는 모델을 반환한다. 사용량을 소모하지 않는다.

AI 기능을 열 때 한 번 호출한다. 이후 값은 검색 응답(quota)에 실려 오므로 주기적으로 호출하지 않는다.

누구의 사용량인지는 자격증명이 정한다 — access token 이면 사용자, 클라이언트 키면 해당 앱의 몫이다.

quota 가 비어 오면 걸린 한도가 없다는 뜻이다. 사용량을 확인할 수 없는 상태면 503 이며, 이때는 검색도 거절되므로 AI 기능을 비활성화하는 것이 맞다.

Request

🔒 Bearer 인증 필요 · 인증 방법
GET/ai/capabilities

Response

200

CapabilitiesResponse

FieldTypeRequiredConstraintsDescription
quotaQuotaoptional사용량. 한도가 걸려 있지 않으면 오지 않는다.
modelsModelChoice[]required선택할 수 있는 모델 목록.

Quota

FieldTypeRequiredConstraintsDescription
appAppQuotaoptional앱 단위 사용량.
dailyQuotaWindowoptional일 단위 사용량. KST 자정에 리셋된다.
usednumberrequired사용한 양
limitnumberrequired한도
monthlyQuotaWindowoptional월 단위 사용량. 매월 1일에 리셋된다. `daily` 와 함께 걸리며, 먼저 소진되는 쪽이 적용된다.
usednumberrequired사용한 양
limitnumberrequired한도
userUserQuotaoptional사용자 단위 사용량. 로그인 상태에서만 온다.
balanceQuotaWindowoptional사용자 잔여량. 주기적으로 리셋되지 않는다.
usednumberrequired사용한 양
limitnumberrequired한도

ModelChoice

FieldTypeRequiredConstraintsDescription
idstringrequired모델 식별자.
lockedbooleanrequired선택할 수 없는 모델. 목록에는 표시되지만 요청에는 실리지 않는다.
{
  "quota": {
    "app": {
      "daily": {
        "used": 21400,
        "limit": 2000000
      },
      "monthly": {
        "used": 21400,
        "limit": 2000000
      }
    },
    "user": {
      "balance": {
        "used": 21400,
        "limit": 2000000
      }
    }
  },
  "models": [
    {
      "id": "claude-haiku-4-5",
      "locked": true
    }
  ]
}

Playground

Server
Authorization

Samples

Powered by VitePress OpenAPI

MCP

Model Context Protocol — Streamable HTTP 기반입니다. 인증만 우리 것이고(서비스 키), 나머지는 규약 그대로입니다.

도메인마다 엔드포인트가 하나입니다. 도구를 한 서버에 몰지 않는 이유는 도구 스키마가 매 턴 모델 컨텍스트에 실리기 때문입니다 — 쓰지 않는 도메인의 도구까지 짊어지지 않습니다.

엔드포인트도메인도구
https://api.plzhans.com/mcp/healthcare헬스케어search_hospitals · get_hospital · list_medical_codes

공통

인증서비스 키 (Authorization: Bearer sk_...)
호출 한도엔드포인트당 60초에 60회
언어모든 도구가 lang 인자를 받습니다 (ko en ja zh, 기본 ko)

코드 값은 도구 스키마에 enum 으로 실려 나갑니다 — 없는 코드는 클라이언트 단에서 걸립니다.

서비스 키로 붙습니다

MCP 클라이언트는 브라우저가 아니라 사용자 PC 에서 도는 프로그램이라 오리진 검사를 받지 않습니다. 그래서 X-Client-Id 가 아니라 서비스 키를 씁니다 — 키를 나눠 주는 것은 곧 호출 권한을 주는 것이니 배포용 키와 나눠 쓰세요.

/mcp/healthcare

search_hospitals

지역·진료과목·평가등급·응급실 여부로 검색합니다. 한 번에 최대 8건입니다.

인자타입설명
subjectCdsstring[] (최대 5)진료과목 코드. 신고 기준이라 전문의 보유와는 다릅니다
specialistCdsstring[] (최대 5)그 과목 전문의를 보유한 병원만
asmItemCdsstring[] (최대 3)적정성평가 항목. 그 항목 1등급만 걸립니다
specialtyCdsstring[] (최대 3)보건복지부 지정 전문병원 분야
tiersstring[]병원 등급. 비우면 요양병원·정신병원이 빠집니다
regionCdstring시도 또는 시군구 코드. 시도를 주면 하위 전체로 넓힙니다
namestring병원 이름(부분 일치)
emergency · babyboolean응급실 운영 / 달빛어린이병원만

get_hospital

병원 상세 — 진료시간·장비·병상·전문의 수·평가등급.

인자타입설명
idnumbersearch_hospitals 결과의 id

없는 id 는 오류가 아니라 { "found": false } 로 옵니다.

list_medical_codes

검색에 쓰는 코드의 뜻.

인자타입설명
typesubject assessment specialty tier진료과목 · 적정성평가 · 전문병원 분야 · 병원 등급

적정성평가 1등급은 항목마다 뜻이 다릅니다

주사제 처방률 1등급은 "주사를 적게 놓는다" 는 뜻입니다. asmItemCds 를 쓰기 전에 항목의 의미를 확인하세요.

더 읽을거리