k-skill 的 seoul-bike(首尔따릉이)实时租赁点查询:基于 k-skill-proxy 的无密钥 API 架构与 CLI 实战指南
2026/9/17 8:20:48 网站建设 项目流程

k-skill 的 seoul-bike(首尔따릉이)实时租赁点查询:基于 k-skill-proxy 的无密钥 API 架构与 CLI 实战指南

【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill

本篇技术指南围绕 k-skill 仓库中的seoul-bike(首尔公共自行车 따릉이)技能展开,讲解如何通过k-skill-proxy以"零客户端密钥"的方式查询首尔 따릉이 实时租赁点的可租自行车数量、空置停车桩数量,并支持按坐标半径或站名关键词检索。读完本文,你将掌握GET /v1/seoul-bike/{realtime,stations,nearby}三个代理路由的调用方式、seoul_bike.py单入口 CLI 的全部子命令与参数、代理服务端缓存的元数据结构,以及异常场景下的错误处理与测试验证方法。

这个功能能做什么

  • 查询当前坐标周边 따릉이 租赁点的可租自行车数量parkingBikeTotCnt);
  • 计算并返回空置停车桩数量rackTotCnt - parkingBikeTotCnt);
  • 支持按租赁点名称关键词搜索实时状态(如"광화문");
  • 无需用户自行申请首尔开放数据广场(서울 열린데이터 광장)的SEOUL_OPEN_API_KEY,全部经k-skill-proxy代理完成,upstream key 只存放在代理服务器端。

技能元数据在 seoul-bike/skill.json 中定义为category: transitlocale: ko-KRphase: v1,说明该技能定位为面向韩国本地场景的交通类实时查询能力。

前置条件:零密钥设计的前提

在使用前,建议先阅读 公共设置指南 了解 k-skill 整体的环境变量与密钥解析顺序。对于seoul-bike技能而言,前置条件非常轻量:

  • 客户端仅依赖 Python 3 标准库(argparsejsonosurllib.*),无需安装任何第三方依赖;
  • 无需任何必填环境变量。用户不需要亲自申请 서울 열린데이터 광장 OpenAPI key,因为/v1/seoul-bike/*路由默认由 hosted 代理调用,upstream key 仅保存在代理服务器端;
  • 可选的KSKILL_PROXY_BASE_URL仅在自托管(self-host)或使用其他代理时设置;留空时使用默认 hosted 地址https://k-skill-proxy.nomadamas.org

KSKILL_PROXY_BASE_URL 的解析逻辑

源码 seoul-bike/scripts/seoul_bike.py 中get_proxy_base_url()的解析规则为:

value = os.environ.get("KSKILL_PROXY_BASE_URL") if value and value.strip() and value.strip() != "replace-me": return value.strip().rstrip("/") return DEFAULT_PROXY_BASE_URL # https://k-skill-proxy.nomadamas.org

注意两个细节:

  • 空值、纯空白以及占位符replace-me都会被忽略并回退到默认 hosted 地址;
  • 返回值会去掉末尾的/,避免与后续路径拼接时出现双斜杠。

这与 docs/setup.md 中secrets.envKSKILL_PROXY_BASE_URL=留空的推荐做法完全一致:普通用户留空即使用 hosted 代理,只有自托管代理运营者才需要填入自己的地址。

基本路径与 Proxy 路由

所有请求默认发往https://k-skill-proxy.nomadamas.org/v1/seoul-bike/*。三个路由的职责如下:

endpointupstream / 行为主要输入
GET /v1/seoul-bike/realtime서울 열린데이터 광장bikeList实时租赁信息页startIndexendIndex
GET /v1/seoul-bike/stations서울 열린데이터 광장tbCycleStationInfo租赁点主数据页startIndexendIndex
GET /v1/seoul-bike/nearby代理服务端对 realtime 行按坐标半径过滤latlonradius_mlimit

代理服务端实现(server.js)

在代理源码 packages/k-skill-proxy/src/server.js 中可以印证以上三个路由的具体行为:

  • realtime / stations:先经normalizeSeoulBikePageQuery校验并规范化查询参数(非法输入返回400 bad_request),然后以config.seoulOpenApiKey注入 upstream 请求;响应会解析 JSON 并通过getSeoulOpenApiKey语义错误检测,最后在 payload 上附加proxy元数据(namecacherequested_at),成功响应(2xx)会按 TTL 写入缓存。
  • nearby:代理服务端先调用fetchAllSeoulBikeRealtimeRows拉取全部实时行,再在服务端完成"归一化 → 过滤坐标缺失行 → 按distance_m <= radiusMeters过滤 → 按距离升序排序 →slice(0, limit)截断"的完整流水线,最后构造{query, count, items, proxy}响应结构。这意味着半径过滤与排序完全在代理端完成,客户端拿到即为按距离排序的最终结果。

三个路由都实现了makeCacheKey缓存的读取与回写:缓存命中时返回proxy.cache.hit: true,未命中时回写并附带ttl_ms。这一点在"响应结构"一节还会细说。

基本请求流程

  1. 客户端/技能调用默认 hosted 路径或KSKILL_PROXY_BASE_URL下的/v1/seoul-bike/nearby端点;
  2. 代理使用服务器端SEOUL_OPEN_API_KEY调用 서울 열린데이터 광장bikeList
  3. 代理按坐标与半径对租赁点排序,返回available_bikesempty_docksdistance_m
  4. 响应附带proxy.cache.hitproxy.requested_at元数据。

curl 直接调用示例

BASE="${KSKILL_PROXY_BASE_URL:-https://k-skill-proxy.nomadamas.org}" curl -fsS --get "${BASE}/v1/seoul-bike/nearby" \ --data-urlencode 'lat=37.5717' \ --data-urlencode 'lon=126.9763' \ --data-urlencode 'radius_m=500' \ --data-urlencode 'limit=5'

BASE变量使用了${KSKILL_PROXY_BASE_URL:-默认值}展开,与源码中get_proxy_base_url()的默认行为保持一致。其他两个路由同样可直接调用,例如:

curl -fsS --get "${BASE}/v1/seoul-bike/realtime" \ --data-urlencode 'startIndex=1' \ --data-urlencode 'endIndex=1000'

CLI 使用:单入口脚本

技能采用单一入口点设计,统一通过 k-skill CLI 执行:

npx -y @nomadamas/k-skill@0 exec seoul-bike scripts/seoul_bike.py -- <subcommand> [args]

首次使用时批准Bash(python3 *seoul_bike.py:*)这一条命令模式后,后续调用会自动放行。脚本本身是一个完整的argparse程序(seoul-bike/scripts/seoul_bike.py),子命令与参数如下:

子命令参数与默认值说明
nearby--lat(必填)、--lon(必填)、--radius-m 500--limit 10--json查询坐标周边实时租赁点
search<keyword>(必填)、--start-index 1--end-index 1000--limit 10--json按租赁点名称关键词搜索实时状态
realtime--start-index 1--end-index 1000查询实时租赁信息原始 JSON 页

示例 1:坐标周边查询

npx -y @nomadamas/k-skill@0 exec seoul-bike scripts/seoul_bike.py -- nearby --lat 37.5717 --lon 126.9763 --radius-m 500

输出摘要包含:租赁点名称、可租自行车数、空置停车桩数、距离(米)、查询时间(proxy.requested_at)。预期输出格式如下:

따릉이 주변 대여소 2곳 기준 좌표: 37.5717, 126.9763 / 반경 500m - 101. 광화문역 1번출구 앞: 대여 가능 4대, 빈 거치대 11개, 거리 0m 조회 시각: 2026-05-21T06:10:00.000Z

示例 2:按名称搜索

npx -y @nomadamas/k-skill@0 exec seoul-bike scripts/seoul_bike.py -- search "광화문" --limit 5

search的底层行为值得注意:fetch_realtime_payload()(seoul-bike/scripts/seoul_bike.py)会自动按list_total_count翻页拉取全部实时行,直到current_end >= total_count或该页无行才停止,然后再用关键词做大小写不敏感的包含匹配(filter_realtime_rows)。也就是说,即使--end-index 1000指定的首页只有 1000 行,只要list_total_count更大,脚本会继续请求后续页直到拉完整个数据集,避免漏检。找不到匹配时向stderr输出提示并返回退出码1

字段归一化与空值兜底

normalize_realtime_row()(seoul-bike/scripts/seoul_bike.py)同时兼容 upstream 韩文/英文两套字段名:

统一字段upstream 候选字段
station_idstationId/station_id
station_namestationName/station_name
rack_total_countrackTotCnt/rack_total_count
available_bikesparkingBikeTotCnt/available_bikes
shared_percentshared/shared_percent
latitude/longitudestationLatitude/latitude

empty_docksmax(0, rack_total_count - available_bikes)计算;rack_total_countavailable_bikes任一缺失时empty_docksNone,格式化输出会显示"알 수 없음"而非报错。字符串型数值(如"4")会经_to_int()安全转换为int

响应结构与元数据

nearby响应的 JSON 结构为:

{ "query": { "latitude": 37.5717, "longitude": 126.9763, "radius_m": 500, "limit": 5 }, "count": 2, "items": [ { "station_id": "ST-101", "station_name": "101. 광화문역 1번출구 앞", "rack_total_count": 15, "available_bikes": 4, "empty_docks": 11, "shared_percent": 27, "distance_m": 0, "latitude": 37.5717, "longitude": 126.9763 } ], "proxy": { "name": "k-skill-proxy", "cache": { "hit": false, "ttl_ms": 30000 }, "requested_at": "2026-05-21T06:10:00.000Z" } }

其中proxy.cache.hit标识本次响应是否来自代理缓存,proxy.requested_at为服务端发起请求的 ISO 时间戳。由于实时数据持续变化,向用户呈现结果时必须附带该查询时间,这正是脚本在format_nearby()中无条件追加"조회 시각"行的原因。

使用--json参数可直接输出原始 JSON(nearbysearch均支持),方便集成到其他程序或进行调试。

fallback / 替代流程

  • 显式设置KSKILL_PROXY_BASE_URL时,优先使用该代理;
  • 默认 hosted 路径为https://k-skill-proxy.nomadamas.org/v1/seoul-bike/*
  • 自托管运营者只需在服务器端配置SEOUL_OPEN_API_KEY(参见 k-skill 代理服务器指南 中SEOUL_OPEN_API_KEY=...一节),客户端无需任何密钥。

注意事项与失败模式

  • 实时数据不断变化,回答中必须同时给出查询时间;
  • 本技能仅用于查询,不进行预约/租赁自动化;
  • 서울 열린데이터 광장 可能出现 quota 超限或临时故障;
  • 半径内没有租赁点时,items: []是正常返回,不代表报错。

CLI 层的错误处理

main()(seoul-bike/scripts/seoul_bike.py)对三类异常做了差异化处理:

  • HTTPError503 且响应体error == "upstream_not_configured"时,输出"代理未配置所需 API 密钥,请联系运营者";
  • 其余 HTTP 错误优先输出代理返回的message,否则输出API HTTP 오류: <code> <reason>
  • URLError(代理不可达)输出"代理服务器无响应,请稍后重试或联系运营者";
  • JSON 解析失败则输出解析错误信息。

对应的失败场景在 seoul-bike/instruction.md 中归纳为:代理 upstream key 未设置(无SEOUL_OPEN_API_KEY)、서울 열린데이터 광장 quota 超限、实时 API 返回空行或临时错误、坐标缺失或半径内无租赁点。

测试验证

仓库在 scripts/test_seoul_bike.py 中提供了基于unittest的测试,覆盖了核心行为:

  • format_nearby输出包含站名、可租数、空桩数、距离与查询时间;
  • filter_realtime_rows能按关键词过滤并按empty_docks正确计算;
  • 分页行为:mockfetch_json返回两页数据时,fetch_realtime_pages会连续请求 2 次并合并两页结果(对应list_total_count翻页逻辑);
  • CLI 层search/nearby输出与--json输出结构正确;
  • 清空环境变量后get_proxy_base_url()回退到 hosted 默认地址。

这些测试既验证了脚本的字段归一化、分页抓取、格式化输出,也印证了本文前述的默认代理地址与响应结构,可作为二次开发时的回归保障。

相关文档与源码

  • 技能说明:seoul-bike/SKILL.md、seoul-bike/instruction.md
  • 核心脚本:seoul-bike/scripts/seoul_bike.py
  • 代理服务端路由:packages/k-skill-proxy/src/server.js
  • 代理部署与密钥管理:docs/features/k-skill-proxy.md、docs/setup.md
  • 测试用例:scripts/test_seoul_bike.py

参考表面(서울 열린데이터 광장):bikeList(따릉이 실시간 대여정보)、tbCycleStationInfo(따릉이 대여소 정보)。

【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询