k-skill emergency-room-beds:基于 E-Gen 公开接口的韩国附近 응급실(急诊室)与 입원실(住院病床)运行状态检索实现
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
本篇围绕 k-skill 仓库中的emergency-room-beds技能与同名 npm 包展开:以用户显式提供的地点为锚点,经由 Kakao Map 公开表面把“地名文字”解析成坐标,再调用 E-Gen(韩国保健社会部/国民保险公团 응급의료정보제공)公开附近急诊室列表接口,输出距离、医院等级与一组“运行状态标志”。读完本文,你能掌握该技能的完整调用链、全部参数取值与默认值、响应字段结构(含三态布尔标志与时间戳归一化),以及其测试套件如何验证“仅报告运行标志、不报告实时病床数”这一数据边界。
一、技能定位与核心原则
emergency-room-beds属于 k-skill 的 health 类目技能(locale: ko-KR,见 emergency-room-beds/SKILL.md 与 skill.json 的 frontmatter)。文档 docs/features/emergency-room-beds.md 给出的四条核心原则是理解整个实现的出发点:
- 不做位置自动追踪。位置缺失时,第一步必须先向用户提问,且提问文案是固定的:
현재 위치를 알려주세요. 동네/역명/랜드마크/위도·경도 중 편한 형식으로 보내주시면 근처 응급실 상태를 찾아볼게요. - 数据源限定为公开表面:NEMC/E-Gen 公开页面与 E-Gen nearby 急诊室列表 endpoint。
- 能力边界明确:E-Gen nearby 列表只提供“急诊室是否运营”“住院部/病床是否运营”等运行标志(operation flags),不提供医院级的实时剩余病床数或床位使用率。
- 紧急场景兜底:无论查询结果如何,都要引导用户拨打 119 或医院代表电话确认。
这四条原则直接映射为代码行为:源码中没有定位权限调用,resolveAnchor()只接受字符串查询;bedCountLimitation常量随每次响应返回;instruction.md 的 “Done when” 一节也把“明确披露公开数据限制”列为完成条件。
二、公开数据表面与端点清单
文档“참고 표면”一节列出的五个公开表面全部可以在 packages/emergency-room-beds/README.md 中逐一对应,它们在 packages/emergency-room-beds/src/index.js 中定义为常量:
| 表面 | 用途 | 对应源码 |
|---|---|---|
NEMC 모니터링:https://dw.nemc.or.kr/nemcMonitoring/mainmgr/Main.do | 国家层面的急诊医疗监测入口(作为meta.dashboardUrl返回) | buildMeta() |
E-Gen 응급실 찾기页面:https://www.e-gen.or.kr/egen/search_emergency_room.do | 人工查阅的公开页面(作为sourceUrl/referer) | EGEN_REFERER_URL |
E-Gen nearby 列表 endpoint:https://www.e-gen.or.kr/egen/retrieve_emergency_room_list.do | 实际被 POST 调用的数据接口 | EGEN_EMERGENCY_ROOM_LIST_URL |
Kakao Map 移动端搜索:https://m.map.kakao.com/actions/searchView?q=<query> | 地名 → 候选锚点(HTML 列表) | SEARCH_VIEW_URL |
Kakao Map 地点面板 JSON:https://place-api.map.kakao.com/places/panel3/<confirmId> | 锚点 → 精确经纬度(JSON) | PLACE_PANEL_URL_BASE |
需要注意这些端点是第三方公开表面,可能延迟、失败、限速或返回不完整数据;packages/emergency-room-beds/README.md 明确说明其输出“不构成医疗建议(not medical advice)”。
三、快速上手:官方 Node.js 示例
以下是原文档中给出的完整用法(emergency-room-beds包要求 Node.js >= 18,见 package.json 的engines字段):
const { searchNearbyEmergencyRoomsByLocationQuery } = require("emergency-room-beds"); async function main() { const result = await searchNearbyEmergencyRoomsByLocationQuery("광화문", { limit: 3, radius: 5 }); console.log(result.anchor); console.log(result.items.map((item) => ({ name: item.name, distanceKm: item.distanceKm, emergencyRoomOperating: item.bedStatus.emergencyRoomOperating, inpatientBedsOperating: item.bedStatus.inpatientBedsOperating, updatedAt: item.updatedAt, phone: item.phone, mapUrl: item.mapUrl }))); console.log(result.meta.bedCountLimitation); } main().catch((error) => { console.error(error); process.exitCode = 1; });运行结果为三部分组成:anchor(解析出的位置锚点)、items(按距离升序截断到limit的医院条目)、meta(总数、来源与限制说明)。其中meta.bedCountLimitation固定输出:
E-Gen nearby ER list exposes operation flags, not exact real-time remaining bed counts.该常量定义于 src/index.js,并在 test/index.test.js 中被断言为逐字一致,防止措辞在迭代中漂移。
在 Agent 侧(k-skill CLI),获取该技能的完整指令的方式是:
npx -y @nomadamas/k-skill@0 instruct emergency-room-beds npx -y @nomadamas/k-skill@0 files emergency-room-beds四、参数详解:取值范围、默认值与上游字段映射
searchNearbyEmergencyRoomsByLocationQuery(locationQuery, options)与searchNearbyEmergencyRoomsByCoordinates(options)接受同一套 options。结合 src/index.js 中的校验函数normalizeBoundedInteger/normalizeCoordinates/normalizeOrder与测试用例 test/index.test.js,各参数约束如下:
| 参数 | 默认值 | 合法范围 | 说明(对应上游表单字段) |
|---|---|---|---|
locationQuery | 必填(第一个位置参数) | 字符串 | 地名;若形如37.573713, 126.978338(逗号/斜杠/空格分隔,且通过经纬度范围校验)则由parseCoordinateQuery直接解析为坐标,跳过 Kakao 锚点解析 |
latitude/longitude(或lat/lon) | 由锚点解析得到 | -90~90 / -180~180 | 必须为有限数值,否则抛出latitude and longitude must be finite numbers.等错误 |
limit | 5 | 1~50 | 返回条目上限,超出范围抛limit must be between 1 and 50. |
radius(或maxDistanceKm) | 3 | 1~50 | 请求 E-Gen 的搜索半径(km),同时用于客户端二次过滤(item.distanceKm <= radius) |
order | distance | distance|accuracy | E-Gen 排序方式,非法值抛错 |
currentPageNum(或pageNo) | 1 | 1~1000 | 分页页码 |
emergencyGradeCodes(或emoggrdcStr) | 空 | 字符串或字符串数组 | 急诊医疗机构等级代码过滤,数组会被 join 为逗号串(如["A","C"]→A,C) |
silson24 | false | 布尔 | 是否只看 119联动机构,映射为表单值Y/N |
hospitalName(或emogdesc) | 空 | 字符串 | 医院名关键字 |
apiBaseUrl | E-Gen 端点 | URL | 用于测试时替换上游地址 |
fetchImpl | 全局fetch | 函数 | 可注入的 fetch 实现,是全部离线测试的基础 |
上述请求组装逻辑集中在buildEmergencyRoomListRequest()(src/index.js):以URLSearchParams构造表单体lat、lon、emoggrdcStr、silson24、emogdesc、radius、order、currentPageNum,以POST方法发往 E-Gen 端点。测试buildEmergencyRoomListRequest targets E-Gen's public nearby ER endpoint(test/index.test.js)断言了每个字段的逐一对应关系。
此外,为了让请求在真实浏览器语义下通过,源码为三类请求准备了不同的请求头(src/index.js):
DEFAULT_BROWSER_HEADERS:Kakao 搜索 HTML 页面,含 Chrome UA 与ko优先的accept-language;DEFAULT_PANEL_HEADERS:Kakao 面板 JSON,追加origin: place.map.kakao.com、pf: PC、appVersion;DEFAULT_JSON_HEADERS:E-Gen 表单 POST,追加origin: www.e-gen.or.kr、referer指向急诊室搜索页、x-requested-with: XMLHttpRequest,模拟站点自身的 AJAX 行为。
五、调用链解析:从地名到急诊室列表
从源码结构看(src/index.js 的searchNearbyEmergencyRoomsByLocationQuery),完整调用链为:
locationQuery ├─ parseCoordinateQuery() 命中“纬度,经度”文本 → 直接进入坐标分支 └─ resolveAnchor() 1. GET m.map.kakao.com/actions/searchView?q=<query> (HTML 文本) 2. parseSearchResultsHtml() 提取 <li class="search_item base"> 的 >{ anchor: { id, name, category, address, phone, latitude, longitude, sourceUrl }, items: [ <医院条目> ... ], // 已按 distanceKm 升序,截断到 limit meta: { total, upstreamTotal, limit, radius, source: "e-gen", sourceUrl, dashboardUrl, bedCountLimitation, anchorCandidates? } }meta.anchorCandidates仅在走地名查询分支时出现(见 src/index.js),值为候选数量;直接给坐标时,anchor退化为{ name: options.anchorName || "입력 좌표", address, latitude, longitude }。
6.2 医院条目字段
对应文档“응답 필드”一节的完整字段清单,均来自normalizeEmergencyRoomRows()的映射(src/parse.js):
| 字段 | 上游字段 | 说明 |
|---|---|---|
id/name | EMOGCODE/TITLE | 机构代码与医院名(无TITLE的条目被丢弃) |
emergencyGrade/hospitalType | CATEGORY1/CATEGORY2 | 如“지역응급의료센터”“권역응급의료센터”与“상급종합병원” |
address/phone | ADDRROAD(回退ADDRLAGE)/TEL | 道路地址与代表电话 |
latitude/longitude/distanceKm | LAT/LON/DISTANCE2 | 距离四舍五入到千分之一千米 |
bedStatus.emergencyRoomOperating | EMOGERYN | 急诊室运营与否 |
bedStatus.inpatientBedsOperating | EMOGPRYN | 住院部/病床运营与否 |
bedStatus.traumaCenter | EMOGTRYN | 是否区域创伤中心(권역외상센터) |
bedStatus.pediatricSpecialty | CHILD_SPCLTY_AT | 是否小专科门 |
bedStatus.currentGeneralCareAvailable | OPERATIONYN | 当前一般诊疗可用与否 |
bedStatus.pediatricNightCare | NIGHTCAREYN | 小儿夜间诊疗与否 |
bedStatus.holidayOpen | HOLIDAYYN | 假日开放与否 |
bedStatus.silson24Linked | SILSON24_CHK | 是否 119(SILSON24)联动机构 |
schedules.monday..sunday / holiday / note | MONDAY…SUNDAY/HOLIDAY/OPN_BIGO | 各科室(急诊/入馆等)时间窗文本 |
updatedAt | EMOGUPDT | 20260311142633→2026-03-11T14:26:33+09:00(KST) |
sourceUrl | — | 固定为 E-Gen 搜索页 |
mapUrl | — | Kakao Map 分享链接,格式https://map.kakao.com/link/map/<编码名>,lat,lon |
三态布尔是关键设计。toBooleanYesNo()(src/parse.js)把上游Y/N映射为true/false,其余任意值(空串、null、未知串)一律映射为null。README.md 明确指出这是有意为之的三态语义:上游省略或变更标志值时,结果表达为“未知”而非误判为关闭。测试normalizeEmergencyRoomRows preserves unknown operation flags as null(test/index.test.js)专门验证EMOGERYN: ""、EMOGPRYN: "UNKNOWN"都得到null。这正是文档“공개 데이터 한계 문구”原则的落地:宁可说“未知”,也不把缺失当成“未运营”。
6.3 时间戳与真实数据样本
parseEgenTimestamp()只接受yyyyMMddHHmmss形态并附加+09:00时区。仓库测试夹具 test/fixtures/emergency-room-list.json 提供了一份贴近真实 E-Gen 返回形态的样本(含list+paging.totalCount两种负载结构的兼容解析getEmergencyRoomRows):其中강북삼성병원 条目EMOGERYN: "Y"、EMOGPRYN: "Y"对应emergencyRoomOperating: true、inpatientBedsOperating: true,而EMOGTRYN: null对应traumaCenter: null——三个来源字段、三种取值形态在夹具中同时出现,测试断言(test/index.test.js)逐字段核对。
七、输入校验与失败模式
包对输入采用“早失败(fail fast)”策略,全部由 test/index.test.js 的...validates bounded inputs用例覆盖:
- 经纬度非有限数值 →
latitude and longitude must be finite numbers. latitude: 91→latitude must be between -90 and 90.;longitude: 181同理limit: 0→limit must be between 1 and 50.;radius: 0→radius must be between 1 and 50.- E-Gen 返回既非数组也非
{ list: [...] }的负载 →Unexpected E-Gen emergency room payload shape.(防止接口改版时被静默消费) - Kakao 面板 429 限速 → 携带
status=429与url的错误直接上抛,不会降级
请求层的错误处理在request()(src/index.js)中统一完成:非 2xx 响应抛出附带status与url的 Error,调用方(如锚点解析循环)可据此区分“可跳过的失效地点”与“必须中断的故障”。
八、Agent 工作流与完成标准
emergency-room-beds/instruction.md 定义了技能面向 Agent 的完整工作流,与文档“사용 예”一节一致:
- 确认用户当前位置(缺失时先问,且只问一次固定文案);
- 调用
searchNearbyEmergencyRoomsByLocationQuery(); - 通常按距离序整理 3~5 家以内;
- 必须声明“这是公开 E-Gen nearby 列表结果,精确的剩余病床数/使用率不提供”;
- 紧急情况附加 119 或医院电话确认引导。
其 “Done when” 清单可作为自动化验收条件:锚点已确认、找到了医院或给出了未找到的原因与下一步扩大范围的建议、披露了数据限制、紧急情况包含 119/电话引导。
九、仓库内的相关文件索引
| 路径 | 内容 |
|---|---|
| docs/features/emergency-room-beds.md | 本文主体所依据的功能文档(核心原则、示例、字段与参考表面) |
| emergency-room-beds/SKILL.md | 技能入口(CLI 指令获取方式与硬性安全规则) |
| emergency-room-beds/instruction.md | Agent 工作流、完成标准、公开表面清单 |
| emergency-room-beds/skill.json | 技能元数据与 frontmatter |
| packages/emergency-room-beds/src/index.js | 端点常量、请求头、参数校验、锚点解析与主 API |
| packages/emergency-room-beds/src/parse.js | Kakao HTML/面板解析、打分排序、E-Gen 行归一化、Haversine、时间戳解析 |
| packages/emergency-room-beds/test/index.test.js | 注入fetchImpl的离线测试:请求构造、三态标志、半径过滤、陈旧面板跳过、429 失败、输入校验 |
| packages/emergency-room-beds/test/fixtures/ | anchor-search.html、anchor-panel.json、emergency-room-list.json三份夹具 |
| packages/emergency-room-beds/README.md | 包级说明:能力/非能力、公开 API 列表、结果字段 |
十、局限性与设计取舍小结
- 能力边界:该包刻意不回答“现在还有几张床”。
BED_COUNT_LIMITATION常量随meta返回,Agent 指令层也强制复述该限制;这与其“只做公开数据、不虚构实时数字”的原则一致。 - 数据新鲜度:
updatedAt暴露了每条记录的上游更新时间(夹具样本中两家医院的更新时间相差数周),调用方应向用户呈现该时间而不是默认“实时”。 - 依赖的脆弱性:解析逻辑深度耦合 Kakao 移动端 HTML 结构(
li.search_item.base)与 E-Gen 表单字段名;一旦上游改版,Unexpected E-Gen emergency room payload shape一类错误会显式暴露而非静默出错。 - 运行前提:Node.js >= 18(全局
fetch或注入fetchImpl);包测试可离线执行(node --test,见 package.json 的test脚本)。
综上,emergency-room-beds用一个“地名 → 锚点坐标 → 公开列表接口 → 三态运行标志”的短链路,把韩国附近急诊室查询做成了一个边界清晰、可离线测试、且持续提醒用户数据局限的检索技能。
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考